Skip to content

Commit 5abd18b

Browse files
authored
network: Adds methods to IP address to check if they are in reserved ranges (#40272)
This commit adds the following two methods to the `Ip` interface: * `isLinkLocalAddress()` * `isTeredoAddress()` * `isUniqueLocalAddress()` * `isSiteLocalAddress()` These methods will be used by Envoy Mobile to determine IPv6 connectivity on the current network. --------- Signed-off-by: Ali Beyad <[email protected]>
1 parent 49a2a87 commit 5abd18b

File tree

5 files changed

+198
-0
lines changed

5 files changed

+198
-0
lines changed

envoy/network/address.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,46 @@ class Ip {
100100
*/
101101
virtual bool isUnicastAddress() const PURE;
102102

103+
/**
104+
* Determines whether the address is a link-local address. For IPv6, the prefix is fe80::/10. For
105+
* IPv4, the prefix is 169.254.0.0/16.
106+
*
107+
* See https://datatracker.ietf.org/doc/html/rfc3513#section-2.4 for details.
108+
*
109+
* @return true if the address is a link-local address, false otherwise.
110+
*/
111+
virtual bool isLinkLocalAddress() const PURE;
112+
113+
/**
114+
* Determines whether the address is a Unique Local Address. Applies to IPv6 addresses only, where
115+
* the prefix is fc00::/7.
116+
*
117+
* See https://datatracker.ietf.org/doc/html/rfc4193 for details.
118+
*
119+
* @return true if the address is a Unique Local Address, false otherwise.
120+
*/
121+
virtual bool isUniqueLocalAddress() const PURE;
122+
123+
/**
124+
* Determines whether the address is a Site-Local Address. Applies to IPv6 addresses only, where
125+
* the prefix is fec0::/10.
126+
*
127+
* See https://datatracker.ietf.org/doc/html/rfc3513#section-2.4 for details.
128+
*
129+
* @return true if the address is a Site-Local Address, false otherwise.
130+
*/
131+
virtual bool isSiteLocalAddress() const PURE;
132+
133+
/**
134+
* Determines whether the address is a Teredo address. Applies to IPv6 addresses only, where the
135+
* prefix is 2001:0000::/32.
136+
*
137+
* See https://datatracker.ietf.org/doc/html/rfc4380 for details.
138+
*
139+
* @return true if the address is a Teredo address, false otherwise.
140+
*/
141+
virtual bool isTeredoAddress() const PURE;
142+
103143
/**
104144
* @return Ipv4 address data IFF version() == IpVersion::v4, otherwise nullptr.
105145
*/

source/common/network/address_impl.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,22 @@ class Ipv4Instance : public InstanceBase {
179179
// inlined IN_MULTICAST() to avoid byte swapping
180180
!((ipv4_.address_.sin_addr.s_addr & htonl(0xf0000000)) == htonl(0xe0000000));
181181
}
182+
bool isLinkLocalAddress() const override {
183+
// Check if the address is in the link-local range: 169.254.0.0/16.
184+
return (ipv4_.address_.sin_addr.s_addr & htonl(0xffff0000)) == htonl(0xa9fe0000);
185+
}
186+
bool isUniqueLocalAddress() const override {
187+
// Unique Local Addresses (ULA) are not applicable to IPv4.
188+
return false;
189+
}
190+
bool isSiteLocalAddress() const override {
191+
// Site-Local Addresses are not applicable to IPv4.
192+
return false;
193+
}
194+
bool isTeredoAddress() const override {
195+
// Teredo addresses are not applicable to IPv4.
196+
return false;
197+
}
182198
const Ipv4* ipv4() const override { return &ipv4_; }
183199
const Ipv6* ipv6() const override { return nullptr; }
184200
uint32_t port() const override { return ntohs(ipv4_.address_.sin_port); }
@@ -291,6 +307,29 @@ class Ipv6Instance : public InstanceBase {
291307
bool isUnicastAddress() const override {
292308
return !isAnyAddress() && !IN6_IS_ADDR_MULTICAST(&ipv6_.address_.sin6_addr);
293309
}
310+
bool isLinkLocalAddress() const override {
311+
// Check if the address is in the link-local range: fe80::/10 or in the v4 mapped link-local
312+
// range: [::ffff:169.254.0.0].
313+
return IN6_IS_ADDR_LINKLOCAL(&ipv6_.address_.sin6_addr) ||
314+
(IN6_IS_ADDR_V4MAPPED(&ipv6_.address_.sin6_addr) &&
315+
(ipv6_.address_.sin6_addr.s6_addr[12] == 0xa9 &&
316+
ipv6_.address_.sin6_addr.s6_addr[13] == 0xfe));
317+
}
318+
bool isUniqueLocalAddress() const override {
319+
// Unique Local Addresses (ULA) are in the range fc00::/7.
320+
return (ipv6_.address_.sin6_addr.s6_addr[0] & 0xfe) == 0xfc;
321+
}
322+
bool isSiteLocalAddress() const override {
323+
// Site-Local Addresses are in the range fec0::/10.
324+
return IN6_IS_ADDR_SITELOCAL(&ipv6_.address_.sin6_addr);
325+
}
326+
bool isTeredoAddress() const override {
327+
// Teredo addresses have the prefix 2001:0000::/32.
328+
return ipv6_.address_.sin6_addr.s6_addr[0] == 0x20 &&
329+
ipv6_.address_.sin6_addr.s6_addr[1] == 0x01 &&
330+
ipv6_.address_.sin6_addr.s6_addr[2] == 0x00 &&
331+
ipv6_.address_.sin6_addr.s6_addr[3] == 0x00;
332+
}
294333
const Ipv4* ipv4() const override { return nullptr; }
295334
const Ipv6* ipv6() const override { return &ipv6_; }
296335
uint32_t port() const override { return ipv6_.port(); }

test/common/network/address_impl_test.cc

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ namespace Network {
2929
namespace Address {
3030
namespace {
3131

32+
Ipv6Instance v4MappedV6Instance(const std::string& address) {
33+
return Ipv6Instance(address, /*port=*/0, /*sock_interface=*/nullptr, /*v6only=*/false);
34+
}
35+
3236
bool addressesEqual(const InstanceConstSharedPtr& a, const Instance& b) {
3337
if (a == nullptr || a->type() != Type::Ip || b.type() != Type::Ip) {
3438
return false;
@@ -228,6 +232,36 @@ TEST(Ipv4InstanceTest, Broadcast) {
228232
EXPECT_FALSE(address.ip()->isUnicastAddress());
229233
}
230234

235+
TEST(Ipv4InstanceTest, LinkLocal) {
236+
// Link-local addresses.
237+
EXPECT_TRUE(Ipv4Instance("169.254.0.0").ip()->isLinkLocalAddress());
238+
EXPECT_TRUE(Ipv4Instance("169.254.42.43").ip()->isLinkLocalAddress());
239+
EXPECT_TRUE(Ipv4Instance("169.254.255.255").ip()->isLinkLocalAddress());
240+
241+
// Not link-local addresses.
242+
EXPECT_FALSE(Ipv4Instance("169.255.0.0").ip()->isLinkLocalAddress());
243+
EXPECT_FALSE(Ipv4Instance("169.255.255.255").ip()->isLinkLocalAddress());
244+
EXPECT_FALSE(Ipv4Instance("170.254.0.0").ip()->isLinkLocalAddress());
245+
}
246+
247+
TEST(Ipv4InstanceTest, Teredo) {
248+
// Teredo addresses are not applicable to IPv4.
249+
EXPECT_FALSE(Ipv4Instance("20.1.1.1").ip()->isTeredoAddress());
250+
EXPECT_FALSE(Ipv4Instance("200.1.1.1").ip()->isTeredoAddress());
251+
}
252+
253+
TEST(Ipv4InstanceTest, SiteLocal) {
254+
// Site-local addresses are not applicable to IPv4.
255+
EXPECT_FALSE(Ipv4Instance("1.2.3.4").ip()->isSiteLocalAddress());
256+
EXPECT_FALSE(Ipv4Instance("200.1.1.1").ip()->isSiteLocalAddress());
257+
}
258+
259+
TEST(Ipv4InstanceTest, UniqueLocal) {
260+
// Unique Local Addresses (ULA) are not applicable to IPv4.
261+
EXPECT_FALSE(Ipv4Instance("1.2.3.4").ip()->isUniqueLocalAddress());
262+
EXPECT_FALSE(Ipv4Instance("200.1.1.1").ip()->isUniqueLocalAddress());
263+
}
264+
231265
TEST(Ipv4InstanceTest, BadAddress) {
232266
EXPECT_THROW(Ipv4Instance("foo"), EnvoyException);
233267
EXPECT_THROW(Ipv4Instance("bar", 1), EnvoyException);
@@ -341,6 +375,85 @@ TEST(Ipv6InstanceTest, Broadcast) {
341375
EXPECT_FALSE(address.ip()->isUnicastAddress());
342376
}
343377

378+
TEST(Ipv6InstanceTest, LinkLocal) {
379+
// Link-local addresses are in the range "fe80::0" to "febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff".
380+
EXPECT_TRUE(Ipv6Instance("fe80:0:0:0:0:0:0:0").ip()->isLinkLocalAddress());
381+
EXPECT_TRUE(Ipv6Instance("fe80::0").ip()->isLinkLocalAddress());
382+
EXPECT_TRUE(Ipv6Instance("fe80::1").ip()->isLinkLocalAddress());
383+
EXPECT_TRUE(Ipv6Instance("fe80::42:43").ip()->isLinkLocalAddress());
384+
EXPECT_TRUE(Ipv6Instance("fe80::ffff:ffff:ffff:ffff").ip()->isLinkLocalAddress());
385+
EXPECT_TRUE(Ipv6Instance("fe81::1").ip()->isLinkLocalAddress());
386+
EXPECT_TRUE(Ipv6Instance("fe90::1").ip()->isLinkLocalAddress());
387+
EXPECT_TRUE(Ipv6Instance("febf::0").ip()->isLinkLocalAddress());
388+
EXPECT_TRUE(Ipv6Instance("febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff").ip()->isLinkLocalAddress());
389+
390+
// Not link-local addresses.
391+
EXPECT_FALSE(Ipv6Instance("::fe80").ip()->isLinkLocalAddress());
392+
EXPECT_FALSE(Ipv6Instance("fec0::0").ip()->isLinkLocalAddress());
393+
EXPECT_FALSE(Ipv6Instance("ff00::0").ip()->isLinkLocalAddress());
394+
EXPECT_FALSE(Ipv6Instance("ab80::0").ip()->isLinkLocalAddress());
395+
EXPECT_FALSE(Ipv6Instance("abcd::0").ip()->isLinkLocalAddress());
396+
EXPECT_FALSE(Ipv6Instance("::ffff").ip()->isLinkLocalAddress());
397+
}
398+
399+
TEST(Ipv6InstanceTest, V4MappedLinkLocal) {
400+
// Link-local addresses in the range ::ffff:169.254.0.0/16.
401+
EXPECT_TRUE(v4MappedV6Instance("::ffff:169.254.0.0").ip()->isLinkLocalAddress());
402+
EXPECT_TRUE(v4MappedV6Instance("::ffff:169.254.42.42").ip()->isLinkLocalAddress());
403+
EXPECT_TRUE(v4MappedV6Instance("::ffff:169.254.255.255").ip()->isLinkLocalAddress());
404+
405+
// Not link-local addresses.
406+
EXPECT_FALSE(v4MappedV6Instance("::ffff:169.255.0.0").ip()->isLinkLocalAddress());
407+
EXPECT_FALSE(v4MappedV6Instance("::ffff:170.254.0.0").ip()->isLinkLocalAddress());
408+
EXPECT_FALSE(v4MappedV6Instance("::ffff:0.0.0.0").ip()->isLinkLocalAddress());
409+
EXPECT_FALSE(v4MappedV6Instance("::ffff:192.168.1.1").ip()->isLinkLocalAddress());
410+
EXPECT_FALSE(v4MappedV6Instance("::ffff:10.54.1.1").ip()->isLinkLocalAddress());
411+
}
412+
413+
TEST(Ipv6InstanceTest, Teredo) {
414+
// Teredo addresses are in the range 2001::/32.
415+
EXPECT_TRUE(Ipv6Instance("2001:0:0:0:0:0:0:0").ip()->isTeredoAddress());
416+
EXPECT_TRUE(Ipv6Instance("2001::1").ip()->isTeredoAddress());
417+
EXPECT_TRUE(Ipv6Instance("2001::42:43").ip()->isTeredoAddress());
418+
EXPECT_TRUE(Ipv6Instance("2001::ffff:ffff:ffff:ffff").ip()->isTeredoAddress());
419+
420+
// Not Teredo addresses.
421+
EXPECT_FALSE(Ipv6Instance("2002::0").ip()->isTeredoAddress());
422+
EXPECT_FALSE(Ipv6Instance("2002::1").ip()->isTeredoAddress());
423+
EXPECT_FALSE(Ipv6Instance("3001::1").ip()->isTeredoAddress());
424+
}
425+
426+
TEST(Ipv6InstanceTest, UniqueLocal) {
427+
// Unique Local Addresses (ULA) are in the range fc00::/7.
428+
EXPECT_TRUE(Ipv6Instance("fc00:0:0:0:0:0:0:0").ip()->isUniqueLocalAddress());
429+
EXPECT_TRUE(Ipv6Instance("fc00::1").ip()->isUniqueLocalAddress());
430+
EXPECT_TRUE(Ipv6Instance("fc00::42:43").ip()->isUniqueLocalAddress());
431+
EXPECT_TRUE(Ipv6Instance("fdff::ffff:ffff:ffff:ffff:ffff:ffff").ip()->isUniqueLocalAddress());
432+
433+
// Not ULA addresses.
434+
EXPECT_FALSE(Ipv6Instance("fec0:0:0:0:0:0:0:0").ip()->isUniqueLocalAddress());
435+
EXPECT_FALSE(
436+
Ipv6Instance("feff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").ip()->isUniqueLocalAddress());
437+
EXPECT_FALSE(Ipv6Instance("fe00::0").ip()->isUniqueLocalAddress());
438+
EXPECT_FALSE(Ipv6Instance("fe80::0").ip()->isUniqueLocalAddress());
439+
EXPECT_FALSE(Ipv6Instance("ff00::0").ip()->isUniqueLocalAddress());
440+
}
441+
442+
TEST(Ipv6InstanceTest, SiteLocal) {
443+
// Site-local addresses are in the range fec0::/10.
444+
EXPECT_TRUE(Ipv6Instance("fec0:0:0:0:0:0:0:0").ip()->isSiteLocalAddress());
445+
EXPECT_TRUE(Ipv6Instance("fec0::1").ip()->isSiteLocalAddress());
446+
EXPECT_TRUE(Ipv6Instance("fec0::42:43").ip()->isSiteLocalAddress());
447+
EXPECT_TRUE(Ipv6Instance("feff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").ip()->isSiteLocalAddress());
448+
449+
// Not site-local addresses.
450+
EXPECT_FALSE(Ipv6Instance("fc00:0:0:0:0:0:0:0").ip()->isSiteLocalAddress());
451+
EXPECT_FALSE(Ipv6Instance("fdff::ffff:ffff:ffff:ffff:ffff:ffff").ip()->isSiteLocalAddress());
452+
EXPECT_FALSE(Ipv6Instance("ff00::0").ip()->isSiteLocalAddress());
453+
EXPECT_FALSE(Ipv6Instance("2002::1").ip()->isSiteLocalAddress());
454+
EXPECT_FALSE(Ipv6Instance("3001::1").ip()->isSiteLocalAddress());
455+
}
456+
344457
TEST(Ipv6InstanceTest, BadAddress) {
345458
EXPECT_THROW(Ipv6Instance("foo"), EnvoyException);
346459
EXPECT_THROW(Ipv6Instance("bar", 1), EnvoyException);

test/mocks/network/mocks.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,10 @@ class MockIp : public Address::Ip {
588588
MOCK_METHOD(const std::string&, addressAsString, (), (const));
589589
MOCK_METHOD(bool, isAnyAddress, (), (const));
590590
MOCK_METHOD(bool, isUnicastAddress, (), (const));
591+
MOCK_METHOD(bool, isLinkLocalAddress, (), (const));
592+
MOCK_METHOD(bool, isUniqueLocalAddress, (), (const));
593+
MOCK_METHOD(bool, isSiteLocalAddress, (), (const));
594+
MOCK_METHOD(bool, isTeredoAddress, (), (const));
591595
MOCK_METHOD(const Address::Ipv4*, ipv4, (), (const));
592596
MOCK_METHOD(const Address::Ipv6*, ipv6, (), (const));
593597
MOCK_METHOD(uint32_t, port, (), (const));

tools/spelling/spelling_dictionary.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ STX
115115
SkyWalking
116116
TIDs
117117
Timedout
118+
ULA
118119
WRSQ
119120
WASI
120121
ceil
@@ -1418,6 +1419,7 @@ templating
14181419
templatize
14191420
templatized
14201421
templatizing
1422+
teredo
14211423
testability
14221424
testcase
14231425
testcases

0 commit comments

Comments
 (0)