From 69203fba700ea8d7b0c4f4e3d1e1f809ac4644a1 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 22 Jul 2020 11:55:07 +0900 Subject: [PATCH] network: ndisc: remove old addresses and routes after at least one SLAAC address becomes ready Otherwise, the old addresses will exist in deperecated state. --- src/network/networkd-address.c | 2 + src/network/networkd-link.c | 4 + src/network/networkd-link.h | 2 + src/network/networkd-ndisc.c | 217 ++++++++++++++++++++++++++------- src/network/networkd-route.c | 2 + 5 files changed, 183 insertions(+), 44 deletions(-) diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 6fc3074eac..1d2db3a6ba 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -129,6 +129,8 @@ void address_free(Address *address) { address->link->dhcp_address = NULL; if (address->link->dhcp_address_old == address) address->link->dhcp_address_old = NULL; + set_remove(address->link->ndisc_addresses, address); + set_remove(address->link->ndisc_addresses_old, address); if (in_addr_equal(AF_INET6, &address->in_addr, (const union in_addr_union *) &address->link->ipv6ll_address)) memzero(&address->link->ipv6ll_address, sizeof(struct in6_addr)); diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 6db57b9f3d..79bbd25fba 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -714,6 +714,8 @@ static Link *link_free(Link *link) { link->routes_foreign = set_free(link->routes_foreign); link->dhcp_routes = set_free(link->dhcp_routes); link->dhcp_routes_old = set_free(link->dhcp_routes_old); + link->ndisc_routes = set_free(link->ndisc_routes); + link->ndisc_routes_old = set_free(link->ndisc_routes_old); link->nexthops = set_free(link->nexthops); link->nexthops_foreign = set_free(link->nexthops_foreign); @@ -723,6 +725,8 @@ static Link *link_free(Link *link) { link->addresses = set_free(link->addresses); link->addresses_foreign = set_free(link->addresses_foreign); + link->ndisc_addresses = set_free(link->ndisc_addresses); + link->ndisc_addresses_old = set_free(link->ndisc_addresses_old); while ((address = link->pool_addresses)) { LIST_REMOVE(addresses, link->pool_addresses, address); diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index f837ec2a24..290e84a06e 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -142,6 +142,8 @@ typedef struct Link { sd_ndisc *ndisc; Set *ndisc_rdnss; Set *ndisc_dnssl; + Set *ndisc_addresses, *ndisc_addresses_old; + Set *ndisc_routes, *ndisc_routes_old; sd_radv *radv; diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index 2f6cc4042e..0c86fa139b 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -35,6 +35,69 @@ #define NDISC_APP_ID SD_ID128_MAKE(13,ac,81,a7,d5,3f,49,78,92,79,5d,0c,29,3a,bc,7e) +static int ndisc_remove_old(Link *link, bool force); + +static int ndisc_address_callback(Address *address) { + Address *a; + Iterator i; + + assert(address); + assert(address->link); + + /* Make this called only once */ + SET_FOREACH(a, address->link->ndisc_addresses, i) + a->callback = NULL; + + return ndisc_remove_old(address->link, true); +} + +static int ndisc_remove_old(Link *link, bool force) { + Address *address; + Route *route; + Iterator i; + int k, r = 0; + + assert(link); + + if (!force) { + bool set_callback = !set_isempty(link->ndisc_addresses); + + if (!link->ndisc_addresses_configured || !link->ndisc_routes_configured) + return 0; + + SET_FOREACH(address, link->ndisc_addresses, i) + if (address_is_ready(address)) { + set_callback = false; + break; + } + + if (set_callback) { + SET_FOREACH(address, link->ndisc_addresses, i) + address->callback = ndisc_address_callback; + return 0; + } + } + + if (!set_isempty(link->ndisc_addresses_old) || !set_isempty(link->ndisc_routes_old)) + log_link_debug(link, "Removing old NDisc addresses and routes."); + + link_dirty(link); + + SET_FOREACH(address, link->ndisc_addresses_old, i) { + k = address_remove(address, link, NULL); + if (k < 0) + r = k; + } + + SET_FOREACH(route, link->ndisc_routes_old, i) { + k = route_remove(route, link, NULL); + if (k < 0) + r = k; + } + + return r; +} + static int ndisc_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { int r; @@ -56,6 +119,13 @@ static int ndisc_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *li if (link->ndisc_routes_messages == 0) { log_link_debug(link, "NDisc routes set."); link->ndisc_routes_configured = true; + + r = ndisc_remove_old(link, false); + if (r < 0) { + link_enter_failed(link); + return 1; + } + link_check_ready(link); } @@ -84,6 +154,13 @@ static int ndisc_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link * if (link->ndisc_addresses_messages == 0) { log_link_debug(link, "NDisc SLAAC addresses set."); link->ndisc_addresses_configured = true; + + r = ndisc_remove_old(link, false); + if (r < 0) { + link_enter_failed(link); + return 1; + } + r = link_request_set_routes(link); if (r < 0) { link_enter_failed(link); @@ -94,6 +171,50 @@ static int ndisc_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link * return 1; } +static int ndisc_route_configure(Route *route, Link *link) { + Route *ret; + int r; + + assert(route); + assert(link); + + r = route_configure(route, link, ndisc_route_handler, &ret); + if (r < 0) + return log_link_error_errno(link, r, "Failed to set NDisc route: %m"); + + link->ndisc_routes_messages++; + + r = set_ensure_put(&link->ndisc_routes, &route_hash_ops, ret); + if (r < 0) + return log_link_error_errno(link, r, "Failed to store NDisc route: %m"); + + (void) set_remove(link->ndisc_routes_old, ret); + + return 0; +} + +static int ndisc_address_configure(Address *address, Link *link) { + Address *ret; + int r; + + assert(address); + assert(link); + + r = address_configure(address, link, ndisc_address_handler, true, &ret); + if (r < 0) + return log_link_error_errno(link, r, "Failed to set NDisc SLAAC address: %m"); + + link->ndisc_addresses_messages++; + + r = set_ensure_put(&link->ndisc_addresses, &address_hash_ops, ret); + if (r < 0) + return log_link_error_errno(link, r, "Failed to store NDisc SLAAC address: %m"); + + (void) set_remove(link->ndisc_addresses_old, ret); + + return 0; +} + static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) { _cleanup_(route_freep) Route *route = NULL; union in_addr_union gateway; @@ -155,11 +276,9 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) { route->lifetime = time_now + lifetime * USEC_PER_SEC; route->mtu = mtu; - r = route_configure(route, link, ndisc_route_handler, NULL); + r = ndisc_route_configure(route, link); if (r < 0) return log_link_error_errno(link, r, "Could not set default route: %m"); - if (r > 0) - link->ndisc_routes_messages++; Route *route_gw; LIST_FOREACH(routes, route_gw, link->network->static_routes) { @@ -171,11 +290,9 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) { route_gw->gw = gateway; - r = route_configure(route_gw, link, ndisc_route_handler, NULL); + r = ndisc_route_configure(route_gw, link); if (r < 0) return log_link_error_errno(link, r, "Could not set gateway: %m"); - if (r > 0) - link->ndisc_routes_messages++; } return 0; @@ -387,11 +504,9 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r address->in_addr.in6 = *a; - r = address_configure(address, link, ndisc_address_handler, true, NULL); + r = ndisc_address_configure(address, link); if (r < 0) return log_link_error_errno(link, r, "Could not set SLAAC address: %m"); - if (r > 0) - link->ndisc_addresses_messages++; } return 0; @@ -435,11 +550,9 @@ static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) { if (r < 0) return log_link_error_errno(link, r, "Failed to get prefix address: %m"); - r = route_configure(route, link, ndisc_route_handler, NULL); + r = ndisc_route_configure(route, link); if (r < 0) return log_link_error_errno(link, r, "Could not set prefix route: %m");; - if (r > 0) - link->ndisc_routes_messages++; return 0; } @@ -494,11 +607,9 @@ static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) { if (r < 0) return log_link_error_errno(link, r, "Failed to get route address: %m"); - r = route_configure(route, link, ndisc_route_handler, NULL); + r = ndisc_route_configure(route, link); if (r < 0) return log_link_error_errno(link, r, "Could not set additional route: %m"); - if (r > 0) - link->ndisc_routes_messages++; return 0; } @@ -736,6 +847,8 @@ static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) { } static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) { + Address *address; + Route *route; uint64_t flags; int r; @@ -744,6 +857,23 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) { assert(link->manager); assert(rt); + link->ndisc_addresses_configured = false; + link->ndisc_routes_configured = false; + + link_dirty(link); + + while ((address = set_steal_first(link->ndisc_addresses))) { + r = set_ensure_put(&link->ndisc_addresses_old, &address_hash_ops, address); + if (r < 0) + return log_link_error_errno(link, r, "Failed to store old NDisc SLAAC address: %m"); + } + + while ((route = set_steal_first(link->ndisc_routes))) { + r = set_ensure_put(&link->ndisc_routes_old, &route_hash_ops, route); + if (r < 0) + return log_link_error_errno(link, r, "Failed to store old NDisc route: %m"); + } + r = sd_ndisc_router_get_flags(rt, &flags); if (r < 0) return log_link_error_errno(link, r, "Failed to get RA flags: %m"); @@ -757,10 +887,8 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) { r = dhcp6_request_address(link, !(flags & ND_RA_FLAG_MANAGED)); if (r < 0 && r != -EBUSY) return log_link_error_errno(link, r, "Could not acquire DHCPv6 lease on NDisc request: %m"); - else { + else log_link_debug(link, "Acquiring DHCPv6 lease on NDisc request"); - r = 0; - } } r = ndisc_router_process_default(link, rt); @@ -770,7 +898,33 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) { if (r < 0) return r; - return r; + if (link->ndisc_addresses_messages == 0) + link->ndisc_addresses_configured = true; + else { + log_link_debug(link, "Setting SLAAC addresses."); + + /* address_handler calls link_request_set_routes() and link_request_set_nexthop(). + * Before they are called, the related flags must be cleared. Otherwise, the link + * becomes configured state before routes are configured. */ + link->static_routes_configured = false; + link->static_nexthops_configured = false; + } + + if (link->ndisc_routes_messages == 0) + link->ndisc_routes_configured = true; + else + log_link_debug(link, "Setting NDisc routes."); + + r = ndisc_remove_old(link, false); + if (r < 0) + return r; + + if (link->ndisc_addresses_configured && link->ndisc_routes_configured) + link_check_ready(link); + else + link_set_state(link, LINK_STATE_CONFIGURING); + + return 0; } static void ndisc_handler(sd_ndisc *nd, sd_ndisc_event event, sd_ndisc_router *rt, void *userdata) { @@ -785,36 +939,11 @@ static void ndisc_handler(sd_ndisc *nd, sd_ndisc_event event, sd_ndisc_router *r switch (event) { case SD_NDISC_EVENT_ROUTER: - link->ndisc_addresses_configured = false; - link->ndisc_routes_configured = false; - r = ndisc_router_handler(link, rt); if (r < 0) { link_enter_failed(link); return; } - - if (link->ndisc_addresses_messages == 0) - link->ndisc_addresses_configured = true; - else { - log_link_debug(link, "Setting SLAAC addresses."); - - /* address_handler calls link_request_set_routes() and link_request_set_nexthop(). - * Before they are called, the related flags must be cleared. Otherwise, the link - * becomes configured state before routes are configured. */ - link->static_routes_configured = false; - link->static_nexthops_configured = false; - } - - if (link->ndisc_routes_messages == 0) - link->ndisc_routes_configured = true; - else - log_link_debug(link, "Setting NDisc routes."); - - if (link->ndisc_addresses_configured && link->ndisc_routes_configured) - link_check_ready(link); - else - link_set_state(link, LINK_STATE_CONFIGURING); break; case SD_NDISC_EVENT_TIMEOUT: diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index 322e819bc1..d04c33a5d8 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -146,6 +146,8 @@ void route_free(Route *route) { set_remove(route->link->routes_foreign, route); set_remove(route->link->dhcp_routes, route); set_remove(route->link->dhcp_routes_old, route); + set_remove(route->link->ndisc_routes, route); + set_remove(route->link->ndisc_routes_old, route); } ordered_set_free_free(route->multipath_routes);