From: Roy Marples Date: Wed, 11 Sep 2024 09:28:58 +0000 (+0100) Subject: IPv6: Only advertise addresses when needed X-Git-Tag: v10.1.0~11 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=d0fef9f768aaa9266180571064010ff04293634e;p=thirdparty%2Fdhcpcd.git IPv6: Only advertise addresses when needed Remember when we have advertised an address. If we want to advertise it again, check this first. If we still want to advertise it, clear this flag for all other matching addresses. Clear advertised flags from all addresses on carrier up. This reduces needless NA spam from dhcpcd when the IPv6 Router is needlessly chatty with RA. --- diff --git a/src/ipv6.c b/src/ipv6.c index 8877381a..52a3c88a 100644 --- a/src/ipv6.c +++ b/src/ipv6.c @@ -1832,16 +1832,19 @@ ipv6_startstatic(struct interface *ifp) int ipv6_start(struct interface *ifp) { -#ifdef IPV6_POLLADDRFLAG +#if defined(ND6_ADVERTISE) || defined(IPV6_POLLADDRFLAG) struct ipv6_state *state; /* We need to update the address flags. */ if ((state = IPV6_STATE(ifp)) != NULL) { struct ipv6_addr *ia; +#ifdef IPV6_POLLADDRFLAG const char *alias; int flags; +#endif TAILQ_FOREACH(ia, &state->addrs, next) { +#ifdef IPV6_POLLADDRFLAG #ifdef ALIAS_ADDR alias = ia->alias; #else @@ -1850,6 +1853,9 @@ ipv6_start(struct interface *ifp) flags = if_addrflags6(ia->iface, &ia->addr, alias); if (flags != -1) ia->addr_flags = flags; +#endif + /* hwaddr could have changed */ + ia->flags &= ~IPV6_AF_ADVERTISED; } } #endif diff --git a/src/ipv6.h b/src/ipv6.h index a2989858..2a93727a 100644 --- a/src/ipv6.h +++ b/src/ipv6.h @@ -160,7 +160,8 @@ /* * ND6 Advertising is only used for IP address sharing to prefer - * the address on a specific interface. + * the address on a specific interface or when the hardware address + * of the interface changes. * This just fails to work on OpenBSD and causes erroneous duplicate * address messages on BSD's other then DragonFly and NetBSD. */ @@ -227,8 +228,9 @@ struct ipv6_addr { #define IPV6_AF_EXTENDED (1U << 13) #define IPV6_AF_REGEN (1U << 14) #define IPV6_AF_ROUTER (1U << 15) +#define IPV6_AF_ADVERTISED (1U << 16) #ifdef IPV6_MANAGETEMPADDR -#define IPV6_AF_TEMPORARY (1U << 16) +#define IPV6_AF_TEMPORARY (1U << 17) #endif struct ll_callback { diff --git a/src/ipv6nd.c b/src/ipv6nd.c index 71b1230c..49f425b7 100644 --- a/src/ipv6nd.c +++ b/src/ipv6nd.c @@ -514,6 +514,7 @@ ipv6nd_advertise(struct ipv6_addr *ia) struct interface *ifp; struct ipv6_state *state; struct ipv6_addr *iap, *iaf; + bool found_another = false; struct nd_neighbor_advert *na; if (IN6_IS_ADDR_MULTICAST(&ia->addr)) @@ -529,20 +530,20 @@ ipv6nd_advertise(struct ipv6_addr *ia) iaf = NULL; TAILQ_FOREACH(ifp, ctx->ifaces, next) { state = IPV6_STATE(ifp); - if (state == NULL || !if_is_link_up(ifp)) + if (state == NULL) continue; TAILQ_FOREACH(iap, &state->addrs, next) { if (!IN6_ARE_ADDR_EQUAL(&iap->addr, &ia->addr)) continue; - /* Cancel any current advertisement. */ - eloop_timeout_delete(ctx->eloop, - ipv6nd_sendadvertisement, iap); + if (iaf != NULL) + found_another = true; /* Don't advertise what we can't use. */ if (iap->prefix_vltime == 0 || - iap->addr_flags & IN6_IFF_NOTUSEABLE) + iap->addr_flags & IN6_IFF_NOTUSEABLE || + !if_is_link_up(ifp)) continue; if (iaf == NULL || @@ -550,9 +551,32 @@ ipv6nd_advertise(struct ipv6_addr *ia) iaf = iap; } } - if (iaf == NULL) + + /* If we have already advertised the address, return. */ + if (iaf == NULL || iaf->flags & IPV6_AF_ADVERTISED) return; + /* Now cancel any other advertisements for the same address. */ + if (found_another) { + TAILQ_FOREACH(ifp, ctx->ifaces, next) { + state = IPV6_STATE(ifp); + if (state == NULL) + continue; + + TAILQ_FOREACH(iap, &state->addrs, next) { + if (!IN6_ARE_ADDR_EQUAL(&iap->addr, &ia->addr)) + continue; + + iap->flags &= ~IPV6_AF_ADVERTISED; + eloop_timeout_delete(ctx->eloop, + ipv6nd_sendadvertisement, iap); + } + } + } else { + eloop_timeout_delete(ctx->eloop, + ipv6nd_sendadvertisement, iaf); + } + /* Make the packet. */ ifp = iaf->iface; iaf->na_len = sizeof(*na); @@ -588,7 +612,7 @@ ipv6nd_advertise(struct ipv6_addr *ia) iaf->na_count = 0; free(iaf->na); iaf->na = na; - eloop_timeout_delete(ctx->eloop, ipv6nd_sendadvertisement, iaf); + iaf->flags |= IPV6_AF_ADVERTISED; ipv6nd_sendadvertisement(iaf); } #elif !defined(SMALL)