Skip to content

Commit 07b7337

Browse files
committed
network: dhcp-pd: allow to assign the same subnet prefix to multiple interfaces
There is no reason networkd refuses that. Especially, when multiple downstream interfaces are connected to the same network, it is natural to assign the same subnet prefix to them. Prompted by systemd#22571.
1 parent 38488ba commit 07b7337

File tree

3 files changed

+38
-46
lines changed

3 files changed

+38
-46
lines changed

src/network/networkd-dhcp-prefix-delegation.c

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -487,20 +487,13 @@ static int dhcp_pd_get_preferred_subnet_prefix(
487487
"subnet id %" PRIu64 " is out of range. Only have %" PRIu64 " subnets.",
488488
link->network->dhcp_pd_subnet_id, UINT64_C(1) << (64 - pd_prefix_len));
489489

490-
if (link_get_by_dhcp_pd_subnet_prefix(link->manager, &prefix, &assigned_link) >= 0 &&
491-
assigned_link != link) {
492-
_cleanup_free_ char *assigned_buf = NULL;
493-
494-
(void) in6_addr_to_string(&prefix, &assigned_buf);
495-
return log_link_warning_errno(link, SYNTHETIC_ERRNO(EAGAIN),
496-
"The requested prefix %s is already assigned to another link.",
497-
strna(assigned_buf));
498-
}
499-
500490
*ret = prefix;
501491
return 0;
502492
}
503493

494+
if (dhcp_pd_get_assigned_subnet_prefix(link, pd_prefix, pd_prefix_len, ret) >= 0)
495+
return 0;
496+
504497
for (uint64_t n = 0; ; n++) {
505498
/* If we do not have an allocation preference just iterate
506499
* through the address space and return the first free prefix. */
@@ -517,11 +510,16 @@ static int dhcp_pd_get_preferred_subnet_prefix(
517510

518511
/* Check that the prefix is not assigned to another link. */
519512
if (link_get_by_dhcp_pd_subnet_prefix(link->manager, &prefix, &assigned_link) < 0 ||
520-
assigned_link == link) {
521-
*ret = prefix;
522-
return 0;
523-
}
513+
assigned_link == link)
514+
break;
524515
}
516+
517+
r = link_add_dhcp_pd_subnet_prefix(link, &prefix);
518+
if (r < 0)
519+
return log_link_warning_errno(link, r, "Failed to save acquired free subnet prefix: %m");
520+
521+
*ret = prefix;
522+
return 0;
525523
}
526524

