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