From 8df8ce781c440ca4241fe416059ce3459724d2af Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 15 May 2019 14:55:31 +0900 Subject: [PATCH 01/18] network: set preferred source in removing route entry on address lost Closes #12490. --- src/network/networkd-dhcp4.c | 43 +++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index b170906f21..156ac9257b 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -182,11 +182,7 @@ static int link_set_dhcp_routes(Link *link) { } static int dhcp_lease_lost(Link *link) { - _cleanup_(address_freep) Address *address = NULL; - const struct in_addr *router; - struct in_addr addr; - struct in_addr netmask; - unsigned prefixlen = 0; + struct in_addr address = {}; int r; assert(link); @@ -194,10 +190,16 @@ static int dhcp_lease_lost(Link *link) { log_link_warning(link, "DHCP lease lost"); + (void) sd_dhcp_lease_get_address(link->dhcp_lease, &address); + if (link->network->dhcp_use_routes) { _cleanup_free_ sd_dhcp_route **routes = NULL; + const struct in_addr *router; + uint32_t table; int n, i; + table = link_get_dhcp_route_table(link); + n = sd_dhcp_lease_get_routes(link->dhcp_lease, &routes); if (n >= 0) { for (i = 0; i < n; i++) { @@ -209,6 +211,9 @@ static int dhcp_lease_lost(Link *link) { assert_se(sd_dhcp_route_get_gateway(routes[i], &route->gw.in) >= 0); assert_se(sd_dhcp_route_get_destination(routes[i], &route->dst.in) >= 0); assert_se(sd_dhcp_route_get_destination_prefix_length(routes[i], &route->dst_prefixlen) >= 0); + route->priority = link->network->dhcp_route_metric; + route->table = table; + route->scope = route_scope_from_address(route, &address); route_remove(route, link, NULL); } @@ -216,7 +221,7 @@ static int dhcp_lease_lost(Link *link) { } r = sd_dhcp_lease_get_router(link->dhcp_lease, &router); - if (r > 0 && !in4_addr_is_null(&router[0])) { + if (r > 0 && !in4_addr_is_null(&router[0]) && !in4_addr_is_null(&address)) { _cleanup_(route_freep) Route *route_gw = NULL; _cleanup_(route_freep) Route *route = NULL; @@ -225,7 +230,11 @@ static int dhcp_lease_lost(Link *link) { route_gw->family = AF_INET; route_gw->dst.in = router[0]; route_gw->dst_prefixlen = 32; + route_gw->prefsrc.in = address; route_gw->scope = RT_SCOPE_LINK; + route_gw->protocol = RTPROT_DHCP; + route_gw->priority = link->network->dhcp_route_metric; + route_gw->table = table; route_remove(route_gw, link, NULL); } @@ -234,25 +243,33 @@ static int dhcp_lease_lost(Link *link) { if (r >= 0) { route->family = AF_INET; route->gw.in = router[0]; + route->prefsrc.in = address; + route->protocol = RTPROT_DHCP; + route->priority = link->network->dhcp_route_metric; + route->table = table; route_remove(route, link, NULL); } } } - r = address_new(&address); - if (r >= 0) { - r = sd_dhcp_lease_get_address(link->dhcp_lease, &addr); + if (!in4_addr_is_null(&address)) { + _cleanup_(address_freep) Address *a = NULL; + + r = address_new(&a); if (r >= 0) { + struct in_addr netmask; + unsigned prefixlen = 0; + r = sd_dhcp_lease_get_netmask(link->dhcp_lease, &netmask); if (r >= 0) prefixlen = in4_addr_netmask_to_prefixlen(&netmask); - address->family = AF_INET; - address->in_addr.in = addr; - address->prefixlen = prefixlen; + a->family = AF_INET; + a->in_addr.in = address; + a->prefixlen = prefixlen; - address_remove(address, link, NULL); + address_remove(a, link, NULL); } } From 4c9c82723138d885698bdc9ab3676ced733a854a Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 28 May 2019 22:13:01 +0900 Subject: [PATCH 02/18] network: reset Link::dhcp4_configured flag earlier --- src/network/networkd-dhcp4.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index 156ac9257b..71efd03ff0 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -190,6 +190,8 @@ static int dhcp_lease_lost(Link *link) { log_link_warning(link, "DHCP lease lost"); + link->dhcp4_configured = false; + (void) sd_dhcp_lease_get_address(link->dhcp_lease, &address); if (link->network->dhcp_use_routes) { @@ -306,7 +308,6 @@ static int dhcp_lease_lost(Link *link) { link->dhcp_lease = sd_dhcp_lease_unref(link->dhcp_lease); link_dirty(link); - link->dhcp4_configured = false; return 0; } From a20c909c68af3fbc7f22e8c8bfe13e65aa63496c Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 28 May 2019 22:14:13 +0900 Subject: [PATCH 03/18] network: set dhcp4_configured flag false before configuring address Otherwise, even if dhcp_lease_acquired() fails, the flag is still on. --- src/network/networkd-dhcp4.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index 71efd03ff0..ae74508c87 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -427,6 +427,8 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) { assert(client); assert(link); + link->dhcp4_configured = false; + r = sd_dhcp_client_get_lease(client, &lease); if (r < 0) return log_link_error_errno(link, r, "DHCP error: No lease: %m"); From 7fa472f9b71b6cfa8503d45674a062d69966b11d Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 24 May 2019 16:47:47 +0900 Subject: [PATCH 04/18] network: split dhcp_lease_lost() into small pieces --- src/network/networkd-dhcp4.c | 291 +++++++++++++++++++++-------------- 1 file changed, 178 insertions(+), 113 deletions(-) diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index ae74508c87..42d8c43248 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -181,9 +181,181 @@ static int link_set_dhcp_routes(Link *link) { return 0; } +static int dhcp_remove_routes(Link *link, struct in_addr *address) { + _cleanup_free_ sd_dhcp_route **routes = NULL; + uint32_t table; + int n, i, r; + + assert(link); + assert(address); + + if (!link->network->dhcp_use_routes) + return 0; + + n = sd_dhcp_lease_get_routes(link->dhcp_lease, &routes); + if (IN_SET(n, 0, -ENODATA)) { + log_link_debug(link, "DHCP: No routes received from DHCP server: %m"); + return 0; + } else if (n < 0) + return log_link_error_errno(link, n, "DHCP error: could not get routes: %m"); + + table = link_get_dhcp_route_table(link); + + for (i = 0; i < n; i++) { + _cleanup_(route_freep) Route *route = NULL; + + r = route_new(&route); + if (r < 0) + return log_oom(); + + route->family = AF_INET; + assert_se(sd_dhcp_route_get_gateway(routes[i], &route->gw.in) >= 0); + assert_se(sd_dhcp_route_get_destination(routes[i], &route->dst.in) >= 0); + assert_se(sd_dhcp_route_get_destination_prefix_length(routes[i], &route->dst_prefixlen) >= 0); + route->priority = link->network->dhcp_route_metric; + route->table = table; + route->scope = route_scope_from_address(route, address); + + (void) route_remove(route, link, NULL); + } + + return n; +} + +static int dhcp_remove_router(Link *link, struct in_addr *address) { + _cleanup_(route_freep) Route *route_gw = NULL, *route = NULL; + const struct in_addr *router; + uint32_t table; + int r; + + assert(link); + assert(address); + + if (!link->network->dhcp_use_routes) + return 0; + + r = sd_dhcp_lease_get_router(link->dhcp_lease, &router); + if (IN_SET(r, 0, -ENODATA)) { + log_link_debug(link, "DHCP: No gateway received from DHCP server."); + return 0; + } else if (r < 0) + return log_link_error_errno(link, r, "DHCP error: could not get gateway: %m"); + else if (in4_addr_is_null(&router[0])) { + log_link_info(link, "DHCP: Received gateway is null, ignoring."); + return 0; + } + + table = link_get_dhcp_route_table(link); + + r = route_new(&route_gw); + if (r < 0) + return log_oom(); + + route_gw->family = AF_INET; + route_gw->dst.in = router[0]; + route_gw->dst_prefixlen = 32; + route_gw->prefsrc.in = *address; + route_gw->scope = RT_SCOPE_LINK; + route_gw->protocol = RTPROT_DHCP; + route_gw->priority = link->network->dhcp_route_metric; + route_gw->table = table; + + (void) route_remove(route_gw, link, NULL); + + r = route_new(&route); + if (r < 0) + return log_oom(); + + route->family = AF_INET; + route->gw.in = router[0]; + route->prefsrc.in = *address; + route->protocol = RTPROT_DHCP; + route->priority = link->network->dhcp_route_metric; + route->table = table; + + (void) route_remove(route, link, NULL); + + return 0; +} + +static int dhcp_remove_address(Link *link, struct in_addr *address) { + _cleanup_(address_freep) Address *a = NULL; + struct in_addr netmask; + int r; + + assert(link); + assert(address); + + if (in4_addr_is_null(address)) + return 0; + + r = address_new(&a); + if (r < 0) + return log_oom(); + + a->family = AF_INET; + a->in_addr.in = *address; + + if (sd_dhcp_lease_get_netmask(link->dhcp_lease, &netmask) >= 0) + a->prefixlen = in4_addr_netmask_to_prefixlen(&netmask); + + (void) address_remove(a, link, NULL); + + return 0; +} + +static int dhcp_reset_mtu(Link *link) { + uint16_t mtu; + int r; + + assert(link); + + if (!link->network->dhcp_use_mtu) + return 0; + + r = sd_dhcp_lease_get_mtu(link->dhcp_lease, &mtu); + if (r < 0) + return r; + + if (link->original_mtu == mtu) + return 0; + + r = link_set_mtu(link, link->original_mtu); + if (r < 0) { + log_link_error_errno(link, r, "DHCP error: could not reset MTU: %m"); + link_enter_failed(link); + return r; + } + + return 0; +} + +static int dhcp_reset_hostname(Link *link) { + const char *hostname; + int r; + + assert(link); + + if (!link->network->dhcp_use_hostname) + return 0; + + hostname = link->network->dhcp_hostname; + if (!hostname) + (void) sd_dhcp_lease_get_hostname(link->dhcp_lease, &hostname); + + if (!hostname) + return 0; + + /* If a hostname was set due to the lease, then unset it now. */ + r = manager_set_hostname(link->manager, NULL); + if (r < 0) + return log_link_error_errno(link, r, "DHCP error: Failed to reset transient hostname: %m"); + + return 0; +} + static int dhcp_lease_lost(Link *link) { struct in_addr address = {}; - int r; assert(link); assert(link->dhcp_lease); @@ -193,118 +365,11 @@ static int dhcp_lease_lost(Link *link) { link->dhcp4_configured = false; (void) sd_dhcp_lease_get_address(link->dhcp_lease, &address); - - if (link->network->dhcp_use_routes) { - _cleanup_free_ sd_dhcp_route **routes = NULL; - const struct in_addr *router; - uint32_t table; - int n, i; - - table = link_get_dhcp_route_table(link); - - n = sd_dhcp_lease_get_routes(link->dhcp_lease, &routes); - if (n >= 0) { - for (i = 0; i < n; i++) { - _cleanup_(route_freep) Route *route = NULL; - - r = route_new(&route); - if (r >= 0) { - route->family = AF_INET; - assert_se(sd_dhcp_route_get_gateway(routes[i], &route->gw.in) >= 0); - assert_se(sd_dhcp_route_get_destination(routes[i], &route->dst.in) >= 0); - assert_se(sd_dhcp_route_get_destination_prefix_length(routes[i], &route->dst_prefixlen) >= 0); - route->priority = link->network->dhcp_route_metric; - route->table = table; - route->scope = route_scope_from_address(route, &address); - - route_remove(route, link, NULL); - } - } - } - - r = sd_dhcp_lease_get_router(link->dhcp_lease, &router); - if (r > 0 && !in4_addr_is_null(&router[0]) && !in4_addr_is_null(&address)) { - _cleanup_(route_freep) Route *route_gw = NULL; - _cleanup_(route_freep) Route *route = NULL; - - r = route_new(&route_gw); - if (r >= 0) { - route_gw->family = AF_INET; - route_gw->dst.in = router[0]; - route_gw->dst_prefixlen = 32; - route_gw->prefsrc.in = address; - route_gw->scope = RT_SCOPE_LINK; - route_gw->protocol = RTPROT_DHCP; - route_gw->priority = link->network->dhcp_route_metric; - route_gw->table = table; - - route_remove(route_gw, link, NULL); - } - - r = route_new(&route); - if (r >= 0) { - route->family = AF_INET; - route->gw.in = router[0]; - route->prefsrc.in = address; - route->protocol = RTPROT_DHCP; - route->priority = link->network->dhcp_route_metric; - route->table = table; - - route_remove(route, link, NULL); - } - } - } - - if (!in4_addr_is_null(&address)) { - _cleanup_(address_freep) Address *a = NULL; - - r = address_new(&a); - if (r >= 0) { - struct in_addr netmask; - unsigned prefixlen = 0; - - r = sd_dhcp_lease_get_netmask(link->dhcp_lease, &netmask); - if (r >= 0) - prefixlen = in4_addr_netmask_to_prefixlen(&netmask); - - a->family = AF_INET; - a->in_addr.in = address; - a->prefixlen = prefixlen; - - address_remove(a, link, NULL); - } - } - - if (link->network->dhcp_use_mtu) { - uint16_t mtu; - - r = sd_dhcp_lease_get_mtu(link->dhcp_lease, &mtu); - if (r >= 0 && link->original_mtu != mtu) { - r = link_set_mtu(link, link->original_mtu); - if (r < 0) { - log_link_warning(link, - "DHCP error: could not reset MTU"); - link_enter_failed(link); - return r; - } - } - } - - if (link->network->dhcp_use_hostname) { - const char *hostname = NULL; - - if (link->network->dhcp_hostname) - hostname = link->network->dhcp_hostname; - else - (void) sd_dhcp_lease_get_hostname(link->dhcp_lease, &hostname); - - if (hostname) { - /* If a hostname was set due to the lease, then unset it now. */ - r = manager_set_hostname(link->manager, NULL); - if (r < 0) - log_link_warning_errno(link, r, "Failed to reset transient hostname: %m"); - } - } + (void) dhcp_remove_routes(link, &address); + (void) dhcp_remove_router(link, &address); + (void) dhcp_remove_address(link, &address); + (void) dhcp_reset_mtu(link); + (void) dhcp_reset_hostname(link); link->dhcp_lease = sd_dhcp_lease_unref(link->dhcp_lease); link_dirty(link); From 5b11f372f02f31bb38e8d3a18dc92ecfa8813cd7 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 24 May 2019 14:14:12 +0900 Subject: [PATCH 05/18] network: lower log level about critical connection --- src/network/networkd-dhcp4.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index 42d8c43248..8bcd345731 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -657,7 +657,7 @@ static int dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) { case SD_DHCP_CLIENT_EVENT_IP_CHANGE: if (link->network->dhcp_critical) { - log_link_error(link, "DHCPv4 connection considered system critical, ignoring request to reconfigure it."); + log_link_notice(link, "DHCPv4 connection considered system critical, ignoring request to reconfigure it."); return 0; } From b6efd6614c3007ce8ad122db70d7a319813fee36 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 24 May 2019 11:39:26 +0900 Subject: [PATCH 06/18] test-network: add tests for issue #12490 --- test/test-network/systemd-networkd-tests.py | 52 ++++++++++++++++++++- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index 2eda211285..11f128e119 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -142,8 +142,8 @@ class Utilities(): if (os.path.exists(os.path.join(network_unit_file_path, unit + '.d'))): shutil.rmtree(os.path.join(network_unit_file_path, unit + '.d')) - def start_dnsmasq(self, additional_options='', lease_time='1h'): - dnsmasq_command = f'dnsmasq -8 /var/run/networkd-ci/test-dnsmasq-log-file --log-queries=extra --log-dhcp --pid-file=/var/run/networkd-ci/test-test-dnsmasq.pid --conf-file=/dev/null --interface=veth-peer --enable-ra --dhcp-range=2600::10,2600::20,{lease_time} --dhcp-range=192.168.5.10,192.168.5.200,{lease_time} -R --dhcp-leasefile=/var/run/networkd-ci/lease --dhcp-option=26,1492 --dhcp-option=option:router,192.168.5.1 --dhcp-option=33,192.168.5.4,192.168.5.5 --port=0 ' + additional_options + def start_dnsmasq(self, additional_options='', ipv4_range='192.168.5.10,192.168.5.200', ipv6_range='2600::10,2600::20', lease_time='1h'): + dnsmasq_command = f'dnsmasq -8 /var/run/networkd-ci/test-dnsmasq-log-file --log-queries=extra --log-dhcp --pid-file=/var/run/networkd-ci/test-test-dnsmasq.pid --conf-file=/dev/null --interface=veth-peer --enable-ra --dhcp-range={ipv6_range},{lease_time} --dhcp-range={ipv4_range},{lease_time} -R --dhcp-leasefile=/var/run/networkd-ci/lease --dhcp-option=26,1492 --dhcp-option=option:router,192.168.5.1 --dhcp-option=33,192.168.5.4,192.168.5.5 --port=0 ' + additional_options subprocess.check_call(dnsmasq_command, shell=True) time.sleep(10) @@ -2242,6 +2242,54 @@ class NetworkdNetworkDHCPClientTests(unittest.TestCase, Utilities): output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'veth99', 'scope', 'link'], universal_newlines=True).rstrip() self.assertRegex(output, 'inet .* scope link') + def test_dhcp_client_route_remove_on_renew(self): + self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', + 'dhcp-client-ipv4-only-ipv6-disabled.network') + self.start_networkd(0) + self.wait_online(['veth-peer:carrier']) + self.start_dnsmasq(ipv4_range='192.168.5.100,192.168.5.199', lease_time='2m') + self.wait_online(['veth99:routable', 'veth-peer:routable']) + + # test for issue #12490 + + output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'veth99', 'scope', 'global', 'dynamic'], universal_newlines=True).rstrip() + print(output) + self.assertRegex(output, 'inet 192.168.5.1[0-9]*/24 brd 192.168.5.255 scope global dynamic veth99') + address1=None + for line in output.splitlines(): + if 'brd 192.168.5.255 scope global dynamic veth99' in line: + address1 = line.split()[1].split('/')[0] + break + + output = subprocess.check_output(['ip', '-4', 'route', 'show', 'dev', 'veth99'], universal_newlines=True).rstrip() + print(output) + self.assertRegex(output, f'default via 192.168.5.1 proto dhcp src {address1} metric 1024') + self.assertRegex(output, f'192.168.5.1 proto dhcp scope link src {address1} metric 1024') + + self.stop_dnsmasq(dnsmasq_pid_file) + self.start_dnsmasq(ipv4_range='192.168.5.200,192.168.5.250', lease_time='2m') + + print('Wait for the dynamic address to be expired') + time.sleep(130) + + output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'veth99', 'scope', 'global', 'dynamic'], universal_newlines=True).rstrip() + print(output) + self.assertRegex(output, 'inet 192.168.5.2[0-9]*/24 brd 192.168.5.255 scope global dynamic veth99') + address2=None + for line in output.splitlines(): + if 'brd 192.168.5.255 scope global dynamic veth99' in line: + address2 = line.split()[1].split('/')[0] + break + + self.assertNotEqual(address1, address2) + + output = subprocess.check_output(['ip', '-4', 'route', 'show', 'dev', 'veth99'], universal_newlines=True).rstrip() + print(output) + self.assertNotRegex(output, f'default via 192.168.5.1 proto dhcp src {address1} metric 1024') + self.assertNotRegex(output, f'192.168.5.1 proto dhcp scope link src {address1} metric 1024') + self.assertRegex(output, f'default via 192.168.5.1 proto dhcp src {address2} metric 1024') + self.assertRegex(output, f'192.168.5.1 proto dhcp scope link src {address2} metric 1024') + if __name__ == '__main__': unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout, verbosity=3)) From 1836bedf92ab7b2319e83e8ff1a63ddf8446ad0a Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 24 May 2019 12:04:59 +0900 Subject: [PATCH 07/18] test-network: warn when firewalld.service is running --- test/test-network/systemd-networkd-tests.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index 11f128e119..0d6e5284f9 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -142,7 +142,13 @@ class Utilities(): if (os.path.exists(os.path.join(network_unit_file_path, unit + '.d'))): shutil.rmtree(os.path.join(network_unit_file_path, unit + '.d')) + def warn_about_firewalld(self): + rc = subprocess.call(['systemctl', '-q', 'is-active', 'firewalld.service']) + if rc == 0: + print('\nWARNING: firewalld.service is active. The test may fail.') + def start_dnsmasq(self, additional_options='', ipv4_range='192.168.5.10,192.168.5.200', ipv6_range='2600::10,2600::20', lease_time='1h'): + self.warn_about_firewalld() dnsmasq_command = f'dnsmasq -8 /var/run/networkd-ci/test-dnsmasq-log-file --log-queries=extra --log-dhcp --pid-file=/var/run/networkd-ci/test-test-dnsmasq.pid --conf-file=/dev/null --interface=veth-peer --enable-ra --dhcp-range={ipv6_range},{lease_time} --dhcp-range={ipv4_range},{lease_time} -R --dhcp-leasefile=/var/run/networkd-ci/lease --dhcp-option=26,1492 --dhcp-option=option:router,192.168.5.1 --dhcp-option=33,192.168.5.4,192.168.5.5 --port=0 ' + additional_options subprocess.check_call(dnsmasq_command, shell=True) @@ -1800,6 +1806,7 @@ class NetworkdNetworkRATests(unittest.TestCase, Utilities): self.remove_unit_from_networkd_path(self.units) def test_ipv6_prefix_delegation(self): + self.warn_about_firewalld() self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'ipv6-prefix.network', 'ipv6-prefix-veth.network') self.start_networkd() @@ -1831,6 +1838,7 @@ class NetworkdNetworkDHCPServerTests(unittest.TestCase, Utilities): self.remove_unit_from_networkd_path(self.units) def test_dhcp_server(self): + self.warn_about_firewalld() self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-client.network', 'dhcp-server.network') self.start_networkd() @@ -1856,6 +1864,7 @@ class NetworkdNetworkDHCPServerTests(unittest.TestCase, Utilities): self.assertRegex(output, 'Search Domains: one') def test_emit_router_timezone(self): + self.warn_about_firewalld() self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-client-timezone-router.network', 'dhcp-server-timezone-router.network') self.start_networkd() From 9f10126f3c10797335363e4c8de7e4be700a7ec0 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 24 May 2019 13:38:08 +0900 Subject: [PATCH 08/18] test-network: stop dnsmasq before removing links --- test/test-network/systemd-networkd-tests.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index 0d6e5284f9..d809e72abc 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -1906,15 +1906,15 @@ class NetworkdNetworkDHCPClientTests(unittest.TestCase, Utilities): 'static.network'] def setUp(self): - self.link_remove(self.links) self.stop_dnsmasq(dnsmasq_pid_file) + self.link_remove(self.links) def tearDown(self): - self.link_remove(self.links) - self.remove_unit_from_networkd_path(self.units) self.stop_dnsmasq(dnsmasq_pid_file) self.remove_lease_file() self.remove_log_file() + self.link_remove(self.links) + self.remove_unit_from_networkd_path(self.units) def test_dhcp_client_ipv6_only(self): self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-ipv6-only.network') From def9fc0d657ec22549f10da567aa62f44f97b7de Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 24 May 2019 13:39:50 +0900 Subject: [PATCH 09/18] test-network: remove fou ports on failure --- test/test-network/systemd-networkd-tests.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index d809e72abc..32efc22929 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -112,6 +112,10 @@ class Utilities(): subprocess.call(['ip', 'link', 'del', 'dev', link]) time.sleep(1) + def remove_fou_ports(self, ports): + for port in ports: + subprocess.call(['ip', 'fou', 'del', 'port', port]) + def l2tp_tunnel_remove(self, tunnel_ids): output = subprocess.check_output(['ip', 'l2tp', 'show', 'tunnel'], universal_newlines=True).rstrip() for tid in tunnel_ids: @@ -371,10 +375,16 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): 'vxlan-test1.network', 'vxlan.network'] + fou_ports = [ + '55555', + '55556'] + def setUp(self): + self.remove_fou_ports(self.fou_ports) self.link_remove(self.links) def tearDown(self): + self.remove_fou_ports(self.fou_ports) self.link_remove(self.links) self.remove_unit_from_networkd_path(self.units) @@ -924,9 +934,6 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): print(output) self.assertRegex(output, 'encap fou encap-sport auto encap-dport 55556') - subprocess.call(['ip', 'fou', 'del', 'port', '55555']) - subprocess.call(['ip', 'fou', 'del', 'port', '55556']) - def test_vxlan(self): self.copy_unit_to_networkd_unit_path('25-vxlan.netdev', 'vxlan.network', '11-dummy.netdev', 'vxlan-test1.network') From 95c74b0aff01f00de0e0d49635e3d70b698298b7 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 24 May 2019 13:44:49 +0900 Subject: [PATCH 10/18] test-network: use setUp() and tearDown() to delete routing policy rule tables --- test/test-network/systemd-networkd-tests.py | 26 +++++++-------------- 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index 32efc22929..877ca20047 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -116,6 +116,10 @@ class Utilities(): for port in ports: subprocess.call(['ip', 'fou', 'del', 'port', port]) + def remove_routing_policy_rule_tables(self, tables): + for table in tables: + subprocess.call(['ip', 'rule', 'del', 'table', table]) + def l2tp_tunnel_remove(self, tunnel_ids): output = subprocess.check_output(['ip', 'l2tp', 'show', 'tunnel'], universal_newlines=True).rstrip() for tid in tunnel_ids: @@ -1098,10 +1102,14 @@ class NetworkdNetWorkTests(unittest.TestCase, Utilities): 'routing-policy-rule-dummy98.network', 'routing-policy-rule-test1.network'] + routing_policy_rule_tables = ['7', '8'] + def setUp(self): + self.remove_routing_policy_rule_tables(self.routing_policy_rule_tables) self.link_remove(self.links) def tearDown(self): + self.remove_routing_policy_rule_tables(self.routing_policy_rule_tables) self.link_remove(self.links) self.remove_unit_from_networkd_path(self.units) @@ -1170,8 +1178,6 @@ class NetworkdNetWorkTests(unittest.TestCase, Utilities): def test_routing_policy_rule(self): self.copy_unit_to_networkd_unit_path('routing-policy-rule-test1.network', '11-dummy.netdev') - subprocess.call(['ip', 'rule', 'del', 'table', '7']) - self.start_networkd() self.check_link_exists('test1') @@ -1185,15 +1191,10 @@ class NetworkdNetWorkTests(unittest.TestCase, Utilities): self.assertRegex(output, 'oif test1') self.assertRegex(output, 'lookup 7') - subprocess.call(['ip', 'rule', 'del', 'table', '7']) - def test_routing_policy_rule_issue_11280(self): self.copy_unit_to_networkd_unit_path('routing-policy-rule-test1.network', '11-dummy.netdev', 'routing-policy-rule-dummy98.network', '12-dummy.netdev') - subprocess.call(['ip', 'rule', 'del', 'table', '7']) - subprocess.call(['ip', 'rule', 'del', 'table', '8']) - for trial in range(3): # Remove state files only first time self.start_networkd(remove_state_files=(trial == 0)) @@ -1209,15 +1210,10 @@ class NetworkdNetWorkTests(unittest.TestCase, Utilities): print(output) self.assertRegex(output, '112: from 192.168.101.18 tos (?:0x08|throughput) iif dummy98 oif dummy98 lookup 8') - subprocess.call(['ip', 'rule', 'del', 'table', '7']) - subprocess.call(['ip', 'rule', 'del', 'table', '8']) - @expectedFailureIfRoutingPolicyPortRangeIsNotAvailable() def test_routing_policy_rule_port_range(self): self.copy_unit_to_networkd_unit_path('25-fibrule-port-range.network', '11-dummy.netdev') - subprocess.call(['ip', 'rule', 'del', 'table', '7']) - self.start_networkd() self.check_link_exists('test1') @@ -1231,14 +1227,10 @@ class NetworkdNetWorkTests(unittest.TestCase, Utilities): self.assertRegex(output, 'tcp') self.assertRegex(output, 'lookup 7') - subprocess.call(['ip', 'rule', 'del', 'table', '7']) - @expectedFailureIfRoutingPolicyIPProtoIsNotAvailable() def test_routing_policy_rule_invert(self): self.copy_unit_to_networkd_unit_path('25-fibrule-invert.network', '11-dummy.netdev') - subprocess.call(['ip', 'rule', 'del', 'table', '7']) - self.start_networkd() self.check_link_exists('test1') @@ -1250,8 +1242,6 @@ class NetworkdNetWorkTests(unittest.TestCase, Utilities): self.assertRegex(output, 'tcp') self.assertRegex(output, 'lookup 7') - subprocess.call(['ip', 'rule', 'del', 'table', '7']) - def test_route_static(self): self.copy_unit_to_networkd_unit_path('25-route-static.network', '12-dummy.netdev') self.start_networkd(0) From 5f68a6a46a4ccc08e1c3124fe302857ac08c421e Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 24 May 2019 13:49:51 +0900 Subject: [PATCH 11/18] test-network: remove routes even on failure --- test/test-network/systemd-networkd-tests.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index 877ca20047..425e19f80a 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -120,6 +120,10 @@ class Utilities(): for table in tables: subprocess.call(['ip', 'rule', 'del', 'table', table]) + def remove_routes(self, routes): + for route_type, addr in routes: + subprocess.call(['ip', 'route', 'del', route_type, addr]) + def l2tp_tunnel_remove(self, tunnel_ids): output = subprocess.check_output(['ip', 'l2tp', 'show', 'tunnel'], universal_newlines=True).rstrip() for tid in tunnel_ids: @@ -1103,13 +1107,16 @@ class NetworkdNetWorkTests(unittest.TestCase, Utilities): 'routing-policy-rule-test1.network'] routing_policy_rule_tables = ['7', '8'] + routes = [['blackhole', '202.54.1.2'], ['unreachable', '202.54.1.3'], ['prohibit', '202.54.1.4']] def setUp(self): self.remove_routing_policy_rule_tables(self.routing_policy_rule_tables) + self.remove_routes(self.routes) self.link_remove(self.links) def tearDown(self): self.remove_routing_policy_rule_tables(self.routing_policy_rule_tables) + self.remove_routes(self.routes) self.link_remove(self.links) self.remove_unit_from_networkd_path(self.units) @@ -1281,10 +1288,6 @@ class NetworkdNetWorkTests(unittest.TestCase, Utilities): print(output) self.assertRegex(output, 'prohibit 202.54.1.4 proto static') - subprocess.call(['ip', 'route', 'del', 'blackhole', '202.54.1.2']) - subprocess.call(['ip', 'route', 'del', 'unreachable', '202.54.1.3']) - subprocess.call(['ip', 'route', 'del', 'prohibit', '202.54.1.4']) - 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 From 350e05d27408dc713440a8bf259d9521759f32fb Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 24 May 2019 13:50:51 +0900 Subject: [PATCH 12/18] test-network: rename link_remove() to remove_links() --- test/test-network/systemd-networkd-tests.py | 38 ++++++++++----------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index 425e19f80a..3a51e5cfc8 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -106,7 +106,7 @@ class Utilities(): def check_link_exists(self, link): self.assertTrue(self.link_exists(link)) - def link_remove(self, links): + def remove_links(self, links): for link in links: if self.link_exists(link): subprocess.call(['ip', 'link', 'del', 'dev', link]) @@ -389,11 +389,11 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): def setUp(self): self.remove_fou_ports(self.fou_ports) - self.link_remove(self.links) + self.remove_links(self.links) def tearDown(self): self.remove_fou_ports(self.fou_ports) - self.link_remove(self.links) + self.remove_links(self.links) self.remove_unit_from_networkd_path(self.units) def test_dropin_and_networkctl_glob(self): @@ -1016,11 +1016,11 @@ class NetworkdL2TPTests(unittest.TestCase, Utilities): def setUp(self): self.l2tp_tunnel_remove(self.l2tp_tunnel_ids) - self.link_remove(self.links) + self.remove_links(self.links) def tearDown(self): self.l2tp_tunnel_remove(self.l2tp_tunnel_ids) - self.link_remove(self.links) + self.remove_links(self.links) self.remove_unit_from_networkd_path(self.units) @expectedFailureIfModuleIsNotAvailable('l2tp_eth') @@ -1112,12 +1112,12 @@ class NetworkdNetWorkTests(unittest.TestCase, Utilities): def setUp(self): self.remove_routing_policy_rule_tables(self.routing_policy_rule_tables) self.remove_routes(self.routes) - self.link_remove(self.links) + self.remove_links(self.links) def tearDown(self): self.remove_routing_policy_rule_tables(self.routing_policy_rule_tables) self.remove_routes(self.routes) - self.link_remove(self.links) + self.remove_links(self.links) self.remove_unit_from_networkd_path(self.units) def test_address_static(self): @@ -1517,10 +1517,10 @@ class NetworkdNetWorkBondTests(unittest.TestCase, Utilities): 'bond-slave.network'] def setUp(self): - self.link_remove(self.links) + self.remove_links(self.links) def tearDown(self): - self.link_remove(self.links) + self.remove_links(self.links) self.remove_unit_from_networkd_path(self.units) def test_bond_active_slave(self): @@ -1619,10 +1619,10 @@ class NetworkdNetWorkBridgeTests(unittest.TestCase, Utilities): 'bridge99.network'] def setUp(self): - self.link_remove(self.links) + self.remove_links(self.links) def tearDown(self): - self.link_remove(self.links) + self.remove_links(self.links) self.remove_unit_from_networkd_path(self.units) def test_bridge_property(self): @@ -1773,10 +1773,10 @@ class NetworkdNetWorkLLDPTests(unittest.TestCase, Utilities): '25-veth.netdev'] def setUp(self): - self.link_remove(self.links) + self.remove_links(self.links) def tearDown(self): - self.link_remove(self.links) + self.remove_links(self.links) self.remove_unit_from_networkd_path(self.units) def test_lldp(self): @@ -1799,10 +1799,10 @@ class NetworkdNetworkRATests(unittest.TestCase, Utilities): 'ipv6-prefix-veth.network'] def setUp(self): - self.link_remove(self.links) + self.remove_links(self.links) def tearDown(self): - self.link_remove(self.links) + self.remove_links(self.links) self.remove_unit_from_networkd_path(self.units) def test_ipv6_prefix_delegation(self): @@ -1831,10 +1831,10 @@ class NetworkdNetworkDHCPServerTests(unittest.TestCase, Utilities): 'dhcp-server-timezone-router.network'] def setUp(self): - self.link_remove(self.links) + self.remove_links(self.links) def tearDown(self): - self.link_remove(self.links) + self.remove_links(self.links) self.remove_unit_from_networkd_path(self.units) def test_dhcp_server(self): @@ -1907,13 +1907,13 @@ class NetworkdNetworkDHCPClientTests(unittest.TestCase, Utilities): def setUp(self): self.stop_dnsmasq(dnsmasq_pid_file) - self.link_remove(self.links) + self.remove_links(self.links) def tearDown(self): self.stop_dnsmasq(dnsmasq_pid_file) self.remove_lease_file() self.remove_log_file() - self.link_remove(self.links) + self.remove_links(self.links) self.remove_unit_from_networkd_path(self.units) def test_dhcp_client_ipv6_only(self): From 58df8fb7dc23f13ebbaa5e759d9467c6a892bee8 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 24 May 2019 14:03:07 +0900 Subject: [PATCH 13/18] test-network: drop unused link --- test/test-network/systemd-networkd-tests.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index 3a51e5cfc8..d9b4b51ee4 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -1878,7 +1878,6 @@ class NetworkdNetworkDHCPServerTests(unittest.TestCase, Utilities): class NetworkdNetworkDHCPClientTests(unittest.TestCase, Utilities): links = [ - 'dummy98', 'veth99', 'vrf99'] From 2629df47107bca109f4aaf2b1e4ff351d0b0cdf0 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 24 May 2019 13:59:34 +0900 Subject: [PATCH 14/18] test-network: use wait_online() instead of waiting for 10 seconds But some tests requires both IPv4 and IPv6 addresses are configured. Only in that case wait for addresses to be configured. --- ...hcp-client-gateway-onlink-implicit.network | 1 + test/test-network/systemd-networkd-tests.py | 129 ++++++++++-------- 2 files changed, 72 insertions(+), 58 deletions(-) diff --git a/test/test-network/conf/dhcp-client-gateway-onlink-implicit.network b/test/test-network/conf/dhcp-client-gateway-onlink-implicit.network index 35aa8e7f50..63e6e3a048 100644 --- a/test/test-network/conf/dhcp-client-gateway-onlink-implicit.network +++ b/test/test-network/conf/dhcp-client-gateway-onlink-implicit.network @@ -3,6 +3,7 @@ Name=veth99 [Network] DHCP=ipv4 +IPv6AcceptRA=no [Route] Gateway=192.168.0.1 diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index d9b4b51ee4..101fd3d323 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -164,8 +164,6 @@ class Utilities(): dnsmasq_command = f'dnsmasq -8 /var/run/networkd-ci/test-dnsmasq-log-file --log-queries=extra --log-dhcp --pid-file=/var/run/networkd-ci/test-test-dnsmasq.pid --conf-file=/dev/null --interface=veth-peer --enable-ra --dhcp-range={ipv6_range},{lease_time} --dhcp-range={ipv4_range},{lease_time} -R --dhcp-leasefile=/var/run/networkd-ci/lease --dhcp-option=26,1492 --dhcp-option=option:router,192.168.5.1 --dhcp-option=33,192.168.5.4,192.168.5.5 --port=0 ' + additional_options subprocess.check_call(dnsmasq_command, shell=True) - time.sleep(10) - def stop_dnsmasq(self, pid_file): if os.path.exists(pid_file): with open(pid_file, 'r') as f: @@ -230,6 +228,16 @@ class Utilities(): def check_operstate(self, link, expected, show_status=True, setup_state='configured'): self.assertRegex(self.get_operstate(link, show_status, setup_state), expected) + def wait_address(self, link, address_regex, scope='global', ipv='', timeout_sec=10): + for i in range(timeout_sec): + if i > 0: + time.sleep(1) + output = subprocess.check_output(['ip', ipv, 'address', 'show', 'dev', link, 'scope', scope], universal_newlines=True).rstrip() + if re.search(address_regex, output): + break + else: + self.assertRegex(output, address_regex) + class NetworkdNetDevTests(unittest.TestCase, Utilities): @@ -1935,11 +1943,11 @@ class NetworkdNetworkDHCPClientTests(unittest.TestCase, Utilities): def test_dhcp_client_ipv4_only(self): self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-ipv4-only-ipv6-disabled.network') - self.start_networkd() - - self.check_link_exists('veth99') + self.start_networkd(0) + self.wait_online(['veth-peer:carrier']) self.start_dnsmasq() + self.wait_online(['veth99:routable', 'veth-peer:routable']) output = subprocess.check_output(['networkctl', 'status', 'veth99'], universal_newlines=True).rstrip() print(output) @@ -1949,11 +1957,14 @@ class NetworkdNetworkDHCPClientTests(unittest.TestCase, Utilities): def test_dhcp_client_ipv4_ipv6(self): self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-ipv6-only.network', 'dhcp-client-ipv4-only.network') - self.start_networkd() - - self.check_link_exists('veth99') - + self.start_networkd(0) + self.wait_online(['veth-peer:carrier']) self.start_dnsmasq() + self.wait_online(['veth99:routable', 'veth-peer:routable']) + + # link become 'routable' when at least one protocol provide an valid address. + self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 brd 192.168.5.255', ipv='-4') + self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128', ipv='-6') output = subprocess.check_output(['networkctl', 'status', 'veth99'], universal_newlines=True).rstrip() print(output) @@ -1962,11 +1973,11 @@ class NetworkdNetworkDHCPClientTests(unittest.TestCase, Utilities): def test_dhcp_client_settings(self): self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-ipv4-dhcp-settings.network') - self.start_networkd() - - self.check_link_exists('veth99') + self.start_networkd(0) + self.wait_online(['veth-peer:carrier']) self.start_dnsmasq() + self.wait_online(['veth99:routable', 'veth-peer:routable']) print('## ip address show dev veth99') output = subprocess.check_output(['ip', 'address', 'show', 'dev', 'veth99'], universal_newlines=True).rstrip() @@ -1996,11 +2007,10 @@ class NetworkdNetworkDHCPClientTests(unittest.TestCase, Utilities): def test_dhcp6_client_settings_rapidcommit_true(self): self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-ipv6-only.network') - self.start_networkd() - - self.check_link_exists('veth99') - + self.start_networkd(0) + self.wait_online(['veth-peer:carrier']) self.start_dnsmasq() + self.wait_online(['veth99:routable', 'veth-peer:routable']) output = subprocess.check_output(['ip', 'address', 'show', 'dev', 'veth99'], universal_newlines=True).rstrip() print(output) @@ -2009,11 +2019,10 @@ class NetworkdNetworkDHCPClientTests(unittest.TestCase, Utilities): def test_dhcp6_client_settings_rapidcommit_false(self): self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-ipv6-rapid-commit.network') - self.start_networkd() - - self.check_link_exists('veth99') - + self.start_networkd(0) + self.wait_online(['veth-peer:carrier']) self.start_dnsmasq() + self.wait_online(['veth99:routable', 'veth-peer:routable']) output = subprocess.check_output(['ip', 'address', 'show', 'dev', 'veth99'], universal_newlines=True).rstrip() print(output) @@ -2022,11 +2031,10 @@ class NetworkdNetworkDHCPClientTests(unittest.TestCase, Utilities): def test_dhcp_client_settings_anonymize(self): self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-anonymize.network') - self.start_networkd() - - self.check_link_exists('veth99') - + self.start_networkd(0) + self.wait_online(['veth-peer:carrier']) self.start_dnsmasq() + self.wait_online(['veth99:routable', 'veth-peer:routable']) self.assertFalse(self.search_words_in_dnsmasq_log('VendorClassIdentifier=SusantVendorTest', True)) self.assertFalse(self.search_words_in_dnsmasq_log('test-hostname')) @@ -2034,11 +2042,14 @@ class NetworkdNetworkDHCPClientTests(unittest.TestCase, Utilities): def test_dhcp_client_listen_port(self): self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-listen-port.network') - self.start_networkd() - - self.check_link_exists('veth99') - + self.start_networkd(0) + self.wait_online(['veth-peer:carrier']) self.start_dnsmasq('--dhcp-alternate-port=67,5555') + self.wait_online(['veth99:routable', 'veth-peer:routable']) + + # link become 'routable' when at least one protocol provide an valid address. + self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 brd 192.168.5.255', ipv='-4') + self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128', ipv='-6') output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'veth99'], universal_newlines=True).rstrip() print(output) @@ -2046,11 +2057,10 @@ class NetworkdNetworkDHCPClientTests(unittest.TestCase, Utilities): def test_dhcp_route_table_id(self): self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-v4-server-veth-peer.network', 'dhcp-client-route-table.network') - self.start_networkd() - - self.check_link_exists('veth99') - + self.start_networkd(0) + self.wait_online(['veth-peer:carrier']) self.start_dnsmasq() + self.wait_online(['veth99:routable', 'veth-peer:routable']) output = subprocess.check_output(['ip', 'route', 'show', 'table', '12'], universal_newlines=True).rstrip() print(output) @@ -2059,11 +2069,10 @@ class NetworkdNetworkDHCPClientTests(unittest.TestCase, Utilities): def test_dhcp_route_metric(self): self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-v4-server-veth-peer.network', 'dhcp-client-route-metric.network') - self.start_networkd() - - self.check_link_exists('veth99') - + self.start_networkd(0) + self.wait_online(['veth-peer:carrier']) self.start_dnsmasq() + self.wait_online(['veth99:routable', 'veth-peer:routable']) output = subprocess.check_output(['ip', 'route', 'show', 'dev', 'veth99'], universal_newlines=True).rstrip() print(output) @@ -2071,11 +2080,10 @@ class NetworkdNetworkDHCPClientTests(unittest.TestCase, Utilities): def test_dhcp_route_criticalconnection_true(self): self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-v4-server-veth-peer.network', 'dhcp-client-critical-connection.network') - self.start_networkd() - - self.check_link_exists('veth99') - + self.start_networkd(0) + self.wait_online(['veth-peer:carrier']) self.start_dnsmasq() + self.wait_online(['veth99:routable', 'veth-peer:routable']) output = subprocess.check_output(['networkctl', 'status', 'veth99'], universal_newlines=True).rstrip() print(output) @@ -2093,19 +2101,22 @@ class NetworkdNetworkDHCPClientTests(unittest.TestCase, Utilities): def test_dhcp_client_reuse_address_as_static(self): self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client.network') - self.start_networkd() - - self.check_link_exists('veth99') - + self.start_networkd(0) + self.wait_online(['veth-peer:carrier']) self.start_dnsmasq() + self.wait_online(['veth99:routable', 'veth-peer:routable']) + + # link become 'routable' when at least one protocol provide an valid address. + self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 brd 192.168.5.255', ipv='-4') + self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128', ipv='-6') output = subprocess.check_output(['ip', 'address', 'show', 'dev', 'veth99', 'scope', 'global'], universal_newlines=True).rstrip() print(output) self.assertRegex(output, '192.168.5') self.assertRegex(output, '2600::') - ipv4_address = re.search('192\.168\.5\.[0-9]*/24', output) - ipv6_address = re.search('2600::[0-9a-f:]*/128', output) + ipv4_address = re.search(r'192.168.5.[0-9]*/24', output) + ipv6_address = re.search(r'2600::[0-9a-f:]*/128', output) static_network = '\n'.join(['[Match]', 'Name=veth99', '[Network]', 'IPv6AcceptRA=no', 'Address=' + ipv4_address.group(), 'Address=' + ipv6_address.group()]) print(static_network) @@ -2114,9 +2125,10 @@ class NetworkdNetworkDHCPClientTests(unittest.TestCase, Utilities): with open(os.path.join(network_unit_file_path, 'static.network'), mode='w') as f: f.write(static_network) - self.start_networkd() - - self.check_link_exists('veth99') + # When networkd started, the links are already configured, so let's wait for 5 seconds + # the links to be re-configured. + self.start_networkd(5) + self.wait_online(['veth99:routable', 'veth-peer:routable']) output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'veth99', 'scope', 'global'], universal_newlines=True).rstrip() print(output) @@ -2132,12 +2144,14 @@ class NetworkdNetworkDHCPClientTests(unittest.TestCase, Utilities): def test_dhcp_client_vrf(self): self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-vrf.network', '25-vrf.netdev', '25-vrf.network') - self.start_networkd() - - self.check_link_exists('veth99') - self.check_link_exists('vrf99') - + self.start_networkd(0) + self.wait_online(['veth-peer:carrier']) self.start_dnsmasq() + self.wait_online(['veth99:routable', 'veth-peer:routable', 'vrf99:carrier']) + + # link become 'routable' when at least one protocol provide an valid address. + self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 brd 192.168.5.255', ipv='-4') + self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128', ipv='-6') print('## ip -d link show dev vrf99') output = subprocess.check_output(['ip', '-d', 'link', 'show', 'dev', 'vrf99'], universal_newlines=True).rstrip() @@ -2178,11 +2192,10 @@ class NetworkdNetworkDHCPClientTests(unittest.TestCase, Utilities): def test_dhcp_client_gateway_onlink_implicit(self): self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-gateway-onlink-implicit.network') - self.start_networkd() - - self.check_link_exists('veth99') - + self.start_networkd(0) + self.wait_online(['veth-peer:carrier']) self.start_dnsmasq() + self.wait_online(['veth99:routable', 'veth-peer:routable']) output = subprocess.check_output(['networkctl', 'status', 'veth99'], universal_newlines=True).rstrip() print(output) From be68c2c9653138238357da45c431e521ff7d7139 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 24 May 2019 15:17:41 +0900 Subject: [PATCH 15/18] test-network: rename needlessly long class names --- test/test-network/systemd-networkd-tests.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index 101fd3d323..af3993d465 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -1083,7 +1083,7 @@ class NetworkdL2TPTests(unittest.TestCase, Utilities): self.assertRegex(output, "Peer session 28, tunnel 12") self.assertRegex(output, "interface name: l2tp-ses4") -class NetworkdNetWorkTests(unittest.TestCase, Utilities): +class NetworkdNetworkTests(unittest.TestCase, Utilities): links = [ 'bond199', 'dummy98', @@ -1505,7 +1505,7 @@ class NetworkdNetWorkTests(unittest.TestCase, Utilities): self.assertRegex(output, 'inet 192.168.10.30/24 brd 192.168.10.255 scope global test1') self.check_operstate('test1', 'routable') -class NetworkdNetWorkBondTests(unittest.TestCase, Utilities): +class NetworkdBondTests(unittest.TestCase, Utilities): links = [ 'bond199', 'bond99', @@ -1611,7 +1611,7 @@ class NetworkdNetWorkBondTests(unittest.TestCase, Utilities): # Let's confirm that networkd's operstate is consistent with ip's result. self.assertNotRegex(output, 'NO-CARRIER') -class NetworkdNetWorkBridgeTests(unittest.TestCase, Utilities): +class NetworkdBridgeTests(unittest.TestCase, Utilities): links = [ 'bridge99', 'dummy98', @@ -1772,7 +1772,7 @@ class NetworkdNetWorkBridgeTests(unittest.TestCase, Utilities): subprocess.call(['ip', 'rule', 'del', 'table', '100']) -class NetworkdNetWorkLLDPTests(unittest.TestCase, Utilities): +class NetworkdLLDPTests(unittest.TestCase, Utilities): links = ['veth99'] units = [ @@ -1798,7 +1798,7 @@ class NetworkdNetWorkLLDPTests(unittest.TestCase, Utilities): self.assertRegex(output, 'veth-peer') self.assertRegex(output, 'veth99') -class NetworkdNetworkRATests(unittest.TestCase, Utilities): +class NetworkdRATests(unittest.TestCase, Utilities): links = ['veth99'] units = [ @@ -1824,7 +1824,7 @@ class NetworkdNetworkRATests(unittest.TestCase, Utilities): print(output) self.assertRegex(output, '2002:da8:1:0') -class NetworkdNetworkDHCPServerTests(unittest.TestCase, Utilities): +class NetworkdDHCPServerTests(unittest.TestCase, Utilities): links = [ 'dummy98', 'veth99'] @@ -1884,7 +1884,7 @@ class NetworkdNetworkDHCPServerTests(unittest.TestCase, Utilities): self.assertRegex(output, '192.168.5.*') self.assertRegex(output, 'Europe/Berlin') -class NetworkdNetworkDHCPClientTests(unittest.TestCase, Utilities): +class NetworkdDHCPClientTests(unittest.TestCase, Utilities): links = [ 'veth99', 'vrf99'] From fdcd1ec54ab5bc9a8b8edd07ce3f94a5769cf1fe Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 24 May 2019 15:34:19 +0900 Subject: [PATCH 16/18] test-network: move test_domain() to NetworkdNetworkTests class --- test/test-network/systemd-networkd-tests.py | 30 +++++++++------------ 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index af3993d465..ad3860f17d 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -1094,6 +1094,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): '11-dummy.netdev', '12-dummy.netdev', '23-active-slave.network', + '24-search-domain.network', '25-address-link-section.network', '25-address-preferred-lifetime-zero-ipv6.network', '25-address-static.network', @@ -1505,6 +1506,17 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): self.assertRegex(output, 'inet 192.168.10.30/24 brd 192.168.10.255 scope global test1') self.check_operstate('test1', 'routable') + def test_domain(self): + self.copy_unit_to_networkd_unit_path('12-dummy.netdev', '24-search-domain.network') + self.start_networkd(0) + self.wait_online(['dummy98:routable']) + + output = subprocess.check_output(['networkctl', 'status', 'dummy98'], universal_newlines=True).rstrip() + print(output) + self.assertRegex(output, 'Address: 192.168.42.100') + self.assertRegex(output, 'DNS: 192.168.42.1') + self.assertRegex(output, 'Search Domains: one') + class NetworkdBondTests(unittest.TestCase, Utilities): links = [ 'bond199', @@ -1825,13 +1837,9 @@ class NetworkdRATests(unittest.TestCase, Utilities): self.assertRegex(output, '2002:da8:1:0') class NetworkdDHCPServerTests(unittest.TestCase, Utilities): - links = [ - 'dummy98', - 'veth99'] + links = ['veth99'] units = [ - '12-dummy.netdev', - '24-search-domain.network', '25-veth.netdev', 'dhcp-client.network', 'dhcp-client-timezone-router.network', @@ -1859,18 +1867,6 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities): self.assertRegex(output, 'DNS: 192.168.5.1') self.assertRegex(output, 'NTP: 192.168.5.1') - def test_domain(self): - self.copy_unit_to_networkd_unit_path('12-dummy.netdev', '24-search-domain.network') - self.start_networkd() - - self.check_link_exists('dummy98') - - output = subprocess.check_output(['networkctl', 'status', 'dummy98'], universal_newlines=True).rstrip() - print(output) - self.assertRegex(output, 'Address: 192.168.42.100') - self.assertRegex(output, 'DNS: 192.168.42.1') - self.assertRegex(output, 'Search Domains: one') - def test_emit_router_timezone(self): self.warn_about_firewalld() self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-client-timezone-router.network', 'dhcp-server-timezone-router.network') From 8ad8292452a708f42b67cda6e9b2074b803197c4 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 24 May 2019 15:43:14 +0900 Subject: [PATCH 17/18] test-network: use wait_online() in NetworkdDHCPServerTests or friends --- test/test-network/systemd-networkd-tests.py | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index ad3860f17d..c0d7c9cbe0 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -1801,9 +1801,8 @@ class NetworkdLLDPTests(unittest.TestCase, Utilities): def test_lldp(self): self.copy_unit_to_networkd_unit_path('23-emit-lldp.network', '24-lldp.network', '25-veth.netdev') - self.start_networkd() - - self.check_link_exists('veth99') + self.start_networkd(0) + self.wait_online(['veth99:degraded', 'veth-peer:degraded']) output = subprocess.check_output(['networkctl', 'lldp'], universal_newlines=True).rstrip() print(output) @@ -1828,9 +1827,8 @@ class NetworkdRATests(unittest.TestCase, Utilities): def test_ipv6_prefix_delegation(self): self.warn_about_firewalld() self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'ipv6-prefix.network', 'ipv6-prefix-veth.network') - self.start_networkd() - - self.check_link_exists('veth99') + self.start_networkd(0) + self.wait_online(['veth99:routable', 'veth-peer:degraded']) output = subprocess.check_output(['networkctl', 'status', 'veth99'], universal_newlines=True).rstrip() print(output) @@ -1856,9 +1854,8 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities): def test_dhcp_server(self): self.warn_about_firewalld() self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-client.network', 'dhcp-server.network') - self.start_networkd() - - self.check_link_exists('veth99') + self.start_networkd(0) + self.wait_online(['veth99:routable', 'veth-peer:routable']) output = subprocess.check_output(['networkctl', 'status', 'veth99'], universal_newlines=True).rstrip() print(output) @@ -1870,9 +1867,8 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities): def test_emit_router_timezone(self): self.warn_about_firewalld() self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-client-timezone-router.network', 'dhcp-server-timezone-router.network') - self.start_networkd() - - self.check_link_exists('veth99') + self.start_networkd(0) + self.wait_online(['veth99:routable', 'veth-peer:routable']) output = subprocess.check_output(['networkctl', 'status', 'veth99'], universal_newlines=True).rstrip() print(output) From efdb62dfd04f92a7714777046fd1ba68a84c9f63 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 28 May 2019 05:45:10 +0900 Subject: [PATCH 18/18] network: do not send DHCPRELEASE when CriticalConnection= is enabled --- src/network/networkd-dhcp4.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index 8bcd345731..4ed020ea8e 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -649,15 +649,28 @@ static int dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) { return log_link_warning_errno(link, r, "Could not acquire IPv4 link-local address: %m"); } + if (link->network->dhcp_critical) { + log_link_notice(link, "DHCPv4 connection considered critical, ignoring request to reconfigure it."); + return 0; + } + if (link->network->dhcp_send_release) (void) sd_dhcp_client_send_release(client); - _fallthrough_; + if (link->dhcp_lease) { + r = dhcp_lease_lost(link); + if (r < 0) { + link_enter_failed(link); + return r; + } + } + + break; case SD_DHCP_CLIENT_EVENT_EXPIRED: case SD_DHCP_CLIENT_EVENT_IP_CHANGE: if (link->network->dhcp_critical) { - log_link_notice(link, "DHCPv4 connection considered system critical, ignoring request to reconfigure it."); + log_link_notice(link, "DHCPv4 connection considered critical, ignoring request to reconfigure it."); return 0; }