Merge pull request #29122 from yuwata/network-allow-multiple-ipv6-null-addresses

network: allow multiple IPv6 null addresses
This commit is contained in:
Zbigniew Jędrzejewski-Szmek
2023-09-19 16:13:42 +02:00
committed by GitHub
4 changed files with 98 additions and 63 deletions
+44 -18
View File
@@ -337,18 +337,21 @@ static bool address_is_static_null(const Address *address) {
return true;
}
static uint32_t address_prefix(const Address *a) {
static int address_ipv4_prefix(const Address *a, struct in_addr *ret) {
struct in_addr p;
int r;
assert(a);
assert(a->family == AF_INET);
assert(ret);
/* make sure we don't try to shift by 32.
* See ISO/IEC 9899:TC3 § 6.5.7.3. */
if (a->prefixlen == 0)
return 0;
p = in4_addr_is_set(&a->in_addr_peer.in) ? a->in_addr_peer.in : a->in_addr.in;
r = in4_addr_mask(&p, a->prefixlen);
if (r < 0)
return r;
if (a->in_addr_peer.in.s_addr != 0)
return be32toh(a->in_addr_peer.in.s_addr) >> (32 - a->prefixlen);
else
return be32toh(a->in_addr.in.s_addr) >> (32 - a->prefixlen);
*ret = p;
return 0;
}
static void address_hash_func(const Address *a, struct siphash *state) {
@@ -357,16 +360,24 @@ static void address_hash_func(const Address *a, struct siphash *state) {
siphash24_compress(&a->family, sizeof(a->family), state);
switch (a->family) {
case AF_INET:
case AF_INET: {
struct in_addr prefix;
siphash24_compress(&a->prefixlen, sizeof(a->prefixlen), state);
uint32_t prefix = address_prefix(a);
assert_se(address_ipv4_prefix(a, &prefix) >= 0);
siphash24_compress(&prefix, sizeof(prefix), state);
_fallthrough_;
case AF_INET6:
siphash24_compress(&a->in_addr, FAMILY_ADDRESS_SIZE(a->family), state);
siphash24_compress(&a->in_addr.in, sizeof(a->in_addr.in), state);
break;
}
case AF_INET6:
siphash24_compress(&a->in_addr.in6, sizeof(a->in_addr.in6), state);
if (in6_addr_is_null(&a->in_addr.in6))
siphash24_compress(&a->prefixlen, sizeof(a->prefixlen), state);
break;
default:
/* treat any other address family as AF_UNSPEC */
break;
@@ -381,20 +392,35 @@ static int address_compare_func(const Address *a1, const Address *a2) {
return r;
switch (a1->family) {
case AF_INET:
case AF_INET: {
struct in_addr p1, p2;
/* See kernel's find_matching_ifa() in net/ipv4/devinet.c */
r = CMP(a1->prefixlen, a2->prefixlen);
if (r != 0)
return r;
r = CMP(address_prefix(a1), address_prefix(a2));
assert_se(address_ipv4_prefix(a1, &p1) >= 0);
assert_se(address_ipv4_prefix(a2, &p2) >= 0);
r = memcmp(&p1, &p2, sizeof(p1));
if (r != 0)
return r;
_fallthrough_;
return memcmp(&a1->in_addr.in, &a2->in_addr.in, sizeof(a1->in_addr.in));
}
case AF_INET6:
/* See kernel's ipv6_get_ifaddr() in net/ipv6/addrconf.c */
return memcmp(&a1->in_addr, &a2->in_addr, FAMILY_ADDRESS_SIZE(a1->family));
r = memcmp(&a1->in_addr.in6, &a2->in_addr.in6, sizeof(a1->in_addr.in6));
if (r != 0)
return r;
/* To distinguish IPv6 null addresses with different prefixlen, e.g. ::48 vs ::64, let's
* compare the prefix length. */
if (in6_addr_is_null(&a1->in_addr.in6))
r = CMP(a1->prefixlen, a2->prefixlen);
return r;
default:
/* treat any other address family as AF_UNSPEC */
return 0;
@@ -1,14 +0,0 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
[Network]
IPv6AcceptRA=no
Address=0.0.0.0/16
Address=0.0.0.0/30
Address=::/64
[Address]
Address=0.0.0.0/24
# The broadcast address will be ignored.
Broadcast=192.168.0.255
@@ -173,6 +173,9 @@ Label=subnet24
[Address]
Address=::/73
[Address]
Address=::/74
[Address]
Address=10.10.1.1/24
# just a random label which should exist
+51 -31
View File
@@ -2383,6 +2383,10 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
flag2: str,
flag3: str,
flag4: str,
ip4_null_16: str,
ip4_null_24: str,
ip6_null_73: str,
ip6_null_74: str,
):
output = check_output('ip address show dev dummy98')
print(output)
@@ -2442,9 +2446,14 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
self.assertIn(f'inet6 2001:db8:0:f106::2/64 scope global{flag4}', output)
# null address
self.assertRegex(output, r'inet [0-9]*.[0-9]*.0.1/16 brd [0-9]*.[0-9]*.255.255 scope global subnet16')
self.assertRegex(output, r'inet [0-9]*.[0-9]*.[0-9]*.1/24 brd [0-9]*.[0-9]*.[0-9]*.255 scope global subnet24')
self.assertRegex(output, r'inet6 [0-9a-f:]*:1/73 scope global')
self.assertTrue(ip4_null_16.endswith('.0.1'))
prefix16 = ip4_null_16[:-len('.0.1')]
self.assertTrue(ip4_null_24.endswith('.1'))
prefix24 = ip4_null_24[:-len('.1')]
self.assertIn(f'inet {ip4_null_16}/16 brd {prefix16}.255.255 scope global subnet16', output)
self.assertIn(f'inet {ip4_null_24}/24 brd {prefix24}.255 scope global subnet24', output)
self.assertIn(f'inet6 {ip6_null_73}/73 scope global', output)
self.assertIn(f'inet6 {ip6_null_74}/74 scope global', output)
# invalid sections
self.assertNotIn('10.4.4.1', output)
@@ -2469,6 +2478,29 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
self.setup_nftset('ifindex', 'iface_index')
self.wait_online(['dummy98:routable'])
ip4_null_16 = None
ip4_null_24 = None
output = check_output('ip -4 --json address show dev dummy98')
for i in json.loads(output)[0]['addr_info']:
if i['label'] == 'subnet16':
ip4_null_16 = i['local']
elif i['label'] == 'subnet24':
ip4_null_24 = i['local']
self.assertTrue(ip4_null_16.endswith('.0.1'))
self.assertTrue(ip4_null_24.endswith('.1'))
ip6_null_73 = None
ip6_null_74 = None
output = check_output('ip -6 --json address show dev dummy98')
for i in json.loads(output)[0]['addr_info']:
if i['prefixlen'] == 73:
ip6_null_73 = i['local']
elif i['prefixlen'] == 74:
ip6_null_74 = i['local']
self.assertTrue(ip6_null_73.endswith(':1'))
self.assertTrue(ip6_null_74.endswith(':1'))
self.verify_address_static(
label1='label1',
label2='label2',
@@ -2493,6 +2525,10 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
flag2='',
flag3=' noprefixroute',
flag4=' home mngtmpaddr',
ip4_null_16=ip4_null_16,
ip4_null_24=ip4_null_24,
ip6_null_73=ip6_null_73,
ip6_null_74=ip6_null_74,
)
# nft set
self.check_nftset('addr4', r'10\.10\.1\.1')
@@ -2528,6 +2564,10 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
flag2=' noprefixroute',
flag3=' home mngtmpaddr',
flag4=' noprefixroute',
ip4_null_16=ip4_null_16,
ip4_null_24=ip4_null_24,
ip6_null_73=ip6_null_73,
ip6_null_74=ip6_null_74,
)
networkctl_reconfigure('dummy98')
@@ -2556,6 +2596,10 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
flag2=' noprefixroute',
flag3=' home mngtmpaddr',
flag4=' noprefixroute',
ip4_null_16=ip4_null_16,
ip4_null_24=ip4_null_24,
ip6_null_73=ip6_null_73,
ip6_null_74=ip6_null_74,
)
# Tests for #20891.
@@ -2594,6 +2638,10 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
flag2=' noprefixroute',
flag3=' home mngtmpaddr',
flag4=' noprefixroute',
ip4_null_16=ip4_null_16,
ip4_null_24=ip4_null_24,
ip6_null_73=ip6_null_73,
ip6_null_74=ip6_null_74,
)
# test for ENOBUFS issue #17012 (with reload)
@@ -2611,34 +2659,6 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
for i in range(1, 254):
self.assertIn(f'inet 10.3.3.{i}/16 brd 10.3.255.255', output)
def test_address_null(self):
copy_network_unit('25-address-null.network', '12-dummy.netdev')
start_networkd()
self.wait_online(['dummy98:routable'])
output = check_output('ip address show dev dummy98 scope global')
print(output)
ipv4_address_16 = re.findall(r'inet 172.[0-9]*.0.1/16 brd 172.[0-9]*.255.255', output)
self.assertEqual(len(ipv4_address_16), 1)
ipv4_address_24 = re.findall(r'inet 192.168.[0-9]*.1/24 brd 192.168.[0-9]*.255', output)
self.assertEqual(len(ipv4_address_24), 1)
ipv4_address_30 = re.findall(r'inet 192.168.[0-9]*.[0-9]*/30 brd 192.168.[0-9]*.[0-9]*', output)
self.assertEqual(len(ipv4_address_30), 1)
ipv6_address = re.findall(r'inet6 fd[0-9a-f:]*/64', output)
self.assertEqual(len(ipv6_address), 1)
networkctl_reconfigure('dummy98')
self.wait_online(['dummy98:routable'])
output = check_output('ip address show dev dummy98 scope global')
print(output)
self.assertIn(ipv4_address_16[0], output)
self.assertIn(ipv4_address_24[0], output)
self.assertIn(ipv4_address_30[0], output)
self.assertIn(ipv6_address[0], output)
def test_address_ipv4acd(self):
check_output('ip netns add ns99')
check_output('ip link add veth99 type veth peer veth-peer')