}
}
-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;
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"));
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"));
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;
}
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;
}
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;
}
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. */
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;
}
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;
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;
}
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) {
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);