netowrk/ndisc: drop NDisc configurations when received NA without Router flag

Closes #28421.
This commit is contained in:
Yu Watanabe 2024-02-14 21:32:56 +09:00
parent ecab9b6040
commit 87a33c0740
2 changed files with 157 additions and 4 deletions

View file

@ -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, &current_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(&current_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);

View file

@ -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