12
12
#include < sync.h>
13
13
#include < timedata.h>
14
14
#include < util/system.h>
15
+ #include < clientversion.h>
15
16
16
17
#include < map>
17
18
#include < set>
18
19
#include < stdint.h>
19
20
#include < vector>
21
+ #include < iostream>
22
+ #include < streams.h>
23
+ #include < fs.h>
24
+ #include < hash.h>
25
+
20
26
21
27
/* *
22
28
* Extended statistics about a CAddress
@@ -72,15 +78,15 @@ class CAddrInfo : public CAddress
72
78
}
73
79
74
80
// ! Calculate in which "tried" bucket this entry belongs
75
- int GetTriedBucket (const uint256 &nKey) const ;
81
+ int GetTriedBucket (const uint256 &nKey, const std::vector< bool > &asmap ) const ;
76
82
77
83
// ! Calculate in which "new" bucket this entry belongs, given a certain source
78
- int GetNewBucket (const uint256 &nKey, const CNetAddr& src) const ;
84
+ int GetNewBucket (const uint256 &nKey, const CNetAddr& src, const std::vector< bool > &asmap ) const ;
79
85
80
86
// ! Calculate in which "new" bucket this entry belongs, using its default source
81
- int GetNewBucket (const uint256 &nKey) const
87
+ int GetNewBucket (const uint256 &nKey, const std::vector< bool > &asmap ) const
82
88
{
83
- return GetNewBucket (nKey, source);
89
+ return GetNewBucket (nKey, source, asmap );
84
90
}
85
91
86
92
// ! Calculate in which position of a bucket to store this entry.
@@ -174,6 +180,7 @@ static const int64_t ADDRMAN_TEST_WINDOW = 40*60; // 40 minutes
174
180
*/
175
181
class CAddrMan
176
182
{
183
+ friend class CAddrManTest ;
177
184
protected:
178
185
// ! critical section to protect the inner data structures
179
186
mutable CCriticalSection cs;
@@ -268,9 +275,29 @@ class CAddrMan
268
275
void SetServices_ (const CService &addr, ServiceFlags nServices) EXCLUSIVE_LOCKS_REQUIRED(cs);
269
276
270
277
public:
278
+ // Compressed IP->ASN mapping, loaded from a file when a node starts.
279
+ // Should be always empty if no file was provided.
280
+ // This mapping is then used for bucketing nodes in Addrman.
281
+ //
282
+ // If asmap is provided, nodes will be bucketed by
283
+ // AS they belong to, in order to make impossible for a node
284
+ // to connect to several nodes hosted in a single AS.
285
+ // This is done in response to Erebus attack, but also to generally
286
+ // diversify the connections every node creates,
287
+ // especially useful when a large fraction of nodes
288
+ // operate under a couple of cloud providers.
289
+ //
290
+ // If a new asmap was provided, the existing records
291
+ // would be re-bucketed accordingly.
292
+ std::vector<bool > m_asmap;
293
+
294
+ // Read asmap from provided binary file
295
+ static std::vector<bool > DecodeAsmap (fs::path path);
296
+
297
+
271
298
/* *
272
299
* serialized format:
273
- * * version byte (currently 1 )
300
+ * * version byte (1 for pre-asmap files, 2 for files including asmap version )
274
301
* * 0x20 + nKey (serialized as if it were a vector, for backward compatibility)
275
302
* * nNew
276
303
* * nTried
@@ -302,7 +329,7 @@ class CAddrMan
302
329
{
303
330
LOCK (cs);
304
331
305
- unsigned char nVersion = 1 ;
332
+ unsigned char nVersion = 2 ;
306
333
s << nVersion;
307
334
s << ((unsigned char )32 );
308
335
s << nKey;
@@ -345,6 +372,13 @@ class CAddrMan
345
372
}
346
373
}
347
374
}
375
+ // Store asmap version after bucket entries so that it
376
+ // can be ignored by older clients for backward compatibility.
377
+ uint256 asmap_version;
378
+ if (m_asmap.size () != 0 ) {
379
+ asmap_version = SerializeHash (m_asmap);
380
+ }
381
+ s << asmap_version;
348
382
}
349
383
350
384
template <typename Stream>
@@ -353,7 +387,6 @@ class CAddrMan
353
387
LOCK (cs);
354
388
355
389
Clear ();
356
-
357
390
unsigned char nVersion;
358
391
s >> nVersion;
359
392
unsigned char nKeySize;
@@ -383,16 +416,6 @@ class CAddrMan
383
416
mapAddr[info] = n;
384
417
info.nRandomPos = vRandom.size ();
385
418
vRandom.push_back (n);
386
- if (nVersion != 1 || nUBuckets != ADDRMAN_NEW_BUCKET_COUNT) {
387
- // In case the new table data cannot be used (nVersion unknown, or bucket count wrong),
388
- // immediately try to give them a reference based on their primary source address.
389
- int nUBucket = info.GetNewBucket (nKey);
390
- int nUBucketPos = info.GetBucketPosition (nKey, true , nUBucket);
391
- if (vvNew[nUBucket][nUBucketPos] == -1 ) {
392
- vvNew[nUBucket][nUBucketPos] = n;
393
- info.nRefCount ++;
394
- }
395
- }
396
419
}
397
420
nIdCount = nNew;
398
421
@@ -401,7 +424,7 @@ class CAddrMan
401
424
for (int n = 0 ; n < nTried; n++) {
402
425
CAddrInfo info;
403
426
s >> info;
404
- int nKBucket = info.GetTriedBucket (nKey);
427
+ int nKBucket = info.GetTriedBucket (nKey, m_asmap );
405
428
int nKBucketPos = info.GetBucketPosition (nKey, false , nKBucket);
406
429
if (vvTried[nKBucket][nKBucketPos] == -1 ) {
407
430
info.nRandomPos = vRandom.size ();
@@ -417,20 +440,48 @@ class CAddrMan
417
440
}
418
441
nTried -= nLost;
419
442
420
- // Deserialize positions in the new table (if possible).
443
+ // Store positions in the new table buckets to apply later (if possible).
444
+ std::map<int , int > entryToBucket; // Represents which entry belonged to which bucket when serializing
445
+
421
446
for (int bucket = 0 ; bucket < nUBuckets; bucket++) {
422
447
int nSize = 0 ;
423
448
s >> nSize;
424
449
for (int n = 0 ; n < nSize; n++) {
425
450
int nIndex = 0 ;
426
451
s >> nIndex;
427
452
if (nIndex >= 0 && nIndex < nNew) {
428
- CAddrInfo &info = mapInfo[nIndex];
429
- int nUBucketPos = info.GetBucketPosition (nKey, true , bucket);
430
- if (nVersion == 1 && nUBuckets == ADDRMAN_NEW_BUCKET_COUNT && vvNew[bucket][nUBucketPos] == -1 && info.nRefCount < ADDRMAN_NEW_BUCKETS_PER_ADDRESS) {
431
- info.nRefCount ++;
432
- vvNew[bucket][nUBucketPos] = nIndex;
433
- }
453
+ entryToBucket[nIndex] = bucket;
454
+ }
455
+ }
456
+ }
457
+
458
+ uint256 supplied_asmap_version;
459
+ if (m_asmap.size () != 0 ) {
460
+ supplied_asmap_version = SerializeHash (m_asmap);
461
+ }
462
+ uint256 serialized_asmap_version;
463
+ if (nVersion > 1 ) {
464
+ s >> serialized_asmap_version;
465
+ }
466
+
467
+ for (int n = 0 ; n < nNew; n++) {
468
+ CAddrInfo &info = mapInfo[n];
469
+ int bucket = entryToBucket[n];
470
+ int nUBucketPos = info.GetBucketPosition (nKey, true , bucket);
471
+ if (nVersion == 2 && nUBuckets == ADDRMAN_NEW_BUCKET_COUNT && vvNew[bucket][nUBucketPos] == -1 &&
472
+ info.nRefCount < ADDRMAN_NEW_BUCKETS_PER_ADDRESS && serialized_asmap_version == supplied_asmap_version) {
473
+ // Bucketing has not changed, using existing bucket positions for the new table
474
+ vvNew[bucket][nUBucketPos] = n;
475
+ info.nRefCount ++;
476
+ } else {
477
+ // In case the new table data cannot be used (nVersion unknown, bucket count wrong or new asmap),
478
+ // try to give them a reference based on their primary source address.
479
+ LogPrint (BCLog::ADDRMAN, " Bucketing method was updated, re-bucketing addrman entries from disk\n " );
480
+ bucket = info.GetNewBucket (nKey, m_asmap);
481
+ nUBucketPos = info.GetBucketPosition (nKey, true , bucket);
482
+ if (vvNew[bucket][nUBucketPos] == -1 ) {
483
+ vvNew[bucket][nUBucketPos] = n;
484
+ info.nRefCount ++;
434
485
}
435
486
}
436
487
}
0 commit comments