From 87a33c0740524e894a170f75638012c2c5f90f24 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 14 Feb 2024 21:32:56 +0900 Subject: [PATCH] netowrk/ndisc: drop NDisc configurations when received NA without Router flag Closes #28421. --- src/network/networkd-ndisc.c | 159 +++++++++++++++++- .../conf/25-dhcp-server-veth-peer.network | 2 + 2 files changed, 157 insertions(+), 4 deletions(-) diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index 61d591a2af2..71e3c30727e 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -1391,7 +1391,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, const struct in6_addr *router, usec_t timestamp_usec) { bool updated = false; NDiscDNSSL *dnssl; NDiscRDNSS *rdnss; @@ -1420,6 +1420,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; + r = route_remove_and_cancel(route, link->manager); if (r < 0) RET_GATHER(ret, log_link_warning_errno(link, r, "Failed to remove outdated SLAAC route, ignoring: %m")); @@ -1432,6 +1435,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; + r = address_remove_and_cancel(address, link); if (r < 0) RET_GATHER(ret, log_link_warning_errno(link, r, "Failed to remove outdated SLAAC address, ignoring: %m")); @@ -1441,6 +1447,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; } @@ -1449,6 +1458,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; } @@ -1457,6 +1469,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; } @@ -1465,6 +1480,9 @@ static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec) { if (p64->lifetime_usec > timestamp_usec) continue; /* the pref64 prefix is still valid */ + if (router && !in6_addr_equal(&p64->router, router)) + continue; + free(set_remove(link->ndisc_pref64, p64)); /* The pref64 prefix is not exported through the state file, hence it is not necessary to set * the 'updated' flag. */ @@ -1486,7 +1504,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, /* router = */ NULL, now_usec); (void) ndisc_setup_expire(link); return 0; } @@ -1635,7 +1653,7 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) { if (r < 0) return r; - r = ndisc_drop_outdated(link, timestamp_usec); + r = ndisc_drop_outdated(link, /* router = */ NULL, timestamp_usec); if (r < 0) return r; @@ -1679,6 +1697,131 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) { return 0; } +static int ndisc_neighbor_handle_non_router_message(Link *link, sd_ndisc_neighbor *na) { + struct in6_addr address; + int r; + + assert(link); + assert(na); + + /* Received Neighbor Advertisement message without Router flag. The node might have been a router, + * and now it is not. Let's drop all configurations based on RAs sent from the node. */ + + r = sd_ndisc_neighbor_get_target_address(na, &address); + if (r == -ENODATA) + return 0; + if (r < 0) + return r; + + (void) ndisc_drop_outdated(link, /* router = */ &address, /* timestamp_usec = */ USEC_INFINITY); + return 0; +} + +static int ndisc_neighbor_handle_router_message(Link *link, sd_ndisc_neighbor *na) { + struct in6_addr current_address, original_address; + int r; + + assert(link); + assert(link->manager); + assert(na); + + /* Received Neighbor Advertisement message with Router flag. If the router address is changed, update + * the provider field of configurations. */ + + r = sd_ndisc_neighbor_get_sender_address(na, ¤t_address); + if (r == -ENODATA) + return 0; + if (r < 0) + return r; + + r = sd_ndisc_neighbor_get_target_address(na, &original_address); + if (r == -ENODATA) + return 0; + if (r < 0) + return r; + + if (in6_addr_equal(¤t_address, &original_address)) + return 0; /* the router address is not changed */ + + Route *route; + SET_FOREACH(route, link->manager->routes) { + if (route->source != NETWORK_CONFIG_SOURCE_NDISC) + continue; + + if (route->nexthop.ifindex != link->ifindex) + continue; + + if (!in6_addr_equal(&route->provider.in6, &original_address)) + continue; + + route->provider.in6 = current_address; + } + + Address *address; + SET_FOREACH(address, link->addresses) { + if (address->source != NETWORK_CONFIG_SOURCE_NDISC) + continue; + + if (!in6_addr_equal(&address->provider.in6, &original_address)) + continue; + + address->provider.in6 = current_address; + } + + NDiscRDNSS *rdnss; + SET_FOREACH(rdnss, link->ndisc_rdnss) { + if (!in6_addr_equal(&rdnss->router, &original_address)) + continue; + + rdnss->router = current_address; + } + + NDiscDNSSL *dnssl; + SET_FOREACH(dnssl, link->ndisc_dnssl) { + if (!in6_addr_equal(&dnssl->router, &original_address)) + continue; + + dnssl->router = current_address; + } + + NDiscCaptivePortal *cp; + SET_FOREACH(cp, link->ndisc_captive_portals) { + if (!in6_addr_equal(&cp->router, &original_address)) + continue; + + cp->router = current_address; + } + + NDiscPREF64 *p64; + SET_FOREACH(p64, link->ndisc_pref64) { + if (!in6_addr_equal(&p64->router, &original_address)) + continue; + + p64->router = current_address; + } + + return 0; +} + +static int ndisc_neighbor_handler(Link *link, sd_ndisc_neighbor *na) { + int r; + + assert(link); + assert(na); + + r = sd_ndisc_neighbor_is_router(na); + if (r < 0) + return r; + if (r == 0) + r = ndisc_neighbor_handle_non_router_message(link, na); + else + r = ndisc_neighbor_handle_router_message(link, na); + if (r < 0) + return r; + + return 0; +} + static void ndisc_handler(sd_ndisc *nd, sd_ndisc_event_t event, void *message, void *userdata) { Link *link = ASSERT_PTR(userdata); int r; @@ -1696,6 +1839,14 @@ static void ndisc_handler(sd_ndisc *nd, sd_ndisc_event_t event, void *message, v } break; + case SD_NDISC_EVENT_NEIGHBOR: + r = ndisc_neighbor_handler(link, ASSERT_PTR(message)); + if (r < 0 && r != -EBADMSG) { + link_enter_failed(link); + return; + } + break; + case SD_NDISC_EVENT_TIMEOUT: log_link_debug(link, "NDisc handler get timeout event"); if (link->ndisc_messages == 0) { @@ -1825,7 +1976,7 @@ void ndisc_flush(Link *link) { assert(link); /* Remove all addresses, routes, RDNSS, DNSSL, and Captive Portal entries, without exception. */ - (void) ndisc_drop_outdated(link, /* timestamp_usec = */ USEC_INFINITY); + (void) ndisc_drop_outdated(link, /* router = */ NULL, /* timestamp_usec = */ USEC_INFINITY); link->ndisc_rdnss = set_free(link->ndisc_rdnss); link->ndisc_dnssl = set_free(link->ndisc_dnssl); diff --git a/test/test-network/conf/25-dhcp-server-veth-peer.network b/test/test-network/conf/25-dhcp-server-veth-peer.network index d5cc6d3334b..abe3fa25969 100644 --- a/test/test-network/conf/25-dhcp-server-veth-peer.network +++ b/test/test-network/conf/25-dhcp-server-veth-peer.network @@ -6,3 +6,5 @@ Name=veth-peer IPv6AcceptRA=no Address=2600::1/0 Address=192.168.5.1/24 +# To make the kernel send NA with IsRouter flag. +IPv6Forwarding=yes -- 2.47.3