From 6401c828ce32490598942185dfe0bb78fc18caf7 Mon Sep 17 00:00:00 2001 From: Hiroki Sato Date: Sat, 3 Oct 2015 12:09:12 +0000 Subject: [PATCH] - Schedule DAD for IN6_IFF_TENTATIVE addresses in nd6_timer(). This catches cases that DAD probes cannot be sent because of IFF_UP && !IFF_DRV_RUNNING. - nd6_dad_starttimer() now calls nd6_dad_ns_output(), instead of calling it before nd6_dad_starttimer(). - Do not release an entry in dadq when a duplicate entry is being added. --- sys/netinet6/nd6.c | 26 +++++++++++++++++++++++++- sys/netinet6/nd6_nbr.c | 40 ++++++++++++++++++++++------------------ 2 files changed, 47 insertions(+), 19 deletions(-) diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c index 4098ffb88b71..be227904edda 100644 --- a/sys/netinet6/nd6.c +++ b/sys/netinet6/nd6.c @@ -810,7 +810,30 @@ nd6_timer(void *arg) goto addrloop; } } + } else if ((ia6->ia6_flags & IN6_IFF_TENTATIVE) != 0) { + /* + * Schedule DAD for a tentative address. This happens + * if the interface was down or not running + * when the address was configured. + */ + int delay; + + delay = arc4random() % + (MAX_RTR_SOLICITATION_DELAY * hz); + nd6_dad_start((struct ifaddr *)ia6, delay); } else { + /* + * Check status of the interface. If it is down, + * mark the address as tentative for future DAD. + */ + if ((ia6->ia_ifp->if_flags & IFF_UP) == 0 || + (ia6->ia_ifp->if_drv_flags & IFF_DRV_RUNNING) + == 0 || + (ND_IFINFO(ia6->ia_ifp)->flags & + ND6_IFF_IFDISABLED) != 0) { + ia6->ia6_flags &= ~IN6_IFF_DUPLICATED; + ia6->ia6_flags |= IN6_IFF_TENTATIVE; + } /* * A new RA might have made a deprecated address * preferred. @@ -1452,7 +1475,8 @@ nd6_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp) /* Mark all IPv6 address as tentative. */ ND_IFINFO(ifp)->flags |= ND6_IFF_IFDISABLED; - if ((ND_IFINFO(ifp)->flags & ND6_IFF_NO_DAD) == 0) { + if (V_ip6_dad_count > 0 && + (ND_IFINFO(ifp)->flags & ND6_IFF_NO_DAD) == 0) { IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { diff --git a/sys/netinet6/nd6_nbr.c b/sys/netinet6/nd6_nbr.c index 7c3c87ca30b6..199dbee9e88b 100644 --- a/sys/netinet6/nd6_nbr.c +++ b/sys/netinet6/nd6_nbr.c @@ -85,11 +85,11 @@ static struct dadq *nd6_dad_find(struct ifaddr *, struct nd_opt_nonce *); static void nd6_dad_add(struct dadq *dp); static void nd6_dad_del(struct dadq *dp); static void nd6_dad_rele(struct dadq *); -static void nd6_dad_starttimer(struct dadq *, int); +static void nd6_dad_starttimer(struct dadq *, int, int); static void nd6_dad_stoptimer(struct dadq *); static void nd6_dad_timer(struct dadq *); static void nd6_dad_duplicated(struct ifaddr *, struct dadq *); -static void nd6_dad_ns_output(struct dadq *, struct ifaddr *); +static void nd6_dad_ns_output(struct dadq *); static void nd6_dad_ns_input(struct ifaddr *, struct nd_opt_nonce *); static void nd6_dad_na_input(struct ifaddr *); static void nd6_na_output_fib(struct ifnet *, const struct in6_addr *, @@ -1199,9 +1199,11 @@ nd6_dad_find(struct ifaddr *ifa, struct nd_opt_nonce *n) } static void -nd6_dad_starttimer(struct dadq *dp, int ticks) +nd6_dad_starttimer(struct dadq *dp, int ticks, int send_ns) { + if (send_ns != 0) + nd6_dad_ns_output(dp); callout_reset(&dp->dad_timer_ch, ticks, (void (*)(void *))nd6_dad_timer, (void *)dp); } @@ -1240,6 +1242,7 @@ nd6_dad_start(struct ifaddr *ifa, int delay) struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa; struct dadq *dp; char ip6buf[INET6_ADDRSTRLEN]; + int send_ns; /* * If we don't need DAD, don't do it. @@ -1276,8 +1279,10 @@ nd6_dad_start(struct ifaddr *ifa, int delay) return; } if ((dp = nd6_dad_find(ifa, NULL)) != NULL) { - /* DAD already in progress */ - nd6_dad_rele(dp); + /* + * DAD already in progress. Let the existing entry + * to finish it. + */ return; } @@ -1310,13 +1315,12 @@ nd6_dad_start(struct ifaddr *ifa, int delay) dp->dad_ns_lcount = dp->dad_loopbackprobe = 0; refcount_init(&dp->dad_refcnt, 1); nd6_dad_add(dp); + send_ns = 0; if (delay == 0) { - nd6_dad_ns_output(dp, ifa); - nd6_dad_starttimer(dp, - (long)ND_IFINFO(ifa->ifa_ifp)->retrans * hz / 1000); - } else { - nd6_dad_starttimer(dp, delay); + send_ns = 1; + delay = (long)ND_IFINFO(ifa->ifa_ifp)->retrans * hz / 1000; } + nd6_dad_starttimer(dp, delay, send_ns); } /* @@ -1386,7 +1390,8 @@ nd6_dad_timer(struct dadq *dp) if ((dp->dad_ns_tcount > V_dad_maxtry) && (((ifp->if_flags & IFF_UP) == 0) || ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0))) { - nd6log((LOG_INFO, "%s: could not run DAD, driver problem?\n", + nd6log((LOG_INFO, "%s: could not run DAD " + "because the interface was down or not running.\n", if_name(ifa->ifa_ifp))); goto err; } @@ -1396,9 +1401,8 @@ nd6_dad_timer(struct dadq *dp) /* * We have more NS to go. Send NS packet for DAD. */ - nd6_dad_ns_output(dp, ifa); nd6_dad_starttimer(dp, - (long)ND_IFINFO(ifa->ifa_ifp)->retrans * hz / 1000); + (long)ND_IFINFO(ifa->ifa_ifp)->retrans * hz / 1000, 1); goto done; } else { /* @@ -1426,11 +1430,11 @@ nd6_dad_timer(struct dadq *dp) * Send an NS immediately and increase dad_count by * V_nd6_mmaxtries - 1. */ - nd6_dad_ns_output(dp, ifa); dp->dad_count = dp->dad_ns_ocount + V_nd6_mmaxtries - 1; nd6_dad_starttimer(dp, - (long)ND_IFINFO(ifa->ifa_ifp)->retrans * hz / 1000); + (long)ND_IFINFO(ifa->ifa_ifp)->retrans * hz / 1000, + 1); goto done; } else { /* @@ -1517,10 +1521,10 @@ nd6_dad_duplicated(struct ifaddr *ifa, struct dadq *dp) } static void -nd6_dad_ns_output(struct dadq *dp, struct ifaddr *ifa) +nd6_dad_ns_output(struct dadq *dp) { - struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa; - struct ifnet *ifp = ifa->ifa_ifp; + struct in6_ifaddr *ia = (struct in6_ifaddr *)dp->dad_ifa; + struct ifnet *ifp = dp->dad_ifa->ifa_ifp; int i; dp->dad_ns_tcount++;