diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c index 60693e1..4bb8498 100644 --- a/sys/netinet6/nd6.c +++ b/sys/netinet6/nd6.c @@ -127,7 +127,7 @@ static int nd6_is_new_addr_neighbor(const struct sockaddr_in6 *, static void nd6_setmtu0(struct ifnet *, struct nd_ifinfo *); static void nd6_slowtimo(void *); static int regen_tmpaddr(struct in6_ifaddr *); -static void nd6_free(struct llentry *, int); +static void nd6_free(struct llentry *, struct nd_defrouter *, int); static void nd6_free_redirect(const struct llentry *); static void nd6_llinfo_timer(void *); static void nd6_llinfo_settimer_locked(struct llentry *, long); @@ -723,12 +723,17 @@ nd6_llinfo_timer(void *arg) struct llentry *ln; struct in6_addr *dst, *pdst, *psrc, src; struct ifnet *ifp; - struct nd_ifinfo *ndi = NULL; + struct nd_defrouter *dr; + struct nd_ifinfo *ndi; int do_switch, send_ns; long delay; KASSERT(arg != NULL, ("%s: arg NULL", __func__)); ln = (struct llentry *)arg; + ifp = lltable_get_ifp(ln->lle_tbl); + CURVNET_SET(ifp->if_vnet); + + ND6_RLOCK(); LLE_WLOCK(ln); if (callout_pending(&ln->lle_timer)) { /* @@ -748,15 +753,21 @@ nd6_llinfo_timer(void *arg) * would have been 1. */ LLE_WUNLOCK(ln); + ND6_RUNLOCK(); + CURVNET_RESTORE(); return; } - ifp = ln->lle_tbl->llt_ifp; - CURVNET_SET(ifp->if_vnet); ndi = ND_IFINFO(ifp); send_ns = 0; dst = &ln->r_l3addr.addr6; pdst = dst; + if ((ND_IFINFO(ifp)->flags & ND6_IFF_ACCEPT_RTADV) != 0) + dr = defrouter_lookup_locked(&ln->r_l3addr.addr6, ifp); + else + dr = NULL; + ND6_RUNLOCK(); + if (ln->ln_ntick > 0) { if (ln->ln_ntick > INT_MAX) { ln->ln_ntick -= INT_MAX; @@ -768,12 +779,13 @@ nd6_llinfo_timer(void *arg) goto done; } + if (ln->la_flags & LLE_STATIC) { goto done; } if (ln->la_flags & LLE_DELETED) { - nd6_free(ln, 0); + nd6_free(ln, dr, 0); ln = NULL; goto done; } @@ -800,7 +812,7 @@ nd6_llinfo_timer(void *arg) clear_llinfo_pqueue(ln); } EVENTHANDLER_INVOKE(lle_event, ln, LLENTRY_TIMEDOUT); - nd6_free(ln, 0); + nd6_free(ln, dr, 0); ln = NULL; if (m != NULL) icmp6_error2(m, ICMP6_DST_UNREACH, @@ -833,7 +845,7 @@ nd6_llinfo_timer(void *arg) if (!ND6_LLINFO_PERMANENT(ln)) { EVENTHANDLER_INVOKE(lle_event, ln, LLENTRY_EXPIRED); - nd6_free(ln, 1); + nd6_free(ln, dr, 1); ln = NULL; } break; @@ -858,7 +870,7 @@ nd6_llinfo_timer(void *arg) send_ns = 1; } else { EVENTHANDLER_INVOKE(lle_event, ln, LLENTRY_EXPIRED); - nd6_free(ln, 0); + nd6_free(ln, dr, 0); ln = NULL; } break; @@ -877,6 +889,8 @@ done: if (ln != NULL) LLE_FREE_LOCKED(ln); + if (dr != NULL) + defrouter_rele(dr); CURVNET_RESTORE(); } @@ -1262,8 +1276,7 @@ nd6_is_new_addr_neighbor(const struct sockaddr_in6 *addr, struct ifnet *ifp) fibnum = RT_DEFAULT_FIB; /* - * If the address matches one of our addresses, - * it should be a neighbor. + * If the address matches one of our addresses, it should be a neighbor. * If the address matches one of our on-link prefixes, it should be a * neighbor. */ @@ -1363,9 +1376,8 @@ nd6_is_addr_neighbor(const struct sockaddr_in6 *addr, struct ifnet *ifp) * Set noinline to be dtrace-friendly */ static __noinline void -nd6_free(struct llentry *ln, int gc) +nd6_free(struct llentry *ln, struct nd_defrouter *dr, int gc) { - struct nd_defrouter *dr; struct ifnet *ifp; LLE_WLOCK_ASSERT(ln); @@ -1378,11 +1390,8 @@ nd6_free(struct llentry *ln, int gc) /* cancel timer */ nd6_llinfo_settimer_locked(ln, -1); - dr = NULL; - ifp = ln->lle_tbl->llt_ifp; + ifp = lltable_get_ifp(ln->lle_tbl); if (ND_IFINFO(ifp)->flags & ND6_IFF_ACCEPT_RTADV) { - dr = defrouter_lookup(&ln->r_l3addr.addr6, ifp); - if (dr != NULL && dr->expire && ln->ln_state == ND6_LLINFO_STALE && gc) { /* @@ -1406,7 +1415,6 @@ nd6_free(struct llentry *ln, int gc) LLE_REMREF(ln); LLE_WUNLOCK(ln); - defrouter_rele(dr); return; } @@ -1487,8 +1495,6 @@ nd6_free(struct llentry *ln, int gc) IF_AFDATA_UNLOCK(ifp); llentry_free(ln); - if (dr != NULL) - defrouter_rele(dr); } static int @@ -2108,7 +2114,7 @@ nd6_grab_holdchain(struct llentry *ln, struct mbuf **chain, * The first time we send a packet to a * neighbor whose entry is STALE, we have * to change the state to DELAY and a sets - * a timer to expire in DELAY_FIRST_PROBE_TIME + * a timer to expire in DELAY_FIRST_PROBE_TIME (V_nd6_delay) * seconds to ensure do neighbor unreachability * detection on expiration. * (RFC 2461 7.3.3) @@ -2310,7 +2316,7 @@ nd6_resolve_slow(struct ifnet *ifp, int flags, struct mbuf *m, /* * The first time we send a packet to a neighbor whose entry is * STALE, we have to change the state to DELAY and a sets a timer to - * expire in DELAY_FIRST_PROBE_TIME seconds to ensure do + * expire in DELAY_FIRST_PROBE_TIME (V_nd6_delay) seconds to ensure do * neighbor unreachability detection on expiration. * (RFC 2461 7.3.3) */ diff --git a/sys/netinet6/nd6.h b/sys/netinet6/nd6.h index 4c83467..149e6f5 100644 --- a/sys/netinet6/nd6.h +++ b/sys/netinet6/nd6.h @@ -459,7 +459,7 @@ void defrouter_reset(void); void defrouter_select(void); void defrouter_ref(struct nd_defrouter *); void defrouter_rele(struct nd_defrouter *); -void defrouter_remove(struct nd_defrouter *); +bool defrouter_remove(struct in6_addr *, struct ifnet *); void defrouter_unlink(struct nd_defrouter *, struct nd_drhead *); void defrouter_del(struct nd_defrouter *); void prelist_remove(struct nd_prefix *); diff --git a/sys/netinet6/nd6_nbr.c b/sys/netinet6/nd6_nbr.c index d528575..89ef6a9 100644 --- a/sys/netinet6/nd6_nbr.c +++ b/sys/netinet6/nd6_nbr.c @@ -857,30 +857,26 @@ nd6_na_input(struct mbuf *m, int off, int icmp6len) * Remove the sender from the Default Router List and * update the Destination Cache entries. */ - struct nd_defrouter *dr; struct ifnet *nd6_ifp; + LLE_ADDREF(ln); + LLE_WUNLOCK(ln); nd6_ifp = lltable_get_ifp(ln->lle_tbl); - ND6_WLOCK(); - dr = defrouter_lookup_locked(&ln->r_l3addr.addr6, - nd6_ifp); - if (dr != NULL) { - /* releases the ND lock */ - defrouter_remove(dr); - dr = NULL; - } else { - ND6_WUNLOCK(); - if ((ND_IFINFO(nd6_ifp)->flags & ND6_IFF_ACCEPT_RTADV) != 0) { - /* - * Even if the neighbor is not in the default - * router list, the neighbor may be used - * as a next hop for some destinations - * (e.g. redirect case). So we must - * call rt6_flush explicitly. - */ - rt6_flush(&ip6->ip6_src, ifp); - } - } + + if (!defrouter_remove(&ln->r_l3addr.addr6, nd6_ifp) && + (ND_IFINFO(nd6_ifp)->flags & + ND6_IFF_ACCEPT_RTADV) != 0) + /* + * Even if the neighbor is not in the default + * router list, the neighbor may be used as a + * next hop for some destinations (e.g. redirect + * case). So we must call rt6_flush() + * explicitly. + */ + rt6_flush(&ip6->ip6_src, ifp); + + LLE_WLOCK(ln); + LLE_REMREF(ln); } ln->ln_router = is_router; } diff --git a/sys/netinet6/nd6_rtr.c b/sys/netinet6/nd6_rtr.c index 294c90a..1752b65 100644 --- a/sys/netinet6/nd6_rtr.c +++ b/sys/netinet6/nd6_rtr.c @@ -627,22 +627,26 @@ defrouter_reset(void) } /* - * Remove a router from the global list and free it. - * - * The ND lock must be held and is released before returning. The caller must - * hold a reference on the router object. + * Look up a matching default router list entry and remove it. Returns true if a + * matching entry was found, false otherwise. */ -void -defrouter_remove(struct nd_defrouter *dr) +bool +defrouter_remove(struct in6_addr *addr, struct ifnet *ifp) { + struct nd_defrouter *dr; - ND6_WLOCK_ASSERT(); - KASSERT(dr->refcnt >= 2, ("unexpected refcount 0x%x", dr->refcnt)); + ND6_WLOCK(); + dr = defrouter_lookup_locked(addr, ifp); + if (dr == NULL) { + ND6_WUNLOCK(); + return (false); + } defrouter_unlink(dr, NULL); ND6_WUNLOCK(); defrouter_del(dr); defrouter_rele(dr); + return (true); } /* @@ -850,13 +854,14 @@ defrtrlist_update(struct nd_defrouter *new) struct nd_defrouter *dr, *n; int oldpref; + if (new->rtlifetime == 0) { + defrouter_remove(&new->rtaddr, new->ifp); + return (NULL); + } + ND6_WLOCK(); - if ((dr = defrouter_lookup_locked(&new->rtaddr, new->ifp)) != NULL) { - if (new->rtlifetime == 0) { - /* releases the ND lock */ - defrouter_remove(dr); - return (NULL); - } + dr = defrouter_lookup_locked(&new->rtaddr, new->ifp); + if (dr != NULL) { oldpref = rtpref(dr); @@ -881,25 +886,17 @@ defrtrlist_update(struct nd_defrouter *new) */ TAILQ_REMOVE(&V_nd_defrouter, dr, dr_entry); n = dr; - goto insert; - } - - /* entry does not exist */ - if (new->rtlifetime == 0) { - ND6_WUNLOCK(); - return (NULL); - } - - n = malloc(sizeof(*n), M_IP6NDP, M_NOWAIT | M_ZERO); - if (n == NULL) { - ND6_WUNLOCK(); - return (NULL); + } else { + n = malloc(sizeof(*n), M_IP6NDP, M_NOWAIT | M_ZERO); + if (n == NULL) { + ND6_WUNLOCK(); + return (NULL); + } + memcpy(n, new, sizeof(*n)); + /* Initialize with an extra reference for the caller. */ + refcount_init(&n->refcnt, 2); } - memcpy(n, new, sizeof(*n)); - /* Initialize with an extra reference for the caller. */ - refcount_init(&n->refcnt, 2); -insert: /* * Insert the new router in the Default Router List; * The Default Router List should be in the descending order