Merge #19534: net: save the network type explicitly in CNetAddr

bcfebb6d5511ad4c156868bc799831ace628a225 net: save the network type explicitly in CNetAddr (Vasil Dimov)
100c64a95b518a6a19241aec4058b866a8872d9b net: document `enum Network` (Vasil Dimov)

Pull request description:

  (chopped off from https://github.com/bitcoin/bitcoin/pull/19031 to ease review)

  Before this change, we would analyze the contents of `CNetAddr::ip[16]`
  in order to tell which type is an address. Change this by introducing a
  new member `CNetAddr::m_net` that explicitly tells the type of the
  address.

  This is necessary because in BIP155 we will not be able to tell the
  address type by just looking at its raw representation (e.g. both TORv3
  and I2P are "seemingly random" 32 bytes).

  As a side effect of this change we no longer need to store IPv4
  addresses encoded as IPv6 addresses - we can store them in proper 4
  bytes (will be done in a separate commit). Also the code gets
  somewhat simplified - instead of
  `memcmp(ip, pchIPv4, sizeof(pchIPv4)) == 0` we can use
  `m_net == NET_IPV4`.

ACKs for top commit:
  troygiorshev:
    reACK bcfebb6d5511ad4c156868bc799831ace628a225 via `git range-diff master 64897c5 bcfebb6`
  jonatack:
    re-ACK bcfebb6 per `git diff 662bb25 bcfebb6`, code review, debug build/tests clean, ran bitcoind.
  laanwj:
    Code review ACK bcfebb6d5511ad4c156868bc799831ace628a225

Tree-SHA512: 9347e2a50feac617a994bfb46a8f77e31c236bde882e4fd4f03eea4766cd5110216f5f3d24dee91d25218bab7f8bb6e1d2d6212a44db9e34594299fd6ff7606b
Signed-off-by: pasta <pasta@dashboost.org>

# Conflicts:
#	src/netaddress.cpp
#	src/netaddress.h
This commit is contained in:
Wladimir J. van der Laan 2020-07-29 13:31:10 +02:00 committed by pasta
parent 3af7c994db
commit 888f36abe7
No known key found for this signature in database
GPG Key ID: 52527BEDABE87984
3 changed files with 111 additions and 43 deletions

View File

@ -24,19 +24,35 @@ CNetAddr::CNetAddr()
void CNetAddr::SetIP(const CNetAddr& ipIn) void CNetAddr::SetIP(const CNetAddr& ipIn)
{ {
m_net = ipIn.m_net;
memcpy(ip, ipIn.ip, sizeof(ip)); memcpy(ip, ipIn.ip, sizeof(ip));
} }
void CNetAddr::SetLegacyIPv6(const uint8_t ipv6[16])
{
if (memcmp(ipv6, pchIPv4, sizeof(pchIPv4)) == 0) {
m_net = NET_IPV4;
} else if (memcmp(ipv6, pchOnionCat, sizeof(pchOnionCat)) == 0) {
m_net = NET_ONION;
} else if (memcmp(ipv6, g_internal_prefix, sizeof(g_internal_prefix)) == 0) {
m_net = NET_INTERNAL;
} else {
m_net = NET_IPV6;
}
memcpy(ip, ipv6, 16);
}
void CNetAddr::SetRaw(Network network, const uint8_t *ip_in) void CNetAddr::SetRaw(Network network, const uint8_t *ip_in)
{ {
switch(network) switch(network)
{ {
case NET_IPV4: case NET_IPV4:
m_net = NET_IPV4;
memcpy(ip, pchIPv4, 12); memcpy(ip, pchIPv4, 12);
memcpy(ip+12, ip_in, 4); memcpy(ip+12, ip_in, 4);
break; break;
case NET_IPV6: case NET_IPV6:
memcpy(ip, ip_in, 16); SetLegacyIPv6(ip_in);
break; break;
default: default:
assert(!"invalid network"); assert(!"invalid network");
@ -48,6 +64,7 @@ bool CNetAddr::SetInternal(const std::string &name)
if (name.empty()) { if (name.empty()) {
return false; return false;
} }
m_net = NET_INTERNAL;
unsigned char hash[32] = {}; unsigned char hash[32] = {};
CSHA256().Write((const unsigned char*)name.data(), name.size()).Finalize(hash); CSHA256().Write((const unsigned char*)name.data(), name.size()).Finalize(hash);
memcpy(ip, g_internal_prefix, sizeof(g_internal_prefix)); memcpy(ip, g_internal_prefix, sizeof(g_internal_prefix));
@ -61,6 +78,7 @@ bool CNetAddr::SetSpecial(const std::string &strName)
std::vector<unsigned char> vchAddr = DecodeBase32(strName.substr(0, strName.size() - 6).c_str()); std::vector<unsigned char> vchAddr = DecodeBase32(strName.substr(0, strName.size() - 6).c_str());
if (vchAddr.size() != 16-sizeof(pchOnionCat)) if (vchAddr.size() != 16-sizeof(pchOnionCat))
return false; return false;
m_net = NET_ONION;
memcpy(ip, pchOnionCat, sizeof(pchOnionCat)); memcpy(ip, pchOnionCat, sizeof(pchOnionCat));
for (unsigned int i=0; i<16-sizeof(pchOnionCat); i++) for (unsigned int i=0; i<16-sizeof(pchOnionCat); i++)
ip[i + sizeof(pchOnionCat)] = vchAddr[i]; ip[i + sizeof(pchOnionCat)] = vchAddr[i];
@ -95,15 +113,9 @@ bool CNetAddr::IsBindAny() const
return true; return true;
} }
bool CNetAddr::IsIPv4() const bool CNetAddr::IsIPv4() const { return m_net == NET_IPV4; }
{
return (memcmp(ip, pchIPv4, sizeof(pchIPv4)) == 0);
}
bool CNetAddr::IsIPv6() const bool CNetAddr::IsIPv6() const { return m_net == NET_IPV6; }
{
return (!IsIPv4() && !IsTor() && !IsInternal());
}
bool CNetAddr::IsRFC1918() const bool CNetAddr::IsRFC1918() const
{ {
@ -137,56 +149,57 @@ bool CNetAddr::IsRFC5737() const
bool CNetAddr::IsRFC3849() const bool CNetAddr::IsRFC3849() const
{ {
return GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x0D && GetByte(12) == 0xB8; return IsIPv6() && GetByte(15) == 0x20 && GetByte(14) == 0x01 &&
GetByte(13) == 0x0D && GetByte(12) == 0xB8;
} }
bool CNetAddr::IsRFC3964() const bool CNetAddr::IsRFC3964() const
{ {
return (GetByte(15) == 0x20 && GetByte(14) == 0x02); return IsIPv6() && GetByte(15) == 0x20 && GetByte(14) == 0x02;
} }
bool CNetAddr::IsRFC6052() const bool CNetAddr::IsRFC6052() const
{ {
static const unsigned char pchRFC6052[] = {0,0x64,0xFF,0x9B,0,0,0,0,0,0,0,0}; static const unsigned char pchRFC6052[] = {0,0x64,0xFF,0x9B,0,0,0,0,0,0,0,0};
return (memcmp(ip, pchRFC6052, sizeof(pchRFC6052)) == 0); return IsIPv6() && memcmp(ip, pchRFC6052, sizeof(pchRFC6052)) == 0;
} }
bool CNetAddr::IsRFC4380() const bool CNetAddr::IsRFC4380() const
{ {
return (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0 && GetByte(12) == 0); return IsIPv6() && GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0 &&
GetByte(12) == 0;
} }
bool CNetAddr::IsRFC4862() const bool CNetAddr::IsRFC4862() const
{ {
static const unsigned char pchRFC4862[] = {0xFE,0x80,0,0,0,0,0,0}; static const unsigned char pchRFC4862[] = {0xFE,0x80,0,0,0,0,0,0};
return (memcmp(ip, pchRFC4862, sizeof(pchRFC4862)) == 0); return IsIPv6() && memcmp(ip, pchRFC4862, sizeof(pchRFC4862)) == 0;
} }
bool CNetAddr::IsRFC4193() const bool CNetAddr::IsRFC4193() const
{ {
return ((GetByte(15) & 0xFE) == 0xFC); return IsIPv6() && (GetByte(15) & 0xFE) == 0xFC;
} }
bool CNetAddr::IsRFC6145() const bool CNetAddr::IsRFC6145() const
{ {
static const unsigned char pchRFC6145[] = {0,0,0,0,0,0,0,0,0xFF,0xFF,0,0}; static const unsigned char pchRFC6145[] = {0,0,0,0,0,0,0,0,0xFF,0xFF,0,0};
return (memcmp(ip, pchRFC6145, sizeof(pchRFC6145)) == 0); return IsIPv6() && memcmp(ip, pchRFC6145, sizeof(pchRFC6145)) == 0;
} }
bool CNetAddr::IsRFC4843() const bool CNetAddr::IsRFC4843() const
{ {
return (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x00 && (GetByte(12) & 0xF0) == 0x10); return IsIPv6() && GetByte(15) == 0x20 && GetByte(14) == 0x01 &&
GetByte(13) == 0x00 && (GetByte(12) & 0xF0) == 0x10;
} }
bool CNetAddr::IsRFC7343() const bool CNetAddr::IsRFC7343() const
{ {
return (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x00 && (GetByte(12) & 0xF0) == 0x20); return IsIPv6() && GetByte(15) == 0x20 && GetByte(14) == 0x01 &&
GetByte(13) == 0x00 && (GetByte(12) & 0xF0) == 0x20;
} }
bool CNetAddr::IsTor() const bool CNetAddr::IsTor() const { return m_net == NET_ONION; }
{
return (memcmp(ip, pchOnionCat, sizeof(pchOnionCat)) == 0);
}
bool CNetAddr::IsLocal() const bool CNetAddr::IsLocal() const
{ {
@ -196,7 +209,7 @@ bool CNetAddr::IsLocal() const
// IPv6 loopback (::1/128) // IPv6 loopback (::1/128)
static const unsigned char pchLocal[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}; static const unsigned char pchLocal[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1};
if (memcmp(ip, pchLocal, 16) == 0) if (IsIPv6() && memcmp(ip, pchLocal, 16) == 0)
return true; return true;
return false; return false;
@ -210,12 +223,12 @@ bool CNetAddr::IsValid() const
// header20 vectorlen3 addr26 addr26 addr26 header20 vectorlen3 addr26 addr26 addr26... // header20 vectorlen3 addr26 addr26 addr26 header20 vectorlen3 addr26 addr26 addr26...
// so if the first length field is garbled, it reads the second batch // so if the first length field is garbled, it reads the second batch
// of addr misaligned by 3 bytes. // of addr misaligned by 3 bytes.
if (memcmp(ip, pchIPv4+3, sizeof(pchIPv4)-3) == 0) if (IsIPv6() && memcmp(ip, pchIPv4+3, sizeof(pchIPv4)-3) == 0)
return false; return false;
// unspecified IPv6 address (::/128) // unspecified IPv6 address (::/128)
unsigned char ipNone6[16] = {}; unsigned char ipNone6[16] = {};
if (memcmp(ip, ipNone6, 16) == 0) if (IsIPv6() && memcmp(ip, ipNone6, 16) == 0)
return false; return false;
// documentation IPv6 address // documentation IPv6 address
@ -252,7 +265,7 @@ bool CNetAddr::IsRoutable() const
bool CNetAddr::IsInternal() const bool CNetAddr::IsInternal() const
{ {
return memcmp(ip, g_internal_prefix, sizeof(g_internal_prefix)) == 0; return m_net == NET_INTERNAL;
} }
enum Network CNetAddr::GetNetwork() const enum Network CNetAddr::GetNetwork() const
@ -263,13 +276,7 @@ enum Network CNetAddr::GetNetwork() const
if (!IsRoutable()) if (!IsRoutable())
return NET_UNROUTABLE; return NET_UNROUTABLE;
if (IsIPv4()) return m_net;
return NET_IPV4;
if (IsTor())
return NET_ONION;
return NET_IPV6;
} }
std::string CNetAddr::ToStringIP(bool fUseGetnameinfo) const std::string CNetAddr::ToStringIP(bool fUseGetnameinfo) const
@ -306,12 +313,12 @@ std::string CNetAddr::ToString() const
bool operator==(const CNetAddr& a, const CNetAddr& b) bool operator==(const CNetAddr& a, const CNetAddr& b)
{ {
return (memcmp(a.ip, b.ip, 16) == 0); return a.m_net == b.m_net && memcmp(a.ip, b.ip, 16) == 0;
} }
bool operator<(const CNetAddr& a, const CNetAddr& b) bool operator<(const CNetAddr& a, const CNetAddr& b)
{ {
return (memcmp(a.ip, b.ip, 16) < 0); return a.m_net < b.m_net || (a.m_net == b.m_net && memcmp(a.ip, b.ip, 16) < 0);
} }
bool CNetAddr::GetInAddr(struct in_addr* pipv4Addr) const bool CNetAddr::GetInAddr(struct in_addr* pipv4Addr) const
@ -661,7 +668,7 @@ CSubNet::CSubNet(const CNetAddr &addr):
bool CSubNet::Match(const CNetAddr &addr) const bool CSubNet::Match(const CNetAddr &addr) const
{ {
if (!valid || !addr.IsValid()) if (!valid || !addr.IsValid() || network.m_net != addr.m_net)
return false; return false;
for(int x=0; x<16; ++x) for(int x=0; x<16; ++x)
if ((addr.ip[x] & netmask[x]) != network.ip[x]) if ((addr.ip[x] & netmask[x]) != network.ip[x])

View File

@ -19,21 +19,50 @@
extern bool fAllowPrivateNet; extern bool fAllowPrivateNet;
/**
* A network type.
* @note An address may belong to more than one network, for example `10.0.0.1`
* belongs to both `NET_UNROUTABLE` and `NET_IPV4`.
* Keep these sequential starting from 0 and `NET_MAX` as the last entry.
* We have loops like `for (int i = 0; i < NET_MAX; i++)` that expect to iterate
* over all enum values and also `GetExtNetwork()` "extends" this enum by
* introducing standalone constants starting from `NET_MAX`.
*/
enum Network enum Network
{ {
/// Addresses from these networks are not publicly routable on the global Internet.
NET_UNROUTABLE = 0, NET_UNROUTABLE = 0,
/// IPv4
NET_IPV4, NET_IPV4,
/// IPv6
NET_IPV6, NET_IPV6,
/// TORv2
NET_ONION, NET_ONION,
/// A set of dummy addresses that map a name to an IPv6 address. These
/// addresses belong to RFC4193's fc00::/7 subnet (unique-local addresses).
/// We use them to map a string or FQDN to an IPv6 address in CAddrMan to
/// keep track of which DNS seeds were used.
NET_INTERNAL, NET_INTERNAL,
/// Dummy value to indicate the number of NET_* constants.
NET_MAX, NET_MAX,
}; };
/** IP address (IPv6, or IPv4 using mapped IPv6 range (::FFFF:0:0/96)) */ /**
* Network address.
*/
class CNetAddr class CNetAddr
{ {
protected: protected:
/**
* Network to which this address belongs.
*/
Network m_net{NET_IPV6};
unsigned char ip[16]; // in network byte order unsigned char ip[16]; // in network byte order
uint32_t scopeId{0}; // for scoped/link-local ipv6 addresses uint32_t scopeId{0}; // for scoped/link-local ipv6 addresses
@ -42,6 +71,14 @@ class CNetAddr
explicit CNetAddr(const struct in_addr& ipv4Addr); explicit CNetAddr(const struct in_addr& ipv4Addr);
void SetIP(const CNetAddr& ip); void SetIP(const CNetAddr& ip);
/**
* Set from a legacy IPv6 address.
* Legacy IPv6 address may be a normal IPv6 address, or another address
* (e.g. IPv4) disguised as IPv6. This encoding is used in the legacy
* `addr` encoding.
*/
void SetLegacyIPv6(const uint8_t ipv6[16]);
private: private:
/** /**
* Set raw IPv4 or IPv6 address (in network byte order) * Set raw IPv4 or IPv6 address (in network byte order)
@ -96,11 +133,26 @@ class CNetAddr
friend bool operator!=(const CNetAddr& a, const CNetAddr& b) { return !(a == b); } friend bool operator!=(const CNetAddr& a, const CNetAddr& b) { return !(a == b); }
friend bool operator<(const CNetAddr& a, const CNetAddr& b); friend bool operator<(const CNetAddr& a, const CNetAddr& b);
ADD_SERIALIZE_METHODS; /**
* Serialize to a stream.
*/
template <typename Stream>
void Serialize(Stream& s) const
{
s << ip;
}
template <typename Stream, typename Operation> /**
inline void SerializationOp(Stream& s, Operation ser_action) { * Unserialize from a stream.
READWRITE(ip); */
template <typename Stream>
void Unserialize(Stream& s)
{
unsigned char ip_temp[sizeof(ip)];
s >> ip_temp;
// Use SetLegacyIPv6() so that m_net is set correctly. For example
// ::FFFF:0102:0304 should be set as m_net=NET_IPV4 (1.2.3.4).
SetLegacyIPv6(ip_temp);
} }
friend class CSubNet; friend class CSubNet;

View File

@ -136,6 +136,14 @@ BOOST_AUTO_TEST_CASE(onioncat_test)
} }
BOOST_AUTO_TEST_CASE(embedded_test)
{
CNetAddr addr1(ResolveIP("1.2.3.4"));
CNetAddr addr2(ResolveIP("::FFFF:0102:0304"));
BOOST_CHECK(addr2.IsIPv4());
BOOST_CHECK_EQUAL(addr1.ToString(), addr2.ToString());
}
BOOST_AUTO_TEST_CASE(subnet_test) BOOST_AUTO_TEST_CASE(subnet_test)
{ {
@ -156,12 +164,13 @@ BOOST_AUTO_TEST_CASE(subnet_test)
BOOST_CHECK(ResolveSubNet("1.2.2.1/24").Match(ResolveIP("1.2.2.4"))); BOOST_CHECK(ResolveSubNet("1.2.2.1/24").Match(ResolveIP("1.2.2.4")));
BOOST_CHECK(ResolveSubNet("1.2.2.110/31").Match(ResolveIP("1.2.2.111"))); BOOST_CHECK(ResolveSubNet("1.2.2.110/31").Match(ResolveIP("1.2.2.111")));
BOOST_CHECK(ResolveSubNet("1.2.2.20/26").Match(ResolveIP("1.2.2.63"))); BOOST_CHECK(ResolveSubNet("1.2.2.20/26").Match(ResolveIP("1.2.2.63")));
// All-Matching IPv6 Matches arbitrary IPv4 and IPv6 // All-Matching IPv6 Matches arbitrary IPv6
BOOST_CHECK(ResolveSubNet("::/0").Match(ResolveIP("1:2:3:4:5:6:7:1234"))); BOOST_CHECK(ResolveSubNet("::/0").Match(ResolveIP("1:2:3:4:5:6:7:1234")));
// But not `::` or `0.0.0.0` because they are considered invalid addresses // But not `::` or `0.0.0.0` because they are considered invalid addresses
BOOST_CHECK(!ResolveSubNet("::/0").Match(ResolveIP("::"))); BOOST_CHECK(!ResolveSubNet("::/0").Match(ResolveIP("::")));
BOOST_CHECK(!ResolveSubNet("::/0").Match(ResolveIP("0.0.0.0"))); BOOST_CHECK(!ResolveSubNet("::/0").Match(ResolveIP("0.0.0.0")));
BOOST_CHECK(ResolveSubNet("::/0").Match(ResolveIP("1.2.3.4"))); // Addresses from one network (IPv4) don't belong to subnets of another network (IPv6)
BOOST_CHECK(!ResolveSubNet("::/0").Match(ResolveIP("1.2.3.4")));
// All-Matching IPv4 does not Match IPv6 // All-Matching IPv4 does not Match IPv6
BOOST_CHECK(!ResolveSubNet("0.0.0.0/0").Match(ResolveIP("1:2:3:4:5:6:7:1234"))); BOOST_CHECK(!ResolveSubNet("0.0.0.0/0").Match(ResolveIP("1:2:3:4:5:6:7:1234")));
// Invalid subnets Match nothing (not even invalid addresses) // Invalid subnets Match nothing (not even invalid addresses)