diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index 7570d37662..c6ca7f95e2 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -448,10 +448,10 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) { _cleanup_(route_freep) Route *route = NULL; + unsigned prefixlen, preference; usec_t timestamp_usec; uint32_t lifetime_sec; struct in6_addr prefix; - unsigned prefixlen; int r; assert(link); @@ -477,6 +477,11 @@ static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) { if (r < 0) return log_link_warning_errno(link, r, "Failed to get prefix length: %m"); + /* Prefix Information option does not have preference, hence we use the 'main' preference here */ + r = sd_ndisc_router_get_preference(rt, &preference); + if (r < 0) + log_link_warning_errno(link, r, "Failed to get default router preference from RA: %m"); + r = route_new(&route); if (r < 0) return log_oom(); @@ -484,6 +489,7 @@ static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) { route->family = AF_INET6; route->dst.in6 = prefix; route->dst_prefixlen = prefixlen; + route->pref = preference; route->lifetime_usec = sec_to_usec(lifetime_sec, timestamp_usec); r = ndisc_request_route(TAKE_PTR(route), link, rt); @@ -972,7 +978,7 @@ static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) { } } -static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec) { +static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec, const struct in6_addr *router) { bool updated = false; NDiscDNSSL *dnssl; NDiscRDNSS *rdnss; @@ -996,6 +1002,9 @@ static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec) { if (route->lifetime_usec >= timestamp_usec) continue; /* the route is still valid */ + if (router && !in6_addr_equal(&route->provider.in6, router)) + continue; + k = route_remove_and_drop(route); if (k < 0) r = log_link_warning_errno(link, k, "Failed to remove outdated SLAAC route, ignoring: %m"); @@ -1008,6 +1017,9 @@ static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec) { if (address->lifetime_valid_usec >= timestamp_usec) continue; /* the address is still valid */ + if (router && !in6_addr_equal(&address->provider.in6, router)) + continue; + k = address_remove_and_drop(address); if (k < 0) r = log_link_warning_errno(link, k, "Failed to remove outdated SLAAC address, ignoring: %m"); @@ -1017,6 +1029,9 @@ static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec) { if (rdnss->lifetime_usec >= timestamp_usec) continue; /* the DNS server is still valid */ + if (router && !in6_addr_equal(&rdnss->router, router)) + continue; + free(set_remove(link->ndisc_rdnss, rdnss)); updated = true; } @@ -1025,6 +1040,9 @@ static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec) { if (dnssl->lifetime_usec >= timestamp_usec) continue; /* the DNS domain is still valid */ + if (router && !in6_addr_equal(&dnssl->router, router)) + continue; + free(set_remove(link->ndisc_dnssl, dnssl)); updated = true; } @@ -1033,6 +1051,9 @@ static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec) { if (cp->lifetime_usec >= timestamp_usec) continue; /* the captive portal is still valid */ + if (router && !in6_addr_equal(&cp->router, router)) + continue; + ndisc_captive_portal_free(set_remove(link->ndisc_captive_portals, cp)); updated = true; } @@ -1053,7 +1074,7 @@ static int ndisc_expire_handler(sd_event_source *s, uint64_t usec, void *userdat assert_se(sd_event_now(link->manager->event, CLOCK_BOOTTIME, &now_usec) >= 0); - (void) ndisc_drop_outdated(link, now_usec); + (void) ndisc_drop_outdated(link, now_usec, NULL); (void) ndisc_setup_expire(link); return 0; } @@ -1154,6 +1175,7 @@ static int ndisc_start_dhcp6_client(Link *link, sd_ndisc_router *rt) { } static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) { + uint16_t router_lifetime_sec; struct in6_addr router; usec_t timestamp_usec; int r; @@ -1186,12 +1208,28 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) { log_link_debug(link, "Received RA without timestamp, ignoring."); return 0; } + + r = ndisc_drop_outdated(link, timestamp_usec, NULL); if (r < 0) return r; - r = ndisc_drop_outdated(link, timestamp_usec); + r = sd_ndisc_router_get_lifetime(rt, &router_lifetime_sec); if (r < 0) - return r; + return log_link_warning_errno(link, r, "Failed to get lifetime of RA message: %m"); + + /* https://datatracker.ietf.org/doc/html/rfc4861 + * Router Lifetime: A Lifetime of 0 indicates that the router is not a default router + * and SHOULD NOT appear on the default router list. + */ + if (router_lifetime_sec == 0) { + log_link_debug(link, "Received RA with lifetime = 0, dropping configurations."); + + r = ndisc_drop_outdated(link, USEC_INFINITY, &router); + if (r < 0) + log_link_warning_errno(link, r, "Failed to process RA with zero lifetime, ignoring: %m"); + + return 0; + } r = ndisc_start_dhcp6_client(link, rt); if (r < 0)