527525
static int dhcp_pd_assign_subnet_prefix(
@@ -540,9 +538,9 @@ static int dhcp_pd_assign_subnet_prefix(
540538
assert(link->network);
541539
assert(pd_prefix);
542540

543-
if (dhcp_pd_get_assigned_subnet_prefix(link, pd_prefix, pd_prefix_len, &prefix) < 0 &&
544-
dhcp_pd_get_preferred_subnet_prefix(link, pd_prefix, pd_prefix_len, &prefix) < 0)
545-
return 0;
541+
r = dhcp_pd_get_preferred_subnet_prefix(link, pd_prefix, pd_prefix_len, &prefix);
542+
if (r < 0)
543+
return r == -ERANGE ? 0 : r;
546544

547545
(void) in6_addr_prefix_to_string(&prefix, 64, &buf);
548546

@@ -570,12 +568,6 @@ static int dhcp_pd_assign_subnet_prefix(
570568
"Failed to assign/update address for prefix %s: %m",
571569
strna(buf));
572570

573-
r = link_add_dhcp_pd_subnet_prefix(link, &prefix);
574-
if (r < 0)
575-
return log_link_warning_errno(link, r,
576-
"Failed to save assigned prefix %s: %m",
577-
strna(buf));
578-
579571
log_link_debug(link, "Assigned prefix %s", strna(buf));
580572
return 1;
581573
}

test/test-network/conf/25-dhcp-pd-downstream-dummy98.network

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ DHCPPrefixDelegation=yes
1010

1111
[DHCPPrefixDelegation]
1212
UplinkInterface=veth99
13-
SubnetId=2
13+
SubnetId=0
1414
Announce=no
1515
Token=eui64
1616
Token=::1a:2b:3c:4d

test/test-network/systemd-networkd-tests.py

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5269,8 +5269,8 @@ def test_dhcp6pd(self):
52695269
# Link Subnet IDs
52705270
# test1: 0x00
52715271
# dummy97: 0x01 (The link will appear later)
5272-
# dummy98: 0x02
5273-
# dummy99: auto -> 0x03 (No address assignment)
5272+
# dummy98: 0x00
5273+
# dummy99: auto -> 0x02 (No address assignment)
52745274
# veth97: 0x08
52755275
# veth98: 0x09
52765276
# veth99: 0x10
@@ -5300,15 +5300,15 @@ def test_dhcp6pd(self):
53005300
output = check_output('ip -6 address show dev dummy98 scope global')
53015301
print(output)
53025302
# address in IA_PD (Token=static)
5303-
self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]02:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
5303+
self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
53045304
# address in IA_PD (temporary)
5305-
self.wait_address('dummy98', 'inet6 3ffe:501:ffff:[2-9a-f]02:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6')
5305+
self.wait_address('dummy98', 'inet6 3ffe:501:ffff:[2-9a-f]00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6')
53065306

53075307
print('### ip -6 address show dev dummy99 scope global')
53085308
output = check_output('ip -6 address show dev dummy99 scope global')
53095309
print(output)
53105310
# Assign=no
5311-
self.assertNotRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]03')
5311+
self.assertNotRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]02')
53125312

53135313
print('### ip -6 address show dev veth97 scope global')
53145314
output = check_output('ip -6 address show dev veth97 scope global')
@@ -5368,12 +5368,12 @@ def test_dhcp6pd(self):
53685368
print('### ip -6 route show dev dummy98')
53695369
output = check_output('ip -6 route show dev dummy98')
53705370
print(output)
5371-
self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]02::/64 proto kernel metric [0-9]* expires')
5371+
self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
53725372

53735373
print('### ip -6 route show dev dummy99')
53745374
output = check_output('ip -6 route show dev dummy99')
53755375
print(output)
5376-
self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]03::/64 proto dhcp metric [0-9]* expires')
5376+
self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires')
53775377

53785378
print('### ip -6 route show dev veth97')
53795379
output = check_output('ip -6 route show dev veth97')
@@ -5420,25 +5420,25 @@ def test_dhcp6pd(self):
54205420
output = check_output('ip -6 address show dev dummy98 scope global')
54215421
print(output)
54225422
# address in IA_PD (Token=static)
5423-
self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]02:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
5423+
self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
54245424
# address in IA_PD (temporary)
5425-
self.wait_address('dummy98', 'inet6 3ffe:501:ffff:[2-9a-f]02:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6')
5425+
self.wait_address('dummy98', 'inet6 3ffe:501:ffff:[2-9a-f]00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6')
54265426

54275427
print('### ip -6 address show dev dummy99 scope global')
54285428
output = check_output('ip -6 address show dev dummy99 scope global')
54295429
print(output)
54305430
# Assign=no
5431-
self.assertNotRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]03')
5431+
self.assertNotRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]02')
54325432

54335433
print('### ip -6 route show dev dummy98')
54345434
output = check_output('ip -6 route show dev dummy98')
54355435
print(output)
5436-
self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]02::/64 proto kernel metric [0-9]* expires')
5436+
self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
54375437

54385438
print('### ip -6 route show dev dummy99')
54395439
output = check_output('ip -6 route show dev dummy99')
54405440
print(output)
5441-
self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]03::/64 proto dhcp metric [0-9]* expires')
5441+
self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires')
54425442

