diff options
Diffstat (limited to 'sys/netinet6')
-rw-r--r-- | sys/netinet6/nd6.c | 87 | ||||
-rw-r--r-- | sys/netinet6/nd6.h | 2 | ||||
-rw-r--r-- | sys/netinet6/nd6_nbr.c | 40 |
3 files changed, 79 insertions, 50 deletions
diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c index 4098ffb..23e7a9b 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; @@ -512,12 +513,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; @@ -530,7 +532,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); @@ -541,8 +543,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; @@ -576,17 +580,12 @@ 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); -} - -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; @@ -810,8 +809,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. */ @@ -1156,8 +1178,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; @@ -1452,7 +1476,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) { @@ -1725,9 +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); + if (lladdr != NULL) /* (7) */ + EVENTHANDLER_INVOKE(lle_event, ln, + LLENTRY_RESOLVED); } else { lltable_free_entry(LLTABLE6(ifp), ln); ln = ln_tmp; @@ -1764,10 +1792,9 @@ 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? @@ -1778,10 +1805,8 @@ nd6_cache_lladdr(struct ifnet *ifp, struct in6_addr *from, char *lladdr, 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 */ @@ -2011,8 +2036,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) { 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 *, 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++; |