X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/9bccf70c0258c7cac2dcb80011b2a964d884c552..d1ecb069dfe24481e4a83f44cb5217a2b06746d7:/bsd/netinet6/nd6_nbr.c diff --git a/bsd/netinet6/nd6_nbr.c b/bsd/netinet6/nd6_nbr.c index 70fbfef80..5b0d744a6 100644 --- a/bsd/netinet6/nd6_nbr.c +++ b/bsd/netinet6/nd6_nbr.c @@ -1,3 +1,30 @@ +/* + * Copyright (c) 2009 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_nbr.c,v 1.4.2.4 2001/07/06 05:32:25 sumikawa Exp $ */ /* $KAME: nd6_nbr.c,v 1.64 2001/05/17 03:48:30 itojun Exp $ */ @@ -68,22 +95,23 @@ extern int ipsec_bypass; #define SDL(s) ((struct sockaddr_dl *)s) struct dadq; -static struct dadq *nd6_dad_find __P((struct ifaddr *)); +static struct dadq *nd6_dad_find(struct ifaddr *); #ifndef __APPLE__ -static void nd6_dad_starttimer __P((struct dadq *, int)); -static void nd6_dad_stoptimer __P((struct dadq *)); +static void nd6_dad_starttimer(struct dadq *, int); +static void nd6_dad_stoptimer(struct dadq *); #else -void nd6_dad_stoptimer __P((struct ifaddr *)); +void nd6_dad_stoptimer(struct ifaddr *); #endif -static void nd6_dad_timer __P((struct ifaddr *)); -static void nd6_dad_timer_funnel __P((struct ifaddr *)); -static void nd6_dad_ns_output __P((struct dadq *, struct ifaddr *)); -static void nd6_dad_ns_input __P((struct ifaddr *)); -static void nd6_dad_na_input __P((struct ifaddr *)); +static void nd6_dad_timer(struct ifaddr *); +static void nd6_dad_ns_output(struct dadq *, struct ifaddr *); +static void nd6_dad_ns_input(struct ifaddr *); +static void nd6_dad_na_input(struct ifaddr *); static int dad_ignore_ns = 0; /* ignore NS in DAD - specwise incorrect*/ static int dad_maxtry = 15; /* max # of *tries* to transmit DAD packet */ +extern lck_mtx_t *dad6_mutex; +extern lck_mtx_t *nd6_mutex; /* * Input an Neighbor Solicitation Message. * @@ -91,9 +119,10 @@ static int dad_maxtry = 15; /* max # of *tries* to transmit DAD packet */ * Based on RFC 2462 (duplicated address detection) */ void -nd6_ns_input(m, off, icmp6len) - struct mbuf *m; - int off, icmp6len; +nd6_ns_input( + struct mbuf *m, + int off, + int icmp6len) { struct ifnet *ifp = m->m_pkthdr.rcvif; struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); @@ -103,15 +132,15 @@ nd6_ns_input(m, off, icmp6len) struct in6_addr taddr6; struct in6_addr myaddr6; char *lladdr = NULL; - struct ifaddr *ifa; + struct ifaddr *ifa = NULL; int lladdrlen = 0; int anycast = 0, proxy = 0, tentative = 0; int tlladdr; union nd_opts ndopts; - struct sockaddr_dl *proxydl = NULL; + struct sockaddr_dl proxydl; #ifndef PULLDOWN_TEST - IP6_EXTHDR_CHECK(m, off, icmp6len,); + IP6_EXTHDR_CHECK(m, off, icmp6len, return); nd_ns = (struct nd_neighbor_solicit *)((caddr_t)ip6 + off); #else IP6_EXTHDR_GET(nd_ns, struct nd_neighbor_solicit *, m, off, icmp6len); @@ -134,11 +163,11 @@ nd6_ns_input(m, off, icmp6len) if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) { /* dst has to be solicited node multicast address. */ if (daddr6.s6_addr16[0] == IPV6_ADDR_INT16_MLL - /*don't check ifindex portion*/ + /* don't check ifindex portion */ && daddr6.s6_addr32[1] == 0 && daddr6.s6_addr32[2] == IPV6_ADDR_INT32_ONE && daddr6.s6_addr8[12] == 0xff) { - ; /*good*/ + ; /* good */ } else { nd6log((LOG_INFO, "nd6_ns_input: bad DAD packet " "(wrong ip6 dst)\n")); @@ -164,7 +193,7 @@ nd6_ns_input(m, off, icmp6len) } if (ndopts.nd_opts_src_lladdr) { - lladdr = (char *)(ndopts.nd_opts_src_lladdr +1); + lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1); lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3; } @@ -215,20 +244,23 @@ nd6_ns_input(m, off, icmp6len) tsin6.sin6_addr = taddr6; rt = rtalloc1((struct sockaddr *)&tsin6, 0, 0); - if (rt && (rt->rt_flags & RTF_ANNOUNCE) != 0 && - rt->rt_gateway->sa_family == AF_LINK) { - /* - * proxy NDP for single entry - */ - ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, - IN6_IFF_NOTREADY|IN6_IFF_ANYCAST); - if (ifa) { - proxy = 1; - proxydl = SDL(rt->rt_gateway); + if (rt != NULL) { + RT_LOCK(rt); + if ((rt->rt_flags & RTF_ANNOUNCE) != 0 && + rt->rt_gateway->sa_family == AF_LINK) { + /* + * proxy NDP for single entry + */ + ifa = (struct ifaddr *)in6ifa_ifpforlinklocal( + ifp, IN6_IFF_NOTREADY|IN6_IFF_ANYCAST); + if (ifa) { + proxy = 1; + proxydl = *SDL(rt->rt_gateway); + } } - } - if (rt) + RT_UNLOCK(rt); rtfree(rt); + } } if (!ifa) { /* @@ -253,9 +285,9 @@ nd6_ns_input(m, off, icmp6len) } if (IN6_ARE_ADDR_EQUAL(&myaddr6, &saddr6)) { - log(LOG_INFO, - "nd6_ns_input: duplicate IP6 address %s\n", - ip6_sprintf(&saddr6)); + nd6log((LOG_INFO, + "nd6_ns_input: duplicate IP6 address %s\n", + ip6_sprintf(&saddr6))); goto freeit; } @@ -300,7 +332,7 @@ nd6_ns_input(m, off, icmp6len) ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) | (ip6_forwarding ? ND_NA_FLAG_ROUTER : 0), - tlladdr, (struct sockaddr *)proxydl); + tlladdr, proxy ? (struct sockaddr *)&proxydl : NULL); goto freeit; } @@ -310,9 +342,11 @@ nd6_ns_input(m, off, icmp6len) ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) | (ip6_forwarding ? ND_NA_FLAG_ROUTER : 0) | ND_NA_FLAG_SOLICITED, - tlladdr, (struct sockaddr *)proxydl); + tlladdr, proxy ? (struct sockaddr *)&proxydl : NULL); freeit: m_freem(m); + if (ifa != NULL) + ifafree(ifa); return; bad: @@ -321,6 +355,8 @@ nd6_ns_input(m, off, icmp6len) nd6log((LOG_ERR, "nd6_ns_input: tgt=%s\n", ip6_sprintf(&taddr6))); icmp6stat.icp6s_badns++; m_freem(m); + if (ifa != NULL) + ifafree(ifa); } /* @@ -331,13 +367,18 @@ nd6_ns_input(m, off, icmp6len) * * Based on RFC 2461 * Based on RFC 2462 (duplicated address detection) + * + * Caller must bump up ln->ln_rt refcnt to make sure 'ln' doesn't go + * away if there is a llinfo_nd6 passed in. */ void -nd6_ns_output(ifp, daddr6, taddr6, ln, dad) - struct ifnet *ifp; - const struct in6_addr *daddr6, *taddr6; - struct llinfo_nd6 *ln; /* for source address determination */ - int dad; /* duplicated address detection */ +nd6_ns_output( + struct ifnet *ifp, + const struct in6_addr *daddr6, + const struct in6_addr *taddr6, + struct llinfo_nd6 *ln, /* for source address determination */ + int dad, /* duplicated address detection */ + int locked) { struct mbuf *m; struct ip6_hdr *ip6; @@ -363,7 +404,7 @@ nd6_ns_output(ifp, daddr6, taddr6, ln, dad) return; } - MGETHDR(m, M_DONTWAIT, MT_DATA); + MGETHDR(m, M_DONTWAIT, MT_DATA); /* XXXMAC: mac_create_mbuf_linklayer() probably */ if (m && max_linkhdr + maxlen >= MHLEN) { MCLGET(m, M_DONTWAIT); if ((m->m_flags & M_EXT) == 0) { @@ -384,7 +425,7 @@ nd6_ns_output(ifp, daddr6, taddr6, ln, dad) icmp6len = sizeof(*nd_ns); m->m_pkthdr.len = m->m_len = sizeof(*ip6) + icmp6len; - m->m_data += max_linkhdr; /*or MH_ALIGN() equivalent?*/ + m->m_data += max_linkhdr; /* or MH_ALIGN() equivalent? */ /* fill neighbor solicitation packet */ ip6 = mtod(m, struct ip6_hdr *); @@ -434,28 +475,54 @@ nd6_ns_output(ifp, daddr6, taddr6, ln, dad) * - saddr6 belongs to the outgoing interface. * Otherwise, we perform a scope-wise match. */ - struct ip6_hdr *hip6; /*hold ip6*/ - struct in6_addr *saddr6; - - if (ln && ln->ln_hold) { - hip6 = mtod(ln->ln_hold, struct ip6_hdr *); - /* XXX pullup? */ - if (sizeof(*hip6) < ln->ln_hold->m_len) - saddr6 = &hip6->ip6_src; - else - saddr6 = NULL; - } else - saddr6 = NULL; - if (saddr6 && in6ifa_ifpwithaddr(ifp, saddr6)) - bcopy(saddr6, &ip6->ip6_src, sizeof(*saddr6)); - else { + struct ip6_hdr *hip6 = NULL; /* hold ip6 */ + struct in6_addr saddr6; + + /* Caller holds ref on this route */ + if (ln != NULL) { + RT_LOCK(ln->ln_rt); + if (ln->ln_hold != NULL) { + hip6 = mtod(ln->ln_hold, struct ip6_hdr *); + /* XXX pullup? */ + if (sizeof (*hip6) < ln->ln_hold->m_len) + saddr6 = hip6->ip6_src; + else + hip6 = NULL; + } + /* + * hip6 is used only to indicate whether or + * not there is a valid source address from + * the held packet in ln_hold. For obvious + * reasons we should not dereference it after + * releasing the lock though we can simply + * test if it's non-NULL. + */ + RT_UNLOCK(ln->ln_rt); + } + + if (ia != NULL) + ifafree(&ia->ia_ifa); + if (hip6 != NULL && (ia = in6ifa_ifpwithaddr(ifp, &saddr6))) { + bcopy(&saddr6, &ip6->ip6_src, sizeof (saddr6)); + } else { ia = in6_ifawithifp(ifp, &ip6->ip6_dst); if (ia == NULL) { - m_freem(m); /*XXX*/ + if (ln != NULL) { + RT_LOCK(ln->ln_rt); + if (ln->ln_hold != NULL) + m_freem(ln->ln_hold); + ln->ln_hold = NULL; + RT_UNLOCK(ln->ln_rt); + } + m_freem(m); return; } ip6->ip6_src = ia->ia_addr.sin6_addr; } + if (ia != NULL) { + ifafree(&ia->ia_ifa); + ia = NULL; + } #endif } else { /* @@ -510,7 +577,7 @@ nd6_ns_output(ifp, daddr6, taddr6, ln, dad) if (ipsec_bypass == 0) (void)ipsec_setsocket(m, NULL); #endif - ip6_output(m, NULL, NULL, dad ? IPV6_DADOUTPUT : 0, &im6o, &outif); + ip6_output(m, NULL, NULL, dad ? IPV6_DADOUTPUT : 0, &im6o, &outif, locked); if (outif) { icmp6_ifstat_inc(outif, ifs6_out_msg); icmp6_ifstat_inc(outif, ifs6_out_neighborsolicit); @@ -529,9 +596,10 @@ nd6_ns_output(ifp, daddr6, taddr6, ln, dad) * - anycast advertisement delay rule (RFC2461 7.2.7, SHOULD) */ void -nd6_na_input(m, off, icmp6len) - struct mbuf *m; - int off, icmp6len; +nd6_na_input( + struct mbuf *m, + int off, + int icmp6len) { struct ifnet *ifp = m->m_pkthdr.rcvif; struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); @@ -547,11 +615,12 @@ nd6_na_input(m, off, icmp6len) int is_override; char *lladdr = NULL; int lladdrlen = 0; - struct ifaddr *ifa; + struct ifaddr *ifa = NULL; struct llinfo_nd6 *ln; struct rtentry *rt; struct sockaddr_dl *sdl; union nd_opts ndopts; + struct timeval timenow; if (ip6->ip6_hlim != 255) { nd6log((LOG_ERR, @@ -562,7 +631,7 @@ nd6_na_input(m, off, icmp6len) } #ifndef PULLDOWN_TEST - IP6_EXTHDR_CHECK(m, off, icmp6len,); + IP6_EXTHDR_CHECK(m, off, icmp6len, return); nd_na = (struct nd_neighbor_advert *)((caddr_t)ip6 + off); #else IP6_EXTHDR_GET(nd_na, struct nd_neighbor_advert *, m, off, icmp6len); @@ -624,7 +693,7 @@ nd6_na_input(m, off, icmp6len) goto freeit; } - /* Just for safety, maybe unnecessery. */ + /* Just for safety, maybe unnecessary. */ if (ifa) { log(LOG_ERR, "nd6_na_input: duplicate IP6 address %s\n", @@ -642,20 +711,30 @@ nd6_na_input(m, off, icmp6len) /* * If no neighbor cache entry is found, NA SHOULD silently be discarded. + * Callee returns a locked route upon success. */ - rt = nd6_lookup(&taddr6, 0, ifp); - if ((rt == NULL) || - ((ln = (struct llinfo_nd6 *)rt->rt_llinfo) == NULL) || - ((sdl = SDL(rt->rt_gateway)) == NULL)) + if ((rt = nd6_lookup(&taddr6, 0, ifp, 0)) == NULL) goto freeit; + RT_LOCK_ASSERT_HELD(rt); + if ((ln = rt->rt_llinfo) == NULL || + (sdl = SDL(rt->rt_gateway)) == NULL) { + RT_REMREF_LOCKED(rt); + RT_UNLOCK(rt); + goto freeit; + } + + getmicrotime(&timenow); if (ln->ln_state == ND6_LLINFO_INCOMPLETE) { /* * If the link-layer has address, and no lladdr option came, * discard the packet. */ - if (ifp->if_addrlen && !lladdr) + if (ifp->if_addrlen && !lladdr) { + RT_REMREF_LOCKED(rt); + RT_UNLOCK(rt); goto freeit; + } /* * Record link-layer address, and update the state. @@ -665,12 +744,16 @@ nd6_na_input(m, off, icmp6len) if (is_solicited) { ln->ln_state = ND6_LLINFO_REACHABLE; ln->ln_byhint = 0; - if (ln->ln_expire) - ln->ln_expire = time_second + - nd_ifinfo[rt->rt_ifp->if_index].reachable; + if (ln->ln_expire) { + lck_rw_lock_shared(nd_if_rwlock); + ln->ln_expire = rt_expiry(rt, timenow.tv_sec, + nd_ifinfo[rt->rt_ifp->if_index].reachable); + lck_rw_done(nd_if_rwlock); + } } else { ln->ln_state = ND6_LLINFO_STALE; - ln->ln_expire = time_second + nd6_gctimer; + ln->ln_expire = rt_expiry(rt, timenow.tv_sec, + nd6_gctimer); } if ((ln->ln_router = is_router) != 0) { /* @@ -678,7 +761,9 @@ nd6_na_input(m, off, icmp6len) * non-reachable to probably reachable, and might * affect the status of associated prefixes.. */ - pfxlist_onlink_check(); + RT_UNLOCK(rt); + pfxlist_onlink_check(0); + RT_LOCK(rt); } } else { int llchange; @@ -724,8 +809,11 @@ nd6_na_input(m, off, icmp6len) */ if (ln->ln_state == ND6_LLINFO_REACHABLE) { ln->ln_state = ND6_LLINFO_STALE; - ln->ln_expire = time_second + nd6_gctimer; + ln->ln_expire = rt_expiry(rt, timenow.tv_sec, + nd6_gctimer); } + RT_REMREF_LOCKED(rt); + RT_UNLOCK(rt); goto freeit; } else if (is_override /* (2a) */ || (!is_override && (lladdr && !llchange)) /* (2b) */ @@ -747,13 +835,17 @@ nd6_na_input(m, off, icmp6len) ln->ln_state = ND6_LLINFO_REACHABLE; ln->ln_byhint = 0; if (ln->ln_expire) { - ln->ln_expire = time_second + - nd_ifinfo[ifp->if_index].reachable; + lck_rw_lock_shared(nd_if_rwlock); + ln->ln_expire = + rt_expiry(rt, timenow.tv_sec, + nd_ifinfo[ifp->if_index].reachable); + lck_rw_done(nd_if_rwlock); } } else { if (lladdr && llchange) { ln->ln_state = ND6_LLINFO_STALE; - ln->ln_expire = time_second + nd6_gctimer; + ln->ln_expire = rt_expiry(rt, + timenow.tv_sec, nd6_gctimer); } } } @@ -766,46 +858,69 @@ nd6_na_input(m, off, icmp6len) */ struct nd_defrouter *dr; struct in6_addr *in6; - int s; + struct ifnet *rt_ifp = rt->rt_ifp; in6 = &((struct sockaddr_in6 *)rt_key(rt))->sin6_addr; - s = splnet(); - dr = defrouter_lookup(in6, rt->rt_ifp); - if (dr) - defrtrlist_del(dr); - else if (!ip6_forwarding && ip6_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, rt->rt_ifp); + + /* + * 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. + */ + RT_UNLOCK(rt); + lck_mtx_lock(nd6_mutex); + dr = defrouter_lookup(in6, rt_ifp); + if (dr) { + defrtrlist_del(dr, 1); + lck_mtx_unlock(nd6_mutex); + } + else { + lck_mtx_unlock(nd6_mutex); + if (!ip6_forwarding && (ip6_accept_rtadv || (rt_ifp->if_eflags & IFEF_ACCEPT_RTADVD))) { + /* + * 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, rt_ifp); + } } - splx(s); + RT_LOCK(rt); } ln->ln_router = is_router; } + RT_LOCK_ASSERT_HELD(rt); rt->rt_flags &= ~RTF_REJECT; ln->ln_asked = 0; - if (ln->ln_hold) { + if (ln->ln_hold != NULL) { + struct mbuf *n = ln->ln_hold; + ln->ln_hold = NULL; /* - * we assume ifp is not a p2p here, so just set the 2nd + * we assume ifp is not a loopback here, so just set the 2nd * argument as the 1st one. */ - nd6_output(ifp, ifp, ln->ln_hold, - (struct sockaddr_in6 *)rt_key(rt), rt); - ln->ln_hold = 0; + RT_UNLOCK(rt); + nd6_output(ifp, ifp, n, (struct sockaddr_in6 *)rt_key(rt), + rt, 0); + RT_LOCK_SPIN(rt); } + RT_REMREF_LOCKED(rt); + RT_UNLOCK(rt); freeit: m_freem(m); + if (ifa != NULL) + ifafree(ifa); return; bad: icmp6stat.icp6s_badna++; m_freem(m); + if (ifa != NULL) + ifafree(ifa); } /* @@ -818,12 +933,13 @@ nd6_na_input(m, off, icmp6len) * - anycast advertisement delay rule (RFC2461 7.2.7, SHOULD) */ void -nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0) - struct ifnet *ifp; - const struct in6_addr *daddr6, *taddr6; - u_long flags; - int tlladdr; /* 1 if include target link-layer address */ - struct sockaddr *sdl0; /* sockaddr_dl (= proxy NA) or NULL */ +nd6_na_output( + struct ifnet *ifp, + const struct in6_addr *daddr6, + const struct in6_addr *taddr6, + uint32_t flags, + int tlladdr, /* 1 if include target link-layer address */ + struct sockaddr *sdl0) /* sockaddr_dl (= proxy NA) or NULL */ { struct mbuf *m; struct ip6_hdr *ip6; @@ -832,7 +948,7 @@ nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0) struct ip6_moptions im6o; int icmp6len; int maxlen; - caddr_t mac; + caddr_t mac = NULL; struct ifnet *outif = NULL; /* estimate the size of message */ @@ -846,7 +962,7 @@ nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0) return; } - MGETHDR(m, M_DONTWAIT, MT_DATA); + MGETHDR(m, M_DONTWAIT, MT_DATA); /* XXXMAC: mac_create_mbuf_linklayer() probably */ if (m && max_linkhdr + maxlen >= MHLEN) { MCLGET(m, M_DONTWAIT); if ((m->m_flags & M_EXT) == 0) { @@ -867,7 +983,7 @@ nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0) icmp6len = sizeof(*nd_na); m->m_pkthdr.len = m->m_len = sizeof(struct ip6_hdr) + icmp6len; - m->m_data += max_linkhdr; /*or MH_ALIGN() equivalent?*/ + m->m_data += max_linkhdr; /* or MH_ALIGN() equivalent? */ /* fill neighbor advertisement packet */ ip6 = mtod(m, struct ip6_hdr *); @@ -896,6 +1012,9 @@ nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0) return; } ip6->ip6_src = ia->ia_addr.sin6_addr; + ifafree(&ia->ia_ifa); + ia = NULL; + nd_na = (struct nd_neighbor_advert *)(ip6 + 1); nd_na->nd_na_type = ND_NEIGHBOR_ADVERT; nd_na->nd_na_code = 0; @@ -910,7 +1029,6 @@ nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0) * target lladdr option SHOULD NOT be included. */ if (tlladdr) { - mac = NULL; /* * sdl0 != NULL indicates proxy NA. If we do proxy, use * lladdr in sdl0. If we are not proxying (sending NA for @@ -953,7 +1071,7 @@ nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0) if (ipsec_bypass == 0) (void)ipsec_setsocket(m, NULL); #endif - ip6_output(m, NULL, NULL, 0, &im6o, &outif); + ip6_output(m, NULL, NULL, 0, &im6o, &outif, 0); if (outif) { icmp6_ifstat_inc(outif, ifs6_out_msg); icmp6_ifstat_inc(outif, ifs6_out_neighboradvert); @@ -962,25 +1080,10 @@ nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0) } caddr_t -nd6_ifptomac(ifp) - struct ifnet *ifp; +nd6_ifptomac( + struct ifnet *ifp) { - switch (ifp->if_type) { - case IFT_ARCNET: - case IFT_ETHER: - case IFT_FDDI: - case IFT_IEEE1394: -#if IFT_L2VLAN - case IFT_L2VLAN: -#endif -#if IFT_IEEE80211 - case IFT_IEEE80211: -#endif - return ((caddr_t)(ifp + 1)); - break; - default: - return NULL; - } + return ((caddr_t)ifnet_lladdr(ifp)); } TAILQ_HEAD(dadq_head, dadq); @@ -992,48 +1095,49 @@ struct dadq { int dad_ns_ocount; /* NS sent so far */ int dad_ns_icount; int dad_na_icount; -#if defined(__FreeBSD__) && __FreeBSD__ >= 3 - struct callout_handle dad_timer; -#endif }; static struct dadq_head dadq; static int dad_init = 0; static struct dadq * -nd6_dad_find(ifa) - struct ifaddr *ifa; +nd6_dad_find( + struct ifaddr *ifa) { struct dadq *dp; - + lck_mtx_lock(dad6_mutex); for (dp = dadq.tqh_first; dp; dp = dp->dad_list.tqe_next) { - if (dp->dad_ifa == ifa) + if (dp->dad_ifa == ifa) { + lck_mtx_unlock(dad6_mutex); return dp; + } } + lck_mtx_unlock(dad6_mutex); return NULL; } #ifdef __APPLE__ void -nd6_dad_stoptimer(ifa) - struct ifaddr *ifa; +nd6_dad_stoptimer( + struct ifaddr *ifa) { - untimeout((void (*) __P((void *)))nd6_dad_timer_funnel, (void *)ifa); + untimeout((void (*)(void *))nd6_dad_timer, (void *)ifa); } #else static void -nd6_dad_starttimer(dp, ticks) - struct dadq *dp; - int ticks; +nd6_dad_starttimer( + struct dadq *dp, + int ticks) { callout_reset(&dp->dad_timer_ch, ticks, - (void (*) __P((void *)))nd6_dad_timer, (void *)dp->dad_ifa); + (void (*)(void *))nd6_dad_timer, (void *)dp->dad_ifa); } + static void -nd6_dad_stoptimer(dp) - struct dadq *dp; +nd6_dad_stoptimer( + struct dadq *dp) { callout_stop(&dp->dad_timer_ch); @@ -1044,9 +1148,9 @@ nd6_dad_stoptimer(dp) * Start Duplicated Address Detection (DAD) for specified interface address. */ void -nd6_dad_start(ifa, tick) - struct ifaddr *ifa; - int *tick; /* minimum delay ticks for IFF_UP event */ +nd6_dad_start( + struct ifaddr *ifa, + int *tick_delay) /* minimum delay ticks for IFF_UP event */ { struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa; struct dadq *dp; @@ -1096,10 +1200,9 @@ nd6_dad_start(ifa, tick) return; } bzero(dp, sizeof(*dp)); -#if defined(__FreeBSD__) && __FreeBSD__ >= 3 - callout_init(&dp->dad_timer_ch); -#endif + lck_mtx_lock(dad6_mutex); TAILQ_INSERT_TAIL(&dadq, (struct dadq *)dp, dad_list); + lck_mtx_unlock(dad6_mutex); nd6log((LOG_DEBUG, "%s: starting DAD for %s\n", if_name(ifa->ifa_ifp), ip6_sprintf(&ia->ia_addr.sin6_addr))); @@ -1115,25 +1218,22 @@ nd6_dad_start(ifa, tick) dp->dad_count = ip6_dad_count; dp->dad_ns_icount = dp->dad_na_icount = 0; dp->dad_ns_ocount = dp->dad_ns_tcount = 0; - if (!tick) { + if (tick_delay == NULL) { + u_int32_t retrans; nd6_dad_ns_output(dp, ifa); -#if defined(__FreeBSD__) && __FreeBSD__ >= 3 - dp->dad_timer = -#endif - timeout((void (*) __P((void *)))nd6_dad_timer_funnel, (void *)ifa, - nd_ifinfo[ifa->ifa_ifp->if_index].retrans * hz / 1000); + lck_rw_lock_shared(nd_if_rwlock); + retrans = nd_ifinfo[ifa->ifa_ifp->if_index].retrans * hz / 1000; + lck_rw_done(nd_if_rwlock); + timeout((void (*)(void *))nd6_dad_timer, (void *)ifa, retrans); } else { int ntick; - if (*tick == 0) + if (*tick_delay == 0) ntick = random() % (MAX_RTR_SOLICITATION_DELAY * hz); else - ntick = *tick + random() % (hz / 2); - *tick = ntick; -#if defined(__FreeBSD__) && __FreeBSD__ >= 3 - dp->dad_timer = -#endif - timeout((void (*) __P((void *)))nd6_dad_timer_funnel, (void *)ifa, + ntick = *tick_delay + random() % (hz / 2); + *tick_delay = ntick; + timeout((void (*)(void *))nd6_dad_timer, (void *)ifa, ntick); } } @@ -1142,8 +1242,8 @@ nd6_dad_start(ifa, tick) * terminate DAD unconditionally. used for address removals. */ void -nd6_dad_stop(ifa) - struct ifaddr *ifa; +nd6_dad_stop( + struct ifaddr *ifa) { struct dadq *dp; @@ -1155,41 +1255,24 @@ nd6_dad_stop(ifa) return; } - untimeout((void (*) __P((void *)))nd6_dad_timer_funnel, (void *)ifa); + untimeout((void (*)(void *))nd6_dad_timer, (void *)ifa); + lck_mtx_lock(dad6_mutex); TAILQ_REMOVE(&dadq, (struct dadq *)dp, dad_list); + lck_mtx_unlock(dad6_mutex); FREE(dp, M_IP6NDP); dp = NULL; ifafree(ifa); } -static void -nd6_dad_timer_funnel(ifa) - struct ifaddr *ifa; -{ - -#ifdef __APPLE__ - boolean_t funnel_state; - funnel_state = thread_funnel_set(network_flock, TRUE); -#endif - nd6_dad_timer(ifa); -#ifdef __APPLE__ - (void) thread_funnel_set(network_flock, FALSE); -#endif - -} - static void -nd6_dad_timer(ifa) - struct ifaddr *ifa; +nd6_dad_timer( + struct ifaddr *ifa) { - int s; struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa; struct dadq *dp; - s = splnet(); /*XXX*/ - /* Sanity check */ if (ia == NULL) { log(LOG_ERR, "nd6_dad_timer: called with null parameter\n"); @@ -1220,7 +1303,9 @@ nd6_dad_timer(ifa) nd6log((LOG_INFO, "%s: could not run DAD, driver problem?\n", if_name(ifa->ifa_ifp))); + lck_mtx_lock(dad6_mutex); TAILQ_REMOVE(&dadq, (struct dadq *)dp, dad_list); + lck_mtx_unlock(dad6_mutex); FREE(dp, M_IP6NDP); dp = NULL; ifafree(ifa); @@ -1229,15 +1314,15 @@ nd6_dad_timer(ifa) /* Need more checks? */ if (dp->dad_ns_ocount < dp->dad_count) { + u_int32_t retrans; /* * We have more NS to go. Send NS packet for DAD. */ nd6_dad_ns_output(dp, ifa); -#if defined(__FreeBSD__) && __FreeBSD__ >= 3 - dp->dad_timer = -#endif - timeout((void (*) __P((void *)))nd6_dad_timer_funnel, (void *)ifa, - nd_ifinfo[ifa->ifa_ifp->if_index].retrans * hz / 1000); + lck_rw_lock_shared(nd_if_rwlock); + retrans = nd_ifinfo[ifa->ifa_ifp->if_index].retrans * hz / 1000; + lck_rw_done(nd_if_rwlock); + timeout((void (*)(void *))nd6_dad_timer, (void *)ifa, retrans); } else { /* * We have transmitted sufficient number of DAD packets. @@ -1256,7 +1341,7 @@ nd6_dad_timer(ifa) } if (dp->dad_ns_icount) { -#if 0 /*heuristics*/ +#if 0 /* heuristics */ /* * if * - we have sent many(?) DAD NS, and @@ -1301,7 +1386,10 @@ nd6_dad_timer(ifa) if_name(ifa->ifa_ifp), ip6_sprintf(&ia->ia_addr.sin6_addr))); + lck_mtx_lock(dad6_mutex); TAILQ_REMOVE(&dadq, (struct dadq *)dp, dad_list); + lck_mtx_unlock(dad6_mutex); + in6_post_msg(ia->ia_ifp, KEV_INET6_NEW_USER_ADDR, ia); FREE(dp, M_IP6NDP); dp = NULL; ifafree(ifa); @@ -1309,12 +1397,12 @@ nd6_dad_timer(ifa) } done: - splx(s); + return; } void -nd6_dad_duplicated(ifa) - struct ifaddr *ifa; +nd6_dad_duplicated( + struct ifaddr *ifa) { struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa; struct dadq *dp; @@ -1334,7 +1422,7 @@ nd6_dad_duplicated(ifa) ia->ia6_flags |= IN6_IFF_DUPLICATED; /* We are done with DAD, with duplicated address found. (failure) */ - untimeout((void (*) __P((void *)))nd6_dad_timer_funnel, (void *)ifa); + untimeout((void (*)(void *))nd6_dad_timer, (void *)ifa); log(LOG_ERR, "%s: DAD complete for %s - duplicate found\n", @@ -1342,16 +1430,18 @@ nd6_dad_duplicated(ifa) log(LOG_ERR, "%s: manual intervention required\n", if_name(ifa->ifa_ifp)); + lck_mtx_lock(dad6_mutex); TAILQ_REMOVE(&dadq, (struct dadq *)dp, dad_list); + lck_mtx_unlock(dad6_mutex); FREE(dp, M_IP6NDP); dp = NULL; ifafree(ifa); } static void -nd6_dad_ns_output(dp, ifa) - struct dadq *dp; - struct ifaddr *ifa; +nd6_dad_ns_output( + struct dadq *dp, + struct ifaddr *ifa) { struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa; struct ifnet *ifp = ifa->ifa_ifp; @@ -1371,15 +1461,14 @@ nd6_dad_ns_output(dp, ifa) } dp->dad_ns_ocount++; - nd6_ns_output(ifp, NULL, &ia->ia_addr.sin6_addr, NULL, 1); + nd6_ns_output(ifp, NULL, &ia->ia_addr.sin6_addr, NULL, 1, 0); } static void -nd6_dad_ns_input(ifa) - struct ifaddr *ifa; +nd6_dad_ns_input( + struct ifaddr *ifa) { struct in6_ifaddr *ia; - struct ifnet *ifp; const struct in6_addr *taddr6; struct dadq *dp; int duplicate; @@ -1388,7 +1477,6 @@ nd6_dad_ns_input(ifa) panic("ifa == NULL in nd6_dad_ns_input"); ia = (struct in6_ifaddr *)ifa; - ifp = ifa->ifa_ifp; taddr6 = &ia->ia_addr.sin6_addr; duplicate = 0; dp = nd6_dad_find(ifa); @@ -1425,8 +1513,8 @@ nd6_dad_ns_input(ifa) } static void -nd6_dad_na_input(ifa) - struct ifaddr *ifa; +nd6_dad_na_input( + struct ifaddr *ifa) { struct dadq *dp;