~ubuntu-branches/ubuntu/maverick/libtorrent-rasterbar/maverick

« back to all changes in this revision

Viewing changes to src/kademlia/routing_table.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Cristian Greco
  • Date: 2008-07-02 10:46:21 UTC
  • Revision ID: james.westby@ubuntu.com-20080702104621-jzx3pfke9lkcxfxn
Tags: upstream-0.13.1
ImportĀ upstreamĀ versionĀ 0.13.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 
 
3
Copyright (c) 2006, Arvid Norberg
 
4
All rights reserved.
 
5
 
 
6
Redistribution and use in source and binary forms, with or without
 
7
modification, are permitted provided that the following conditions
 
8
are met:
 
9
 
 
10
    * Redistributions of source code must retain the above copyright
 
11
      notice, this list of conditions and the following disclaimer.
 
12
    * Redistributions in binary form must reproduce the above copyright
 
13
      notice, this list of conditions and the following disclaimer in
 
14
      the documentation and/or other materials provided with the distribution.
 
15
    * Neither the name of the author nor the names of its
 
16
      contributors may be used to endorse or promote products derived
 
17
      from this software without specific prior written permission.
 
18
 
 
19
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 
20
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 
21
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 
22
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 
23
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 
24
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 
25
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 
26
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 
27
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 
28
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 
29
POSSIBILITY OF SUCH DAMAGE.
 
30
 
 
31
*/
 
32
 
 
33
#include "libtorrent/pch.hpp"
 
34
 
 
35
#include <vector>
 
36
#include <deque>
 
37
#include <algorithm>
 
38
#include <functional>
 
39
#include <numeric>
 
40
#include <boost/cstdint.hpp>
 
41
#include <boost/bind.hpp>
 
42
 
 
43
#include "libtorrent/kademlia/routing_table.hpp"
 
44
#include "libtorrent/kademlia/node_id.hpp"
 
45
#include "libtorrent/session_settings.hpp"
 
46
 
 
47
using boost::bind;
 
48
using boost::uint8_t;
 
49
 
 
50
namespace libtorrent { namespace dht
 
51
{
 
52
 
 
53
using asio::ip::udp;
 
54
typedef asio::ip::address_v4 address;
 
55
 
 
56
routing_table::routing_table(node_id const& id, int bucket_size
 
57
        , dht_settings const& settings)
 
58
        : m_bucket_size(bucket_size)
 
59
        , m_settings(settings)
 
60
        , m_id(id)
 
61
        , m_lowest_active_bucket(160)
 
62
{
 
63
        // distribute the refresh times for the buckets in an
 
64
        // attempt do even out the network load
 
65
        for (int i = 0; i < 160; ++i)
 
66
                m_bucket_activity[i] = time_now() - milliseconds(i*5625);
 
67
        m_bucket_activity[0] = time_now() - minutes(15);
 
68
}
 
69
 
 
70
boost::tuple<int, int> routing_table::size() const
 
71
{
 
72
        int nodes = 0;
 
73
        int replacements = 0;
 
74
        for (table_t::const_iterator i = m_buckets.begin()
 
75
                , end(m_buckets.end()); i != end; ++i)
 
76
        {
 
77
                nodes += i->first.size();
 
78
                replacements += i->second.size();
 
79
        }
 
80
        return boost::make_tuple(nodes, replacements);
 
81
}
 
82
 
 
83
size_type routing_table::num_global_nodes() const
 
84
{
 
85
        int first_full = m_lowest_active_bucket;
 
86
        int num_nodes = 1; // we are one of the nodes
 
87
        for (; first_full < 160
 
88
                && int(m_buckets[first_full].first.size()) < m_bucket_size;
 
89
                ++first_full)
 
90
        {
 
91
                num_nodes += m_buckets[first_full].first.size();
 
92
        }
 
93
 
 
94
        return (2 << (160 - first_full)) * num_nodes;
 
95
}
 
96
 
 
97
#ifdef TORRENT_DHT_VERBOSE_LOGGING
 
98
 
 
99
void routing_table::print_state(std::ostream& os) const
 
100
{
 
101
        os << "kademlia routing table state\n"
 
102
                << "bucket_size: " << m_bucket_size << "\n"
 
103
                << "global node count: " << num_global_nodes() << "\n"
 
104
                << "node_id: " << m_id << "\n\n";
 
105
 
 
106
        os << "number of nodes per bucket:\n-- live ";
 
107
        for (int i = 8; i < 160; ++i)
 
108
                os << "-";
 
109
        os << "\n";
 
110
 
 
111
        for (int k = 0; k < 8; ++k)
 
112
        {
 
113
                for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end());
 
114
                        i != end; ++i)
 
115
                {
 
116
                        os << (int(i->first.size()) > (7 - k) ? "|" : " ");
 
117
                }
 
118
                os << "\n";
 
119
        }
 
120
        for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end());
 
