diff options
author | Lennart Poettering <lennart@poettering.net> | 2020-07-29 15:59:11 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-07-29 15:59:11 +0200 |
commit | 0cfb490fe9e3af39f7d7f9431b9fee9b5c98b992 (patch) | |
tree | 94a2ab37d2172437ff5160e9a57d5a2c1e12631f /src | |
parent | Merge pull request #16590 from keszybz/test-fs-util-relax (diff) | |
parent | network: rename settings about DHCPv6 Prefix Delegation (diff) | |
download | systemd-0cfb490fe9e3af39f7d7f9431b9fee9b5c98b992.tar.xz systemd-0cfb490fe9e3af39f7d7f9431b9fee9b5c98b992.zip |
Merge pull request #16585 from yuwata/network-dhcp6-fixes
network: fix several DHCP6 PD issues, and cleanup DHCP4 and NDISC
Diffstat (limited to 'src')
-rw-r--r-- | src/network/networkd-address.c | 61 | ||||
-rw-r--r-- | src/network/networkd-address.h | 6 | ||||
-rw-r--r-- | src/network/networkd-dhcp4.c | 466 | ||||
-rw-r--r-- | src/network/networkd-dhcp6.c | 1639 | ||||
-rw-r--r-- | src/network/networkd-dhcp6.h | 19 | ||||
-rw-r--r-- | src/network/networkd-ipv4ll.c | 10 | ||||
-rw-r--r-- | src/network/networkd-link.c | 222 | ||||
-rw-r--r-- | src/network/networkd-link.h | 41 | ||||
-rw-r--r-- | src/network/networkd-manager.c | 27 | ||||
-rw-r--r-- | src/network/networkd-manager.h | 1 | ||||
-rw-r--r-- | src/network/networkd-ndisc.c | 348 | ||||
-rw-r--r-- | src/network/networkd-ndisc.h | 4 | ||||
-rw-r--r-- | src/network/networkd-neighbor.c | 2 | ||||
-rw-r--r-- | src/network/networkd-network-gperf.gperf | 6 | ||||
-rw-r--r-- | src/network/networkd-network.c | 4 | ||||
-rw-r--r-- | src/network/networkd-network.h | 8 | ||||
-rw-r--r-- | src/network/networkd-radv.c | 51 | ||||
-rw-r--r-- | src/network/networkd-radv.h | 11 | ||||
-rw-r--r-- | src/network/networkd-route.c | 14 | ||||
-rw-r--r-- | src/network/networkd-route.h | 2 | ||||
-rw-r--r-- | src/network/networkd-routing-policy-rule.c | 7 | ||||
-rw-r--r-- | src/network/test-routing-policy-rule.c | 2 |
22 files changed, 1669 insertions, 1282 deletions
diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 2c36854588..b09d75e615 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -125,6 +125,17 @@ 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) + 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); 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)); @@ -205,7 +216,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) @@ -344,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); @@ -356,18 +364,27 @@ 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); - 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; @@ -586,9 +603,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); @@ -609,6 +628,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); @@ -690,9 +716,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"); @@ -712,6 +738,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 d55059ee25..3fc9935d16 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); @@ -60,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-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); - assert(link); + /* Do not call this callback again. */ + address->callback = NULL; - if (!link->dhcp_lease_old) - return; + return dhcp4_release_old_lease(address->link, true); +} - assert(link->dhcp_lease); +static int dhcp4_release_old_lease(Link *link, bool force) { + Route *route; + Iterator i; + int k, r = 0; + + assert(link); - (void) sd_dhcp_lease_get_address(link->dhcp_lease_old, &address_old); - (void) sd_dhcp_lease_get_address(link->dhcp_lease, &address); + if (!link->dhcp_address_old && set_isempty(link->dhcp_routes_old)) + return 0; - (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 (!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; + } - if (!in4_addr_equal(&address_old, &address)) - (void) dhcp_remove_address(link, link->dhcp_lease_old, &address_old, NULL); + log_link_debug(link, "Removing old DHCPv4 address and routes."); - 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"); - - r = dhcp_remove_routes(link, link->dhcp_lease, &address, true, dhcp4_remove_route_handler); - if (r < 0) - return r; - - 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; + 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_address(link, link->dhcp_lease, &address, dhcp4_remove_address_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++; + } - 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-dhcp6.c b/src/network/networkd-dhcp6.c index 1f78e24110..d671284b00 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -24,500 +24,907 @@ #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); - -bool dhcp6_get_prefix_delegation(Link *link) { - if (!link->network) +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; + + if (!lease) return false; - return IN_SET(link->network->router_prefix_delegation, - RADV_PREFIX_DELEGATION_DHCP6, - RADV_PREFIX_DELEGATION_BOTH); + 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; } -static bool dhcp6_has_preferred_subnet_id(Link *link) { - if (!link->network) - return false; +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); + } - return link->network->router_prefix_subnet_id >= 0; + link_unref(p->link); + return mfree(p); } -static int dhcp6_get_preferred_delegated_prefix( - Manager* manager, - Link *link, - const struct in6_addr *pd_prefix, - uint8_t pd_prefix_len, - struct in6_addr *ret_addr) { - - 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, - }; +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; - assert(pd_prefix_len <= 64); - assert(manager); + 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->network); + assert(link->manager); + assert(prefix); - 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); + pd = hashmap_get(link->manager->dhcp6_prefixes, &prefix->in6); + if (!pd) + return NULL; - 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 pd->link; +} - /* 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); +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); - Link* assigned_link = dhcp6_prefix_get(manager, &prefix.in6); +static int dhcp6_pd_address_callback(Address *address) { + Address *a; + Iterator i; + + assert(address); + assert(address->link); - (void) in_addr_to_string(AF_INET6, &prefix, &assigned_buf); + /* Make this called only once */ + SET_FOREACH(a, address->link->dhcp6_pd_addresses, i) + a->callback = NULL; - 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)); + return dhcp6_pd_remove_old(address->link, true); +} - *ret_addr = prefix.in6; +static int dhcp6_pd_remove_old(Link *link, bool force) { + Address *address; + Route *route; + Iterator i; + int k, r = 0; - log_link_debug(link, "The requested prefix %s is available. Using it.", - strnull(assigned_buf)); + assert(link); + assert(link->manager); + + if (!force && (link->dhcp6_pd_address_messages != 0 || link->dhcp6_pd_route_configured != 0)) return 0; - } - for (uint64_t n = 0; n < n_prefixes; n++) { - /* 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); + if (set_isempty(link->dhcp6_pd_addresses_old) && set_isempty(link->dhcp6_pd_routes_old)) + return 0; - if (!assigned_link || assigned_link == link) { - *ret_addr = prefix.in6; + 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; } + } - 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"); + 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)); } - return log_link_warning_errno(link, SYNTHETIC_ERRNO(ERANGE), "Couldn't find a suitable prefix. Ran out of address space."); + SET_FOREACH(address, link->dhcp6_pd_addresses_old, i) { + k = address_remove(address, link, NULL); + if (k < 0) + r = k; + } + + return r; } -static bool dhcp6_enable_prefix_delegation(Link *dhcp6_link) { - Manager *manager; - Link *l; +int dhcp6_pd_remove(Link *link) { + Address *address; + Route *route; Iterator i; + int k, r = 0; - assert(dhcp6_link); + assert(link); + assert(link->manager); - manager = dhcp6_link->manager; - assert(manager); + link->dhcp6_pd_address_configured = false; + link->dhcp6_pd_route_configured = false; - HASHMAP_FOREACH(l, manager->links, i) { - if (l == dhcp6_link) - continue; + k = dhcp6_pd_remove_old(link, true); + if (k < 0) + r = k; - if (!dhcp6_get_prefix_delegation(l)) - continue; + if (set_isempty(link->dhcp6_pd_addresses) && set_isempty(link->dhcp6_pd_routes)) + return r; - return true; + 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)); } - return false; + SET_FOREACH(address, link->dhcp6_pd_addresses, i) { + k = address_remove(address, link, NULL); + if (k < 0) + r = k; + } + + return r; } -static int dhcp6_lease_information_acquired(sd_dhcp6_client *client, Link *link) { - return 0; +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_pd_prefix_assign(Link *link, struct in6_addr *prefix, - uint8_t prefix_len, - uint32_t lifetime_preferred, - uint32_t lifetime_valid) { +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; - r = radv_add_prefix(link, prefix, prefix_len, lifetime_preferred, lifetime_valid); + assert(link); + assert(link->manager); + assert(prefix); + assert(pd_prefix); + + r = route_new(&route); if (r < 0) return r; - r = dhcp6_prefix_add(link->manager, prefix, link); + 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 r; + return log_link_error_errno(link, r, "Failed to set DHCPv6 prefix route: %m"); - r = dhcp6_assign_delegated_prefix(link, prefix, prefix_len, lifetime_preferred, lifetime_valid); + link->dhcp6_pd_route_messages++; + + r = set_ensure_put(&link->dhcp6_pd_routes, &route_hash_ops, ret); if (r < 0) - return r; + 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_route_remove_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) { +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) - log_link_message_warning_errno(link, m, r, "Received error on unreachable route removal for DHCPv6 delegated subnet"); + 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; } -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; +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; - r = sd_dhcp6_client_get_lease(client, &lease); + assert(link); + assert(link->network); + assert(prefix); + + if (!link->network->dhcp6_pd_assign) + 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_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) + 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; - sd_dhcp6_lease_reset_pd_prefix_iter(lease); + r = dhcp6_set_pd_route(link, prefix, pd_prefix); + if (r < 0) + return r; - 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; + r = dhcp6_set_pd_address(link, prefix, prefix_len, lifetime_preferred, lifetime_valid); + if (r < 0) + return r; - if (pd_prefix_len >= 64) - continue; + return 0; +} - (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf); +bool link_dhcp6_pd_is_enabled(Link *link) { + if (!link->network) + return false; + + return link->network->router_prefix_delegation & RADV_PREFIX_DELEGATION_DHCP6; +} + +static bool link_has_preferred_subnet_id(Link *link) { + if (!link->network) + return false; - r = route_new(&route); + return link->network->dhcp6_pd_subnet_id >= 0; +} + +static int dhcp6_get_preferred_delegated_prefix( + Link *link, + const union in_addr_union *masked_pd_prefix, + uint8_t pd_prefix_len, + union in_addr_union *ret) { + + /* 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(link); + 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->dhcp6_pd_subnet_id; + + /* If the link has a preference for a particular subnet id try to allocate that */ + 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 r; + 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, masked_pd_prefix, pd_prefix_len, &prefix) > 0); - route->family = AF_INET6; - route->dst = pd_prefix; - route->dst_prefixlen = pd_prefix_len; - route->type = RTN_UNREACHABLE; + assigned_link = dhcp6_pd_get_link_by_prefix(link, &prefix); + if (assigned_link && assigned_link != link) { + _cleanup_free_ char *assigned_buf = NULL; - 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; + (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)); } - log_link_debug(link, "Removing unreachable route %s/%u", - strnull(buf), pd_prefix_len); + *ret = prefix; + return 0; } - return 0; + for (uint64_t n = 0; n < n_prefixes; n++) { + /* If we do not have an allocation preference just iterate + * through the address space and return the first free prefix. */ + assigned_link = dhcp6_pd_get_link_by_prefix(link, &prefix); + if (!assigned_link || assigned_link == link) { + *ret = prefix; + return 0; + } + + r = in_addr_prefix_next(AF_INET6, &prefix, 64); + if (r < 0) + 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 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)); } - - /* If one of the link requests couldn't be fulfilled, signal that we - should try again with another prefix. */ - if (pool_depleted) - return -EAGAIN; - - return 0; } -static int dhcp6_route_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) { +static int dhcp6_pd_prepare(Link *link) { + Address *address; + Route *route; int r; - assert(link); - assert(link->dhcp6_route_messages > 0); + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 0; - link->dhcp6_route_messages--; + if (!link_dhcp6_pd_is_enabled(link)) + return 0; - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; + link_dirty(link); - r = sd_netlink_message_get_errno(m); - if (r < 0 && r != -EEXIST) { - log_link_message_warning_errno(link, m, r, "Failed to add unreachable route for DHCPv6 delegated subnet"); - link_enter_failed(link); - return 1; + 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 (link->dhcp6_route_messages == 0) { - log_link_debug(link, "Unreachable routes for DHCPv6 delegated subnets set"); - link->dhcp6_route_configured = true; - link_check_ready(link); + 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 1; + return 0; } -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_pd_finalize(Link *link) { int r; - link->dhcp6_route_configured = false; + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 0; - r = sd_dhcp6_client_get_lease(client, &lease); + 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; - sd_dhcp6_lease_reset_pd_prefix_iter(lease); + if (link->dhcp6_pd_address_configured && link->dhcp6_pd_route_configured) + link_check_ready(link); + else + link_set_state(link, LINK_STATE_CONFIGURING); - while (sd_dhcp6_lease_get_pd(lease, &pd_prefix.in6, &pd_prefix_len, - &lifetime_preferred, - &lifetime_valid) >= 0) { + return 0; +} + +static void dhcp6_pd_prefix_lost(Link *dhcp6_link) { + Link *link; + Iterator i; + int r; - _cleanup_free_ char *buf = NULL; + assert(dhcp6_link); + assert(dhcp6_link->manager); - (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf); + HASHMAP_FOREACH(link, dhcp6_link->manager->links, i) { + if (link == dhcp6_link) + continue; - if (pd_prefix_len > 64) { - log_link_debug(link, "PD Prefix length > 64, ignoring prefix %s/%u", - strnull(buf), pd_prefix_len); + if (!link_dhcp6_pd_is_enabled(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_remove(link); + if (r < 0) + link_enter_failed(link); + } +} - if (pd_prefix_len < 64) { - _cleanup_(route_freep) Route *route = NULL; +static int dhcp6_remove_old(Link *link, bool force); - r = route_new(&route); - if (r < 0) - return r; +static int dhcp6_address_callback(Address *address) { + Address *a; + Iterator i; - 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; + assert(address); + assert(address->link); - r = route_configure(route, link, dhcp6_route_handler); - 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; + /* 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 (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"); + if (set_callback) { + SET_FOREACH(address, link->dhcp6_addresses, i) + address->callback = dhcp6_address_callback; + return 0; + } + } - /* 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. + log_link_debug(link, "Removing old DHCPv6 addresses and routes."); - * 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. + link_dirty(link); - * 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. */ + SET_FOREACH(route, link->dhcp6_routes_old, i) { + k = route_remove(route, link, NULL); + if (k < 0) + r = k; + } - r = dhcp6_pd_prefix_distribute(link, &pd_prefix.in6, - pd_prefix_len, - lifetime_preferred, - lifetime_valid, - true); - if (r < 0 && r != -EAGAIN) - return r; + SET_FOREACH(address, link->dhcp6_addresses_old, i) { + k = address_remove(address, link, NULL); + if (k < 0) + r = k; + } - /* 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. */ - - r = dhcp6_pd_prefix_distribute(link, &pd_prefix.in6, - pd_prefix_len, - lifetime_preferred, - lifetime_valid, - false); - if (r < 0 && r != -EAGAIN) - return r; + 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; - /* If the prefix distribution did return -EAGAIN we will try to - * fulfill those with the next available pd delegated prefix. */ + 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; + + assert(link); + assert(link->dhcp6_route_messages > 0); + + link->dhcp6_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 unreachable route for DHCPv6 delegated subnet"); + link_enter_failed(link); + return 1; } 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); - } else { - log_link_debug(link, "Setting unreachable routes for DHCPv6 delegated subnets"); - link_set_state(link, LINK_STATE_CONFIGURING); } - return 0; + return 1; } -int dhcp6_request_prefix_delegation(Link *link) { - Link *l; - Iterator i; +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; - assert_return(link, -EINVAL); - assert_return(link->manager, -EOPNOTSUPP); + assert(link); + assert(addr); + + (void) in_addr_to_string(AF_INET6, addr, &buf); - if (dhcp6_get_prefix_delegation(link) <= 0) + if (prefixlen > 64) { + log_link_debug(link, "PD Prefix length > 64, ignoring prefix %s/%u", + strna(buf), prefixlen); return 0; + } - log_link_debug(link, "Requesting DHCPv6 prefixes to be delegated for new link"); + 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; + } - HASHMAP_FOREACH(l, link->manager->links, i) { - int r, enabled; + if (prefixlen < 48) + log_link_warning(link, "PD Prefix length < 48, looks unusual %s/%u", + strna(buf), prefixlen); - if (l == link) - continue; + r = route_new(&route); + if (r < 0) + return log_oom(); - if (!l->dhcp6_client) - continue; + route->family = AF_INET6; + route->dst = *addr; + route->dst_prefixlen = prefixlen; + route->table = link_get_dhcp_route_table(link); + route->type = RTN_UNREACHABLE; - 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; - } + 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); - 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; - } - } + link->dhcp6_route_messages++; - r = sd_dhcp6_client_is_running(l->dhcp6_client); - if (r <= 0) - continue; + 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 (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); + (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; - } - 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"); + r = dhcp6_pd_prepare(link); + if (r < 0) + link_enter_failed(link); + } + + 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 = sd_dhcp6_lease_get_pd(dhcp6_link->dhcp6_lease, &pd_prefix.in6, &pd_prefix_len, + &lifetime_preferred, &lifetime_valid); + if (r < 0) + break; + + 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. + * 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. */ + + assert(pd_prefix_len <= 64); + + 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"); + + if (DEBUG_LOGGING) { + uint64_t n_prefixes = UINT64_C(1) << (64 - pd_prefix_len); + _cleanup_free_ char *buf = NULL; + + (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); } - 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"); + 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); + } + + HASHMAP_FOREACH(link, dhcp6_link->manager->links, i) { + if (link == dhcp6_link) 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 +952,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 +969,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 +992,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); + 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; + + assert(link); + assert(link->dhcp6_lease); + + for (sd_dhcp6_lease_reset_address_iter(link->dhcp6_lease);;) { + uint32_t lifetime_preferred, lifetime_valid; + struct in6_addr ip6_addr; + + r = sd_dhcp6_lease_get_address(link->dhcp6_lease, &ip6_addr, &lifetime_preferred, &lifetime_valid); + if (r < 0) + break; + + r = dhcp6_update_address(link, &ip6_addr, lifetime_preferred, lifetime_valid); + if (r < 0) + return r; + } + + 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; - struct in6_addr ip6_addr; - uint32_t lifetime_preferred, lifetime_valid; + 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 r; + return log_link_error_errno(link, r, "Failed to get DHCPv6 lease: %m"); - sd_dhcp6_lease_reset_address_iter(lease); - while (sd_dhcp6_lease_get_address(lease, &ip6_addr, - &lifetime_preferred, - &lifetime_valid) >= 0) { + lease_old = TAKE_PTR(link->dhcp6_lease); + link->dhcp6_lease = sd_dhcp6_lease_ref(lease); - r = dhcp6_address_change(link, &ip6_addr, lifetime_preferred, lifetime_valid); + 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) { + 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 void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) { +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) { Link *link = userdata; + int r; assert(link); assert(link->network); @@ -638,39 +1139,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 +1169,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 +1178,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 +1192,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 +1227,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 +1331,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 +1480,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); - 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); - 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,14 +1550,24 @@ 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; } 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, @@ -1228,7 +1579,8 @@ 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); @@ -1236,34 +1588,69 @@ int config_parse_dhcp6_delegated_prefix_token( assert(rvalue); assert(data); - if (isempty(rvalue)) { - network->dhcp6_delegation_prefix_token = IN_ADDR_NULL; + if (isempty(rvalue) || streq(rvalue, "auto")) { + *p = -1; return 0; } - r = in_addr_from_string(AF_INET6, rvalue, &network->dhcp6_delegation_prefix_token); + r = safe_atoux64(rvalue, &t); if (r < 0) { log_syntax(unit, LOG_WARNING, filename, line, r, - "Failed to parse DHCPv6 %s, ignoring: %s", lvalue, rvalue); + "Failed to parse %s=, ignoring assignment: %s", + lvalue, rvalue); return 0; } - - if (in_addr_is_null(AF_INET6, &network->dhcp6_delegation_prefix_token)) { + if (t > INT64_MAX) { log_syntax(unit, LOG_WARNING, filename, line, 0, - "DHCPv6 %s cannot be the ANY address, ignoring: %s", lvalue, rvalue); + "Invalid subnet id '%s', ignoring assignment.", + rvalue); return 0; } + *p = (int64_t) t; + return 0; } -DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp6_client_start_mode, dhcp6_client_start_mode, DHCP6ClientStartMode, - "Failed to parse WithoutRA= setting"); +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) { -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", -}; + union in_addr_union *addr = data, tmp; + int r; -DEFINE_STRING_TABLE_LOOKUP(dhcp6_client_start_mode, DHCP6ClientStartMode); + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue)) { + *addr = IN_ADDR_NULL; + return 0; + } + + r = in_addr_from_string(AF_INET6, rvalue, &tmp); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse DHCPv6 Prefix Delegation token, ignoring: %s", rvalue); + return 0; + } + + if (in_addr_is_null(AF_INET6, &tmp)) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "DHCPv6 Prefix Delegation token cannot be the ANY address, ignoring: %s", rvalue); + return 0; + } + + *addr = tmp; + + return 0; +} diff --git a/src/network/networkd-dhcp6.h b/src/network/networkd-dhcp6.h index cc416b98d2..214456096d 100644 --- a/src/network/networkd-dhcp6.h +++ b/src/network/networkd-dhcp6.h @@ -17,17 +17,26 @@ 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); -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-ipv4ll.c b/src/network/networkd-ipv4ll.c index aaca215e56..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) @@ -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 dd98bf2685..0057d184f7 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); @@ -699,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); } @@ -711,17 +711,32 @@ 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->nexthops = set_free_with_destructor(link->nexthops, nexthop_free); - link->nexthops_foreign = set_free_with_destructor(link->nexthops_foreign, nexthop_free); - - link->neighbors = set_free_with_destructor(link->neighbors, neighbor_free); - link->neighbors_foreign = set_free_with_destructor(link->neighbors_foreign, neighbor_free); - - link->addresses = set_free_with_destructor(link->addresses, address_free); - link->addresses_foreign = set_free_with_destructor(link->addresses_foreign, address_free); + 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->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); + + link->nexthops = set_free(link->nexthops); + link->nexthops_foreign = set_free(link->nexthops_foreign); + + link->neighbors = set_free(link->neighbors); + link->neighbors_foreign = set_free(link->neighbors_foreign); + + 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); + 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); while ((address = link->pool_addresses)) { LIST_REMOVE(addresses, link->pool_addresses, address); @@ -834,6 +849,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) @@ -1037,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. */ @@ -1061,7 +1083,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) @@ -1082,12 +1104,14 @@ int link_request_set_routes(Link *link) { void link_check_ready(Link *link) { Address *a; Iterator i; - int r; 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; } @@ -1113,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; @@ -1149,7 +1164,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; } @@ -1160,17 +1175,19 @@ 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; } - 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) && !(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__); @@ -1189,8 +1206,7 @@ void link_check_ready(Link *link) { } } - if (link->state != LINK_STATE_CONFIGURED) - link_enter_configured(link); + link_enter_configured(link); return; } @@ -1222,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; @@ -1247,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; } @@ -1302,16 +1389,12 @@ 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 = 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, - 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; @@ -1320,22 +1403,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); + 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) { @@ -1346,8 +1427,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) @@ -1357,7 +1437,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); @@ -1612,12 +1695,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; } @@ -4235,7 +4320,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", @@ -4247,12 +4331,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); /************************************************************/ @@ -4269,7 +4347,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); @@ -4293,7 +4371,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); @@ -4312,8 +4390,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 4534180128..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; @@ -95,32 +96,20 @@ 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; 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:1; + bool ipv4ll_address_configured:1; bool addresses_configured:1; bool addresses_ready:1; @@ -141,10 +130,30 @@ typedef struct Link { sd_ndisc *ndisc; Set *ndisc_rdnss; 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 59ec0de8d3..a6c1a39e23 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; @@ -1821,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); @@ -1857,9 +1852,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-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-ndisc.c b/src/network/networkd-ndisc.c index a91c099996..349f0548af 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -35,6 +35,79 @@ #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; + NDiscDNSSL *dnssl; + NDiscRDNSS *rdnss; + 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; + } + + 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; +} + static int ndisc_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { int r; @@ -56,6 +129,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 +164,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 +181,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 +286,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); + 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 +300,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); + 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 +514,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); + 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 +560,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); + 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 +617,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); + 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; } @@ -511,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); @@ -534,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++) { - _cleanup_free_ NDiscRDNSS *x = NULL; - NDiscRDNSS d = { - .address = a[i], - }, *y; + SET_FOREACH(rdnss, link->ndisc_rdnss, i) + rdnss->marked = true; - if (lifetime == 0) { - (void) set_remove(link->ndisc_rdnss, &d); - link_dirty(link); - continue; - } + if (lifetime == 0) + return 0; - y = set_get(link->ndisc_rdnss, &d); - if (y) { - y->valid_until = time_now + lifetime * USEC_PER_SEC; - continue; - } + 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; + } - ndisc_vacuum(link); + for (int j = 0; j < n; j++) { + _cleanup_free_ NDiscRDNSS *x = NULL; + NDiscRDNSS d = { + .address = a[j], + }; - 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; } @@ -564,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, }; @@ -572,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; @@ -587,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); @@ -611,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 (!s) - return log_oom(); + if (lifetime == 0) + return 0; - strcpy(NDISC_DNSSL_DOMAIN(s), *i); + 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); + } - if (lifetime == 0) { - (void) set_remove(link->ndisc_dnssl, s); - link_dirty(link); - continue; - } + STRV_FOREACH(j, l) { + _cleanup_free_ NDiscDNSSL *s = NULL; - x = set_get(link->ndisc_dnssl, s); - if (x) { - x->valid_until = time_now + lifetime * USEC_PER_SEC; - continue; - } + s = malloc0(ALIGN(sizeof(NDiscDNSSL)) + strlen(*j) + 1); + if (!s) + return log_oom(); - ndisc_vacuum(link); + strcpy(NDISC_DNSSL_DOMAIN(s), *j); - 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; } @@ -646,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; @@ -736,6 +865,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 +875,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 +905,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 +916,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,44 +957,20 @@ 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: 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"); @@ -862,6 +1010,7 @@ void ndisc_vacuum(Link *link) { NDiscDNSSL *d; Iterator i; usec_t time_now; + bool updated = false; assert(link); @@ -872,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) { @@ -887,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; 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-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 088cdf11a3..e0c490baba 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,13 +670,12 @@ int radv_configure(Link *link) { if (r < 0) return r; } - } 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; @@ -870,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 741aa8692f..496ef97adc 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; @@ -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_; @@ -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/src/network/networkd-route.c b/src/network/networkd-route.c index 62a9b8b9b3..541bf1e793 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -144,6 +144,14 @@ 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); + 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); } ordered_set_free_free(route->multipath_routes); @@ -597,7 +605,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 +812,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); 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) { |