Skip to content

Commit 1b978a7

Browse files
committed
[netgroupman] Move GetMappedAS() and GetGroup() logic to NetGroupManager
Reviewer hint: use: `git diff --color-moved=dimmed-zebra --color-moved-ws=ignore-all-space`
1 parent ddb4101 commit 1b978a7

File tree

5 files changed

+107
-118
lines changed

5 files changed

+107
-118
lines changed

src/addrman.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -610,7 +610,7 @@ bool AddrManImpl::AddSingle(const CAddress& addr, const CNetAddr& source, int64_
610610
pinfo->nRefCount++;
611611
vvNew[nUBucket][nUBucketPos] = nId;
612612
LogPrint(BCLog::ADDRMAN, "Added %s mapped to AS%i to new[%i][%i]\n",
613-
addr.ToString(), addr.GetMappedAS(m_netgroupman.GetAsmap()), nUBucket, nUBucketPos);
613+
addr.ToString(), m_netgroupman.GetMappedAS(addr), nUBucket, nUBucketPos);
614614
} else {
615615
if (pinfo->nRefCount == 0) {
616616
Delete(nId);
@@ -669,7 +669,7 @@ bool AddrManImpl::Good_(const CService& addr, bool test_before_evict, int64_t nT
669669
// move nId to the tried tables
670670
MakeTried(info, nId);
671671
LogPrint(BCLog::ADDRMAN, "Moved %s mapped to AS%i to tried[%i][%i]\n",
672-
addr.ToString(), addr.GetMappedAS(m_netgroupman.GetAsmap()), tried_bucket, tried_bucket_pos);
672+
addr.ToString(), m_netgroupman.GetMappedAS(addr), tried_bucket, tried_bucket_pos);
673673
return true;
674674
}
675675
}

src/netaddress.cpp

Lines changed: 0 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
#include <hash.h>
1111
#include <prevector.h>
1212
#include <tinyformat.h>
13-
#include <util/asmap.h>
1413
#include <util/strencodings.h>
1514
#include <util/string.h>
1615

@@ -722,113 +721,6 @@ Network CNetAddr::GetNetClass() const
722721
return m_net;
723722
}
724723