121
                i != end; ++i)
 
122
        {
 
123
                os << "+";
 
124
        }
 
125
        os << "\n";
 
126
        for (int k = 0; k < 8; ++k)
 
127
        {
 
128
                for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end());
 
129
                        i != end; ++i)
 
130
                {
 
131
                        os << (int(i->second.size()) > k ? "|" : " ");
 
132
                }
 
133
                os << "\n";
 
134
        }
 
135
        os << "-- cached ";
 
136
        for (int i = 10; i < 160; ++i)
 
137
                os << "-";
 
138
        os << "\n\n";
 
139
 
 
140
        os << "nodes:\n";
 
141
        for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end());
 
142
                i != end; ++i)
 
143
        {
 
144
                int bucket_index = int(i - m_buckets.begin());
 
145
                os << "=== BUCKET = " << bucket_index
 
146
                        << " = " << (bucket_index >= m_lowest_active_bucket?"active":"inactive")
 
147
                        << " = " << total_seconds(time_now() - m_bucket_activity[bucket_index])
 
148
                        << " s ago ===== \n";
 
149
                for (bucket_t::const_iterator j = i->first.begin()
 
150
                        , end(i->first.end()); j != end; ++j)
 
151
                {
 
152
                        os << "ip: " << j->addr << "    fails: " << j->fail_count
 
153
                                << "    id: " << j->id << "\n";
 
154
                }
 
155
        }
 
156
}
 
157
 
 
158
#endif
 
159
 
 
160
void routing_table::touch_bucket(int bucket)
 
161
{
 
162
        m_bucket_activity[bucket] = time_now();
 
163
}
 
164
 
 
165
ptime routing_table::next_refresh(int bucket)
 
166
{
 
167
        TORRENT_ASSERT(bucket < 160);
 
168
        TORRENT_ASSERT(bucket >= 0);
 
169
        // lower than or equal to since a refresh of bucket 0 will
 
170
        // effectively refresh the lowest active bucket as well
 
171
        if (bucket < m_lowest_active_bucket && bucket > 0)
 
172
                return time_now() + minutes(15);
 
173
        return m_bucket_activity[bucket] + minutes(15);
 
174
}
 
175
 
 
176
void routing_table::replacement_cache(bucket_t& nodes) const
 
177
{
 
178
        for (table_t::const_iterator i = m_buckets.begin()
 
179
                , end(m_buckets.end()); i != end; ++i)
 
180
        {
 
181
                std::copy(i->second.begin(), i->second.end()
 
182
                        , std::back_inserter(nodes));
 
183
        }
 
184
}
 
185
 
 
186
bool routing_table::need_node(node_id const& id)
 