54435443
def verify_dhcp4_6rd(self, tunnel_name):
54445444
print('### ip -4 address show dev veth-peer scope global')
@@ -5449,9 +5449,9 @@ def verify_dhcp4_6rd(self, tunnel_name):
54495449
# Link Subnet IDs
54505450
# test1: 0x00
54515451
# dummy97: 0x01 (The link will appear later)
5452-
# dummy98: 0x02
5453-
# dummy99: auto -> 0x0[34] (No address assignment)
5454-
# 6rd-XXX: auto -> 0x0[34]
5452+
# dummy98: 0x00
5453+
# dummy99: auto -> 0x0[23] (No address assignment)
5454+
# 6rd-XXX: auto -> 0x0[23]
54555455
# veth97: 0x08
54565456
# veth98: 0x09
54575457
# veth99: 0x10
@@ -5484,15 +5484,15 @@ def verify_dhcp4_6rd(self, tunnel_name):
54845484
output = check_output('ip -6 address show dev dummy98 scope global')
54855485
print(output)
54865486
# address in IA_PD (Token=static)
5487-
self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+02:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
5487+
self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
54885488
# address in IA_PD (temporary)
5489-
self.wait_address('dummy98', 'inet6 2001:db8:6464:[0-9a-f]+02:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6')
5489+
self.wait_address('dummy98', 'inet6 2001:db8:6464:[0-9a-f]+00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6')
54905490

54915491
print('### ip -6 address show dev dummy99 scope global')
54925492
output = check_output('ip -6 address show dev dummy99 scope global')
54935493
print(output)
54945494
# Assign=no
5495-
self.assertNotRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+0[34]')
5495+
self.assertNotRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+0[23]')
54965496

54975497
print('### ip -6 address show dev veth97 scope global')
54985498
output = check_output('ip -6 address show dev veth97 scope global')
@@ -5552,12 +5552,12 @@ def verify_dhcp4_6rd(self, tunnel_name):
55525552
print('### ip -6 route show dev dummy98')
55535553
output = check_output('ip -6 route show dev dummy98')
55545554
print(output)
5555-
self.assertRegex(output, '2001:db8:6464:[0-9a-f]+02::/64 proto kernel metric [0-9]* expires')
5555+
self.assertRegex(output, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires')
55565556

55575557
print('### ip -6 route show dev dummy99')
55585558
output = check_output('ip -6 route show dev dummy99')
55595559
print(output)
5560-
self.assertRegex(output, '2001:db8:6464:[0-9a-f]+0[34]::/64 proto dhcp metric [0-9]* expires')
5560+
self.assertRegex(output, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto dhcp metric [0-9]* expires')
55615561

55625562
print('### ip -6 route show dev veth97')
55635563
output = check_output('ip -6 route show dev veth97')
@@ -5604,13 +5604,13 @@ def verify_dhcp4_6rd(self, tunnel_name):
56045604
print('### ip -6 address show dev {}'.format(tunnel_name))
56055605
output = check_output('ip -6 address show dev {}'.format(tunnel_name))
56065606
print(output)
5607-
self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+0[34]:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global dynamic')
5607+
self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+0[23]:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global dynamic')
56085608
self.assertRegex(output, 'inet6 ::10.100.100.[0-9]+/96 scope global')
56095609

56105610
print('### ip -6 route show dev {}'.format(tunnel_name))
56115611
output = check_output('ip -6 route show dev {}'.format(tunnel_name))
56125612
print(output)
5613-
self.assertRegex(output, '2001:db8:6464:[0-9a-f]+0[34]::/64 proto kernel metric [0-9]* expires')
5613+
self.assertRegex(output, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto kernel metric [0-9]* expires')
56145614
self.assertRegex(output, '::/96 proto kernel metric [0-9]*')
56155615

56165616
print('### ip -6 route show default')

0 commit comments

Comments
 (0)