Skip to content

Commit a76ccb0

Browse files
committed
Merge #19534: net: save the network type explicitly in CNetAddr
bcfebb6 net: save the network type explicitly in CNetAddr (Vasil Dimov) 100c64a net: document `enum Network` (Vasil Dimov) Pull request description: (chopped off from bitcoin/bitcoin#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 bcfebb6 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 bcfebb6 Tree-SHA512: 9347e2a50feac617a994bfb46a8f77e31c236bde882e4fd4f03eea4766cd5110216f5f3d24dee91d25218bab7f8bb6e1d2d6212a44db9e34594299fd6ff7606b
2 parents 2f71a1e + bcfebb6 commit a76ccb0

File tree

3 files changed

+113
-40
lines changed

3 files changed

+113
-40
lines changed

src/netaddress.cpp

Lines changed: 43 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -28,19 +28,35 @@ CNetAddr::CNetAddr()
2828

2929
void CNetAddr::SetIP(const CNetAddr& ipIn)
3030
{
31+
m_net = ipIn.m_net;
3132
memcpy(ip, ipIn.ip, sizeof(ip));
3233
}
3334

35+
void CNetAddr::SetLegacyIPv6(const uint8_t ipv6[16])
36+
{
37+
if (memcmp(ipv6, pchIPv4, sizeof(pchIPv4)) == 0) {
38+
m_net = NET_IPV4;
39+
} else if (memcmp(ipv6, pchOnionCat, sizeof(pchOnionCat)) == 0) {
40+
m_net = NET_ONION;
41+
} else if (memcmp(ipv6, g_internal_prefix, sizeof(g_internal_prefix)) == 0) {
42+
m_net = NET_INTERNAL;
43+
} else {
44+
m_net = NET_IPV6;
45+
}
46+
memcpy(ip, ipv6, 16);
47+
}
48+
3449
void CNetAddr::SetRaw(Network network, const uint8_t *ip_in)
3550
{
3651
switch(network)
3752
{
3853
case NET_IPV4:
54+
m_net = NET_IPV4;
3955
memcpy(ip, pchIPv4, 12);
4056
memcpy(ip+12, ip_in, 4);
4157
break;
4258
case NET_IPV6:
43-
memcpy(ip, ip_in, 16);
59+
SetLegacyIPv6(ip_in);
4460
break;
4561
default:
4662
assert(!"invalid network");
@@ -66,6 +82,7 @@ bool CNetAddr::SetInternal(const std::string &name)
6682
if (name.empty()) {
6783
return false;
6884
}
85+
m_net = NET_INTERNAL;
6986
unsigned char hash[32] = {};
7087
CSHA256().Write((const unsigned char*)name.data(), name.size()).Finalize(hash);
7188
memcpy(ip, g_internal_prefix, sizeof(g_internal_prefix));
@@ -89,6 +106,7 @@ bool CNetAddr::SetSpecial(const std::string &strName)
89106
std::vector<unsigned char> vchAddr = DecodeBase32(strName.substr(0, strName.size() - 6).c_str());
90107
if (vchAddr.size() != 16-sizeof(pchOnionCat))
91108
return false;
109+
m_net = NET_ONION;
92110
memcpy(ip, pchOnionCat, sizeof(pchOnionCat));
93111
for (unsigned int i=0; i<16-sizeof(pchOnionCat); i++)
94112
ip[i + sizeof(pchOnionCat)] = vchAddr[i];
@@ -123,15 +141,9 @@ bool CNetAddr::IsBindAny() const
123141
return true;
124142
}
125143

126-
bool CNetAddr::IsIPv4() const
127-
{
128-
return (memcmp(ip, pchIPv4, sizeof(pchIPv4)) == 0);
129-
}
144+
bool CNetAddr::IsIPv4() const { return m_net == NET_IPV4; }
130145

131-
bool CNetAddr::IsIPv6() const
132-
{
133-
return (!IsIPv4() && !IsTor() && !IsInternal());
134-
}
146+
bool CNetAddr::IsIPv6() const { return m_net == NET_IPV6; }
135147

136148
bool CNetAddr::IsRFC1918() const
137149
{
@@ -165,50 +177,54 @@ bool CNetAddr::IsRFC5737() const
165177

166178
bool CNetAddr::IsRFC3849() const
167179
{
168-
return GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x0D && GetByte(12) == 0xB8;
180+
return IsIPv6() && GetByte(15) == 0x20 && GetByte(14) == 0x01 &&
181+
GetByte(13) == 0x0D && GetByte(12) == 0xB8;
169182
}
170183

171184
bool CNetAddr::IsRFC3964() const
172185
{
173-
return (GetByte(15) == 0x20 && GetByte(14) == 0x02);
186+
return IsIPv6() && GetByte(15) == 0x20 && GetByte(14) == 0x02;
174187
}
175188

176189
bool CNetAddr::IsRFC6052() const
177190
{
178191
static const unsigned char pchRFC6052[] = {0,0x64,0xFF,0x9B,0,0,0,0,0,0,0,0};
179-
return (memcmp(ip, pchRFC6052, sizeof(pchRFC6052)) == 0);
192+
return IsIPv6() && memcmp(ip, pchRFC6052, sizeof(pchRFC6052)) == 0;
180193
}
181194

182195
bool CNetAddr::IsRFC4380() const
183196
{
184-
return (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0 && GetByte(12) == 0);
197+
return IsIPv6() && GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0 &&
198+
GetByte(12) == 0;
185199
}
186200

187201
bool CNetAddr::IsRFC4862() const
188202
{
189203
static const unsigned char pchRFC4862[] = {0xFE,0x80,0,0,0,0,0,0};
190-
return (memcmp(ip, pchRFC4862, sizeof(pchRFC4862)) == 0);
204+
return IsIPv6() && memcmp(ip, pchRFC4862, sizeof(pchRFC4862)) == 0;
191205
}
192206

193207
bool CNetAddr::IsRFC4193() const
194208
{
195-
return ((GetByte(15) & 0xFE) == 0xFC);
209+
return IsIPv6() && (GetByte(15) & 0xFE) == 0xFC;
196210
}
197211

198212
bool CNetAddr::IsRFC6145() const
199213
{
200214
static const unsigned char pchRFC6145[] = {0,0,0,0,0,0,0,0,0xFF,0xFF,0,0};
201-
return (memcmp(ip, pchRFC6145, sizeof(pchRFC6145)) == 0);
215+
return IsIPv6() && memcmp(ip, pchRFC6145, sizeof(pchRFC6145)) == 0;
202216
}
203217

204218
bool CNetAddr::IsRFC4843() const
205219
{
206-
return (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x00 && (GetByte(12) & 0xF0) == 0x10);
220+
return IsIPv6() && GetByte(15) == 0x20 && GetByte(14) == 0x01 &&
221+
GetByte(13) == 0x00 && (GetByte(12) & 0xF0) == 0x10;
207222
}
208223

209224
bool CNetAddr::IsRFC7343() const
210225
{
211-
return (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x00 && (GetByte(12) & 0xF0) == 0x20);
226+
return IsIPv6() && GetByte(15) == 0x20 && GetByte(14) == 0x01 &&
227+
GetByte(13) == 0x00 && (GetByte(12) & 0xF0) == 0x20;
212228
}
213229

214230
bool CNetAddr::IsHeNet() const
@@ -222,10 +238,7 @@ bool CNetAddr::IsHeNet() const
222238
*
223239
* @see CNetAddr::SetSpecial(const std::string &)
224240
*/
225-
bool CNetAddr::IsTor() const
226-
{
227-
return (memcmp(ip, pchOnionCat, sizeof(pchOnionCat)) == 0);
228-
}
241+
bool CNetAddr::IsTor() const { return m_net == NET_ONION; }
229242

230243
bool CNetAddr::IsLocal() const
231244
{
@@ -235,7 +248,7 @@ bool CNetAddr::IsLocal() const
235248

236249
// IPv6 loopback (::1/128)
237250
static const unsigned char pchLocal[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1};
238-
if (memcmp(ip, pchLocal, 16) == 0)
251+
if (IsIPv6() && memcmp(ip, pchLocal, 16) == 0)
239252
return true;
240253

241254
return false;
@@ -259,12 +272,12 @@ bool CNetAddr::IsValid() const
259272
// header20 vectorlen3 addr26 addr26 addr26 header20 vectorlen3 addr26 addr26 addr26...
260273
// so if the first length field is garbled, it reads the second batch
261274
// of addr misaligned by 3 bytes.
262-
if (memcmp(ip, pchIPv4+3, sizeof(pchIPv4)-3) == 0)
275+
if (IsIPv6() && memcmp(ip, pchIPv4+3, sizeof(pchIPv4)-3) == 0)
263276
return false;
264277

265278
// unspecified IPv6 address (::/128)
266279
unsigned char ipNone6[16] = {};
267-
if (memcmp(ip, ipNone6, 16) == 0)
280+
if (IsIPv6() && memcmp(ip, ipNone6, 16) == 0)
268281
return false;
269282

270283
// documentation IPv6 address
@@ -311,7 +324,7 @@ bool CNetAddr::IsRoutable() const
311324
*/
312325
bool CNetAddr::IsInternal() const
313326
{
314-
return memcmp(ip, g_internal_prefix, sizeof(g_internal_prefix)) == 0;
327+
return m_net == NET_INTERNAL;
315328
}
316329

317330
enum Network CNetAddr::GetNetwork() const
@@ -322,13 +335,7 @@ enum Network CNetAddr::GetNetwork() const
322335
if (!IsRoutable())
323336
return NET_UNROUTABLE;
324337

325-
if (IsIPv4())
326-
return NET_IPV4;
327-
328-
if (IsTor())
329-
return NET_ONION;
330-
331-
return NET_IPV6;
338+
return m_net;
332339
}
333340

334341
std::string CNetAddr::ToStringIP() const
@@ -362,12 +369,12 @@ std::string CNetAddr::ToString() const
362369

363370
bool operator==(const CNetAddr& a, const CNetAddr& b)
364371
{
365-
return (memcmp(a.ip, b.ip, 16) == 0);
372+
return a.m_net == b.m_net && memcmp(a.ip, b.ip, 16) == 0;
366373
}
367374

368375
bool operator<(const CNetAddr& a, const CNetAddr& b)
369376
{
370-
return (memcmp(a.ip, b.ip, 16) < 0);
377+
return a.m_net < b.m_net || (a.m_net == b.m_net && memcmp(a.ip, b.ip, 16) < 0);
371378
}
372379

373380
/**
@@ -813,7 +820,7 @@ CSubNet::CSubNet(const CNetAddr &addr):
813820
*/
814821
bool CSubNet::Match(const CNetAddr &addr) const
815822
{
816-
if (!valid || !addr.IsValid())
823+
if (!valid || !addr.IsValid() || network.m_net != addr.m_net)
817824
return false;
818825
for(int x=0; x<16; ++x)
819826
if ((addr.ip[x] & netmask[x]) != network.ip[x])

src/netaddress.h

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,50 @@
1616
#include <string>
1717
#include <vector>
1818

19+
/**
20+
* A network type.
21+
* @note An address may belong to more than one network, for example `10.0.0.1`
22+
* belongs to both `NET_UNROUTABLE` and `NET_IPV4`.
23+
* Keep these sequential starting from 0 and `NET_MAX` as the last entry.
24+
* We have loops like `for (int i = 0; i < NET_MAX; i++)` that expect to iterate
25+
* over all enum values and also `GetExtNetwork()` "extends" this enum by
26+
* introducing standalone constants starting from `NET_MAX`.
27+
*/
1928
enum Network
2029
{
30+
/// Addresses from these networks are not publicly routable on the global Internet.
2131
NET_UNROUTABLE = 0,
32+
33+
/// IPv4
2234
NET_IPV4,
35+
36+
/// IPv6
2337
NET_IPV6,
38+
39+
/// TORv2
2440
NET_ONION,
41+
42+
/// A set of dummy addresses that map a name to an IPv6 address. These
43+
/// addresses belong to RFC4193's fc00::/7 subnet (unique-local addresses).
44+
/// We use them to map a string or FQDN to an IPv6 address in CAddrMan to
45+
/// keep track of which DNS seeds were used.
2546
NET_INTERNAL,
2647

48+
/// Dummy value to indicate the number of NET_* constants.
2749
NET_MAX,
2850
};
2951

30-
/** IP address (IPv6, or IPv4 using mapped IPv6 range (::FFFF:0:0/96)) */
52+
/**
53+
* Network address.
54+
*/
3155
class CNetAddr
3256
{
3357
protected:
58+
/**
59+
* Network to which this address belongs.
60+
*/
61+
Network m_net{NET_IPV6};
62+
3463
unsigned char ip[16]; // in network byte order
3564
uint32_t scopeId{0}; // for scoped/link-local ipv6 addresses
3665

@@ -39,6 +68,14 @@ class CNetAddr
3968
explicit CNetAddr(const struct in_addr& ipv4Addr);
4069
void SetIP(const CNetAddr& ip);
4170

71+
/**
72+
* Set from a legacy IPv6 address.
73+
* Legacy IPv6 address may be a normal IPv6 address, or another address
74+
* (e.g. IPv4) disguised as IPv6. This encoding is used in the legacy
75+
* `addr` encoding.
76+
*/
77+
void SetLegacyIPv6(const uint8_t ipv6[16]);
78+
4279
/**
4380
* Set raw IPv4 or IPv6 address (in network byte order)
4481
* @note Only NET_IPV4 and NET_IPV6 are allowed for network.
@@ -100,7 +137,27 @@ class CNetAddr
100137
friend bool operator!=(const CNetAddr& a, const CNetAddr& b) { return !(a == b); }
101138
friend bool operator<(const CNetAddr& a, const CNetAddr& b);
102139

103-
SERIALIZE_METHODS(CNetAddr, obj) { READWRITE(obj.ip); }
140+
/**
141+
* Serialize to a stream.
142+
*/
143+
template <typename Stream>
144+
void Serialize(Stream& s) const
145+
{
146+
s << ip;
147+
}
148+
149+
/**
150+
* Unserialize from a stream.
151+
*/
152+
template <typename Stream>
153+
void Unserialize(Stream& s)
154+
{
155+
unsigned char ip_temp[sizeof(ip)];
156+
s >> ip_temp;
157+
// Use SetLegacyIPv6() so that m_net is set correctly. For example
158+
// ::FFFF:0102:0304 should be set as m_net=NET_IPV4 (1.2.3.4).
159+
SetLegacyIPv6(ip_temp);
160+
}
104161

105162
friend class CSubNet;
106163
};

src/test/netbase_tests.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,14 @@ BOOST_AUTO_TEST_CASE(onioncat_test)
138138

139139
}
140140

141+
BOOST_AUTO_TEST_CASE(embedded_test)
142+
{
143+
CNetAddr addr1(ResolveIP("1.2.3.4"));
144+
CNetAddr addr2(ResolveIP("::FFFF:0102:0304"));
145+
BOOST_CHECK(addr2.IsIPv4());
146+
BOOST_CHECK_EQUAL(addr1.ToString(), addr2.ToString());
147+
}
148+
141149
BOOST_AUTO_TEST_CASE(subnet_test)
142150
{
143151

@@ -158,12 +166,13 @@ BOOST_AUTO_TEST_CASE(subnet_test)
158166
BOOST_CHECK(ResolveSubNet("1.2.2.1/24").Match(ResolveIP("1.2.2.4")));
159167
BOOST_CHECK(ResolveSubNet("1.2.2.110/31").Match(ResolveIP("1.2.2.111")));
160168
BOOST_CHECK(ResolveSubNet("1.2.2.20/26").Match(ResolveIP("1.2.2.63")));
161-
// All-Matching IPv6 Matches arbitrary IPv4 and IPv6
169+
// All-Matching IPv6 Matches arbitrary IPv6
162170
BOOST_CHECK(ResolveSubNet("::/0").Match(ResolveIP("1:2:3:4:5:6:7:1234")));
163171
// But not `::` or `0.0.0.0` because they are considered invalid addresses
164172
BOOST_CHECK(!ResolveSubNet("::/0").Match(ResolveIP("::")));
165173
BOOST_CHECK(!ResolveSubNet("::/0").Match(ResolveIP("0.0.0.0")));
166-
BOOST_CHECK(ResolveSubNet("::/0").Match(ResolveIP("1.2.3.4")));
174+
// Addresses from one network (IPv4) don't belong to subnets of another network (IPv6)
175+
BOOST_CHECK(!ResolveSubNet("::/0").Match(ResolveIP("1.2.3.4")));
167176
// All-Matching IPv4 does not Match IPv6
168177
BOOST_CHECK(!ResolveSubNet("0.0.0.0/0").Match(ResolveIP("1:2:3:4:5:6:7:1234")));
169178
// Invalid subnets Match nothing (not even invalid addresses)

0 commit comments

Comments
 (0)