From defd45464bac7e51a09de4af316f8b54538081bf Mon Sep 17 00:00:00 2001 From: hrs Date: Sat, 3 Oct 2015 12:09:12 +0000 Subject: - 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(-) (limited to 'sys/netinet6') diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c index 4098ffb..be22790 100644 --- a/sys/netinet6/nd6.c +++ b/sys/netinet6/nd6.c @@ -810,8 +810,31 @@ 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 7c3c87c..199dbee 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++; -- cgit v1.1 From 15bc65f144a8e7501d5b41594aa7436b28858d6f Mon Sep 17 00:00:00 2001 From: melifaro Date: Sun, 4 Oct 2015 07:02:17 +0000 Subject: Fix condition for nd6_llinfo_getholdsrc() introduced in r287484. Effectively it always returned NULL so SAS was always performed and sometimes the result might have been different. Fix state machine change accidentally introduced in r287985: state (4) inside nd6_cache_lladdr() (existing entry got nd message with the same lladdress) started to cause lle state transition to STALE instead of no-action. --- sys/netinet6/nd6.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'sys/netinet6') diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c index be22790..7d7f23c 100644 --- a/sys/netinet6/nd6.c +++ b/sys/netinet6/nd6.c @@ -530,7 +530,7 @@ nd6_llinfo_get_holdsrc(struct llentry *ln, struct in6_addr *src) * assume every packet in la_hold has the same IP header */ m = ln->la_hold; - if (sizeof(hdr) < m->m_len) + if (sizeof(hdr) > m->m_len) return (NULL); m_copydata(m, 0, sizeof(hdr), (caddr_t)&hdr); @@ -1798,7 +1798,8 @@ nd6_cache_lladdr(struct ifnet *ifp, struct in6_addr *from, char *lladdr, */ bcopy(lladdr, &ln->ll_addr, ifp->if_addrlen); ln->la_flags |= LLE_VALID; - nd6_llinfo_setstate(ln, ND6_LLINFO_STALE); + if (do_update != 0) /* 3,5,7 */ + nd6_llinfo_setstate(ln, ND6_LLINFO_STALE); EVENTHANDLER_INVOKE(lle_event, ln, LLENTRY_RESOLVED); -- cgit v1.1 From 02d9938404f08a2f46ca258feb3e7345112b722e Mon Sep 17 00:00:00 2001 From: melifaro Date: Sun, 4 Oct 2015 08:21:15 +0000 Subject: Add __noinline attribute to several functions to ease dtrace instrumentation --- sys/netinet6/nd6.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) (limited to 'sys/netinet6') diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c index 7d7f23c..b319982 100644 --- a/sys/netinet6/nd6.c +++ b/sys/netinet6/nd6.c @@ -512,12 +512,13 @@ nd6_llinfo_settimer_locked(struct llentry *ln, long tick) } /* -* Gets source address of the first packet in hold queue -* and stores it in @src. -* Returns pointer to @src (if hold queue is not empty) or NULL. -* -*/ -static struct in6_addr * + * Gets source address of the first packet in hold queue + * and stores it in @src. + * Returns pointer to @src (if hold queue is not empty) or NULL. + * + * Set noinline to be dtrace-friendly + */ +static __noinline struct in6_addr * nd6_llinfo_get_holdsrc(struct llentry *ln, struct in6_addr *src) { struct ip6_hdr hdr; @@ -541,8 +542,10 @@ nd6_llinfo_get_holdsrc(struct llentry *ln, struct in6_addr *src) /* * Switch @lle state to new state optionally arming timers. + * + * Set noinline to be dtrace-friendly */ -void +__noinline void nd6_llinfo_setstate(struct llentry *lle, int newstate) { struct ifnet *ifp; @@ -586,7 +589,12 @@ nd6_llinfo_settimer(struct llentry *ln, long tick) LLE_WUNLOCK(ln); } -static void +/* + * Timer-dependent part of nd state machine. + * + * Set noinline to be dtrace-friendly + */ +static __noinline void nd6_llinfo_timer(void *arg) { struct llentry *ln; @@ -1179,8 +1187,10 @@ nd6_is_addr_neighbor(const struct sockaddr_in6 *addr, struct ifnet *ifp) * Since the function would cause significant changes in the kernel, DO NOT * make it global, unless you have a strong reason for the change, and are sure * that the change is safe. + * + * Set noinline to be dtrace-friendly */ -static void +static __noinline void nd6_free(struct llentry *ln, int gc) { struct nd_defrouter *dr; @@ -2036,8 +2046,10 @@ nd6_resolve(struct ifnet *ifp, int is_gw, struct mbuf *m, * Heavy version. * Function assume that destination LLE does not exist, * is invalid or stale, so LLE_EXCLUSIVE lock needs to be acquired. + * + * Set noinline to be dtrace-friendly */ -static int +static __noinline int nd6_resolve_slow(struct ifnet *ifp, struct mbuf *m, const struct sockaddr_in6 *dst, u_char *desten, uint32_t *pflags) { -- cgit v1.1 From 0dee60f8c8e7a05cd88ff2cba642fdffd378d433 Mon Sep 17 00:00:00 2001 From: melifaro Date: Sun, 4 Oct 2015 08:33:16 +0000 Subject: Eliminate nd6_llinfo_settimer(). All consumers were converted to use nd6_llinfo_settimer_locked() in r216022. Make nd6_llinfo_settimer_locked() static: last external consumer was converted in r288124. --- sys/netinet6/nd6.c | 13 ++----------- sys/netinet6/nd6.h | 2 -- 2 files changed, 2 insertions(+), 13 deletions(-) (limited to 'sys/netinet6') diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c index b319982..6cbe618 100644 --- a/sys/netinet6/nd6.c +++ b/sys/netinet6/nd6.c @@ -134,6 +134,7 @@ static int regen_tmpaddr(struct in6_ifaddr *); static void nd6_free(struct llentry *, int); static void nd6_free_redirect(const struct llentry *); static void nd6_llinfo_timer(void *); +static void nd6_llinfo_settimer_locked(struct llentry *, long); static void clear_llinfo_pqueue(struct llentry *); static void nd6_rtrequest(int, struct rtentry *, struct rt_addrinfo *); static int nd6_resolve_slow(struct ifnet *, struct mbuf *, @@ -483,7 +484,7 @@ skip1: /* * ND6 timer routine to handle ND6 entries */ -void +static void nd6_llinfo_settimer_locked(struct llentry *ln, long tick) { int canceled; @@ -579,16 +580,6 @@ nd6_llinfo_setstate(struct llentry *lle, int newstate) lle->ln_state = newstate; } - -void -nd6_llinfo_settimer(struct llentry *ln, long tick) -{ - - LLE_WLOCK(ln); - nd6_llinfo_settimer_locked(ln, tick); - LLE_WUNLOCK(ln); -} - /* * Timer-dependent part of nd state machine. * diff --git a/sys/netinet6/nd6.h b/sys/netinet6/nd6.h index c5f0eea..4c39a09 100644 --- a/sys/netinet6/nd6.h +++ b/sys/netinet6/nd6.h @@ -408,8 +408,6 @@ struct llentry *nd6_lookup(const struct in6_addr *, int, struct ifnet *); struct llentry *nd6_alloc(const struct in6_addr *, int, struct ifnet *); void nd6_setmtu(struct ifnet *); void nd6_llinfo_setstate(struct llentry *lle, int newstate); -void nd6_llinfo_settimer(struct llentry *, long); -void nd6_llinfo_settimer_locked(struct llentry *, long); void nd6_timer(void *); void nd6_purge(struct ifnet *); int nd6_resolve(struct ifnet *, int, struct mbuf *, -- cgit v1.1 From 78672053553cdd4f366badf1c185c7b75d5eb6a5 Mon Sep 17 00:00:00 2001 From: melifaro Date: Sun, 4 Oct 2015 12:42:07 +0000 Subject: Simplify if (lladdr) condition in nd6_cache_lladdr(): For case (7) (new entry) nothing has to be done except lle_event. Invoke this event directly from "create new lle" code block. For case (4) (existing entry, same mac) useless mac update was performed, along with LLENTRY_RESOLVED lle_event. There was no sense in doing that, since nothing really had changed. Simply avoid this condition instead. Given that, condition was simplified to (3),(5) states which can be merged with previous block. --- sys/netinet6/nd6.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'sys/netinet6') diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c index 6cbe618..4744574 100644 --- a/sys/netinet6/nd6.c +++ b/sys/netinet6/nd6.c @@ -1753,6 +1753,7 @@ nd6_cache_lladdr(struct ifnet *ifp, struct in6_addr *from, char *lladdr, /* No existing lle, mark as new entry */ is_newentry = 1; nd6_llinfo_setstate(ln, ND6_LLINFO_STALE); + EVENTHANDLER_INVOKE(lle_event, ln, LLENTRY_RESOLVED); } else { lltable_free_entry(LLTABLE6(ifp), ln); ln = ln_tmp; @@ -1789,25 +1790,21 @@ nd6_cache_lladdr(struct ifnet *ifp, struct in6_addr *from, char *lladdr, */ do_update = 0; - if (!is_newentry && llchange != 0) + if (is_newentry == 0 && llchange != 0) { do_update = 1; /* (3,5) */ - if (lladdr) { /* (3-5) and (7) */ /* * Record source link-layer address * XXX is it dependent to ifp->if_type? */ bcopy(lladdr, &ln->ll_addr, ifp->if_addrlen); ln->la_flags |= LLE_VALID; - if (do_update != 0) /* 3,5,7 */ - nd6_llinfo_setstate(ln, ND6_LLINFO_STALE); + nd6_llinfo_setstate(ln, ND6_LLINFO_STALE); EVENTHANDLER_INVOKE(lle_event, ln, LLENTRY_RESOLVED); - if (do_update) { - if (ln->la_hold != NULL) - nd6_grab_holdchain(ln, &chain, &sin6); - } + if (ln->la_hold != NULL) + nd6_grab_holdchain(ln, &chain, &sin6); } /* Calculates new router status */ -- cgit v1.1 From e2681931e491c40d3418500651c1578e5b89a567 Mon Sep 17 00:00:00 2001 From: melifaro Date: Sun, 4 Oct 2015 19:10:27 +0000 Subject: Invoke lle_event for new entry iff it has lladdr set. --- sys/netinet6/nd6.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'sys/netinet6') diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c index 4744574..23e7a9b 100644 --- a/sys/netinet6/nd6.c +++ b/sys/netinet6/nd6.c @@ -1750,10 +1750,12 @@ nd6_cache_lladdr(struct ifnet *ifp, struct in6_addr *from, char *lladdr, lltable_link_entry(LLTABLE6(ifp), ln); IF_AFDATA_WUNLOCK(ifp); if (ln_tmp == NULL) { - /* No existing lle, mark as new entry */ + /* No existing lle, mark as new entry (6,7) */ is_newentry = 1; nd6_llinfo_setstate(ln, ND6_LLINFO_STALE); - EVENTHANDLER_INVOKE(lle_event, ln, LLENTRY_RESOLVED); + if (lladdr != NULL) /* (7) */ + EVENTHANDLER_INVOKE(lle_event, ln, + LLENTRY_RESOLVED); } else { lltable_free_entry(LLTABLE6(ifp), ln); ln = ln_tmp; -- cgit v1.1