187
{
 
188
        int bucket_index = distance_exp(m_id, id);
 
189
        TORRENT_ASSERT(bucket_index < (int)m_buckets.size());
 
190
        TORRENT_ASSERT(bucket_index >= 0);
 
191
        bucket_t& b = m_buckets[bucket_index].first;
 
192
        bucket_t& rb = m_buckets[bucket_index].second;
 
193
 
 
194
        // if the replacement cache is full, we don't
 
195
        // need another node. The table is fine the
 
196
        // way it is.
 
197
        if ((int)rb.size() >= m_bucket_size) return false;
 
198
        
 
199
        // if the node already exists, we don't need it
 
200
        if (std::find_if(b.begin(), b.end(), bind(&node_entry::id, _1) == id)
 
201
                != b.end()) return false;
 
202
 
 
203
        if (std::find_if(rb.begin(), rb.end(), bind(&node_entry::id, _1) == id)
 
204
                != rb.end()) return false;
 
205
 
 
206
        return true;
 
207
}
 
208
 
 
209
void routing_table::node_failed(node_id const& id)
 
210
{
 
211
        int bucket_index = distance_exp(m_id, id);
 
212
        TORRENT_ASSERT(bucket_index < (int)m_buckets.size());
 
213
        TORRENT_ASSERT(bucket_index >= 0);
 
214
        bucket_t& b = m_buckets[bucket_index].first;
 
215
        bucket_t& rb = m_buckets[bucket_index].second;
 
216
 
 
217
        bucket_t::iterator i = std::find_if(b.begin(), b.end()
 
218
                , bind(&node_entry::id, _1) == id);
 
219
 
 
220
        if (i == b.end()) return;
 
221
        
 
222
        // if messages to ourself fails, ignore it
 
223
        if (bucket_index == 0) return;
 
224
 
 
225
        if (rb.empty())
 
226
        {
 
227
                ++i->fail_count;
 
228
                if (i->fail_count >= m_settings.max_fail_count)
 
229
                {
 
230
                        b.erase(i);
 
231
                        TORRENT_ASSERT(m_lowest_active_bucket <= bucket_index);
 
232
                        while (m_lowest_active_bucket < 160
 
233
                                && m_buckets[m_lowest_active_bucket].first.empty())
 
234
                        {
 
235
                                ++m_lowest_active_bucket;
 
236
                        }
 
237
                }
 
238
                return;
 
239
        }
 
240
 
 
241
        b.erase(i);
 
242
        b.push_back(rb.back());
 
243
        rb.erase(rb.end() - 1);
 
244
}
 
245
 
 
246
void routing_table::add_router_node(udp::endpoint router)
 
247
{
 
248
        m_router_nodes.insert(router);
 
249
}
 
250
 
 
251
// this function is called every time the node sees
 
252
// a sign of a node being alive. This node will either
 
253
// be inserted in the k-buckets or be moved to the top
 
254
// of its bucket.
 
255
// the return value indicates if the table needs a refresh.
 
256
// if true, the node should refresh the table (i.e. do a find_node
 
257
// on its own id)
 
258
bool routing_table::node_seen(node_id const& id, udp::endpoint addr)
 
