Skip to content

Commit adea5e1

Browse files
committed
Merge #18023: Fix some asmap issues
c86bc14 Make asmap Interpret tolerant of malicious map data (Pieter Wuille) 38c2395 Use ASNs for mapped IPv4 addresses correctly (Pieter Wuille) 6f8c937 Mark asmap const in statistics code (Pieter Wuille) d58bcdc Avoid asmap copies in initialization (Pieter Wuille) Pull request description: Here are a few things to improve in the asmap implementation. The first two commits are just code improvements. The last one is a bugfix (the exsting code wouldn't correctly apply ASN lookups to mapped/embedded IPv4 addresses). ACKs for top commit: practicalswift: ACK c86bc14 -- patch looks correct naumenkogs: utACK c86bc14 laanwj: ACK c86bc14 jonatack: ACK c86bc14 code looks correct, built/ran tests, bitcoind with -asmap pointed to asmap/demo.map Tree-SHA512: 1036f43152754d621bfbecfd3b7c7276e4670598fcaed42a3d275e51fa2cf3653e2c9e9cfa714f6c7719362541510e92171e076ac4169b55a0cc8908b2d514c0
2 parents bd5c4c6 + c86bc14 commit adea5e1

File tree

6 files changed

+89
-63
lines changed

6 files changed

+89
-63
lines changed

src/init.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1835,8 +1835,8 @@ bool AppInitMain(NodeContext& node)
18351835
InitError(strprintf(_("Could not find or parse specified asmap: '%s'").translated, asmap_path));
18361836
return false;
18371837
}
1838-
node.connman->SetAsmap(asmap);
18391838
const uint256 asmap_version = SerializeHash(asmap);
1839+
node.connman->SetAsmap(std::move(asmap));
18401840
LogPrintf("Using asmap version %s for IP bucketing.\n", asmap_version.ToString());
18411841
} else {
18421842
LogPrintf("Using /16 prefix for IP bucketing.\n");

src/net.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,7 @@ void CNode::SetAddrLocal(const CService& addrLocalIn) {
498498

499499
#undef X
500500
#define X(name) stats.name = name
501-
void CNode::copyStats(CNodeStats &stats, std::vector<bool> &m_asmap)
501+
void CNode::copyStats(CNodeStats &stats, const std::vector<bool> &m_asmap)
502502
{
503503
stats.nodeid = this->GetId();
504504
X(nServices);

src/net.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ class CConnman
331331
*/
332332
int64_t PoissonNextSendInbound(int64_t now, int average_interval_seconds);
333333

334-
void SetAsmap(std::vector<bool> asmap) { addrman.m_asmap = asmap; }
334+
void SetAsmap(std::vector<bool> asmap) { addrman.m_asmap = std::move(asmap); }
335335

336336
private:
337337
struct ListenSocket {
@@ -983,7 +983,7 @@ class CNode
983983

984984
void CloseSocketDisconnect();
985985

986-
void copyStats(CNodeStats &stats, std::vector<bool> &m_asmap);
986+
void copyStats(CNodeStats &stats, const std::vector<bool> &m_asmap);
987987

988988
ServiceFlags GetLocalServices() const
989989
{

src/netaddress.cpp

Lines changed: 56 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,26 @@ bool CNetAddr::GetIn6Addr(struct in6_addr* pipv6Addr) const
401401
return true;
402402
}
403403

404+
bool CNetAddr::HasLinkedIPv4() const
405+
{
406+
return IsRoutable() && (IsIPv4() || IsRFC6145() || IsRFC6052() || IsRFC3964() || IsRFC4380());
407+
}
408+
409+
uint32_t CNetAddr::GetLinkedIPv4() const
410+
{
411+
if (IsIPv4() || IsRFC6145() || IsRFC6052()) {
412+
// IPv4, mapped IPv4, SIIT translated IPv4: the IPv4 address is the last 4 bytes of the address
413+
return ReadBE32(ip + 12);
414+
} else if (IsRFC3964()) {
415+
// 6to4 tunneled IPv4: the IPv4 address is in bytes 2-6
416+
return ReadBE32(ip + 2);
417+
} else if (IsRFC4380()) {
418+
// Teredo tunneled IPv4: the IPv4 address is in the last 4 bytes of the address, but bitflipped
419+
return ~ReadBE32(ip + 12);
420+
}
421+
assert(false);
422+
}
423+
404424
uint32_t CNetAddr::GetNetClass() const {
405425
uint32_t net_class = NET_IPV6;
406426
if (IsLocal()) {
@@ -410,7 +430,7 @@ uint32_t CNetAddr::GetNetClass() const {
410430
net_class = NET_INTERNAL;
411431
} else if (!IsRoutable()) {
412432
net_class = NET_UNROUTABLE;
413-
} else if (IsIPv4() || IsRFC6145() || IsRFC6052() || IsRFC3964() || IsRFC4380()) {
433+
} else if (HasLinkedIPv4()) {
414434
net_class = NET_IPV4;
415435
} else if (IsTor()) {
416436
net_class = NET_ONION;
@@ -424,10 +444,24 @@ uint32_t CNetAddr::GetMappedAS(const std::vector<bool> &asmap) const {
424444
return 0; // Indicates not found, safe because AS0 is reserved per RFC7607.
425445
}
426446
std::vector<bool> ip_bits(128);
427-
for (int8_t byte_i = 0; byte_i < 16; ++byte_i) {
428-
uint8_t cur_byte = GetByte(15 - byte_i);
429-
for (uint8_t bit_i = 0; bit_i < 8; ++bit_i) {
430-
ip_bits[byte_i * 8 + bit_i] = (cur_byte >> (7 - bit_i)) & 1;
447+
if (HasLinkedIPv4()) {
448+
// For lookup, treat as if it was just an IPv4 address (pchIPv4 prefix + IPv4 bits)
449+
for (int8_t byte_i = 0; byte_i < 12; ++byte_i) {
450+
for (uint8_t bit_i = 0; bit_i < 8; ++bit_i) {
451+
ip_bits[byte_i * 8 + bit_i] = (pchIPv4[byte_i] >> (7 - bit_i)) & 1;
452+
}
453+
}
454+
uint32_t ipv4 = GetLinkedIPv4();
455+
for (int i = 0; i < 32; ++i) {
456+
ip_bits[96 + i] = (ipv4 >> (31 - i)) & 1;
457+
}
458+
} else {
459+
// Use all 128 bits of the IPv6 address otherwise
460+
for (int8_t byte_i = 0; byte_i < 16; ++byte_i) {
461+
uint8_t cur_byte = GetByte(15 - byte_i);
462+
for (uint8_t bit_i = 0; bit_i < 8; ++bit_i) {
463+
ip_bits[byte_i * 8 + bit_i] = (cur_byte >> (7 - bit_i)) & 1;
464+
}
431465
}
432466
}
433467
uint32_t mapped_as = Interpret(asmap, ip_bits);
@@ -463,51 +497,32 @@ std::vector<unsigned char> CNetAddr::GetGroup(const std::vector<bool> &asmap) co
463497
int nStartByte = 0;
464498
int nBits = 16;
465499

466-
// all local addresses belong to the same group
467-
if (IsLocal())
468-
{
500+
if (IsLocal()) {
501+
// all local addresses belong to the same group
469502
nBits = 0;
470-
}
471-
// all internal-usage addresses get their own group
472-
if (IsInternal())
473-
{
503+
} else if (IsInternal()) {
504+
// all internal-usage addresses get their own group
474505
nStartByte = sizeof(g_internal_prefix);
475506
nBits = (sizeof(ip) - sizeof(g_internal_prefix)) * 8;
476-
}
477-
// all other unroutable addresses belong to the same group
478-
else if (!IsRoutable())
479-
{
507+
} else if (!IsRoutable()) {
508+
// all other unroutable addresses belong to the same group
480509
nBits = 0;
481-
}
482-
// for IPv4 addresses, '1' + the 16 higher-order bits of the IP
483-
// includes mapped IPv4, SIIT translated IPv4, and the well-known prefix
484-
else if (IsIPv4() || IsRFC6145() || IsRFC6052())
485-
{
486-
nStartByte = 12;
487-
}
488-
// for 6to4 tunnelled addresses, use the encapsulated IPv4 address
489-
else if (IsRFC3964())
490-
{
491-
nStartByte = 2;
492-
}
493-
// for Teredo-tunnelled IPv6 addresses, use the encapsulated IPv4 address
494-
else if (IsRFC4380())
495-
{
496-
vchRet.push_back(GetByte(3) ^ 0xFF);
497-
vchRet.push_back(GetByte(2) ^ 0xFF);
510+
} else if (HasLinkedIPv4()) {
511+
// IPv4 addresses (and mapped IPv4 addresses) use /16 groups
512+
uint32_t ipv4 = GetLinkedIPv4();
513+
vchRet.push_back((ipv4 >> 24) & 0xFF);
514+
vchRet.push_back((ipv4 >> 16) & 0xFF);
498515
return vchRet;
499-
}
500-
else if (IsTor())
501-
{
516+
} else if (IsTor()) {
502517
nStartByte = 6;
503518
nBits = 4;
504-
}
505-
// for he.net, use /36 groups
506-
else if (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x04 && GetByte(12) == 0x70)
519+
} else if (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x04 && GetByte(12) == 0x70) {
520+
// for he.net, use /36 groups
507521
nBits = 36;
508-
// for the rest of the IPv6 network, use /32 groups
509-
else
522+
} else {
523+
// for the rest of the IPv6 network, use /32 groups
510524
nBits = 32;
525+
}
511526

512527
// push our ip onto vchRet byte by byte...
513528
while (nBits >= 8)

src/netaddress.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,11 @@ class CNetAddr
7979
bool GetInAddr(struct in_addr* pipv4Addr) const;
8080
uint32_t GetNetClass() const;
8181

82+
//! For IPv4, mapped IPv4, SIIT translated IPv4, Teredo, 6to4 tunneled addresses, return the relevant IPv4 address as a uint32.
83+
uint32_t GetLinkedIPv4() const;
84+
//! Whether this address has a linked IPv4 address (see GetLinkedIPv4()).
85+
bool HasLinkedIPv4() const;
86+
8287
// The AS on the BGP path to the node we use to diversify
8388
// peers in AddrMan bucketing based on the AS infrastructure.
8489
// The ip->AS mapping depends on how asmap is constructed.

src/util/asmap.cpp

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,14 @@
88

99
namespace {
1010

11-
uint32_t DecodeBits(std::vector<bool>::const_iterator& bitpos, uint8_t minval, const std::vector<uint8_t> &bit_sizes)
11+
uint32_t DecodeBits(std::vector<bool>::const_iterator& bitpos, const std::vector<bool>::const_iterator& endpos, uint8_t minval, const std::vector<uint8_t> &bit_sizes)
1212
{
1313
uint32_t val = minval;
1414
bool bit;
1515
for (std::vector<uint8_t>::const_iterator bit_sizes_it = bit_sizes.begin();
1616
bit_sizes_it != bit_sizes.end(); ++bit_sizes_it) {
1717
if (bit_sizes_it + 1 != bit_sizes.end()) {
18+
if (bitpos == endpos) break;
1819
bit = *bitpos;
1920
bitpos++;
2021
} else {
@@ -24,6 +25,7 @@ uint32_t DecodeBits(std::vector<bool>::const_iterator& bitpos, uint8_t minval, c
2425
val += (1 << *bit_sizes_it);
2526
} else {
2627
for (int b = 0; b < *bit_sizes_it; b++) {
28+
if (bitpos == endpos) break;
2729
bit = *bitpos;
2830
bitpos++;
2931
val += bit << (*bit_sizes_it - 1 - b);
@@ -35,63 +37,67 @@ uint32_t DecodeBits(std::vector<bool>::const_iterator& bitpos, uint8_t minval, c
3537
}
3638

3739
const std::vector<uint8_t> TYPE_BIT_SIZES{0, 0, 1};
38-
uint32_t DecodeType(std::vector<bool>::const_iterator& bitpos)
40+
uint32_t DecodeType(std::vector<bool>::const_iterator& bitpos, const std::vector<bool>::const_iterator& endpos)
3941
{
40-
return DecodeBits(bitpos, 0, TYPE_BIT_SIZES);
42+
return DecodeBits(bitpos, endpos, 0, TYPE_BIT_SIZES);
4143
}
4244

4345
const std::vector<uint8_t> ASN_BIT_SIZES{15, 16, 17, 18, 19, 20, 21, 22, 23, 24};
44-
uint32_t DecodeASN(std::vector<bool>::const_iterator& bitpos)
46+
uint32_t DecodeASN(std::vector<bool>::const_iterator& bitpos, const std::vector<bool>::const_iterator& endpos)
4547
{
46-
return DecodeBits(bitpos, 1, ASN_BIT_SIZES);
48+
return DecodeBits(bitpos, endpos, 1, ASN_BIT_SIZES);
4749
}
4850

4951

5052
const std::vector<uint8_t> MATCH_BIT_SIZES{1, 2, 3, 4, 5, 6, 7, 8};
51-
uint32_t DecodeMatch(std::vector<bool>::const_iterator& bitpos)
53+
uint32_t DecodeMatch(std::vector<bool>::const_iterator& bitpos, const std::vector<bool>::const_iterator& endpos)
5254
{
53-
return DecodeBits(bitpos, 2, MATCH_BIT_SIZES);
55+
return DecodeBits(bitpos, endpos, 2, MATCH_BIT_SIZES);
5456
}
5557

5658

5759
const std::vector<uint8_t> JUMP_BIT_SIZES{5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30};
58-
uint32_t DecodeJump(std::vector<bool>::const_iterator& bitpos)
60+
uint32_t DecodeJump(std::vector<bool>::const_iterator& bitpos, const std::vector<bool>::const_iterator& endpos)
5961
{
60-
return DecodeBits(bitpos, 17, JUMP_BIT_SIZES);
62+
return DecodeBits(bitpos, endpos, 17, JUMP_BIT_SIZES);
6163
}
6264

6365
}
6466

6567
uint32_t Interpret(const std::vector<bool> &asmap, const std::vector<bool> &ip)
6668
{
6769
std::vector<bool>::const_iterator pos = asmap.begin();
70+
const std::vector<bool>::const_iterator endpos = asmap.end();
6871
uint8_t bits = ip.size();
69-
uint8_t default_asn = 0;
72+
uint32_t default_asn = 0;
7073
uint32_t opcode, jump, match, matchlen;
71-
while (1) {
72-
assert(pos != asmap.end());
73-
opcode = DecodeType(pos);
74+
while (pos != endpos) {
75+
opcode = DecodeType(pos, endpos);
7476
if (opcode == 0) {
75-
return DecodeASN(pos);
77+
return DecodeASN(pos, endpos);
7678
} else if (opcode == 1) {
77-
jump = DecodeJump(pos);
79+
jump = DecodeJump(pos, endpos);
80+
if (bits == 0) break;
7881
if (ip[ip.size() - bits]) {
82+
if (jump >= endpos - pos) break;
7983
pos += jump;
8084
}
8185
bits--;
8286
} else if (opcode == 2) {
83-
match = DecodeMatch(pos);
87+
match = DecodeMatch(pos, endpos);
8488
matchlen = CountBits(match) - 1;
8589
for (uint32_t bit = 0; bit < matchlen; bit++) {
90+
if (bits == 0) break;
8691
if ((ip[ip.size() - bits]) != ((match >> (matchlen - 1 - bit)) & 1)) {
8792
return default_asn;
8893
}
8994
bits--;
9095
}
9196
} else if (opcode == 3) {
92-
default_asn = DecodeASN(pos);
97+
default_asn = DecodeASN(pos, endpos);
9398
} else {
94-
assert(0);
99+
break;
95100
}
96101
}
102+
return 0; // 0 is not a valid ASN
97103
}

0 commit comments

Comments
 (0)