summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/netinet6/nd6_rtr.c
diff options
context:
space:
mode:
Diffstat (limited to 'freebsd/sys/netinet6/nd6_rtr.c')
-rw-r--r--freebsd/sys/netinet6/nd6_rtr.c493
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
}