From 6accfd3139a0ccef9859b742452c04926f52515c Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Wed, 5 Dec 2018 20:39:41 +1100 Subject: [PATCH 1/3] Move link_check_ready() to later in the file We're about to need it to be later in the file for the next commit. Moving it now means that when we change it in the next commit, it's not intermingled with the move. No functional change intended. Signed-off-by: Daniel Axtens --- src/network/networkd-link.c | 114 ++++++++++++++++++------------------ 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 5353b9daaf..11dca0162b 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -730,63 +730,6 @@ static void link_enter_configured(Link *link) { link_dirty(link); } -void link_check_ready(Link *link) { - Address *a; - Iterator i; - - assert(link); - - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return; - - if (!link->network) - return; - - if (!link->addresses_configured) - return; - - if (!link->neighbors_configured) - return; - - if (!link->static_routes_configured) - return; - - if (!link->routing_policy_rules_configured) - return; - - if (link_ipv4ll_enabled(link)) - if (!link->ipv4ll_address || - !link->ipv4ll_route) - return; - - if (!link->network->bridge) { - - if (link_ipv6ll_enabled(link)) - if (in_addr_is_null(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address) > 0) - return; - - if ((link_dhcp4_enabled(link) && !link_dhcp6_enabled(link) && - !link->dhcp4_configured) || - (link_dhcp6_enabled(link) && !link_dhcp4_enabled(link) && - !link->dhcp6_configured) || - (link_dhcp4_enabled(link) && link_dhcp6_enabled(link) && - !link->dhcp4_configured && !link->dhcp6_configured)) - return; - - if (link_ipv6_accept_ra_enabled(link) && !link->ndisc_configured) - return; - } - - SET_FOREACH(a, link->addresses, i) - if (!address_is_ready(a)) - return; - - if (link->state != LINK_STATE_CONFIGURED) - link_enter_configured(link); - - return; -} - static int link_request_set_routing_policy_rule(Link *link) { RoutingPolicyRule *rule, *rrule = NULL; int r; @@ -900,6 +843,63 @@ static int link_request_set_routes(Link *link) { return 0; } +void link_check_ready(Link *link) { + Address *a; + Iterator i; + + assert(link); + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return; + + if (!link->network) + return; + + if (!link->addresses_configured) + return; + + if (!link->neighbors_configured) + return; + + if (!link->static_routes_configured) + return; + + if (!link->routing_policy_rules_configured) + return; + + if (link_ipv4ll_enabled(link)) + if (!link->ipv4ll_address || + !link->ipv4ll_route) + return; + + if (!link->network->bridge) { + + if (link_ipv6ll_enabled(link)) + if (in_addr_is_null(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address) > 0) + return; + + if ((link_dhcp4_enabled(link) && !link_dhcp6_enabled(link) && + !link->dhcp4_configured) || + (link_dhcp6_enabled(link) && !link_dhcp4_enabled(link) && + !link->dhcp6_configured) || + (link_dhcp4_enabled(link) && link_dhcp6_enabled(link) && + !link->dhcp4_configured && !link->dhcp6_configured)) + return; + + if (link_ipv6_accept_ra_enabled(link) && !link->ndisc_configured) + return; + } + + SET_FOREACH(a, link->addresses, i) + if (!address_is_ready(a)) + return; + + if (link->state != LINK_STATE_CONFIGURED) + link_enter_configured(link); + + return; +} + static int link_request_set_neighbors(Link *link) { Neighbor *neighbor; int r; From 6aa5773bfff0a92d64da70426cae833df6f84daf Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Wed, 5 Dec 2018 21:49:35 +1100 Subject: [PATCH 2/3] Install routes after addresses are ready If an IPv6 route is added with a source address that is still tentative, the kernel will refuse to install it. Previously, once we sent the messages to the kernel to add the addresses, we would immediately proceed to add the routes. The addresses would usually still be tentative at this point, so adding static IPv6 routes was broken - see issue #5882. Now, only begin to configure routes once the addresses are ready, by restructuring the state machine, and tracking when addresses are ready, not just added. Fixes: #5882 Signed-off-by: Daniel Axtens --- src/network/networkd-link.c | 18 ++++++++++++------ src/network/networkd-link.h | 1 + 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 11dca0162b..f413e61911 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -861,6 +861,15 @@ void link_check_ready(Link *link) { if (!link->neighbors_configured) return; + SET_FOREACH(a, link->addresses, i) + if (!address_is_ready(a)) + return; + + if (!link->addresses_ready) { + link->addresses_ready = true; + link_request_set_routes(link); + } + if (!link->static_routes_configured) return; @@ -890,10 +899,6 @@ void link_check_ready(Link *link) { return; } - SET_FOREACH(a, link->addresses, i) - if (!address_is_ready(a)) - return; - if (link->state != LINK_STATE_CONFIGURED) link_enter_configured(link); @@ -954,7 +959,7 @@ static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) if (link->address_messages == 0) { log_link_debug(link, "Addresses set"); link->addresses_configured = true; - link_request_set_routes(link); + link_check_ready(link); } return 1; @@ -1084,6 +1089,7 @@ static int link_request_set_addresses(Link *link) { /* Reset all *_configured flags we are configuring. */ link->addresses_configured = false; + link->addresses_ready = false; link->neighbors_configured = false; link->static_routes_configured = false; link->routing_policy_rules_configured = false; @@ -1238,7 +1244,7 @@ static int link_request_set_addresses(Link *link) { if (link->address_messages == 0) { link->addresses_configured = true; - link_request_set_routes(link); + link_check_ready(link); } else log_link_debug(link, "Setting addresses"); diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index 00e68fdfaa..e417ea26ef 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -82,6 +82,7 @@ typedef struct Link { Set *routes_foreign; bool addresses_configured; + bool addresses_ready; sd_dhcp_client *dhcp_client; sd_dhcp_lease *dhcp_lease; From 20ca06a6692089c94d25f7a2eea0a65ce71970a8 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Tue, 15 Jan 2019 01:15:15 +1100 Subject: [PATCH 3/3] tests: Add test for IPv6 source routing The test is a bit messy because it must be done on a device that enforces a tentative state for IPv6 addresses, and it appears that the dummy device does not. So we use a bond instead. Signed-off-by: Daniel Axtens --- .../test-network/conf/25-route-ipv6-src.network | 16 ++++++++++++++++ test/test-network/systemd-networkd-tests.py | 17 +++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 test/test-network/conf/25-route-ipv6-src.network diff --git a/test/test-network/conf/25-route-ipv6-src.network b/test/test-network/conf/25-route-ipv6-src.network new file mode 100644 index 0000000000..4e551c024a --- /dev/null +++ b/test/test-network/conf/25-route-ipv6-src.network @@ -0,0 +1,16 @@ +# This test cannot use a dummy interface: IPv6 addresses +# are added without having to go through tentative state + +[Match] +Name=bond199 + +[Network] +LinkLocalAddressing=ipv6 +Address=2001:1234:56:8f63::1/64 +Address=2001:1234:56:8f63::2/64 +IPv6AcceptRA=no + +[Route] +Destination=abcd::/16 +Gateway=2001:1234:56:8f63::1:1 +PreferredSource=2001:1234:56:8f63::2 diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index 183d24d7a7..535761a757 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -538,6 +538,7 @@ class NetworkdNetWorkTests(unittest.TestCase, Utilities): '25-link-section-unmanaged.network', '25-route-gateway.network', '25-route-gateway-on-link.network', + '25-route-ipv6-src.network', '25-route-reverse-order.network', '25-route-section.network', '25-route-tcp-window-settings.network', @@ -770,6 +771,22 @@ class NetworkdNetWorkTests(unittest.TestCase, Utilities): self.assertRegex(output, 'scope') self.assertRegex(output, 'link') + def test_ip_route_ipv6_src_route(self): + # a dummy device does not make the addresses go through tentative state, so we + # reuse a bond from an earlier test, which does make the addresses go through + # tentative state, and do our test on that + self.copy_unit_to_networkd_unit_path('23-active-slave.network', '25-route-ipv6-src.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev') + self.start_networkd() + + self.assertTrue(self.link_exits('dummy98')) + self.assertTrue(self.link_exits('bond199')) + + output = subprocess.check_output(['ip', '-6', 'route', 'list', 'dev', 'bond199']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'abcd::/16') + self.assertRegex(output, 'src') + self.assertRegex(output, '2001:1234:56:8f63::2') + def test_ip_link_mac_address(self): self.copy_unit_to_networkd_unit_path('25-address-link-section.network', '12-dummy.netdev') self.start_networkd()