725-
uint32_t CNetAddr::GetMappedAS(const std::vector<bool> &m_asmap) const {
726-
const CNetAddr& address = *this;
727-
uint32_t net_class = address.GetNetClass();
728-
if (m_asmap.size() == 0 || (net_class != NET_IPV4 && net_class != NET_IPV6)) {
729-
return 0; // Indicates not found, safe because AS0 is reserved per RFC7607.
730-
}
731-
std::vector<bool> ip_bits(128);
732-
if (address.HasLinkedIPv4()) {
733-
// For lookup, treat as if it was just an IPv4 address (IPV4_IN_IPV6_PREFIX + IPv4 bits)
734-
for (int8_t byte_i = 0; byte_i < 12; ++byte_i) {
735-
for (uint8_t bit_i = 0; bit_i < 8; ++bit_i) {
736-
ip_bits[byte_i * 8 + bit_i] = (IPV4_IN_IPV6_PREFIX[byte_i] >> (7 - bit_i)) & 1;
737-
}
738-
}
739-
uint32_t ipv4 = address.GetLinkedIPv4();
740-
for (int i = 0; i < 32; ++i) {
741-
ip_bits[96 + i] = (ipv4 >> (31 - i)) & 1;
742-
}
743-
} else {
744-
// Use all 128 bits of the IPv6 address otherwise
745-
assert(address.IsIPv6());
746-
auto addr_bytes = address.GetAddrBytes();
747-
for (int8_t byte_i = 0; byte_i < 16; ++byte_i) {
748-
uint8_t cur_byte = addr_bytes[byte_i];
749-
for (uint8_t bit_i = 0; bit_i < 8; ++bit_i) {
750-
ip_bits[byte_i * 8 + bit_i] = (cur_byte >> (7 - bit_i)) & 1;
751-
}
752-
}
753-
}
754-
uint32_t mapped_as = Interpret(m_asmap, ip_bits);
755-
return mapped_as;
756-
}
757-
758-
/**
759-
* Get the canonical identifier of our network group
760-
*
761-
* The groups are assigned in a way where it should be costly for an attacker to
762-
* obtain addresses with many different group identifiers, even if it is cheap
763-
* to obtain addresses with the same identifier.
764-
*
765-
* @note No two connections will be attempted to addresses with the same network
766-
* group.
767-
*/
768-
std::vector<unsigned char> CNetAddr::GetGroup(const std::vector<bool> &m_asmap) const
769-
{
770-
const CNetAddr& address = *this;
771-
std::vector<unsigned char> vchRet;
772-
// If non-empty asmap is supplied and the address is IPv4/IPv6,
773-
// return ASN to be used for bucketing.
774-
uint32_t asn = GetMappedAS(m_asmap);
775-
if (asn != 0) { // Either asmap was empty, or address has non-asmappable net class (e.g. TOR).
776-
vchRet.push_back(NET_IPV6); // IPv4 and IPv6 with same ASN should be in the same bucket
777-
for (int i = 0; i < 4; i++) {
778-
vchRet.push_back((asn >> (8 * i)) & 0xFF);
779-
}
780-
return vchRet;
781-
}
782-
783-
vchRet.push_back(address.GetNetClass());
784-
int nStartByte{0};
785-
int nBits{0};
786-
787-
if (address.IsLocal()) {
788-
// all local addresses belong to the same group
789-
} else if (address.IsInternal()) {
790-
// All internal-usage addresses get their own group.
791-
// Skip over the INTERNAL_IN_IPV6_PREFIX returned by CAddress::GetAddrBytes().
792-
nStartByte = INTERNAL_IN_IPV6_PREFIX.size();
793-
nBits = ADDR_INTERNAL_SIZE * 8;
794-
} else if (!address.IsRoutable()) {
795-
// all other unroutable addresses belong to the same group
796-
} else if (address.HasLinkedIPv4()) {
797-
// IPv4 addresses (and mapped IPv4 addresses) use /16 groups
798-
uint32_t ipv4 = address.GetLinkedIPv4();
799-
vchRet.push_back((ipv4 >> 24) & 0xFF);
800-
vchRet.push_back((ipv4 >> 16) & 0xFF);
801-
return vchRet;
802-
} else if (address.IsTor() || address.IsI2P()) {
803-
nBits = 4;
804-
} else if (address.IsCJDNS()) {
805-
// Treat in the same way as Tor and I2P because the address in all of
806-
// them is "random" bytes (derived from a public key). However in CJDNS
807-
// the first byte is a constant 0xfc, so the random bytes come after it.
808-
// Thus skip the constant 8 bits at the start.
809-
nBits = 12;
810-
} else if (address.IsHeNet()) {
811-
// for he.net, use /36 groups
812-
nBits = 36;
813-
} else {
814-
// for the rest of the IPv6 network, use /32 groups
815-
nBits = 32;
816-
}
817-
818-
// Push our address onto vchRet.
819-
auto addr_bytes = address.GetAddrBytes();
820-
const size_t num_bytes = nBits / 8;
821-
vchRet.insert(vchRet.end(), addr_bytes.begin() + nStartByte, addr_bytes.begin() + nStartByte + num_bytes);
822-
nBits %= 8;
823-
// ...for the last byte, push nBits and for the rest of the byte push 1's
824-
if (nBits > 0) {
825-
assert(num_bytes < addr_bytes.size());
826-
vchRet.push_back(addr_bytes[num_bytes] | ((1 << (8 - nBits)) - 1));
827-
}
828-
829-
return vchRet;
830-
}
831-
832724
std::vector<unsigned char> CNetAddr::GetAddrBytes() const
833725
{
834726
if (IsAddrV1Compatible()) {

src/netaddress.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -202,12 +202,6 @@ class CNetAddr
202202
//! Whether this address has a linked IPv4 address (see GetLinkedIPv4()).
203203
bool HasLinkedIPv4() const;
204204

205-
// The AS on the BGP path to the node we use to diversify
206-
// peers in AddrMan bucketing based on the AS infrastructure.
207-
// The ip->AS mapping depends on how asmap is constructed.
208-
uint32_t GetMappedAS(const std::vector<bool>& asmap) const;
209-
210-
std::vector<unsigned char> GetGroup(const std::vector<bool>& asmap) const;
211205
std::vector<unsigned char> GetAddrBytes() const;
212206
int GetReachabilityFrom(const CNetAddr* paddrPartner = nullptr) const;
213207

src/netgroup.cpp

Lines changed: 90 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,100 @@
44

55
#include <netgroup.h>
66

7+
#include <util/asmap.h>
8+
79
std::vector<unsigned char> NetGroupManager::GetGroup(const CNetAddr& address) const
810
{
9-
return address.GetGroup(m_asmap);
11+
std::vector<unsigned char> vchRet;
12+
// If non-empty asmap is supplied and the address is IPv4/IPv6,
13+
// return ASN to be used for bucketing.
14+
uint32_t asn = GetMappedAS(address);
15+
if (asn != 0) { // Either asmap was empty, or address has non-asmappable net class (e.g. TOR).
16+
vchRet.push_back(NET_IPV6); // IPv4 and IPv6 with same ASN should be in the same bucket
17+
for (int i = 0; i < 4; i++) {
18+
vchRet.push_back((asn >> (8 * i)) & 0xFF);
19+
}
20+
return vchRet;
21+
}
22+
23+
vchRet.push_back(address.GetNetClass());
24+
int nStartByte{0};
25+
int nBits{0};
26+
27+
if (address.IsLocal()) {
28+
// all local addresses belong to the same group
29+
} else if (address.IsInternal()) {
30+
// All internal-usage addresses get their own group.
31+
// Skip over the INTERNAL_IN_IPV6_PREFIX returned by CAddress::GetAddrBytes().
32+
nStartByte = INTERNAL_IN_IPV6_PREFIX.size();
33+
nBits = ADDR_INTERNAL_SIZE * 8;
34+
} else if (!address.IsRoutable()) {
35+
// all other unroutable addresses belong to the same group
36+
} else if (address.HasLinkedIPv4()) {
37+
// IPv4 addresses (and mapped IPv4 addresses) use /16 groups
38+
uint32_t ipv4 = address.GetLinkedIPv4();
39+
vchRet.push_back((ipv4 >> 24) & 0xFF);
40+
vchRet.push_back((ipv4 >> 16) & 0xFF);
41+
return vchRet;
42+
} else if (address.IsTor() || address.IsI2P()) {
43+
nBits = 4;
44+
} else if (address.IsCJDNS()) {
45+
// Treat in the same way as Tor and I2P because the address in all of
46+
// them is "random" bytes (derived from a public key). However in CJDNS
47+
// the first byte is a constant 0xfc, so the random bytes come after it.
48+
// Thus skip the constant 8 bits at the start.
49+
nBits = 12;
50+
} else if (address.IsHeNet()) {
51+
// for he.net, use /36 groups
52+
nBits = 36;
53+
} else {
54+
// for the rest of the IPv6 network, use /32 groups
55+
nBits = 32;
56+
}
57+
58+
// Push our address onto vchRet.
59+
auto addr_bytes = address.GetAddrBytes();
60+
const size_t num_bytes = nBits / 8;
61+
vchRet.insert(vchRet.end(), addr_bytes.begin() + nStartByte, addr_bytes.begin() + nStartByte + num_bytes);
62+
nBits %= 8;
63+
// ...for the last byte, push nBits and for the rest of the byte push 1's
64+
if (nBits > 0) {
65+
assert(num_bytes < addr_bytes.size());
66+
vchRet.push_back(addr_bytes[num_bytes] | ((1 << (8 - nBits)) - 1));
67+
}
68+
69+
return vchRet;
1070
}
1171

1272
uint32_t NetGroupManager::GetMappedAS(const CNetAddr& address) const
1373
{
14-
return address.GetMappedAS(m_asmap);
74+
uint32_t net_class = address.GetNetClass();
75+
if (m_asmap.size() == 0 || (net_class != NET_IPV4 && net_class != NET_IPV6)) {
76+
return 0; // Indicates not found, safe because AS0 is reserved per RFC7607.
77+
}
78+
std::vector<bool> ip_bits(128);
79+
if (address.HasLinkedIPv4()) {
80+
// For lookup, treat as if it was just an IPv4 address (IPV4_IN_IPV6_PREFIX + IPv4 bits)
81+
for (int8_t byte_i = 0; byte_i < 12; ++byte_i) {
82+
for (uint8_t bit_i = 0; bit_i < 8; ++bit_i) {
83+
ip_bits[byte_i * 8 + bit_i] = (IPV4_IN_IPV6_PREFIX[byte_i] >> (7 - bit_i)) & 1;
84+
}
85+
}
86+
uint32_t ipv4 = address.GetLinkedIPv4();
87+
for (int i = 0; i < 32; ++i) {
88+
ip_bits[96 + i] = (ipv4 >> (31 - i)) & 1;
89+
}
90+
} else {
91+
// Use all 128 bits of the IPv6 address otherwise
92+
assert(address.IsIPv6());
93+
auto addr_bytes = address.GetAddrBytes();
94+
for (int8_t byte_i = 0; byte_i < 16; ++byte_i) {
95+
uint8_t cur_byte = addr_bytes[byte_i];
96+
for (uint8_t bit_i = 0; bit_i < 8; ++bit_i) {
97+
ip_bits[byte_i * 8 + bit_i] = (cur_byte >> (7 - bit_i)) & 1;
98+
}
99+
}
100+
}
101+
uint32_t mapped_as = Interpret(m_asmap, ip_bits);
102+
return mapped_as;
15103
}

src/netgroup.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,23 @@ class NetGroupManager {
2222
* exists, since the data is const. */
2323
const std::vector<bool>& GetAsmap() const { return m_asmap; }
2424

25+
/**
26+
* Get the canonical identifier of the network group for address.
27+
*
28+
* The groups are assigned in a way where it should be costly for an attacker to
29+
* obtain addresses with many different group identifiers, even if it is cheap
30+
* to obtain addresses with the same identifier.
31+
*
32+
* @note No two connections will be attempted to addresses with the same network
33+
* group.
34+
*/
2535
std::vector<unsigned char> GetGroup(const CNetAddr& address) const;
2636

37+
/**
38+
* Get the autonomous system on the BGP path to address.
39+
*
40+
* The ip->AS mapping depends on how asmap is constructed.
41+
*/
2742
uint32_t GetMappedAS(const CNetAddr& address) const;
2843

2944
private:

0 commit comments

Comments
 (0)