diff --git a/sys/netinet6/in6_var.h b/sys/netinet6/in6_var.h --- a/sys/netinet6/in6_var.h +++ b/sys/netinet6/in6_var.h @@ -134,6 +134,8 @@ * currently used for temporary addresses only. */ time_t ia6_updatetime; + time_t ia6_dad_lastattempt; /* Last DAD attempt */ + uint32_t ia6_dad_restarts; /* Number of restarts */ /* back pointer to the ND prefix (for autoconfigured addresses only) */ struct nd_prefix *ia6_ndpr; diff --git a/sys/netinet6/nd6.h b/sys/netinet6/nd6.h --- a/sys/netinet6/nd6.h +++ b/sys/netinet6/nd6.h @@ -392,6 +392,7 @@ caddr_t nd6_ifptomac(struct ifnet *); void nd6_dad_init(void); void nd6_dad_start(struct ifaddr *, int); +void nd6_dad_restart(struct in6_ifaddr *); void nd6_dad_stop(struct ifaddr *); /* nd6_rtr.c */ diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c --- a/sys/netinet6/nd6.c +++ b/sys/netinet6/nd6.c @@ -1030,9 +1030,17 @@ ((ifp->if_flags & IFF_UP) == 0 || (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || (ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED) != 0)){ + nd6_dad_stop(&ia6->ia_ifa); ia6->ia6_flags &= ~IN6_IFF_DUPLICATED; ia6->ia6_flags |= IN6_IFF_TENTATIVE; } + if ((ia6->ia6_flags & IN6_IFF_DUPLICATED) != 0) { + /* + * Retry DAD to recover in the case the other + * client is no longer using this address + */ + nd6_dad_restart(ia6); + } /* * A new RA might have made a deprecated address diff --git a/sys/netinet6/nd6_nbr.c b/sys/netinet6/nd6_nbr.c --- a/sys/netinet6/nd6_nbr.c +++ b/sys/netinet6/nd6_nbr.c @@ -113,6 +113,15 @@ transmit DAD packet */ #define V_dad_maxtry VNET(dad_maxtry) +#define MAX_DAD_RESTARTS 10 /* Maximum restarts */ +#define DAD_RESTART_INTERVAL 30 /* In seconds */ + +VNET_DEFINE_STATIC(int, max_dad_restarts) = MAX_DAD_RESTARTS; +#define V_max_dad_restarts VNET(max_dad_restarts) + +SYSCTL_INT(_net_inet6_ip6, OID_AUTO, dad_restart, CTLFLAG_VNET | CTLFLAG_RW, + &VNET_NAME(max_dad_restarts), 0, "Maximum DAD restart attempts per address"); + VNET_DEFINE_STATIC(int, nd6_onlink_ns_rfc4861) = 0; #define V_nd6_onlink_ns_rfc4861 VNET(nd6_onlink_ns_rfc4861) SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ND6_ONLINKNSRFC4861, @@ -1337,6 +1346,33 @@ DADQ_WUNLOCK(); } +/* + * Restarts the Duplicate Address Detection (DAD) process for an IPv6 interface address. + * + * This function reinitializes the DAD process for the specified IPv6 interface + * address (`ia6`). It is typically invoked when a DAD failure occurs or when + * the DAD process needs to be retried due to changes in the network state or + * configuration. + */ +void +nd6_dad_restart(struct in6_ifaddr *ia6) +{ + if (((ia6->ia6_flags & IN6_IFF_DUPLICATED) == 0) || + (ia6->ia6_dad_lastattempt == 0) || + (ia6->ia6_dad_restarts >= V_max_dad_restarts)) { + return; + } + + if (((time_uptime - ia6->ia6_dad_lastattempt) >= DAD_RESTART_INTERVAL) && + ((ia6->ia6_flags & IN6_IFF_DETACHED) == 0)) { + ia6->ia6_dad_lastattempt = 0; + ia6->ia6_dad_restarts++; + ia6->ia6_flags &= ~IN6_IFF_DUPLICATED; + ia6->ia6_flags |= IN6_IFF_TENTATIVE; + nd6_dad_start(&ia6->ia_ifa, arc4random() % (MAX_RTR_SOLICITATION_DELAY * hz)); + } +} + /* * terminate DAD unconditionally. used for address removals. */