]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
IPv6: Only advertise addresses when needed
authorRoy Marples <roy@marples.name>
Wed, 11 Sep 2024 09:28:58 +0000 (10:28 +0100)
committerRoy Marples <roy@marples.name>
Wed, 11 Sep 2024 09:33:40 +0000 (10:33 +0100)
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.

src/ipv6.c
src/ipv6.h
src/ipv6nd.c

index 8877381ae26b96e3ccad6238e32cea1d142a73c1..52a3c88a014f0c0b08f34db9df7cd0f58861d850 100644 (file)
@@ -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
index a298985864f7a00819cb3ef45f12a4d0899cf69b..2a93727a00e7f9a789dceced3ed7a08fe95748f1 100644 (file)
 
 /*
  * 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 {
index 71b1230ca1c48fad34974d23120d36dbb8eb86b3..49f425b765b96fa3cce02d0ec276f73333a391e7 100644 (file)
@@ -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)