259
{
 
260
        if (m_router_nodes.find(addr) != m_router_nodes.end()) return false;
 
261
        int bucket_index = distance_exp(m_id, id);
 
262
        TORRENT_ASSERT(bucket_index < (int)m_buckets.size());
 
263
        TORRENT_ASSERT(bucket_index >= 0);
 
264
        bucket_t& b = m_buckets[bucket_index].first;
 
265
 
 
266
        bucket_t::iterator i = std::find_if(b.begin(), b.end()
 
267
                , bind(&node_entry::id, _1) == id);
 
268
 
 
269
        bool ret = need_bootstrap();
 
270
 
 
271
        //m_bucket_activity[bucket_index] = time_now();
 
272
 
 
273
        if (i != b.end())
 
274
        {
 
275
                // TODO: what do we do if we see a node with
 
276
                // the same id as a node at a different address?
 
277
//              TORRENT_ASSERT(i->addr == addr);
 
278
 
 
279
                // we already have the node in our bucket
 
280
                // just move it to the back since it was
 
281
                // the last node we had any contact with
 
282
                // in this bucket
 
283
                b.erase(i);
 
284
                b.push_back(node_entry(id, addr));
 
285
//              TORRENT_LOG(table) << "replacing node: " << id << " " << addr;
 
286
                return ret;
 
287
        }
 
288
 
 
289
        // if the node was not present in our list
 
290
        // we will only insert it if there is room
 
291
        // for it, or if some of our nodes have gone
 
292
        // offline
 
293
        if ((int)b.size() < m_bucket_size)
 
294
        {
 
295
                if (b.empty()) b.reserve(m_bucket_size);
 
296
                b.push_back(node_entry(id, addr));
 
297
                // if bucket index is 0, the node is ourselves
 
298
                // don't updated m_lowest_active_bucket
 
299
                if (bucket_index < m_lowest_active_bucket
 
300
                        && bucket_index > 0)
 
301
                        m_lowest_active_bucket = bucket_index;
 
302
//              TORRENT_LOG(table) << "inserting node: " << id << " " << addr;
 
303
                return ret;
 
304
        }
 
305
 
 
306
        // if there is no room, we look for nodes marked as stale
 
307
        // in the k-bucket. If we find one, we can replace it.
 
308
        // A node is considered stale if it has failed at least one
 
309
        // time. Here we choose the node that has failed most times.
 
310
        // If we don't find one, place this node in the replacement-
 
311
        // cache and replace any nodes that will fail in the future
 
312
        // with nodes from that cache.
 
313
 
 
314
        i = std::max_element(b.begin(), b.end()
 
315
                , bind(&node_entry::fail_count, _1)
 
316
                < bind(&node_entry::fail_count, _2));
 
317
 
 
318
        if (i != b.end() && i->fail_count > 0)
 
319
        {
 
320
                // i points to a node that has been marked
 
321
                // as stale. Replace it with this new one
 
322
                b.erase(i);
 
323
                b.push_back(node_entry(id, addr));
 
324
//              TORRENT_LOG(table) << "replacing stale node: " << id << " " << addr;
 
325
                return ret;
 
326
        }
 
327
 
 
328
        // if we don't have any identified stale nodes in
 
329
        // the bucket, and the bucket is full, we have to
 
330
        // cache this node and wait until some node fails
 
331
        // and then replace it.
 
332
 
 
333
        bucket_t& rb = m_buckets[bucket_index].second;
 
334
 
 
335
        i = std::find_if(rb.begin(), rb.end()
 
336
                , bind(&node_entry::id, _1) == id);
 
337
 
 
338
        // if the node is already in the replacement bucket
 
339
        // just return.
 
340
        if (i != rb.end()) return ret;
 
341
        
 
342
        if ((int)rb.size() > m_bucket_size) rb.erase(rb.begin());
 
343
        if (rb.empty()) rb.reserve(m_bucket_size);
 
344
        rb.push_back(node_entry(id, addr));
 
345
//      TORRENT_LOG(table) << "inserting node in replacement cache: " << id << " " << addr;
 
346
        return ret;
 
347
}
 
348
 
 
349
bool routing_table::need_bootstrap() const
 
350
{
 
351
        for (const_iterator i = begin(); i != end(); ++i)
 
352
        {
 
353
                if (i->fail_count == 0) return false;
 
354
        }
 
355
        return true;
 
356
}
 
357
 
 
358
// fills the vector with the k nodes from our buckets that
 
359
// are nearest to the given id.
 
360
void routing_table::find_node(node_id const& target
 
361
        , std::vector<node_entry>& l, bool include_self, int count)
 
