diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c index 4371a02..4bdd049 100644 --- a/sys/netinet6/nd6.c +++ b/sys/netinet6/nd6.c @@ -113,13 +113,9 @@ VNET_DEFINE(int, nd6_debug) = 0; static eventhandler_tag lle_event_eh, iflladdr_event_eh; -/* for debugging? */ -#if 0 -static int nd6_inuse, nd6_allocated; -#endif - VNET_DEFINE(struct nd_drhead, nd_defrouter); VNET_DEFINE(struct nd_prhead, nd_prefix); +VNET_DEFINE(struct mtx, nd_lock); VNET_DEFINE(int, nd6_recalc_reachtm_interval) = ND6_RECALC_REACHTM_INTERVAL; #define V_nd6_recalc_reachtm_interval VNET(nd6_recalc_reachtm_interval) @@ -210,6 +206,8 @@ void nd6_init(void) { + mtx_init(&V_nd_lock, "nd6", NULL, MTX_DEF); + LIST_INIT(&V_nd_prefix); /* initialization of the default router list */ @@ -248,7 +246,7 @@ nd6_ifattach(struct ifnet *ifp) { struct nd_ifinfo *nd; - nd = (struct nd_ifinfo *)malloc(sizeof(*nd), M_IP6NDP, M_WAITOK|M_ZERO); + nd = malloc(sizeof(*nd), M_IP6NDP, M_WAITOK | M_ZERO); nd->initialized = 1; nd->chlim = IPV6_DEFHLIM; @@ -889,6 +887,7 @@ void nd6_timer(void *arg) { CURVNET_SET((struct vnet *) arg); + struct nd_drhead drq; struct nd_defrouter *dr, *ndr; struct nd_prefix *pr, *npr; struct in6_ifaddr *ia6, *nia6; @@ -896,10 +895,18 @@ nd6_timer(void *arg) callout_reset(&V_nd6_timer_ch, V_nd6_prune * hz, nd6_timer, curvnet); + TAILQ_INIT(&drq); + /* expire default router list */ - TAILQ_FOREACH_SAFE(dr, &V_nd_defrouter, dr_entry, ndr) { + ND_LOCK(); + TAILQ_FOREACH_SAFE(dr, &V_nd_defrouter, dr_entry, ndr) if (dr->expire && dr->expire < time_uptime) - defrtrlist_del(dr); + defrouter_unlink(dr, &drq); + ND_UNLOCK(); + + while ((dr = TAILQ_FIRST(&drq)) != NULL) { + TAILQ_REMOVE(&drq, dr, dr_entry); + defrouter_del(dr); } /* @@ -1094,29 +1101,37 @@ regen_tmpaddr(struct in6_ifaddr *ia6) void nd6_purge(struct ifnet *ifp) { + struct nd_drhead drq; struct nd_defrouter *dr, *ndr; struct nd_prefix *pr, *npr; + TAILQ_INIT(&drq); + /* * Nuke default router list entries toward ifp. * We defer removal of default router list entries that is installed * in the routing table, in order to keep additional side effects as * small as possible. */ + ND_LOCK(); TAILQ_FOREACH_SAFE(dr, &V_nd_defrouter, dr_entry, ndr) { if (dr->installed) continue; - if (dr->ifp == ifp) - defrtrlist_del(dr); + defrouter_unlink(dr, &drq); } TAILQ_FOREACH_SAFE(dr, &V_nd_defrouter, dr_entry, ndr) { if (!dr->installed) continue; - if (dr->ifp == ifp) - defrtrlist_del(dr); + defrouter_unlink(dr, &drq); + } + ND_UNLOCK(); + + while ((dr = TAILQ_FIRST(&drq)) != NULL) { + TAILQ_REMOVE(&drq, dr, dr_entry); + defrouter_del(dr); } /* Nuke prefix list entries toward ifp */ @@ -1362,8 +1377,8 @@ nd6_free(struct llentry *ln, int gc) /* cancel timer */ nd6_llinfo_settimer_locked(ln, -1); + dr = NULL; ifp = ln->lle_tbl->llt_ifp; - if (ND_IFINFO(ifp)->flags & ND6_IFF_ACCEPT_RTADV) { dr = defrouter_lookup(&ln->r_l3addr.addr6, ifp); @@ -1390,6 +1405,7 @@ nd6_free(struct llentry *ln, int gc) LLE_REMREF(ln); LLE_WUNLOCK(ln); + defrouter_rele(dr); return; } @@ -1470,6 +1486,8 @@ nd6_free(struct llentry *ln, int gc) IF_AFDATA_UNLOCK(ifp); llentry_free(ln); + if (dr != NULL) + defrouter_rele(dr); } static int @@ -1530,12 +1548,13 @@ nd6_rtrequest(int req, struct rtentry *rt, struct rt_addrinfo *info) /* * check for default route */ - if (IN6_ARE_ADDR_EQUAL(&in6addr_any, - &SIN6(rt_key(rt))->sin6_addr)) { - + if (IN6_ARE_ADDR_EQUAL(&in6addr_any, + &SIN6(rt_key(rt))->sin6_addr)) { dr = defrouter_lookup(&gateway->sin6_addr, ifp); - if (dr != NULL) + if (dr != NULL) { dr->installed = 0; + defrouter_rele(dr); + } } break; } @@ -1723,12 +1742,22 @@ nd6_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp) case SIOCSRTRFLUSH_IN6: { /* flush all the default routers */ - struct nd_defrouter *dr, *next; + struct nd_drhead drq; + struct nd_defrouter *dr; + + TAILQ_INIT(&drq); defrouter_reset(); - TAILQ_FOREACH_SAFE(dr, &V_nd_defrouter, dr_entry, next) { - defrtrlist_del(dr); + + ND_LOCK(); + while ((dr = TAILQ_FIRST(&V_nd_defrouter)) != NULL) + defrouter_unlink(dr, &drq); + ND_UNLOCK(); + while ((dr = TAILQ_FIRST(&drq)) != NULL) { + TAILQ_REMOVE(&drq, dr, dr_entry); + defrouter_del(dr); } + defrouter_select(); break; } @@ -2517,7 +2546,6 @@ clear_llinfo_pqueue(struct llentry *ln) } ln->la_hold = NULL; - return; } static int nd6_sysctl_drlist(SYSCTL_HANDLER_ARGS); @@ -2541,30 +2569,33 @@ nd6_sysctl_drlist(SYSCTL_HANDLER_ARGS) struct nd_defrouter *dr; int error; - if (req->newptr) + if (req->newptr != NULL) return (EPERM); + error = sysctl_wire_old_buffer(req, 0); + if (error != 0) + return (error); + bzero(&d, sizeof(d)); d.rtaddr.sin6_family = AF_INET6; d.rtaddr.sin6_len = sizeof(d.rtaddr); - /* - * XXX locking - */ + ND_LOCK(); TAILQ_FOREACH(dr, &V_nd_defrouter, dr_entry) { d.rtaddr.sin6_addr = dr->rtaddr; error = sa6_recoverscope(&d.rtaddr); if (error != 0) - return (error); - d.flags = dr->flags; + break; + d.flags = dr->raflags; d.rtlifetime = dr->rtlifetime; d.expire = dr->expire + (time_second - time_uptime); d.if_index = dr->ifp->if_index; error = SYSCTL_OUT(req, &d, sizeof(d)); if (error != 0) - return (error); + break; } - return (0); + ND_UNLOCK(); + return (error); } static int diff --git a/sys/netinet6/nd6.h b/sys/netinet6/nd6.h index 6ff4cac..2111eed 100644 --- a/sys/netinet6/nd6.h +++ b/sys/netinet6/nd6.h @@ -232,14 +232,15 @@ struct in6_ndifreq { ((MAX_RANDOM_FACTOR - MIN_RANDOM_FACTOR) * (x >> 10)))) /1000) TAILQ_HEAD(nd_drhead, nd_defrouter); -struct nd_defrouter { +struct nd_defrouter { TAILQ_ENTRY(nd_defrouter) dr_entry; struct in6_addr rtaddr; - u_char flags; /* flags on RA message */ + u_char raflags; /* flags on RA message */ u_short rtlifetime; u_long expire; struct ifnet *ifp; int installed; /* is installed into kernel routing table */ + int refcnt; }; struct nd_prefixctl { @@ -339,6 +340,15 @@ VNET_DECLARE(int, nd6_onlink_ns_rfc4861); #define V_nd6_debug VNET(nd6_debug) #define V_nd6_onlink_ns_rfc4861 VNET(nd6_onlink_ns_rfc4861) +/* Lock for the prefix and default router lists. */ +VNET_DECLARE(struct mtx, nd_lock); +#define V_nd_lock VNET(nd_lock) + +#define ND_LOCK() mtx_lock(&V_nd_lock) +#define ND_UNLOCK() mtx_unlock(&V_nd_lock) +#define ND_LOCK_ASSERT() mtx_assert(&V_nd_lock, MA_OWNED) +#define ND_UNLOCK_ASSERT() mtx_assert(&V_nd_lock, MA_NOTOWNED) + #define nd6log(x) do { if (V_nd6_debug) log x; } while (/*CONSTCOND*/ 0) VNET_DECLARE(struct callout, nd6_timer_ch); @@ -444,12 +454,17 @@ void nd6_ra_input(struct mbuf *, int, int); void prelist_del(struct nd_prefix *); void defrouter_reset(void); void defrouter_select(void); -void defrtrlist_del(struct nd_defrouter *); +void defrouter_ref(struct nd_defrouter *); +void defrouter_rele(struct nd_defrouter *); +void defrouter_remove(struct nd_defrouter *); +void defrouter_unlink(struct nd_defrouter *, struct nd_drhead *); +void defrouter_del(struct nd_defrouter *); void prelist_remove(struct nd_prefix *); int nd6_prelist_add(struct nd_prefixctl *, struct nd_defrouter *, struct nd_prefix **); void pfxlist_onlink_check(void); struct nd_defrouter *defrouter_lookup(struct in6_addr *, struct ifnet *); +struct nd_defrouter *defrouter_lookup_locked(struct in6_addr *, struct ifnet *); struct nd_prefix *nd6_prefix_lookup(struct nd_prefixctl *); void rt6_flush(struct in6_addr *, struct ifnet *); int nd6_setdefaultiface(int); diff --git a/sys/netinet6/nd6_nbr.c b/sys/netinet6/nd6_nbr.c index 3a4e8a0..797cb8d 100644 --- a/sys/netinet6/nd6_nbr.c +++ b/sys/netinet6/nd6_nbr.c @@ -584,7 +584,6 @@ nd6_ns_output_fib(struct ifnet *ifp, const struct in6_addr *saddr6, bad: m_freem(m); - return; } #ifndef BURN_BRIDGES @@ -859,31 +858,28 @@ nd6_na_input(struct mbuf *m, int off, int icmp6len) * update the Destination Cache entries. */ struct nd_defrouter *dr; - struct in6_addr *in6; struct ifnet *nd6_ifp; - in6 = &ln->r_l3addr.addr6; - - /* - * Lock to protect the default router list. - * XXX: this might be unnecessary, since this function - * is only called under the network software interrupt - * context. However, we keep it just for safety. - */ nd6_ifp = lltable_get_ifp(ln->lle_tbl); - dr = defrouter_lookup(in6, nd6_ifp); - if (dr) - defrtrlist_del(dr); - else if (ND_IFINFO(nd6_ifp)->flags & - ND6_IFF_ACCEPT_RTADV) { - /* - * 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); + ND_LOCK(); + dr = defrouter_lookup_locked(&ln->r_l3addr.addr6, + nd6_ifp); + if (dr != NULL) { + /* releases the ND lock */ + defrouter_remove(dr); + dr = NULL; + } else { + ND_UNLOCK(); + 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); + } } } ln->ln_router = is_router; @@ -1078,7 +1074,6 @@ nd6_na_output_fib(struct ifnet *ifp, const struct in6_addr *daddr6_0, bad: m_freem(m); - return; } #ifndef BURN_BRIDGES @@ -1273,6 +1268,7 @@ nd6_dad_start(struct ifaddr *ifa, int delay) * DAD already in progress. Let the existing entry * to finish it. */ + nd6_dad_rele(dp); return; } diff --git a/sys/netinet6/nd6_rtr.c b/sys/netinet6/nd6_rtr.c index ca64908..8680103 100644 --- a/sys/netinet6/nd6_rtr.c +++ b/sys/netinet6/nd6_rtr.c @@ -39,6 +39,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -220,6 +221,8 @@ nd6_ra_input(struct mbuf *m, int off, int icmp6len) struct nd_defrouter *dr; char ip6bufs[INET6_ADDRSTRLEN], ip6bufd[INET6_ADDRSTRLEN]; + dr = NULL; + /* * We only accept RAs only when the per-interface flag * ND6_IFF_ACCEPT_RTADV is on the receiving interface. @@ -272,7 +275,7 @@ nd6_ra_input(struct mbuf *m, int off, int icmp6len) bzero(&dr0, sizeof(dr0)); dr0.rtaddr = saddr6; - dr0.flags = nd_ra->nd_ra_flags_reserved; + dr0.raflags = nd_ra->nd_ra_flags_reserved; /* * Effectively-disable routes from RA messages when * ND6_IFF_NO_RADR enabled on the receiving interface or @@ -369,6 +372,10 @@ nd6_ra_input(struct mbuf *m, int off, int icmp6len) (void)prelist_update(&pr, dr, m, mcast); } } + if (dr != NULL) { + defrouter_rele(dr); + dr = NULL; + } /* * MTU @@ -446,10 +453,6 @@ nd6_ra_input(struct mbuf *m, int off, int icmp6len) m_freem(m); } -/* - * default router list proccessing sub routines - */ - /* tell the change to user processes watching the routing socket. */ static void nd6_rtmsg(int cmd, struct rtentry *rt) @@ -478,6 +481,10 @@ nd6_rtmsg(int cmd, struct rtentry *rt) ifa_free(ifa); } +/* + * default router list proccessing sub routines + */ + static void defrouter_addreq(struct nd_defrouter *new) { @@ -503,20 +510,46 @@ defrouter_addreq(struct nd_defrouter *new) } if (error == 0) new->installed = 1; - return; } struct nd_defrouter * -defrouter_lookup(struct in6_addr *addr, struct ifnet *ifp) +defrouter_lookup_locked(struct in6_addr *addr, struct ifnet *ifp) { struct nd_defrouter *dr; - TAILQ_FOREACH(dr, &V_nd_defrouter, dr_entry) { - if (dr->ifp == ifp && IN6_ARE_ADDR_EQUAL(addr, &dr->rtaddr)) + ND_LOCK_ASSERT(); + TAILQ_FOREACH(dr, &V_nd_defrouter, dr_entry) + if (dr->ifp == ifp && IN6_ARE_ADDR_EQUAL(addr, &dr->rtaddr)) { + defrouter_ref(dr); return (dr); - } + } + return (NULL); +} + +struct nd_defrouter * +defrouter_lookup(struct in6_addr *addr, struct ifnet *ifp) +{ + struct nd_defrouter *dr; - return (NULL); /* search failed */ + ND_LOCK(); + dr = defrouter_lookup_locked(addr, ifp); + ND_UNLOCK(); + return (dr); +} + +void +defrouter_ref(struct nd_defrouter *dr) +{ + + refcount_acquire(&dr->refcnt); +} + +void +defrouter_rele(struct nd_defrouter *dr) +{ + + if (refcount_release(&dr->refcnt)) + free(dr, M_IP6NDP); } /* @@ -551,15 +584,41 @@ defrouter_delreq(struct nd_defrouter *dr) } /* - * remove all default routes from default router list + * Remove all default routes from default router list. */ void defrouter_reset(void) { - struct nd_defrouter *dr; + struct nd_defrouter *dr, **dra; + int count, i; + count = i = 0; + + /* + * We can't delete routes with the ND lock held, so make a copy of the + * current default router list and use that when deleting routes. + */ + ND_LOCK(); TAILQ_FOREACH(dr, &V_nd_defrouter, dr_entry) - defrouter_delreq(dr); + count++; + ND_UNLOCK(); + + dra = malloc(count * sizeof(*dra), M_TEMP, M_WAITOK | M_ZERO); + + ND_LOCK(); + TAILQ_FOREACH(dr, &V_nd_defrouter, dr_entry) { + if (i == count) + break; + defrouter_ref(dr); + dra[i++] = dr; + } + ND_UNLOCK(); + + for (i = 0; i < count && dra[i] != NULL; i++) { + defrouter_delreq(dra[i]); + defrouter_rele(dra[i]); + } + free(dra, M_TEMP); /* * XXX should we also nuke any default routers in the kernel, by @@ -567,12 +626,49 @@ 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. + */ +void +defrouter_remove(struct nd_defrouter *dr) +{ + + ND_LOCK_ASSERT(); + MPASS(dr->refcnt >= 2); + + defrouter_unlink(dr, NULL); + ND_UNLOCK(); + defrouter_del(dr); + defrouter_rele(dr); +} + +/* + * Remove a router from the global list and optionally stash it in a + * caller-supplied queue. + * + * The ND lock must be held. + */ void -defrtrlist_del(struct nd_defrouter *dr) +defrouter_unlink(struct nd_defrouter *dr, struct nd_drhead *drq) +{ + + ND_LOCK_ASSERT(); + TAILQ_REMOVE(&V_nd_defrouter, dr, dr_entry); + if (drq != NULL) + TAILQ_INSERT_TAIL(drq, dr, dr_entry); +} + +void +defrouter_del(struct nd_defrouter *dr) { struct nd_defrouter *deldr = NULL; struct nd_prefix *pr; + ND_UNLOCK_ASSERT(); + /* * Flush all the routing table entries that use the router * as a next hop. @@ -584,7 +680,6 @@ defrtrlist_del(struct nd_defrouter *dr) deldr = dr; defrouter_delreq(dr); } - TAILQ_REMOVE(&V_nd_defrouter, dr, dr_entry); /* * Also delete all the pointers to the router in each prefix lists. @@ -604,7 +699,10 @@ defrtrlist_del(struct nd_defrouter *dr) if (deldr) defrouter_select(); - free(dr, M_IP6NDP); + /* + * Release the list reference. + */ + defrouter_rele(dr); } /* @@ -631,27 +729,32 @@ defrtrlist_del(struct nd_defrouter *dr) void defrouter_select(void) { - struct nd_defrouter *dr, *selected_dr = NULL, *installed_dr = NULL; + struct nd_defrouter *dr, *selected_dr, *installed_dr; struct llentry *ln = NULL; + ND_LOCK(); /* * Let's handle easy case (3) first: * If default router list is empty, there's nothing to be done. */ - if (TAILQ_EMPTY(&V_nd_defrouter)) + if (TAILQ_EMPTY(&V_nd_defrouter)) { + ND_UNLOCK(); return; + } /* * Search for a (probably) reachable router from the list. * We just pick up the first reachable one (if any), assuming that * the ordering rule of the list described in defrtrlist_update(). */ + selected_dr = installed_dr = NULL; TAILQ_FOREACH(dr, &V_nd_defrouter, dr_entry) { IF_AFDATA_RLOCK(dr->ifp); if (selected_dr == NULL && (ln = nd6_lookup(&dr->rtaddr, 0, dr->ifp)) && ND6_IS_LLINFO_PROBREACH(ln)) { selected_dr = dr; + defrouter_ref(selected_dr); } IF_AFDATA_RUNLOCK(dr->ifp); if (ln != NULL) { @@ -659,12 +762,15 @@ defrouter_select(void) ln = NULL; } - if (dr->installed && installed_dr == NULL) - installed_dr = dr; - else if (dr->installed && installed_dr) { - /* this should not happen. warn for diagnosis. */ - log(LOG_ERR, "defrouter_select: more than one router" - " is installed\n"); + if (dr->installed) { + if (installed_dr == NULL) { + installed_dr = dr; + defrouter_ref(installed_dr); + } else { + /* this should not happen. warn for diagnosis. */ + log(LOG_ERR, + "defrouter_select: more than one router is installed\n"); + } } } /* @@ -676,21 +782,27 @@ defrouter_select(void) * or when the new one has a really higher preference value. */ if (selected_dr == NULL) { - if (installed_dr == NULL || !TAILQ_NEXT(installed_dr, dr_entry)) + if (installed_dr == NULL || + TAILQ_NEXT(installed_dr, dr_entry) == NULL) selected_dr = TAILQ_FIRST(&V_nd_defrouter); else selected_dr = TAILQ_NEXT(installed_dr, dr_entry); - } else if (installed_dr) { + defrouter_ref(selected_dr); + } else if (installed_dr != NULL) { + ND_UNLOCK(); IF_AFDATA_RLOCK(installed_dr->ifp); if ((ln = nd6_lookup(&installed_dr->rtaddr, 0, installed_dr->ifp)) && ND6_IS_LLINFO_PROBREACH(ln) && rtpref(selected_dr) <= rtpref(installed_dr)) { + defrouter_rele(selected_dr); selected_dr = installed_dr; } IF_AFDATA_RUNLOCK(installed_dr->ifp); if (ln != NULL) LLE_RUNLOCK(ln); + ND_LOCK(); } + ND_UNLOCK(); /* * If the selected router is different than the installed one, @@ -701,9 +813,10 @@ defrouter_select(void) if (installed_dr) defrouter_delreq(installed_dr); defrouter_addreq(selected_dr); + defrouter_rele(selected_dr); } - - return; + if (installed_dr != NULL) + defrouter_rele(installed_dr); } /* @@ -713,7 +826,7 @@ defrouter_select(void) static int rtpref(struct nd_defrouter *dr) { - switch (dr->flags & ND_RA_FLAG_RTPREF_MASK) { + switch (dr->raflags & ND_RA_FLAG_RTPREF_MASK) { case ND_RA_FLAG_RTPREF_HIGH: return (RTPREF_HIGH); case ND_RA_FLAG_RTPREF_MEDIUM: @@ -727,7 +840,7 @@ rtpref(struct nd_defrouter *dr) * serious bug of kernel internal. We thus always bark here. * Or, can we even panic? */ - log(LOG_ERR, "rtpref: impossible RA flag %x\n", dr->flags); + log(LOG_ERR, "rtpref: impossible RA flag %x\n", dr->raflags); return (RTPREF_INVALID); } /* NOTREACHED */ @@ -737,53 +850,56 @@ static struct nd_defrouter * defrtrlist_update(struct nd_defrouter *new) { struct nd_defrouter *dr, *n; + int oldpref; - if ((dr = defrouter_lookup(&new->rtaddr, new->ifp)) != NULL) { - /* entry exists */ + ND_LOCK(); + if ((dr = defrouter_lookup_locked(&new->rtaddr, new->ifp)) != NULL) { if (new->rtlifetime == 0) { - defrtrlist_del(dr); - dr = NULL; - } else { - int oldpref = rtpref(dr); + /* releases the ND lock */ + defrouter_remove(dr); + return (NULL); + } - /* override */ - dr->flags = new->flags; /* xxx flag check */ - dr->rtlifetime = new->rtlifetime; - dr->expire = new->expire; + oldpref = rtpref(dr); - /* - * If the preference does not change, there's no need - * to sort the entries. Also make sure the selected - * router is still installed in the kernel. - */ - if (dr->installed && rtpref(new) == oldpref) - return (dr); + /* override */ + dr->raflags = new->raflags; /* XXX flag check */ + dr->rtlifetime = new->rtlifetime; + dr->expire = new->expire; - /* - * preferred router may be changed, so relocate - * this router. - * XXX: calling TAILQ_REMOVE directly is a bad manner. - * However, since defrtrlist_del() has many side - * effects, we intentionally do so here. - * defrouter_select() below will handle routing - * changes later. - */ - TAILQ_REMOVE(&V_nd_defrouter, dr, dr_entry); - n = dr; - goto insert; + /* + * If the preference does not change, there's no need + * to sort the entries. Also make sure the selected + * router is still installed in the kernel. + */ + if (dr->installed && rtpref(new) == oldpref) { + ND_UNLOCK(); + return (dr); } - return (dr); + + /* + * The preferred router may have changed, so relocate this + * router. + */ + TAILQ_REMOVE(&V_nd_defrouter, dr, dr_entry); + n = dr; + goto insert; } /* entry does not exist */ - if (new->rtlifetime == 0) + if (new->rtlifetime == 0) { + ND_UNLOCK(); return (NULL); + } - n = (struct nd_defrouter *)malloc(sizeof(*n), M_IP6NDP, M_NOWAIT); - if (n == NULL) + n = malloc(sizeof(*n), M_IP6NDP, M_NOWAIT | M_ZERO); + if (n == NULL) { + ND_UNLOCK(); return (NULL); - bzero(n, sizeof(*n)); - *n = *new; + } + memcpy(n, new, sizeof(*n)); + /* Initialize with an extra reference for the caller. */ + refcount_init(&n->refcnt, 2); insert: /* @@ -798,10 +914,11 @@ insert: if (rtpref(n) > rtpref(dr)) break; } - if (dr) + if (dr != NULL) TAILQ_INSERT_BEFORE(dr, n, dr_entry); else TAILQ_INSERT_TAIL(&V_nd_defrouter, n, dr_entry); + ND_UNLOCK(); defrouter_select(); @@ -826,11 +943,11 @@ pfxrtr_add(struct nd_prefix *pr, struct nd_defrouter *dr) { struct nd_pfxrouter *new; - new = (struct nd_pfxrouter *)malloc(sizeof(*new), M_IP6NDP, M_NOWAIT); + new = malloc(sizeof(*new), M_IP6NDP, M_NOWAIT | M_ZERO); if (new == NULL) return; - bzero(new, sizeof(*new)); new->router = dr; + defrouter_ref(dr); LIST_INSERT_HEAD(&pr->ndpr_advrtrs, new, pfr_entry); @@ -840,7 +957,9 @@ pfxrtr_add(struct nd_prefix *pr, struct nd_defrouter *dr) static void pfxrtr_del(struct nd_pfxrouter *pfr) { + LIST_REMOVE(pfr, pfr_entry); + defrouter_rele(pfr->router); free(pfr, M_IP6NDP); } @@ -869,10 +988,9 @@ nd6_prelist_add(struct nd_prefixctl *pr, struct nd_defrouter *dr, int error = 0; char ip6buf[INET6_ADDRSTRLEN]; - new = (struct nd_prefix *)malloc(sizeof(*new), M_IP6NDP, M_NOWAIT); + new = malloc(sizeof(*new), M_IP6NDP, M_NOWAIT | M_ZERO); if (new == NULL) return(ENOMEM); - bzero(new, sizeof(*new)); new->ndpr_ifp = pr->ndpr_ifp; new->ndpr_prefix = pr->ndpr_prefix; new->ndpr_plen = pr->ndpr_plen; diff --git a/sys/netinet6/scope6.c b/sys/netinet6/scope6.c index 1a9d038..a00842e 100644 --- a/sys/netinet6/scope6.c +++ b/sys/netinet6/scope6.c @@ -371,7 +371,7 @@ sa6_recoverscope(struct sockaddr_in6 *sin6) zoneid != sin6->sin6_scope_id) { log(LOG_NOTICE, "%s: embedded scope mismatch: %s%%%d. " - "sin6_scope_id was overridden.", __func__, + "sin6_scope_id was overridden\n", __func__, ip6_sprintf(ip6buf, &sin6->sin6_addr), sin6->sin6_scope_id); }