diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2016-10-07 15:10:20 +0200 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2017-01-10 09:53:31 +0100 |
commit | c40e45b75eb76d79a05c7fa85c1fa9b5c728a12f (patch) | |
tree | ad4f2519067709f00ab98b3c591186c26dc3a21f /freebsd/sys/netinet6/nd6_nbr.c | |
parent | userspace-header-gen.py: Simplify program ports (diff) | |
download | rtems-libbsd-c40e45b75eb76d79a05c7fa85c1fa9b5c728a12f.tar.bz2 |
Update to FreeBSD head 2016-08-23
Git mirror commit 9fe7c416e6abb28b1398fd3e5687099846800cfd.
Diffstat (limited to 'freebsd/sys/netinet6/nd6_nbr.c')
-rw-r--r-- | freebsd/sys/netinet6/nd6_nbr.c | 787 |
1 files changed, 389 insertions, 398 deletions
diff --git a/freebsd/sys/netinet6/nd6_nbr.c b/freebsd/sys/netinet6/nd6_nbr.c index cb765549..df50fa93 100644 --- a/freebsd/sys/netinet6/nd6_nbr.c +++ b/freebsd/sys/netinet6/nd6_nbr.c @@ -42,6 +42,7 @@ __FBSDID("$FreeBSD$"); #include <rtems/bsd/sys/param.h> #include <sys/systm.h> #include <sys/malloc.h> +#include <sys/libkern.h> #include <rtems/bsd/sys/lock.h> #include <sys/rwlock.h> #include <sys/mbuf.h> @@ -50,9 +51,11 @@ __FBSDID("$FreeBSD$"); #include <sys/time.h> #include <sys/kernel.h> #include <rtems/bsd/sys/errno.h> +#include <sys/sysctl.h> #include <sys/syslog.h> #include <sys/queue.h> #include <sys/callout.h> +#include <sys/refcount.h> #include <net/if.h> #include <net/if_types.h> @@ -62,11 +65,11 @@ __FBSDID("$FreeBSD$"); #ifdef RADIX_MPATH #include <net/radix_mpath.h> #endif +#include <net/vnet.h> #include <netinet/in.h> #include <netinet/in_var.h> #include <net/if_llatbl.h> -#define L3_ADDR_SIN6(le) ((struct sockaddr_in6 *) L3_ADDR(le)) #include <netinet6/in6_var.h> #include <netinet6/in6_ifattach.h> #include <netinet/ip6.h> @@ -80,19 +83,32 @@ __FBSDID("$FreeBSD$"); #define SDL(s) ((struct sockaddr_dl *)s) struct dadq; -static struct dadq *nd6_dad_find(struct ifaddr *); -static void nd6_dad_starttimer(struct dadq *, int); +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, int); static void nd6_dad_stoptimer(struct dadq *); static void nd6_dad_timer(struct dadq *); -static void nd6_dad_ns_output(struct dadq *, struct ifaddr *); -static void nd6_dad_ns_input(struct ifaddr *); +static void nd6_dad_duplicated(struct ifaddr *, struct dadq *); +static void nd6_dad_ns_output(struct dadq *); +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 *, const struct in6_addr *, uint8_t *, u_int); -VNET_DEFINE(int, dad_ignore_ns) = 0; /* ignore NS in DAD - specwise incorrect*/ -VNET_DEFINE(int, dad_maxtry) = 15; /* max # of *tries* to transmit DAD packet */ -#define V_dad_ignore_ns VNET(dad_ignore_ns) +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_maxtry) = 15; /* max # of *tries* to + transmit DAD packet */ #define V_dad_maxtry VNET(dad_maxtry) /* @@ -229,42 +245,40 @@ nd6_ns_input(struct mbuf *m, int off, int icmp6len) /* (1) and (3) check. */ if (ifp->if_carp) ifa = (*carp_iamatch6_p)(ifp, &taddr6); - if (ifa == NULL) + else ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6); /* (2) check. */ if (ifa == NULL) { - struct route_in6 ro; - int need_proxy; + struct sockaddr_dl rt_gateway; + struct rt_addrinfo info; + struct sockaddr_in6 dst6; - bzero(&ro, sizeof(ro)); - ro.ro_dst.sin6_len = sizeof(struct sockaddr_in6); - ro.ro_dst.sin6_family = AF_INET6; - ro.ro_dst.sin6_addr = taddr6; + bzero(&dst6, sizeof(dst6)); + dst6.sin6_len = sizeof(struct sockaddr_in6); + dst6.sin6_family = AF_INET6; + dst6.sin6_addr = taddr6; + + bzero(&rt_gateway, sizeof(rt_gateway)); + rt_gateway.sdl_len = sizeof(rt_gateway); + bzero(&info, sizeof(info)); + info.rti_info[RTAX_GATEWAY] = (struct sockaddr *)&rt_gateway; /* Always use the default FIB. */ -#ifdef RADIX_MPATH - rtalloc_mpath_fib((struct route *)&ro, RTF_ANNOUNCE, - RT_DEFAULT_FIB); -#else - in6_rtalloc(&ro, RT_DEFAULT_FIB); -#endif - need_proxy = (ro.ro_rt && - (ro.ro_rt->rt_flags & RTF_ANNOUNCE) != 0 && - ro.ro_rt->rt_gateway->sa_family == AF_LINK); - if (ro.ro_rt != NULL) { - if (need_proxy) - proxydl = *SDL(ro.ro_rt->rt_gateway); - RTFREE(ro.ro_rt); - } - if (need_proxy) { - /* - * proxy NDP for single entry - */ - ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, - IN6_IFF_NOTREADY|IN6_IFF_ANYCAST); - if (ifa) - proxy = 1; + if (rib_lookup_info(RT_DEFAULT_FIB, (struct sockaddr *)&dst6, + 0, 0, &info) == 0) { + if ((info.rti_flags & RTF_ANNOUNCE) != 0 && + rt_gateway.sdl_family == AF_LINK) { + + /* + * proxy NDP for single entry + */ + proxydl = *SDL(&rt_gateway); + ifa = (struct ifaddr *)in6ifa_ifpforlinklocal( + ifp, IN6_IFF_NOTREADY|IN6_IFF_ANYCAST); + if (ifa) + proxy = 1; + } } } if (ifa == NULL) { @@ -316,7 +330,7 @@ nd6_ns_input(struct mbuf *m, int off, int icmp6len) * 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 +391,14 @@ nd6_ns_input(struct mbuf *m, int off, int icmp6len) * 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 *saddr6, + const struct in6_addr *daddr6, const struct in6_addr *taddr6, + uint8_t *nonce, u_int fibnum) { struct mbuf *m; struct m_tag *mtag; @@ -392,7 +408,6 @@ nd6_ns_output(struct ifnet *ifp, const struct in6_addr *daddr6, int icmp6len; int maxlen; caddr_t mac; - struct route_in6 ro; if (IN6_IS_ADDR_MULTICAST(taddr6)) return; @@ -400,27 +415,17 @@ nd6_ns_output(struct ifnet *ifp, const struct in6_addr *daddr6, /* estimate the size of message */ maxlen = sizeof(*ip6) + sizeof(*nd_ns); maxlen += (sizeof(struct nd_opt_hdr) + ifp->if_addrlen + 7) & ~7; - if (max_linkhdr + maxlen >= MCLBYTES) { -#ifdef DIAGNOSTIC - printf("nd6_ns_output: max_linkhdr + maxlen >= MCLBYTES " - "(%d + %d > %d)\n", max_linkhdr, maxlen, MCLBYTES); -#endif - return; - } + KASSERT(max_linkhdr + maxlen <= MCLBYTES, ( + "%s: max_linkhdr + maxlen > MCLBYTES (%d + %d > %d)", + __func__, max_linkhdr, maxlen, MCLBYTES)); - MGETHDR(m, M_DONTWAIT, MT_DATA); - if (m && max_linkhdr + maxlen >= MHLEN) { - MCLGET(m, M_DONTWAIT); - if ((m->m_flags & M_EXT) == 0) { - m_free(m); - m = NULL; - } - } + 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->m_pkthdr.rcvif = NULL; - - bzero(&ro, sizeof(ro)); + M_SETFIB(m, fibnum); if (daddr6 == NULL || IN6_IS_ADDR_MULTICAST(daddr6)) { m->m_flags |= M_MCAST; @@ -431,7 +436,7 @@ nd6_ns_output(struct ifnet *ifp, const struct in6_addr *daddr6, 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 M_ALIGN() equivalent? */ /* fill neighbor solicitation packet */ ip6 = mtod(m, struct ip6_hdr *); @@ -453,8 +458,8 @@ nd6_ns_output(struct ifnet *ifp, const struct in6_addr *daddr6, if (in6_setscope(&ip6->ip6_dst, ifp, NULL) != 0) goto bad; } - if (!dad) { - struct ifaddr *ifa; + if (nonce == NULL) { + struct ifaddr *ifa = NULL; /* * RFC2461 7.2.2: @@ -466,60 +471,33 @@ nd6_ns_output(struct ifnet *ifp, const struct in6_addr *daddr6, * interface should be used." * * We use the source address for the prompting packet - * (saddr6), if: - * - saddr6 is given from the caller (by giving "ln"), and - * - saddr6 belongs to the outgoing interface. + * (saddr6), if saddr6 belongs to the outgoing interface. * Otherwise, we perform the source address selection as usual. */ - struct in6_addr *hsrc; - hsrc = NULL; - if (ln != NULL) { - LLE_RLOCK(ln); - if (ln->la_hold != NULL) { - struct ip6_hdr *hip6; /* hold ip6 */ - - /* - * assuming every packet in la_hold has the same IP - * header - */ - hip6 = mtod(ln->la_hold, struct ip6_hdr *); - /* XXX pullup? */ - if (sizeof(*hip6) < ln->la_hold->m_len) { - ip6->ip6_src = hip6->ip6_src; - hsrc = &hip6->ip6_src; - } - } - LLE_RUNLOCK(ln); - } - if (hsrc && (ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, - hsrc)) != NULL) { + if (saddr6 != NULL) + ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, saddr6); + if (ifa != NULL) { /* ip6_src set already. */ + ip6->ip6_src = *saddr6; ifa_free(ifa); } else { int error; - struct sockaddr_in6 dst_sa; - struct in6_addr src_in; - struct ifnet *oifp; - - bzero(&dst_sa, sizeof(dst_sa)); - dst_sa.sin6_family = AF_INET6; - dst_sa.sin6_len = sizeof(dst_sa); - dst_sa.sin6_addr = ip6->ip6_dst; - - oifp = ifp; - error = in6_selectsrc(&dst_sa, NULL, - NULL, &ro, NULL, &oifp, &src_in); + struct in6_addr dst6, src6; + uint32_t scopeid; + + in6_splitscope(&ip6->ip6_dst, &dst6, &scopeid); + error = in6_selectsrc_addr(RT_DEFAULT_FIB, &dst6, + scopeid, ifp, &src6, NULL); if (error) { char ip6buf[INET6_ADDRSTRLEN]; - nd6log((LOG_DEBUG, - "nd6_ns_output: source can't be " - "determined: dst=%s, error=%d\n", - ip6_sprintf(ip6buf, &dst_sa.sin6_addr), + nd6log((LOG_DEBUG, "%s: source can't be " + "determined: dst=%s, error=%d\n", __func__, + ip6_sprintf(ip6buf, &dst6), error)); goto bad; } - ip6->ip6_src = src_in; + ip6->ip6_src = src6; } } else { /* @@ -550,7 +528,7 @@ nd6_ns_output(struct ifnet *ifp, const struct in6_addr *daddr6, * 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... */ @@ -564,7 +542,26 @@ nd6_ns_output(struct ifnet *ifp, const struct in6_addr *daddr6, 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 RFC 7527. + * 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 = @@ -579,24 +576,27 @@ nd6_ns_output(struct ifnet *ifp, const struct in6_addr *daddr6, m_tag_prepend(m, mtag); } - ip6_output(m, NULL, &ro, dad ? IPV6_UNSPECSRC : 0, &im6o, NULL, NULL); + ip6_output(m, NULL, NULL, (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]); - /* We don't cache this route. */ - RO_RTFREE(&ro); - return; bad: - if (ro.ro_rt) { - RTFREE(ro.ro_rt); - } m_freem(m); - return; } +#ifndef BURN_BRIDGES +void +nd6_ns_output(struct ifnet *ifp, const struct in6_addr *saddr6, + const struct in6_addr *daddr6, const struct in6_addr *taddr6,uint8_t *nonce) +{ + + nd6_ns_output_fib(ifp, saddr6, daddr6, taddr6, nonce, RT_DEFAULT_FIB); +} +#endif /* * Neighbor advertisement input handling. * @@ -626,8 +626,10 @@ nd6_na_input(struct mbuf *m, int off, int icmp6len) struct llentry *ln = NULL; union nd_opts ndopts; struct mbuf *chain = NULL; - struct m_tag *mtag; struct sockaddr_in6 sin6; + u_char linkhdr[LLE_MAX_LINKHDR]; + size_t linkhdrsize; + int lladdr_off; char ip6bufs[INET6_ADDRSTRLEN], ip6bufd[INET6_ADDRSTRLEN]; if (ip6->ip6_hlim != 255) { @@ -653,6 +655,7 @@ nd6_na_input(struct mbuf *m, int off, int icmp6len) 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)) @@ -685,7 +688,14 @@ nd6_na_input(struct mbuf *m, int off, int icmp6len) lladdrlen = ndopts.nd_opts_tgt_lladdr->nd_opt_len << 3; } - ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6); + /* + * This effectively disables the DAD check on a non-master CARP + * address. + */ + if (ifp->if_carp) + ifa = (*carp_iamatch6_p)(ifp, &taddr6); + else + ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6); /* * Target address matches one of my interface address. @@ -742,20 +752,21 @@ nd6_na_input(struct mbuf *m, int off, int icmp6len) /* * Record link-layer address, and update the state. */ - bcopy(lladdr, &ln->ll_addr, ifp->if_addrlen); - ln->la_flags |= LLE_VALID; - EVENTHANDLER_INVOKE(lle_event, ln, LLENTRY_RESOLVED); - if (is_solicited) { - ln->ln_state = ND6_LLINFO_REACHABLE; - ln->ln_byhint = 0; - if (!ND6_LLINFO_PERMANENT(ln)) { - nd6_llinfo_settimer_locked(ln, - (long)ND_IFINFO(ln->lle_tbl->llt_ifp)->reachable * hz); - } - } else { - ln->ln_state = ND6_LLINFO_STALE; - nd6_llinfo_settimer_locked(ln, (long)V_nd6_gctimer * hz); + linkhdrsize = sizeof(linkhdr); + if (lltable_calc_llheader(ifp, AF_INET6, lladdr, + linkhdr, &linkhdrsize, &lladdr_off) != 0) + return; + + if (lltable_try_set_entry_addr(ifp, ln, linkhdr, linkhdrsize, + lladdr_off) == 0) { + ln = NULL; + goto freeit; } + EVENTHANDLER_INVOKE(lle_event, ln, LLENTRY_RESOLVED); + if (is_solicited) + nd6_llinfo_setstate(ln, ND6_LLINFO_REACHABLE); + else + nd6_llinfo_setstate(ln, ND6_LLINFO_STALE); if ((ln->ln_router = is_router) != 0) { /* * This means a router's state has changed from @@ -774,7 +785,7 @@ nd6_na_input(struct mbuf *m, int off, int icmp6len) llchange = 0; else { if (ln->la_flags & LLE_VALID) { - if (bcmp(lladdr, &ln->ll_addr, ifp->if_addrlen)) + if (bcmp(lladdr, ln->ll_addr, ifp->if_addrlen)) llchange = 1; else llchange = 0; @@ -806,10 +817,8 @@ nd6_na_input(struct mbuf *m, int off, int icmp6len) * If state is REACHABLE, make it STALE. * no other updates should be done. */ - if (ln->ln_state == ND6_LLINFO_REACHABLE) { - ln->ln_state = ND6_LLINFO_STALE; - nd6_llinfo_settimer_locked(ln, (long)V_nd6_gctimer * hz); - } + if (ln->ln_state == ND6_LLINFO_REACHABLE) + nd6_llinfo_setstate(ln, ND6_LLINFO_STALE); goto freeit; } else if (is_override /* (2a) */ || (!is_override && (lladdr != NULL && !llchange)) /* (2b) */ @@ -818,8 +827,15 @@ nd6_na_input(struct mbuf *m, int off, int icmp6len) * Update link-local address, if any. */ if (lladdr != NULL) { - bcopy(lladdr, &ln->ll_addr, ifp->if_addrlen); - ln->la_flags |= LLE_VALID; + linkhdrsize = sizeof(linkhdr); + if (lltable_calc_llheader(ifp, AF_INET6, lladdr, + linkhdr, &linkhdrsize, &lladdr_off) != 0) + goto freeit; + if (lltable_try_set_entry_addr(ifp, ln, linkhdr, + linkhdrsize, lladdr_off) == 0) { + ln = NULL; + goto freeit; + } EVENTHANDLER_INVOKE(lle_event, ln, LLENTRY_RESOLVED); } @@ -829,19 +845,11 @@ nd6_na_input(struct mbuf *m, int off, int icmp6len) * If not solicited and the link-layer address was * changed, make it STALE. */ - if (is_solicited) { - ln->ln_state = ND6_LLINFO_REACHABLE; - ln->ln_byhint = 0; - if (!ND6_LLINFO_PERMANENT(ln)) { - nd6_llinfo_settimer_locked(ln, - (long)ND_IFINFO(ifp)->reachable * hz); - } - } else { - if (lladdr != NULL && llchange) { - ln->ln_state = ND6_LLINFO_STALE; - nd6_llinfo_settimer_locked(ln, - (long)V_nd6_gctimer * hz); - } + if (is_solicited) + nd6_llinfo_setstate(ln, ND6_LLINFO_REACHABLE); + else { + if (lladdr != NULL && llchange) + nd6_llinfo_setstate(ln, ND6_LLINFO_STALE); } } @@ -851,31 +859,19 @@ nd6_na_input(struct mbuf *m, int off, int icmp6len) * Remove the sender from the Default Router List and * update the Destination Cache entries. */ - struct nd_defrouter *dr; - struct in6_addr *in6; - - in6 = &L3_ADDR_SIN6(ln)->sin6_addr; + struct ifnet *nd6_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. - */ - dr = defrouter_lookup(in6, ln->lle_tbl->llt_ifp); - if (dr) - defrtrlist_del(dr); - else if (ND_IFINFO(ln->lle_tbl->llt_ifp)->flags & - ND6_IFF_ACCEPT_RTADV) { + nd6_ifp = lltable_get_ifp(ln->lle_tbl); + if (!defrouter_remove(&ln->r_l3addr.addr6, nd6_ifp) && + (ND_IFINFO(nd6_ifp)->flags & + ND6_IFF_ACCEPT_RTADV) != 0) /* * 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. + * 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, ifp); - } } ln->ln_router = is_router; } @@ -884,43 +880,15 @@ nd6_na_input(struct mbuf *m, int off, int icmp6len) * 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); - } - } + if (ln->la_hold != NULL) + nd6_grab_holdchain(ln, &chain, &sin6); freeit: - if (ln != NULL) { - if (chain) - memcpy(&sin6, L3_ADDR_SIN6(ln), sizeof(sin6)); + if (ln != NULL) LLE_WUNLOCK(ln); - if (chain) - nd6_output_flush(ifp, ifp, chain, &sin6, NULL); - } + if (chain != NULL) + nd6_flush_holdchain(ifp, ifp, chain, &sin6); + if (checklink) pfxlist_onlink_check(); @@ -954,42 +922,30 @@ nd6_na_output_fib(struct ifnet *ifp, const struct in6_addr *daddr6_0, { struct mbuf *m; struct m_tag *mtag; - struct ifnet *oifp; struct ip6_hdr *ip6; struct nd_neighbor_advert *nd_na; struct ip6_moptions im6o; - struct in6_addr src, daddr6; - struct sockaddr_in6 dst_sa; + struct in6_addr daddr6, dst6, src6; + uint32_t scopeid; + int icmp6len, maxlen, error; caddr_t mac = NULL; - struct route_in6 ro; - - bzero(&ro, sizeof(ro)); daddr6 = *daddr6_0; /* make a local copy for modification */ /* 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 - printf("nd6_na_output: max_linkhdr + maxlen >= MCLBYTES " - "(%d + %d > %d)\n", max_linkhdr, maxlen, MCLBYTES); -#endif - return; - } + KASSERT(max_linkhdr + maxlen <= MCLBYTES, ( + "%s: max_linkhdr + maxlen > MCLBYTES (%d + %d > %d)", + __func__, max_linkhdr, maxlen, MCLBYTES)); - MGETHDR(m, M_DONTWAIT, MT_DATA); - if (m && max_linkhdr + maxlen >= MHLEN) { - MCLGET(m, M_DONTWAIT); - if ((m->m_flags & M_EXT) == 0) { - m_free(m); - m = NULL; - } - } + 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->m_pkthdr.rcvif = NULL; M_SETFIB(m, fibnum); if (IN6_IS_ADDR_MULTICAST(&daddr6)) { @@ -1001,7 +957,7 @@ nd6_na_output_fib(struct ifnet *ifp, const struct in6_addr *daddr6_0, 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 M_ALIGN() equivalent? */ /* fill neighbor advertisement packet */ ip6 = mtod(m, struct ip6_hdr *); @@ -1023,25 +979,21 @@ nd6_na_output_fib(struct ifnet *ifp, const struct in6_addr *daddr6_0, flags &= ~ND_NA_FLAG_SOLICITED; } ip6->ip6_dst = daddr6; - bzero(&dst_sa, sizeof(struct sockaddr_in6)); - dst_sa.sin6_family = AF_INET6; - dst_sa.sin6_len = sizeof(struct sockaddr_in6); - dst_sa.sin6_addr = daddr6; /* * Select a source whose scope is the same as that of the dest. */ - bcopy(&dst_sa, &ro.ro_dst, sizeof(dst_sa)); - oifp = ifp; - error = in6_selectsrc(&dst_sa, NULL, NULL, &ro, NULL, &oifp, &src); + in6_splitscope(&daddr6, &dst6, &scopeid); + error = in6_selectsrc_addr(RT_DEFAULT_FIB, &dst6, + scopeid, ifp, &src6, NULL); if (error) { char ip6buf[INET6_ADDRSTRLEN]; nd6log((LOG_DEBUG, "nd6_na_output: source can't be " "determined: dst=%s, error=%d\n", - ip6_sprintf(ip6buf, &dst_sa.sin6_addr), error)); + ip6_sprintf(ip6buf, &daddr6), error)); goto bad; } - ip6->ip6_src = src; + ip6->ip6_src = src6; nd_na = (struct nd_neighbor_advert *)(ip6 + 1); nd_na->nd_na_type = ND_NEIGHBOR_ADVERT; nd_na->nd_na_code = 0; @@ -1104,22 +1056,15 @@ nd6_na_output_fib(struct ifnet *ifp, const struct in6_addr *daddr6_0, m_tag_prepend(m, mtag); } - ip6_output(m, NULL, &ro, 0, &im6o, NULL, NULL); + ip6_output(m, NULL, NULL, 0, &im6o, NULL, NULL); icmp6_ifstat_inc(ifp, ifs6_out_msg); icmp6_ifstat_inc(ifp, ifs6_out_neighboradvert); ICMP6STAT_INC(icp6s_outhist[ND_NEIGHBOR_ADVERT]); - /* We don't cache this route. */ - RO_RTFREE(&ro); - return; bad: - if (ro.ro_rt) { - RTFREE(ro.ro_rt); - } m_freem(m); - return; } #ifndef BURN_BRIDGES @@ -1142,15 +1087,8 @@ nd6_ifptomac(struct ifnet *ifp) case IFT_ETHER: case IFT_FDDI: case IFT_IEEE1394: -#ifdef IFT_L2VLAN case IFT_L2VLAN: -#endif -#ifdef IFT_IEEE80211 case IFT_IEEE80211: -#endif -#ifdef IFT_CARP - case IFT_CARP: -#endif case IFT_INFINIBAND: case IFT_BRIDGE: case IFT_ISO88025: @@ -1168,31 +1106,80 @@ struct dadq { int dad_ns_ocount; /* NS sent so far */ int dad_ns_icount; int dad_na_icount; + int dad_ns_lcount; /* looped back NS */ + int dad_loopbackprobe; /* probing state for loopback detection */ struct callout dad_timer_ch; struct vnet *dad_vnet; + u_int dad_refcnt; +#define ND_OPT_NONCE_LEN32 \ + ((ND_OPT_NONCE_LEN + sizeof(uint32_t) - 1)/sizeof(uint32_t)) + uint32_t dad_nonce[ND_OPT_NONCE_LEN32]; }; static VNET_DEFINE(TAILQ_HEAD(, dadq), dadq); -VNET_DEFINE(int, dad_init) = 0; -#define V_dadq VNET(dadq) -#define V_dad_init VNET(dad_init) +static VNET_DEFINE(struct rwlock, dad_rwlock); +#define V_dadq VNET(dadq) +#define V_dad_rwlock VNET(dad_rwlock) + +#define DADQ_RLOCK() rw_rlock(&V_dad_rwlock) +#define DADQ_RUNLOCK() rw_runlock(&V_dad_rwlock) +#define DADQ_WLOCK() rw_wlock(&V_dad_rwlock) +#define DADQ_WUNLOCK() rw_wunlock(&V_dad_rwlock) + +static void +nd6_dad_add(struct dadq *dp) +{ + + DADQ_WLOCK(); + TAILQ_INSERT_TAIL(&V_dadq, dp, dad_list); + DADQ_WUNLOCK(); +} + +static void +nd6_dad_del(struct dadq *dp) +{ + + DADQ_WLOCK(); + TAILQ_REMOVE(&V_dadq, dp, dad_list); + DADQ_WUNLOCK(); + nd6_dad_rele(dp); +} static struct dadq * -nd6_dad_find(struct ifaddr *ifa) +nd6_dad_find(struct ifaddr *ifa, struct nd_opt_nonce *n) { struct dadq *dp; - TAILQ_FOREACH(dp, &V_dadq, dad_list) - if (dp->dad_ifa == ifa) - return (dp); + DADQ_RLOCK(); + TAILQ_FOREACH(dp, &V_dadq, dad_list) { + if (dp->dad_ifa != ifa) + continue; + /* + * Skip if the nonce matches the received one. + * +2 in the length is required because of type and + * length fields are included in a header. + */ + if (n != NULL && + n->nd_opt_nonce_len == (ND_OPT_NONCE_LEN + 2) / 8 && + memcmp(&n->nd_opt_nonce[0], &dp->dad_nonce[0], + ND_OPT_NONCE_LEN) == 0) { + dp->dad_ns_lcount++; + continue; + } + refcount_acquire(&dp->dad_refcnt); + break; + } + DADQ_RUNLOCK(); - return (NULL); + return (dp); } static void -nd6_dad_starttimer(struct dadq *dp, int ticks) +nd6_dad_starttimer(struct dadq *dp, int ticks, int send_ns) { + if (send_ns != 0) + nd6_dad_ns_output(dp); callout_reset(&dp->dad_timer_ch, ticks, (void (*)(void *))nd6_dad_timer, (void *)dp); } @@ -1201,7 +1188,25 @@ static void nd6_dad_stoptimer(struct dadq *dp) { - callout_stop(&dp->dad_timer_ch); + callout_drain(&dp->dad_timer_ch); +} + +static void +nd6_dad_rele(struct dadq *dp) +{ + + if (refcount_release(&dp->dad_refcnt)) { + ifa_free(dp->dad_ifa); + free(dp, M_IP6NDP); + } +} + +void +nd6_dad_init(void) +{ + + rw_init(&V_dad_rwlock, "nd6 DAD queue"); + TAILQ_INIT(&V_dadq); } /* @@ -1214,11 +1219,6 @@ nd6_dad_start(struct ifaddr *ifa, int delay) struct dadq *dp; char ip6buf[INET6_ADDRSTRLEN]; - if (!V_dad_init) { - TAILQ_INIT(&V_dadq); - V_dad_init++; - } - /* * If we don't need DAD, don't do it. * There are several cases: @@ -1243,17 +1243,26 @@ nd6_dad_start(struct ifaddr *ifa, int delay) } if (ifa->ifa_ifp == NULL) panic("nd6_dad_start: ifa->ifa_ifp == NULL"); - if (!(ifa->ifa_ifp->if_flags & IFF_UP)) { + if (ND_IFINFO(ifa->ifa_ifp)->flags & ND6_IFF_NO_DAD) { + ia->ia6_flags &= ~IN6_IFF_TENTATIVE; return; } - if (ND_IFINFO(ifa->ifa_ifp)->flags & ND6_IFF_IFDISABLED) + if (!(ifa->ifa_ifp->if_flags & IFF_UP) || + !(ifa->ifa_ifp->if_drv_flags & IFF_DRV_RUNNING) || + (ND_IFINFO(ifa->ifa_ifp)->flags & ND6_IFF_IFDISABLED)) { + ia->ia6_flags |= IN6_IFF_TENTATIVE; return; - if (nd6_dad_find(ifa) != NULL) { - /* DAD already in progress */ + } + if ((dp = nd6_dad_find(ifa, NULL)) != NULL) { + /* + * DAD is already in progress. Let the existing entry + * finish it. + */ + nd6_dad_rele(dp); return; } - dp = malloc(sizeof(*dp), M_IP6NDP, M_NOWAIT); + dp = malloc(sizeof(*dp), M_IP6NDP, M_NOWAIT | M_ZERO); if (dp == NULL) { log(LOG_ERR, "nd6_dad_start: memory allocation failed for " "%s(%s)\n", @@ -1261,13 +1270,10 @@ nd6_dad_start(struct ifaddr *ifa, int delay) ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???"); return; } - bzero(dp, sizeof(*dp)); callout_init(&dp->dad_timer_ch, 0); #ifdef VIMAGE dp->dad_vnet = curvnet; #endif - TAILQ_INSERT_TAIL(&V_dadq, (struct dadq *)dp, dad_list); - nd6log((LOG_DEBUG, "%s: starting DAD for %s\n", if_name(ifa->ifa_ifp), ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr))); @@ -1278,17 +1284,14 @@ nd6_dad_start(struct ifaddr *ifa, int delay) * (re)initialization. */ dp->dad_ifa = ifa; - ifa_ref(ifa); /* just for safety */ + ifa_ref(dp->dad_ifa); dp->dad_count = V_ip6_dad_count; dp->dad_ns_icount = dp->dad_na_icount = 0; dp->dad_ns_ocount = dp->dad_ns_tcount = 0; - if (delay == 0) { - nd6_dad_ns_output(dp, ifa); - nd6_dad_starttimer(dp, - (long)ND_IFINFO(ifa->ifa_ifp)->retrans * hz / 1000); - } else { - nd6_dad_starttimer(dp, delay); - } + dp->dad_ns_lcount = dp->dad_loopbackprobe = 0; + refcount_init(&dp->dad_refcnt, 1); + nd6_dad_add(dp); + nd6_dad_starttimer(dp, delay, 0); } /* @@ -1299,9 +1302,7 @@ nd6_dad_stop(struct ifaddr *ifa) { struct dadq *dp; - if (!V_dad_init) - return; - dp = nd6_dad_find(ifa); + dp = nd6_dad_find(ifa, NULL); if (!dp) { /* DAD wasn't started yet */ return; @@ -1309,53 +1310,61 @@ nd6_dad_stop(struct ifaddr *ifa) nd6_dad_stoptimer(dp); - TAILQ_REMOVE(&V_dadq, (struct dadq *)dp, dad_list); - free(dp, M_IP6NDP); - dp = NULL; - ifa_free(ifa); + /* + * The DAD queue entry may have been removed by nd6_dad_timer() while + * we were waiting for it to stop, so re-do the lookup. + */ + nd6_dad_rele(dp); + if (nd6_dad_find(ifa, NULL) == NULL) + return; + + nd6_dad_del(dp); + nd6_dad_rele(dp); } static void nd6_dad_timer(struct dadq *dp) { CURVNET_SET(dp->dad_vnet); - int s; struct ifaddr *ifa = dp->dad_ifa; + struct ifnet *ifp = dp->dad_ifa->ifa_ifp; struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa; char ip6buf[INET6_ADDRSTRLEN]; - s = splnet(); /* XXX */ - /* Sanity check */ if (ia == NULL) { log(LOG_ERR, "nd6_dad_timer: called with null parameter\n"); - goto done; + goto err; + } + if (ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED) { + /* Do not need DAD for ifdisabled interface. */ + log(LOG_ERR, "nd6_dad_timer: cancel DAD on %s because of " + "ND6_IFF_IFDISABLED.\n", ifp->if_xname); + goto err; } if (ia->ia6_flags & IN6_IFF_DUPLICATED) { log(LOG_ERR, "nd6_dad_timer: called with duplicated address " "%s(%s)\n", ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr), ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???"); - goto done; + goto err; } if ((ia->ia6_flags & IN6_IFF_TENTATIVE) == 0) { log(LOG_ERR, "nd6_dad_timer: called with non-tentative address " "%s(%s)\n", ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr), ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???"); - goto done; + goto err; } - /* timeouted with IFF_{RUNNING,UP} check */ - if (dp->dad_ns_tcount > V_dad_maxtry) { - nd6log((LOG_INFO, "%s: could not run DAD, driver problem?\n", + /* Stop DAD if the interface is down even after dad_maxtry attempts. */ + if ((dp->dad_ns_tcount > V_dad_maxtry) && + (((ifp->if_flags & IFF_UP) == 0) || + ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0))) { + nd6log((LOG_INFO, "%s: could not run DAD " + "because the interface was down or not running.\n", if_name(ifa->ifa_ifp))); - - TAILQ_REMOVE(&V_dadq, (struct dadq *)dp, dad_list); - free(dp, M_IP6NDP); - dp = NULL; - ifa_free(ifa); - goto done; + goto err; } /* Need more checks? */ @@ -1363,84 +1372,85 @@ nd6_dad_timer(struct dadq *dp) /* * We have more NS to go. Send NS packet for DAD. */ - nd6_dad_ns_output(dp, ifa); nd6_dad_starttimer(dp, - (long)ND_IFINFO(ifa->ifa_ifp)->retrans * hz / 1000); + (long)ND_IFINFO(ifa->ifa_ifp)->retrans * hz / 1000, 1); + goto done; } else { /* * We have transmitted sufficient number of DAD packets. * See what we've got. */ - int duplicate; - - duplicate = 0; - - if (dp->dad_na_icount) { + if (dp->dad_ns_icount > 0 || dp->dad_na_icount > 0) + /* We've seen NS or NA, means DAD has failed. */ + nd6_dad_duplicated(ifa, dp); + else if (V_dad_enhanced != 0 && + dp->dad_ns_lcount > 0 && + dp->dad_ns_lcount > dp->dad_loopbackprobe) { /* - * the check is in nd6_dad_na_input(), - * but just in case + * Sec. 4.1 in RFC 7527 requires transmission of + * additional probes until the loopback condition + * becomes clear when a looped back probe is detected. */ - duplicate++; - } - - if (dp->dad_ns_icount) { - /* We've seen NS, means DAD has failed. */ - duplicate++; - } - - if (duplicate) { - /* (*dp) will be freed in nd6_dad_duplicated() */ - dp = NULL; - nd6_dad_duplicated(ifa); + log(LOG_ERR, "%s: a looped back NS message is " + "detected during DAD for %s. " + "Another DAD probes are being sent.\n", + if_name(ifa->ifa_ifp), + ip6_sprintf(ip6buf, IFA_IN6(ifa))); + dp->dad_loopbackprobe = dp->dad_ns_lcount; + /* + * Send an NS immediately and increase dad_count by + * V_nd6_mmaxtries - 1. + */ + dp->dad_count = + dp->dad_ns_ocount + V_nd6_mmaxtries - 1; + nd6_dad_starttimer(dp, + (long)ND_IFINFO(ifa->ifa_ifp)->retrans * hz / 1000, + 1); + goto done; } else { /* * We are done with DAD. No NA came, no NS came. - * No duplicate address found. + * No duplicate address found. Check IFDISABLED flag + * again in case that it is changed between the + * beginning of this function and here. */ - ia->ia6_flags &= ~IN6_IFF_TENTATIVE; + if ((ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED) == 0) + ia->ia6_flags &= ~IN6_IFF_TENTATIVE; nd6log((LOG_DEBUG, "%s: DAD complete for %s - no duplicates found\n", if_name(ifa->ifa_ifp), ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr))); - - TAILQ_REMOVE(&V_dadq, (struct dadq *)dp, dad_list); - free(dp, M_IP6NDP); - dp = NULL; - ifa_free(ifa); + if (dp->dad_ns_lcount > 0) + log(LOG_ERR, "%s: DAD completed while " + "a looped back NS message is detected " + "during DAD for %s.\n", + if_name(ifa->ifa_ifp), + ip6_sprintf(ip6buf, IFA_IN6(ifa))); } } - +err: + nd6_dad_del(dp); done: - splx(s); CURVNET_RESTORE(); } -void -nd6_dad_duplicated(struct ifaddr *ifa) +static void +nd6_dad_duplicated(struct ifaddr *ifa, struct dadq *dp) { struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa; struct ifnet *ifp; - struct dadq *dp; char ip6buf[INET6_ADDRSTRLEN]; - dp = nd6_dad_find(ifa); - if (dp == NULL) { - log(LOG_ERR, "nd6_dad_duplicated: DAD structure not found\n"); - return; - } - log(LOG_ERR, "%s: DAD detected duplicate IPv6 address %s: " - "NS in/out=%d/%d, NA in=%d\n", + "NS in/out/loopback=%d/%d/%d, NA in=%d\n", if_name(ifa->ifa_ifp), ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr), - dp->dad_ns_icount, dp->dad_ns_ocount, dp->dad_na_icount); + dp->dad_ns_icount, dp->dad_ns_ocount, dp->dad_ns_lcount, + dp->dad_na_icount); ia->ia6_flags &= ~IN6_IFF_TENTATIVE; ia->ia6_flags |= IN6_IFF_DUPLICATED; - /* We are done with DAD, with duplicate address found. (failure) */ - nd6_dad_stoptimer(dp); - ifp = ifa->ifa_ifp; log(LOG_ERR, "%s: DAD complete for %s - duplicate found\n", if_name(ifp), ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr)); @@ -1466,9 +1476,7 @@ nd6_dad_duplicated(struct ifaddr *ifa) case IFT_FDDI: case IFT_ATM: case IFT_IEEE1394: -#ifdef IFT_IEEE80211 case IFT_IEEE80211: -#endif case IFT_INFINIBAND: in6 = ia->ia_addr.sin6_addr; if (in6_get_hw_ifid(ifp, &in6) == 0 && @@ -1481,18 +1489,14 @@ nd6_dad_duplicated(struct ifaddr *ifa) break; } } - - TAILQ_REMOVE(&V_dadq, (struct dadq *)dp, dad_list); - free(dp, M_IP6NDP); - dp = NULL; - ifa_free(ifa); } static void -nd6_dad_ns_output(struct dadq *dp, struct ifaddr *ifa) +nd6_dad_ns_output(struct dadq *dp) { - struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa; - struct ifnet *ifp = ifa->ifa_ifp; + struct in6_ifaddr *ia = (struct in6_ifaddr *)dp->dad_ifa; + struct ifnet *ifp = dp->dad_ifa->ifa_ifp; + int i; dp->dad_ns_tcount++; if ((ifp->if_flags & IFF_UP) == 0) { @@ -1503,17 +1507,29 @@ nd6_dad_ns_output(struct dadq *dp, struct ifaddr *ifa) } dp->dad_ns_ocount++; - nd6_ns_output(ifp, NULL, &ia->ia_addr.sin6_addr, NULL, 1); + if (V_dad_enhanced != 0) { + for (i = 0; i < ND_OPT_NONCE_LEN32; i++) + dp->dad_nonce[i] = arc4random(); + /* + * XXXHRS: Note that in the case that + * DupAddrDetectTransmits > 1, multiple NS messages with + * different nonces can be looped back in an unexpected + * order. The current implementation recognizes only + * the latest nonce on the sender side. Practically it + * should work well in almost all cases. + */ + } + nd6_ns_output(ifp, NULL, NULL, &ia->ia_addr.sin6_addr, + (uint8_t *)&dp->dad_nonce[0]); } static void -nd6_dad_ns_input(struct ifaddr *ifa) +nd6_dad_ns_input(struct ifaddr *ifa, struct nd_opt_nonce *ndopt_nonce) { struct in6_ifaddr *ia; struct ifnet *ifp; const struct in6_addr *taddr6; struct dadq *dp; - int duplicate; if (ifa == NULL) panic("ifa == NULL in nd6_dad_ns_input"); @@ -1521,39 +1537,15 @@ nd6_dad_ns_input(struct ifaddr *ifa) ia = (struct in6_ifaddr *)ifa; ifp = ifa->ifa_ifp; taddr6 = &ia->ia_addr.sin6_addr; - duplicate = 0; - dp = nd6_dad_find(ifa); - - /* Quickhack - completely ignore DAD NS packets */ - if (V_dad_ignore_ns) { - char ip6buf[INET6_ADDRSTRLEN]; - nd6log((LOG_INFO, - "nd6_dad_ns_input: ignoring DAD NS packet for " - "address %s(%s)\n", ip6_sprintf(ip6buf, taddr6), - if_name(ifa->ifa_ifp))); + /* Ignore Nonce option when Enhanced DAD is disabled. */ + if (V_dad_enhanced == 0) + ndopt_nonce = NULL; + dp = nd6_dad_find(ifa, ndopt_nonce); + if (dp == NULL) return; - } - - /* - * if I'm yet to start DAD, someone else started using this address - * first. I have a duplicate and you win. - */ - if (dp == NULL || dp->dad_ns_ocount == 0) - duplicate++; - - /* XXX more checks for loopback situation - see nd6_dad_timer too */ - if (duplicate) { - dp = NULL; /* will be freed in nd6_dad_duplicated() */ - nd6_dad_duplicated(ifa); - } else { - /* - * not sure if I got a duplicate. - * increment ns count and see what happens. - */ - if (dp) - dp->dad_ns_icount++; - } + dp->dad_ns_icount++; + nd6_dad_rele(dp); } static void @@ -1564,10 +1556,9 @@ nd6_dad_na_input(struct ifaddr *ifa) if (ifa == NULL) panic("ifa == NULL in nd6_dad_na_input"); - dp = nd6_dad_find(ifa); - if (dp) + dp = nd6_dad_find(ifa, NULL); + if (dp != NULL) { dp->dad_na_icount++; - - /* remove the address. */ - nd6_dad_duplicated(ifa); + nd6_dad_rele(dp); + } } |