diff options
Diffstat (limited to 'freebsd/sys/netinet6/nd6_rtr.c')
-rw-r--r-- | freebsd/sys/netinet6/nd6_rtr.c | 493 |
1 files changed, 266 insertions, 227 deletions
diff --git a/freebsd/sys/netinet6/nd6_rtr.c b/freebsd/sys/netinet6/nd6_rtr.c index 8d150ae4..c8d7c0ef 100644 --- a/freebsd/sys/netinet6/nd6_rtr.c +++ b/freebsd/sys/netinet6/nd6_rtr.c @@ -41,20 +41,24 @@ __FBSDID("$FreeBSD$"); #include <sys/systm.h> #include <sys/malloc.h> #include <sys/mbuf.h> +#include <sys/refcount.h> #include <sys/socket.h> #include <sys/sockio.h> #include <sys/time.h> #include <sys/kernel.h> #include <rtems/bsd/sys/lock.h> #include <rtems/bsd/sys/errno.h> +#include <sys/rmlock.h> #include <sys/rwlock.h> #include <sys/syslog.h> #include <sys/queue.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_types.h> #include <net/if_dl.h> #include <net/route.h> +#include <net/route_var.h> #include <net/radix.h> #include <net/vnet.h> @@ -89,7 +93,7 @@ static void in6_init_address_ltimes(struct nd_prefix *, static int nd6_prefix_onlink(struct nd_prefix *); static int nd6_prefix_offlink(struct nd_prefix *); -static int rt6_deleteroute(struct radix_node *, void *); +static int rt6_deleteroute(const struct rtentry *, void *); VNET_DECLARE(int, nd6_recalc_reachtm_interval); #define V_nd6_recalc_reachtm_interval VNET(nd6_recalc_reachtm_interval) @@ -220,6 +224,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 +278,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 @@ -284,7 +290,7 @@ nd6_ra_input(struct mbuf *m, int off, int icmp6len) dr0.rtlifetime = 0; else dr0.rtlifetime = ntohs(nd_ra->nd_ra_router_lifetime); - dr0.expire = time_second + dr0.rtlifetime; + dr0.expire = time_uptime + dr0.rtlifetime; dr0.ifp = ifp; /* unspecified or not? (RFC 2461 6.3.4) */ if (advreachable) { @@ -369,6 +375,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 +456,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,12 +484,15 @@ nd6_rtmsg(int cmd, struct rtentry *rt) ifa_free(ifa); } +/* + * default router list processing sub routines + */ + static void defrouter_addreq(struct nd_defrouter *new) { struct sockaddr_in6 def, mask, gate; struct rtentry *newrt = NULL; - int s; int error; bzero(&def, sizeof(def)); @@ -495,7 +504,6 @@ defrouter_addreq(struct nd_defrouter *new) def.sin6_family = gate.sin6_family = AF_INET6; gate.sin6_addr = new->rtaddr; - s = splnet(); error = in6_rtrequest(RTM_ADD, (struct sockaddr *)&def, (struct sockaddr *)&gate, (struct sockaddr *)&mask, RTF_GATEWAY, &newrt, RT_DEFAULT_FIB); @@ -505,21 +513,46 @@ defrouter_addreq(struct nd_defrouter *new) } if (error == 0) new->installed = 1; - splx(s); - 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)) + ND6_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; + + ND6_RLOCK(); + dr = defrouter_lookup_locked(addr, ifp); + ND6_RUNLOCK(); + return (dr); +} + +void +defrouter_ref(struct nd_defrouter *dr) +{ + + refcount_acquire(&dr->refcnt); +} + +void +defrouter_rele(struct nd_defrouter *dr) +{ - return (NULL); /* search failed */ + if (refcount_release(&dr->refcnt)) + free(dr, M_IP6NDP); } /* @@ -554,15 +587,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. + */ + ND6_RLOCK(); TAILQ_FOREACH(dr, &V_nd_defrouter, dr_entry) - defrouter_delreq(dr); + count++; + ND6_RUNLOCK(); + + dra = malloc(count * sizeof(*dra), M_TEMP, M_WAITOK | M_ZERO); + + ND6_RLOCK(); + TAILQ_FOREACH(dr, &V_nd_defrouter, dr_entry) { + if (i == count) + break; + defrouter_ref(dr); + dra[i++] = dr; + } + ND6_RUNLOCK(); + + 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 @@ -570,12 +629,53 @@ defrouter_reset(void) */ } +/* + * Look up a matching default router list entry and remove it. Returns true if a + * matching entry was found, false otherwise. + */ +bool +defrouter_remove(struct in6_addr *addr, struct ifnet *ifp) +{ + struct nd_defrouter *dr; + + 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); +} + +/* + * Remove a router from the global list and optionally stash it in a + * caller-supplied queue. + * + * The ND lock must be held. + */ +void +defrouter_unlink(struct nd_defrouter *dr, struct nd_drhead *drq) +{ + + ND6_WLOCK_ASSERT(); + TAILQ_REMOVE(&V_nd_defrouter, dr, dr_entry); + if (drq != NULL) + TAILQ_INSERT_TAIL(drq, dr, dr_entry); +} + void -defrtrlist_del(struct nd_defrouter *dr) +defrouter_del(struct nd_defrouter *dr) { struct nd_defrouter *deldr = NULL; struct nd_prefix *pr; + ND6_UNLOCK_ASSERT(); + /* * Flush all the routing table entries that use the router * as a next hop. @@ -587,7 +687,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. @@ -607,7 +706,10 @@ defrtrlist_del(struct nd_defrouter *dr) if (deldr) defrouter_select(); - free(dr, M_IP6NDP); + /* + * Release the list reference. + */ + defrouter_rele(dr); } /* @@ -634,16 +736,16 @@ defrtrlist_del(struct nd_defrouter *dr) void defrouter_select(void) { - int s = splnet(); - struct nd_defrouter *dr, *selected_dr = NULL, *installed_dr = NULL; + struct nd_defrouter *dr, *selected_dr, *installed_dr; struct llentry *ln = NULL; + ND6_RLOCK(); /* * 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)) { - splx(s); + ND6_RUNLOCK(); return; } @@ -652,12 +754,14 @@ defrouter_select(void) * 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) { @@ -665,12 +769,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"); + } } } /* @@ -682,21 +789,25 @@ 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) { 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); } + ND6_RUNLOCK(); /* * If the selected router is different than the installed one, @@ -704,13 +815,13 @@ defrouter_select(void) * Note that the selected router is never NULL here. */ if (installed_dr != selected_dr) { - if (installed_dr) + if (installed_dr != NULL) { defrouter_delreq(installed_dr); + defrouter_rele(installed_dr); + } defrouter_addreq(selected_dr); } - - splx(s); - return; + defrouter_rele(selected_dr); } /* @@ -720,7 +831,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: @@ -734,7 +845,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 */ @@ -744,63 +855,50 @@ static struct nd_defrouter * defrtrlist_update(struct nd_defrouter *new) { struct nd_defrouter *dr, *n; - int s = splnet(); + int oldpref; - if ((dr = defrouter_lookup(&new->rtaddr, new->ifp)) != NULL) { - /* entry exists */ - if (new->rtlifetime == 0) { - defrtrlist_del(dr); - dr = NULL; - } else { - int oldpref = rtpref(dr); + if (new->rtlifetime == 0) { + defrouter_remove(&new->rtaddr, new->ifp); + return (NULL); + } - /* override */ - dr->flags = new->flags; /* xxx flag check */ - dr->rtlifetime = new->rtlifetime; - dr->expire = new->expire; + ND6_WLOCK(); + dr = defrouter_lookup_locked(&new->rtaddr, new->ifp); + if (dr != NULL) { + 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) { - splx(s); - 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) { + ND6_WUNLOCK(); + return (dr); } - splx(s); - return (dr); - } - - /* entry does not exist */ - if (new->rtlifetime == 0) { - splx(s); - return (NULL); - } - n = (struct nd_defrouter *)malloc(sizeof(*n), M_IP6NDP, M_NOWAIT); - if (n == NULL) { - splx(s); - return (NULL); + /* + * The preferred router may have changed, so relocate this + * router. + */ + TAILQ_REMOVE(&V_nd_defrouter, dr, dr_entry); + n = dr; + } 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); } - bzero(n, sizeof(*n)); - *n = *new; -insert: /* * Insert the new router in the Default Router List; * The Default Router List should be in the descending order @@ -813,15 +911,14 @@ 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); + ND6_WUNLOCK(); defrouter_select(); - splx(s); - return (n); } @@ -843,11 +940,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); @@ -857,7 +954,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); } @@ -884,13 +983,11 @@ nd6_prelist_add(struct nd_prefixctl *pr, struct nd_defrouter *dr, { struct nd_prefix *new = NULL; int error = 0; - int i, s; 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)); + return (ENOMEM); new->ndpr_ifp = pr->ndpr_ifp; new->ndpr_prefix = pr->ndpr_prefix; new->ndpr_plen = pr->ndpr_plen; @@ -899,24 +996,18 @@ nd6_prelist_add(struct nd_prefixctl *pr, struct nd_defrouter *dr, new->ndpr_flags = pr->ndpr_flags; if ((error = in6_init_prefix_ltimes(new)) != 0) { free(new, M_IP6NDP); - return(error); + return (error); } - new->ndpr_lastupdate = time_second; - if (newp != NULL) - *newp = new; + new->ndpr_lastupdate = time_uptime; /* initialization */ LIST_INIT(&new->ndpr_advrtrs); in6_prefixlen2mask(&new->ndpr_mask, new->ndpr_plen); /* make prefix in the canonical form */ - for (i = 0; i < 4; i++) - new->ndpr_prefix.sin6_addr.s6_addr32[i] &= - new->ndpr_mask.s6_addr32[i]; + IN6_MASK_ADDR(&new->ndpr_prefix.sin6_addr, &new->ndpr_mask); - s = splnet(); /* link ndpr_entry to nd_prefix list */ LIST_INSERT_HEAD(&V_nd_prefix, new, ndpr_entry); - splx(s); /* ND_OPT_PI_FLAG_ONLINK processing */ if (new->ndpr_raf_onlink) { @@ -931,17 +1022,18 @@ nd6_prelist_add(struct nd_prefixctl *pr, struct nd_defrouter *dr, } } - if (dr) + if (dr != NULL) pfxrtr_add(new, dr); - - return 0; + if (newp != NULL) + *newp = new; + return (0); } void prelist_remove(struct nd_prefix *pr) { struct nd_pfxrouter *pfr, *next; - int e, s; + int e; char ip6buf[INET6_ADDRSTRLEN]; /* make sure to invalidate the prefix until it is really freed. */ @@ -966,17 +1058,13 @@ prelist_remove(struct nd_prefix *pr) if (pr->ndpr_refcnt > 0) return; /* notice here? */ - s = splnet(); - /* unlink ndpr_entry from nd_prefix list */ LIST_REMOVE(pr, ndpr_entry); - /* free list of routers that adversed the prefix */ + /* free list of routers that advertised the prefix */ LIST_FOREACH_SAFE(pfr, &pr->ndpr_advrtrs, pfr_entry, next) { - free(pfr, M_IP6NDP); + pfxrtr_del(pfr); } - splx(s); - free(pr, M_IP6NDP); pfxlist_onlink_check(); @@ -994,9 +1082,7 @@ prelist_update(struct nd_prefixctl *new, struct nd_defrouter *dr, struct ifaddr *ifa; struct ifnet *ifp = new->ndpr_ifp; struct nd_prefix *pr; - int s = splnet(); int error = 0; - int newprefix = 0; int auth; struct in6_addrlifetime lt6_tmp; char ip6buf[INET6_ADDRSTRLEN]; @@ -1032,7 +1118,7 @@ prelist_update(struct nd_prefixctl *new, struct nd_defrouter *dr, pr->ndpr_vltime = new->ndpr_vltime; pr->ndpr_pltime = new->ndpr_pltime; (void)in6_init_prefix_ltimes(pr); /* XXX error case? */ - pr->ndpr_lastupdate = time_second; + pr->ndpr_lastupdate = time_uptime; } if (new->ndpr_raf_onlink && @@ -1054,23 +1140,17 @@ prelist_update(struct nd_prefixctl *new, struct nd_defrouter *dr, if (dr && pfxrtr_lookup(pr, dr) == NULL) pfxrtr_add(pr, dr); } else { - struct nd_prefix *newpr = NULL; - - newprefix = 1; - if (new->ndpr_vltime == 0) goto end; if (new->ndpr_raf_onlink == 0 && new->ndpr_raf_auto == 0) goto end; - error = nd6_prelist_add(new, dr, &newpr); - if (error != 0 || newpr == NULL) { + error = nd6_prelist_add(new, dr, &pr); + if (error != 0) { nd6log((LOG_NOTICE, "prelist_update: " - "nd6_prelist_add failed for %s/%d on %s " - "errno=%d, returnpr=%p\n", + "nd6_prelist_add failed for %s/%d on %s errno=%d\n", ip6_sprintf(ip6buf, &new->ndpr_prefix.sin6_addr), - new->ndpr_plen, if_name(new->ndpr_ifp), - error, newpr)); + new->ndpr_plen, if_name(new->ndpr_ifp), error)); goto end; /* we should just give up in this case. */ } @@ -1081,13 +1161,11 @@ prelist_update(struct nd_prefixctl *new, struct nd_defrouter *dr, * addresses. Thus, we explicitly make sure that the prefix * itself expires now. */ - if (newpr->ndpr_raf_onlink == 0) { - newpr->ndpr_vltime = 0; - newpr->ndpr_pltime = 0; - in6_init_prefix_ltimes(newpr); + if (pr->ndpr_raf_onlink == 0) { + pr->ndpr_vltime = 0; + pr->ndpr_pltime = 0; + in6_init_prefix_ltimes(pr); } - - pr = newpr; } /* @@ -1170,7 +1248,7 @@ prelist_update(struct nd_prefixctl *new, struct nd_defrouter *dr, if (lt6_tmp.ia6t_vltime == ND6_INFINITE_LIFETIME) remaininglifetime = ND6_INFINITE_LIFETIME; - else if (time_second - ifa6->ia6_updatetime > + else if (time_uptime - ifa6->ia6_updatetime > lt6_tmp.ia6t_vltime) { /* * The case of "invalid" address. We should usually @@ -1179,7 +1257,7 @@ prelist_update(struct nd_prefixctl *new, struct nd_defrouter *dr, remaininglifetime = 0; } else remaininglifetime = lt6_tmp.ia6t_vltime - - (time_second - ifa6->ia6_updatetime); + (time_uptime - ifa6->ia6_updatetime); /* when not updating, keep the current stored lifetime. */ lt6_tmp.ia6t_vltime = remaininglifetime; @@ -1215,18 +1293,18 @@ prelist_update(struct nd_prefixctl *new, struct nd_defrouter *dr, u_int32_t maxvltime, maxpltime; if (V_ip6_temp_valid_lifetime > - (u_int32_t)((time_second - ifa6->ia6_createtime) + + (u_int32_t)((time_uptime - ifa6->ia6_createtime) + V_ip6_desync_factor)) { maxvltime = V_ip6_temp_valid_lifetime - - (time_second - ifa6->ia6_createtime) - + (time_uptime - ifa6->ia6_createtime) - V_ip6_desync_factor; } else maxvltime = 0; if (V_ip6_temp_preferred_lifetime > - (u_int32_t)((time_second - ifa6->ia6_createtime) + + (u_int32_t)((time_uptime - ifa6->ia6_createtime) + V_ip6_desync_factor)) { maxpltime = V_ip6_temp_preferred_lifetime - - (time_second - ifa6->ia6_createtime) - + (time_uptime - ifa6->ia6_createtime) - V_ip6_desync_factor; } else maxpltime = 0; @@ -1241,7 +1319,7 @@ prelist_update(struct nd_prefixctl *new, struct nd_defrouter *dr, } } ifa6->ia6_lifetime = lt6_tmp; - ifa6->ia6_updatetime = time_second; + ifa6->ia6_updatetime = time_uptime; } IF_ADDR_RUNLOCK(ifp); if (ia6_match == NULL && new->ndpr_vltime) { @@ -1319,7 +1397,6 @@ prelist_update(struct nd_prefixctl *new, struct nd_defrouter *dr, } end: - splx(s); return error; } @@ -1363,12 +1440,13 @@ find_pfxlist_reachable_router(struct nd_prefix *pr) * is no router around us. */ void -pfxlist_onlink_check() +pfxlist_onlink_check(void) { struct nd_prefix *pr; struct in6_ifaddr *ifa; struct nd_defrouter *dr; struct nd_pfxrouter *pfxrtr = NULL; + struct rm_priotracker in6_ifa_tracker; /* * Check if there is a prefix that has a reachable advertising @@ -1384,6 +1462,7 @@ pfxlist_onlink_check() * that does not advertise any prefixes. */ if (pr == NULL) { + ND6_RLOCK(); TAILQ_FOREACH(dr, &V_nd_defrouter, dr_entry) { struct nd_prefix *pr0; @@ -1394,6 +1473,7 @@ pfxlist_onlink_check() if (pfxrtr != NULL) break; } + ND6_RUNLOCK(); } if (pr != NULL || (!TAILQ_EMPTY(&V_nd_defrouter) && pfxrtr == NULL)) { /* @@ -1424,7 +1504,7 @@ pfxlist_onlink_check() find_pfxlist_reachable_router(pr) == NULL) pr->ndpr_stateflags |= NDPRF_DETACHED; if ((pr->ndpr_stateflags & NDPRF_DETACHED) != 0 && - find_pfxlist_reachable_router(pr) != 0) + find_pfxlist_reachable_router(pr) != NULL) pr->ndpr_stateflags &= ~NDPRF_DETACHED; } } else { @@ -1497,9 +1577,8 @@ pfxlist_onlink_check() * detached. Note, however, that a manually configured address should * always be attached. * The precise detection logic is same as the one for prefixes. - * - * XXXRW: in6_ifaddrhead locking. */ + IN6_IFADDR_RLOCK(&in6_ifa_tracker); TAILQ_FOREACH(ifa, &V_in6_ifaddrhead, ia_link) { if (!(ifa->ia6_flags & IN6_IFF_AUTOCONF)) continue; @@ -1534,8 +1613,7 @@ pfxlist_onlink_check() ifa->ia6_flags |= IN6_IFF_DETACHED; } } - } - else { + } else { TAILQ_FOREACH(ifa, &V_in6_ifaddrhead, ia_link) { if ((ifa->ia6_flags & IN6_IFF_AUTOCONF) == 0) continue; @@ -1548,13 +1626,14 @@ pfxlist_onlink_check() } } } + IN6_IFADDR_RUNLOCK(&in6_ifa_tracker); } static int nd6_prefix_onlink_rtrequest(struct nd_prefix *pr, struct ifaddr *ifa) { static struct sockaddr_dl null_sdl = {sizeof(null_sdl), AF_LINK}; - struct radix_node_head *rnh; + struct rib_head *rnh; struct rtentry *rt; struct sockaddr_in6 mask6; u_long rtflags; @@ -1583,7 +1662,7 @@ nd6_prefix_onlink_rtrequest(struct nd_prefix *pr, struct ifaddr *ifa) rnh = rt_tables_get_rnh(rt->rt_fibnum, AF_INET6); /* XXX what if rhn == NULL? */ - RADIX_NODE_HEAD_LOCK(rnh); + RIB_WLOCK(rnh); RT_LOCK(rt); if (rt_setgate(rt, rt_key(rt), (struct sockaddr *)&null_sdl) == 0) { @@ -1593,7 +1672,7 @@ nd6_prefix_onlink_rtrequest(struct nd_prefix *pr, struct ifaddr *ifa) dl->sdl_type = rt->rt_ifp->if_type; dl->sdl_index = rt->rt_ifp->if_index; } - RADIX_NODE_HEAD_UNLOCK(rnh); + RIB_WUNLOCK(rnh); nd6_rtmsg(RTM_ADD, rt); RT_UNLOCK(rt); pr->ndpr_stateflags |= NDPRF_ONLINK; @@ -1755,6 +1834,7 @@ nd6_prefix_offlink(struct nd_prefix *pr) } } error = a_failure; + a_failure = 1; if (error == 0) { pr->ndpr_stateflags &= ~NDPRF_ONLINK; @@ -1793,7 +1873,8 @@ nd6_prefix_offlink(struct nd_prefix *pr) &opr->ndpr_prefix.sin6_addr), opr->ndpr_plen, if_name(ifp), if_name(opr->ndpr_ifp), e)); - } + } else + a_failure = 0; } } } else { @@ -1805,6 +1886,10 @@ nd6_prefix_offlink(struct nd_prefix *pr) if_name(ifp), error)); } + if (a_failure) + lltable_prefix_free(AF_INET6, (struct sockaddr *)&sa6, + (struct sockaddr *)&mask6, LLE_STATIC); + return (error); } @@ -1860,22 +1945,9 @@ in6_ifadd(struct nd_prefixctl *pr, int mcast) } /* make ifaddr */ + in6_prepare_ifra(&ifra, &pr->ndpr_prefix.sin6_addr, &mask); - bzero(&ifra, sizeof(ifra)); - /* - * in6_update_ifa() does not use ifra_name, but we accurately set it - * for safety. - */ - strncpy(ifra.ifra_name, if_name(ifp), sizeof(ifra.ifra_name)); - ifra.ifra_addr.sin6_family = AF_INET6; - ifra.ifra_addr.sin6_len = sizeof(struct sockaddr_in6); - /* prefix */ - ifra.ifra_addr.sin6_addr = pr->ndpr_prefix.sin6_addr; - ifra.ifra_addr.sin6_addr.s6_addr32[0] &= mask.s6_addr32[0]; - ifra.ifra_addr.sin6_addr.s6_addr32[1] &= mask.s6_addr32[1]; - ifra.ifra_addr.sin6_addr.s6_addr32[2] &= mask.s6_addr32[2]; - ifra.ifra_addr.sin6_addr.s6_addr32[3] &= mask.s6_addr32[3]; - + IN6_MASK_ADDR(&ifra.ifra_addr.sin6_addr, &mask); /* interface ID */ ifra.ifra_addr.sin6_addr.s6_addr32[0] |= (ib->ia_addr.sin6_addr.s6_addr32[0] & ~mask.s6_addr32[0]); @@ -1887,12 +1959,6 @@ in6_ifadd(struct nd_prefixctl *pr, int mcast) (ib->ia_addr.sin6_addr.s6_addr32[3] & ~mask.s6_addr32[3]); ifa_free(ifa); - /* new prefix mask. */ - ifra.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6); - ifra.ifra_prefixmask.sin6_family = AF_INET6; - bcopy(&mask, &ifra.ifra_prefixmask.sin6_addr, - sizeof(ifra.ifra_prefixmask.sin6_addr)); - /* lifetimes. */ ifra.ifra_lifetime.ia6t_vltime = pr->ndpr_vltime; ifra.ifra_lifetime.ia6t_pltime = pr->ndpr_pltime; @@ -1949,24 +2015,21 @@ int in6_tmpifadd(const struct in6_ifaddr *ia0, int forcegen, int delay) { struct ifnet *ifp = ia0->ia_ifa.ifa_ifp; - struct in6_ifaddr *newia, *ia; + struct in6_ifaddr *newia; struct in6_aliasreq ifra; - int i, error; + int error; int trylimit = 3; /* XXX: adhoc value */ int updateflags; u_int32_t randid[2]; time_t vltime0, pltime0; - bzero(&ifra, sizeof(ifra)); - strncpy(ifra.ifra_name, if_name(ifp), sizeof(ifra.ifra_name)); - ifra.ifra_addr = ia0->ia_addr; - /* copy prefix mask */ - ifra.ifra_prefixmask = ia0->ia_prefixmask; + in6_prepare_ifra(&ifra, &ia0->ia_addr.sin6_addr, + &ia0->ia_prefixmask.sin6_addr); + + ifra.ifra_addr = ia0->ia_addr; /* XXX: do we need this ? */ /* clear the old IFID */ - for (i = 0; i < 4; i++) { - ifra.ifra_addr.sin6_addr.s6_addr32[i] &= - ifra.ifra_prefixmask.sin6_addr.s6_addr32[i]; - } + IN6_MASK_ADDR(&ifra.ifra_addr.sin6_addr, + &ifra.ifra_prefixmask.sin6_addr); again: if (in6_get_tmpifid(ifp, (u_int8_t *)randid, @@ -1986,26 +2049,18 @@ in6_tmpifadd(const struct in6_ifaddr *ia0, int forcegen, int delay) * there may be a time lag between generation of the ID and generation * of the address. So, we'll do one more sanity check. */ - IN6_IFADDR_RLOCK(); - TAILQ_FOREACH(ia, &V_in6_ifaddrhead, ia_link) { - if (IN6_ARE_ADDR_EQUAL(&ia->ia_addr.sin6_addr, - &ifra.ifra_addr.sin6_addr)) { - if (trylimit-- == 0) { - IN6_IFADDR_RUNLOCK(); - /* - * Give up. Something strange should have - * happened. - */ - nd6log((LOG_NOTICE, "in6_tmpifadd: failed to " - "find a unique random IFID\n")); - return (EEXIST); - } - IN6_IFADDR_RUNLOCK(); + + if (in6_localip(&ifra.ifra_addr.sin6_addr) != 0) { + if (trylimit-- > 0) { forcegen = 1; goto again; } + + /* Give up. Something strange should have happened. */ + nd6log((LOG_NOTICE, "in6_tmpifadd: failed to " + "find a unique random IFID\n")); + return (EEXIST); } - IN6_IFADDR_RUNLOCK(); /* * The Valid Lifetime is the lower of the Valid Lifetime of the @@ -2017,7 +2072,7 @@ in6_tmpifadd(const struct in6_ifaddr *ia0, int forcegen, int delay) if (ia0->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME) { vltime0 = IFA6_IS_INVALID(ia0) ? 0 : (ia0->ia6_lifetime.ia6t_vltime - - (time_second - ia0->ia6_updatetime)); + (time_uptime - ia0->ia6_updatetime)); if (vltime0 > V_ip6_temp_valid_lifetime) vltime0 = V_ip6_temp_valid_lifetime; } else @@ -2025,7 +2080,7 @@ in6_tmpifadd(const struct in6_ifaddr *ia0, int forcegen, int delay) if (ia0->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME) { pltime0 = IFA6_IS_DEPRECATED(ia0) ? 0 : (ia0->ia6_lifetime.ia6t_pltime - - (time_second - ia0->ia6_updatetime)); + (time_uptime - ia0->ia6_updatetime)); if (pltime0 > V_ip6_temp_preferred_lifetime - V_ip6_desync_factor){ pltime0 = V_ip6_temp_preferred_lifetime - V_ip6_desync_factor; @@ -2083,11 +2138,11 @@ in6_init_prefix_ltimes(struct nd_prefix *ndpr) if (ndpr->ndpr_pltime == ND6_INFINITE_LIFETIME) ndpr->ndpr_preferred = 0; else - ndpr->ndpr_preferred = time_second + ndpr->ndpr_pltime; + ndpr->ndpr_preferred = time_uptime + ndpr->ndpr_pltime; if (ndpr->ndpr_vltime == ND6_INFINITE_LIFETIME) ndpr->ndpr_expire = 0; else - ndpr->ndpr_expire = time_second + ndpr->ndpr_vltime; + ndpr->ndpr_expire = time_uptime + ndpr->ndpr_vltime; return 0; } @@ -2099,7 +2154,7 @@ in6_init_address_ltimes(struct nd_prefix *new, struct in6_addrlifetime *lt6) if (lt6->ia6t_vltime == ND6_INFINITE_LIFETIME) lt6->ia6t_expire = 0; else { - lt6->ia6t_expire = time_second; + lt6->ia6t_expire = time_uptime; lt6->ia6t_expire += lt6->ia6t_vltime; } @@ -2107,7 +2162,7 @@ in6_init_address_ltimes(struct nd_prefix *new, struct in6_addrlifetime *lt6) if (lt6->ia6t_pltime == ND6_INFINITE_LIFETIME) lt6->ia6t_preferred = 0; else { - lt6->ia6t_preferred = time_second; + lt6->ia6t_preferred = time_uptime; lt6->ia6t_preferred += lt6->ia6t_pltime; } } @@ -2120,34 +2175,19 @@ in6_init_address_ltimes(struct nd_prefix *new, struct in6_addrlifetime *lt6) void rt6_flush(struct in6_addr *gateway, struct ifnet *ifp) { - struct radix_node_head *rnh; - u_int fibnum; - int s = splnet(); /* We'll care only link-local addresses */ - if (!IN6_IS_ADDR_LINKLOCAL(gateway)) { - splx(s); + if (!IN6_IS_ADDR_LINKLOCAL(gateway)) return; - } /* XXX Do we really need to walk any but the default FIB? */ - for (fibnum = 0; fibnum < rt_numfibs; fibnum++) { - rnh = rt_tables_get_rnh(fibnum, AF_INET6); - if (rnh == NULL) - continue; - - RADIX_NODE_HEAD_LOCK(rnh); - rnh->rnh_walktree(rnh, rt6_deleteroute, (void *)gateway); - RADIX_NODE_HEAD_UNLOCK(rnh); - } - splx(s); + rt_foreach_fib_walk_del(AF_INET6, rt6_deleteroute, (void *)gateway); } static int -rt6_deleteroute(struct radix_node *rn, void *arg) +rt6_deleteroute(const struct rtentry *rt, void *arg) { #define SIN6(s) ((struct sockaddr_in6 *)s) - struct rtentry *rt = (struct rtentry *)rn; struct in6_addr *gate = (struct in6_addr *)arg; if (rt->rt_gateway == NULL || rt->rt_gateway->sa_family != AF_INET6) @@ -2172,8 +2212,7 @@ rt6_deleteroute(struct radix_node *rn, void *arg) if ((rt->rt_flags & RTF_HOST) == 0) return (0); - return (in6_rtrequest(RTM_DELETE, rt_key(rt), rt->rt_gateway, - rt_mask(rt), rt->rt_flags, NULL, rt->rt_fibnum)); + return (1); #undef SIN6 } |