From 3606ca659ec305a9ac73d9436d3f087db899d9ac Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 3 Jun 2020 10:11:08 +0900 Subject: [PATCH 01/18] network: add debug log for configuring address --- src/network/networkd-address.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 2c36854588..048d3ff183 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -609,6 +609,13 @@ int address_configure( if (r < 0) return log_link_error_errno(link, r, "Failed to acquire an address from pool: %m"); + if (DEBUG_LOGGING) { + _cleanup_free_ char *str = NULL; + + (void) in_addr_to_string(address->family, &address->in_addr, &str); + log_link_debug(link, "%s address: %s", update ? "Updating" : "Configuring", strna(str)); + } + if (update) r = sd_rtnl_message_new_addr_update(link->manager->rtnl, &req, link->ifindex, address->family); From c9d223e803092ea6ba1899f44fa48a01f5ba0bd3 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Thu, 4 Jun 2020 13:21:02 +0900 Subject: [PATCH 02/18] test-network: add tests for prefix routes --- .../conf/25-prefix-route-with-vrf.network | 26 +++++++ .../conf/25-prefix-route-without-vrf.network | 25 +++++++ test/test-network/systemd-networkd-tests.py | 74 +++++++++++++++++++ 3 files changed, 125 insertions(+) create mode 100644 test/test-network/conf/25-prefix-route-with-vrf.network create mode 100644 test/test-network/conf/25-prefix-route-without-vrf.network diff --git a/test/test-network/conf/25-prefix-route-with-vrf.network b/test/test-network/conf/25-prefix-route-with-vrf.network new file mode 100644 index 0000000000..fdc1e11c5e --- /dev/null +++ b/test/test-network/conf/25-prefix-route-with-vrf.network @@ -0,0 +1,26 @@ +[Match] +Name=dummy98 + +[Network] +IPv6AcceptRA=no +VRF=vrf99 +Address=fdde:11:22::1/128 +Address=fdde:11:33::1/64 +Address=10.20.22.1/32 +Address=10.20.33.1/24 + +[Address] +Address=fdde:11:44::1/128 +AddPrefixRoute=no + +[Address] +Address=fdde:11:55::1/64 +AddPrefixRoute=no + +[Address] +Address=10.20.44.1/32 +AddPrefixRoute=no + +[Address] +Address=10.20.55.1/24 +AddPrefixRoute=no diff --git a/test/test-network/conf/25-prefix-route-without-vrf.network b/test/test-network/conf/25-prefix-route-without-vrf.network new file mode 100644 index 0000000000..9354b55d5c --- /dev/null +++ b/test/test-network/conf/25-prefix-route-without-vrf.network @@ -0,0 +1,25 @@ +[Match] +Name=test1 + +[Network] +IPv6AcceptRA=no +Address=fdde:12:22::1/128 +Address=fdde:12:33::1/64 +Address=10.21.22.1/32 +Address=10.21.33.1/24 + +[Address] +Address=fdde:12:44::1/128 +AddPrefixRoute=no + +[Address] +Address=fdde:12:55::1/64 +AddPrefixRoute=no + +[Address] +Address=10.21.44.1/32 +AddPrefixRoute=no + +[Address] +Address=10.21.55.1/24 +AddPrefixRoute=no diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index efd3db8f44..0ca1fb3bf9 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -1717,6 +1717,8 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): '25-qdisc-ingress-netem-compat.network', '25-qdisc-pie.network', '25-qdisc-qfq.network', + '25-prefix-route-with-vrf.network', + '25-prefix-route-without-vrf.network', '25-route-ipv6-src.network', '25-route-static.network', '25-route-vrf.network', @@ -1729,6 +1731,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): '25-veth-peer.network', '25-veth.netdev', '25-vrf.netdev', + '25-vrf.network', '26-link-local-addressing-ipv6.network', 'routing-policy-rule-dummy98.network', 'routing-policy-rule-test1.network'] @@ -1815,6 +1818,77 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): print(output) self.assertNotRegex(output, '192.168.100.10/24') + @expectedFailureIfModuleIsNotAvailable('vrf') + def test_prefix_route(self): + copy_unit_to_networkd_unit_path('25-prefix-route-with-vrf.network', '12-dummy.netdev', + '25-prefix-route-without-vrf.network', '11-dummy.netdev', + '25-vrf.netdev', '25-vrf.network') + for trial in range(2): + if trial == 0: + start_networkd() + else: + restart_networkd(3) + + self.wait_online(['dummy98:routable', 'test1:routable', 'vrf99:carrier']) + + output = check_output('ip route show table 42 dev dummy98') + print('### ip route show table 42 dev dummy98') + print(output) + self.assertRegex(output, 'local 10.20.22.1 proto kernel scope host src 10.20.22.1') + self.assertRegex(output, 'broadcast 10.20.33.0 proto kernel scope link src 10.20.33.1') + self.assertRegex(output, '10.20.33.0/24 proto kernel scope link src 10.20.33.1') + self.assertRegex(output, 'local 10.20.33.1 proto kernel scope host src 10.20.33.1') + self.assertRegex(output, 'broadcast 10.20.33.255 proto kernel scope link src 10.20.33.1') + self.assertRegex(output, 'local 10.20.44.1 proto kernel scope host src 10.20.44.1') + self.assertRegex(output, 'broadcast 10.20.55.0 proto kernel scope link src 10.20.55.1') + self.assertRegex(output, 'local 10.20.55.1 proto kernel scope host src 10.20.55.1') + self.assertRegex(output, 'broadcast 10.20.55.255 proto kernel scope link src 10.20.55.1') + output = check_output('ip -6 route show table 42 dev dummy98') + print('### ip -6 route show table 42 dev dummy98') + print(output) + if trial == 0: + # Kernel's bug? + self.assertRegex(output, 'local fdde:11:22::1 proto kernel metric 0 pref medium') + #self.assertRegex(output, 'fdde:11:22::1 proto kernel metric 256 pref medium') + self.assertRegex(output, 'local fdde:11:33::1 proto kernel metric 0 pref medium') + self.assertRegex(output, 'fdde:11:33::/64 proto kernel metric 256 pref medium') + self.assertRegex(output, 'local fdde:11:44::1 proto kernel metric 0 pref medium') + self.assertRegex(output, 'local fdde:11:55::1 proto kernel metric 0 pref medium') + self.assertRegex(output, 'fe80::/64 proto kernel metric 256 pref medium') + self.assertRegex(output, 'ff00::/8 metric 256 pref medium') + + print() + + output = check_output('ip route show dev test1') + print('### ip route show dev test1') + print(output) + self.assertRegex(output, '10.21.33.0/24 proto kernel scope link src 10.21.33.1') + output = check_output('ip route show table local dev test1') + print('### ip route show table local dev test1') + print(output) + self.assertRegex(output, 'local 10.21.22.1 proto kernel scope host src 10.21.22.1') + self.assertRegex(output, 'broadcast 10.21.33.0 proto kernel scope link src 10.21.33.1') + self.assertRegex(output, 'local 10.21.33.1 proto kernel scope host src 10.21.33.1') + self.assertRegex(output, 'broadcast 10.21.33.255 proto kernel scope link src 10.21.33.1') + self.assertRegex(output, 'local 10.21.44.1 proto kernel scope host src 10.21.44.1') + self.assertRegex(output, 'broadcast 10.21.55.0 proto kernel scope link src 10.21.55.1') + self.assertRegex(output, 'local 10.21.55.1 proto kernel scope host src 10.21.55.1') + self.assertRegex(output, 'broadcast 10.21.55.255 proto kernel scope link src 10.21.55.1') + output = check_output('ip -6 route show dev test1') + print('### ip -6 route show dev test1') + print(output) + self.assertRegex(output, 'fdde:12:22::1 proto kernel metric 256 pref medium') + self.assertRegex(output, 'fdde:12:33::/64 proto kernel metric 256 pref medium') + self.assertRegex(output, 'fe80::/64 proto kernel metric 256 pref medium') + output = check_output('ip -6 route show table local dev test1') + print('### ip -6 route show table local dev test1') + print(output) + self.assertRegex(output, 'local fdde:12:22::1 proto kernel metric 0 pref medium') + self.assertRegex(output, 'local fdde:12:33::1 proto kernel metric 0 pref medium') + self.assertRegex(output, 'local fdde:12:44::1 proto kernel metric 0 pref medium') + self.assertRegex(output, 'local fdde:12:55::1 proto kernel metric 0 pref medium') + self.assertRegex(output, 'ff00::/8 metric 256 pref medium') + def test_configure_without_carrier(self): copy_unit_to_networkd_unit_path('11-dummy.netdev') start_networkd() From 97f000744f967f6849798be3f82e7fbac589963a Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 21 Jul 2020 23:02:35 +0900 Subject: [PATCH 03/18] network: introduce callback called when an address becomes ready --- src/network/networkd-address.c | 22 ++++++++++++++-------- src/network/networkd-address.h | 4 ++++ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 048d3ff183..216c425b34 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -359,15 +359,21 @@ int address_update( link_update_operstate(address->link, true); link_check_ready(address->link); - if (!ready && - address_is_ready(address) && - address->family == AF_INET6 && - in_addr_is_link_local(AF_INET6, &address->in_addr) > 0 && - in_addr_is_null(AF_INET6, (const union in_addr_union*) &address->link->ipv6ll_address) > 0) { + if (!ready && address_is_ready(address)) { + if (address->callback) { + r = address->callback(address); + if (r < 0) + return r; + } - r = link_ipv6ll_gained(address->link, &address->in_addr.in6); - if (r < 0) - return r; + if (address->family == AF_INET6 && + in_addr_is_link_local(AF_INET6, &address->in_addr) > 0 && + IN6_IS_ADDR_UNSPECIFIED(&address->link->ipv6ll_address) > 0) { + + r = link_ipv6ll_gained(address->link, &address->in_addr.in6); + if (r < 0) + return r; + } } return 0; diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h index d55059ee25..18d2546553 100644 --- a/src/network/networkd-address.h +++ b/src/network/networkd-address.h @@ -20,6 +20,7 @@ typedef struct Address Address; typedef struct Network Network; typedef struct Link Link; typedef struct NetworkConfigSection NetworkConfigSection; +typedef int (*address_ready_callback_t)(Address *address); struct Address { Network *network; @@ -47,6 +48,9 @@ struct Address { bool autojoin:1; AddressFamily duplicate_address_detection; + /* Called when address become ready */ + address_ready_callback_t callback; + sd_ipv4acd *acd; LIST_FIELDS(Address, addresses); From 5d8c3ec1e92ffd4463c85ab057d6532b5950a6d9 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 24 Jul 2020 03:16:42 +0900 Subject: [PATCH 04/18] network: make link enter failed state if address_update() failed --- src/network/networkd-manager.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index 59ec0de8d3..f2c956c70f 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -857,8 +857,10 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, valid_str ? "for " : "forever", strempty(valid_str)); } - /* address_update() logs internally, so we don't need to. */ - (void) address_update(address, flags, scope, &cinfo); + /* address_update() logs internally, so we don't need to here. */ + r = address_update(address, flags, scope, &cinfo); + if (r < 0) + link_enter_failed(link); break; From 3336e946daab7417fc33170ca4d77581dd799748 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 22 Jul 2020 06:03:10 +0900 Subject: [PATCH 05/18] network: ndisc: do not set configured flags when addresses or routes are not assigned yet Just for safety. --- src/network/networkd-ndisc.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index a91c099996..dcfe3e726c 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -819,10 +819,11 @@ static void ndisc_handler(sd_ndisc *nd, sd_ndisc_event event, sd_ndisc_router *r case SD_NDISC_EVENT_TIMEOUT: log_link_debug(link, "NDisc handler get timeout event"); - link->ndisc_addresses_configured = true; - link->ndisc_routes_configured = true; - link_check_ready(link); - + if (link->ndisc_addresses_messages == 0 && link->ndisc_routes_messages == 0) { + link->ndisc_addresses_configured = true; + link->ndisc_routes_configured = true; + link_check_ready(link); + } break; default: assert_not_reached("Unknown NDisc event"); From 5f58af25e6c3b449b6b6657d413f98002bc3f0b7 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 22 Jul 2020 06:07:42 +0900 Subject: [PATCH 06/18] network: make link_check_ready() return earlier if the link is not in 'configuring' state --- src/network/networkd-link.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index dd98bf2685..15cfe39553 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -1086,8 +1086,11 @@ void link_check_ready(Link *link) { assert(link); - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) { - log_link_debug(link, "%s(): link is in failed or linger state.", __func__); + if (link->state == LINK_STATE_CONFIGURED) + return; + + if (link->state != LINK_STATE_CONFIGURING) { + log_link_debug(link, "%s(): link is in %s state.", __func__, link_state_to_string(link->state)); return; } @@ -1189,8 +1192,7 @@ void link_check_ready(Link *link) { } } - if (link->state != LINK_STATE_CONFIGURED) - link_enter_configured(link); + link_enter_configured(link); return; } From 8eec0b9da598ba3b0042b82775ff6130a38f390f Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 22 Jul 2020 08:22:55 +0900 Subject: [PATCH 07/18] network: set key destructor in several hash_ops --- src/network/networkd-address.c | 2 +- src/network/networkd-link.c | 16 ++++++++-------- src/network/networkd-manager.c | 6 +++--- src/network/networkd-neighbor.c | 2 +- src/network/networkd-routing-policy-rule.c | 7 ++++++- src/network/test-routing-policy-rule.c | 2 +- 6 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 216c425b34..96782d608a 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -205,7 +205,7 @@ static int address_compare_func(const Address *a1, const Address *a2) { } } -DEFINE_HASH_OPS(address_hash_ops, Address, address_hash_func, address_compare_func); +DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(address_hash_ops, Address, address_hash_func, address_compare_func, address_free); bool address_equal(Address *a1, Address *a2) { if (a1 == a2) diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 15cfe39553..0acd35444e 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -711,17 +711,17 @@ static Link *link_free(Link *link) { link_ntp_settings_clear(link); link_dns_settings_clear(link); - link->routes = set_free_with_destructor(link->routes, route_free); - link->routes_foreign = set_free_with_destructor(link->routes_foreign, route_free); + link->routes = set_free(link->routes); + link->routes_foreign = set_free(link->routes_foreign); - link->nexthops = set_free_with_destructor(link->nexthops, nexthop_free); - link->nexthops_foreign = set_free_with_destructor(link->nexthops_foreign, nexthop_free); + link->nexthops = set_free(link->nexthops); + link->nexthops_foreign = set_free(link->nexthops_foreign); - link->neighbors = set_free_with_destructor(link->neighbors, neighbor_free); - link->neighbors_foreign = set_free_with_destructor(link->neighbors_foreign, neighbor_free); + link->neighbors = set_free(link->neighbors); + link->neighbors_foreign = set_free(link->neighbors_foreign); - link->addresses = set_free_with_destructor(link->addresses, address_free); - link->addresses_foreign = set_free_with_destructor(link->addresses_foreign, address_free); + link->addresses = set_free(link->addresses); + link->addresses_foreign = set_free(link->addresses_foreign); while ((address = link->pool_addresses)) { LIST_REMOVE(addresses, link->pool_addresses, address); diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index f2c956c70f..a67ff250ee 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -1859,9 +1859,9 @@ void manager_free(Manager *m) { /* routing_policy_rule_free() access m->rules and m->rules_foreign. * So, it is necessary to set NULL after the sets are freed. */ - m->rules = set_free_with_destructor(m->rules, routing_policy_rule_free); - m->rules_foreign = set_free_with_destructor(m->rules_foreign, routing_policy_rule_free); - set_free_with_destructor(m->rules_saved, routing_policy_rule_free); + m->rules = set_free(m->rules); + m->rules_foreign = set_free(m->rules_foreign); + set_free(m->rules_saved); sd_netlink_unref(m->rtnl); sd_netlink_unref(m->genl); diff --git a/src/network/networkd-neighbor.c b/src/network/networkd-neighbor.c index 0e97b225c4..09ddb9c8a5 100644 --- a/src/network/networkd-neighbor.c +++ b/src/network/networkd-neighbor.c @@ -247,7 +247,7 @@ static int neighbor_compare_func(const Neighbor *a, const Neighbor *b) { return memcmp(&a->lladdr, &b->lladdr, a->lladdr_size); } -DEFINE_PRIVATE_HASH_OPS(neighbor_hash_ops, Neighbor, neighbor_hash_func, neighbor_compare_func); +DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(neighbor_hash_ops, Neighbor, neighbor_hash_func, neighbor_compare_func, neighbor_free); int neighbor_get(Link *link, int family, const union in_addr_union *addr, const union lladdr_union *lladdr, size_t lladdr_size, Neighbor **ret) { Neighbor neighbor, *existing; diff --git a/src/network/networkd-routing-policy-rule.c b/src/network/networkd-routing-policy-rule.c index e06d4b070a..36dad527d0 100644 --- a/src/network/networkd-routing-policy-rule.c +++ b/src/network/networkd-routing-policy-rule.c @@ -227,7 +227,12 @@ static int routing_policy_rule_compare_func(const RoutingPolicyRule *a, const Ro } } -DEFINE_PRIVATE_HASH_OPS(routing_policy_rule_hash_ops, RoutingPolicyRule, routing_policy_rule_hash_func, routing_policy_rule_compare_func); +DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR( + routing_policy_rule_hash_ops, + RoutingPolicyRule, + routing_policy_rule_hash_func, + routing_policy_rule_compare_func, + routing_policy_rule_free); int routing_policy_rule_get(Manager *m, RoutingPolicyRule *rule, RoutingPolicyRule **ret) { diff --git a/src/network/test-routing-policy-rule.c b/src/network/test-routing-policy-rule.c index d441099b5a..d84d746c3f 100644 --- a/src/network/test-routing-policy-rule.c +++ b/src/network/test-routing-policy-rule.c @@ -53,7 +53,7 @@ static void test_rule_serialization(const char *title, const char *ruleset, cons log_info("$ %s", cmd); assert_se(system(cmd) == 0); - set_free_with_destructor(rules, routing_policy_rule_free); + set_free(rules); } int main(int argc, char **argv) { From 80b0e8608496df5c9592378fb7d3cc1a9583583f Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 22 Jul 2020 09:41:30 +0900 Subject: [PATCH 08/18] network: make address/route_configure optionally return created Address/Route object --- src/network/networkd-address.c | 11 ++++++++--- src/network/networkd-address.h | 2 +- src/network/networkd-dhcp6.c | 8 ++++---- src/network/networkd-ipv4ll.c | 2 +- src/network/networkd-link.c | 6 +++--- src/network/networkd-ndisc.c | 10 +++++----- src/network/networkd-route.c | 6 +++++- src/network/networkd-route.h | 2 +- 8 files changed, 28 insertions(+), 19 deletions(-) diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 96782d608a..19a007906b 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -592,9 +592,11 @@ int address_configure( Address *address, Link *link, link_netlink_message_handler_t callback, - bool update) { + bool update, + Address **ret) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + Address *a; int r; assert(address); @@ -703,9 +705,9 @@ int address_configure( link_ref(link); if (address->family == AF_INET6 && !in_addr_is_null(address->family, &address->in_addr_peer)) - r = address_add(link, address->family, &address->in_addr_peer, address->prefixlen, NULL); + r = address_add(link, address->family, &address->in_addr_peer, address->prefixlen, &a); else - r = address_add(link, address->family, &address->in_addr, address->prefixlen, NULL); + r = address_add(link, address->family, &address->in_addr, address->prefixlen, &a); if (r < 0) { address_release(address); return log_link_error_errno(link, r, "Could not add address: %m"); @@ -725,6 +727,9 @@ int address_configure( log_link_warning_errno(link, r, "Failed to start IPv4ACD client, ignoring: %m"); } + if (ret) + *ret = a; + return 1; } diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h index 18d2546553..3fc9935d16 100644 --- a/src/network/networkd-address.h +++ b/src/network/networkd-address.h @@ -64,7 +64,7 @@ int address_get(Link *link, int family, const union in_addr_union *in_addr, unsi bool address_exists(Link *link, int family, const union in_addr_union *in_addr); int address_update(Address *address, unsigned char flags, unsigned char scope, const struct ifa_cacheinfo *cinfo); int address_drop(Address *address); -int address_configure(Address *address, Link *link, link_netlink_message_handler_t callback, bool update); +int address_configure(Address *address, Link *link, link_netlink_message_handler_t callback, bool update, Address **ret); int address_remove(Address *address, Link *link, link_netlink_message_handler_t callback); bool address_equal(Address *a1, Address *a2); bool address_is_ready(const Address *a); diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index 1f78e24110..b571d923d5 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -390,7 +390,7 @@ static int dhcp6_lease_pd_prefix_acquired(sd_dhcp6_client *client, Link *link) { route->table = link_get_dhcp_route_table(link); route->type = RTN_UNREACHABLE; - r = route_configure(route, link, dhcp6_route_handler); + r = route_configure(route, link, dhcp6_route_handler, NULL); if (r < 0) { log_link_warning_errno(link, r, "Cannot configure unreachable route for delegated subnet %s/%u: %m", strnull(buf), @@ -581,7 +581,7 @@ static int dhcp6_address_change( "DHCPv6 address %s/%d timeout preferred %d valid %d", strnull(buffer), addr->prefixlen, lifetime_preferred, lifetime_valid); - r = address_configure(addr, link, dhcp6_address_handler, true); + r = address_configure(addr, link, dhcp6_address_handler, true, NULL); if (r < 0) return log_link_warning_errno(link, r, "Could not assign DHCPv6 address: %m"); if (r > 0) @@ -954,7 +954,7 @@ static int dhcp6_prefix_add(Manager *m, struct in6_addr *addr, Link *link) { link->dhcp6_pd_route_configured = false; link_set_state(link, LINK_STATE_CONFIGURING); - r = route_configure(route, link, dhcp6_pd_route_handler); + r = route_configure(route, link, dhcp6_pd_route_handler, NULL); if (r < 0) return r; if (r > 0) @@ -1130,7 +1130,7 @@ static int dhcp6_assign_delegated_prefix(Link *link, link->dhcp6_pd_address_configured = false; link_set_state(link, LINK_STATE_CONFIGURING); - r = address_configure(address, link, dhcp6_pd_address_handler, true); + r = address_configure(address, link, dhcp6_pd_address_handler, true, NULL); if (r < 0) return log_link_warning_errno(link, r, "Failed to set acquired DHCPv6 delegated prefix address: %m"); if (r > 0) diff --git a/src/network/networkd-ipv4ll.c b/src/network/networkd-ipv4ll.c index aaca215e56..e57b9e420f 100644 --- a/src/network/networkd-ipv4ll.c +++ b/src/network/networkd-ipv4ll.c @@ -92,7 +92,7 @@ static int ipv4ll_address_claimed(sd_ipv4ll *ll, Link *link) { ll_addr->broadcast.s_addr = ll_addr->in_addr.in.s_addr | htobe32(0xfffffffflu >> ll_addr->prefixlen); ll_addr->scope = RT_SCOPE_LINK; - r = address_configure(ll_addr, link, ipv4ll_address_handler, false); + r = address_configure(ll_addr, link, ipv4ll_address_handler, false, NULL); if (r < 0) return r; diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 0acd35444e..23870eab3f 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -1061,7 +1061,7 @@ int link_request_set_routes(Link *link) { if ((in_addr_is_null(rt->family, &rt->gw) && ordered_set_isempty(rt->multipath_routes)) != (phase == PHASE_NON_GATEWAY)) continue; - r = route_configure(rt, link, route_handler); + r = route_configure(rt, link, route_handler, NULL); if (r < 0) return log_link_warning_errno(link, r, "Could not set routes: %m"); if (r > 0) @@ -1304,7 +1304,7 @@ static int link_request_set_addresses(Link *link) { else update = address_get(link, ad->family, &ad->in_addr, ad->prefixlen, NULL) > 0; - r = address_configure(ad, link, address_handler, update); + r = address_configure(ad, link, address_handler, update, NULL); if (r < 0) return log_link_warning_errno(link, r, "Could not set addresses: %m"); if (r > 0) @@ -1333,7 +1333,7 @@ static int link_request_set_addresses(Link *link) { return r; address->family = AF_INET6; - r = address_configure(address, link, address_handler, true); + r = address_configure(address, link, address_handler, true, NULL); if (r < 0) return log_link_warning_errno(link, r, "Could not set addresses: %m"); if (r > 0) diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index dcfe3e726c..2f6cc4042e 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -155,7 +155,7 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) { route->lifetime = time_now + lifetime * USEC_PER_SEC; route->mtu = mtu; - r = route_configure(route, link, ndisc_route_handler); + r = route_configure(route, link, ndisc_route_handler, NULL); if (r < 0) return log_link_error_errno(link, r, "Could not set default route: %m"); if (r > 0) @@ -171,7 +171,7 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) { route_gw->gw = gateway; - r = route_configure(route_gw, link, ndisc_route_handler); + r = route_configure(route_gw, link, ndisc_route_handler, NULL); if (r < 0) return log_link_error_errno(link, r, "Could not set gateway: %m"); if (r > 0) @@ -387,7 +387,7 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r address->in_addr.in6 = *a; - r = address_configure(address, link, ndisc_address_handler, true); + r = address_configure(address, link, ndisc_address_handler, true, NULL); if (r < 0) return log_link_error_errno(link, r, "Could not set SLAAC address: %m"); if (r > 0) @@ -435,7 +435,7 @@ static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) { if (r < 0) return log_link_error_errno(link, r, "Failed to get prefix address: %m"); - r = route_configure(route, link, ndisc_route_handler); + r = route_configure(route, link, ndisc_route_handler, NULL); if (r < 0) return log_link_error_errno(link, r, "Could not set prefix route: %m");; if (r > 0) @@ -494,7 +494,7 @@ static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) { if (r < 0) return log_link_error_errno(link, r, "Failed to get route address: %m"); - r = route_configure(route, link, ndisc_route_handler); + r = route_configure(route, link, ndisc_route_handler, NULL); if (r < 0) return log_link_error_errno(link, r, "Could not set additional route: %m"); if (r > 0) diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index 62a9b8b9b3..d8739b7679 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -597,7 +597,8 @@ static int append_nexthops(Route *route, sd_netlink_message *req) { int route_configure( Route *route, Link *link, - link_netlink_message_handler_t callback) { + link_netlink_message_handler_t callback, + Route **ret) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; _cleanup_(sd_event_source_unrefp) sd_event_source *expire = NULL; @@ -803,6 +804,9 @@ int route_configure( sd_event_source_unref(route->expire); route->expire = TAKE_PTR(expire); + if (ret) + *ret = route; + return 1; } diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h index e926ccd7a4..3beee9b03b 100644 --- a/src/network/networkd-route.h +++ b/src/network/networkd-route.h @@ -66,7 +66,7 @@ extern const struct hash_ops route_hash_ops; int route_new(Route **ret); void route_free(Route *route); -int route_configure(Route *route, Link *link, link_netlink_message_handler_t callback); +int route_configure(Route *route, Link *link, link_netlink_message_handler_t callback, Route **ret); int route_remove(Route *route, Link *link, link_netlink_message_handler_t callback); int route_get(Link *link, Route *in, Route **ret); From 6e537f62d71edea5bfcd2e2675c35ee840ffdc1f Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 21 Jul 2020 23:06:51 +0900 Subject: [PATCH 09/18] network: dhcp4: release old lease after the new address become ready Previously, on DHCPv4 address renewal, the old address may be removed while the new address is not ready yet. This also simplifies the logic of removing address and routes. --- src/network/networkd-address.c | 4 + src/network/networkd-dhcp4.c | 468 ++++++++------------------------- src/network/networkd-link.c | 3 +- src/network/networkd-link.h | 5 +- src/network/networkd-route.c | 2 + 5 files changed, 119 insertions(+), 363 deletions(-) diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 19a007906b..6fc3074eac 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -125,6 +125,10 @@ void address_free(Address *address) { if (address->link && !address->acd) { set_remove(address->link->addresses, address); set_remove(address->link->addresses_foreign, address); + if (address->link->dhcp_address == address) + address->link->dhcp_address = NULL; + if (address->link->dhcp_address_old == address) + address->link->dhcp_address_old = NULL; if (in_addr_equal(AF_INET6, &address->in_addr, (const union in_addr_union *) &address->link->ipv6ll_address)) memzero(&address->link->ipv6ll_address, sizeof(struct in6_addr)); diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index e076965ee6..7576aaf62c 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -20,38 +20,58 @@ #include "sysctl-util.h" #include "web-util.h" -static int dhcp_remove_routes(Link *link, sd_dhcp_lease *lease, const struct in_addr *address, bool remove_all, link_netlink_message_handler_t callback); -static int dhcp_remove_router(Link *link, sd_dhcp_lease *lease, const struct in_addr *address, bool remove_all, link_netlink_message_handler_t callback); -static int dhcp_remove_dns_routes(Link *link, sd_dhcp_lease *lease, const struct in_addr *address, bool remove_all, link_netlink_message_handler_t callback); -static int dhcp_remove_address(Link *link, sd_dhcp_lease *lease, const struct in_addr *address, link_netlink_message_handler_t callback); static int dhcp4_update_address(Link *link, bool announce); static int dhcp4_remove_all(Link *link); +static int dhcp4_release_old_lease(Link *link, bool force); -static void dhcp4_release_old_lease(Link *link) { - struct in_addr address = {}, address_old = {}; +static int dhcp4_address_callback(Address *address) { + assert(address); + assert(address->link); + + /* Do not call this callback again. */ + address->callback = NULL; + + return dhcp4_release_old_lease(address->link, true); +} + +static int dhcp4_release_old_lease(Link *link, bool force) { + Route *route; + Iterator i; + int k, r = 0; assert(link); - if (!link->dhcp_lease_old) - return; + if (!link->dhcp_address_old && set_isempty(link->dhcp_routes_old)) + return 0; - assert(link->dhcp_lease); + if (!force && (link->dhcp_address && !address_is_ready(link->dhcp_address))) { + log_link_debug(link, "New DHCPv4 address is not ready. The old lease will be removed later."); + link->dhcp_address->callback = dhcp4_address_callback; + return 0; + } - (void) sd_dhcp_lease_get_address(link->dhcp_lease_old, &address_old); - (void) sd_dhcp_lease_get_address(link->dhcp_lease, &address); + log_link_debug(link, "Removing old DHCPv4 address and routes."); - (void) dhcp_remove_routes(link, link->dhcp_lease_old, &address_old, false, NULL); - (void) dhcp_remove_router(link, link->dhcp_lease_old, &address_old, false, NULL); - (void) dhcp_remove_dns_routes(link, link->dhcp_lease_old, &address_old, false, NULL); - - if (!in4_addr_equal(&address_old, &address)) - (void) dhcp_remove_address(link, link->dhcp_lease_old, &address_old, NULL); - - link->dhcp_lease_old = sd_dhcp_lease_unref(link->dhcp_lease_old); link_dirty(link); + + SET_FOREACH(route, link->dhcp_routes_old, i) { + k = route_remove(route, link, NULL); + if (k < 0) + r = k; + } + + if (link->dhcp_address_old) { + k = address_remove(link->dhcp_address_old, link, NULL); + if (k < 0) + r = k; + } + + return r; } static void dhcp4_check_ready(Link *link) { + int r; + if (link->network->dhcp_send_decline && !link->dhcp4_address_bind) return; @@ -59,8 +79,14 @@ static void dhcp4_check_ready(Link *link) { return; link->dhcp4_configured = true; + /* New address and routes are configured now. Let's release old lease. */ - dhcp4_release_old_lease(link); + r = dhcp4_release_old_lease(link, false); + if (r < 0) { + link_enter_failed(link); + return; + } + link_check_ready(link); } @@ -124,27 +150,25 @@ static bool link_prefixroute(Link *link) { link->manager->dhcp4_prefix_root_cannot_set_table; } -static int dhcp_route_configure(Route **route, Link *link) { +static int dhcp_route_configure(Route *route, Link *link) { + Route *ret; int r; assert(route); - assert(*route); assert(link); - if (set_contains(link->dhcp_routes, *route)) - return 0; - - r = route_configure(*route, link, dhcp4_route_handler); - if (r <= 0) - return r; + r = route_configure(route, link, dhcp4_route_handler, &ret); + if (r < 0) + return log_link_error_errno(link, r, "Failed to set DHCPv4 route: %m"); link->dhcp4_messages++; - r = set_put(link->dhcp_routes, *route); + r = set_ensure_put(&link->dhcp_routes, &route_hash_ops, ret); if (r < 0) - return r; + return log_link_error_errno(link, r, "Failed to store DHCPv4 route: %m"); + + (void) set_remove(link->dhcp_routes_old, ret); - TAKE_PTR(*route); return 0; } @@ -187,7 +211,7 @@ static int link_set_dns_routes(Link *link, const struct in_addr *address) { route->priority = link->network->dhcp_route_metric; route->table = table; - r = dhcp_route_configure(&route, link); + r = dhcp_route_configure(route, link); if (r < 0) return log_link_error_errno(link, r, "Could not set route to DNS server: %m"); } @@ -231,6 +255,7 @@ static int link_set_dhcp_routes(Link *link) { struct in_addr address; int r, n, i; uint32_t table; + Route *rt; assert(link); @@ -245,12 +270,11 @@ static int link_set_dhcp_routes(Link *link) { * the addresses now, let's not configure the routes either. */ return 0; - r = set_ensure_allocated(&link->dhcp_routes, &route_hash_ops); - if (r < 0) - return log_oom(); - - /* Clear old entries in case the set was already allocated */ - set_clear(link->dhcp_routes); + while ((rt = set_steal_first(link->dhcp_routes))) { + r = set_ensure_put(&link->dhcp_routes_old, &route_hash_ops, rt); + if (r < 0) + return log_link_error_errno(link, r, "Failed to store old DHCPv4 route: %m"); + } table = link_get_dhcp_route_table(link); @@ -265,7 +289,7 @@ static int link_set_dhcp_routes(Link *link) { if (r < 0) return log_link_error_errno(link, r, "Could not create prefix route: %m"); - r = dhcp_route_configure(&prefix_route, link); + r = dhcp_route_configure(prefix_route, link); if (r < 0) return log_link_error_errno(link, r, "Could not set prefix route: %m"); } @@ -316,7 +340,7 @@ static int link_set_dhcp_routes(Link *link) { if (set_contains(link->dhcp_routes, route)) continue; - r = dhcp_route_configure(&route, link); + r = dhcp_route_configure(route, link); if (r < 0) return log_link_error_errno(link, r, "Could not set route: %m"); } @@ -356,7 +380,7 @@ static int link_set_dhcp_routes(Link *link) { route_gw->table = table; route_gw->mtu = link->network->dhcp_route_mtu; - r = dhcp_route_configure(&route_gw, link); + r = dhcp_route_configure(route_gw, link); if (r < 0) return log_link_error_errno(link, r, "Could not set host route: %m"); @@ -372,12 +396,11 @@ static int link_set_dhcp_routes(Link *link) { route->table = table; route->mtu = link->network->dhcp_route_mtu; - r = dhcp_route_configure(&route, link); + r = dhcp_route_configure(route, link); if (r < 0) return log_link_error_errno(link, r, "Could not set router: %m"); } - Route *rt; LIST_FOREACH(routes, rt, link->network->static_routes) { if (!rt->gateway_from_dhcp) continue; @@ -387,272 +410,15 @@ static int link_set_dhcp_routes(Link *link) { rt->gw.in = router[0]; - r = route_configure(rt, link, dhcp4_route_handler); + r = dhcp_route_configure(rt, link); if (r < 0) return log_link_error_errno(link, r, "Could not set gateway: %m"); - if (r > 0) - link->dhcp4_messages++; } } return link_set_dns_routes(link, &address); } -static int dhcp_route_remove(Route *route, Link *link, link_netlink_message_handler_t callback) { - int r; - - r = route_remove(route, link, callback); - if (r < 0) - return r; - - if (callback) - link->dhcp4_remove_messages++; - - return 0; -} - -static int dhcp_remove_routes( - Link *link, - sd_dhcp_lease *lease, - const struct in_addr *address, - bool remove_all, - link_netlink_message_handler_t callback) { - - _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(lease, &routes); - if (IN_SET(n, 0, -ENODATA)) - return 0; - else if (n < 0) - return log_link_error_errno(link, n, "DHCP error: Failed to 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); - if (IN_SET(route->scope, RT_SCOPE_LINK, RT_SCOPE_UNIVERSE)) - route->prefsrc.in = *address; - - if (!remove_all && set_contains(link->dhcp_routes, route)) - continue; - - r = dhcp_route_remove(route, link, callback); - if (r < 0) - return r; - } - - return n; -} - -static int dhcp_remove_router( - Link *link, - sd_dhcp_lease *lease, - const struct in_addr *address, - bool remove_all, - link_netlink_message_handler_t callback) { - - _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_gateway) - return 0; - - r = sd_dhcp_lease_get_router(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; - - if (remove_all || !set_contains(link->dhcp_routes, route_gw)) { - r = dhcp_route_remove(route_gw, link, callback); - if (r < 0) - return r; - } - - 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; - - if (remove_all || !set_contains(link->dhcp_routes, route)) { - r = dhcp_route_remove(route, link, callback); - if (r < 0) - return r; - } - - Route *rt; - LIST_FOREACH(routes, rt, link->network->static_routes) { - if (!rt->gateway_from_dhcp) - continue; - - if (rt->family != AF_INET) - continue; - - if (!remove_all && in4_addr_equal(router, &rt->gw.in)) - continue; - - r = dhcp_route_remove(rt, link, callback); - if (r < 0) - return r; - } - - return 0; -} - -static int dhcp_remove_dns_routes( - Link *link, - sd_dhcp_lease *lease, - const struct in_addr *address, - bool remove_all, - link_netlink_message_handler_t callback) { - - const struct in_addr *dns; - uint32_t table; - int i, n, r; - - assert(link); - assert(lease); - assert(link->network); - - if (!link->network->dhcp_use_dns || - !link->network->dhcp_routes_to_dns) - return 0; - - n = sd_dhcp_lease_get_dns(lease, &dns); - if (IN_SET(n, 0, -ENODATA)) - return 0; - if (n < 0) - return log_link_warning_errno(link, n, "DHCP error: could not get DNS servers: %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_link_error_errno(link, r, "Could not allocate route: %m"); - - route->family = AF_INET; - route->dst.in = dns[i]; - route->dst_prefixlen = 32; - route->prefsrc.in = *address; - route->scope = RT_SCOPE_LINK; - route->protocol = RTPROT_DHCP; - route->priority = link->network->dhcp_route_metric; - route->table = table; - - if (!remove_all && set_contains(link->dhcp_routes, route)) - continue; - - r = dhcp_route_remove(route, link, callback); - if (r < 0) - return r; - } - - if (!link_prefixroute(link)) { - _cleanup_(route_freep) Route *prefix_route = NULL; - - r = dhcp_prefix_route_from_lease(lease, table, address, &prefix_route); - if (r < 0) - return log_link_warning_errno(link, r, "Could not create prefix route: %m"); - - if (remove_all || !set_contains(link->dhcp_routes, prefix_route)) { - r = dhcp_route_remove(prefix_route, link, callback); - if (r < 0) - return r; - } - } - - return 0; -} - -static int dhcp_remove_address( - Link *link, sd_dhcp_lease *lease, - const struct in_addr *address, - link_netlink_message_handler_t callback) { - - _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(lease, &netmask) >= 0) - a->prefixlen = in4_addr_netmask_to_prefixlen(&netmask); - - r = address_remove(a, link, callback); - if (r < 0) - return r; - - if (callback) - link->dhcp4_remove_messages++; - - return 0; -} - static int dhcp_reset_mtu(Link *link) { uint16_t mtu; int r; @@ -756,37 +522,33 @@ static int dhcp4_remove_address_handler(sd_netlink *rtnl, sd_netlink_message *m, } static int dhcp4_remove_all(Link *link) { - struct in_addr address; - int r; + Route *route; + Iterator i; + int k, r = 0; assert(link); - assert(link->dhcp_lease); - r = sd_dhcp_lease_get_address(link->dhcp_lease, &address); - if (r < 0) - return log_link_error_errno(link, r, "Failed to get DHCPv4 address: %m"); + SET_FOREACH(route, link->dhcp_routes, i) { + k = route_remove(route, link, dhcp4_remove_route_handler); + if (k < 0) + r = k; + else + link->dhcp4_remove_messages++; + } - r = dhcp_remove_routes(link, link->dhcp_lease, &address, true, dhcp4_remove_route_handler); - if (r < 0) - return r; + if (link->dhcp_address) { + k = address_remove(link->dhcp_address, link, dhcp4_remove_address_handler); + if (k < 0) + r = k; + else + link->dhcp4_remove_messages++; + } - r = dhcp_remove_router(link, link->dhcp_lease, &address, true, dhcp4_remove_route_handler); - if (r < 0) - return r; - - r = dhcp_remove_dns_routes(link, link->dhcp_lease, &address, true, dhcp4_remove_route_handler); - if (r < 0) - return r; - - r = dhcp_remove_address(link, link->dhcp_lease, &address, dhcp4_remove_address_handler); - if (r < 0) - return r; - - return 0; + return r; } static int dhcp_lease_lost(Link *link) { - int r; + int k, r; assert(link); assert(link->dhcp_lease); @@ -796,24 +558,26 @@ static int dhcp_lease_lost(Link *link) { link->dhcp4_configured = false; /* dhcp_lease_lost() may be called during renewing IP address. */ - dhcp4_release_old_lease(link); + k = dhcp4_release_old_lease(link, true); + if (k < 0) + r = k; - r = dhcp4_remove_all(link); - if (r < 0) - return r; + k = dhcp4_remove_all(link); + if (k < 0) + r = k; - r = dhcp_reset_mtu(link); - if (r < 0) - return r; + k = dhcp_reset_mtu(link); + if (k < 0) + r = k; - r = dhcp_reset_hostname(link); - if (r < 0) - return r; + k = dhcp_reset_hostname(link); + if (k < 0) + r = k; link->dhcp_lease = sd_dhcp_lease_unref(link->dhcp_lease); link_dirty(link); - return 0; + return r; } static void dhcp_address_on_acd(sd_ipv4acd *acd, int event, void *userdata) { @@ -975,6 +739,7 @@ static int dhcp4_update_address(Link *link, bool announce) { uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME; struct in_addr address, netmask; unsigned prefixlen; + Address *ret; int r; assert(link); @@ -1049,9 +814,13 @@ static int dhcp4_update_address(Link *link, bool announce) { /* allow reusing an existing address and simply update its lifetime * in case it already exists */ - r = address_configure(addr, link, dhcp4_address_handler, true); + r = address_configure(addr, link, dhcp4_address_handler, true, &ret); if (r < 0) - return r; + return log_link_error_errno(link, r, "Failed to set DHCPv4 address: %m"); + + if (!address_equal(link->dhcp_address, ret)) + link->dhcp_address_old = link->dhcp_address; + link->dhcp_address = ret; return 0; } @@ -1151,32 +920,11 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) { static int dhcp_lease_ip_change(sd_dhcp_client *client, Link *link) { int r; - link->dhcp_lease_old = TAKE_PTR(link->dhcp_lease); - - /* On IP address change, to keep the connectability, we would like to assign new address and - * routes, and then release old lease. There are two possible success paths: - * - * 1. new address and routes are configured. - * -> handled by dhcp_release_old_lease() in dhcp4_route_handler(). - * 2. new address is configured and no route is requested. - * -> handled by dhcp_release_old_lease() in dhcp4_address_handler(). - * - * On error in assigning new address and routes, then the link always enters to the failed - * state. And link_enter_failed() leads to the DHCP client to be stopped. So, - * dhcp_release_old_lease() will be also called by link_stop_clients(). - */ - r = dhcp_lease_acquired(client, link); - if (r < 0) { - /* If it fails, then the new address is not configured yet. - * So, let's simply drop the old lease. */ - sd_dhcp_lease_unref(link->dhcp_lease); - link->dhcp_lease = TAKE_PTR(link->dhcp_lease_old); + if (r < 0) (void) dhcp_lease_lost(link); - return r; - } - return 0; + return r; } static int dhcp_server_is_deny_listed(Link *link, sd_dhcp_client *client) { diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 23870eab3f..6db57b9f3d 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -691,7 +691,6 @@ static void link_free_engines(Link *link) { link->dhcp_server = sd_dhcp_server_unref(link->dhcp_server); link->dhcp_client = sd_dhcp_client_unref(link->dhcp_client); link->dhcp_lease = sd_dhcp_lease_unref(link->dhcp_lease); - link->dhcp_routes = set_free(link->dhcp_routes); link->lldp = sd_lldp_unref(link->lldp); @@ -713,6 +712,8 @@ static Link *link_free(Link *link) { link->routes = set_free(link->routes); link->routes_foreign = set_free(link->routes_foreign); + link->dhcp_routes = set_free(link->dhcp_routes); + link->dhcp_routes_old = set_free(link->dhcp_routes_old); link->nexthops = set_free(link->nexthops); link->nexthops_foreign = set_free(link->nexthops_foreign); diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index 4534180128..f837ec2a24 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -95,8 +95,9 @@ typedef struct Link { Set *nexthops_foreign; sd_dhcp_client *dhcp_client; - sd_dhcp_lease *dhcp_lease, *dhcp_lease_old; - Set *dhcp_routes; + sd_dhcp_lease *dhcp_lease; + Address *dhcp_address, *dhcp_address_old; + Set *dhcp_routes, *dhcp_routes_old; char *lease_file; uint32_t original_mtu; unsigned dhcp4_messages; diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index d8739b7679..322e819bc1 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -144,6 +144,8 @@ void route_free(Route *route) { if (route->link) { set_remove(route->link->routes, route); set_remove(route->link->routes_foreign, route); + set_remove(route->link->dhcp_routes, route); + set_remove(route->link->dhcp_routes_old, route); } ordered_set_free_free(route->multipath_routes); From 69203fba700ea8d7b0c4f4e3d1e1f809ac4644a1 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 22 Jul 2020 11:55:07 +0900 Subject: [PATCH 10/18] network: ndisc: remove old addresses and routes after at least one SLAAC address becomes ready Otherwise, the old addresses will exist in deperecated state. --- src/network/networkd-address.c | 2 + src/network/networkd-link.c | 4 + src/network/networkd-link.h | 2 + src/network/networkd-ndisc.c | 217 ++++++++++++++++++++++++++------- src/network/networkd-route.c | 2 + 5 files changed, 183 insertions(+), 44 deletions(-) diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 6fc3074eac..1d2db3a6ba 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -129,6 +129,8 @@ void address_free(Address *address) { address->link->dhcp_address = NULL; if (address->link->dhcp_address_old == address) address->link->dhcp_address_old = NULL; + set_remove(address->link->ndisc_addresses, address); + set_remove(address->link->ndisc_addresses_old, address); if (in_addr_equal(AF_INET6, &address->in_addr, (const union in_addr_union *) &address->link->ipv6ll_address)) memzero(&address->link->ipv6ll_address, sizeof(struct in6_addr)); diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 6db57b9f3d..79bbd25fba 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -714,6 +714,8 @@ static Link *link_free(Link *link) { link->routes_foreign = set_free(link->routes_foreign); link->dhcp_routes = set_free(link->dhcp_routes); link->dhcp_routes_old = set_free(link->dhcp_routes_old); + link->ndisc_routes = set_free(link->ndisc_routes); + link->ndisc_routes_old = set_free(link->ndisc_routes_old); link->nexthops = set_free(link->nexthops); link->nexthops_foreign = set_free(link->nexthops_foreign); @@ -723,6 +725,8 @@ static Link *link_free(Link *link) { link->addresses = set_free(link->addresses); link->addresses_foreign = set_free(link->addresses_foreign); + link->ndisc_addresses = set_free(link->ndisc_addresses); + link->ndisc_addresses_old = set_free(link->ndisc_addresses_old); while ((address = link->pool_addresses)) { LIST_REMOVE(addresses, link->pool_addresses, address); diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index f837ec2a24..290e84a06e 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -142,6 +142,8 @@ typedef struct Link { sd_ndisc *ndisc; Set *ndisc_rdnss; Set *ndisc_dnssl; + Set *ndisc_addresses, *ndisc_addresses_old; + Set *ndisc_routes, *ndisc_routes_old; sd_radv *radv; diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index 2f6cc4042e..0c86fa139b 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -35,6 +35,69 @@ #define NDISC_APP_ID SD_ID128_MAKE(13,ac,81,a7,d5,3f,49,78,92,79,5d,0c,29,3a,bc,7e) +static int ndisc_remove_old(Link *link, bool force); + +static int ndisc_address_callback(Address *address) { + Address *a; + Iterator i; + + assert(address); + assert(address->link); + + /* Make this called only once */ + SET_FOREACH(a, address->link->ndisc_addresses, i) + a->callback = NULL; + + return ndisc_remove_old(address->link, true); +} + +static int ndisc_remove_old(Link *link, bool force) { + Address *address; + Route *route; + Iterator i; + int k, r = 0; + + assert(link); + + if (!force) { + bool set_callback = !set_isempty(link->ndisc_addresses); + + if (!link->ndisc_addresses_configured || !link->ndisc_routes_configured) + return 0; + + SET_FOREACH(address, link->ndisc_addresses, i) + if (address_is_ready(address)) { + set_callback = false; + break; + } + + if (set_callback) { + SET_FOREACH(address, link->ndisc_addresses, i) + address->callback = ndisc_address_callback; + return 0; + } + } + + if (!set_isempty(link->ndisc_addresses_old) || !set_isempty(link->ndisc_routes_old)) + log_link_debug(link, "Removing old NDisc addresses and routes."); + + link_dirty(link); + + SET_FOREACH(address, link->ndisc_addresses_old, i) { + k = address_remove(address, link, NULL); + if (k < 0) + r = k; + } + + SET_FOREACH(route, link->ndisc_routes_old, i) { + k = route_remove(route, link, NULL); + if (k < 0) + r = k; + } + + return r; +} + static int ndisc_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { int r; @@ -56,6 +119,13 @@ static int ndisc_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *li if (link->ndisc_routes_messages == 0) { log_link_debug(link, "NDisc routes set."); link->ndisc_routes_configured = true; + + r = ndisc_remove_old(link, false); + if (r < 0) { + link_enter_failed(link); + return 1; + } + link_check_ready(link); } @@ -84,6 +154,13 @@ static int ndisc_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link * if (link->ndisc_addresses_messages == 0) { log_link_debug(link, "NDisc SLAAC addresses set."); link->ndisc_addresses_configured = true; + + r = ndisc_remove_old(link, false); + if (r < 0) { + link_enter_failed(link); + return 1; + } + r = link_request_set_routes(link); if (r < 0) { link_enter_failed(link); @@ -94,6 +171,50 @@ static int ndisc_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link * return 1; } +static int ndisc_route_configure(Route *route, Link *link) { + Route *ret; + int r; + + assert(route); + assert(link); + + r = route_configure(route, link, ndisc_route_handler, &ret); + if (r < 0) + return log_link_error_errno(link, r, "Failed to set NDisc route: %m"); + + link->ndisc_routes_messages++; + + r = set_ensure_put(&link->ndisc_routes, &route_hash_ops, ret); + if (r < 0) + return log_link_error_errno(link, r, "Failed to store NDisc route: %m"); + + (void) set_remove(link->ndisc_routes_old, ret); + + return 0; +} + +static int ndisc_address_configure(Address *address, Link *link) { + Address *ret; + int r; + + assert(address); + assert(link); + + r = address_configure(address, link, ndisc_address_handler, true, &ret); + if (r < 0) + return log_link_error_errno(link, r, "Failed to set NDisc SLAAC address: %m"); + + link->ndisc_addresses_messages++; + + r = set_ensure_put(&link->ndisc_addresses, &address_hash_ops, ret); + if (r < 0) + return log_link_error_errno(link, r, "Failed to store NDisc SLAAC address: %m"); + + (void) set_remove(link->ndisc_addresses_old, ret); + + return 0; +} + static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) { _cleanup_(route_freep) Route *route = NULL; union in_addr_union gateway; @@ -155,11 +276,9 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) { route->lifetime = time_now + lifetime * USEC_PER_SEC; route->mtu = mtu; - r = route_configure(route, link, ndisc_route_handler, NULL); + r = ndisc_route_configure(route, link); if (r < 0) return log_link_error_errno(link, r, "Could not set default route: %m"); - if (r > 0) - link->ndisc_routes_messages++; Route *route_gw; LIST_FOREACH(routes, route_gw, link->network->static_routes) { @@ -171,11 +290,9 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) { route_gw->gw = gateway; - r = route_configure(route_gw, link, ndisc_route_handler, NULL); + r = ndisc_route_configure(route_gw, link); if (r < 0) return log_link_error_errno(link, r, "Could not set gateway: %m"); - if (r > 0) - link->ndisc_routes_messages++; } return 0; @@ -387,11 +504,9 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r address->in_addr.in6 = *a; - r = address_configure(address, link, ndisc_address_handler, true, NULL); + r = ndisc_address_configure(address, link); if (r < 0) return log_link_error_errno(link, r, "Could not set SLAAC address: %m"); - if (r > 0) - link->ndisc_addresses_messages++; } return 0; @@ -435,11 +550,9 @@ static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) { if (r < 0) return log_link_error_errno(link, r, "Failed to get prefix address: %m"); - r = route_configure(route, link, ndisc_route_handler, NULL); + r = ndisc_route_configure(route, link); if (r < 0) return log_link_error_errno(link, r, "Could not set prefix route: %m");; - if (r > 0) - link->ndisc_routes_messages++; return 0; } @@ -494,11 +607,9 @@ static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) { if (r < 0) return log_link_error_errno(link, r, "Failed to get route address: %m"); - r = route_configure(route, link, ndisc_route_handler, NULL); + r = ndisc_route_configure(route, link); if (r < 0) return log_link_error_errno(link, r, "Could not set additional route: %m"); - if (r > 0) - link->ndisc_routes_messages++; return 0; } @@ -736,6 +847,8 @@ static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) { } static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) { + Address *address; + Route *route; uint64_t flags; int r; @@ -744,6 +857,23 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) { assert(link->manager); assert(rt); + link->ndisc_addresses_configured = false; + link->ndisc_routes_configured = false; + + link_dirty(link); + + while ((address = set_steal_first(link->ndisc_addresses))) { + r = set_ensure_put(&link->ndisc_addresses_old, &address_hash_ops, address); + if (r < 0) + return log_link_error_errno(link, r, "Failed to store old NDisc SLAAC address: %m"); + } + + while ((route = set_steal_first(link->ndisc_routes))) { + r = set_ensure_put(&link->ndisc_routes_old, &route_hash_ops, route); + if (r < 0) + return log_link_error_errno(link, r, "Failed to store old NDisc route: %m"); + } + r = sd_ndisc_router_get_flags(rt, &flags); if (r < 0) return log_link_error_errno(link, r, "Failed to get RA flags: %m"); @@ -757,10 +887,8 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) { r = dhcp6_request_address(link, !(flags & ND_RA_FLAG_MANAGED)); if (r < 0 && r != -EBUSY) return log_link_error_errno(link, r, "Could not acquire DHCPv6 lease on NDisc request: %m"); - else { + else log_link_debug(link, "Acquiring DHCPv6 lease on NDisc request"); - r = 0; - } } r = ndisc_router_process_default(link, rt); @@ -770,7 +898,33 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) { if (r < 0) return r; - return r; + if (link->ndisc_addresses_messages == 0) + link->ndisc_addresses_configured = true; + else { + log_link_debug(link, "Setting SLAAC addresses."); + + /* address_handler calls link_request_set_routes() and link_request_set_nexthop(). + * Before they are called, the related flags must be cleared. Otherwise, the link + * becomes configured state before routes are configured. */ + link->static_routes_configured = false; + link->static_nexthops_configured = false; + } + + if (link->ndisc_routes_messages == 0) + link->ndisc_routes_configured = true; + else + log_link_debug(link, "Setting NDisc routes."); + + r = ndisc_remove_old(link, false); + if (r < 0) + return r; + + if (link->ndisc_addresses_configured && link->ndisc_routes_configured) + link_check_ready(link); + else + link_set_state(link, LINK_STATE_CONFIGURING); + + return 0; } static void ndisc_handler(sd_ndisc *nd, sd_ndisc_event event, sd_ndisc_router *rt, void *userdata) { @@ -785,36 +939,11 @@ static void ndisc_handler(sd_ndisc *nd, sd_ndisc_event event, sd_ndisc_router *r switch (event) { case SD_NDISC_EVENT_ROUTER: - link->ndisc_addresses_configured = false; - link->ndisc_routes_configured = false; - r = ndisc_router_handler(link, rt); if (r < 0) { link_enter_failed(link); return; } - - if (link->ndisc_addresses_messages == 0) - link->ndisc_addresses_configured = true; - else { - log_link_debug(link, "Setting SLAAC addresses."); - - /* address_handler calls link_request_set_routes() and link_request_set_nexthop(). - * Before they are called, the related flags must be cleared. Otherwise, the link - * becomes configured state before routes are configured. */ - link->static_routes_configured = false; - link->static_nexthops_configured = false; - } - - if (link->ndisc_routes_messages == 0) - link->ndisc_routes_configured = true; - else - log_link_debug(link, "Setting NDisc routes."); - - if (link->ndisc_addresses_configured && link->ndisc_routes_configured) - link_check_ready(link); - else - link_set_state(link, LINK_STATE_CONFIGURING); break; case SD_NDISC_EVENT_TIMEOUT: diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index 322e819bc1..d04c33a5d8 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -146,6 +146,8 @@ void route_free(Route *route) { set_remove(route->link->routes_foreign, route); set_remove(route->link->dhcp_routes, route); set_remove(route->link->dhcp_routes_old, route); + set_remove(route->link->ndisc_routes, route); + set_remove(route->link->ndisc_routes_old, route); } ordered_set_free_free(route->multipath_routes); From b0b97766564b9a2b7f78a77bba00c0a577b39712 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 22 Jul 2020 21:44:44 +0900 Subject: [PATCH 11/18] network: ndisc: also remove old DNSSL or RDNSS records after an SLAAC address is ready --- src/network/networkd-ndisc.c | 126 ++++++++++++++++++++--------------- src/network/networkd-ndisc.h | 4 ++ 2 files changed, 78 insertions(+), 52 deletions(-) diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index 0c86fa139b..349f0548af 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -54,6 +54,8 @@ static int ndisc_address_callback(Address *address) { static int ndisc_remove_old(Link *link, bool force) { Address *address; Route *route; + NDiscDNSSL *dnssl; + NDiscRDNSS *rdnss; Iterator i; int k, r = 0; @@ -95,6 +97,14 @@ static int ndisc_remove_old(Link *link, bool force) { r = k; } + SET_FOREACH(rdnss, link->ndisc_rdnss, i) + if (rdnss->marked) + free(set_remove(link->ndisc_rdnss, rdnss)); + + SET_FOREACH(dnssl, link->ndisc_dnssl, i) + if (dnssl->marked) + free(set_remove(link->ndisc_dnssl, dnssl)); + return r; } @@ -622,12 +632,19 @@ static int ndisc_rdnss_compare_func(const NDiscRDNSS *a, const NDiscRDNSS *b) { return memcmp(&a->address, &b->address, sizeof(a->address)); } -DEFINE_PRIVATE_HASH_OPS(ndisc_rdnss_hash_ops, NDiscRDNSS, ndisc_rdnss_hash_func, ndisc_rdnss_compare_func); +DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR( + ndisc_rdnss_hash_ops, + NDiscRDNSS, + ndisc_rdnss_hash_func, + ndisc_rdnss_compare_func, + free); static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) { uint32_t lifetime; const struct in6_addr *a; + NDiscRDNSS *rdnss; usec_t time_now; + Iterator i; int n, r; assert(link); @@ -645,28 +662,27 @@ static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) { if (n < 0) return log_link_error_errno(link, n, "Failed to get RDNSS addresses: %m"); - for (int i = 0; i < n; i++) { + SET_FOREACH(rdnss, link->ndisc_rdnss, i) + rdnss->marked = true; + + if (lifetime == 0) + return 0; + + if (n >= (int) NDISC_RDNSS_MAX) { + log_link_warning(link, "Too many RDNSS records per link. Only first %i records will be used.", NDISC_RDNSS_MAX); + n = NDISC_RDNSS_MAX; + } + + for (int j = 0; j < n; j++) { _cleanup_free_ NDiscRDNSS *x = NULL; NDiscRDNSS d = { - .address = a[i], - }, *y; + .address = a[j], + }; - if (lifetime == 0) { - (void) set_remove(link->ndisc_rdnss, &d); - link_dirty(link); - continue; - } - - y = set_get(link->ndisc_rdnss, &d); - if (y) { - y->valid_until = time_now + lifetime * USEC_PER_SEC; - continue; - } - - ndisc_vacuum(link); - - if (set_size(link->ndisc_rdnss) >= NDISC_RDNSS_MAX) { - log_link_warning(link, "Too many RDNSS records per link, ignoring."); + rdnss = set_get(link->ndisc_rdnss, &d); + if (rdnss) { + rdnss->marked = false; + rdnss->valid_until = time_now + lifetime * USEC_PER_SEC; continue; } @@ -675,7 +691,7 @@ static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) { return log_oom(); *x = (NDiscRDNSS) { - .address = a[i], + .address = a[j], .valid_until = time_now + lifetime * USEC_PER_SEC, }; @@ -683,8 +699,6 @@ static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) { if (r < 0) return log_oom(); assert(r > 0); - - link_dirty(link); } return 0; @@ -698,13 +712,20 @@ static int ndisc_dnssl_compare_func(const NDiscDNSSL *a, const NDiscDNSSL *b) { return strcmp(NDISC_DNSSL_DOMAIN(a), NDISC_DNSSL_DOMAIN(b)); } -DEFINE_PRIVATE_HASH_OPS(ndisc_dnssl_hash_ops, NDiscDNSSL, ndisc_dnssl_hash_func, ndisc_dnssl_compare_func); +DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR( + ndisc_dnssl_hash_ops, + NDiscDNSSL, + ndisc_dnssl_hash_func, + ndisc_dnssl_compare_func, + free); static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) { _cleanup_strv_free_ char **l = NULL; uint32_t lifetime; usec_t time_now; - char **i; + NDiscDNSSL *dnssl; + Iterator i; + char **j; int r; assert(link); @@ -722,32 +743,31 @@ static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) { if (r < 0) return log_link_error_errno(link, r, "Failed to get DNSSL addresses: %m"); - STRV_FOREACH(i, l) { - _cleanup_free_ NDiscDNSSL *s = NULL; - NDiscDNSSL *x; + SET_FOREACH(dnssl, link->ndisc_dnssl, i) + dnssl->marked = true; - s = malloc0(ALIGN(sizeof(NDiscDNSSL)) + strlen(*i) + 1); + if (lifetime == 0) + return 0; + + if (strv_length(l) >= NDISC_DNSSL_MAX) { + log_link_warning(link, "Too many DNSSL records per link. Only first %i records will be used.", NDISC_DNSSL_MAX); + STRV_FOREACH(j, l + NDISC_DNSSL_MAX) + *j = mfree(*j); + } + + STRV_FOREACH(j, l) { + _cleanup_free_ NDiscDNSSL *s = NULL; + + s = malloc0(ALIGN(sizeof(NDiscDNSSL)) + strlen(*j) + 1); if (!s) return log_oom(); - strcpy(NDISC_DNSSL_DOMAIN(s), *i); + strcpy(NDISC_DNSSL_DOMAIN(s), *j); - if (lifetime == 0) { - (void) set_remove(link->ndisc_dnssl, s); - link_dirty(link); - continue; - } - - x = set_get(link->ndisc_dnssl, s); - if (x) { - x->valid_until = time_now + lifetime * USEC_PER_SEC; - continue; - } - - ndisc_vacuum(link); - - if (set_size(link->ndisc_dnssl) >= NDISC_DNSSL_MAX) { - log_link_warning(link, "Too many DNSSL records per link, ignoring."); + dnssl = set_get(link->ndisc_dnssl, s); + if (dnssl) { + dnssl->marked = false; + dnssl->valid_until = time_now + lifetime * USEC_PER_SEC; continue; } @@ -757,8 +777,6 @@ static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) { if (r < 0) return log_oom(); assert(r > 0); - - link_dirty(link); } return 0; @@ -992,6 +1010,7 @@ void ndisc_vacuum(Link *link) { NDiscDNSSL *d; Iterator i; usec_t time_now; + bool updated = false; assert(link); @@ -1002,14 +1021,17 @@ void ndisc_vacuum(Link *link) { SET_FOREACH(r, link->ndisc_rdnss, i) if (r->valid_until < time_now) { free(set_remove(link->ndisc_rdnss, r)); - link_dirty(link); + updated = true; } SET_FOREACH(d, link->ndisc_dnssl, i) if (d->valid_until < time_now) { free(set_remove(link->ndisc_dnssl, d)); - link_dirty(link); + updated = true; } + + if (updated) + link_dirty(link); } void ndisc_flush(Link *link) { @@ -1017,8 +1039,8 @@ void ndisc_flush(Link *link) { /* Removes all RDNSS and DNSSL entries, without exception */ - link->ndisc_rdnss = set_free_free(link->ndisc_rdnss); - link->ndisc_dnssl = set_free_free(link->ndisc_dnssl); + link->ndisc_rdnss = set_free(link->ndisc_rdnss); + link->ndisc_dnssl = set_free(link->ndisc_dnssl); } int ipv6token_new(IPv6Token **ret) { diff --git a/src/network/networkd-ndisc.h b/src/network/networkd-ndisc.h index 0bc2882a6c..c459f42456 100644 --- a/src/network/networkd-ndisc.h +++ b/src/network/networkd-ndisc.h @@ -24,11 +24,15 @@ typedef enum IPv6AcceptRAStartDHCP6Client { } IPv6AcceptRAStartDHCP6Client; typedef struct NDiscRDNSS { + /* Used when GC'ing old DNS servers when configuration changes. */ + bool marked; usec_t valid_until; struct in6_addr address; } NDiscRDNSS; typedef struct NDiscDNSSL { + /* Used when GC'ing old domains when configuration changes. */ + bool marked; usec_t valid_until; /* The domain name follows immediately. */ } NDiscDNSSL; From 1c09d84e42bc4a2f55ed22fcf02c57dd1d82517f Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Thu, 23 Jul 2020 01:21:40 +0900 Subject: [PATCH 12/18] network: rename ipv4ll_address -> ipv4ll_address_configured --- src/network/networkd-ipv4ll.c | 8 ++++---- src/network/networkd-link.c | 4 ++-- src/network/networkd-link.h | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/network/networkd-ipv4ll.c b/src/network/networkd-ipv4ll.c index e57b9e420f..e844799b57 100644 --- a/src/network/networkd-ipv4ll.c +++ b/src/network/networkd-ipv4ll.c @@ -17,7 +17,7 @@ static int ipv4ll_address_lost(Link *link) { assert(link); - link->ipv4ll_address = false; + link->ipv4ll_address_configured = false; r = sd_ipv4ll_get_address(link->ipv4ll, &addr); if (r < 0) @@ -47,7 +47,7 @@ static int ipv4ll_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link int r; assert(link); - assert(!link->ipv4ll_address); + assert(!link->ipv4ll_address_configured); r = sd_netlink_message_get_errno(m); if (r < 0 && r != -EEXIST) { @@ -57,7 +57,7 @@ static int ipv4ll_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link } else if (r >= 0) (void) manager_rtnl_process_address(rtnl, m, link->manager); - link->ipv4ll_address = true; + link->ipv4ll_address_configured = true; link_check_ready(link); return 1; @@ -71,7 +71,7 @@ static int ipv4ll_address_claimed(sd_ipv4ll *ll, Link *link) { assert(ll); assert(link); - link->ipv4ll_address = false; + link->ipv4ll_address_configured = false; r = sd_ipv4ll_get_address(ll, &address); if (r == -ENOENT) diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 79bbd25fba..5faf027396 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -1157,7 +1157,7 @@ void link_check_ready(Link *link) { if (link_has_carrier(link) || !link->network->configure_without_carrier) { - if (link_ipv4ll_enabled(link, ADDRESS_FAMILY_IPV4) && !link->ipv4ll_address) { + if (link_ipv4ll_enabled(link, ADDRESS_FAMILY_IPV4) && !link->ipv4ll_address_configured) { log_link_debug(link, "%s(): IPv4LL is not configured.", __func__); return; } @@ -1178,7 +1178,7 @@ void link_check_ready(Link *link) { !(link->dhcp6_address_configured && link->dhcp6_route_configured) && !(link->dhcp6_pd_address_configured && link->dhcp6_pd_route_configured) && !(link->ndisc_addresses_configured && link->ndisc_routes_configured) && - !(link_ipv4ll_enabled(link, ADDRESS_FAMILY_FALLBACK_IPV4) && link->ipv4ll_address)) { + !(link_ipv4ll_enabled(link, ADDRESS_FAMILY_FALLBACK_IPV4) && link->ipv4ll_address_configured)) { /* When DHCP or RA is enabled, at least one protocol must provide an address, or * an IPv4ll fallback address must be configured. */ log_link_debug(link, "%s(): dynamic addresses or routes are not configured.", __func__); diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index 290e84a06e..e079a919bc 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -121,7 +121,7 @@ typedef struct Link { bool ndisc_routes_configured:1; sd_ipv4ll *ipv4ll; - bool ipv4ll_address:1; + bool ipv4ll_address_configured:1; bool addresses_configured:1; bool addresses_ready:1; From 1633c45731701e1c7c6bf43f221ffabaa81eceab Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Thu, 23 Jul 2020 03:13:42 +0900 Subject: [PATCH 13/18] network: dhcp6: drop addresses and delegated prefixes on client stop Previously, we did not drop addresses and delegated prefixes when DHCP6 client is stopped. Fixes #15455. Fixes #13564. --- src/network/networkd-address.c | 4 + src/network/networkd-dhcp6.c | 1640 +++++++++++++++++++------------- src/network/networkd-dhcp6.h | 16 +- src/network/networkd-link.c | 38 +- src/network/networkd-link.h | 31 +- src/network/networkd-manager.c | 15 +- src/network/networkd-manager.h | 1 + src/network/networkd-radv.c | 2 +- src/network/networkd-radv.h | 2 +- src/network/networkd-route.c | 4 + 10 files changed, 1060 insertions(+), 693 deletions(-) diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 1d2db3a6ba..a848151c8b 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -129,6 +129,10 @@ void address_free(Address *address) { address->link->dhcp_address = NULL; if (address->link->dhcp_address_old == address) address->link->dhcp_address_old = NULL; + set_remove(address->link->dhcp6_addresses, address); + set_remove(address->link->dhcp6_addresses_old, address); + set_remove(address->link->dhcp6_pd_addresses, address); + set_remove(address->link->dhcp6_pd_addresses_old, address); set_remove(address->link->ndisc_addresses, address); set_remove(address->link->ndisc_addresses_old, address); diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index b571d923d5..3ac03c3eee 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -24,15 +24,409 @@ #include "radv-internal.h" #include "web-util.h" -static Link *dhcp6_prefix_get(Manager *m, struct in6_addr *addr); -static int dhcp6_prefix_add(Manager *m, struct in6_addr *addr, Link *link); -static int dhcp6_prefix_remove_all(Manager *m, Link *link); -static int dhcp6_assign_delegated_prefix(Link *link, const struct in6_addr *prefix, - uint8_t prefix_len, - uint32_t lifetime_preferred, - uint32_t lifetime_valid); +static bool dhcp6_lease_has_pd_prefix(sd_dhcp6_lease *lease) { + uint32_t lifetime_preferred, lifetime_valid; + union in_addr_union pd_prefix; + uint8_t pd_prefix_len; -bool dhcp6_get_prefix_delegation(Link *link) { + if (!lease) + return false; + + sd_dhcp6_lease_reset_pd_prefix_iter(lease); + + return sd_dhcp6_lease_get_pd(lease, &pd_prefix.in6, &pd_prefix_len, &lifetime_preferred, &lifetime_valid) >= 0; +} + +DHCP6DelegatedPrefix *dhcp6_pd_free(DHCP6DelegatedPrefix *p) { + if (!p) + return NULL; + + if (p->link && p->link->manager) { + hashmap_remove(p->link->manager->dhcp6_prefixes, &p->prefix); + set_remove(p->link->manager->dhcp6_pd_prefixes, p); + } + + link_unref(p->link); + return mfree(p); +} + +static void dhcp6_pd_hash_func(const DHCP6DelegatedPrefix *p, struct siphash *state) { + assert(p); + + siphash24_compress(&p->pd_prefix, sizeof(p->pd_prefix), state); + siphash24_compress(&p->link, sizeof(p->link), state); +} + +static int dhcp6_pd_compare_func(const DHCP6DelegatedPrefix *a, const DHCP6DelegatedPrefix *b) { + int r; + + r = memcmp(&a->pd_prefix, &b->pd_prefix, sizeof(a->pd_prefix)); + if (r != 0) + return r; + + return CMP(a->link, b->link); +} + +DEFINE_HASH_OPS(dhcp6_pd_hash_ops, DHCP6DelegatedPrefix, dhcp6_pd_hash_func, dhcp6_pd_compare_func); + +static Link *dhcp6_pd_get_link_by_prefix(Link *link, const union in_addr_union *prefix) { + DHCP6DelegatedPrefix *pd; + + assert(link); + assert(link->manager); + assert(prefix); + + pd = hashmap_get(link->manager->dhcp6_prefixes, &prefix->in6); + if (!pd) + return NULL; + + return pd->link; +} + +static int dhcp6_pd_get_assigned_prefix(Link *link, const union in_addr_union *pd_prefix, union in_addr_union *ret_prefix) { + DHCP6DelegatedPrefix *pd, in; + + assert(link); + assert(link->manager); + assert(pd_prefix); + assert(ret_prefix); + + in = (DHCP6DelegatedPrefix) { + .pd_prefix = pd_prefix->in6, + .link = link, + }; + + pd = set_get(link->manager->dhcp6_pd_prefixes, &in); + if (!pd) + return -ENOENT; + + ret_prefix->in6 = pd->prefix; + return 0; +} + +static int dhcp6_pd_remove_old(Link *link, bool force); + +static int dhcp6_pd_address_callback(Address *address) { + Address *a; + Iterator i; + + assert(address); + assert(address->link); + + /* Make this called only once */ + SET_FOREACH(a, address->link->dhcp6_pd_addresses, i) + a->callback = NULL; + + return dhcp6_pd_remove_old(address->link, true); +} + +static int dhcp6_pd_remove_old(Link *link, bool force) { + Address *address; + Route *route; + Iterator i; + int k, r = 0; + + assert(link); + assert(link->manager); + + if (!force && (link->dhcp6_pd_address_messages != 0 || link->dhcp6_pd_route_configured != 0)) + return 0; + + if (set_isempty(link->dhcp6_pd_addresses_old) && set_isempty(link->dhcp6_pd_routes_old)) + return 0; + + if (!force) { + bool set_callback = !set_isempty(link->dhcp6_pd_addresses); + + SET_FOREACH(address, link->dhcp6_pd_addresses, i) + if (address_is_ready(address)) { + set_callback = false; + break; + } + + if (set_callback) { + SET_FOREACH(address, link->dhcp6_pd_addresses, i) + address->callback = dhcp6_pd_address_callback; + return 0; + } + } + + log_link_debug(link, "Removing old DHCPv6 Prefix Delegation addresses and routes."); + + link_dirty(link); + + SET_FOREACH(route, link->dhcp6_pd_routes_old, i) { + k = route_remove(route, link, NULL); + if (k < 0) + r = k; + + (void) sd_radv_remove_prefix(link->radv, &route->dst.in6, 64); + dhcp6_pd_free(hashmap_get(link->manager->dhcp6_prefixes, &route->dst.in6)); + } + + SET_FOREACH(address, link->dhcp6_pd_addresses_old, i) { + k = address_remove(address, link, NULL); + if (k < 0) + r = k; + } + + return r; +} + +int dhcp6_pd_remove(Link *link) { + Address *address; + Route *route; + Iterator i; + int k, r = 0; + + assert(link); + assert(link->manager); + + link->dhcp6_pd_address_configured = false; + link->dhcp6_pd_route_configured = false; + + k = dhcp6_pd_remove_old(link, true); + if (k < 0) + r = k; + + if (set_isempty(link->dhcp6_pd_addresses) && set_isempty(link->dhcp6_pd_routes)) + return r; + + log_link_debug(link, "Removing DHCPv6 Prefix Delegation addresses and routes."); + + link_dirty(link); + + SET_FOREACH(route, link->dhcp6_pd_routes, i) { + k = route_remove(route, link, NULL); + if (k < 0) + r = k; + + (void) sd_radv_remove_prefix(link->radv, &route->dst.in6, 64); + dhcp6_pd_free(hashmap_get(link->manager->dhcp6_prefixes, &route->dst.in6)); + } + + SET_FOREACH(address, link->dhcp6_pd_addresses, i) { + k = address_remove(address, link, NULL); + if (k < 0) + r = k; + } + + return r; +} + +static int dhcp6_pd_route_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) { + int r; + + assert(link); + assert(link->dhcp6_pd_route_messages > 0); + + link->dhcp6_pd_route_messages--; + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 1; + + r = sd_netlink_message_get_errno(m); + if (r < 0 && r != -EEXIST) { + log_link_message_warning_errno(link, m, r, "Failed to add DHCPv6 Prefix Delegation route"); + link_enter_failed(link); + return 1; + } + + if (link->dhcp6_pd_route_messages == 0) { + log_link_debug(link, "DHCPv6 prefix delegation routes set"); + if (link->dhcp6_pd_prefixes_assigned) + link->dhcp6_pd_route_configured = true; + + r = dhcp6_pd_remove_old(link, false); + if (r < 0) { + link_enter_failed(link); + return 1; + } + + link_check_ready(link); + } + + return 1; +} + +static int dhcp6_set_pd_route(Link *link, const union in_addr_union *prefix, const union in_addr_union *pd_prefix) { + _cleanup_(dhcp6_pd_freep) DHCP6DelegatedPrefix *pd = NULL; + _cleanup_(route_freep) Route *route = NULL; + Link *assigned_link; + Route *ret; + int r; + + assert(link); + assert(link->manager); + assert(prefix); + assert(pd_prefix); + + r = route_new(&route); + if (r < 0) + return r; + + route->family = AF_INET6; + route->dst = *prefix; + route->dst_prefixlen = 64; + + r = route_configure(route, link, dhcp6_pd_route_handler, &ret); + if (r < 0) + return log_link_error_errno(link, r, "Failed to set DHCPv6 prefix route: %m"); + + link->dhcp6_pd_route_messages++; + + r = set_ensure_put(&link->dhcp6_pd_routes, &route_hash_ops, ret); + if (r < 0) + return log_link_error_errno(link, r, "Failed to store DHCPv6 prefix route: %m"); + + (void) set_remove(link->dhcp6_pd_routes_old, ret); + + assigned_link = dhcp6_pd_get_link_by_prefix(link, prefix); + if (assigned_link) { + assert(assigned_link == link); + return 0; + } + + pd = new(DHCP6DelegatedPrefix, 1); + if (!pd) + return log_oom(); + + *pd = (DHCP6DelegatedPrefix) { + .prefix = prefix->in6, + .pd_prefix = pd_prefix->in6, + .link = link_ref(link), + }; + + r = hashmap_ensure_allocated(&link->manager->dhcp6_prefixes, &in6_addr_hash_ops); + if (r < 0) + return log_oom(); + + r = hashmap_put(link->manager->dhcp6_prefixes, &pd->prefix, pd); + if (r < 0) + return log_link_error_errno(link, r, "Failed to store DHCPv6 prefix route at manager: %m"); + + r = set_ensure_put(&link->manager->dhcp6_pd_prefixes, &dhcp6_pd_hash_ops, pd); + if (r < 0) + return log_link_error_errno(link, r, "Failed to store DHCPv6 prefix route at manager: %m"); + + TAKE_PTR(pd); + return 0; +} + +static int dhcp6_pd_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { + int r; + + assert(link); + assert(link->dhcp6_pd_address_messages > 0); + + link->dhcp6_pd_address_messages--; + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 1; + + r = sd_netlink_message_get_errno(m); + if (r < 0 && r != -EEXIST) { + log_link_message_warning_errno(link, m, r, "Could not set DHCPv6 delegated prefix address"); + link_enter_failed(link); + return 1; + } else if (r >= 0) + (void) manager_rtnl_process_address(rtnl, m, link->manager); + + if (link->dhcp6_pd_address_messages == 0) { + log_link_debug(link, "DHCPv6 delegated prefix addresses set"); + if (link->dhcp6_pd_prefixes_assigned) + link->dhcp6_pd_address_configured = true; + + r = dhcp6_pd_remove_old(link, false); + if (r < 0) { + link_enter_failed(link); + return 1; + } + + r = link_request_set_routes(link); + if (r < 0) { + link_enter_failed(link); + return 1; + } + } + + return 1; +} + +static int dhcp6_set_pd_address(Link *link, + const union in_addr_union *prefix, + uint8_t prefix_len, + uint32_t lifetime_preferred, + uint32_t lifetime_valid) { + + _cleanup_(address_freep) Address *address = NULL; + Address *ret; + int r; + + assert(link); + assert(link->network); + assert(prefix); + + if (!link->network->dhcp6_pd_assign_prefix) + return 0; + + r = address_new(&address); + if (r < 0) + return log_link_error_errno(link, r, "Failed to allocate address for DHCPv6 delegated prefix: %m"); + + address->in_addr = *prefix; + + if (!in_addr_is_null(AF_INET6, &link->network->dhcp6_delegation_prefix_token)) + memcpy(address->in_addr.in6.s6_addr + 8, link->network->dhcp6_delegation_prefix_token.in6.s6_addr + 8, 8); + else { + r = generate_ipv6_eui_64_address(link, &address->in_addr.in6); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to generate EUI64 address for acquired DHCPv6 delegated prefix: %m"); + } + + address->prefixlen = prefix_len; + address->family = AF_INET6; + address->cinfo.ifa_prefered = lifetime_preferred; + address->cinfo.ifa_valid = lifetime_valid; + + r = address_configure(address, link, dhcp6_pd_address_handler, true, &ret); + if (r < 0) + return log_link_error_errno(link, r, "Failed to set DHCPv6 delegated prefix address: %m"); + + link->dhcp6_pd_address_messages++; + + r = set_ensure_put(&link->dhcp6_pd_addresses, &address_hash_ops, ret); + if (r < 0) + return log_link_error_errno(link, r, "Failed to store DHCPv6 delegated prefix address: %m"); + + (void) set_remove(link->dhcp6_pd_addresses_old, ret); + + return 0; +} + +static int dhcp6_pd_assign_prefix(Link *link, const union in_addr_union *prefix, const union in_addr_union *pd_prefix, + uint8_t prefix_len, uint32_t lifetime_preferred, uint32_t lifetime_valid) { + int r; + + assert(link); + assert(prefix); + + r = radv_add_prefix(link, &prefix->in6, prefix_len, lifetime_preferred, lifetime_valid); + if (r < 0) + return r; + + r = dhcp6_set_pd_route(link, prefix, pd_prefix); + if (r < 0) + return r; + + r = dhcp6_set_pd_address(link, prefix, prefix_len, lifetime_preferred, lifetime_valid); + if (r < 0) + return r; + + return 0; +} + +bool link_dhcp6_pd_is_enabled(Link *link) { if (!link->network) return false; @@ -41,7 +435,7 @@ bool dhcp6_get_prefix_delegation(Link *link) { RADV_PREFIX_DELEGATION_BOTH); } -static bool dhcp6_has_preferred_subnet_id(Link *link) { +static bool link_has_preferred_subnet_id(Link *link) { if (!link->network) return false; @@ -49,274 +443,322 @@ static bool dhcp6_has_preferred_subnet_id(Link *link) { } static int dhcp6_get_preferred_delegated_prefix( - Manager* manager, Link *link, - const struct in6_addr *pd_prefix, + const union in_addr_union *masked_pd_prefix, uint8_t pd_prefix_len, - struct in6_addr *ret_addr) { + union in_addr_union *ret) { - int64_t subnet_id = link->network->router_prefix_subnet_id; - uint8_t prefix_bits = 64 - pd_prefix_len; - uint64_t n_prefixes = UINT64_C(1) << prefix_bits; - _cleanup_free_ char *assigned_buf = NULL; - union in_addr_union pd_prefix_union = { - .in6 = *pd_prefix, - }; - /* We start off with the original PD prefix we have been assigned and - * iterate from there */ - union in_addr_union prefix = { - .in6 = *pd_prefix, - }; + /* We start off with the original PD prefix we have been assigned and iterate from there */ + union in_addr_union prefix; + uint64_t n_prefixes; + Link *assigned_link; int r; - assert(pd_prefix_len <= 64); - assert(manager); assert(link); - assert(link->network); + assert(link->manager); + assert(masked_pd_prefix); + assert(pd_prefix_len <= 64); + + n_prefixes = UINT64_C(1) << (64 - pd_prefix_len); + prefix = *masked_pd_prefix; + + if (link_has_preferred_subnet_id(link)) { + uint64_t subnet_id = link->network->router_prefix_subnet_id; - if (subnet_id >= 0) { /* If the link has a preference for a particular subnet id try to allocate that */ - if ((uint64_t) subnet_id >= n_prefixes) - return log_link_debug_errno(link, - SYNTHETIC_ERRNO(ERANGE), - "subnet id %" PRIi64 " is out of range. Only have %" PRIu64 " subnets.", - subnet_id, - n_prefixes); + if (subnet_id >= n_prefixes) + return log_link_warning_errno(link, SYNTHETIC_ERRNO(ERANGE), + "subnet id %" PRIu64 " is out of range. Only have %" PRIu64 " subnets.", + subnet_id, n_prefixes); r = in_addr_prefix_nth(AF_INET6, &prefix, 64, subnet_id); if (r < 0) - return log_link_debug_errno(link, - r, - "subnet id %" PRIi64 " is out of range. Only have %" PRIu64 " subnets.", - subnet_id, - n_prefixes); + return log_link_warning_errno(link, r, + "subnet id %" PRIu64 " is out of range. Only have %" PRIu64 " subnets.", + subnet_id, n_prefixes); /* Verify that the prefix we did calculate fits in the pd prefix. * This should not fail as we checked the prefix size beforehand */ - assert_se(in_addr_prefix_covers(AF_INET6, &pd_prefix_union, pd_prefix_len, &prefix) > 0); + assert_se(in_addr_prefix_covers(AF_INET6, masked_pd_prefix, pd_prefix_len, &prefix) > 0); - Link* assigned_link = dhcp6_prefix_get(manager, &prefix.in6); + assigned_link = dhcp6_pd_get_link_by_prefix(link, &prefix); + if (assigned_link && assigned_link != link) { + _cleanup_free_ char *assigned_buf = NULL; - (void) in_addr_to_string(AF_INET6, &prefix, &assigned_buf); + (void) in_addr_to_string(AF_INET6, &prefix, &assigned_buf); + return log_link_warning_errno(link, SYNTHETIC_ERRNO(EAGAIN), + "The requested prefix %s is already assigned to another link.", + strna(assigned_buf)); + } - if (assigned_link && assigned_link != link) - return log_link_error_errno(link, SYNTHETIC_ERRNO(EAGAIN), - "The requested prefix %s is already assigned to another link: %s", - strnull(assigned_buf), - strnull(assigned_link->ifname)); - - *ret_addr = prefix.in6; - - log_link_debug(link, "The requested prefix %s is available. Using it.", - strnull(assigned_buf)); + *ret = prefix; return 0; } for (uint64_t n = 0; n < n_prefixes; n++) { - /* if we do not have an allocation preference just iterate + /* If we do not have an allocation preference just iterate * through the address space and return the first free prefix. */ - Link* assigned_link = dhcp6_prefix_get(manager, &prefix.in6); - + assigned_link = dhcp6_pd_get_link_by_prefix(link, &prefix); if (!assigned_link || assigned_link == link) { - *ret_addr = prefix.in6; + *ret = prefix; return 0; } r = in_addr_prefix_next(AF_INET6, &prefix, 64); if (r < 0) - return log_link_error_errno(link, r, "Can't allocate another prefix. Out of address space?: %m"); + return log_link_warning_errno(link, r, "Can't allocate another prefix. Out of address space?: %m"); } return log_link_warning_errno(link, SYNTHETIC_ERRNO(ERANGE), "Couldn't find a suitable prefix. Ran out of address space."); } -static bool dhcp6_enable_prefix_delegation(Link *dhcp6_link) { - Manager *manager; - Link *l; - Iterator i; - - assert(dhcp6_link); - - manager = dhcp6_link->manager; - assert(manager); - - HASHMAP_FOREACH(l, manager->links, i) { - if (l == dhcp6_link) - continue; - - if (!dhcp6_get_prefix_delegation(l)) - continue; - - return true; - } - - return false; -} - -static int dhcp6_lease_information_acquired(sd_dhcp6_client *client, Link *link) { - return 0; -} - -static int dhcp6_pd_prefix_assign(Link *link, struct in6_addr *prefix, - uint8_t prefix_len, - uint32_t lifetime_preferred, - uint32_t lifetime_valid) { - int r; - - r = radv_add_prefix(link, prefix, prefix_len, lifetime_preferred, lifetime_valid); - if (r < 0) - return r; - - r = dhcp6_prefix_add(link->manager, prefix, link); - if (r < 0) - return r; - - r = dhcp6_assign_delegated_prefix(link, prefix, prefix_len, lifetime_preferred, lifetime_valid); - if (r < 0) - return r; - - return 0; -} - -static int dhcp6_route_remove_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) { - int r; - - assert(link); - - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; - - r = sd_netlink_message_get_errno(m); - if (r < 0) - log_link_message_warning_errno(link, m, r, "Received error on unreachable route removal for DHCPv6 delegated subnet"); - - return 1; -} - -int dhcp6_lease_pd_prefix_lost(sd_dhcp6_client *client, Link* link) { - uint32_t lifetime_preferred, lifetime_valid; - union in_addr_union pd_prefix; - uint8_t pd_prefix_len; - sd_dhcp6_lease *lease; - int r; - - r = sd_dhcp6_client_get_lease(client, &lease); - if (r < 0) - return r; - - sd_dhcp6_lease_reset_pd_prefix_iter(lease); - - while (sd_dhcp6_lease_get_pd(lease, &pd_prefix.in6, &pd_prefix_len, - &lifetime_preferred, - &lifetime_valid) >= 0) { - _cleanup_free_ char *buf = NULL; - _cleanup_(route_freep) Route *route = NULL; - - if (pd_prefix_len >= 64) - continue; - - (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf); - - r = route_new(&route); - if (r < 0) - return r; - - route->family = AF_INET6; - route->dst = pd_prefix; - route->dst_prefixlen = pd_prefix_len; - route->type = RTN_UNREACHABLE; - - r = route_remove(route, link, dhcp6_route_remove_handler); - if (r < 0) { - log_link_warning_errno(link, r, "Cannot delete unreachable route for DHCPv6 delegated subnet %s/%u: %m", - strnull(buf), - pd_prefix_len); - continue; - } - - log_link_debug(link, "Removing unreachable route %s/%u", - strnull(buf), pd_prefix_len); - } - - return 0; -} - -static int dhcp6_pd_prefix_distribute(Link *dhcp6_link, - struct in6_addr *pd_prefix, +static void dhcp6_pd_prefix_distribute(Link *dhcp6_link, + const union in_addr_union *masked_pd_prefix, uint8_t pd_prefix_len, uint32_t lifetime_preferred, uint32_t lifetime_valid, bool assign_preferred_subnet_id) { - _cleanup_free_ char *assigned_buf = NULL, *buf = NULL; - Manager *manager = dhcp6_link->manager; - union in_addr_union prefix = { - .in6 = *pd_prefix, - }; - bool pool_depleted = false; - uint64_t n_prefixes; Iterator i; Link *link; int r; - assert(manager); + assert(dhcp6_link); + assert(dhcp6_link->manager); + assert(masked_pd_prefix); assert(pd_prefix_len <= 64); - r = in_addr_mask(AF_INET6, &prefix, pd_prefix_len); - if (r < 0) - return r; - - n_prefixes = UINT64_C(1) << (64 - pd_prefix_len); - - (void) in_addr_to_string(AF_INET6, &prefix, &buf); - log_link_debug(dhcp6_link, "Assigning up to %" PRIu64 " prefixes from %s/%u", - n_prefixes, strnull(buf), pd_prefix_len); - - HASHMAP_FOREACH(link, manager->links, i) { + HASHMAP_FOREACH(link, dhcp6_link->manager->links, i) { + _cleanup_free_ char *assigned_buf = NULL; union in_addr_union assigned_prefix; if (link == dhcp6_link) continue; - if (!dhcp6_get_prefix_delegation(link)) + if (!link_dhcp6_pd_is_enabled(link)) continue; - if (assign_preferred_subnet_id != dhcp6_has_preferred_subnet_id(link)) + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) continue; - r = dhcp6_get_preferred_delegated_prefix(manager, link, &prefix.in6, pd_prefix_len, - &assigned_prefix.in6); - - if (assign_preferred_subnet_id && r == -EAGAIN) { - /* A link has a preferred subnet_id but that one is - * already taken by another link. Now all the remaining - * links will also not obtain a prefix. */ - pool_depleted = true; + if (assign_preferred_subnet_id != link_has_preferred_subnet_id(link)) continue; - } else if (r < 0) - return r; + + r = dhcp6_pd_get_assigned_prefix(link, masked_pd_prefix, &assigned_prefix); + if (r < 0) { + r = dhcp6_get_preferred_delegated_prefix(link, masked_pd_prefix, pd_prefix_len, &assigned_prefix); + if (r < 0) { + link->dhcp6_pd_prefixes_assigned = false; + continue; + } + } (void) in_addr_to_string(AF_INET6, &assigned_prefix, &assigned_buf); - r = dhcp6_pd_prefix_assign(link, &assigned_prefix.in6, 64, + r = dhcp6_pd_assign_prefix(link, &assigned_prefix, masked_pd_prefix, 64, lifetime_preferred, lifetime_valid); if (r < 0) { - log_link_error_errno(link, r, "Unable to assign/update prefix %s/64 from %s/%u for link: %m", - strnull(assigned_buf), - strnull(buf), pd_prefix_len); + log_link_error_errno(link, r, "Unable to assign/update prefix %s/64: %m", + strna(assigned_buf)); + link_enter_failed(link); } else - log_link_debug(link, "Assigned prefix %s/64 from %s/%u to link", - strnull(assigned_buf), - strnull(buf), pd_prefix_len); + log_link_debug(link, "Assigned prefix %s/64", strna(assigned_buf)); + } +} + +static int dhcp6_pd_prepare(Link *link) { + Address *address; + Route *route; + int r; + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 0; + + if (!link_dhcp6_pd_is_enabled(link)) + return 0; + + link_dirty(link); + + link->dhcp6_pd_address_configured = false; + link->dhcp6_pd_route_configured = false; + link->dhcp6_pd_prefixes_assigned = true; + + while ((address = set_steal_first(link->dhcp6_pd_addresses))) { + r = set_ensure_put(&link->dhcp6_pd_addresses_old, &address_hash_ops, address); + if (r < 0) + return log_link_error_errno(link, r, "Failed to store old DHCPv6 Prefix Delegation address: %m"); } - /* If one of the link requests couldn't be fulfilled, signal that we - should try again with another prefix. */ - if (pool_depleted) - return -EAGAIN; + while ((route = set_steal_first(link->dhcp6_pd_routes))) { + r = set_ensure_put(&link->dhcp6_pd_routes_old, &route_hash_ops, route); + if (r < 0) + return log_link_error_errno(link, r, "Failed to store old DHCPv6 Prefix Delegation route: %m"); + } return 0; } +static int dhcp6_pd_finalize(Link *link) { + int r; + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 0; + + if (!link_dhcp6_pd_is_enabled(link)) + return 0; + + if (link->dhcp6_pd_address_messages == 0) { + if (link->dhcp6_pd_prefixes_assigned) + link->dhcp6_pd_address_configured = true; + } else { + log_link_debug(link, "Setting DHCPv6 PD addresses"); + /* address_handler calls link_request_set_routes() and link_request_set_nexthop(). + * Before they are called, the related flags must be cleared. Otherwise, the link + * becomes configured state before routes are configured. */ + link->static_routes_configured = false; + link->static_nexthops_configured = false; + } + + if (link->dhcp6_pd_route_messages == 0) { + if (link->dhcp6_pd_prefixes_assigned) + link->dhcp6_pd_route_configured = true; + } else + log_link_debug(link, "Setting DHCPv6 PD routes"); + + r = dhcp6_pd_remove_old(link, false); + if (r < 0) + return r; + + if (link->dhcp6_pd_address_configured && link->dhcp6_pd_route_configured) + link_check_ready(link); + else + link_set_state(link, LINK_STATE_CONFIGURING); + + return 0; +} + +static void dhcp6_pd_prefix_lost(Link *dhcp6_link) { + Link *link; + Iterator i; + int r; + + assert(dhcp6_link); + assert(dhcp6_link->manager); + + HASHMAP_FOREACH(link, dhcp6_link->manager->links, i) { + if (link == dhcp6_link) + continue; + + if (!link_dhcp6_pd_is_enabled(link)) + continue; + + r = dhcp6_pd_remove(link); + if (r < 0) + link_enter_failed(link); + } +} + +static int dhcp6_remove_old(Link *link, bool force); + +static int dhcp6_address_callback(Address *address) { + Address *a; + Iterator i; + + assert(address); + assert(address->link); + + /* Make this called only once */ + SET_FOREACH(a, address->link->dhcp6_addresses, i) + a->callback = NULL; + + return dhcp6_remove_old(address->link, true); +} + +static int dhcp6_remove_old(Link *link, bool force) { + Address *address; + Route *route; + Iterator i; + int k, r = 0; + + assert(link); + + if (!force && (!link->dhcp6_address_configured || !link->dhcp6_route_configured)) + return 0; + + if (set_isempty(link->dhcp6_addresses_old) && set_isempty(link->dhcp6_routes_old)) + return 0; + + if (!force) { + bool set_callback = !set_isempty(link->dhcp6_addresses); + + SET_FOREACH(address, link->dhcp6_addresses, i) + if (address_is_ready(address)) { + set_callback = false; + break; + } + + if (set_callback) { + SET_FOREACH(address, link->dhcp6_addresses, i) + address->callback = dhcp6_address_callback; + return 0; + } + } + + log_link_debug(link, "Removing old DHCPv6 addresses and routes."); + + link_dirty(link); + + SET_FOREACH(route, link->dhcp6_routes_old, i) { + k = route_remove(route, link, NULL); + if (k < 0) + r = k; + } + + SET_FOREACH(address, link->dhcp6_addresses_old, i) { + k = address_remove(address, link, NULL); + if (k < 0) + r = k; + } + + return r; +} + +static int dhcp6_remove(Link *link) { + Address *address; + Route *route; + Iterator i; + int k, r = 0; + + assert(link); + + link->dhcp6_address_configured = false; + link->dhcp6_route_configured = false; + + k = dhcp6_remove_old(link, true); + if (k < 0) + r = k; + + if (set_isempty(link->dhcp6_addresses) && set_isempty(link->dhcp6_routes)) + return r; + + log_link_debug(link, "Removing DHCPv6 addresses and routes."); + + link_dirty(link); + + SET_FOREACH(route, link->dhcp6_routes, i) { + k = route_remove(route, link, NULL); + if (k < 0) + r = k; + } + + SET_FOREACH(address, link->dhcp6_addresses, i) { + k = address_remove(address, link, NULL); + if (k < 0) + r = k; + } + + return r; +} + static int dhcp6_route_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) { int r; @@ -338,186 +780,153 @@ static int dhcp6_route_handler(sd_netlink *nl, sd_netlink_message *m, Link *link if (link->dhcp6_route_messages == 0) { log_link_debug(link, "Unreachable routes for DHCPv6 delegated subnets set"); link->dhcp6_route_configured = true; + + r = dhcp6_remove_old(link, false); + if (r < 0) { + link_enter_failed(link); + return 1; + } + link_check_ready(link); } return 1; } -static int dhcp6_lease_pd_prefix_acquired(sd_dhcp6_client *client, Link *link) { - uint32_t lifetime_preferred, lifetime_valid; - union in_addr_union pd_prefix; - sd_dhcp6_lease *lease; - uint8_t pd_prefix_len; +static int dhcp6_set_unreachable_route(Link *link, const union in_addr_union *addr, uint8_t prefixlen) { + _cleanup_(route_freep) Route *route = NULL; + _cleanup_free_ char *buf = NULL; + Route *ret; int r; - link->dhcp6_route_configured = false; + assert(link); + assert(addr); - r = sd_dhcp6_client_get_lease(client, &lease); + (void) in_addr_to_string(AF_INET6, addr, &buf); + + if (prefixlen > 64) { + log_link_debug(link, "PD Prefix length > 64, ignoring prefix %s/%u", + strna(buf), prefixlen); + return 0; + } + + if (prefixlen == 64) { + log_link_debug(link, "Not adding a blocking route for DHCPv6 delegated subnet %s/64 since distributed prefix is 64", + strna(buf)); + return 1; + } + + if (prefixlen < 48) + log_link_warning(link, "PD Prefix length < 48, looks unusual %s/%u", + strna(buf), prefixlen); + + r = route_new(&route); if (r < 0) - return r; + return log_oom(); - sd_dhcp6_lease_reset_pd_prefix_iter(lease); + route->family = AF_INET6; + route->dst = *addr; + route->dst_prefixlen = prefixlen; + route->table = link_get_dhcp_route_table(link); + route->type = RTN_UNREACHABLE; - while (sd_dhcp6_lease_get_pd(lease, &pd_prefix.in6, &pd_prefix_len, - &lifetime_preferred, - &lifetime_valid) >= 0) { + r = route_configure(route, link, dhcp6_route_handler, &ret); + if (r < 0) + return log_link_error_errno(link, r, "Failed to set unreachable route for DHCPv6 delegated subnet %s/%u: %m", + strna(buf), prefixlen); - _cleanup_free_ char *buf = NULL; + link->dhcp6_route_messages++; - (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf); + r = set_ensure_put(&link->dhcp6_routes, &route_hash_ops, ret); + if (r < 0) + return log_link_error_errno(link, r, "Failed to store unreachable route for DHCPv6 delegated subnet %s/%u: %m", + strna(buf), prefixlen); - if (pd_prefix_len > 64) { - log_link_debug(link, "PD Prefix length > 64, ignoring prefix %s/%u", - strnull(buf), pd_prefix_len); + (void) set_remove(link->dhcp6_routes_old, ret); + + return 1; +} + +static int dhcp6_pd_prefix_acquired(Link *dhcp6_link) { + Iterator i; + Link *link; + int r; + + assert(dhcp6_link); + assert(dhcp6_link->dhcp6_lease); + + HASHMAP_FOREACH(link, dhcp6_link->manager->links, i) { + if (link == dhcp6_link) continue; - } - if (pd_prefix_len < 48) - log_link_warning(link, "PD Prefix length < 48, looks unusual %s/%u", - strnull(buf), pd_prefix_len); + r = dhcp6_pd_prepare(link); + if (r < 0) + link_enter_failed(link); + } - if (pd_prefix_len < 64) { - _cleanup_(route_freep) Route *route = NULL; + for (sd_dhcp6_lease_reset_pd_prefix_iter(dhcp6_link->dhcp6_lease);;) { + uint32_t lifetime_preferred, lifetime_valid; + union in_addr_union pd_prefix, prefix; + uint8_t pd_prefix_len; - r = route_new(&route); - if (r < 0) - return r; + r = sd_dhcp6_lease_get_pd(dhcp6_link->dhcp6_lease, &pd_prefix.in6, &pd_prefix_len, + &lifetime_preferred, &lifetime_valid); + if (r < 0) + break; - route->family = AF_INET6; - route->dst = pd_prefix; - route->dst_prefixlen = pd_prefix_len; - route->table = link_get_dhcp_route_table(link); - route->type = RTN_UNREACHABLE; - - r = route_configure(route, link, dhcp6_route_handler, NULL); - if (r < 0) { - log_link_warning_errno(link, r, "Cannot configure unreachable route for delegated subnet %s/%u: %m", - strnull(buf), - pd_prefix_len); - continue; - } - if (r > 0) - link->dhcp6_route_messages++; - - log_link_debug(link, "Configuring unreachable route for %s/%u", - strnull(buf), pd_prefix_len); - } else - log_link_debug(link, "Not adding a blocking route since distributed prefix is /64"); + r = dhcp6_set_unreachable_route(dhcp6_link, &pd_prefix, pd_prefix_len); + if (r < 0) + return r; + if (r == 0) + continue; /* We are doing prefix allocation in two steps: * 1. all those links that have a preferred subnet id will be assigned their subnet - * 2. all those links that remain will receive prefixes in sequential - * order. Prefixes that were previously already allocated to another - * link will be skipped. - - * If a subnet id request couldn't be fulfilled the failure will be logged (as error) - * and no further attempts at obtaining a prefix will be made. - + * 2. all those links that remain will receive prefixes in sequential order. Prefixes + * that were previously already allocated to another link will be skipped. * The assignment has to be split in two phases since subnet id * preferences should be honored. Meaning that any subnet id should be * handed out to the requesting link and not to some link that didn't * specify any preference. */ - r = dhcp6_pd_prefix_distribute(link, &pd_prefix.in6, - pd_prefix_len, - lifetime_preferred, - lifetime_valid, - true); - if (r < 0 && r != -EAGAIN) - return r; + assert(pd_prefix_len <= 64); - /* if r == -EAGAIN then the allocation failed because we ran - * out of addresses for the preferred subnet id's. This doesn't - * mean we can't fulfill other prefix requests. - * - * Since we do not have dedicated lists of links that request - * specific subnet id's and those that accept any prefix we - * *must* reset the iterator to the start as otherwise some - * links might not get their requested prefix. */ + prefix = pd_prefix; + r = in_addr_mask(AF_INET6, &prefix, pd_prefix_len); + if (r < 0) + return log_link_error_errno(dhcp6_link, r, "Failed to mask DHCPv6 PD prefix: %m"); - r = dhcp6_pd_prefix_distribute(link, &pd_prefix.in6, - pd_prefix_len, - lifetime_preferred, - lifetime_valid, - false); - if (r < 0 && r != -EAGAIN) - return r; + if (DEBUG_LOGGING) { + uint64_t n_prefixes = UINT64_C(1) << (64 - pd_prefix_len); + _cleanup_free_ char *buf = NULL; - /* If the prefix distribution did return -EAGAIN we will try to - * fulfill those with the next available pd delegated prefix. */ + (void) in_addr_to_string(AF_INET6, &prefix, &buf); + log_link_debug(dhcp6_link, "Assigning up to %" PRIu64 " prefixes from %s/%u", + n_prefixes, strna(buf), pd_prefix_len); + } + + dhcp6_pd_prefix_distribute(dhcp6_link, + &prefix, + pd_prefix_len, + lifetime_preferred, + lifetime_valid, + true); + + dhcp6_pd_prefix_distribute(dhcp6_link, + &prefix, + pd_prefix_len, + lifetime_preferred, + lifetime_valid, + false); } - if (link->dhcp6_route_messages == 0) { - link->dhcp6_route_configured = true; - link_check_ready(link); - } else { - log_link_debug(link, "Setting unreachable routes for DHCPv6 delegated subnets"); - link_set_state(link, LINK_STATE_CONFIGURING); - } - - return 0; -} - -int dhcp6_request_prefix_delegation(Link *link) { - Link *l; - Iterator i; - - assert_return(link, -EINVAL); - assert_return(link->manager, -EOPNOTSUPP); - - if (dhcp6_get_prefix_delegation(link) <= 0) - return 0; - - log_link_debug(link, "Requesting DHCPv6 prefixes to be delegated for new link"); - - HASHMAP_FOREACH(l, link->manager->links, i) { - int r, enabled; - - if (l == link) + HASHMAP_FOREACH(link, dhcp6_link->manager->links, i) { + if (link == dhcp6_link) continue; - if (!l->dhcp6_client) - continue; - - r = sd_dhcp6_client_get_prefix_delegation(l->dhcp6_client, &enabled); - if (r < 0) { - log_link_warning_errno(l, r, "Cannot get prefix delegation when adding new link: %m"); - continue; - } - - if (enabled == 0) { - r = sd_dhcp6_client_set_prefix_delegation(l->dhcp6_client, 1); - if (r < 0) { - log_link_warning_errno(l, r, "Cannot enable prefix delegation when adding new link: %m"); - continue; - } - } - - r = sd_dhcp6_client_is_running(l->dhcp6_client); - if (r <= 0) - continue; - - if (enabled != 0) { - log_link_debug(l, "Requesting re-assignment of delegated prefixes after adding new link"); - (void) dhcp6_lease_pd_prefix_acquired(l->dhcp6_client, l); - - continue; - } - - r = sd_dhcp6_client_stop(l->dhcp6_client); - if (r < 0) { - log_link_warning_errno(l, r, "Cannot stop DHCPv6 prefix delegation client after adding new link: %m"); - continue; - } - - r = sd_dhcp6_client_start(l->dhcp6_client); - if (r < 0) { - log_link_warning_errno(l, r, "Cannot restart DHCPv6 prefix delegation client after adding new link: %m"); - continue; - } - - log_link_debug(l, "Restarted DHCPv6 client to acquire prefix delegations after adding new link"); + r = dhcp6_pd_finalize(link); + if (r < 0) + link_enter_failed(link); } return 0; @@ -545,6 +954,13 @@ static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link * if (link->dhcp6_address_messages == 0) { log_link_debug(link, "DHCPv6 addresses set"); link->dhcp6_address_configured = true; + + r = dhcp6_remove_old(link, false); + if (r < 0) { + link_enter_failed(link); + return 1; + } + r = link_request_set_routes(link); if (r < 0) { link_enter_failed(link); @@ -555,19 +971,20 @@ static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link * return 1; } -static int dhcp6_address_change( +static int dhcp6_update_address( Link *link, - struct in6_addr *ip6_addr, + const struct in6_addr *ip6_addr, uint32_t lifetime_preferred, uint32_t lifetime_valid) { _cleanup_(address_freep) Address *addr = NULL; _cleanup_free_ char *buffer = NULL; + Address *ret; int r; r = address_new(&addr); if (r < 0) - return r; + return log_oom(); addr->family = AF_INET6; addr->in_addr.in6 = *ip6_addr; @@ -577,60 +994,146 @@ static int dhcp6_address_change( addr->cinfo.ifa_valid = lifetime_valid; (void) in_addr_to_string(addr->family, &addr->in_addr, &buffer); - log_link_info(link, - "DHCPv6 address %s/%d timeout preferred %d valid %d", - strnull(buffer), addr->prefixlen, lifetime_preferred, lifetime_valid); + log_link_info(link, "DHCPv6 address %s/%u timeout preferred %d valid %d", + strna(buffer), addr->prefixlen, lifetime_preferred, lifetime_valid); - r = address_configure(addr, link, dhcp6_address_handler, true, NULL); + r = address_configure(addr, link, dhcp6_address_handler, true, &ret); if (r < 0) - return log_link_warning_errno(link, r, "Could not assign DHCPv6 address: %m"); - if (r > 0) - link->dhcp6_address_messages++; + return log_link_error_errno(link, r, "Failed to set DHCPv6 address %s/%u: %m", + strna(buffer), addr->prefixlen); + + link->dhcp6_address_messages++; + + r = set_ensure_put(&link->dhcp6_addresses, &address_hash_ops, ret); + if (r < 0) + return log_link_error_errno(link, r, "Failed to store DHCPv6 address %s/%u: %m", + strna(buffer), addr->prefixlen); + + (void) set_remove(link->dhcp6_addresses_old, ret); return 0; } -static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link) { +static int dhcp6_address_acquired(Link *link) { int r; - sd_dhcp6_lease *lease; - struct in6_addr ip6_addr; - uint32_t lifetime_preferred, lifetime_valid; - link->dhcp6_address_configured = false; + assert(link); + assert(link->dhcp6_lease); - r = sd_dhcp6_client_get_lease(client, &lease); - if (r < 0) - return r; + for (sd_dhcp6_lease_reset_address_iter(link->dhcp6_lease);;) { + uint32_t lifetime_preferred, lifetime_valid; + struct in6_addr ip6_addr; - sd_dhcp6_lease_reset_address_iter(lease); - while (sd_dhcp6_lease_get_address(lease, &ip6_addr, - &lifetime_preferred, - &lifetime_valid) >= 0) { + r = sd_dhcp6_lease_get_address(link->dhcp6_lease, &ip6_addr, &lifetime_preferred, &lifetime_valid); + if (r < 0) + break; - r = dhcp6_address_change(link, &ip6_addr, lifetime_preferred, lifetime_valid); + r = dhcp6_update_address(link, &ip6_addr, lifetime_preferred, lifetime_valid); if (r < 0) return r; } - if (link->dhcp6_address_messages == 0) { + return 0; +} + +static int dhcp6_lease_ip_acquired(sd_dhcp6_client *client, Link *link) { + _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease_old = NULL; + sd_dhcp6_lease *lease; + Address *a; + Route *rt; + int r; + + link->dhcp6_address_configured = false; + link->dhcp6_route_configured = false; + + link_dirty(link); + + while ((a = set_steal_first(link->dhcp6_addresses))) { + r = set_ensure_put(&link->dhcp6_addresses_old, &address_hash_ops, a); + if (r < 0) + return log_link_error_errno(link, r, "Failed to store old DHCPv6 address: %m"); + } + + while ((rt = set_steal_first(link->dhcp6_routes))) { + r = set_ensure_put(&link->dhcp6_routes_old, &route_hash_ops, rt); + if (r < 0) + return log_link_error_errno(link, r, "Failed to store old DHCPv6 route: %m"); + } + + r = sd_dhcp6_client_get_lease(client, &lease); + if (r < 0) + return log_link_error_errno(link, r, "Failed to get DHCPv6 lease: %m"); + + lease_old = TAKE_PTR(link->dhcp6_lease); + link->dhcp6_lease = sd_dhcp6_lease_ref(lease); + + r = dhcp6_address_acquired(link); + if (r < 0) + return r; + + if (dhcp6_lease_has_pd_prefix(lease)) { + r = dhcp6_pd_prefix_acquired(link); + if (r < 0) + return r; + } else if (dhcp6_lease_has_pd_prefix(lease_old)) + /* When we had PD prefixes but not now, we need to remove them. */ + dhcp6_pd_prefix_lost(link); + + if (link->dhcp6_address_messages == 0) link->dhcp6_address_configured = true; - return link_request_set_routes(link); - } else { + else { log_link_debug(link, "Setting DHCPv6 addresses"); /* address_handler calls link_request_set_routes() and link_request_set_nexthop(). * Before they are called, the related flags must be cleared. Otherwise, the link * becomes configured state before routes are configured. */ link->static_routes_configured = false; link->static_nexthops_configured = false; - link_set_state(link, LINK_STATE_CONFIGURING); } + if (link->dhcp6_route_messages == 0) + link->dhcp6_route_configured = true; + else + log_link_debug(link, "Setting unreachable routes for DHCPv6 delegated subnets"); + + r = dhcp6_remove_old(link, false); + if (r < 0) + return r; + + if (link->dhcp6_address_configured && link->dhcp6_route_configured) + link_check_ready(link); + else + link_set_state(link, LINK_STATE_CONFIGURING); + + return 0; +} + +static int dhcp6_lease_information_acquired(sd_dhcp6_client *client, Link *link) { + return 0; +} + +static int dhcp6_lease_lost(Link *link) { + int r; + + assert(link); + assert(link->manager); + + log_link_info(link, "DHCPv6 lease lost"); + + if (dhcp6_lease_has_pd_prefix(link->dhcp6_lease)) + dhcp6_pd_prefix_lost(link); + + link->dhcp6_lease = sd_dhcp6_lease_unref(link->dhcp6_lease); + + r = dhcp6_remove(link); + if (r < 0) + return r; + return 0; } static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) { - int r; Link *link = userdata; + int r; assert(link); assert(link->network); @@ -638,39 +1141,27 @@ static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) { if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) return; - switch(event) { + switch (event) { case SD_DHCP6_CLIENT_EVENT_STOP: case SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE: case SD_DHCP6_CLIENT_EVENT_RETRANS_MAX: - if (sd_dhcp6_client_get_lease(client, NULL) >= 0) - log_link_warning(link, "DHCPv6 lease lost"); - - (void) dhcp6_lease_pd_prefix_lost(client, link); - (void) dhcp6_prefix_remove_all(link->manager, link); - - link_dirty(link); + r = dhcp6_lease_lost(link); + if (r < 0) + link_enter_failed(link); break; case SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE: - r = dhcp6_lease_address_acquired(client, link); + r = dhcp6_lease_ip_acquired(client, link); if (r < 0) { link_enter_failed(link); return; } - r = dhcp6_lease_pd_prefix_acquired(client, link); - if (r < 0) - log_link_debug_errno(link, r, "DHCPv6 did not receive prefixes to delegate: %m"); - _fallthrough_; case SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST: r = dhcp6_lease_information_acquired(client, link); - if (r < 0) { + if (r < 0) link_enter_failed(link); - return; - } - - link_dirty(link); break; default: @@ -680,8 +1171,6 @@ static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) { log_link_warning(link, "DHCPv6 unknown event: %d", event); return; } - - link_check_ready(link); } int dhcp6_request_address(Link *link, int ir) { @@ -691,7 +1180,7 @@ int dhcp6_request_address(Link *link, int ir) { assert(link); assert(link->dhcp6_client); assert(link->network); - assert(in_addr_is_link_local(AF_INET6, (const union in_addr_union*)&link->ipv6ll_address) > 0); + assert(in_addr_is_link_local(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address) > 0); r = sd_dhcp6_client_is_running(link->dhcp6_client); if (r < 0) @@ -705,8 +1194,7 @@ int dhcp6_request_address(Link *link, int ir) { if (pd && ir && link->network->dhcp6_force_pd_other_information) { log_link_debug(link, "Enabling managed mode to request DHCPv6 PD with 'Other Information' set"); - r = sd_dhcp6_client_set_address_request(link->dhcp6_client, - false); + r = sd_dhcp6_client_set_address_request(link->dhcp6_client, false); if (r < 0) return r; @@ -741,6 +1229,81 @@ int dhcp6_request_address(Link *link, int ir) { return 0; } +int dhcp6_request_prefix_delegation(Link *link) { + Link *l; + Iterator i; + + assert(link); + assert(link->manager); + + if (!link_dhcp6_pd_is_enabled(link)) + return 0; + + log_link_debug(link, "Requesting DHCPv6 prefixes to be delegated for new link"); + + HASHMAP_FOREACH(l, link->manager->links, i) { + int r, enabled; + + if (l == link) + continue; + + if (!l->dhcp6_client) + continue; + + r = sd_dhcp6_client_get_prefix_delegation(l->dhcp6_client, &enabled); + if (r < 0) { + log_link_warning_errno(l, r, "Cannot get prefix delegation when adding new link: %m"); + link_enter_failed(l); + continue; + } + + if (enabled == 0) { + r = sd_dhcp6_client_set_prefix_delegation(l->dhcp6_client, 1); + if (r < 0) { + log_link_warning_errno(l, r, "Cannot enable prefix delegation when adding new link: %m"); + link_enter_failed(l); + continue; + } + } + + r = sd_dhcp6_client_is_running(l->dhcp6_client); + if (r <= 0) + continue; + + if (enabled != 0) { + if (dhcp6_lease_has_pd_prefix(l->dhcp6_lease)) { + log_link_debug(l, "Requesting re-assignment of delegated prefixes after adding new link"); + r = dhcp6_pd_prefix_acquired(l); + if (r < 0) + link_enter_failed(l); + } + continue; + } + + r = sd_dhcp6_client_stop(l->dhcp6_client); + if (r < 0) { + log_link_warning_errno(l, r, "Cannot stop DHCPv6 prefix delegation client after adding new link: %m"); + link_enter_failed(l); + continue; + } + + r = sd_dhcp6_client_start(l->dhcp6_client); + if (r < 0) { + log_link_warning_errno(l, r, "Cannot restart DHCPv6 prefix delegation client after adding new link: %m"); + link_enter_failed(l); + continue; + } + + log_link_debug(l, "Restarted DHCPv6 client to acquire prefix delegations after adding new link"); + } + + /* dhcp6_pd_prefix_acquired() may make the link in failed state. */ + if (link->state == LINK_STATE_FAILED) + return -ENOANO; + + return 0; +} + static int dhcp6_set_hostname(sd_dhcp6_client *client, Link *link) { _cleanup_free_ char *hostname = NULL; const char *hn; @@ -770,6 +1333,26 @@ static int dhcp6_set_hostname(sd_dhcp6_client *client, Link *link) { return 0; } +static bool dhcp6_enable_prefix_delegation(Link *dhcp6_link) { + Link *link; + Iterator i; + + assert(dhcp6_link); + assert(dhcp6_link->manager); + + HASHMAP_FOREACH(link, dhcp6_link->manager->links, i) { + if (link == dhcp6_link) + continue; + + if (!link_dhcp6_pd_is_enabled(link)) + continue; + + return true; + } + + return false; +} + int dhcp6_configure(Link *link) { _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL; sd_dhcp6_option *vendor_option; @@ -899,246 +1482,6 @@ int dhcp6_configure(Link *link) { return 0; } -static Link *dhcp6_prefix_get(Manager *m, struct in6_addr *addr) { - assert_return(m, NULL); - assert_return(addr, NULL); - - return hashmap_get(m->dhcp6_prefixes, addr); -} - -static int dhcp6_pd_route_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) { - int r; - - assert(link); - assert(link->dhcp6_pd_route_messages > 0); - - link->dhcp6_pd_route_messages--; - - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; - - r = sd_netlink_message_get_errno(m); - if (r < 0 && r != -EEXIST) { - log_link_message_warning_errno(link, m, r, "Failed to add DHCPv6 Prefix Delegation route"); - link_enter_failed(link); - return 1; - } - - if (link->dhcp6_pd_route_messages == 0) { - log_link_debug(link, "DHCPv6 prefix delegation routes set"); - link->dhcp6_pd_route_configured = true; - link_check_ready(link); - } - - return 1; -} - -static int dhcp6_prefix_add(Manager *m, struct in6_addr *addr, Link *link) { - _cleanup_(route_freep) Route *route = NULL; - _cleanup_free_ struct in6_addr *a = NULL; - _cleanup_free_ char *buf = NULL; - Link *assigned_link; - int r; - - assert_return(m, -EINVAL); - assert_return(addr, -EINVAL); - - r = route_new(&route); - if (r < 0) - return r; - - route->family = AF_INET6; - route->dst.in6 = *addr; - route->dst_prefixlen = 64; - - link->dhcp6_pd_route_configured = false; - link_set_state(link, LINK_STATE_CONFIGURING); - - r = route_configure(route, link, dhcp6_pd_route_handler, NULL); - if (r < 0) - return r; - if (r > 0) - link->dhcp6_pd_route_messages++; - - (void) in_addr_to_string(AF_INET6, (union in_addr_union *) addr, &buf); - log_link_debug(link, "Adding prefix route %s/64", strnull(buf)); - - assigned_link = hashmap_get(m->dhcp6_prefixes, addr); - if (assigned_link) { - assert(assigned_link == link); - return 0; - } - - a = newdup(struct in6_addr, addr, 1); - if (!a) - return -ENOMEM; - - r = hashmap_ensure_allocated(&m->dhcp6_prefixes, &in6_addr_hash_ops); - if (r < 0) - return r; - - r = hashmap_put(m->dhcp6_prefixes, a, link); - if (r < 0) - return r; - - TAKE_PTR(a); - link_ref(link); - return 0; -} - -static int dhcp6_prefix_remove_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) { - int r; - - assert(link); - - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; - - r = sd_netlink_message_get_errno(m); - if (r < 0) { - log_link_message_warning_errno(link, m, r, "Received error on DHCPv6 Prefix Delegation route removal"); - link_enter_failed(link); - return 1; - } - - return 1; -} - -int dhcp6_prefix_remove(Manager *m, struct in6_addr *addr) { - _cleanup_free_ struct in6_addr *a = NULL; - _cleanup_(link_unrefp) Link *l = NULL; - _cleanup_(route_freep) Route *route = NULL; - _cleanup_free_ char *buf = NULL; - int r; - - assert_return(m, -EINVAL); - assert_return(addr, -EINVAL); - - l = hashmap_remove2(m->dhcp6_prefixes, addr, (void **) &a); - if (!l) - return -EINVAL; - - (void) sd_radv_remove_prefix(l->radv, addr, 64); - - r = route_new(&route); - if (r < 0) - return r; - - route->family = AF_INET6; - route->dst.in6 = *addr; - route->dst_prefixlen = 64; - - r = route_remove(route, l, dhcp6_prefix_remove_handler); - if (r < 0) - return r; - - (void) in_addr_to_string(AF_INET6, (union in_addr_union *) addr, &buf); - log_link_debug(l, "Removing prefix route %s/64", strnull(buf)); - - return 0; -} - -static int dhcp6_prefix_remove_all(Manager *m, Link *link) { - struct in6_addr *addr; - Iterator i; - Link *l; - - assert_return(m, -EINVAL); - assert_return(link, -EINVAL); - - HASHMAP_FOREACH_KEY(l, addr, m->dhcp6_prefixes, i) - if (l == link) - (void) dhcp6_prefix_remove(m, addr); - - return 0; -} - -static int dhcp6_pd_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { - int r; - - assert(link); - assert(link->dhcp6_pd_address_messages > 0); - - link->dhcp6_pd_address_messages--; - - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; - - r = sd_netlink_message_get_errno(m); - if (r < 0 && r != -EEXIST) { - log_link_message_warning_errno(link, m, r, "Could not set DHCPv6 delegated prefix address"); - link_enter_failed(link); - return 1; - } else if (r >= 0) - (void) manager_rtnl_process_address(rtnl, m, link->manager); - - if (link->dhcp6_pd_address_messages == 0) { - log_link_debug(link, "DHCPv6 delegated prefix addresses set"); - link->dhcp6_pd_address_configured = true; - r = link_request_set_routes(link); - if (r < 0) { - link_enter_failed(link); - return 1; - } - } - - return 1; -} - -static int dhcp6_assign_delegated_prefix(Link *link, - const struct in6_addr *prefix, - uint8_t prefix_len, - uint32_t lifetime_preferred, - uint32_t lifetime_valid) { - - _cleanup_(address_freep) Address *address = NULL; - int r; - - assert(link); - assert(link->network); - assert(prefix); - - if (!link->network->dhcp6_pd_assign_prefix) { - link->dhcp6_pd_address_configured = true; - return 0; - } - - r = address_new(&address); - if (r < 0) - return log_link_error_errno(link, r, "Failed to allocate address for DHCPv6 delegated prefix: %m"); - - address->in_addr.in6 = *prefix; - - if (!in_addr_is_null(AF_INET6, &link->network->dhcp6_delegation_prefix_token)) - memcpy(address->in_addr.in6.s6_addr + 8, link->network->dhcp6_delegation_prefix_token.in6.s6_addr + 8, 8); - else { - r = generate_ipv6_eui_64_address(link, &address->in_addr.in6); - if (r < 0) - return log_link_warning_errno(link, r, "Failed to generate EUI64 address for acquired DHCPv6 delegated prefix: %m"); - } - - address->prefixlen = prefix_len; - address->family = AF_INET6; - address->cinfo.ifa_prefered = lifetime_preferred; - address->cinfo.ifa_valid = lifetime_valid; - - /* address_handler calls link_request_set_routes() and link_request_set_nexthop(). Before they - * are called, the related flags must be cleared. Otherwise, the link becomes configured state - * before routes are configured. */ - link->static_routes_configured = false; - link->static_nexthops_configured = false; - link->dhcp6_pd_address_configured = false; - link_set_state(link, LINK_STATE_CONFIGURING); - - r = address_configure(address, link, dhcp6_pd_address_handler, true, NULL); - if (r < 0) - return log_link_warning_errno(link, r, "Failed to set acquired DHCPv6 delegated prefix address: %m"); - if (r > 0) - link->dhcp6_pd_address_messages++; - - return 0; -} - int config_parse_dhcp6_pd_hint( const char* unit, const char *filename, @@ -1209,7 +1552,6 @@ int config_parse_dhcp6_mud_url( if (!http_url_is_valid(unescaped) || strlen(unescaped) > UINT8_MAX) { log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse MUD URL '%s', ignoring: %m", rvalue); - return 0; } diff --git a/src/network/networkd-dhcp6.h b/src/network/networkd-dhcp6.h index cc416b98d2..fa44a2eb52 100644 --- a/src/network/networkd-dhcp6.h +++ b/src/network/networkd-dhcp6.h @@ -17,12 +17,20 @@ typedef enum DHCP6ClientStartMode { typedef struct Link Link; typedef struct Manager Manager; -bool dhcp6_get_prefix_delegation(Link *link); -int dhcp6_request_prefix_delegation(Link *link); +typedef struct DHCP6DelegatedPrefix { + struct in6_addr prefix; /* Prefix assigned to the link */ + struct in6_addr pd_prefix; /* PD prefix provided by DHCP6 lease */ + Link *link; +} DHCP6DelegatedPrefix; + +DHCP6DelegatedPrefix *dhcp6_pd_free(DHCP6DelegatedPrefix *p); +DEFINE_TRIVIAL_CLEANUP_FUNC(DHCP6DelegatedPrefix*, dhcp6_pd_free); + +bool link_dhcp6_pd_is_enabled(Link *link); +int dhcp6_pd_remove(Link *link); int dhcp6_configure(Link *link); int dhcp6_request_address(Link *link, int ir); -int dhcp6_lease_pd_prefix_lost(sd_dhcp6_client *client, Link* link); -int dhcp6_prefix_remove(Manager *m, struct in6_addr *addr); +int dhcp6_request_prefix_delegation(Link *link); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_pd_hint); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_mud_url); diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 5faf027396..8adeca7eb2 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -698,6 +698,7 @@ static void link_free_engines(Link *link) { link->ipv4ll = sd_ipv4ll_unref(link->ipv4ll); link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client); + link->dhcp6_lease = sd_dhcp6_lease_unref(link->dhcp6_lease); link->ndisc = sd_ndisc_unref(link->ndisc); link->radv = sd_radv_unref(link->radv); } @@ -714,6 +715,10 @@ static Link *link_free(Link *link) { link->routes_foreign = set_free(link->routes_foreign); link->dhcp_routes = set_free(link->dhcp_routes); link->dhcp_routes_old = set_free(link->dhcp_routes_old); + link->dhcp6_routes = set_free(link->dhcp6_routes); + link->dhcp6_routes_old = set_free(link->dhcp6_routes_old); + link->dhcp6_pd_routes = set_free(link->dhcp6_pd_routes); + link->dhcp6_pd_routes_old = set_free(link->dhcp6_pd_routes_old); link->ndisc_routes = set_free(link->ndisc_routes); link->ndisc_routes_old = set_free(link->ndisc_routes_old); @@ -725,6 +730,10 @@ static Link *link_free(Link *link) { link->addresses = set_free(link->addresses); link->addresses_foreign = set_free(link->addresses_foreign); + link->dhcp6_addresses = set_free(link->dhcp6_addresses); + link->dhcp6_addresses_old = set_free(link->dhcp6_addresses_old); + link->dhcp6_pd_addresses = set_free(link->dhcp6_pd_addresses); + link->dhcp6_pd_addresses_old = set_free(link->dhcp6_pd_addresses_old); link->ndisc_addresses = set_free(link->ndisc_addresses); link->ndisc_addresses_old = set_free(link->ndisc_addresses_old); @@ -839,6 +848,12 @@ int link_stop_clients(Link *link, bool may_keep_dhcp) { r = log_link_warning_errno(link, k, "Could not stop DHCPv6 client: %m"); } + if (link_dhcp6_pd_is_enabled(link)) { + k = dhcp6_pd_remove(link); + if (k < 0) + r = log_link_warning_errno(link, k, "Could not remove DHCPv6 PD addresses and routes: %m"); + } + if (link->ndisc) { k = sd_ndisc_stop(link->ndisc); if (k < 0) @@ -1173,7 +1188,7 @@ void link_check_ready(Link *link) { return; } - if (link_dhcp4_enabled(link) || link_dhcp6_enabled(link) || dhcp6_get_prefix_delegation(link) || link_ipv6_accept_ra_enabled(link)) { + if (link_dhcp4_enabled(link) || link_dhcp6_enabled(link) || link_dhcp6_pd_is_enabled(link) || link_ipv6_accept_ra_enabled(link)) { if (!link->dhcp4_configured && !(link->dhcp6_address_configured && link->dhcp6_route_configured) && !(link->dhcp6_pd_address_configured && link->dhcp6_pd_route_configured) && @@ -1619,12 +1634,14 @@ static int link_acquire_ipv6_conf(Link *link) { r = dhcp6_request_address(link, link->network->dhcp6_without_ra == DHCP6_CLIENT_START_MODE_INFORMATION_REQUEST); if (r < 0 && r != -EBUSY) - return log_link_warning_errno(link, r, "Could not acquire DHCPv6 lease: %m"); + return log_link_warning_errno(link, r, "Could not acquire DHCPv6 lease: %m"); else log_link_debug(link, "Acquiring DHCPv6 lease"); } - (void) dhcp6_request_prefix_delegation(link); + r = dhcp6_request_prefix_delegation(link); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to request DHCPv6 prefix delegation: %m"); return 0; } @@ -4242,7 +4259,6 @@ int link_save(Link *link) { if (link->network) { char **dhcp6_domains = NULL, **dhcp_domains = NULL; const char *dhcp_domainname = NULL, *p; - sd_dhcp6_lease *dhcp6_lease = NULL; bool space; fprintf(f, "REQUIRED_FOR_ONLINE=%s\n", @@ -4254,12 +4270,6 @@ int link_save(Link *link) { st.max != LINK_OPERSTATE_RANGE_DEFAULT.max ? ":" : "", st.max != LINK_OPERSTATE_RANGE_DEFAULT.max ? strempty(link_operstate_to_string(st.max)) : ""); - if (link->dhcp6_client) { - r = sd_dhcp6_client_get_lease(link->dhcp6_client, &dhcp6_lease); - if (r < 0 && r != -ENOMSG) - log_link_debug_errno(link, r, "Failed to get DHCPv6 lease: %m"); - } - fprintf(f, "NETWORK_FILE=%s\n", link->network->filename); /************************************************************/ @@ -4276,7 +4286,7 @@ int link_save(Link *link) { link->dhcp_lease, link->network->dhcp_use_dns, SD_DHCP_LEASE_DNS, - dhcp6_lease, + link->dhcp6_lease, link->network->dhcp6_use_dns, sd_dhcp6_lease_get_dns, NULL); @@ -4300,7 +4310,7 @@ int link_save(Link *link) { link->dhcp_lease, link->network->dhcp_use_ntp, SD_DHCP_LEASE_NTP, - dhcp6_lease, + link->dhcp6_lease, link->network->dhcp6_use_ntp, sd_dhcp6_lease_get_ntp_addrs, sd_dhcp6_lease_get_ntp_fqdn); @@ -4319,8 +4329,8 @@ int link_save(Link *link) { (void) sd_dhcp_lease_get_domainname(link->dhcp_lease, &dhcp_domainname); (void) sd_dhcp_lease_get_search_domains(link->dhcp_lease, &dhcp_domains); } - if (dhcp6_lease) - (void) sd_dhcp6_lease_get_domains(dhcp6_lease, &dhcp6_domains); + if (link->dhcp6_lease) + (void) sd_dhcp6_lease_get_domains(link->dhcp6_lease, &dhcp6_domains); } fputs("DOMAINS=", f); diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index e079a919bc..3cd4a19289 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -102,23 +102,10 @@ typedef struct Link { uint32_t original_mtu; unsigned dhcp4_messages; unsigned dhcp4_remove_messages; - unsigned dhcp6_address_messages; - unsigned dhcp6_route_messages; - unsigned dhcp6_pd_address_messages; - unsigned dhcp6_pd_route_messages; bool dhcp4_route_failed:1; bool dhcp4_route_retrying:1; bool dhcp4_configured:1; bool dhcp4_address_bind:1; - bool dhcp6_address_configured:1; - bool dhcp6_route_configured:1; - bool dhcp6_pd_address_configured:1; - bool dhcp6_pd_route_configured:1; - - unsigned ndisc_addresses_messages; - unsigned ndisc_routes_messages; - bool ndisc_addresses_configured:1; - bool ndisc_routes_configured:1; sd_ipv4ll *ipv4ll; bool ipv4ll_address_configured:1; @@ -144,10 +131,28 @@ typedef struct Link { Set *ndisc_dnssl; Set *ndisc_addresses, *ndisc_addresses_old; Set *ndisc_routes, *ndisc_routes_old; + unsigned ndisc_addresses_messages; + unsigned ndisc_routes_messages; + bool ndisc_addresses_configured:1; + bool ndisc_routes_configured:1; sd_radv *radv; sd_dhcp6_client *dhcp6_client; + sd_dhcp6_lease *dhcp6_lease; + Set *dhcp6_addresses, *dhcp6_addresses_old; + Set *dhcp6_routes, *dhcp6_routes_old; + Set *dhcp6_pd_addresses, *dhcp6_pd_addresses_old; + Set *dhcp6_pd_routes, *dhcp6_pd_routes_old; + unsigned dhcp6_address_messages; + unsigned dhcp6_route_messages; + unsigned dhcp6_pd_address_messages; + unsigned dhcp6_pd_route_messages; + bool dhcp6_address_configured:1; + bool dhcp6_route_configured:1; + bool dhcp6_pd_address_configured:1; + bool dhcp6_pd_route_configured:1; + bool dhcp6_pd_prefixes_assigned:1; /* This is about LLDP reception */ sd_lldp *lldp; diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index a67ff250ee..a6c1a39e23 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -1823,27 +1823,20 @@ int manager_new(Manager **ret) { } void manager_free(Manager *m) { - struct in6_addr *a; AddressPool *pool; Link *link; + Iterator i; if (!m) return; free(m->state_file); - while ((a = hashmap_first_key(m->dhcp6_prefixes))) - (void) dhcp6_prefix_remove(m, a); - m->dhcp6_prefixes = hashmap_free(m->dhcp6_prefixes); - - while ((link = hashmap_steal_first(m->links))) { - if (link->dhcp6_client) - (void) dhcp6_lease_pd_prefix_lost(link->dhcp6_client, link); - + HASHMAP_FOREACH(link, m->links, i) (void) link_stop_clients(link, true); - link_unref(link); - } + m->dhcp6_prefixes = hashmap_free_with_destructor(m->dhcp6_prefixes, dhcp6_pd_free); + m->dhcp6_pd_prefixes = set_free_with_destructor(m->dhcp6_pd_prefixes, dhcp6_pd_free); m->dirty_links = set_free_with_destructor(m->dirty_links, link_unref); m->links_requesting_uuid = set_free_with_destructor(m->links_requesting_uuid, link_unref); diff --git a/src/network/networkd-manager.h b/src/network/networkd-manager.h index 0b3ce911c8..f4b37bd6a9 100644 --- a/src/network/networkd-manager.h +++ b/src/network/networkd-manager.h @@ -44,6 +44,7 @@ struct Manager { Hashmap *netdevs; OrderedHashmap *networks; Hashmap *dhcp6_prefixes; + Set *dhcp6_pd_prefixes; LIST_HEAD(AddressPool, address_pools); usec_t network_dirs_ts_usec; diff --git a/src/network/networkd-radv.c b/src/network/networkd-radv.c index 088cdf11a3..88ca9ee7fa 100644 --- a/src/network/networkd-radv.c +++ b/src/network/networkd-radv.c @@ -679,7 +679,7 @@ int radv_configure(Link *link) { return 0; } -int radv_add_prefix(Link *link, struct in6_addr *prefix, uint8_t prefix_len, +int radv_add_prefix(Link *link, const struct in6_addr *prefix, uint8_t prefix_len, uint32_t lifetime_preferred, uint32_t lifetime_valid) { _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL; int r; diff --git a/src/network/networkd-radv.h b/src/network/networkd-radv.h index 741aa8692f..fbd56d32a6 100644 --- a/src/network/networkd-radv.h +++ b/src/network/networkd-radv.h @@ -52,7 +52,7 @@ DEFINE_NETWORK_SECTION_FUNCTIONS(RoutePrefix, route_prefix_free); int radv_emit_dns(Link *link); int radv_configure(Link *link); -int radv_add_prefix(Link *link, struct in6_addr *prefix, uint8_t prefix_len, +int radv_add_prefix(Link *link, const struct in6_addr *prefix, uint8_t prefix_len, uint32_t lifetime_preferred, uint32_t lifetime_valid); const char* radv_prefix_delegation_to_string(RADVPrefixDelegation i) _const_; diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index d04c33a5d8..541bf1e793 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -146,6 +146,10 @@ void route_free(Route *route) { set_remove(route->link->routes_foreign, route); set_remove(route->link->dhcp_routes, route); set_remove(route->link->dhcp_routes_old, route); + set_remove(route->link->dhcp6_routes, route); + set_remove(route->link->dhcp6_routes_old, route); + set_remove(route->link->dhcp6_pd_routes, route); + set_remove(route->link->dhcp6_pd_routes_old, route); set_remove(route->link->ndisc_routes, route); set_remove(route->link->ndisc_routes_old, route); } From 15797d6a2be870279c3a45f0e5ce010a4581998d Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Thu, 23 Jul 2020 21:18:24 +0900 Subject: [PATCH 14/18] network: check at least one dynamic address is assigned when DHCP is enabled --- src/network/networkd-link.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 8adeca7eb2..894b55f4c5 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -1183,8 +1183,10 @@ void link_check_ready(Link *link) { return; } - if ((link_dhcp4_enabled(link) || link_dhcp6_enabled(link)) && set_isempty(link->addresses)) { - log_link_debug(link, "%s(): DHCP4 or DHCP6 is enabled but no address is assigned yet.", __func__); + if ((link_dhcp4_enabled(link) || link_dhcp6_enabled(link)) && + !link->dhcp_address && set_isempty(link->dhcp6_addresses) && set_isempty(link->ndisc_addresses) && + !(link_ipv4ll_enabled(link, ADDRESS_FAMILY_FALLBACK_IPV4) && link->ipv4ll_address_configured)) { + log_link_debug(link, "%s(): DHCP4 or DHCP6 is enabled but no dynamic address is assigned yet.", __func__); return; } From e55265184b305aef166a41d8eb50237e74b0a6fd Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 24 Jul 2020 00:52:32 +0900 Subject: [PATCH 15/18] network: do not assume static addresses are configured link_request_set_routes() is also called when a dynamic address is configured. At that time, static addresses may not be configured yet. Fixes #16546. --- src/network/networkd-address.c | 1 + src/network/networkd-link.c | 129 ++++++++++++++++++++++++--------- src/network/networkd-link.h | 1 + 3 files changed, 97 insertions(+), 34 deletions(-) diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index a848151c8b..747acfe6c5 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -125,6 +125,7 @@ void address_free(Address *address) { if (address->link && !address->acd) { set_remove(address->link->addresses, address); set_remove(address->link->addresses_foreign, address); + set_remove(address->link->static_addresses, address); if (address->link->dhcp_address == address) address->link->dhcp_address = NULL; if (address->link->dhcp_address_old == address) diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 894b55f4c5..47bdd2a0dc 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -730,6 +730,7 @@ static Link *link_free(Link *link) { link->addresses = set_free(link->addresses); link->addresses_foreign = set_free(link->addresses_foreign); + link->static_addresses = set_free(link->static_addresses); link->dhcp6_addresses = set_free(link->dhcp6_addresses); link->dhcp6_addresses_old = set_free(link->dhcp6_addresses_old); link->dhcp6_pd_addresses = set_free(link->dhcp6_pd_addresses); @@ -1057,12 +1058,13 @@ int link_request_set_routes(Link *link) { assert(link); assert(link->network); - assert(link->addresses_configured); - assert(link->address_messages == 0); assert(link->state != _LINK_STATE_INVALID); link->static_routes_configured = false; + if (!link->addresses_ready) + return 0; + if (!link_has_carrier(link) && !link->network->configure_without_carrier) /* During configuring addresses, the link lost its carrier. As networkd is dropping * the addresses now, let's not configure the routes either. */ @@ -1102,7 +1104,6 @@ int link_request_set_routes(Link *link) { void link_check_ready(Link *link) { Address *a; Iterator i; - int r; assert(link); @@ -1136,15 +1137,6 @@ void link_check_ready(Link *link) { return; } - if (!link->addresses_ready) { - link->addresses_ready = true; - r = link_request_set_routes(link); - if (r < 0) - link_enter_failed(link); - log_link_debug(link, "%s(): static addresses are configured. Configuring static routes.", __func__); - return; - } - if (!link->static_routes_configured) { log_link_debug(link, "%s(): static routes are not configured.", __func__); return; @@ -1246,6 +1238,50 @@ static int link_request_set_neighbors(Link *link) { return 0; } +static int link_set_bridge_fdb(Link *link) { + FdbEntry *fdb_entry; + int r; + + LIST_FOREACH(static_fdb_entries, fdb_entry, link->network->static_fdb_entries) { + r = fdb_entry_configure(link, fdb_entry); + if (r < 0) + return log_link_error_errno(link, r, "Failed to add MAC entry to static MAC table: %m"); + } + + return 0; +} + +static int static_address_ready_callback(Address *address) { + Address *a; + Iterator i; + Link *link; + + assert(address); + assert(address->link); + + link = address->link; + + if (!link->addresses_configured) + return 0; + + SET_FOREACH(a, link->static_addresses, i) + if (!address_is_ready(a)) { + _cleanup_free_ char *str = NULL; + + (void) in_addr_to_string(a->family, &a->in_addr, &str); + log_link_debug(link, "an address %s/%u is not ready", strnull(str), a->prefixlen); + return 0; + } + + /* This should not be called again */ + SET_FOREACH(a, link->static_addresses, i) + a->callback = NULL; + + link->addresses_ready = true; + + return link_request_set_routes(link); +} + static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { int r; @@ -1271,23 +1307,50 @@ static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) (void) manager_rtnl_process_address(rtnl, m, link->manager); if (link->address_messages == 0) { + Address *a; + log_link_debug(link, "Addresses set"); link->addresses_configured = true; - link_check_ready(link); + + /* When all static addresses are already ready, then static_address_ready_callback() + * will not be called automatically. So, call it here. */ + a = set_first(link->static_addresses); + if (!a) { + log_link_warning(link, "No static address is stored."); + link_enter_failed(link); + return 1; + } + if (!a->callback) { + log_link_warning(link, "Address ready callback is not set."); + link_enter_failed(link); + return 1; + } + r = a->callback(a); + if (r < 0) + link_enter_failed(link); } return 1; } -static int link_set_bridge_fdb(Link *link) { - FdbEntry *fdb_entry; +static int static_address_configure(Address *address, Link *link, bool update) { + Address *ret; int r; - LIST_FOREACH(static_fdb_entries, fdb_entry, link->network->static_fdb_entries) { - r = fdb_entry_configure(link, fdb_entry); - if (r < 0) - return log_link_error_errno(link, r, "Failed to add MAC entry to static MAC table: %m"); - } + assert(address); + assert(link); + + r = address_configure(address, link, address_handler, update, &ret); + if (r < 0) + return log_link_warning_errno(link, r, "Could not configure static address: %m"); + + link->address_messages++; + + r = set_ensure_put(&link->static_addresses, &address_hash_ops, ret); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to store static address: %m"); + + ret->callback = static_address_ready_callback; return 0; } @@ -1326,11 +1389,9 @@ static int link_request_set_addresses(Link *link) { else update = address_get(link, ad->family, &ad->in_addr, ad->prefixlen, NULL) > 0; - r = address_configure(ad, link, address_handler, update, NULL); + r = static_address_configure(ad, link, update); if (r < 0) - return log_link_warning_errno(link, r, "Could not set addresses: %m"); - if (r > 0) - link->address_messages++; + return r; } if (IN_SET(link->network->router_prefix_delegation, @@ -1344,22 +1405,20 @@ static int link_request_set_addresses(Link *link) { r = address_new(&address); if (r < 0) - return log_link_error_errno(link, r, "Could not allocate address: %m"); + return log_oom(); r = sd_radv_prefix_get_prefix(p->radv_prefix, &address->in_addr.in6, &address->prefixlen); if (r < 0) - return r; + return log_link_warning_errno(link, r, "Could not get RA prefix: %m"); r = generate_ipv6_eui_64_address(link, &address->in_addr.in6); if (r < 0) - return r; + return log_link_warning_errno(link, r, "Could not generate EUI64 address: %m"); address->family = AF_INET6; - r = address_configure(address, link, address_handler, true, NULL); + r = static_address_configure(address, link, true); if (r < 0) - return log_link_warning_errno(link, r, "Could not set addresses: %m"); - if (r > 0) - link->address_messages++; + return r; } LIST_FOREACH(labels, label, link->network->address_labels) { @@ -1370,8 +1429,7 @@ static int link_request_set_addresses(Link *link) { link->address_label_messages++; } - /* now that we can figure out a default address for the dhcp server, - start it */ + /* now that we can figure out a default address for the dhcp server, start it */ if (link_dhcp4_server_enabled(link) && (link->flags & IFF_UP)) { r = dhcp4_server_configure(link); if (r < 0) @@ -1381,7 +1439,10 @@ static int link_request_set_addresses(Link *link) { if (link->address_messages == 0) { link->addresses_configured = true; - link_check_ready(link); + link->addresses_ready = true; + r = link_request_set_routes(link); + if (r < 0) + return r; } else { log_link_debug(link, "Setting addresses"); link_set_state(link, LINK_STATE_CONFIGURING); diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index 3cd4a19289..7f99c0f47b 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -87,6 +87,7 @@ typedef struct Link { Set *addresses; Set *addresses_foreign; + Set *static_addresses; Set *neighbors; Set *neighbors_foreign; Set *routes; From ea121d8f252010ee796a0d6b6bb383c893c4ce55 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 24 Jul 2020 07:18:39 +0900 Subject: [PATCH 16/18] network: update address infomation even if link is in failed or linger state As the link may be reconfigured later. If we do not update the address information, then its setup state or operstate may not be updated correctly. --- src/network/networkd-address.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 747acfe6c5..b09d75e615 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -355,11 +355,8 @@ int address_update( int r; assert(address); + assert(address->link); assert(cinfo); - assert_return(address->link, 1); - - if (IN_SET(address->link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; ready = address_is_ready(address); @@ -367,6 +364,9 @@ int address_update( address->scope = scope; address->cinfo = *cinfo; + if (IN_SET(address->link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 0; + link_update_operstate(address->link, true); link_check_ready(address->link); From 61c0ef4ff39268fc6ad60dc1483fe0be74becc38 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 29 Jul 2020 05:09:38 +0900 Subject: [PATCH 17/18] network: make RADVPrefixDelegation enum bitfield This should not change any behavior. --- src/network/networkd-dhcp6.c | 4 +--- src/network/networkd-link.c | 4 +--- src/network/networkd-radv.c | 6 +----- src/network/networkd-radv.h | 8 ++++---- 4 files changed, 7 insertions(+), 15 deletions(-) diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index 3ac03c3eee..e87d9d1e7c 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -430,9 +430,7 @@ bool link_dhcp6_pd_is_enabled(Link *link) { if (!link->network) return false; - return IN_SET(link->network->router_prefix_delegation, - RADV_PREFIX_DELEGATION_DHCP6, - RADV_PREFIX_DELEGATION_BOTH); + return link->network->router_prefix_delegation & RADV_PREFIX_DELEGATION_DHCP6; } static bool link_has_preferred_subnet_id(Link *link) { diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 47bdd2a0dc..0057d184f7 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -1394,9 +1394,7 @@ static int link_request_set_addresses(Link *link) { return r; } - if (IN_SET(link->network->router_prefix_delegation, - RADV_PREFIX_DELEGATION_STATIC, - RADV_PREFIX_DELEGATION_BOTH)) + if (link->network->router_prefix_delegation & RADV_PREFIX_DELEGATION_STATIC) LIST_FOREACH(prefixes, p, link->network->static_prefixes) { _cleanup_(address_freep) Address *address = NULL; diff --git a/src/network/networkd-radv.c b/src/network/networkd-radv.c index 88ca9ee7fa..dbc8fd8074 100644 --- a/src/network/networkd-radv.c +++ b/src/network/networkd-radv.c @@ -650,10 +650,7 @@ int radv_configure(Link *link) { return r; } - if (IN_SET(link->network->router_prefix_delegation, - RADV_PREFIX_DELEGATION_STATIC, - RADV_PREFIX_DELEGATION_BOTH)) { - + if (link->network->router_prefix_delegation & RADV_PREFIX_DELEGATION_STATIC) { LIST_FOREACH(prefixes, p, link->network->static_prefixes) { r = sd_radv_add_prefix(link->radv, p->radv_prefix, false); if (r == -EEXIST) @@ -673,7 +670,6 @@ int radv_configure(Link *link) { if (r < 0) return r; } - } return 0; diff --git a/src/network/networkd-radv.h b/src/network/networkd-radv.h index fbd56d32a6..73143e7493 100644 --- a/src/network/networkd-radv.h +++ b/src/network/networkd-radv.h @@ -14,10 +14,10 @@ typedef struct Prefix Prefix; typedef struct RoutePrefix RoutePrefix; typedef enum RADVPrefixDelegation { - RADV_PREFIX_DELEGATION_NONE, - RADV_PREFIX_DELEGATION_STATIC, - RADV_PREFIX_DELEGATION_DHCP6, - RADV_PREFIX_DELEGATION_BOTH, + RADV_PREFIX_DELEGATION_NONE = 0, + RADV_PREFIX_DELEGATION_STATIC = 1 << 0, + RADV_PREFIX_DELEGATION_DHCP6 = 1 << 1, + RADV_PREFIX_DELEGATION_BOTH = RADV_PREFIX_DELEGATION_STATIC | RADV_PREFIX_DELEGATION_DHCP6, _RADV_PREFIX_DELEGATION_MAX, _RADV_PREFIX_DELEGATION_INVALID = -1, } RADVPrefixDelegation; From 99e015e28c8322bf714f45cd1defcf20ac2103c5 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 29 Jul 2020 05:50:04 +0900 Subject: [PATCH 18/18] network: rename settings about DHCPv6 Prefix Delegation Closes #16602. --- man/systemd.network.xml | 71 ++++++++------ src/network/networkd-dhcp6.c | 93 ++++++++++++++----- src/network/networkd-dhcp6.h | 3 +- src/network/networkd-network-gperf.gperf | 6 +- src/network/networkd-network.c | 4 +- src/network/networkd-network.h | 8 +- src/network/networkd-radv.c | 43 --------- src/network/networkd-radv.h | 1 - .../fuzz-network-parser/directives.network | 7 +- 9 files changed, 128 insertions(+), 108 deletions(-) diff --git a/man/systemd.network.xml b/man/systemd.network.xml index 0b0c751e91..77986192d4 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -809,16 +809,6 @@ false. See the [IPv6PrefixDelegation] and the [IPv6Prefix] sections for more configuration options. - - IPv6PDSubnetId= - Configure a specific subnet ID on the interface from a (previously) received prefix delegation. - You can either set "auto" (the default) or a specific subnet ID - (as defined in RFC 4291, section 2.5.4), - in which case the allowed value is hexadecimal, from 0 to 0x7fffffffffffffff inclusive. - This option is only effective when used together with IPv6PrefixDelegation= - and the corresponding configuration on the upstream interface. - - IPv6MTUBytes= Configures IPv6 maximum transmission unit (MTU). @@ -1893,25 +1883,6 @@ - - AssignAcquiredDelegatedPrefixAddress= - - Takes a boolean. Specifies whether to add an address from the delegated prefixes which are received - from the WAN interface by the IPv6PrefixDelegation=. When true (on LAN interfce), the EUI-64 - algorithm will be used to form an interface identifier from the delegated prefixes. Defaults to true. - - - - - AssignAcquiredDelegatedPrefixToken= - - Specifies an optional address generation mode for AssignAcquiredDelegatedPrefixAddress=. - Takes an IPv6 address. When set, the lower bits of the supplied address are combined with the upper bits of a - delegatad prefix received from the WAN interface by the IPv6PrefixDelegation= prefixes to - form a complete address. - - - PrefixDelegationHint= @@ -1966,6 +1937,48 @@ + + [DHCPv6PrefixDelegation] Section Options + The [DHCPv6PrefixDelegation] section configures delegated prefix assigned by DHCPv6 server. + The settings in this section are used only when IPv6PrefixDelegation= setting is + enabled, or set to dhcp6. + + + + SubnetId= + + Configure a specific subnet ID on the interface from a (previously) received prefix + delegation. You can either set "auto" (the default) or a specific subnet ID (as defined in + RFC 4291, section + 2.5.4), in which case the allowed value is hexadecimal, from 0 to 0x7fffffffffffffff + inclusive. This option is only effective when used together with + IPv6PrefixDelegation= and the corresponding configuration on the upstream + interface. + + + + + Assign= + + Takes a boolean. Specifies whether to add an address from the delegated prefixes which + are received from the WAN interface by the IPv6PrefixDelegation=. When + true (on LAN interfce), the EUI-64 algorithm will be used to form an interface identifier + from the delegated prefixes. Defaults to true. + + + + + Token= + + Specifies an optional address generation mode for Assign=. Takes an + IPv6 address. When set, the lower bits of the supplied address are combined with the upper + bits of a delegatad prefix received from the WAN interface by the + IPv6PrefixDelegation= prefixes to form a complete address. + + + + + [IPv6AcceptRA] Section Options The [IPv6AcceptRA] section configures the IPv6 Router Advertisement (RA) client, if it is enabled diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index e87d9d1e7c..d671284b00 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -367,7 +367,7 @@ static int dhcp6_set_pd_address(Link *link, assert(link->network); assert(prefix); - if (!link->network->dhcp6_pd_assign_prefix) + if (!link->network->dhcp6_pd_assign) return 0; r = address_new(&address); @@ -376,8 +376,8 @@ static int dhcp6_set_pd_address(Link *link, address->in_addr = *prefix; - if (!in_addr_is_null(AF_INET6, &link->network->dhcp6_delegation_prefix_token)) - memcpy(address->in_addr.in6.s6_addr + 8, link->network->dhcp6_delegation_prefix_token.in6.s6_addr + 8, 8); + if (!in_addr_is_null(AF_INET6, &link->network->dhcp6_pd_token)) + memcpy(address->in_addr.in6.s6_addr + 8, link->network->dhcp6_pd_token.in6.s6_addr + 8, 8); else { r = generate_ipv6_eui_64_address(link, &address->in_addr.in6); if (r < 0) @@ -437,7 +437,7 @@ static bool link_has_preferred_subnet_id(Link *link) { if (!link->network) return false; - return link->network->router_prefix_subnet_id >= 0; + return link->network->dhcp6_pd_subnet_id >= 0; } static int dhcp6_get_preferred_delegated_prefix( @@ -461,7 +461,7 @@ static int dhcp6_get_preferred_delegated_prefix( prefix = *masked_pd_prefix; if (link_has_preferred_subnet_id(link)) { - uint64_t subnet_id = link->network->router_prefix_subnet_id; + uint64_t subnet_id = link->network->dhcp6_pd_subnet_id; /* If the link has a preference for a particular subnet id try to allocate that */ if (subnet_id >= n_prefixes) @@ -1556,7 +1556,18 @@ int config_parse_dhcp6_mud_url( return free_and_replace(network->dhcp6_mudurl, unescaped); } -int config_parse_dhcp6_delegated_prefix_token( +DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp6_client_start_mode, dhcp6_client_start_mode, DHCP6ClientStartMode, + "Failed to parse WithoutRA= setting"); + +static const char* const dhcp6_client_start_mode_table[_DHCP6_CLIENT_START_MODE_MAX] = { + [DHCP6_CLIENT_START_MODE_NO] = "no", + [DHCP6_CLIENT_START_MODE_INFORMATION_REQUEST] = "information-request", + [DHCP6_CLIENT_START_MODE_SOLICIT] = "solicit", +}; + +DEFINE_STRING_TABLE_LOOKUP(dhcp6_client_start_mode, DHCP6ClientStartMode); + +int config_parse_dhcp6_pd_subnet_id( const char *unit, const char *filename, unsigned line, @@ -1568,7 +1579,52 @@ int config_parse_dhcp6_delegated_prefix_token( void *data, void *userdata) { - Network *network = data; + int64_t *p = data; + uint64_t t; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue) || streq(rvalue, "auto")) { + *p = -1; + return 0; + } + + r = safe_atoux64(rvalue, &t); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse %s=, ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + if (t > INT64_MAX) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Invalid subnet id '%s', ignoring assignment.", + rvalue); + return 0; + } + + *p = (int64_t) t; + + return 0; +} + +int config_parse_dhcp6_pd_token( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + union in_addr_union *addr = data, tmp; int r; assert(filename); @@ -1577,33 +1633,24 @@ int config_parse_dhcp6_delegated_prefix_token( assert(data); if (isempty(rvalue)) { - network->dhcp6_delegation_prefix_token = IN_ADDR_NULL; + *addr = IN_ADDR_NULL; return 0; } - r = in_addr_from_string(AF_INET6, rvalue, &network->dhcp6_delegation_prefix_token); + r = in_addr_from_string(AF_INET6, rvalue, &tmp); if (r < 0) { log_syntax(unit, LOG_WARNING, filename, line, r, - "Failed to parse DHCPv6 %s, ignoring: %s", lvalue, rvalue); + "Failed to parse DHCPv6 Prefix Delegation token, ignoring: %s", rvalue); return 0; } - if (in_addr_is_null(AF_INET6, &network->dhcp6_delegation_prefix_token)) { + if (in_addr_is_null(AF_INET6, &tmp)) { log_syntax(unit, LOG_WARNING, filename, line, 0, - "DHCPv6 %s cannot be the ANY address, ignoring: %s", lvalue, rvalue); + "DHCPv6 Prefix Delegation token cannot be the ANY address, ignoring: %s", rvalue); return 0; } + *addr = tmp; + return 0; } - -DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp6_client_start_mode, dhcp6_client_start_mode, DHCP6ClientStartMode, - "Failed to parse WithoutRA= setting"); - -static const char* const dhcp6_client_start_mode_table[_DHCP6_CLIENT_START_MODE_MAX] = { - [DHCP6_CLIENT_START_MODE_NO] = "no", - [DHCP6_CLIENT_START_MODE_INFORMATION_REQUEST] = "information-request", - [DHCP6_CLIENT_START_MODE_SOLICIT] = "solicit", -}; - -DEFINE_STRING_TABLE_LOOKUP(dhcp6_client_start_mode, DHCP6ClientStartMode); diff --git a/src/network/networkd-dhcp6.h b/src/network/networkd-dhcp6.h index fa44a2eb52..214456096d 100644 --- a/src/network/networkd-dhcp6.h +++ b/src/network/networkd-dhcp6.h @@ -34,8 +34,9 @@ int dhcp6_request_prefix_delegation(Link *link); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_pd_hint); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_mud_url); -CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_delegated_prefix_token); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_client_start_mode); +CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_pd_subnet_id); +CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_pd_token); const char* dhcp6_client_start_mode_to_string(DHCP6ClientStartMode i) _const_; DHCP6ClientStartMode dhcp6_client_start_mode_from_string(const char *s) _pure_; diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index f3c578cbb4..3f1652b190 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -213,8 +213,6 @@ DHCPv6.UserClass, config_parse_dhcp_user_class, DHCPv6.VendorClass, config_parse_dhcp_vendor_class, 0, offsetof(Network, dhcp6_vendor_class) DHCPv6.SendVendorOption, config_parse_dhcp_send_option, AF_INET6, offsetof(Network, dhcp6_client_send_vendor_options) DHCPv6.ForceDHCPv6PDOtherInformation, config_parse_bool, 0, offsetof(Network, dhcp6_force_pd_other_information) -DHCPv6.AssignAcquiredDelegatedPrefixAddress, config_parse_bool, 0, offsetof(Network, dhcp6_pd_assign_prefix) -DHCPv6.AssignAcquiredDelegatedPrefixToken, config_parse_dhcp6_delegated_prefix_token, 0, 0 DHCPv6.PrefixDelegationHint, config_parse_dhcp6_pd_hint, 0, 0 DHCPv6.WithoutRA, config_parse_dhcp6_client_start_mode, 0, offsetof(Network, dhcp6_without_ra) DHCPv6.SendOption, config_parse_dhcp_send_option, AF_INET6, offsetof(Network, dhcp6_client_send_options) @@ -271,7 +269,9 @@ BridgeVLAN.PVID, config_parse_brvlan_pvid, BridgeVLAN.VLAN, config_parse_brvlan_vlan, 0, 0 BridgeVLAN.EgressUntagged, config_parse_brvlan_untagged, 0, 0 Network.IPv6PrefixDelegation, config_parse_router_prefix_delegation, 0, offsetof(Network, router_prefix_delegation) -Network.IPv6PDSubnetId, config_parse_router_prefix_subnet_id, 0, 0 +DHCPv6PrefixDelegation.SubnetId, config_parse_dhcp6_pd_subnet_id, 0, offsetof(Network, dhcp6_pd_subnet_id) +DHCPv6PrefixDelegation.Assign, config_parse_bool, 0, offsetof(Network, dhcp6_pd_assign) +DHCPv6PrefixDelegation.Token, config_parse_dhcp6_pd_token, 0, offsetof(Network, dhcp6_pd_token) IPv6PrefixDelegation.RouterLifetimeSec, config_parse_sec, 0, offsetof(Network, router_lifetime_usec) IPv6PrefixDelegation.Managed, config_parse_bool, 0, offsetof(Network, router_managed) IPv6PrefixDelegation.OtherInformation, config_parse_bool, 0, offsetof(Network, router_other_information) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 591d63c6b2..694d9b0c3a 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -420,7 +420,8 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi .dhcp6_use_ntp = true, .dhcp6_use_dns = true, - .dhcp6_pd_assign_prefix = true, + .dhcp6_pd_subnet_id = -1, + .dhcp6_pd_assign = true, .dhcp_server_emit[SD_DHCP_LEASE_DNS].emit = true, .dhcp_server_emit[SD_DHCP_LEASE_NTP].emit = true, @@ -429,7 +430,6 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi .dhcp_server_emit_router = true, .dhcp_server_emit_timezone = true, - .router_prefix_subnet_id = -1, .router_emit_dns = true, .router_emit_domains = true, diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index 4c3c2bdd74..5dcb3c548b 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -184,7 +184,6 @@ struct Network { /* IPv6 prefix delegation support */ RADVPrefixDelegation router_prefix_delegation; - int64_t router_prefix_subnet_id; usec_t router_lifetime_usec; uint8_t router_preference; bool router_managed; @@ -198,8 +197,11 @@ struct Network { bool dhcp6_force_pd_other_information; /* Start DHCPv6 PD also when 'O' RA flag is set, see RFC 7084, WPD-4 */ - bool dhcp6_pd_assign_prefix; - union in_addr_union dhcp6_delegation_prefix_token; + + /* DHCPv6 Prefix Delegation support */ + int64_t dhcp6_pd_subnet_id; + bool dhcp6_pd_assign; + union in_addr_union dhcp6_pd_token; /* Bridge Support */ int use_bpdu; diff --git a/src/network/networkd-radv.c b/src/network/networkd-radv.c index dbc8fd8074..e0c490baba 100644 --- a/src/network/networkd-radv.c +++ b/src/network/networkd-radv.c @@ -866,46 +866,3 @@ int config_parse_router_preference(const char *unit, return 0; } - -int config_parse_router_prefix_subnet_id(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - Network *network = userdata; - uint64_t t; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - if (isempty(rvalue) || streq(rvalue, "auto")) { - network->router_prefix_subnet_id = -1; - return 0; - } - - r = safe_atoux64(rvalue, &t); - if (r < 0) { - log_syntax(unit, LOG_WARNING, filename, line, r, - "Failed to parse %s=, ignoring assignment: %s", - lvalue, rvalue); - return 0; - } - if (t > INT64_MAX) { - log_syntax(unit, LOG_WARNING, filename, line, r, - "Invalid subnet id '%s', ignoring assignment.", - rvalue); - return 0; - } - - network->router_prefix_subnet_id = (int64_t)t; - - return 0; -} diff --git a/src/network/networkd-radv.h b/src/network/networkd-radv.h index 73143e7493..496ef97adc 100644 --- a/src/network/networkd-radv.h +++ b/src/network/networkd-radv.h @@ -60,7 +60,6 @@ RADVPrefixDelegation radv_prefix_delegation_from_string(const char *s) _pure_; CONFIG_PARSER_PROTOTYPE(config_parse_router_prefix_delegation); CONFIG_PARSER_PROTOTYPE(config_parse_router_preference); -CONFIG_PARSER_PROTOTYPE(config_parse_router_prefix_subnet_id); CONFIG_PARSER_PROTOTYPE(config_parse_prefix); CONFIG_PARSER_PROTOTYPE(config_parse_prefix_flags); CONFIG_PARSER_PROTOTYPE(config_parse_prefix_lifetime); diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network index aa04c1192a..5d80470719 100644 --- a/test/fuzz/fuzz-network-parser/directives.network +++ b/test/fuzz/fuzz-network-parser/directives.network @@ -130,10 +130,12 @@ SendOption= RequestOptions= UserClass= VendorClass= -AssignAcquiredDelegatedPrefixAddress= -AssignAcquiredDelegatedPrefixToken= SendVendorOption= RouteMetric= +[DHCPv6PrefixDelegation] +SubnetId= +Assign= +Token= [Route] Destination= Protocol= @@ -203,7 +205,6 @@ NTP= DHCP= Domains= IPv6PrefixDelegation= -IPv6PDSubnetId= VLAN= DHCPServer= BindCarrier=