-/* $FreeBSD: src/sys/netinet6/nd6_rtr.c,v 1.2.2.3 2001/07/03 11:01:54 ume Exp $ */
+/*
+ * Copyright (c) 2003-2007 Apple Inc. All rights reserved.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
+ *
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
+ */
+
+/* $FreeBSD: src/sys/netinet6/nd6_rtr.c,v 1.11 2002/04/19 04:46:23 suz Exp $ */
/* $KAME: nd6_rtr.c,v 1.111 2001/04/27 01:37:15 jinmei Exp $ */
/*
#include <sys/errno.h>
#include <sys/syslog.h>
#include <sys/queue.h>
+#include <kern/lock.h>
#include <net/if.h>
#include <net/if_types.h>
#define SDL(s) ((struct sockaddr_dl *)s)
-static struct nd_defrouter *defrtrlist_update __P((struct nd_defrouter *));
-static struct in6_ifaddr *in6_ifadd __P((struct nd_prefix *,
- struct in6_addr *));
-static struct nd_pfxrouter *pfxrtr_lookup __P((struct nd_prefix *,
- struct nd_defrouter *));
-static void pfxrtr_add __P((struct nd_prefix *, struct nd_defrouter *));
-static void pfxrtr_del __P((struct nd_pfxrouter *));
-static struct nd_pfxrouter *find_pfxlist_reachable_router
- __P((struct nd_prefix *));
-static void defrouter_addifreq __P((struct ifnet *));
-static void nd6_rtmsg __P((int, struct rtentry *));
+static struct nd_defrouter *defrtrlist_update(struct nd_defrouter *);
+static struct in6_ifaddr *in6_ifadd(struct nd_prefix *,
+ struct in6_addr *);
+static struct nd_pfxrouter *pfxrtr_lookup(struct nd_prefix *,
+ struct nd_defrouter *);
+static void pfxrtr_add(struct nd_prefix *, struct nd_defrouter *);
+static void pfxrtr_del(struct nd_pfxrouter *);
+static struct nd_pfxrouter *find_pfxlist_reachable_router(struct nd_prefix *);
+static void defrouter_addifreq(struct ifnet *);
+static void nd6_rtmsg(int, struct rtentry *);
-static void in6_init_address_ltimes __P((struct nd_prefix *ndpr,
- struct in6_addrlifetime *lt6));
+static void in6_init_address_ltimes(struct nd_prefix *ndpr,
+ struct in6_addrlifetime *lt6);
-static int rt6_deleteroute __P((struct radix_node *, void *));
+static int rt6_deleteroute(struct radix_node *, void *);
extern int nd6_recalc_reachtm_interval;
*/
int ip6_temp_regen_advance = TEMPADDR_REGEN_ADVANCE;
+extern lck_mtx_t *rt_mtx;
+extern lck_mtx_t *nd6_mutex;
+
/*
* Receive Router Solicitation Message - just for routers.
* Router solicitation/advertisement is mostly managed by userland program
* Based on RFC 2461
*/
void
-nd6_rs_input(m, off, icmp6len)
- struct mbuf *m;
- int off, icmp6len;
+nd6_rs_input(
+ struct mbuf *m,
+ int off,
+ int icmp6len)
{
struct ifnet *ifp = m->m_pkthdr.rcvif;
struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
union nd_opts ndopts;
/* If I'm not a router, ignore it. */
- if (ip6_accept_rtadv != 0 || ip6_forwarding != 1)
+ if (ip6_accept_rtadv != 0 || (ifp->if_eflags & IFEF_ACCEPT_RTADVD) || ip6_forwarding != 1)
goto freeit;
/* Sanity checks */
goto freeit;
#ifndef PULLDOWN_TEST
- IP6_EXTHDR_CHECK(m, off, icmp6len,);
+ IP6_EXTHDR_CHECK(m, off, icmp6len, return);
nd_rs = (struct nd_router_solicit *)((caddr_t)ip6 + off);
#else
IP6_EXTHDR_GET(nd_rs, struct nd_router_solicit *, m, off, icmp6len);
* TODO: ND_RA_FLAG_{OTHER,MANAGED} processing
*/
void
-nd6_ra_input(m, off, icmp6len)
- struct mbuf *m;
- int off, icmp6len;
+nd6_ra_input(
+ struct mbuf *m,
+ int off,
+ int icmp6len)
{
struct ifnet *ifp = m->m_pkthdr.rcvif;
struct nd_ifinfo *ndi = &nd_ifinfo[ifp->if_index];
#endif
union nd_opts ndopts;
struct nd_defrouter *dr;
+ struct timeval timenow;
- if (ip6_accept_rtadv == 0)
+ getmicrotime(&timenow);
+
+ if (ip6_accept_rtadv == 0 && ((ifp->if_eflags & IFEF_ACCEPT_RTADVD) == 0))
goto freeit;
if (ip6->ip6_hlim != 255) {
}
#ifndef PULLDOWN_TEST
- IP6_EXTHDR_CHECK(m, off, icmp6len,);
+ IP6_EXTHDR_CHECK(m, off, icmp6len, return);
nd_ra = (struct nd_router_advert *)((caddr_t)ip6 + off);
#else
IP6_EXTHDR_GET(nd_ra, struct nd_router_advert *, m, off, icmp6len);
dr0.rtaddr = saddr6;
dr0.flags = nd_ra->nd_ra_flags_reserved;
dr0.rtlifetime = ntohs(nd_ra->nd_ra_router_lifetime);
- dr0.expire = time_second + dr0.rtlifetime;
+ dr0.expire = timenow.tv_sec + dr0.rtlifetime;
dr0.ifp = ifp;
dr0.advint = 0; /* Mobile IPv6 */
dr0.advint_expire = 0; /* Mobile IPv6 */
dr0.advints_lost = 0; /* Mobile IPv6 */
/* unspecified or not? (RFC 2461 6.3.4) */
if (advreachable) {
- NTOHL(advreachable);
+ advreachable = ntohl(advreachable);
if (advreachable <= MAX_REACHABLE_TIME &&
ndi->basereachable != advreachable) {
ndi->basereachable = advreachable;
pr.ndpr_prefix.sin6_family = AF_INET6;
pr.ndpr_prefix.sin6_len = sizeof(pr.ndpr_prefix);
pr.ndpr_prefix.sin6_addr = pi->nd_opt_pi_prefix;
- pr.ndpr_ifp = (struct ifnet *)m->m_pkthdr.rcvif;
+ pr.ndpr_ifp = m->m_pkthdr.rcvif;
pr.ndpr_raf_onlink = (pi->nd_opt_pi_flags_reserved &
ND_OPT_PI_FLAG_ONLINK) ? 1 : 0;
skip:
/*
- * Src linkaddress
+ * Source link layer address
*/
{
char *lladdr = NULL;
* router's neighbor cache, which might also affect our on-link
* detection of adveritsed prefixes.
*/
- pfxlist_onlink_check();
+ pfxlist_onlink_check(0);
}
freeit:
{
struct rt_addrinfo info;
+ lck_mtx_assert(rt_mtx, LCK_MTX_ASSERT_OWNED);
+
bzero((caddr_t)&info, sizeof(info));
info.rti_info[RTAX_DST] = rt_key(rt);
info.rti_info[RTAX_GATEWAY] = rt->rt_gateway;
info.rti_info[RTAX_NETMASK] = rt_mask(rt);
info.rti_info[RTAX_IFP] =
- (struct sockaddr *)TAILQ_FIRST(&rt->rt_ifp->if_addrlist);
+ TAILQ_FIRST(&rt->rt_ifp->if_addrlist)->ifa_addr;
info.rti_info[RTAX_IFA] = rt->rt_ifa->ifa_addr;
rt_missmsg(cmd, &info, rt->rt_flags, 0);
}
void
-defrouter_addreq(new)
- struct nd_defrouter *new;
+defrouter_addreq(
+ struct nd_defrouter *new)
{
struct sockaddr_in6 def, mask, gate;
struct rtentry *newrt = NULL;
- int s;
Bzero(&def, sizeof(def));
Bzero(&mask, sizeof(mask));
def.sin6_family = mask.sin6_family = gate.sin6_family = AF_INET6;
gate.sin6_addr = new->rtaddr;
- s = splnet();
- (void)rtrequest(RTM_ADD, (struct sockaddr *)&def,
+ lck_mtx_lock(rt_mtx);
+ (void)rtrequest_locked(RTM_ADD, (struct sockaddr *)&def,
(struct sockaddr *)&gate, (struct sockaddr *)&mask,
RTF_GATEWAY, &newrt);
if (newrt) {
nd6_rtmsg(RTM_ADD, newrt); /* tell user process */
rtunref(newrt);
}
- splx(s);
+ lck_mtx_unlock(rt_mtx);
return;
}
/* Add a route to a given interface as default */
void
-defrouter_addifreq(ifp)
- struct ifnet *ifp;
+defrouter_addifreq(
+ struct ifnet *ifp)
{
struct sockaddr_in6 def, mask;
- struct ifaddr *ifa;
+ struct ifaddr *ifa = NULL;
struct rtentry *newrt = NULL;
- int error, flags;
+ int error;
+ u_long flags;
bzero(&def, sizeof(def));
bzero(&mask, sizeof(mask));
return;
}
+ lck_mtx_lock(rt_mtx);
flags = ifa->ifa_flags;
- error = rtrequest(RTM_ADD, (struct sockaddr *)&def, ifa->ifa_addr,
+ error = rtrequest_locked(RTM_ADD, (struct sockaddr *)&def, ifa->ifa_addr,
(struct sockaddr *)&mask, flags, &newrt);
if (error != 0) {
nd6log((LOG_ERR,
nd6_rtmsg(RTM_ADD, newrt);
rtunref(newrt);
}
- in6_post_msg(ifp, KEV_INET6_DEFROUTER, &def);
+ in6_post_msg(ifp, KEV_INET6_DEFROUTER, (struct in6_ifaddr *)ifa);
}
+ lck_mtx_unlock(rt_mtx);
+ ifafree(ifa);
}
struct nd_defrouter *
-defrouter_lookup(addr, ifp)
- struct in6_addr *addr;
- struct ifnet *ifp;
+defrouter_lookup(
+ struct in6_addr *addr,
+ struct ifnet *ifp)
{
struct nd_defrouter *dr;
+
+ lck_mtx_assert(nd6_mutex, LCK_MTX_ASSERT_OWNED);
+
for (dr = TAILQ_FIRST(&nd_defrouter); dr;
dr = TAILQ_NEXT(dr, dr_entry)) {
if (dr->ifp == ifp && IN6_ARE_ADDR_EQUAL(addr, &dr->rtaddr))
}
void
-defrouter_delreq(dr, dofree)
- struct nd_defrouter *dr;
- int dofree;
+defrouter_delreq(
+ struct nd_defrouter *dr,
+ int dofree)
{
struct sockaddr_in6 def, mask, gate;
struct rtentry *oldrt = NULL;
def.sin6_family = mask.sin6_family = gate.sin6_family = AF_INET6;
gate.sin6_addr = dr->rtaddr;
- rtrequest(RTM_DELETE, (struct sockaddr *)&def,
+ lck_mtx_lock(rt_mtx);
+ rtrequest_locked(RTM_DELETE, (struct sockaddr *)&def,
(struct sockaddr *)&gate,
(struct sockaddr *)&mask,
RTF_GATEWAY, &oldrt);
if (oldrt) {
nd6_rtmsg(RTM_DELETE, oldrt);
- if (oldrt->rt_refcnt <= 0) {
- /*
- * XXX: borrowed from the RTM_DELETE case of
- * rtrequest().
- */
- rtref(oldrt);
- rtfree(oldrt);
- }
+ rtfree_locked(oldrt);
}
if (dofree) /* XXX: necessary? */
FREE(dr, M_IP6NDP);
+ lck_mtx_unlock(rt_mtx);
}
void
-defrtrlist_del(dr)
- struct nd_defrouter *dr;
+defrtrlist_del(
+ struct nd_defrouter *dr, int nd6locked)
{
struct nd_defrouter *deldr = NULL;
struct nd_prefix *pr;
* Flush all the routing table entries that use the router
* as a next hop.
*/
- if (!ip6_forwarding && ip6_accept_rtadv) {
+ if (!ip6_forwarding && (ip6_accept_rtadv || (dr->ifp->if_eflags & IFEF_ACCEPT_RTADVD))) {
/* above is a good condition? */
rt6_flush(&dr->rtaddr, dr->ifp);
}
+ if (nd6locked == 0)
+ lck_mtx_lock(nd6_mutex);
if (dr == TAILQ_FIRST(&nd_defrouter))
deldr = dr; /* The router is primary. */
if ((pfxrtr = pfxrtr_lookup(pr, dr)) != NULL)
pfxrtr_del(pfxrtr);
}
- pfxlist_onlink_check();
+ pfxlist_onlink_check(1);
/*
* If the router is the primary one, choose a new one.
if (deldr)
defrouter_select();
+ if (nd6locked == 0)
+ lck_mtx_unlock(nd6_mutex);
+
FREE(dr, M_IP6NDP);
}
void
defrouter_select()
{
- int s = splnet();
struct nd_defrouter *dr, anydr;
struct rtentry *rt = NULL;
struct llinfo_nd6 *ln = NULL;
/*
* Search for a (probably) reachable router from the list.
*/
+ lck_mtx_assert(nd6_mutex, LCK_MTX_ASSERT_OWNED);
+
for (dr = TAILQ_FIRST(&nd_defrouter); dr;
dr = TAILQ_NEXT(dr, dr_entry)) {
- if ((rt = nd6_lookup(&dr->rtaddr, 0, dr->ifp)) &&
+ if ((rt = nd6_lookup(&dr->rtaddr, 0, dr->ifp, 0)) &&
(ln = (struct llinfo_nd6 *)rt->rt_llinfo) &&
ND6_IS_LLINFO_PROBREACH(ln)) {
/* Got it, and move it to the head */
}
}
- splx(s);
return;
}
static struct nd_defrouter *
-defrtrlist_update(new)
- struct nd_defrouter *new;
+defrtrlist_update(
+ struct nd_defrouter *new)
{
struct nd_defrouter *dr, *n;
- int s = splnet();
+ lck_mtx_lock(nd6_mutex);
if ((dr = defrouter_lookup(&new->rtaddr, new->ifp)) != NULL) {
/* entry exists */
if (new->rtlifetime == 0) {
- defrtrlist_del(dr);
+ defrtrlist_del(dr, 1);
dr = NULL;
} else {
/* override */
dr->rtlifetime = new->rtlifetime;
dr->expire = new->expire;
}
- splx(s);
+ lck_mtx_unlock(nd6_mutex);
return(dr);
}
/* entry does not exist */
if (new->rtlifetime == 0) {
- splx(s);
+ lck_mtx_unlock(nd6_mutex);
return(NULL);
}
n = (struct nd_defrouter *)_MALLOC(sizeof(*n), M_IP6NDP, M_NOWAIT);
if (n == NULL) {
- splx(s);
+ lck_mtx_unlock(nd6_mutex);
return(NULL);
}
bzero(n, sizeof(*n));
TAILQ_INSERT_TAIL(&nd_defrouter, n, dr_entry);
if (TAILQ_FIRST(&nd_defrouter) == n)
defrouter_select();
- splx(s);
+ lck_mtx_unlock(nd6_mutex);
return(n);
}
static struct nd_pfxrouter *
-pfxrtr_lookup(pr, dr)
- struct nd_prefix *pr;
- struct nd_defrouter *dr;
+pfxrtr_lookup(
+ struct nd_prefix *pr,
+ struct nd_defrouter *dr)
{
struct nd_pfxrouter *search;
+ lck_mtx_assert(nd6_mutex, LCK_MTX_ASSERT_OWNED);
for (search = pr->ndpr_advrtrs.lh_first; search; search = search->pfr_next) {
if (search->router == dr)
break;
}
static void
-pfxrtr_add(pr, dr)
- struct nd_prefix *pr;
- struct nd_defrouter *dr;
+pfxrtr_add(
+ struct nd_prefix *pr,
+ struct nd_defrouter *dr)
{
struct nd_pfxrouter *new;
+ lck_mtx_assert(nd6_mutex, LCK_MTX_ASSERT_OWNED);
+
new = (struct nd_pfxrouter *)_MALLOC(sizeof(*new), M_IP6NDP, M_NOWAIT);
if (new == NULL)
return;
LIST_INSERT_HEAD(&pr->ndpr_advrtrs, new, pfr_entry);
- pfxlist_onlink_check();
+ pfxlist_onlink_check(1);
}
static void
-pfxrtr_del(pfr)
- struct nd_pfxrouter *pfr;
+pfxrtr_del(
+ struct nd_pfxrouter *pfr)
{
+ lck_mtx_assert(nd6_mutex, LCK_MTX_ASSERT_OWNED);
LIST_REMOVE(pfr, pfr_entry);
FREE(pfr, M_IP6NDP);
}
struct nd_prefix *
-nd6_prefix_lookup(pr)
- struct nd_prefix *pr;
+nd6_prefix_lookup(
+ struct nd_prefix *pr)
{
struct nd_prefix *search;
+ lck_mtx_lock(nd6_mutex);
for (search = nd_prefix.lh_first; search; search = search->ndpr_next) {
if (pr->ndpr_ifp == search->ndpr_ifp &&
pr->ndpr_plen == search->ndpr_plen &&
break;
}
}
+ if (search != NULL)
+ ndpr_hold(search, TRUE);
+ lck_mtx_unlock(nd6_mutex);
return(search);
}
+void
+ndpr_hold(struct nd_prefix *pr, boolean_t locked)
+{
+ if (!locked)
+ lck_mtx_lock(nd6_mutex);
+
+ if (pr->ndpr_usecnt < 0)
+ panic("%s: bad usecnt %d for pr %p\n", __func__,
+ pr->ndpr_usecnt, pr);
+
+ pr->ndpr_usecnt++;
+
+ if (!locked)
+ lck_mtx_unlock(nd6_mutex);
+}
+
+void
+ndpr_rele(struct nd_prefix *pr, boolean_t locked)
+{
+ if (!locked)
+ lck_mtx_lock(nd6_mutex);
+
+ if (pr->ndpr_usecnt <= 0)
+ panic("%s: bad usecnt %d for pr %p\n", __func__,
+ pr->ndpr_usecnt, pr);
+
+ pr->ndpr_usecnt--;
+
+ if (!locked)
+ lck_mtx_unlock(nd6_mutex);
+}
+
int
-nd6_prelist_add(pr, dr, newp)
- struct nd_prefix *pr, **newp;
- struct nd_defrouter *dr;
+nd6_prelist_add(
+ struct nd_prefix *pr,
+ struct nd_defrouter *dr,
+ struct nd_prefix **newp)
{
struct nd_prefix *new = NULL;
- int i, s;
+ int i;
new = (struct nd_prefix *)_MALLOC(sizeof(*new), M_IP6NDP, M_NOWAIT);
if (new == NULL)
new->ndpr_prefix.sin6_addr.s6_addr32[i] &=
new->ndpr_mask.s6_addr32[i];
- s = splnet();
/* link ndpr_entry to nd_prefix list */
+ lck_mtx_lock(nd6_mutex);
LIST_INSERT_HEAD(&nd_prefix, new, ndpr_entry);
- splx(s);
+
+ new->ndpr_usecnt = 0;
+ ndpr_hold(new, TRUE);
/* ND_OPT_PI_FLAG_ONLINK processing */
if (new->ndpr_raf_onlink) {
int e;
- if ((e = nd6_prefix_onlink(new)) != 0) {
+ if ((e = nd6_prefix_onlink(new, 0, 1)) != 0) {
nd6log((LOG_ERR, "nd6_prelist_add: failed to make "
"the prefix %s/%d on-link on %s (errno=%d)\n",
ip6_sprintf(&pr->ndpr_prefix.sin6_addr),
if (dr) {
pfxrtr_add(new, dr);
}
+ lck_mtx_unlock(nd6_mutex);
return 0;
}
void
-prelist_remove(pr)
- struct nd_prefix *pr;
+prelist_remove(
+ struct nd_prefix *pr, int nd6locked)
{
struct nd_pfxrouter *pfr, *next;
- int e, s;
+ int e;
/* make sure to invalidate the prefix until it is really freed. */
pr->ndpr_vltime = 0;
/* what should we do? */
}
- if (pr->ndpr_refcnt > 0)
- return; /* notice here? */
+ if (nd6locked == 0)
+ lck_mtx_lock(nd6_mutex);
- s = splnet();
+ if (pr->ndpr_usecnt > 0 || pr->ndpr_refcnt > 0)
+ goto done; /* notice here? */
/* unlink ndpr_entry from nd_prefix list */
LIST_REMOVE(pr, ndpr_entry);
FREE(pfr, M_IP6NDP);
}
- splx(s);
FREE(pr, M_IP6NDP);
- pfxlist_onlink_check();
+ pfxlist_onlink_check(1);
+done:
+ if (nd6locked == 0)
+ lck_mtx_unlock(nd6_mutex);
}
int
-prelist_update(new, dr, m)
- struct nd_prefix *new;
- struct nd_defrouter *dr; /* may be NULL */
- struct mbuf *m;
+prelist_update(
+ struct nd_prefix *new,
+ struct nd_defrouter *dr, /* may be NULL */
+ struct mbuf *m)
{
struct in6_ifaddr *ia6 = NULL, *ia6_match = NULL;
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;
+ struct timeval timenow;
auth = 0;
if (m) {
(pr->ndpr_stateflags & NDPRF_ONLINK) == 0) {
int e;
- if ((e = nd6_prefix_onlink(pr)) != 0) {
+ if ((e = nd6_prefix_onlink(pr, 0, 0)) != 0) {
nd6log((LOG_ERR,
"prelist_update: failed to make "
"the prefix %s/%d on-link on %s "
/* proceed anyway. XXX: is it correct? */
}
}
-
+
+ lck_mtx_lock(nd6_mutex);
if (dr && pfxrtr_lookup(pr, dr) == NULL)
pfxrtr_add(pr, dr);
+ lck_mtx_unlock(nd6_mutex);
} else {
struct nd_prefix *newpr = NULL;
* form an address. Note that even a manually configured address
* should reject autoconfiguration of a new address.
*/
+ getmicrotime(&timenow);
+
+ ifnet_lock_exclusive(ifp);
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list)
{
struct in6_ifaddr *ifa6;
lt6_tmp = ifa6->ia6_lifetime;
storedlifetime = IFA6_IS_INVALID(ifa6) ? 0 :
- (lt6_tmp.ia6t_expire - time_second);
+ (lt6_tmp.ia6t_expire - timenow.tv_sec);
if (TWOHOUR < new->ndpr_vltime ||
storedlifetime < new->ndpr_vltime) {
ifa6->ia6_lifetime = lt6_tmp;
}
+ ifnet_lock_done(ifp);
if (ia6_match == NULL && new->ndpr_vltime) {
/*
* No address matched and the valid lifetime is non-zero.
/*
* note that we should use pr (not new) for reference.
*/
+ lck_mtx_lock(nd6_mutex);
pr->ndpr_refcnt++;
+ lck_mtx_unlock(nd6_mutex);
ia6->ia6_ndpr = pr;
#if 0
* of other addresses, so we check and update it.
* XXX: what if address duplication happens?
*/
- pfxlist_onlink_check();
+ pfxlist_onlink_check(0);
} else {
/* just set an error. do not bark here. */
error = EADDRNOTAVAIL; /* XXX: might be unused. */
}
}
- afteraddrconf:
+afteraddrconf:
+
+end:
+ if (pr != NULL)
+ ndpr_rele(pr, FALSE);
- end:
- splx(s);
return error;
}
* XXX: lengthy function name...
*/
static struct nd_pfxrouter *
-find_pfxlist_reachable_router(pr)
- struct nd_prefix *pr;
+find_pfxlist_reachable_router(
+ struct nd_prefix *pr)
{
struct nd_pfxrouter *pfxrtr;
struct rtentry *rt;
struct llinfo_nd6 *ln;
+ lck_mtx_assert(nd6_mutex, LCK_MTX_ASSERT_OWNED);
+
for (pfxrtr = LIST_FIRST(&pr->ndpr_advrtrs); pfxrtr;
pfxrtr = LIST_NEXT(pfxrtr, pfr_entry)) {
if ((rt = nd6_lookup(&pfxrtr->router->rtaddr, 0,
- pfxrtr->router->ifp)) &&
+ pfxrtr->router->ifp, 0)) &&
(ln = (struct llinfo_nd6 *)rt->rt_llinfo) &&
ND6_IS_LLINFO_PROBREACH(ln))
break; /* found */
* is no router around us.
*/
void
-pfxlist_onlink_check()
+pfxlist_onlink_check(int nd6locked)
{
struct nd_prefix *pr;
struct in6_ifaddr *ifa;
* Check if there is a prefix that has a reachable advertising
* router.
*/
+ if (nd6locked == 0)
+ lck_mtx_lock(nd6_mutex);
+ lck_mtx_assert(nd6_mutex, LCK_MTX_ASSERT_OWNED);
for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
if (pr->ndpr_raf_onlink && find_pfxlist_reachable_router(pr))
break;
if ((pr->ndpr_stateflags & NDPRF_DETACHED) == 0 &&
(pr->ndpr_stateflags & NDPRF_ONLINK) == 0 &&
pr->ndpr_raf_onlink) {
- if ((e = nd6_prefix_onlink(pr)) != 0) {
+ if ((e = nd6_prefix_onlink(pr, 0, 1)) != 0) {
nd6log((LOG_ERR,
"pfxlist_onlink_check: failed to "
"make %s/%d offlink, errno=%d\n",
* always be attached.
* The precise detection logic is same as the one for prefixes.
*/
- for (ifa = in6_ifaddr; ifa; ifa = ifa->ia_next) {
+ for (ifa = in6_ifaddrs; ifa; ifa = ifa->ia_next) {
if ((ifa->ia6_flags & IN6_IFF_AUTOCONF) == 0)
continue;
break;
}
if (ifa) {
- for (ifa = in6_ifaddr; ifa; ifa = ifa->ia_next) {
+ for (ifa = in6_ifaddrs; ifa; ifa = ifa->ia_next) {
if ((ifa->ia6_flags & IN6_IFF_AUTOCONF) == 0)
continue;
}
}
else {
- for (ifa = in6_ifaddr; ifa; ifa = ifa->ia_next) {
+ for (ifa = in6_ifaddrs; ifa; ifa = ifa->ia_next) {
if ((ifa->ia6_flags & IN6_IFF_AUTOCONF) == 0)
continue;
ifa->ia6_flags &= ~IN6_IFF_DETACHED;
}
}
+ if (nd6locked == 0)
+ lck_mtx_unlock(nd6_mutex);
}
int
-nd6_prefix_onlink(pr)
- struct nd_prefix *pr;
+nd6_prefix_onlink(
+ struct nd_prefix *pr, int rtlocked, int nd6locked)
{
struct ifaddr *ifa;
struct ifnet *ifp = pr->ndpr_ifp;
* Although such a configuration is expected to be rare, we explicitly
* allow it.
*/
+ if (nd6locked == 0)
+ lck_mtx_lock(nd6_mutex);
+ else
+ lck_mtx_assert(nd6_mutex, LCK_MTX_ASSERT_OWNED);
for (opr = nd_prefix.lh_first; opr; opr = opr->ndpr_next) {
if (opr == pr)
continue;
if (opr->ndpr_plen == pr->ndpr_plen &&
in6_are_prefix_equal(&pr->ndpr_prefix.sin6_addr,
&opr->ndpr_prefix.sin6_addr,
- pr->ndpr_plen))
+ pr->ndpr_plen)) {
+ if (nd6locked == 0)
+ lck_mtx_unlock(nd6_mutex);
return(0);
+ }
}
+ if (nd6locked == 0)
+ lck_mtx_unlock(nd6_mutex);
/*
* We prefer link-local addresses as the associated interface address.
*/
IN6_IFF_ANYCAST);
if (ifa == NULL) {
/* XXX: freebsd does not have ifa_ifwithaf */
+ ifnet_lock_exclusive(ifp);
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list)
{
if (ifa->ifa_addr->sa_family == AF_INET6)
break;
}
+ ifnet_lock_done(ifp);
/* should we care about ia6_flags? */
}
if (ifa == NULL) {
bzero(&mask6, sizeof(mask6));
mask6.sin6_len = sizeof(mask6);
mask6.sin6_addr = pr->ndpr_mask;
+
+ if (rtlocked == 0)
+ lck_mtx_lock(rt_mtx);
+
rtflags = ifa->ifa_flags | RTF_CLONING | RTF_UP;
if (nd6_need_cache(ifp)) {
/* explicitly set in case ifa_flags does not set the flag. */
*/
rtflags &= ~RTF_CLONING;
}
- error = rtrequest(RTM_ADD, (struct sockaddr *)&pr->ndpr_prefix,
+ error = rtrequest_locked(RTM_ADD, (struct sockaddr *)&pr->ndpr_prefix,
ifa->ifa_addr, (struct sockaddr *)&mask6,
rtflags, &rt);
if (error == 0) {
if (rt != NULL)
rtunref(rt);
+ if (rtlocked == 0)
+ lck_mtx_unlock(rt_mtx);
return(error);
}
int
-nd6_prefix_offlink(pr)
- struct nd_prefix *pr;
+nd6_prefix_offlink(
+ struct nd_prefix *pr)
{
int error = 0;
struct ifnet *ifp = pr->ndpr_ifp;
mask6.sin6_family = AF_INET6;
mask6.sin6_len = sizeof(sa6);
bcopy(&pr->ndpr_mask, &mask6.sin6_addr, sizeof(struct in6_addr));
- error = rtrequest(RTM_DELETE, (struct sockaddr *)&sa6, NULL,
+ lck_mtx_lock(rt_mtx);
+ error = rtrequest_locked(RTM_DELETE, (struct sockaddr *)&sa6, NULL,
(struct sockaddr *)&mask6, 0, &rt);
if (error == 0) {
pr->ndpr_stateflags &= ~NDPRF_ONLINK;
* If there's one, try to make the prefix on-link on the
* interface.
*/
+ lck_mtx_assert(nd6_mutex, LCK_MTX_ASSERT_OWNED);
for (opr = nd_prefix.lh_first; opr; opr = opr->ndpr_next) {
if (opr == pr)
continue;
pr->ndpr_plen)) {
int e;
- if ((e = nd6_prefix_onlink(opr)) != 0) {
+ if ((e = nd6_prefix_onlink(opr, 1, 1)) != 0) {
nd6log((LOG_ERR,
"nd6_prefix_offlink: failed to "
"recover a prefix %s/%d from %s "
error));
}
- if (rt != NULL) {
- if (rt->rt_refcnt <= 0) {
- /* XXX: we should free the entry ourselves. */
- rtref(rt);
- rtfree(rt);
- }
- }
+ if (rt != NULL)
+ rtfree_locked(rt);
+
+ lck_mtx_unlock(rt_mtx);
return(error);
}
static struct in6_ifaddr *
-in6_ifadd(pr, ifid)
- struct nd_prefix *pr;
- struct in6_addr *ifid; /* Mobile IPv6 addition */
+in6_ifadd(
+ struct nd_prefix *pr,
+ struct in6_addr *ifid) /* Mobile IPv6 addition */
{
struct ifnet *ifp = pr->ndpr_ifp;
struct ifaddr *ifa;
}
int
-in6_tmpifadd(ia0, forcegen)
- const struct in6_ifaddr *ia0; /* corresponding public address */
+in6_tmpifadd(
+ const struct in6_ifaddr *ia0, /* corresponding public address */
+ int forcegen)
{
struct ifnet *ifp = ia0->ia_ifa.ifa_ifp;
struct in6_ifaddr *newia;
int trylimit = 3; /* XXX: adhoc value */
u_int32_t randid[2];
time_t vltime0, pltime0;
+ struct timeval timenow;
+
+ getmicrotime(&timenow);
bzero(&ifra, sizeof(ifra));
strncpy(ifra.ifra_name, if_name(ifp), sizeof(ifra.ifra_name));
*/
if (ia0->ia6_lifetime.ia6t_expire != 0) {
vltime0 = IFA6_IS_INVALID(ia0) ? 0 :
- (ia0->ia6_lifetime.ia6t_expire - time_second);
+ (ia0->ia6_lifetime.ia6t_expire - timenow.tv_sec);
if (vltime0 > ip6_temp_valid_lifetime)
vltime0 = ip6_temp_valid_lifetime;
} else
vltime0 = ip6_temp_valid_lifetime;
if (ia0->ia6_lifetime.ia6t_preferred != 0) {
pltime0 = IFA6_IS_DEPRECATED(ia0) ? 0 :
- (ia0->ia6_lifetime.ia6t_preferred - time_second);
+ (ia0->ia6_lifetime.ia6t_preferred - timenow.tv_sec);
if (pltime0 > ip6_temp_preferred_lifetime - ip6_desync_factor){
pltime0 = ip6_temp_preferred_lifetime -
ip6_desync_factor;
"no ifaddr\n"));
return(EINVAL); /* XXX */
}
+ lck_mtx_lock(nd6_mutex);
newia->ia6_ndpr = ia0->ia6_ndpr;
newia->ia6_ndpr->ndpr_refcnt++;
+ /*
+ * A newly added address might affect the status of other addresses.
+ * XXX: when the temporary address is generated with a new public
+ * address, the onlink check is redundant. However, it would be safe
+ * to do the check explicitly everywhere a new address is generated,
+ * and, in fact, we surely need the check when we create a new
+ * temporary address due to deprecation of an old temporary address.
+ */
+ pfxlist_onlink_check(1);
+ lck_mtx_unlock(nd6_mutex);
+
return(0);
}
int
in6_init_prefix_ltimes(struct nd_prefix *ndpr)
{
+ struct timeval timenow;
+
+ getmicrotime(&timenow);
/* check if preferred lifetime > valid lifetime. RFC2462 5.5.3 (c) */
if (ndpr->ndpr_pltime > ndpr->ndpr_vltime) {
nd6log((LOG_INFO, "in6_init_prefix_ltimes: preferred lifetime"
if (ndpr->ndpr_pltime == ND6_INFINITE_LIFETIME)
ndpr->ndpr_preferred = 0;
else
- ndpr->ndpr_preferred = time_second + ndpr->ndpr_pltime;
+ ndpr->ndpr_preferred = timenow.tv_sec + 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 = timenow.tv_sec + ndpr->ndpr_vltime;
return 0;
}
static void
-in6_init_address_ltimes(struct nd_prefix *new, struct in6_addrlifetime *lt6)
+in6_init_address_ltimes(__unused struct nd_prefix *new, struct in6_addrlifetime *lt6)
{
+ struct timeval timenow;
+
+ getmicrotime(&timenow);
/* Valid lifetime must not be updated unless explicitly specified. */
/* init ia6t_expire */
if (lt6->ia6t_vltime == ND6_INFINITE_LIFETIME)
lt6->ia6t_expire = 0;
else {
- lt6->ia6t_expire = time_second;
+ lt6->ia6t_expire = timenow.tv_sec;
lt6->ia6t_expire += lt6->ia6t_vltime;
}
if (lt6->ia6t_pltime == ND6_INFINITE_LIFETIME)
lt6->ia6t_preferred = 0;
else {
- lt6->ia6t_preferred = time_second;
+ lt6->ia6t_preferred = timenow.tv_sec;
lt6->ia6t_preferred += lt6->ia6t_pltime;
}
}
* it shouldn't be called when acting as a router.
*/
void
-rt6_flush(gateway, ifp)
- struct in6_addr *gateway;
- struct ifnet *ifp;
+rt6_flush(
+ struct in6_addr *gateway,
+ struct ifnet *ifp)
{
struct radix_node_head *rnh = rt_tables[AF_INET6];
- int s = splnet();
/* We'll care only link-local addresses */
if (!IN6_IS_ADDR_LINKLOCAL(gateway)) {
- splx(s);
return;
}
+ lck_mtx_lock(rt_mtx);
/* XXX: hack for KAME's link-local address kludge */
gateway->s6_addr16[1] = htons(ifp->if_index);
rnh->rnh_walktree(rnh, rt6_deleteroute, (void *)gateway);
- splx(s);
+ lck_mtx_unlock(rt_mtx);
}
static int
-rt6_deleteroute(rn, arg)
- struct radix_node *rn;
- void *arg;
+rt6_deleteroute(
+ struct radix_node *rn,
+ void *arg)
{
#define SIN6(s) ((struct sockaddr_in6 *)s)
struct rtentry *rt = (struct rtentry *)rn;
struct in6_addr *gate = (struct in6_addr *)arg;
+ lck_mtx_assert(rt_mtx, LCK_MTX_ASSERT_OWNED);
+
if (rt->rt_gateway == NULL || rt->rt_gateway->sa_family != AF_INET6)
return(0);
if ((rt->rt_flags & RTF_HOST) == 0)
return(0);
- return(rtrequest(RTM_DELETE, rt_key(rt),
+ return(rtrequest_locked(RTM_DELETE, rt_key(rt),
rt->rt_gateway, rt_mask(rt), rt->rt_flags, 0));
#undef SIN6
}
int
-nd6_setdefaultiface(ifindex)
- int ifindex;
+nd6_setdefaultiface(
+ int ifindex)
{
int error = 0;
if (ifindex < 0 || if_index < ifindex)
return(EINVAL);
+ lck_mtx_lock(nd6_mutex);
if (nd6_defifindex != ifindex) {
nd6_defifindex = ifindex;
if (nd6_defifindex > 0)
scope6_setdefault(nd6_defifp);
}
+ lck_mtx_unlock(nd6_mutex);
return(error);
}