362
{
 
363
        l.clear();
 
364
        if (count == 0) count = m_bucket_size;
 
365
        l.reserve(count);
 
366
 
 
367
        int bucket_index = distance_exp(m_id, target);
 
368
        bucket_t& b = m_buckets[bucket_index].first;
 
369
 
 
370
        // copy all nodes that hasn't failed into the target
 
371
        // vector.
 
372
        std::remove_copy_if(b.begin(), b.end(), std::back_inserter(l)
 
373
                , bind(&node_entry::fail_count, _1));
 
374
        TORRENT_ASSERT((int)l.size() <= count);
 
375
 
 
376
        if ((int)l.size() == count)
 
377
        {
 
378
                TORRENT_ASSERT(std::count_if(l.begin(), l.end()
 
379
                        , boost::bind(&node_entry::fail_count, _1) != 0) == 0);
 
380
                return;
 
381
        }
 
382
 
 
383
        // if we didn't have enough nodes in that bucket
 
384
        // we have to reply with nodes from buckets closer
 
385
        // to us. i.e. all the buckets in the range
 
386
        // [0, bucket_index) if we are to include ourself
 
387
        // or [1, bucket_index) if not.
 
388
        bucket_t tmpb;
 
389
        for (int i = include_self?0:1; i < bucket_index; ++i)
 
390
        {
 
391
                bucket_t& b = m_buckets[i].first;
 
392
                std::remove_copy_if(b.begin(), b.end(), std::back_inserter(tmpb)
 
393
                        , bind(&node_entry::fail_count, _1));
 
394
        }
 
395
 
 
396
        std::random_shuffle(tmpb.begin(), tmpb.end());
 
397
        size_t to_copy = (std::min)(m_bucket_size - l.size()
 
398
                , tmpb.size());
 
399
        std::copy(tmpb.begin(), tmpb.begin() + to_copy
 
400
                , std::back_inserter(l));
 
401
                
 
402
        TORRENT_ASSERT((int)l.size() <= m_bucket_size);
 
403
 
 
404
        // return if we have enough nodes or if the bucket index
 
405
        // is the biggest index available (there are no more buckets)
 
406
        // to look in.
 
407
        if ((int)l.size() == count
 
408
                || bucket_index == (int)m_buckets.size() - 1)
 
409
        {
 
410
                TORRENT_ASSERT(std::count_if(l.begin(), l.end()
 
411
                        , boost::bind(&node_entry::fail_count, _1) != 0) == 0);
 
412
                return;
 
413
        }
 
414
 
 
415
        for (size_t i = bucket_index + 1; i < m_buckets.size(); ++i)
 
416
        {
 
417
                bucket_t& b = m_buckets[i].first;
 
418
        
 
419
                std::remove_copy_if(b.begin(), b.end(), std::back_inserter(l)
 
420
                        , bind(&node_entry::fail_count, _1));
 
421
                if ((int)l.size() >= count)
 
422
                {
 
423
                        l.erase(l.begin() + count, l.end());
 
424
                        TORRENT_ASSERT(std::count_if(l.begin(), l.end()
 
425
                                , boost::bind(&node_entry::fail_count, _1) != 0) == 0);
 
426
                        return;
 
427
                }
 
428
        }
 
429
        TORRENT_ASSERT((int)l.size() == count
 
430
                || std::distance(l.begin(), l.end()) < m_bucket_size);
 
431
        TORRENT_ASSERT((int)l.size() <= count);
 
432
 
 
433
        TORRENT_ASSERT(std::count_if(l.begin(), l.end()
 
434
                , boost::bind(&node_entry::fail_count, _1) != 0) == 0);
 
435
}
 
436
 
 
437
routing_table::iterator routing_table::begin() const
 
438
{
 
439
        // +1 to avoid ourself
 
440
        return iterator(m_buckets.begin() + 1, m_buckets.end());
 
441
}
 
442
 
 
443
routing_table::iterator routing_table::end() const
 
444
{
 
445
        return iterator(m_buckets.end(), m_buckets.end());
 
446
}
 
447
 
 
448
} } // namespace libtorrent::dht
 
449