From owner-svn-src-stable-10@FreeBSD.ORG Fri May 8 08:35:09 2015 Return-Path: Delivered-To: svn-src-stable-10@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [8.8.178.115]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id 5DFEA39D; Fri, 8 May 2015 08:35:09 +0000 (UTC) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 49FAE15D7; Fri, 8 May 2015 08:35:09 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.9/8.14.9) with ESMTP id t488Z9im017794; Fri, 8 May 2015 08:35:09 GMT (envelope-from hiren@FreeBSD.org) Received: (from hiren@localhost) by svn.freebsd.org (8.14.9/8.14.9/Submit) id t488Z7gb017784; Fri, 8 May 2015 08:35:07 GMT (envelope-from hiren@FreeBSD.org) Message-Id: <201505080835.t488Z7gb017784@svn.freebsd.org> X-Authentication-Warning: svn.freebsd.org: hiren set sender to hiren@FreeBSD.org using -f From: Hiren Panchasara Date: Fri, 8 May 2015 08:35:07 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-10@freebsd.org Subject: svn commit: r282622 - in stable/10: sbin/ifconfig sys/netinet sys/netinet6 X-SVN-Group: stable-10 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-stable-10@freebsd.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: SVN commit messages for only the 10-stable src tree List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 08 May 2015 08:35:09 -0000 Author: hiren Date: Fri May 8 08:35:06 2015 New Revision: 282622 URL: https://svnweb.freebsd.org/changeset/base/282622 Log: MFC r261708, r261847, r268525, r274316, r274347, r275593, r276844, r276847, r279531, r279559, r279564, r279676 A bunch of IPv6 fixes by melifaro, hrs and ae Major changes: Simplify nd6_output_lle() Add refcounting to DAD and fix races and other errors Implement Enhanced DAD algorithm for IPv6 Suggested by: ae Tested by: Jason Wolfe Sponsored by: Limelight Networks Modified: stable/10/sbin/ifconfig/af_inet6.c stable/10/sbin/ifconfig/af_nd6.c stable/10/sbin/ifconfig/ifconfig.8 stable/10/sys/netinet/icmp6.h stable/10/sys/netinet6/in6.c stable/10/sys/netinet6/nd6.c stable/10/sys/netinet6/nd6.h stable/10/sys/netinet6/nd6_nbr.c Directory Properties: stable/10/ (props changed) Modified: stable/10/sbin/ifconfig/af_inet6.c ============================================================================== --- stable/10/sbin/ifconfig/af_inet6.c Fri May 8 06:02:23 2015 (r282621) +++ stable/10/sbin/ifconfig/af_inet6.c Fri May 8 08:35:06 2015 (r282622) @@ -483,6 +483,10 @@ static struct cmd inet6_cmds[] = { DEF_CMD("-auto_linklocal",-ND6_IFF_AUTO_LINKLOCAL,setnd6flags), DEF_CMD("no_prefer_iface",ND6_IFF_NO_PREFER_IFACE,setnd6flags), DEF_CMD("-no_prefer_iface",-ND6_IFF_NO_PREFER_IFACE,setnd6flags), + DEF_CMD("no_dad", ND6_IFF_NO_DAD, setnd6flags), + DEF_CMD("-no_dad", -ND6_IFF_NO_DAD, setnd6flags), + DEF_CMD("ignoreloop", ND6_IFF_IGNORELOOP, setnd6flags), + DEF_CMD("-ignoreloop", -ND6_IFF_IGNORELOOP, setnd6flags), DEF_CMD_ARG("pltime", setip6pltime), DEF_CMD_ARG("vltime", setip6vltime), DEF_CMD("eui64", 0, setip6eui64), Modified: stable/10/sbin/ifconfig/af_nd6.c ============================================================================== --- stable/10/sbin/ifconfig/af_nd6.c Fri May 8 06:02:23 2015 (r282621) +++ stable/10/sbin/ifconfig/af_nd6.c Fri May 8 08:35:06 2015 (r282622) @@ -58,7 +58,8 @@ static const char rcsid[] = #define MAX_SYSCTL_TRY 5 #define ND6BITS "\020\001PERFORMNUD\002ACCEPT_RTADV\003PREFER_SOURCE" \ "\004IFDISABLED\005DONT_SET_IFROUTE\006AUTO_LINKLOCAL" \ - "\007NO_RADR\010NO_PREFER_IFACE\020DEFAULTIF" + "\007NO_RADR\010NO_PREFER_IFACE\011IGNORELOOP\012NO_DAD" \ + "\020DEFAULTIF" static int isnd6defif(int); void setnd6flags(const char *, int, int, const struct afswtch *); Modified: stable/10/sbin/ifconfig/ifconfig.8 ============================================================================== --- stable/10/sbin/ifconfig/ifconfig.8 Fri May 8 06:02:23 2015 (r282621) +++ stable/10/sbin/ifconfig/ifconfig.8 Fri May 8 08:35:06 2015 (r282622) @@ -28,7 +28,7 @@ .\" From: @(#)ifconfig.8 8.3 (Berkeley) 1/5/94 .\" $FreeBSD$ .\" -.Dd September 9, 2014 +.Dd March 6, 2015 .Dt IFCONFIG 8 .Os .Sh NAME @@ -736,6 +736,20 @@ outgoing interface. .It Cm -no_prefer_iface Clear a flag .Cm no_prefer_iface . +.It Cm no_dad +Set a flag to disable Duplicate Address Detection. +.It Cm -no_dad +Clear a flag +.Cm no_dad . +.It Cm ignoreloop +Set a flag to disable loopback detection in Enhanced Duplicate Address +Detection Algorithm. +When this flag is set, +Duplicate Address Detection will stop in a finite number of probings +even if a loopback configuration is detected. +.It Cm -ignoreloop +Clear a flag +.Cm ignoreloop . .El .Pp The following parameters are specific for IPv6 addresses. Modified: stable/10/sys/netinet/icmp6.h ============================================================================== --- stable/10/sys/netinet/icmp6.h Fri May 8 06:02:23 2015 (r282621) +++ stable/10/sys/netinet/icmp6.h Fri May 8 08:35:06 2015 (r282622) @@ -297,9 +297,11 @@ struct nd_opt_hdr { /* Neighbor discove #define ND_OPT_PREFIX_INFORMATION 3 #define ND_OPT_REDIRECTED_HEADER 4 #define ND_OPT_MTU 5 +#define ND_OPT_NONCE 14 /* RFC 3971 */ #define ND_OPT_ROUTE_INFO 24 /* RFC 4191 */ #define ND_OPT_RDNSS 25 /* RFC 6106 */ #define ND_OPT_DNSSL 31 /* RFC 6106 */ +#define ND_OPT_MAX 31 struct nd_opt_prefix_info { /* prefix information */ u_int8_t nd_opt_pi_type; @@ -330,6 +332,16 @@ struct nd_opt_mtu { /* MTU option */ u_int32_t nd_opt_mtu_mtu; } __packed; +#define ND_OPT_NONCE_LEN ((1 * 8) - 2) +#if ((ND_OPT_NONCE_LEN + 2) % 8) != 0 +#error "(ND_OPT_NONCE_LEN + 2) must be a multiple of 8." +#endif +struct nd_opt_nonce { /* nonce option */ + u_int8_t nd_opt_nonce_type; + u_int8_t nd_opt_nonce_len; + u_int8_t nd_opt_nonce[ND_OPT_NONCE_LEN]; +} __packed; + struct nd_opt_route_info { /* route info */ u_int8_t nd_opt_rti_type; u_int8_t nd_opt_rti_len; Modified: stable/10/sys/netinet6/in6.c ============================================================================== --- stable/10/sys/netinet6/in6.c Fri May 8 06:02:23 2015 (r282621) +++ stable/10/sys/netinet6/in6.c Fri May 8 08:35:06 2015 (r282622) @@ -2370,7 +2370,8 @@ in6if_do_dad(struct ifnet *ifp) if ((ifp->if_flags & IFF_LOOPBACK) != 0) return (0); - if (ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED) + if ((ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED) || + (ND_IFINFO(ifp)->flags & ND6_IFF_NO_DAD)) return (0); switch (ifp->if_type) { Modified: stable/10/sys/netinet6/nd6.c ============================================================================== --- stable/10/sys/netinet6/nd6.c Fri May 8 06:02:23 2015 (r282621) +++ stable/10/sys/netinet6/nd6.c Fri May 8 08:35:06 2015 (r282622) @@ -133,6 +133,10 @@ static int regen_tmpaddr(struct in6_ifad static struct llentry *nd6_free(struct llentry *, int); static void nd6_llinfo_timer(void *); static void clear_llinfo_pqueue(struct llentry *); +static int nd6_output_lle(struct ifnet *, struct ifnet *, struct mbuf *, + struct sockaddr_in6 *); +static int nd6_output_ifp(struct ifnet *, struct ifnet *, struct mbuf *, + struct sockaddr_in6 *); static VNET_DEFINE(struct callout, nd6_slowtimo_ch); #define V_nd6_slowtimo_ch VNET(nd6_slowtimo_ch) @@ -152,6 +156,8 @@ nd6_init(void) callout_init(&V_nd6_slowtimo_ch, 0); callout_reset(&V_nd6_slowtimo_ch, ND6_SLOWTIMER_INTERVAL * hz, nd6_slowtimo, curvnet); + + nd6_dad_init(); } #ifdef VIMAGE @@ -365,6 +371,7 @@ nd6_options(union nd_opts *ndopts) case ND_OPT_TARGET_LINKADDR: case ND_OPT_MTU: case ND_OPT_REDIRECTED_HEADER: + case ND_OPT_NONCE: if (ndopts->nd_opt_array[nd_opt->nd_opt_type]) { nd6log((LOG_INFO, "duplicated ND6 option found (type=%d)\n", @@ -519,7 +526,7 @@ nd6_llinfo_timer(void *arg) ln->la_asked++; nd6_llinfo_settimer_locked(ln, (long)ndi->retrans * hz / 1000); LLE_WUNLOCK(ln); - nd6_ns_output(ifp, NULL, dst, ln, 0); + nd6_ns_output(ifp, NULL, dst, ln, NULL); LLE_WLOCK(ln); } else { struct mbuf *m = ln->la_hold; @@ -566,7 +573,7 @@ nd6_llinfo_timer(void *arg) ln->ln_state = ND6_LLINFO_PROBE; nd6_llinfo_settimer_locked(ln, (long)ndi->retrans * hz / 1000); LLE_WUNLOCK(ln); - nd6_ns_output(ifp, dst, dst, ln, 0); + nd6_ns_output(ifp, dst, dst, ln, NULL); LLE_WLOCK(ln); } else { ln->ln_state = ND6_LLINFO_STALE; /* XXX */ @@ -578,7 +585,7 @@ nd6_llinfo_timer(void *arg) ln->la_asked++; nd6_llinfo_settimer_locked(ln, (long)ndi->retrans * hz / 1000); LLE_WUNLOCK(ln); - nd6_ns_output(ifp, dst, dst, ln, 0); + nd6_ns_output(ifp, dst, dst, ln, NULL); LLE_WLOCK(ln); } else { EVENTHANDLER_INVOKE(lle_event, ln, LLENTRY_EXPIRED); @@ -1662,42 +1669,8 @@ nd6_cache_lladdr(struct ifnet *ifp, stru ln->ln_state = newstate; if (ln->ln_state == ND6_LLINFO_STALE) { - /* - * XXX: since nd6_output() below will cause - * state tansition to DELAY and reset the timer, - * we must set the timer now, although it is actually - * meaningless. - */ - nd6_llinfo_settimer_locked(ln, (long)V_nd6_gctimer * hz); - - if (ln->la_hold) { - struct mbuf *m_hold, *m_hold_next; - - /* - * reset the la_hold in advance, to explicitly - * prevent a la_hold lookup in nd6_output() - * (wouldn't happen, though...) - */ - for (m_hold = ln->la_hold, ln->la_hold = NULL; - m_hold; m_hold = m_hold_next) { - m_hold_next = m_hold->m_nextpkt; - m_hold->m_nextpkt = NULL; - - /* - * we assume ifp is not a p2p here, so - * just set the 2nd argument as the - * 1st one. - */ - nd6_output_lle(ifp, ifp, m_hold, L3_ADDR_SIN6(ln), NULL, ln, &chain); - } - /* - * If we have mbufs in the chain we need to do - * deferred transmit. Copy the address from the - * llentry before dropping the lock down below. - */ - if (chain != NULL) - memcpy(&sin6, L3_ADDR_SIN6(ln), sizeof(sin6)); - } + if (ln->la_hold != NULL) + nd6_grab_holdchain(ln, &chain, &sin6); } else if (ln->ln_state == ND6_LLINFO_INCOMPLETE) { /* probe right away */ nd6_llinfo_settimer_locked((void *)ln, 0); @@ -1780,8 +1753,8 @@ nd6_cache_lladdr(struct ifnet *ifp, stru if (static_route) ln = NULL; } - if (chain) - nd6_output_flush(ifp, ifp, chain, &sin6, NULL); + if (chain != NULL) + nd6_flush_holdchain(ifp, ifp, chain, &sin6); /* * When the link-layer address of a router changes, select the @@ -1849,55 +1822,159 @@ nd6_slowtimo(void *arg) CURVNET_RESTORE(); } -int -nd6_output(struct ifnet *ifp, struct ifnet *origifp, struct mbuf *m0, - struct sockaddr_in6 *dst, struct rtentry *rt0) +void +nd6_grab_holdchain(struct llentry *ln, struct mbuf **chain, + struct sockaddr_in6 *sin6) { - return (nd6_output_lle(ifp, origifp, m0, dst, rt0, NULL, NULL)); + LLE_WLOCK_ASSERT(ln); + + *chain = ln->la_hold; + ln->la_hold = NULL; + memcpy(sin6, L3_ADDR_SIN6(ln), sizeof(*sin6)); + + if (ln->ln_state == ND6_LLINFO_STALE) { + + /* + * The first time we send a packet to a + * neighbor whose entry is STALE, we have + * to change the state to DELAY and a sets + * a timer to expire in DELAY_FIRST_PROBE_TIME + * seconds to ensure do neighbor unreachability + * detection on expiration. + * (RFC 2461 7.3.3) + */ + ln->la_asked = 0; + ln->ln_state = ND6_LLINFO_DELAY; + nd6_llinfo_settimer_locked(ln, (long)V_nd6_delay * hz); + } } +static int +nd6_output_ifp(struct ifnet *ifp, struct ifnet *origifp, struct mbuf *m, + struct sockaddr_in6 *dst) +{ + int error; + int ip6len; + struct ip6_hdr *ip6; + struct m_tag *mtag; + +#ifdef MAC + mac_netinet6_nd6_send(ifp, m); +#endif + + /* + * If called from nd6_ns_output() (NS), nd6_na_output() (NA), + * icmp6_redirect_output() (REDIRECT) or from rip6_output() (RS, RA + * as handled by rtsol and rtadvd), mbufs will be tagged for SeND + * to be diverted to user space. When re-injected into the kernel, + * send_output() will directly dispatch them to the outgoing interface. + */ + if (send_sendso_input_hook != NULL) { + mtag = m_tag_find(m, PACKET_TAG_ND_OUTGOING, NULL); + if (mtag != NULL) { + ip6 = mtod(m, struct ip6_hdr *); + ip6len = sizeof(struct ip6_hdr) + ntohs(ip6->ip6_plen); + /* Use the SEND socket */ + error = send_sendso_input_hook(m, ifp, SND_OUT, + ip6len); + /* -1 == no app on SEND socket */ + if (error == 0 || error != -1) + return (error); + } + } + + m_clrprotoflags(m); /* Avoid confusing lower layers. */ + IP_PROBE(send, NULL, NULL, mtod(m, struct ip6_hdr *), ifp, NULL, + mtod(m, struct ip6_hdr *)); + + if ((ifp->if_flags & IFF_LOOPBACK) == 0) + origifp = ifp; + + error = (*ifp->if_output)(origifp, m, (struct sockaddr *)dst, NULL); + return (error); +} /* - * Note that I'm not enforcing any global serialization - * lle state or asked changes here as the logic is too - * complicated to avoid having to always acquire an exclusive - * lock - * KMM - * + * IPv6 packet output - light version. + * Checks if destination LLE exists and is in proper state + * (e.g no modification required). If not true, fall back to + * "heavy" version. */ -#define senderr(e) { error = (e); goto bad;} - int -nd6_output_lle(struct ifnet *ifp, struct ifnet *origifp, struct mbuf *m0, - struct sockaddr_in6 *dst, struct rtentry *rt0, struct llentry *lle, - struct mbuf **chain) +nd6_output(struct ifnet *ifp, struct ifnet *origifp, struct mbuf *m, + struct sockaddr_in6 *dst, struct rtentry *rt0) { - struct mbuf *m = m0; - struct m_tag *mtag; - struct llentry *ln = lle; - struct ip6_hdr *ip6; - int error = 0; - int flags = 0; - int ip6len; - -#ifdef INVARIANTS - if (lle != NULL) { - - LLE_WLOCK_ASSERT(lle); + struct llentry *ln = NULL; - KASSERT(chain != NULL, (" lle locked but no mbuf chain pointer passed")); + /* discard the packet if IPv6 operation is disabled on the interface */ + if ((ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED)) { + m_freem(m); + return (ENETDOWN); /* better error? */ } -#endif + if (IN6_IS_ADDR_MULTICAST(&dst->sin6_addr)) goto sendpkt; if (nd6_need_cache(ifp) == 0) goto sendpkt; + IF_AFDATA_RLOCK(ifp); + ln = nd6_lookup(&dst->sin6_addr, 0, ifp); + IF_AFDATA_RUNLOCK(ifp); + /* - * next hop determination. This routine is derived from ether_output. + * Perform fast path for the following cases: + * 1) lle state is REACHABLE + * 2) lle state is DELAY (NS message sentNS message sent) + * + * Every other case involves lle modification, so we handle + * them separately. */ + if (ln == NULL || (ln->ln_state != ND6_LLINFO_REACHABLE && + ln->ln_state != ND6_LLINFO_DELAY)) { + /* Fall back to slow processing path */ + if (ln != NULL) + LLE_RUNLOCK(ln); + return (nd6_output_lle(ifp, origifp, m, dst)); + } + +sendpkt: + if (ln != NULL) + LLE_RUNLOCK(ln); + + return (nd6_output_ifp(ifp, origifp, m, dst)); +} + + +/* + * Output IPv6 packet - heavy version. + * Function assume that either + * 1) destination LLE does not exist, is invalid or stale, so + * ND6_EXCLUSIVE lock needs to be acquired + * 2) destination lle is provided (with ND6_EXCLUSIVE lock), + * in that case packets are queued in &chain. + * + */ +static int +nd6_output_lle(struct ifnet *ifp, struct ifnet *origifp, struct mbuf *m, + struct sockaddr_in6 *dst) +{ + struct llentry *lle = NULL; + int flags = 0; + + KASSERT(m != NULL, ("NULL mbuf, nothing to send")); + /* discard the packet if IPv6 operation is disabled on the interface */ + if ((ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED)) { + m_freem(m); + return (ENETDOWN); /* better error? */ + } + + if (IN6_IS_ADDR_MULTICAST(&dst->sin6_addr)) + goto sendpkt; + + if (nd6_need_cache(ifp) == 0) + goto sendpkt; /* * Address resolution or Neighbor Unreachability Detection @@ -1905,48 +1982,43 @@ nd6_output_lle(struct ifnet *ifp, struct * At this point, the destination of the packet must be a unicast * or an anycast address(i.e. not a multicast). */ - - flags = (lle != NULL) ? LLE_EXCLUSIVE : 0; - if (ln == NULL) { - retry: + if (lle == NULL) { IF_AFDATA_RLOCK(ifp); - ln = lla_lookup(LLTABLE6(ifp), flags, (struct sockaddr *)dst); + lle = nd6_lookup(&dst->sin6_addr, ND6_EXCLUSIVE, ifp); IF_AFDATA_RUNLOCK(ifp); - if ((ln == NULL) && nd6_is_addr_neighbor(dst, ifp)) { + if ((lle == NULL) && nd6_is_addr_neighbor(dst, ifp)) { /* * Since nd6_is_addr_neighbor() internally calls nd6_lookup(), * the condition below is not very efficient. But we believe * it is tolerable, because this should be a rare case. */ - flags = ND6_CREATE | (m ? ND6_EXCLUSIVE : 0); + flags = ND6_CREATE | ND6_EXCLUSIVE; IF_AFDATA_LOCK(ifp); - ln = nd6_lookup(&dst->sin6_addr, flags, ifp); + lle = nd6_lookup(&dst->sin6_addr, flags, ifp); IF_AFDATA_UNLOCK(ifp); } } - if (ln == NULL) { + if (lle == NULL) { if ((ifp->if_flags & IFF_POINTOPOINT) == 0 && !(ND_IFINFO(ifp)->flags & ND6_IFF_PERFORMNUD)) { char ip6buf[INET6_ADDRSTRLEN]; log(LOG_DEBUG, "nd6_output: can't allocate llinfo for %s " "(ln=%p)\n", - ip6_sprintf(ip6buf, &dst->sin6_addr), ln); - senderr(EIO); /* XXX: good error? */ + ip6_sprintf(ip6buf, &dst->sin6_addr), lle); + m_freem(m); + return (ENOBUFS); } goto sendpkt; /* send anyway */ } + LLE_WLOCK_ASSERT(lle); + /* We don't have to do link-layer address resolution on a p2p link. */ if ((ifp->if_flags & IFF_POINTOPOINT) != 0 && - ln->ln_state < ND6_LLINFO_REACHABLE) { - if ((flags & LLE_EXCLUSIVE) == 0) { - flags |= LLE_EXCLUSIVE; - LLE_RUNLOCK(ln); - goto retry; - } - ln->ln_state = ND6_LLINFO_STALE; - nd6_llinfo_settimer_locked(ln, (long)V_nd6_gctimer * hz); + lle->ln_state < ND6_LLINFO_REACHABLE) { + lle->ln_state = ND6_LLINFO_STALE; + nd6_llinfo_settimer_locked(lle, (long)V_nd6_gctimer * hz); } /* @@ -1956,15 +2028,10 @@ nd6_output_lle(struct ifnet *ifp, struct * neighbor unreachability detection on expiration. * (RFC 2461 7.3.3) */ - if (ln->ln_state == ND6_LLINFO_STALE) { - if ((flags & LLE_EXCLUSIVE) == 0) { - flags |= LLE_EXCLUSIVE; - LLE_RUNLOCK(ln); - goto retry; - } - ln->la_asked = 0; - ln->ln_state = ND6_LLINFO_DELAY; - nd6_llinfo_settimer_locked(ln, (long)V_nd6_delay * hz); + if (lle->ln_state == ND6_LLINFO_STALE) { + lle->la_asked = 0; + lle->ln_state = ND6_LLINFO_DELAY; + nd6_llinfo_settimer_locked(lle, (long)V_nd6_delay * hz); } /* @@ -1972,7 +2039,7 @@ nd6_output_lle(struct ifnet *ifp, struct * (i.e. its link-layer address is already resolved), just * send the packet. */ - if (ln->ln_state > ND6_LLINFO_INCOMPLETE) + if (lle->ln_state > ND6_LLINFO_INCOMPLETE) goto sendpkt; /* @@ -1982,23 +2049,15 @@ nd6_output_lle(struct ifnet *ifp, struct * does not exceed nd6_maxqueuelen. When it exceeds nd6_maxqueuelen, * the oldest packet in the queue will be removed. */ - if (ln->ln_state == ND6_LLINFO_NOSTATE) - ln->ln_state = ND6_LLINFO_INCOMPLETE; + if (lle->ln_state == ND6_LLINFO_NOSTATE) + lle->ln_state = ND6_LLINFO_INCOMPLETE; - if ((flags & LLE_EXCLUSIVE) == 0) { - flags |= LLE_EXCLUSIVE; - LLE_RUNLOCK(ln); - goto retry; - } - - LLE_WLOCK_ASSERT(ln); - - if (ln->la_hold) { + if (lle->la_hold != NULL) { struct mbuf *m_hold; int i; i = 0; - for (m_hold = ln->la_hold; m_hold; m_hold = m_hold->m_nextpkt) { + for (m_hold = lle->la_hold; m_hold; m_hold = m_hold->m_nextpkt){ i++; if (m_hold->m_nextpkt == NULL) { m_hold->m_nextpkt = m; @@ -2006,135 +2065,44 @@ nd6_output_lle(struct ifnet *ifp, struct } } while (i >= V_nd6_maxqueuelen) { - m_hold = ln->la_hold; - ln->la_hold = ln->la_hold->m_nextpkt; + m_hold = lle->la_hold; + lle->la_hold = lle->la_hold->m_nextpkt; m_freem(m_hold); i--; } } else { - ln->la_hold = m; + lle->la_hold = m; } /* * If there has been no NS for the neighbor after entering the * INCOMPLETE state, send the first solicitation. */ - if (!ND6_LLINFO_PERMANENT(ln) && ln->la_asked == 0) { - ln->la_asked++; + if (!ND6_LLINFO_PERMANENT(lle) && lle->la_asked == 0) { + lle->la_asked++; - nd6_llinfo_settimer_locked(ln, + nd6_llinfo_settimer_locked(lle, (long)ND_IFINFO(ifp)->retrans * hz / 1000); - LLE_WUNLOCK(ln); - nd6_ns_output(ifp, NULL, &dst->sin6_addr, ln, 0); - if (lle != NULL && ln == lle) - LLE_WLOCK(lle); - - } else if (lle == NULL || ln != lle) { - /* - * We did the lookup (no lle arg) so we - * need to do the unlock here. - */ - LLE_WUNLOCK(ln); + LLE_WUNLOCK(lle); + nd6_ns_output(ifp, NULL, &dst->sin6_addr, lle, NULL); + } else { + /* We did the lookup so we need to do the unlock here. */ + LLE_WUNLOCK(lle); } return (0); sendpkt: - /* discard the packet if IPv6 operation is disabled on the interface */ - if ((ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED)) { - error = ENETDOWN; /* better error? */ - goto bad; - } - /* - * ln is valid and the caller did not pass in - * an llentry - */ - if ((ln != NULL) && (lle == NULL)) { - if (flags & LLE_EXCLUSIVE) - LLE_WUNLOCK(ln); - else - LLE_RUNLOCK(ln); - } - -#ifdef MAC - mac_netinet6_nd6_send(ifp, m); -#endif + if (lle != NULL) + LLE_WUNLOCK(lle); - /* - * If called from nd6_ns_output() (NS), nd6_na_output() (NA), - * icmp6_redirect_output() (REDIRECT) or from rip6_output() (RS, RA - * as handled by rtsol and rtadvd), mbufs will be tagged for SeND - * to be diverted to user space. When re-injected into the kernel, - * send_output() will directly dispatch them to the outgoing interface. - */ - if (send_sendso_input_hook != NULL) { - mtag = m_tag_find(m, PACKET_TAG_ND_OUTGOING, NULL); - if (mtag != NULL) { - ip6 = mtod(m, struct ip6_hdr *); - ip6len = sizeof(struct ip6_hdr) + ntohs(ip6->ip6_plen); - /* Use the SEND socket */ - error = send_sendso_input_hook(m, ifp, SND_OUT, - ip6len); - /* -1 == no app on SEND socket */ - if (error == 0 || error != -1) - return (error); - } - } - - /* - * We were passed in a pointer to an lle with the lock held - * this means that we can't call if_output as we will - * recurse on the lle lock - so what we do is we create - * a list of mbufs to send and transmit them in the caller - * after the lock is dropped - */ - if (lle != NULL) { - if (*chain == NULL) - *chain = m; - else { - struct mbuf *mb; - - /* - * append mbuf to end of deferred chain - */ - mb = *chain; - while (mb->m_nextpkt != NULL) - mb = mb->m_nextpkt; - mb->m_nextpkt = m; - } - return (error); - } - m_clrprotoflags(m); /* Avoid confusing lower layers. */ - IP_PROBE(send, NULL, NULL, mtod(m, struct ip6_hdr *), ifp, NULL, - mtod(m, struct ip6_hdr *)); - if ((ifp->if_flags & IFF_LOOPBACK) != 0) { - return ((*ifp->if_output)(origifp, m, (struct sockaddr *)dst, - NULL)); - } - error = (*ifp->if_output)(ifp, m, (struct sockaddr *)dst, NULL); - return (error); - - bad: - /* - * ln is valid and the caller did not pass in - * an llentry - */ - if ((ln != NULL) && (lle == NULL)) { - if (flags & LLE_EXCLUSIVE) - LLE_WUNLOCK(ln); - else - LLE_RUNLOCK(ln); - } - if (m) - m_freem(m); - return (error); + return (nd6_output_ifp(ifp, origifp, m, dst)); } -#undef senderr int -nd6_output_flush(struct ifnet *ifp, struct ifnet *origifp, struct mbuf *chain, - struct sockaddr_in6 *dst, struct route *ro) +nd6_flush_holdchain(struct ifnet *ifp, struct ifnet *origifp, struct mbuf *chain, + struct sockaddr_in6 *dst) { struct mbuf *m, *m_head; struct ifnet *outifp; @@ -2149,7 +2117,7 @@ nd6_output_flush(struct ifnet *ifp, stru while (m_head) { m = m_head; m_head = m_head->m_nextpkt; - error = (*ifp->if_output)(ifp, m, (struct sockaddr *)dst, ro); + error = nd6_output_ifp(ifp, origifp, m, dst); } /* Modified: stable/10/sys/netinet6/nd6.h ============================================================================== --- stable/10/sys/netinet6/nd6.h Fri May 8 06:02:23 2015 (r282621) +++ stable/10/sys/netinet6/nd6.h Fri May 8 08:35:06 2015 (r282622) @@ -87,6 +87,8 @@ struct nd_ifinfo { #define ND6_IFF_AUTO_LINKLOCAL 0x20 #define ND6_IFF_NO_RADR 0x40 #define ND6_IFF_NO_PREFER_IFACE 0x80 /* XXX: not related to ND. */ +#define ND6_IFF_IGNORELOOP 0x100 +#define ND6_IFF_NO_DAD 0x200 #define ND6_CREATE LLE_CREATE #define ND6_EXCLUSIVE LLE_EXCLUSIVE @@ -359,7 +361,7 @@ VNET_DECLARE(int, ip6_temp_regen_advance #define V_ip6_temp_regen_advance VNET(ip6_temp_regen_advance) union nd_opts { - struct nd_opt_hdr *nd_opt_array[8]; /* max = target address list */ + struct nd_opt_hdr *nd_opt_array[16]; /* max = ND_OPT_NONCE */ struct { struct nd_opt_hdr *zero; struct nd_opt_hdr *src_lladdr; @@ -367,6 +369,16 @@ union nd_opts { struct nd_opt_prefix_info *pi_beg; /* multiple opts, start */ struct nd_opt_rd_hdr *rh; struct nd_opt_mtu *mtu; + struct nd_opt_hdr *__res6; + struct nd_opt_hdr *__res7; + struct nd_opt_hdr *__res8; + struct nd_opt_hdr *__res9; + struct nd_opt_hdr *__res10; + struct nd_opt_hdr *__res11; + struct nd_opt_hdr *__res12; + struct nd_opt_hdr *__res13; + struct nd_opt_nonce *nonce; + struct nd_opt_hdr *__res15; struct nd_opt_hdr *search; /* multiple opts */ struct nd_opt_hdr *last; /* multiple opts */ int done; @@ -379,6 +391,7 @@ union nd_opts { #define nd_opts_pi_end nd_opt_each.pi_end #define nd_opts_rh nd_opt_each.rh #define nd_opts_mtu nd_opt_each.mtu +#define nd_opts_nonce nd_opt_each.nonce #define nd_opts_search nd_opt_each.search #define nd_opts_last nd_opt_each.last #define nd_opts_done nd_opt_each.done @@ -410,11 +423,10 @@ struct llentry *nd6_cache_lladdr(struct char *, int, int, int); int nd6_output(struct ifnet *, struct ifnet *, struct mbuf *, struct sockaddr_in6 *, struct rtentry *); -int nd6_output_lle(struct ifnet *, struct ifnet *, struct mbuf *, - struct sockaddr_in6 *, struct rtentry *, struct llentry *, - struct mbuf **); -int nd6_output_flush(struct ifnet *, struct ifnet *, struct mbuf *, - struct sockaddr_in6 *, struct route *); +void nd6_grab_holdchain(struct llentry *, struct mbuf **, + struct sockaddr_in6 *); +int nd6_flush_holdchain(struct ifnet *, struct ifnet *, struct mbuf *, + struct sockaddr_in6 *); int nd6_need_cache(struct ifnet *); int nd6_storelladdr(struct ifnet *, struct mbuf *, const struct sockaddr *, u_char *, struct llentry **); @@ -425,11 +437,11 @@ void nd6_na_output(struct ifnet *, const const struct in6_addr *, u_long, int, struct sockaddr *); void nd6_ns_input(struct mbuf *, int, int); void nd6_ns_output(struct ifnet *, const struct in6_addr *, - const struct in6_addr *, struct llentry *, int); + const struct in6_addr *, struct llentry *, uint8_t *); caddr_t nd6_ifptomac(struct ifnet *); +void nd6_dad_init(void); void nd6_dad_start(struct ifaddr *, int); void nd6_dad_stop(struct ifaddr *); -void nd6_dad_duplicated(struct ifaddr *); /* nd6_rtr.c */ void nd6_rs_input(struct mbuf *, int, int); Modified: stable/10/sys/netinet6/nd6_nbr.c ============================================================================== --- stable/10/sys/netinet6/nd6_nbr.c Fri May 8 06:02:23 2015 (r282621) +++ stable/10/sys/netinet6/nd6_nbr.c Fri May 8 08:35:06 2015 (r282622) @@ -40,6 +40,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -48,9 +49,11 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include +#include #include #include @@ -60,6 +63,7 @@ __FBSDID("$FreeBSD$"); #ifdef RADIX_MPATH #include #endif +#include #include #include @@ -78,21 +82,32 @@ __FBSDID("$FreeBSD$"); #define SDL(s) ((struct sockaddr_dl *)s) struct dadq; -static struct dadq *nd6_dad_find(struct ifaddr *); +static struct dadq *nd6_dad_find(struct ifaddr *, struct nd_opt_nonce *); +static void nd6_dad_add(struct dadq *dp); +static void nd6_dad_del(struct dadq *dp); +static void nd6_dad_rele(struct dadq *); static void nd6_dad_starttimer(struct dadq *, int); static void nd6_dad_stoptimer(struct dadq *); static void nd6_dad_timer(struct dadq *); +static void nd6_dad_duplicated(struct ifaddr *, struct dadq *); static void nd6_dad_ns_output(struct dadq *, struct ifaddr *); -static void nd6_dad_ns_input(struct ifaddr *); +static void nd6_dad_ns_input(struct ifaddr *, struct nd_opt_nonce *); static void nd6_dad_na_input(struct ifaddr *); static void nd6_na_output_fib(struct ifnet *, const struct in6_addr *, const struct in6_addr *, u_long, int, struct sockaddr *, u_int); +static void nd6_ns_output_fib(struct ifnet *, const struct in6_addr *, + const struct in6_addr *, struct llentry *, uint8_t *, u_int); + +static VNET_DEFINE(int, dad_enhanced) = 1; +#define V_dad_enhanced VNET(dad_enhanced) + +SYSCTL_DECL(_net_inet6_ip6); +SYSCTL_INT(_net_inet6_ip6, OID_AUTO, dad_enhanced, CTLFLAG_VNET | CTLFLAG_RW, + &VNET_NAME(dad_enhanced), 0, + "Enable Enhanced DAD, which adds a random nonce to NS messages for DAD."); -static VNET_DEFINE(int, dad_ignore_ns) = 0; /* ignore NS in DAD - - specwise incorrect */ static VNET_DEFINE(int, dad_maxtry) = 15; /* max # of *tries* to transmit DAD packet */ -#define V_dad_ignore_ns VNET(dad_ignore_ns) #define V_dad_maxtry VNET(dad_maxtry) /* @@ -316,7 +331,7 @@ nd6_ns_input(struct mbuf *m, int off, in * silently ignore it. */ if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) - nd6_dad_ns_input(ifa); + nd6_dad_ns_input(ifa, ndopts.nd_opts_nonce); goto freeit; } @@ -377,12 +392,14 @@ nd6_ns_input(struct mbuf *m, int off, in * Based on RFC 2461 * Based on RFC 2462 (duplicate address detection) * - * ln - for source address determination - * dad - duplicate address detection + * ln - for source address determination + * nonce - If non-NULL, NS is used for duplicate address detection and + * the value (length is ND_OPT_NONCE_LEN) is used as a random nonce. */ -void -nd6_ns_output(struct ifnet *ifp, const struct in6_addr *daddr6, - const struct in6_addr *taddr6, struct llentry *ln, int dad) +static void +nd6_ns_output_fib(struct ifnet *ifp, const struct in6_addr *daddr6, + const struct in6_addr *taddr6, struct llentry *ln, uint8_t *nonce, + u_int fibnum) { struct mbuf *m; struct m_tag *mtag; @@ -404,12 +421,14 @@ nd6_ns_output(struct ifnet *ifp, const s "%s: max_linkhdr + maxlen > MCLBYTES (%d + %d > %d)", __func__, max_linkhdr, maxlen, MCLBYTES)); + if (max_linkhdr + maxlen > MHLEN) m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); else m = m_gethdr(M_NOWAIT, MT_DATA); if (m == NULL) return; + M_SETFIB(m, fibnum); bzero(&ro, sizeof(ro)); @@ -444,7 +463,7 @@ nd6_ns_output(struct ifnet *ifp, const s if (in6_setscope(&ip6->ip6_dst, ifp, NULL) != 0) goto bad; } - if (!dad) { + if (nonce == NULL) { struct ifaddr *ifa; /* @@ -503,9 +522,8 @@ nd6_ns_output(struct ifnet *ifp, const s NULL, &ro, NULL, &oifp, &src_in); if (error) { char ip6buf[INET6_ADDRSTRLEN]; - nd6log((LOG_DEBUG, - "nd6_ns_output: source can't be " - "determined: dst=%s, error=%d\n", + nd6log((LOG_DEBUG, "%s: source can't be " + "determined: dst=%s, error=%d\n", __func__, ip6_sprintf(ip6buf, &dst_sa.sin6_addr), error)); goto bad; @@ -541,7 +559,7 @@ nd6_ns_output(struct ifnet *ifp, const s * Multicast NS MUST add one add the option * Unicast NS SHOULD add one add the option */ - if (!dad && (mac = nd6_ifptomac(ifp))) { + if (nonce == NULL && (mac = nd6_ifptomac(ifp))) { int optlen = sizeof(struct nd_opt_hdr) + ifp->if_addrlen; struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)(nd_ns + 1); /* 8 byte alignments... */ @@ -555,7 +573,26 @@ nd6_ns_output(struct ifnet *ifp, const s nd_opt->nd_opt_len = optlen >> 3; bcopy(mac, (caddr_t)(nd_opt + 1), ifp->if_addrlen); } + /* + * Add a Nonce option (RFC 3971) to detect looped back NS messages. + * This behavior is documented as Enhanced Duplicate Address + * Detection in draft-ietf-6man-enhanced-dad-13. + * net.inet6.ip6.dad_enhanced=0 disables this. + */ + if (V_dad_enhanced != 0 && nonce != NULL) { + int optlen = sizeof(struct nd_opt_hdr) + ND_OPT_NONCE_LEN; + struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)(nd_ns + 1); + /* 8-byte alignment is required. */ + optlen = (optlen + 7) & ~7; + m->m_pkthdr.len += optlen; + m->m_len += optlen; + icmp6len += optlen; + bzero((caddr_t)nd_opt, optlen); + nd_opt->nd_opt_type = ND_OPT_NONCE; + nd_opt->nd_opt_len = optlen >> 3; + bcopy(nonce, (caddr_t)(nd_opt + 1), ND_OPT_NONCE_LEN); + } ip6->ip6_plen = htons((u_short)icmp6len); nd_ns->nd_ns_cksum = 0; nd_ns->nd_ns_cksum = @@ -570,7 +607,8 @@ nd6_ns_output(struct ifnet *ifp, const s m_tag_prepend(m, mtag); } - ip6_output(m, NULL, &ro, dad ? IPV6_UNSPECSRC : 0, &im6o, NULL, NULL); + ip6_output(m, NULL, &ro, (nonce != NULL) ? IPV6_UNSPECSRC : 0, + &im6o, NULL, NULL); icmp6_ifstat_inc(ifp, ifs6_out_msg); icmp6_ifstat_inc(ifp, ifs6_out_neighborsolicit); ICMP6STAT_INC(icp6s_outhist[ND_NEIGHBOR_SOLICIT]); @@ -588,6 +626,15 @@ nd6_ns_output(struct ifnet *ifp, const s return; } +#ifndef BURN_BRIDGES +void +nd6_ns_output(struct ifnet *ifp, const struct in6_addr *daddr6, + const struct in6_addr *taddr6, struct llentry *ln, uint8_t *nonce) +{ + + nd6_ns_output_fib(ifp, daddr6, taddr6, ln, nonce, RT_DEFAULT_FIB); +} +#endif /* * Neighbor advertisement input handling. * @@ -617,7 +664,6 @@ nd6_na_input(struct mbuf *m, int off, in struct llentry *ln = NULL; union nd_opts ndopts; struct mbuf *chain = NULL; - struct m_tag *mtag; struct sockaddr_in6 sin6; char ip6bufs[INET6_ADDRSTRLEN], ip6bufd[INET6_ADDRSTRLEN]; @@ -644,6 +690,7 @@ nd6_na_input(struct mbuf *m, int off, in is_router = ((flags & ND_NA_FLAG_ROUTER) != 0); is_solicited = ((flags & ND_NA_FLAG_SOLICITED) != 0); is_override = ((flags & ND_NA_FLAG_OVERRIDE) != 0); + memset(&sin6, 0, sizeof(sin6)); taddr6 = nd_na->nd_na_target; if (in6_setscope(&taddr6, ifp, NULL)) @@ -882,43 +929,15 @@ nd6_na_input(struct mbuf *m, int off, in * rt->rt_flags &= ~RTF_REJECT; */ ln->la_asked = 0; - if (ln->la_hold) { - struct mbuf *m_hold, *m_hold_next; - - /* - * reset the la_hold in advance, to explicitly - * prevent a la_hold lookup in nd6_output() - * (wouldn't happen, though...) - */ - for (m_hold = ln->la_hold, ln->la_hold = NULL; - m_hold; m_hold = m_hold_next) { - m_hold_next = m_hold->m_nextpkt; - m_hold->m_nextpkt = NULL; - /* - * we assume ifp is not a loopback here, so just set - * the 2nd argument as the 1st one. - */ - - if (send_sendso_input_hook != NULL) { - mtag = m_tag_get(PACKET_TAG_ND_OUTGOING, - sizeof(unsigned short), M_NOWAIT); - if (mtag == NULL) - goto bad; - m_tag_prepend(m, mtag); - } - - nd6_output_lle(ifp, ifp, m_hold, L3_ADDR_SIN6(ln), NULL, ln, &chain); *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***