X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/1c79356b52d46aa6b508fb032f5ae709b1f2897b..d1ecb069dfe24481e4a83f44cb5217a2b06746d7:/bsd/netinet6/nd6_nbr.c diff --git a/bsd/netinet6/nd6_nbr.c b/bsd/netinet6/nd6_nbr.c index eca3501f1..5b0d744a6 100644 --- a/bsd/netinet6/nd6_nbr.c +++ b/bsd/netinet6/nd6_nbr.c @@ -1,4 +1,32 @@ -/* $KAME: nd6_nbr.c,v 1.32 2000/03/21 11:37:30 itojun Exp $ */ +/* + * 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 $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -29,13 +57,6 @@ * SUCH DAMAGE. */ -#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(__NetBSD__) -#include "opt_inet.h" -#if __NetBSD__ /*XXX*/ -#include "opt_ipsec.h" -#endif -#endif - #include #include #include @@ -45,9 +66,6 @@ #include #include #include -#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) -#include -#endif #include #include @@ -64,12 +82,12 @@ #include #include -#ifdef __OpenBSD__ /*don't confuse KAME ipsec with OpenBSD ipsec*/ -#undef IPSEC -#endif - #if IPSEC #include +#if INET6 +#include +#endif +extern int ipsec_bypass; #endif #include @@ -77,15 +95,23 @@ #define SDL(s) ((struct sockaddr_dl *)s) struct dadq; -static struct dadq *nd6_dad_find __P((struct ifaddr *)); -static void nd6_dad_timer __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 struct dadq *nd6_dad_find(struct ifaddr *); +#ifndef __APPLE__ +static void nd6_dad_starttimer(struct dadq *, int); +static void nd6_dad_stoptimer(struct dadq *); +#else +void nd6_dad_stoptimer(struct ifaddr *); +#endif +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. * @@ -93,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 *); @@ -105,48 +132,51 @@ 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, return); + nd_ns = (struct nd_neighbor_solicit *)((caddr_t)ip6 + off); +#else + IP6_EXTHDR_GET(nd_ns, struct nd_neighbor_solicit *, m, off, icmp6len); + if (nd_ns == NULL) { + icmp6stat.icp6s_tooshort++; + return; + } +#endif + ip6 = mtod(m, struct ip6_hdr *); /* adjust pointer for safety */ + taddr6 = nd_ns->nd_ns_target; if (ip6->ip6_hlim != 255) { - log(LOG_ERR, - "nd6_ns_input: invalid hlim %d\n", ip6->ip6_hlim); - goto freeit; + nd6log((LOG_ERR, + "nd6_ns_input: invalid hlim (%d) from %s to %s on %s\n", + ip6->ip6_hlim, ip6_sprintf(&ip6->ip6_src), + ip6_sprintf(&ip6->ip6_dst), if_name(ifp))); + goto bad; } 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 { - log(LOG_INFO, "nd6_ns_input: bad DAD packet " - "(wrong ip6 dst)\n"); + nd6log((LOG_INFO, "nd6_ns_input: bad DAD packet " + "(wrong ip6 dst)\n")); goto bad; } } -#ifndef PULLDOWN_TEST - IP6_EXTHDR_CHECK(m, off, icmp6len,); - nd_ns = (struct nd_neighbor_solicit *)((caddr_t)ip6 + off); -#else - IP6_EXTHDR_GET(nd_ns, struct nd_neighbor_solicit *, m, off, icmp6len); - if (nd_ns == NULL) { - icmp6stat.icp6s_tooshort++; - return; - } -#endif - taddr6 = nd_ns->nd_ns_target; - if (IN6_IS_ADDR_MULTICAST(&taddr6)) { - log(LOG_INFO, "nd6_ns_input: bad NS target (multicast)\n"); + nd6log((LOG_INFO, "nd6_ns_input: bad NS target (multicast)\n")); goto bad; } @@ -156,18 +186,20 @@ nd6_ns_input(m, off, icmp6len) icmp6len -= sizeof(*nd_ns); nd6_option_init(nd_ns + 1, icmp6len, &ndopts); if (nd6_options(&ndopts) < 0) { - log(LOG_INFO, "nd6_ns_input: invalid ND option, ignored\n"); - goto bad; + nd6log((LOG_INFO, + "nd6_ns_input: invalid ND option, ignored\n")); + /* nd6_options have incremented stats */ + goto freeit; } 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; } if (IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src) && lladdr) { - log(LOG_INFO, "nd6_ns_input: bad DAD packet " - "(link-layer address option)\n"); + nd6log((LOG_INFO, "nd6_ns_input: bad DAD packet " + "(link-layer address option)\n")); goto bad; } @@ -211,29 +243,28 @@ nd6_ns_input(m, off, icmp6len) tsin6.sin6_family = AF_INET6; tsin6.sin6_addr = taddr6; - rt = rtalloc1((struct sockaddr *)&tsin6, 0 -#if __FreeBSD__ || defined (__APPLE__) - , 0 -#endif /* __FreeBSD__ */ - ); - 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); + rt = rtalloc1((struct sockaddr *)&tsin6, 0, 0); + 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) { /* - * We've got a NS packet, and we don't have that adddress + * We've got an NS packet, and we don't have that adddress * assigned for us. We MUST silently ignore it. * See RFC2461 7.2.3. */ @@ -246,16 +277,17 @@ nd6_ns_input(m, off, icmp6len) goto freeit; if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { - log(LOG_INFO, + nd6log((LOG_INFO, "nd6_ns_input: lladdrlen mismatch for %s " "(if %d, NS packet %d)\n", - ip6_sprintf(&taddr6), ifp->if_addrlen, lladdrlen - 2); + ip6_sprintf(&taddr6), ifp->if_addrlen, lladdrlen - 2)); + goto bad; } 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,16 +342,21 @@ 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: - log(LOG_ERR, "nd6_ns_input: src=%s\n", ip6_sprintf(&saddr6)); - log(LOG_ERR, "nd6_ns_input: dst=%s\n", ip6_sprintf(&daddr6)); - log(LOG_ERR, "nd6_ns_input: tgt=%s\n", ip6_sprintf(&taddr6)); + nd6log((LOG_ERR, "nd6_ns_input: src=%s\n", ip6_sprintf(&saddr6))); + nd6log((LOG_ERR, "nd6_ns_input: dst=%s\n", ip6_sprintf(&daddr6))); + nd6log((LOG_ERR, "nd6_ns_input: tgt=%s\n", ip6_sprintf(&taddr6))); + icmp6stat.icp6s_badns++; m_freem(m); + if (ifa != NULL) + ifafree(ifa); } /* @@ -330,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; - 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; @@ -355,14 +397,14 @@ nd6_ns_output(ifp, daddr6, taddr6, ln, dad) maxlen = sizeof(*ip6) + sizeof(*nd_ns); maxlen += (sizeof(struct nd_opt_hdr) + ifp->if_addrlen + 7) & ~7; if (max_linkhdr + maxlen >= MCLBYTES) { -#ifdef DIAGNOSTIC +#if DIAGNOSTIC printf("nd6_ns_output: max_linkhdr + maxlen >= MCLBYTES " "(%d + %d > %d)\n", max_linkhdr, maxlen, MCLBYTES); #endif 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) { @@ -372,6 +414,7 @@ nd6_ns_output(ifp, daddr6, taddr6, ln, dad) } if (m == NULL) return; + m->m_pkthdr.rcvif = NULL; if (daddr6 == NULL || IN6_IS_ADDR_MULTICAST(daddr6)) { m->m_flags |= M_MCAST; @@ -382,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 *); @@ -432,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 { /* @@ -505,9 +574,10 @@ nd6_ns_output(ifp, daddr6, taddr6, ln, dad) #if IPSEC /* Don't lookup socket */ - ipsec_setsocket(m, NULL); + 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); @@ -526,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 *); @@ -544,20 +615,23 @@ 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) { - log(LOG_ERR, - "nd6_na_input: invalid hlim %d\n", ip6->ip6_hlim); - goto freeit; + nd6log((LOG_ERR, + "nd6_na_input: invalid hlim (%d) from %s to %s on %s\n", + ip6->ip6_hlim, ip6_sprintf(&ip6->ip6_src), + ip6_sprintf(&ip6->ip6_dst), if_name(ifp))); + goto bad; } #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); @@ -576,22 +650,24 @@ nd6_na_input(m, off, icmp6len) taddr6.s6_addr16[1] = htons(ifp->if_index); if (IN6_IS_ADDR_MULTICAST(&taddr6)) { - log(LOG_ERR, + nd6log((LOG_ERR, "nd6_na_input: invalid target address %s\n", - ip6_sprintf(&taddr6)); - goto freeit; + ip6_sprintf(&taddr6))); + goto bad; } if (IN6_IS_ADDR_MULTICAST(&daddr6)) if (is_solicited) { - log(LOG_ERR, - "nd6_na_input: a solicited adv is multicasted\n"); - goto freeit; + nd6log((LOG_ERR, + "nd6_na_input: a solicited adv is multicasted\n")); + goto bad; } icmp6len -= sizeof(*nd_na); nd6_option_init(nd_na + 1, icmp6len, &ndopts); if (nd6_options(&ndopts) < 0) { - log(LOG_INFO, "nd6_na_input: invalid ND option, ignored\n"); + nd6log((LOG_INFO, + "nd6_na_input: invalid ND option, ignored\n")); + /* nd6_options have incremented stats */ goto freeit; } @@ -617,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", @@ -626,28 +702,39 @@ nd6_na_input(m, off, icmp6len) } if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { - log(LOG_INFO, + nd6log((LOG_INFO, "nd6_na_input: lladdrlen mismatch for %s " "(if %d, NA packet %d)\n", - ip6_sprintf(&taddr6), ifp->if_addrlen, lladdrlen - 2); + ip6_sprintf(&taddr6), ifp->if_addrlen, lladdrlen - 2)); + goto bad; } /* * 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. @@ -656,16 +743,28 @@ nd6_na_input(m, off, icmp6len) bcopy(lladdr, LLADDR(sdl), ifp->if_addrlen); if (is_solicited) { ln->ln_state = ND6_LLINFO_REACHABLE; - if (ln->ln_expire) -#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) && !defined(__APPLE__) - ln->ln_expire = time.tv_sec + -#else - ln->ln_expire = time_second + -#endif - nd_ifinfo[rt->rt_ifp->if_index].reachable; - } else + ln->ln_byhint = 0; + 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_router = is_router; + ln->ln_expire = rt_expiry(rt, timenow.tv_sec, + nd6_gctimer); + } + if ((ln->ln_router = is_router) != 0) { + /* + * This means a router's state has changed from + * non-reachable to probably reachable, and might + * affect the status of associated prefixes.. + */ + RT_UNLOCK(rt); + pfxlist_onlink_check(0); + RT_LOCK(rt); + } } else { int llchange; @@ -708,8 +807,13 @@ nd6_na_input(m, off, icmp6len) * If state is REACHABLE, make it STALE. * no other updates should be done. */ - if (ln->ln_state == ND6_LLINFO_REACHABLE) + if (ln->ln_state == ND6_LLINFO_REACHABLE) { ln->ln_state = ND6_LLINFO_STALE; + 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) */ @@ -729,17 +833,20 @@ nd6_na_input(m, off, icmp6len) */ if (is_solicited) { ln->ln_state = ND6_LLINFO_REACHABLE; + ln->ln_byhint = 0; if (ln->ln_expire) { -#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) && !defined(__APPLE__) - ln->ln_expire = time.tv_sec + -#else - ln->ln_expire = time_second + -#endif - 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) + if (lladdr && llchange) { ln->ln_state = ND6_LLINFO_STALE; + ln->ln_expire = rt_expiry(rt, + timenow.tv_sec, nd6_gctimer); + } } } @@ -751,45 +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; -#if __NetBSD__ - s = splsoftnet(); -#else - s = splnet(); -#endif - 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 OLDIP6OUTPUT - (*ifp->if_output)(ifp, ln->ln_hold, rt_key(rt), rt); -#else - nd6_output(ifp, ln->ln_hold, - (struct sockaddr_in6 *)rt_key(rt), rt); -#endif - ln->ln_hold = 0; + if (ln->ln_hold != NULL) { + struct mbuf *n = ln->ln_hold; + ln->ln_hold = NULL; + /* + * we assume ifp is not a loopback here, so just set the 2nd + * argument as the 1st one. + */ + 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); } /* @@ -802,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; - 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; @@ -816,21 +948,21 @@ 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 */ maxlen = sizeof(*ip6) + sizeof(*nd_na); maxlen += (sizeof(struct nd_opt_hdr) + ifp->if_addrlen + 7) & ~7; if (max_linkhdr + maxlen >= MCLBYTES) { -#ifdef DIAGNOSTIC +#if DIAGNOSTIC printf("nd6_na_output: max_linkhdr + maxlen >= MCLBYTES " "(%d + %d > %d)\n", max_linkhdr, maxlen, MCLBYTES); #endif 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) { @@ -840,6 +972,7 @@ nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0) } if (m == NULL) return; + m->m_pkthdr.rcvif = NULL; if (IN6_IS_ADDR_MULTICAST(daddr6)) { m->m_flags |= M_MCAST; @@ -850,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 *); @@ -879,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; @@ -893,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 @@ -933,9 +1068,10 @@ nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0) #if IPSEC /* Don't lookup socket */ - ipsec_setsocket(m, NULL); + 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); @@ -944,22 +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: -#if __NetBSD__ - return LLADDR(ifp->if_sadl); -#else - return ((caddr_t)(ifp + 1)); -#endif - break; - default: - return NULL; - } + return ((caddr_t)ifnet_lladdr(ifp)); } TAILQ_HEAD(dadq_head, dadq); @@ -971,37 +1095,65 @@ 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( + struct ifaddr *ifa) +{ + + untimeout((void (*)(void *))nd6_dad_timer, (void *)ifa); +} +#else +static void +nd6_dad_starttimer( + struct dadq *dp, + int ticks) +{ + + callout_reset(&dp->dad_timer_ch, ticks, + (void (*)(void *))nd6_dad_timer, (void *)dp->dad_ifa); +} + +static void +nd6_dad_stoptimer( + struct dadq *dp) +{ + + callout_stop(&dp->dad_timer_ch); +} +#endif + /* * 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; - static int dad_init = 0; if (!dad_init) { TAILQ_INIT(&dadq); @@ -1048,12 +1200,12 @@ nd6_dad_start(ifa, tick) return; } bzero(dp, sizeof(*dp)); + lck_mtx_lock(dad6_mutex); TAILQ_INSERT_TAIL(&dadq, (struct dadq *)dp, dad_list); + lck_mtx_unlock(dad6_mutex); -#if ND6_DEBUG - log(LOG_DEBUG, "%s: starting DAD for %s\n", if_name(ifa->ifa_ifp), - ip6_sprintf(&ia->ia_addr.sin6_addr)); -#endif + nd6log((LOG_DEBUG, "%s: starting DAD for %s\n", if_name(ifa->ifa_ifp), + ip6_sprintf(&ia->ia_addr.sin6_addr))); /* * Send NS packet for DAD, ip6_dad_count times. @@ -1062,51 +1214,65 @@ nd6_dad_start(ifa, tick) * (re)initialization. */ dp->dad_ifa = ifa; - ifa->ifa_refcnt++; /*just for safety*/ + ifaref(ifa); /*just for safety*/ 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, (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, (void *)ifa, + ntick = *tick_delay + random() % (hz / 2); + *tick_delay = ntick; + timeout((void (*)(void *))nd6_dad_timer, (void *)ifa, ntick); } } +/* + * terminate DAD unconditionally. used for address removals. + */ +void +nd6_dad_stop( + struct ifaddr *ifa) +{ + struct dadq *dp; + + if (!dad_init) + return; + dp = nd6_dad_find(ifa); + if (!dp) { + /* DAD wasn't started yet */ + return; + } + + 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(ifa) - struct ifaddr *ifa; +nd6_dad_timer( + struct ifaddr *ifa) { - int s; struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa; struct dadq *dp; -#ifdef __APPLE__ - boolean_t funnel_state; - funnel_state = thread_set_funneled(TRUE); -#endif -#if __NetBSD__ - s = splsoftnet(); /*XXX*/ -#else - s = splnet(); /*XXX*/ -#endif - /* Sanity check */ if (ia == NULL) { log(LOG_ERR, "nd6_dad_timer: called with null parameter\n"); @@ -1134,27 +1300,29 @@ nd6_dad_timer(ifa) /* timeouted with IFF_{RUNNING,UP} check */ if (dp->dad_ns_tcount > dad_maxtry) { - log(LOG_ERR, "%s: could not run DAD, driver problem?\n", - if_name(ifa->ifa_ifp)); + 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); - _FREE(dp, M_IP6NDP); + lck_mtx_unlock(dad6_mutex); + FREE(dp, M_IP6NDP); dp = NULL; - IFAFREE(ifa); + ifafree(ifa); goto done; } /* 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, (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. @@ -1173,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 @@ -1213,30 +1381,28 @@ nd6_dad_timer(ifa) */ ia->ia6_flags &= ~IN6_IFF_TENTATIVE; -#if ND6_DEBUG - log(LOG_INFO, + nd6log((LOG_DEBUG, "%s: DAD complete for %s - no duplicates found\n", if_name(ifa->ifa_ifp), - ip6_sprintf(&ia->ia_addr.sin6_addr)); -#endif + ip6_sprintf(&ia->ia_addr.sin6_addr))); + lck_mtx_lock(dad6_mutex); TAILQ_REMOVE(&dadq, (struct dadq *)dp, dad_list); - _FREE(dp, M_IP6NDP); + 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); + ifafree(ifa); } } done: - splx(s); -#ifdef __APPLE__ - (void) thread_set_funneled(funnel_state); -#endif + 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; @@ -1247,36 +1413,35 @@ nd6_dad_duplicated(ifa) return; } - log(LOG_ERR, "%s: DAD detected duplicate IPv6 address %s: %d NS, " - "%d NA\n", if_name(ifa->ifa_ifp), - ip6_sprintf(&ia->ia_addr.sin6_addr), - dp->dad_ns_icount, dp->dad_na_icount); + log(LOG_ERR, "%s: DAD detected duplicate IPv6 address %s: " + "NS in/out=%d/%d, NA in=%d\n", + if_name(ifa->ifa_ifp), ip6_sprintf(&ia->ia_addr.sin6_addr), + dp->dad_ns_icount, dp->dad_ns_ocount, dp->dad_na_icount); ia->ia6_flags &= ~IN6_IFF_TENTATIVE; ia->ia6_flags |= IN6_IFF_DUPLICATED; /* We are done with DAD, with duplicated address found. (failure) */ - untimeout((void (*) __P((void *)))nd6_dad_timer, (void *)ifa -#if defined(__FreeBSD__) && __FreeBSD__ >= 3 - , dp->dad_timer -#endif - ); + untimeout((void (*)(void *))nd6_dad_timer, (void *)ifa); + log(LOG_ERR, "%s: DAD complete for %s - duplicate found\n", if_name(ifa->ifa_ifp), ip6_sprintf(&ia->ia_addr.sin6_addr)); 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); - _FREE(dp, M_IP6NDP); + lck_mtx_unlock(dad6_mutex); + FREE(dp, M_IP6NDP); dp = NULL; - IFAFREE(ifa); + 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; @@ -1296,16 +1461,15 @@ 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; - struct in6_addr *taddr6; + const struct in6_addr *taddr6; struct dadq *dp; int duplicate; @@ -1313,22 +1477,16 @@ 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); - /* - * If it is from myself, ignore this. - */ - if (ifp && (ifp->if_flags & IFF_LOOPBACK)) - return; - /* Quickhack - completely ignore DAD NS packets */ if (dad_ignore_ns) { - log(LOG_INFO, "nd6_dad_ns_input: ignoring DAD NS packet for " + nd6log((LOG_INFO, + "nd6_dad_ns_input: ignoring DAD NS packet for " "address %s(%s)\n", ip6_sprintf(taddr6), - if_name(ifa->ifa_ifp)); + if_name(ifa->ifa_ifp))); return; } @@ -1355,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;