nd6_nbr.c revision 183550
1139826Simp/*-
253541Sshin * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
353541Sshin * All rights reserved.
453541Sshin *
553541Sshin * Redistribution and use in source and binary forms, with or without
653541Sshin * modification, are permitted provided that the following conditions
753541Sshin * are met:
853541Sshin * 1. Redistributions of source code must retain the above copyright
953541Sshin *    notice, this list of conditions and the following disclaimer.
1053541Sshin * 2. Redistributions in binary form must reproduce the above copyright
1153541Sshin *    notice, this list of conditions and the following disclaimer in the
1253541Sshin *    documentation and/or other materials provided with the distribution.
1353541Sshin * 3. Neither the name of the project nor the names of its contributors
1453541Sshin *    may be used to endorse or promote products derived from this software
1553541Sshin *    without specific prior written permission.
1653541Sshin *
1753541Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
1853541Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1953541Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2053541Sshin * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2153541Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2253541Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2353541Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2453541Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2553541Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2653541Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2753541Sshin * SUCH DAMAGE.
28174510Sobrien *
29174510Sobrien *	$KAME: nd6_nbr.c,v 1.86 2002/01/21 02:33:04 jinmei Exp $
3053541Sshin */
3153541Sshin
32174510Sobrien#include <sys/cdefs.h>
33174510Sobrien__FBSDID("$FreeBSD: head/sys/netinet6/nd6_nbr.c 183550 2008-10-02 15:37:58Z zec $");
34174510Sobrien
3562587Sitojun#include "opt_inet.h"
3662587Sitojun#include "opt_inet6.h"
37142215Sglebius#include "opt_ipsec.h"
38142215Sglebius#include "opt_carp.h"
39178167Sqingli#include "opt_mpath.h"
4055009Sshin
4153541Sshin#include <sys/param.h>
4253541Sshin#include <sys/systm.h>
4353541Sshin#include <sys/malloc.h>
4453541Sshin#include <sys/mbuf.h>
4553541Sshin#include <sys/socket.h>
4653541Sshin#include <sys/sockio.h>
4753541Sshin#include <sys/time.h>
4853541Sshin#include <sys/kernel.h>
4953541Sshin#include <sys/errno.h>
5053541Sshin#include <sys/syslog.h>
5153541Sshin#include <sys/queue.h>
5278064Sume#include <sys/callout.h>
53181803Sbz#include <sys/vimage.h>
5453541Sshin
5553541Sshin#include <net/if.h>
5653541Sshin#include <net/if_types.h>
5753541Sshin#include <net/if_dl.h>
58147306Sbrooks#include <net/if_var.h>
5953541Sshin#include <net/route.h>
60178167Sqingli#ifdef RADIX_MPATH
61178167Sqingli#include <net/radix_mpath.h>
62178167Sqingli#endif
6353541Sshin
6453541Sshin#include <netinet/in.h>
6553541Sshin#include <netinet/in_var.h>
6653541Sshin#include <netinet6/in6_var.h>
67151477Ssuz#include <netinet6/in6_ifattach.h>
6862587Sitojun#include <netinet/ip6.h>
6953541Sshin#include <netinet6/ip6_var.h>
70148385Sume#include <netinet6/scope6_var.h>
7153541Sshin#include <netinet6/nd6.h>
7262587Sitojun#include <netinet/icmp6.h>
7353541Sshin
74142215Sglebius#ifdef DEV_CARP
75142215Sglebius#include <netinet/ip_carp.h>
76142215Sglebius#endif
77142215Sglebius
7862587Sitojun#define SDL(s) ((struct sockaddr_dl *)s)
7953541Sshin
8062587Sitojunstruct dadq;
81175162Sobrienstatic struct dadq *nd6_dad_find(struct ifaddr *);
82175162Sobrienstatic void nd6_dad_starttimer(struct dadq *, int);
83175162Sobrienstatic void nd6_dad_stoptimer(struct dadq *);
84175162Sobrienstatic void nd6_dad_timer(struct ifaddr *);
85175162Sobrienstatic void nd6_dad_ns_output(struct dadq *, struct ifaddr *);
86175162Sobrienstatic void nd6_dad_ns_input(struct ifaddr *);
87175162Sobrienstatic void nd6_dad_na_input(struct ifaddr *);
8853541Sshin
8962587Sitojunstatic int dad_ignore_ns = 0;	/* ignore NS in DAD - specwise incorrect*/
9062587Sitojunstatic int dad_maxtry = 15;	/* max # of *tries* to transmit DAD packet */
9153541Sshin
9253541Sshin/*
93108470Sschweikh * Input a Neighbor Solicitation Message.
9453541Sshin *
9553541Sshin * Based on RFC 2461
96148987Sume * Based on RFC 2462 (duplicate address detection)
9753541Sshin */
9853541Sshinvoid
99171259Sdelphijnd6_ns_input(struct mbuf *m, int off, int icmp6len)
10053541Sshin{
101183550Szec	INIT_VNET_INET6(curvnet);
10253541Sshin	struct ifnet *ifp = m->m_pkthdr.rcvif;
10353541Sshin	struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
10462587Sitojun	struct nd_neighbor_solicit *nd_ns;
10553541Sshin	struct in6_addr saddr6 = ip6->ip6_src;
10653541Sshin	struct in6_addr daddr6 = ip6->ip6_dst;
10762587Sitojun	struct in6_addr taddr6;
10853541Sshin	struct in6_addr myaddr6;
10953541Sshin	char *lladdr = NULL;
110142215Sglebius	struct ifaddr *ifa = NULL;
11153541Sshin	int lladdrlen = 0;
11253541Sshin	int anycast = 0, proxy = 0, tentative = 0;
11353541Sshin	int tlladdr;
11453541Sshin	union nd_opts ndopts;
11562587Sitojun	struct sockaddr_dl *proxydl = NULL;
116165118Sbz	char ip6bufs[INET6_ADDRSTRLEN], ip6bufd[INET6_ADDRSTRLEN];
11753541Sshin
11878064Sume#ifndef PULLDOWN_TEST
11978064Sume	IP6_EXTHDR_CHECK(m, off, icmp6len,);
12078064Sume	nd_ns = (struct nd_neighbor_solicit *)((caddr_t)ip6 + off);
12178064Sume#else
12278064Sume	IP6_EXTHDR_GET(nd_ns, struct nd_neighbor_solicit *, m, off, icmp6len);
12378064Sume	if (nd_ns == NULL) {
124181803Sbz		V_icmp6stat.icp6s_tooshort++;
12578064Sume		return;
12678064Sume	}
12778064Sume#endif
12878064Sume	ip6 = mtod(m, struct ip6_hdr *); /* adjust pointer for safety */
12978064Sume	taddr6 = nd_ns->nd_ns_target;
130148385Sume	if (in6_setscope(&taddr6, ifp, NULL) != 0)
131148385Sume		goto bad;
13278064Sume
13353541Sshin	if (ip6->ip6_hlim != 255) {
13478064Sume		nd6log((LOG_ERR,
13578064Sume		    "nd6_ns_input: invalid hlim (%d) from %s to %s on %s\n",
136165118Sbz		    ip6->ip6_hlim, ip6_sprintf(ip6bufs, &ip6->ip6_src),
137165118Sbz		    ip6_sprintf(ip6bufd, &ip6->ip6_dst), if_name(ifp)));
13878064Sume		goto bad;
13953541Sshin	}
14053541Sshin
14153541Sshin	if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) {
142148987Sume		/* dst has to be a solicited node multicast address. */
143120941Sume		if (daddr6.s6_addr16[0] == IPV6_ADDR_INT16_MLL &&
14495023Ssuz		    /* don't check ifindex portion */
145120941Sume		    daddr6.s6_addr32[1] == 0 &&
146120941Sume		    daddr6.s6_addr32[2] == IPV6_ADDR_INT32_ONE &&
147120941Sume		    daddr6.s6_addr8[12] == 0xff) {
14895023Ssuz			; /* good */
14953541Sshin		} else {
15078064Sume			nd6log((LOG_INFO, "nd6_ns_input: bad DAD packet "
151120941Sume			    "(wrong ip6 dst)\n"));
15253541Sshin			goto bad;
15353541Sshin		}
154183529Scperciva	} else if (!nd6_onlink_ns_rfc4861) {
155183529Scperciva		struct sockaddr_in6 src_sa6;
156183529Scperciva
157183529Scperciva		/*
158183529Scperciva		 * According to recent IETF discussions, it is not a good idea
159183529Scperciva		 * to accept a NS from an address which would not be deemed
160183529Scperciva		 * to be a neighbor otherwise.  This point is expected to be
161183529Scperciva		 * clarified in future revisions of the specification.
162183529Scperciva		 */
163183529Scperciva		bzero(&src_sa6, sizeof(src_sa6));
164183529Scperciva		src_sa6.sin6_family = AF_INET6;
165183529Scperciva		src_sa6.sin6_len = sizeof(src_sa6);
166183529Scperciva		src_sa6.sin6_addr = saddr6;
167183529Scperciva		if (!nd6_is_addr_neighbor(&src_sa6, ifp)) {
168183529Scperciva			nd6log((LOG_INFO, "nd6_ns_input: "
169183529Scperciva				"NS packet from non-neighbor\n"));
170183529Scperciva			goto bad;
171183529Scperciva		}
17253541Sshin	}
17353541Sshin
17453541Sshin	if (IN6_IS_ADDR_MULTICAST(&taddr6)) {
17578064Sume		nd6log((LOG_INFO, "nd6_ns_input: bad NS target (multicast)\n"));
17653541Sshin		goto bad;
17753541Sshin	}
17853541Sshin
17953541Sshin	icmp6len -= sizeof(*nd_ns);
18053541Sshin	nd6_option_init(nd_ns + 1, icmp6len, &ndopts);
18153541Sshin	if (nd6_options(&ndopts) < 0) {
18278064Sume		nd6log((LOG_INFO,
18378064Sume		    "nd6_ns_input: invalid ND option, ignored\n"));
18478064Sume		/* nd6_options have incremented stats */
18578064Sume		goto freeit;
18653541Sshin	}
18753541Sshin
18853541Sshin	if (ndopts.nd_opts_src_lladdr) {
18995023Ssuz		lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1);
19053541Sshin		lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3;
19153541Sshin	}
192120941Sume
19353541Sshin	if (IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src) && lladdr) {
19478064Sume		nd6log((LOG_INFO, "nd6_ns_input: bad DAD packet "
19578064Sume		    "(link-layer address option)\n"));
19653541Sshin		goto bad;
19753541Sshin	}
19853541Sshin
19953541Sshin	/*
20053541Sshin	 * Attaching target link-layer address to the NA?
20153541Sshin	 * (RFC 2461 7.2.4)
20253541Sshin	 *
20353541Sshin	 * NS IP dst is unicast/anycast			MUST NOT add
20453541Sshin	 * NS IP dst is solicited-node multicast	MUST add
20553541Sshin	 *
20653541Sshin	 * In implementation, we add target link-layer address by default.
20753541Sshin	 * We do not add one in MUST NOT cases.
20853541Sshin	 */
20953541Sshin	if (!IN6_IS_ADDR_MULTICAST(&daddr6))
21053541Sshin		tlladdr = 0;
21153541Sshin	else
21253541Sshin		tlladdr = 1;
21353541Sshin
21453541Sshin	/*
21553541Sshin	 * Target address (taddr6) must be either:
21653541Sshin	 * (1) Valid unicast/anycast address for my receiving interface,
21753541Sshin	 * (2) Unicast address for which I'm offering proxy service, or
21853541Sshin	 * (3) "tentative" address on which DAD is being performed.
21953541Sshin	 */
22053541Sshin	/* (1) and (3) check. */
221142215Sglebius#ifdef DEV_CARP
222142215Sglebius	if (ifp->if_carp)
223142215Sglebius		ifa = carp_iamatch6(ifp->if_carp, &taddr6);
224151465Ssuz	if (ifa == NULL)
225142215Sglebius		ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6);
226142215Sglebius#else
22753541Sshin	ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6);
228142215Sglebius#endif
22953541Sshin
23053541Sshin	/* (2) check. */
231151465Ssuz	if (ifa == NULL) {
23253541Sshin		struct rtentry *rt;
23353541Sshin		struct sockaddr_in6 tsin6;
234121765Ssam		int need_proxy;
235178167Sqingli#ifdef RADIX_MPATH
236178167Sqingli		struct route_in6 ro;
237178167Sqingli#endif
23853541Sshin
239171260Sdelphij		bzero(&tsin6, sizeof tsin6);
24053541Sshin		tsin6.sin6_len = sizeof(struct sockaddr_in6);
24153541Sshin		tsin6.sin6_family = AF_INET6;
24253541Sshin		tsin6.sin6_addr = taddr6;
24353541Sshin
244178167Sqingli#ifdef RADIX_MPATH
245178167Sqingli		bzero(&ro, sizeof(ro));
246178167Sqingli		ro.ro_dst = tsin6;
247178167Sqingli		rtalloc_mpath((struct route *)&ro, RTF_ANNOUNCE);
248178167Sqingli		rt = ro.ro_rt;
249178167Sqingli#else
25053541Sshin		rt = rtalloc1((struct sockaddr *)&tsin6, 0, 0);
251178167Sqingli#endif
252121765Ssam		need_proxy = (rt && (rt->rt_flags & RTF_ANNOUNCE) != 0 &&
253121765Ssam		    rt->rt_gateway->sa_family == AF_LINK);
254121765Ssam		if (rt)
255121765Ssam			rtfree(rt);
256121765Ssam		if (need_proxy) {
25753541Sshin			/*
25862587Sitojun			 * proxy NDP for single entry
25953541Sshin			 */
26062587Sitojun			ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp,
26162587Sitojun				IN6_IFF_NOTREADY|IN6_IFF_ANYCAST);
26262587Sitojun			if (ifa) {
26353541Sshin				proxy = 1;
26462587Sitojun				proxydl = SDL(rt->rt_gateway);
26562587Sitojun			}
26653541Sshin		}
26753541Sshin	}
268151479Ssuz	if (ifa == NULL) {
26953541Sshin		/*
27078064Sume		 * We've got an NS packet, and we don't have that adddress
27153541Sshin		 * assigned for us.  We MUST silently ignore it.
27253541Sshin		 * See RFC2461 7.2.3.
27353541Sshin		 */
27462587Sitojun		goto freeit;
27553541Sshin	}
27653541Sshin	myaddr6 = *IFA_IN6(ifa);
27753541Sshin	anycast = ((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST;
27853541Sshin	tentative = ((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_TENTATIVE;
27953541Sshin	if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DUPLICATED)
28062587Sitojun		goto freeit;
28153541Sshin
28253541Sshin	if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) {
283120941Sume		nd6log((LOG_INFO, "nd6_ns_input: lladdrlen mismatch for %s "
28453541Sshin		    "(if %d, NS packet %d)\n",
285165118Sbz		    ip6_sprintf(ip6bufs, &taddr6),
286120941Sume		    ifp->if_addrlen, lladdrlen - 2));
28778064Sume		goto bad;
28853541Sshin	}
28953541Sshin
29053541Sshin	if (IN6_ARE_ADDR_EQUAL(&myaddr6, &saddr6)) {
291120941Sume		nd6log((LOG_INFO, "nd6_ns_input: duplicate IP6 address %s\n",
292165118Sbz		    ip6_sprintf(ip6bufs, &saddr6)));
29362587Sitojun		goto freeit;
29453541Sshin	}
29553541Sshin
29653541Sshin	/*
29753541Sshin	 * We have neighbor solicitation packet, with target address equals to
29853541Sshin	 * one of my tentative address.
29953541Sshin	 *
30053541Sshin	 * src addr	how to process?
30153541Sshin	 * ---		---
30253541Sshin	 * multicast	of course, invalid (rejected in ip6_input)
30353541Sshin	 * unicast	somebody is doing address resolution -> ignore
30453541Sshin	 * unspec	dup address detection
30553541Sshin	 *
30653541Sshin	 * The processing is defined in RFC 2462.
30753541Sshin	 */
30853541Sshin	if (tentative) {
30953541Sshin		/*
31053541Sshin		 * If source address is unspecified address, it is for
311148987Sume		 * duplicate address detection.
31253541Sshin		 *
31353541Sshin		 * If not, the packet is for addess resolution;
31453541Sshin		 * silently ignore it.
31553541Sshin		 */
31653541Sshin		if (IN6_IS_ADDR_UNSPECIFIED(&saddr6))
31753541Sshin			nd6_dad_ns_input(ifa);
31853541Sshin
31962587Sitojun		goto freeit;
32053541Sshin	}
32153541Sshin
32253541Sshin	/*
32353541Sshin	 * If the source address is unspecified address, entries must not
32453541Sshin	 * be created or updated.
32553541Sshin	 * It looks that sender is performing DAD.  Output NA toward
32653541Sshin	 * all-node multicast address, to tell the sender that I'm using
32753541Sshin	 * the address.
32853541Sshin	 * S bit ("solicited") must be zero.
32953541Sshin	 */
33053541Sshin	if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) {
331148385Sume		struct in6_addr in6_all;
332148385Sume
333148385Sume		in6_all = in6addr_linklocal_allnodes;
334148385Sume		if (in6_setscope(&in6_all, ifp, NULL) != 0)
335148385Sume			goto bad;
336148385Sume		nd6_na_output(ifp, &in6_all, &taddr6,
337120941Sume		    ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) |
338181803Sbz		    (V_ip6_forwarding ? ND_NA_FLAG_ROUTER : 0),
339120941Sume		    tlladdr, (struct sockaddr *)proxydl);
34062587Sitojun		goto freeit;
34153541Sshin	}
34253541Sshin
343120941Sume	nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen,
344120941Sume	    ND_NEIGHBOR_SOLICIT, 0);
34553541Sshin
34653541Sshin	nd6_na_output(ifp, &saddr6, &taddr6,
347120941Sume	    ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) |
348181803Sbz	    (V_ip6_forwarding ? ND_NA_FLAG_ROUTER : 0) | ND_NA_FLAG_SOLICITED,
349120941Sume	    tlladdr, (struct sockaddr *)proxydl);
35062587Sitojun freeit:
35162587Sitojun	m_freem(m);
35253541Sshin	return;
35353541Sshin
35453541Sshin bad:
355165118Sbz	nd6log((LOG_ERR, "nd6_ns_input: src=%s\n",
356165118Sbz		ip6_sprintf(ip6bufs, &saddr6)));
357165118Sbz	nd6log((LOG_ERR, "nd6_ns_input: dst=%s\n",
358165118Sbz		ip6_sprintf(ip6bufs, &daddr6)));
359165118Sbz	nd6log((LOG_ERR, "nd6_ns_input: tgt=%s\n",
360165118Sbz		ip6_sprintf(ip6bufs, &taddr6)));
361181803Sbz	V_icmp6stat.icp6s_badns++;
36262587Sitojun	m_freem(m);
36353541Sshin}
36453541Sshin
36553541Sshin/*
366108470Sschweikh * Output a Neighbor Solicitation Message. Caller specifies:
36753541Sshin *	- ICMP6 header source IP6 address
36853541Sshin *	- ND6 header target IP6 address
36953541Sshin *	- ND6 header source datalink address
37053541Sshin *
37153541Sshin * Based on RFC 2461
372148987Sume * Based on RFC 2462 (duplicate address detection)
373171259Sdelphij *
374171259Sdelphij *   ln - for source address determination
375171259Sdelphij *  dad - duplicate address detection
37653541Sshin */
37753541Sshinvoid
378171259Sdelphijnd6_ns_output(struct ifnet *ifp, const struct in6_addr *daddr6,
379171259Sdelphij    const struct in6_addr *taddr6, struct llinfo_nd6 *ln, int dad)
38053541Sshin{
381183550Szec	INIT_VNET_INET6(ifp->if_vnet);
38253541Sshin	struct mbuf *m;
38353541Sshin	struct ip6_hdr *ip6;
38453541Sshin	struct nd_neighbor_solicit *nd_ns;
385148385Sume	struct in6_addr *src, src_in;
38653541Sshin	struct ip6_moptions im6o;
38753541Sshin	int icmp6len;
38862587Sitojun	int maxlen;
38953541Sshin	caddr_t mac;
390148385Sume	struct route_in6 ro;
391120941Sume
392148385Sume	bzero(&ro, sizeof(ro));
393148385Sume
39453541Sshin	if (IN6_IS_ADDR_MULTICAST(taddr6))
39553541Sshin		return;
39653541Sshin
39762587Sitojun	/* estimate the size of message */
39862587Sitojun	maxlen = sizeof(*ip6) + sizeof(*nd_ns);
39962587Sitojun	maxlen += (sizeof(struct nd_opt_hdr) + ifp->if_addrlen + 7) & ~7;
40062587Sitojun	if (max_linkhdr + maxlen >= MCLBYTES) {
40162587Sitojun#ifdef DIAGNOSTIC
40262587Sitojun		printf("nd6_ns_output: max_linkhdr + maxlen >= MCLBYTES "
40362587Sitojun		    "(%d + %d > %d)\n", max_linkhdr, maxlen, MCLBYTES);
40462587Sitojun#endif
40553541Sshin		return;
40662587Sitojun	}
40753541Sshin
408111119Simp	MGETHDR(m, M_DONTWAIT, MT_DATA);
40962587Sitojun	if (m && max_linkhdr + maxlen >= MHLEN) {
410111119Simp		MCLGET(m, M_DONTWAIT);
41162587Sitojun		if ((m->m_flags & M_EXT) == 0) {
41262587Sitojun			m_free(m);
41362587Sitojun			m = NULL;
41462587Sitojun		}
41562587Sitojun	}
41662587Sitojun	if (m == NULL)
41762587Sitojun		return;
41878064Sume	m->m_pkthdr.rcvif = NULL;
41962587Sitojun
42053541Sshin	if (daddr6 == NULL || IN6_IS_ADDR_MULTICAST(daddr6)) {
42153541Sshin		m->m_flags |= M_MCAST;
42253541Sshin		im6o.im6o_multicast_ifp = ifp;
42353541Sshin		im6o.im6o_multicast_hlim = 255;
42453541Sshin		im6o.im6o_multicast_loop = 0;
42553541Sshin	}
42653541Sshin
42753541Sshin	icmp6len = sizeof(*nd_ns);
42853541Sshin	m->m_pkthdr.len = m->m_len = sizeof(*ip6) + icmp6len;
42995023Ssuz	m->m_data += max_linkhdr;	/* or MH_ALIGN() equivalent? */
43053541Sshin
43153541Sshin	/* fill neighbor solicitation packet */
43253541Sshin	ip6 = mtod(m, struct ip6_hdr *);
43353541Sshin	ip6->ip6_flow = 0;
43462587Sitojun	ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
43562587Sitojun	ip6->ip6_vfc |= IPV6_VERSION;
43653541Sshin	/* ip6->ip6_plen will be set later */
43753541Sshin	ip6->ip6_nxt = IPPROTO_ICMPV6;
43853541Sshin	ip6->ip6_hlim = 255;
43953541Sshin	if (daddr6)
44053541Sshin		ip6->ip6_dst = *daddr6;
44153541Sshin	else {
44253541Sshin		ip6->ip6_dst.s6_addr16[0] = IPV6_ADDR_INT16_MLL;
443148385Sume		ip6->ip6_dst.s6_addr16[1] = 0;
44453541Sshin		ip6->ip6_dst.s6_addr32[1] = 0;
44553541Sshin		ip6->ip6_dst.s6_addr32[2] = IPV6_ADDR_INT32_ONE;
44653541Sshin		ip6->ip6_dst.s6_addr32[3] = taddr6->s6_addr32[3];
44753541Sshin		ip6->ip6_dst.s6_addr8[12] = 0xff;
448148385Sume		if (in6_setscope(&ip6->ip6_dst, ifp, NULL) != 0)
449148385Sume			goto bad;
45053541Sshin	}
45153541Sshin	if (!dad) {
45253541Sshin		/*
45353541Sshin		 * RFC2461 7.2.2:
45453541Sshin		 * "If the source address of the packet prompting the
45553541Sshin		 * solicitation is the same as one of the addresses assigned
45653541Sshin		 * to the outgoing interface, that address SHOULD be placed
45753541Sshin		 * in the IP Source Address of the outgoing solicitation.
45853541Sshin		 * Otherwise, any one of the addresses assigned to the
45953541Sshin		 * interface should be used."
46053541Sshin		 *
46153541Sshin		 * We use the source address for the prompting packet
46253541Sshin		 * (saddr6), if:
46353541Sshin		 * - saddr6 is given from the caller (by giving "ln"), and
46453541Sshin		 * - saddr6 belongs to the outgoing interface.
465148385Sume		 * Otherwise, we perform the source address selection as usual.
46653541Sshin		 */
46795023Ssuz		struct ip6_hdr *hip6;		/* hold ip6 */
468148385Sume		struct in6_addr *hsrc = NULL;
46953541Sshin
47053541Sshin		if (ln && ln->ln_hold) {
471148987Sume			/*
472148987Sume			 * assuming every packet in ln_hold has the same IP
473148987Sume			 * header
474148987Sume			 */
47553541Sshin			hip6 = mtod(ln->ln_hold, struct ip6_hdr *);
47653541Sshin			/* XXX pullup? */
47753541Sshin			if (sizeof(*hip6) < ln->ln_hold->m_len)
478148385Sume				hsrc = &hip6->ip6_src;
47953541Sshin			else
480148385Sume				hsrc = NULL;
481148385Sume		}
482148385Sume		if (hsrc && in6ifa_ifpwithaddr(ifp, hsrc))
483148385Sume			src = hsrc;
48453541Sshin		else {
485148385Sume			int error;
486148385Sume			struct sockaddr_in6 dst_sa;
487148385Sume
488148385Sume			bzero(&dst_sa, sizeof(dst_sa));
489148385Sume			dst_sa.sin6_family = AF_INET6;
490148385Sume			dst_sa.sin6_len = sizeof(dst_sa);
491148385Sume			dst_sa.sin6_addr = ip6->ip6_dst;
492148385Sume
493148385Sume			src = in6_selectsrc(&dst_sa, NULL,
494148385Sume			    NULL, &ro, NULL, NULL, &error);
495148385Sume			if (src == NULL) {
496165118Sbz				char ip6buf[INET6_ADDRSTRLEN];
497148385Sume				nd6log((LOG_DEBUG,
498148385Sume				    "nd6_ns_output: source can't be "
499148385Sume				    "determined: dst=%s, error=%d\n",
500165118Sbz				    ip6_sprintf(ip6buf, &dst_sa.sin6_addr),
501165118Sbz				    error));
502148385Sume				goto bad;
50353541Sshin			}
50453541Sshin		}
50553541Sshin	} else {
50653541Sshin		/*
50753541Sshin		 * Source address for DAD packet must always be IPv6
50853541Sshin		 * unspecified address. (0::0)
509148385Sume		 * We actually don't have to 0-clear the address (we did it
510148385Sume		 * above), but we do so here explicitly to make the intention
511148385Sume		 * clearer.
51253541Sshin		 */
513148385Sume		bzero(&src_in, sizeof(src_in));
514148385Sume		src = &src_in;
51553541Sshin	}
516148385Sume	ip6->ip6_src = *src;
51753541Sshin	nd_ns = (struct nd_neighbor_solicit *)(ip6 + 1);
51853541Sshin	nd_ns->nd_ns_type = ND_NEIGHBOR_SOLICIT;
51953541Sshin	nd_ns->nd_ns_code = 0;
52053541Sshin	nd_ns->nd_ns_reserved = 0;
52153541Sshin	nd_ns->nd_ns_target = *taddr6;
522121315Sume	in6_clearscope(&nd_ns->nd_ns_target); /* XXX */
52353541Sshin
52453541Sshin	/*
52553541Sshin	 * Add source link-layer address option.
52653541Sshin	 *
52753541Sshin	 *				spec		implementation
52853541Sshin	 *				---		---
52953541Sshin	 * DAD packet			MUST NOT	do not add the option
53053541Sshin	 * there's no link layer address:
53153541Sshin	 *				impossible	do not add the option
53253541Sshin	 * there's link layer address:
53353541Sshin	 *	Multicast NS		MUST add one	add the option
53453541Sshin	 *	Unicast NS		SHOULD add one	add the option
53553541Sshin	 */
53653541Sshin	if (!dad && (mac = nd6_ifptomac(ifp))) {
53753541Sshin		int optlen = sizeof(struct nd_opt_hdr) + ifp->if_addrlen;
53853541Sshin		struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)(nd_ns + 1);
53953541Sshin		/* 8 byte alignments... */
54053541Sshin		optlen = (optlen + 7) & ~7;
541120941Sume
54253541Sshin		m->m_pkthdr.len += optlen;
54353541Sshin		m->m_len += optlen;
54453541Sshin		icmp6len += optlen;
54553541Sshin		bzero((caddr_t)nd_opt, optlen);
54653541Sshin		nd_opt->nd_opt_type = ND_OPT_SOURCE_LINKADDR;
54753541Sshin		nd_opt->nd_opt_len = optlen >> 3;
54853541Sshin		bcopy(mac, (caddr_t)(nd_opt + 1), ifp->if_addrlen);
54953541Sshin	}
55053541Sshin
55153541Sshin	ip6->ip6_plen = htons((u_short)icmp6len);
55253541Sshin	nd_ns->nd_ns_cksum = 0;
553120941Sume	nd_ns->nd_ns_cksum =
554120941Sume	    in6_cksum(m, IPPROTO_ICMPV6, sizeof(*ip6), icmp6len);
55553541Sshin
556151536Ssuz	ip6_output(m, NULL, &ro, dad ? IPV6_UNSPECSRC : 0, &im6o, NULL, NULL);
557148385Sume	icmp6_ifstat_inc(ifp, ifs6_out_msg);
558148385Sume	icmp6_ifstat_inc(ifp, ifs6_out_neighborsolicit);
559181803Sbz	V_icmp6stat.icp6s_outhist[ND_NEIGHBOR_SOLICIT]++;
560148385Sume
561148385Sume	if (ro.ro_rt) {		/* we don't cache this route. */
562148385Sume		RTFREE(ro.ro_rt);
56353541Sshin	}
564148385Sume	return;
565148385Sume
566148385Sume  bad:
567148385Sume	if (ro.ro_rt) {
568148385Sume		RTFREE(ro.ro_rt);
569148385Sume	}
570148385Sume	m_freem(m);
571148385Sume	return;
57253541Sshin}
57353541Sshin
57453541Sshin/*
57553541Sshin * Neighbor advertisement input handling.
57653541Sshin *
57753541Sshin * Based on RFC 2461
578148987Sume * Based on RFC 2462 (duplicate address detection)
57962587Sitojun *
58062587Sitojun * the following items are not implemented yet:
58162587Sitojun * - proxy advertisement delay rule (RFC2461 7.2.8, last paragraph, SHOULD)
58262587Sitojun * - anycast advertisement delay rule (RFC2461 7.2.7, SHOULD)
58353541Sshin */
58453541Sshinvoid
585171259Sdelphijnd6_na_input(struct mbuf *m, int off, int icmp6len)
58653541Sshin{
587183550Szec	INIT_VNET_INET6(curvnet);
58853541Sshin	struct ifnet *ifp = m->m_pkthdr.rcvif;
58953541Sshin	struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
59062587Sitojun	struct nd_neighbor_advert *nd_na;
59153541Sshin	struct in6_addr daddr6 = ip6->ip6_dst;
59262587Sitojun	struct in6_addr taddr6;
59362587Sitojun	int flags;
59462587Sitojun	int is_router;
59562587Sitojun	int is_solicited;
59662587Sitojun	int is_override;
59753541Sshin	char *lladdr = NULL;
59853541Sshin	int lladdrlen = 0;
59953541Sshin	struct ifaddr *ifa;
60053541Sshin	struct llinfo_nd6 *ln;
60153541Sshin	struct rtentry *rt;
60253541Sshin	struct sockaddr_dl *sdl;
60353541Sshin	union nd_opts ndopts;
604165118Sbz	char ip6bufs[INET6_ADDRSTRLEN], ip6bufd[INET6_ADDRSTRLEN];
60553541Sshin
60653541Sshin	if (ip6->ip6_hlim != 255) {
60778064Sume		nd6log((LOG_ERR,
60878064Sume		    "nd6_na_input: invalid hlim (%d) from %s to %s on %s\n",
609165118Sbz		    ip6->ip6_hlim, ip6_sprintf(ip6bufs, &ip6->ip6_src),
610165118Sbz		    ip6_sprintf(ip6bufd, &ip6->ip6_dst), if_name(ifp)));
61178064Sume		goto bad;
61262587Sitojun	}
61362587Sitojun
61462587Sitojun#ifndef PULLDOWN_TEST
61562587Sitojun	IP6_EXTHDR_CHECK(m, off, icmp6len,);
61662587Sitojun	nd_na = (struct nd_neighbor_advert *)((caddr_t)ip6 + off);
61762587Sitojun#else
61862587Sitojun	IP6_EXTHDR_GET(nd_na, struct nd_neighbor_advert *, m, off, icmp6len);
61962587Sitojun	if (nd_na == NULL) {
620181803Sbz		V_icmp6stat.icp6s_tooshort++;
62153541Sshin		return;
62253541Sshin	}
62362587Sitojun#endif
624148385Sume
62562587Sitojun	flags = nd_na->nd_na_flags_reserved;
62662587Sitojun	is_router = ((flags & ND_NA_FLAG_ROUTER) != 0);
62762587Sitojun	is_solicited = ((flags & ND_NA_FLAG_SOLICITED) != 0);
62862587Sitojun	is_override = ((flags & ND_NA_FLAG_OVERRIDE) != 0);
62953541Sshin
630148385Sume	taddr6 = nd_na->nd_na_target;
631148385Sume	if (in6_setscope(&taddr6, ifp, NULL))
632150202Ssuz		goto bad;	/* XXX: impossible */
63353541Sshin
63453541Sshin	if (IN6_IS_ADDR_MULTICAST(&taddr6)) {
63578064Sume		nd6log((LOG_ERR,
63653541Sshin		    "nd6_na_input: invalid target address %s\n",
637165118Sbz		    ip6_sprintf(ip6bufs, &taddr6)));
63878064Sume		goto bad;
63953541Sshin	}
64053541Sshin	if (IN6_IS_ADDR_MULTICAST(&daddr6))
64153541Sshin		if (is_solicited) {
64278064Sume			nd6log((LOG_ERR,
64378064Sume			    "nd6_na_input: a solicited adv is multicasted\n"));
64478064Sume			goto bad;
64553541Sshin		}
64653541Sshin
64753541Sshin	icmp6len -= sizeof(*nd_na);
64853541Sshin	nd6_option_init(nd_na + 1, icmp6len, &ndopts);
64953541Sshin	if (nd6_options(&ndopts) < 0) {
65078064Sume		nd6log((LOG_INFO,
65178064Sume		    "nd6_na_input: invalid ND option, ignored\n"));
65278064Sume		/* nd6_options have incremented stats */
65362587Sitojun		goto freeit;
65453541Sshin	}
65553541Sshin
65653541Sshin	if (ndopts.nd_opts_tgt_lladdr) {
65753541Sshin		lladdr = (char *)(ndopts.nd_opts_tgt_lladdr + 1);
65853541Sshin		lladdrlen = ndopts.nd_opts_tgt_lladdr->nd_opt_len << 3;
65953541Sshin	}
66053541Sshin
66153541Sshin	ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6);
66253541Sshin
66353541Sshin	/*
66453541Sshin	 * Target address matches one of my interface address.
66553541Sshin	 *
66653541Sshin	 * If my address is tentative, this means that there's somebody
66753541Sshin	 * already using the same address as mine.  This indicates DAD failure.
66853541Sshin	 * This is defined in RFC 2462.
66953541Sshin	 *
67053541Sshin	 * Otherwise, process as defined in RFC 2461.
67153541Sshin	 */
67253541Sshin	if (ifa
67353541Sshin	 && (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_TENTATIVE)) {
67453541Sshin		nd6_dad_na_input(ifa);
67562587Sitojun		goto freeit;
67653541Sshin	}
67753541Sshin
67895023Ssuz	/* Just for safety, maybe unnecessary. */
67953541Sshin	if (ifa) {
68053541Sshin		log(LOG_ERR,
68153541Sshin		    "nd6_na_input: duplicate IP6 address %s\n",
682165118Sbz		    ip6_sprintf(ip6bufs, &taddr6));
68362587Sitojun		goto freeit;
68453541Sshin	}
68553541Sshin
68653541Sshin	if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) {
687120941Sume		nd6log((LOG_INFO, "nd6_na_input: lladdrlen mismatch for %s "
688165118Sbz		    "(if %d, NA packet %d)\n", ip6_sprintf(ip6bufs, &taddr6),
689120941Sume		    ifp->if_addrlen, lladdrlen - 2));
69078064Sume		goto bad;
69153541Sshin	}
69253541Sshin
69353541Sshin	/*
694120941Sume	 * If no neighbor cache entry is found, NA SHOULD silently be
695120941Sume	 * discarded.
69653541Sshin	 */
69753541Sshin	rt = nd6_lookup(&taddr6, 0, ifp);
69853541Sshin	if ((rt == NULL) ||
69953541Sshin	   ((ln = (struct llinfo_nd6 *)rt->rt_llinfo) == NULL) ||
70053541Sshin	   ((sdl = SDL(rt->rt_gateway)) == NULL))
70162587Sitojun		goto freeit;
70253541Sshin
70353541Sshin	if (ln->ln_state == ND6_LLINFO_INCOMPLETE) {
70453541Sshin		/*
70553541Sshin		 * If the link-layer has address, and no lladdr option came,
70653541Sshin		 * discard the packet.
70753541Sshin		 */
708151465Ssuz		if (ifp->if_addrlen && lladdr == NULL)
70962587Sitojun			goto freeit;
71053541Sshin
71153541Sshin		/*
71253541Sshin		 * Record link-layer address, and update the state.
71353541Sshin		 */
71453541Sshin		sdl->sdl_alen = ifp->if_addrlen;
71553541Sshin		bcopy(lladdr, LLADDR(sdl), ifp->if_addrlen);
71653541Sshin		if (is_solicited) {
71753541Sshin			ln->ln_state = ND6_LLINFO_REACHABLE;
71862587Sitojun			ln->ln_byhint = 0;
719151539Ssuz			if (!ND6_LLINFO_PERMANENT(ln)) {
720151539Ssuz				nd6_llinfo_settimer(ln,
721151539Ssuz				    (long)ND_IFINFO(rt->rt_ifp)->reachable * hz);
722120941Sume			}
72378064Sume		} else {
72453541Sshin			ln->ln_state = ND6_LLINFO_STALE;
725181803Sbz			nd6_llinfo_settimer(ln, (long)V_nd6_gctimer * hz);
72678064Sume		}
72778064Sume		if ((ln->ln_router = is_router) != 0) {
72878064Sume			/*
72978064Sume			 * This means a router's state has changed from
73078064Sume			 * non-reachable to probably reachable, and might
73178064Sume			 * affect the status of associated prefixes..
73278064Sume			 */
73378064Sume			pfxlist_onlink_check();
73478064Sume		}
73553541Sshin	} else {
73653541Sshin		int llchange;
73753541Sshin
73853541Sshin		/*
73953541Sshin		 * Check if the link-layer address has changed or not.
74053541Sshin		 */
741151465Ssuz		if (lladdr == NULL)
74253541Sshin			llchange = 0;
74353541Sshin		else {
74453541Sshin			if (sdl->sdl_alen) {
74553541Sshin				if (bcmp(lladdr, LLADDR(sdl), ifp->if_addrlen))
74653541Sshin					llchange = 1;
74753541Sshin				else
74853541Sshin					llchange = 0;
74953541Sshin			} else
75053541Sshin				llchange = 1;
75153541Sshin		}
75253541Sshin
75353541Sshin		/*
75453541Sshin		 * This is VERY complex.  Look at it with care.
75553541Sshin		 *
75653541Sshin		 * override solicit lladdr llchange	action
75753541Sshin		 *					(L: record lladdr)
75853541Sshin		 *
75953541Sshin		 *	0	0	n	--	(2c)
76053541Sshin		 *	0	0	y	n	(2b) L
76153541Sshin		 *	0	0	y	y	(1)    REACHABLE->STALE
76253541Sshin		 *	0	1	n	--	(2c)   *->REACHABLE
76353541Sshin		 *	0	1	y	n	(2b) L *->REACHABLE
76453541Sshin		 *	0	1	y	y	(1)    REACHABLE->STALE
76553541Sshin		 *	1	0	n	--	(2a)
76653541Sshin		 *	1	0	y	n	(2a) L
76753541Sshin		 *	1	0	y	y	(2a) L *->STALE
76853541Sshin		 *	1	1	n	--	(2a)   *->REACHABLE
76953541Sshin		 *	1	1	y	n	(2a) L *->REACHABLE
77053541Sshin		 *	1	1	y	y	(2a) L *->REACHABLE
77153541Sshin		 */
772151539Ssuz		if (!is_override && (lladdr != NULL && llchange)) {  /* (1) */
77353541Sshin			/*
77453541Sshin			 * If state is REACHABLE, make it STALE.
77553541Sshin			 * no other updates should be done.
77653541Sshin			 */
77778064Sume			if (ln->ln_state == ND6_LLINFO_REACHABLE) {
77853541Sshin				ln->ln_state = ND6_LLINFO_STALE;
779181803Sbz				nd6_llinfo_settimer(ln, (long)V_nd6_gctimer * hz);
78078064Sume			}
78162587Sitojun			goto freeit;
78253541Sshin		} else if (is_override				   /* (2a) */
783151465Ssuz			|| (!is_override && (lladdr != NULL && !llchange)) /* (2b) */
784151465Ssuz			|| lladdr == NULL) {			   /* (2c) */
78553541Sshin			/*
78653541Sshin			 * Update link-local address, if any.
78753541Sshin			 */
788151465Ssuz			if (lladdr != NULL) {
78953541Sshin				sdl->sdl_alen = ifp->if_addrlen;
79053541Sshin				bcopy(lladdr, LLADDR(sdl), ifp->if_addrlen);
79153541Sshin			}
79253541Sshin
79353541Sshin			/*
79453541Sshin			 * If solicited, make the state REACHABLE.
79553541Sshin			 * If not solicited and the link-layer address was
79653541Sshin			 * changed, make it STALE.
79753541Sshin			 */
79853541Sshin			if (is_solicited) {
79953541Sshin				ln->ln_state = ND6_LLINFO_REACHABLE;
80062587Sitojun				ln->ln_byhint = 0;
801151539Ssuz				if (!ND6_LLINFO_PERMANENT(ln)) {
802151539Ssuz					nd6_llinfo_settimer(ln,
803151539Ssuz					    (long)ND_IFINFO(ifp)->reachable * hz);
80453541Sshin				}
80553541Sshin			} else {
806151465Ssuz				if (lladdr != NULL && llchange) {
80753541Sshin					ln->ln_state = ND6_LLINFO_STALE;
808151539Ssuz					nd6_llinfo_settimer(ln,
809181803Sbz					    (long)V_nd6_gctimer * hz);
81078064Sume				}
81153541Sshin			}
81253541Sshin		}
81353541Sshin
81453541Sshin		if (ln->ln_router && !is_router) {
81553541Sshin			/*
81653541Sshin			 * The peer dropped the router flag.
81753541Sshin			 * Remove the sender from the Default Router List and
81853541Sshin			 * update the Destination Cache entries.
81953541Sshin			 */
82053541Sshin			struct nd_defrouter *dr;
82153541Sshin			struct in6_addr *in6;
82253541Sshin			int s;
82353541Sshin
82453541Sshin			in6 = &((struct sockaddr_in6 *)rt_key(rt))->sin6_addr;
82595023Ssuz
82695023Ssuz			/*
82795023Ssuz			 * Lock to protect the default router list.
82895023Ssuz			 * XXX: this might be unnecessary, since this function
82995023Ssuz			 * is only called under the network software interrupt
830120941Sume			 * context.  However, we keep it just for safety.
83195023Ssuz			 */
83253541Sshin			s = splnet();
833128422Sluigi			dr = defrouter_lookup(in6, ifp);
83453541Sshin			if (dr)
83553541Sshin				defrtrlist_del(dr);
836181803Sbz			else if (!V_ip6_forwarding) {
83753541Sshin				/*
83853541Sshin				 * Even if the neighbor is not in the default
83953541Sshin				 * router list, the neighbor may be used
84053541Sshin				 * as a next hop for some destinations
84153541Sshin				 * (e.g. redirect case). So we must
84253541Sshin				 * call rt6_flush explicitly.
84353541Sshin				 */
844128422Sluigi				rt6_flush(&ip6->ip6_src, ifp);
84553541Sshin			}
84653541Sshin			splx(s);
84753541Sshin		}
84853541Sshin		ln->ln_router = is_router;
84953541Sshin	}
85053541Sshin	rt->rt_flags &= ~RTF_REJECT;
85153541Sshin	ln->ln_asked = 0;
85253541Sshin	if (ln->ln_hold) {
853151539Ssuz		struct mbuf *m_hold, *m_hold_next;
854151539Ssuz
855169273Ssuz		/*
856169273Ssuz		 * reset the ln_hold in advance, to explicitly
857169273Ssuz		 * prevent a ln_hold lookup in nd6_output()
858169273Ssuz		 * (wouldn't happen, though...)
859169273Ssuz		 */
860171133Sgnn		for (m_hold = ln->ln_hold;
861169241Ssuz		    m_hold; m_hold = m_hold_next) {
862151539Ssuz			m_hold_next = m_hold->m_nextpkt;
863169241Ssuz			m_hold->m_nextpkt = NULL;
864151539Ssuz			/*
865151539Ssuz			 * we assume ifp is not a loopback here, so just set
866151539Ssuz			 * the 2nd argument as the 1st one.
867151539Ssuz			 */
868169241Ssuz			nd6_output(ifp, ifp, m_hold,
869151539Ssuz			    (struct sockaddr_in6 *)rt_key(rt), rt);
870151539Ssuz		}
871171133Sgnn		ln->ln_hold = NULL;
87253541Sshin	}
87362587Sitojun
87462587Sitojun freeit:
87562587Sitojun	m_freem(m);
87678064Sume	return;
87778064Sume
87878064Sume bad:
879181803Sbz	V_icmp6stat.icp6s_badna++;
88078064Sume	m_freem(m);
88153541Sshin}
88253541Sshin
88353541Sshin/*
88453541Sshin * Neighbor advertisement output handling.
88553541Sshin *
88653541Sshin * Based on RFC 2461
88753541Sshin *
88862587Sitojun * the following items are not implemented yet:
88962587Sitojun * - proxy advertisement delay rule (RFC2461 7.2.8, last paragraph, SHOULD)
89062587Sitojun * - anycast advertisement delay rule (RFC2461 7.2.7, SHOULD)
891171259Sdelphij *
892171259Sdelphij * tlladdr - 1 if include target link-layer address
893171259Sdelphij * sdl0 - sockaddr_dl (= proxy NA) or NULL
89453541Sshin */
89553541Sshinvoid
896171259Sdelphijnd6_na_output(struct ifnet *ifp, const struct in6_addr *daddr6_0,
897171259Sdelphij    const struct in6_addr *taddr6, u_long flags, int tlladdr,
898171259Sdelphij    struct sockaddr *sdl0)
89953541Sshin{
900183550Szec	INIT_VNET_INET6(ifp->if_vnet);
90153541Sshin	struct mbuf *m;
90253541Sshin	struct ip6_hdr *ip6;
90353541Sshin	struct nd_neighbor_advert *nd_na;
90453541Sshin	struct ip6_moptions im6o;
905148385Sume	struct in6_addr *src, daddr6;
906148385Sume	struct sockaddr_in6 dst_sa;
907148385Sume	int icmp6len, maxlen, error;
90892733Speter	caddr_t mac = NULL;
909148385Sume	struct route_in6 ro;
91062587Sitojun
911148385Sume	bzero(&ro, sizeof(ro));
912148385Sume
913148385Sume	daddr6 = *daddr6_0;	/* make a local copy for modification */
914148385Sume
91562587Sitojun	/* estimate the size of message */
91662587Sitojun	maxlen = sizeof(*ip6) + sizeof(*nd_na);
91762587Sitojun	maxlen += (sizeof(struct nd_opt_hdr) + ifp->if_addrlen + 7) & ~7;
91862587Sitojun	if (max_linkhdr + maxlen >= MCLBYTES) {
91962587Sitojun#ifdef DIAGNOSTIC
92062587Sitojun		printf("nd6_na_output: max_linkhdr + maxlen >= MCLBYTES "
92162587Sitojun		    "(%d + %d > %d)\n", max_linkhdr, maxlen, MCLBYTES);
92262587Sitojun#endif
92353541Sshin		return;
92462587Sitojun	}
92553541Sshin
926111119Simp	MGETHDR(m, M_DONTWAIT, MT_DATA);
92762587Sitojun	if (m && max_linkhdr + maxlen >= MHLEN) {
928111119Simp		MCLGET(m, M_DONTWAIT);
92962587Sitojun		if ((m->m_flags & M_EXT) == 0) {
93062587Sitojun			m_free(m);
93162587Sitojun			m = NULL;
93262587Sitojun		}
93362587Sitojun	}
93462587Sitojun	if (m == NULL)
93562587Sitojun		return;
93678064Sume	m->m_pkthdr.rcvif = NULL;
93762587Sitojun
938148385Sume	if (IN6_IS_ADDR_MULTICAST(&daddr6)) {
93953541Sshin		m->m_flags |= M_MCAST;
94053541Sshin		im6o.im6o_multicast_ifp = ifp;
94153541Sshin		im6o.im6o_multicast_hlim = 255;
94253541Sshin		im6o.im6o_multicast_loop = 0;
94353541Sshin	}
94453541Sshin
94553541Sshin	icmp6len = sizeof(*nd_na);
94653541Sshin	m->m_pkthdr.len = m->m_len = sizeof(struct ip6_hdr) + icmp6len;
94795023Ssuz	m->m_data += max_linkhdr;	/* or MH_ALIGN() equivalent? */
94853541Sshin
94953541Sshin	/* fill neighbor advertisement packet */
95053541Sshin	ip6 = mtod(m, struct ip6_hdr *);
95153541Sshin	ip6->ip6_flow = 0;
95262587Sitojun	ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
95362587Sitojun	ip6->ip6_vfc |= IPV6_VERSION;
95453541Sshin	ip6->ip6_nxt = IPPROTO_ICMPV6;
95553541Sshin	ip6->ip6_hlim = 255;
956148385Sume	if (IN6_IS_ADDR_UNSPECIFIED(&daddr6)) {
95753541Sshin		/* reply to DAD */
958153227Sume		daddr6.s6_addr16[0] = IPV6_ADDR_INT16_MLL;
959153227Sume		daddr6.s6_addr16[1] = 0;
960153227Sume		daddr6.s6_addr32[1] = 0;
961153227Sume		daddr6.s6_addr32[2] = 0;
962153227Sume		daddr6.s6_addr32[3] = IPV6_ADDR_INT32_ONE;
963148385Sume		if (in6_setscope(&daddr6, ifp, NULL))
964148385Sume			goto bad;
965148385Sume
96653541Sshin		flags &= ~ND_NA_FLAG_SOLICITED;
967148385Sume	}
968148385Sume	ip6->ip6_dst = daddr6;
969148385Sume	bzero(&dst_sa, sizeof(struct sockaddr_in6));
970148385Sume	dst_sa.sin6_family = AF_INET6;
971148385Sume	dst_sa.sin6_len = sizeof(struct sockaddr_in6);
972148385Sume	dst_sa.sin6_addr = daddr6;
97353541Sshin
97453541Sshin	/*
97553541Sshin	 * Select a source whose scope is the same as that of the dest.
97653541Sshin	 */
977148385Sume	bcopy(&dst_sa, &ro.ro_dst, sizeof(dst_sa));
978148385Sume	src = in6_selectsrc(&dst_sa, NULL, NULL, &ro, NULL, NULL, &error);
979148385Sume	if (src == NULL) {
980165118Sbz		char ip6buf[INET6_ADDRSTRLEN];
981148385Sume		nd6log((LOG_DEBUG, "nd6_na_output: source can't be "
982148385Sume		    "determined: dst=%s, error=%d\n",
983165118Sbz		    ip6_sprintf(ip6buf, &dst_sa.sin6_addr), error));
984148385Sume		goto bad;
98553541Sshin	}
986148385Sume	ip6->ip6_src = *src;
98753541Sshin	nd_na = (struct nd_neighbor_advert *)(ip6 + 1);
98853541Sshin	nd_na->nd_na_type = ND_NEIGHBOR_ADVERT;
98953541Sshin	nd_na->nd_na_code = 0;
99053541Sshin	nd_na->nd_na_target = *taddr6;
991121315Sume	in6_clearscope(&nd_na->nd_na_target); /* XXX */
99253541Sshin
99353541Sshin	/*
99453541Sshin	 * "tlladdr" indicates NS's condition for adding tlladdr or not.
99553541Sshin	 * see nd6_ns_input() for details.
99653541Sshin	 * Basically, if NS packet is sent to unicast/anycast addr,
99753541Sshin	 * target lladdr option SHOULD NOT be included.
99853541Sshin	 */
99962587Sitojun	if (tlladdr) {
100062587Sitojun		/*
100162587Sitojun		 * sdl0 != NULL indicates proxy NA.  If we do proxy, use
100262587Sitojun		 * lladdr in sdl0.  If we are not proxying (sending NA for
100362587Sitojun		 * my address) use lladdr configured for the interface.
100462587Sitojun		 */
1005142215Sglebius		if (sdl0 == NULL) {
1006142215Sglebius#ifdef DEV_CARP
1007142215Sglebius			if (ifp->if_carp)
1008142215Sglebius				mac = carp_macmatch6(ifp->if_carp, m, taddr6);
1009142215Sglebius			if (mac == NULL)
1010142215Sglebius				mac = nd6_ifptomac(ifp);
1011142215Sglebius#else
101262587Sitojun			mac = nd6_ifptomac(ifp);
1013142215Sglebius#endif
1014142215Sglebius		} else if (sdl0->sa_family == AF_LINK) {
101562587Sitojun			struct sockaddr_dl *sdl;
101662587Sitojun			sdl = (struct sockaddr_dl *)sdl0;
101762587Sitojun			if (sdl->sdl_alen == ifp->if_addrlen)
101862587Sitojun				mac = LLADDR(sdl);
101962587Sitojun		}
102062587Sitojun	}
102162587Sitojun	if (tlladdr && mac) {
102253541Sshin		int optlen = sizeof(struct nd_opt_hdr) + ifp->if_addrlen;
102353541Sshin		struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)(nd_na + 1);
1024120941Sume
102553541Sshin		/* roundup to 8 bytes alignment! */
102653541Sshin		optlen = (optlen + 7) & ~7;
102753541Sshin
102853541Sshin		m->m_pkthdr.len += optlen;
102953541Sshin		m->m_len += optlen;
103053541Sshin		icmp6len += optlen;
103153541Sshin		bzero((caddr_t)nd_opt, optlen);
103253541Sshin		nd_opt->nd_opt_type = ND_OPT_TARGET_LINKADDR;
103353541Sshin		nd_opt->nd_opt_len = optlen >> 3;
103453541Sshin		bcopy(mac, (caddr_t)(nd_opt + 1), ifp->if_addrlen);
103553541Sshin	} else
103653541Sshin		flags &= ~ND_NA_FLAG_OVERRIDE;
103753541Sshin
103853541Sshin	ip6->ip6_plen = htons((u_short)icmp6len);
103953541Sshin	nd_na->nd_na_flags_reserved = flags;
104053541Sshin	nd_na->nd_na_cksum = 0;
104153541Sshin	nd_na->nd_na_cksum =
1042120941Sume	    in6_cksum(m, IPPROTO_ICMPV6, sizeof(struct ip6_hdr), icmp6len);
104353541Sshin
1044148385Sume	ip6_output(m, NULL, &ro, 0, &im6o, NULL, NULL);
1045148385Sume	icmp6_ifstat_inc(ifp, ifs6_out_msg);
1046148385Sume	icmp6_ifstat_inc(ifp, ifs6_out_neighboradvert);
1047181803Sbz	V_icmp6stat.icp6s_outhist[ND_NEIGHBOR_ADVERT]++;
1048148385Sume
1049148385Sume	if (ro.ro_rt) {		/* we don't cache this route. */
1050148385Sume		RTFREE(ro.ro_rt);
105153541Sshin	}
1052148385Sume	return;
1053148385Sume
1054148385Sume  bad:
1055148385Sume	if (ro.ro_rt) {
1056148385Sume		RTFREE(ro.ro_rt);
1057148385Sume	}
1058148385Sume	m_freem(m);
1059148385Sume	return;
106053541Sshin}
106153541Sshin
106253541Sshincaddr_t
1063171259Sdelphijnd6_ifptomac(struct ifnet *ifp)
106453541Sshin{
106553541Sshin	switch (ifp->if_type) {
106653541Sshin	case IFT_ARCNET:
106753541Sshin	case IFT_ETHER:
106853541Sshin	case IFT_FDDI:
106978064Sume	case IFT_IEEE1394:
107078468Ssumikawa#ifdef IFT_L2VLAN
107178468Ssumikawa	case IFT_L2VLAN:
107278468Ssumikawa#endif
107378064Sume#ifdef IFT_IEEE80211
107478064Sume	case IFT_IEEE80211:
107578064Sume#endif
1076142215Sglebius#ifdef IFT_CARP
1077142215Sglebius	case IFT_CARP:
1078142215Sglebius#endif
1079149829Sthompsa	case IFT_BRIDGE:
1080120049Smdodd	case IFT_ISO88025:
1081147306Sbrooks		return IF_LLADDR(ifp);
108253541Sshin	default:
108353541Sshin		return NULL;
108453541Sshin	}
108553541Sshin}
108653541Sshin
108760938SjakeTAILQ_HEAD(dadq_head, dadq);
108853541Sshinstruct dadq {
108960938Sjake	TAILQ_ENTRY(dadq) dad_list;
109053541Sshin	struct ifaddr *dad_ifa;
109153541Sshin	int dad_count;		/* max NS to send */
109262587Sitojun	int dad_ns_tcount;	/* # of trials to send NS */
109353541Sshin	int dad_ns_ocount;	/* NS sent so far */
109453541Sshin	int dad_ns_icount;
109553541Sshin	int dad_na_icount;
109678064Sume	struct callout dad_timer_ch;
109753541Sshin};
109853541Sshin
109953541Sshinstatic struct dadq_head dadq;
110078064Sumestatic int dad_init = 0;
110153541Sshin
110253541Sshinstatic struct dadq *
1103171259Sdelphijnd6_dad_find(struct ifaddr *ifa)
110453541Sshin{
1105183550Szec	INIT_VNET_INET6(curvnet);
110653541Sshin	struct dadq *dp;
110753541Sshin
1108181803Sbz	for (dp = V_dadq.tqh_first; dp; dp = dp->dad_list.tqe_next) {
110953541Sshin		if (dp->dad_ifa == ifa)
111053541Sshin			return dp;
111153541Sshin	}
111253541Sshin	return NULL;
111353541Sshin}
111453541Sshin
111578064Sumestatic void
1116171259Sdelphijnd6_dad_starttimer(struct dadq *dp, int ticks)
111778064Sume{
111878064Sume
111978064Sume	callout_reset(&dp->dad_timer_ch, ticks,
1120175162Sobrien	    (void (*)(void *))nd6_dad_timer, (void *)dp->dad_ifa);
112178064Sume}
112278064Sume
112378064Sumestatic void
1124171259Sdelphijnd6_dad_stoptimer(struct dadq *dp)
112578064Sume{
112678064Sume
112778064Sume	callout_stop(&dp->dad_timer_ch);
112878064Sume}
112978064Sume
113053541Sshin/*
1131148987Sume * Start Duplicate Address Detection (DAD) for specified interface address.
113253541Sshin */
113353541Sshinvoid
1134171259Sdelphijnd6_dad_start(struct ifaddr *ifa, int delay)
113553541Sshin{
1136183550Szec	INIT_VNET_INET6(curvnet);
113753541Sshin	struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa;
113853541Sshin	struct dadq *dp;
1139165118Sbz	char ip6buf[INET6_ADDRSTRLEN];
114053541Sshin
1141181803Sbz	if (!V_dad_init) {
1142181803Sbz		TAILQ_INIT(&V_dadq);
1143181803Sbz		V_dad_init++;
114453541Sshin	}
114553541Sshin
114653541Sshin	/*
114753541Sshin	 * If we don't need DAD, don't do it.
114853541Sshin	 * There are several cases:
114953541Sshin	 * - DAD is disabled (ip6_dad_count == 0)
115053541Sshin	 * - the interface address is anycast
115153541Sshin	 */
115253541Sshin	if (!(ia->ia6_flags & IN6_IFF_TENTATIVE)) {
115362587Sitojun		log(LOG_DEBUG,
115462587Sitojun			"nd6_dad_start: called with non-tentative address "
115553541Sshin			"%s(%s)\n",
1156165118Sbz			ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr),
115753541Sshin			ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???");
115853541Sshin		return;
115953541Sshin	}
116053541Sshin	if (ia->ia6_flags & IN6_IFF_ANYCAST) {
116153541Sshin		ia->ia6_flags &= ~IN6_IFF_TENTATIVE;
116253541Sshin		return;
116353541Sshin	}
1164181803Sbz	if (!V_ip6_dad_count) {
116553541Sshin		ia->ia6_flags &= ~IN6_IFF_TENTATIVE;
116653541Sshin		return;
116753541Sshin	}
1168151465Ssuz	if (ifa->ifa_ifp == NULL)
116953541Sshin		panic("nd6_dad_start: ifa->ifa_ifp == NULL");
1170120941Sume	if (!(ifa->ifa_ifp->if_flags & IFF_UP)) {
117153541Sshin		return;
1172120941Sume	}
117353541Sshin	if (nd6_dad_find(ifa) != NULL) {
117453541Sshin		/* DAD already in progress */
117553541Sshin		return;
117653541Sshin	}
117753541Sshin
117853541Sshin	dp = malloc(sizeof(*dp), M_IP6NDP, M_NOWAIT);
117953541Sshin	if (dp == NULL) {
118062587Sitojun		log(LOG_ERR, "nd6_dad_start: memory allocation failed for "
118153541Sshin			"%s(%s)\n",
1182165118Sbz			ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr),
118353541Sshin			ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???");
118453541Sshin		return;
118553541Sshin	}
118653541Sshin	bzero(dp, sizeof(*dp));
118778064Sume	callout_init(&dp->dad_timer_ch, 0);
1188182150Sjulian	TAILQ_INSERT_TAIL(&V_dadq, (struct dadq *)dp, dad_list);
118953541Sshin
119078064Sume	nd6log((LOG_DEBUG, "%s: starting DAD for %s\n", if_name(ifa->ifa_ifp),
1191165118Sbz	    ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr)));
119253541Sshin
119353541Sshin	/*
119453541Sshin	 * Send NS packet for DAD, ip6_dad_count times.
119553541Sshin	 * Note that we must delay the first transmission, if this is the
119653541Sshin	 * first packet to be sent from the interface after interface
119753541Sshin	 * (re)initialization.
119853541Sshin	 */
119953541Sshin	dp->dad_ifa = ifa;
120095023Ssuz	IFAREF(ifa);	/* just for safety */
1201181803Sbz	dp->dad_count = V_ip6_dad_count;
120253541Sshin	dp->dad_ns_icount = dp->dad_na_icount = 0;
120362587Sitojun	dp->dad_ns_ocount = dp->dad_ns_tcount = 0;
1204151539Ssuz	if (delay == 0) {
120562587Sitojun		nd6_dad_ns_output(dp, ifa);
1206121161Sume		nd6_dad_starttimer(dp,
1207151539Ssuz		    (long)ND_IFINFO(ifa->ifa_ifp)->retrans * hz / 1000);
120853541Sshin	} else {
1209151539Ssuz		nd6_dad_starttimer(dp, delay);
121053541Sshin	}
121153541Sshin}
121253541Sshin
121378064Sume/*
121478064Sume * terminate DAD unconditionally.  used for address removals.
121578064Sume */
121678064Sumevoid
1217171259Sdelphijnd6_dad_stop(struct ifaddr *ifa)
121878064Sume{
1219183550Szec	INIT_VNET_INET6(curvnet);
122078064Sume	struct dadq *dp;
122178064Sume
1222181803Sbz	if (!V_dad_init)
122378064Sume		return;
122478064Sume	dp = nd6_dad_find(ifa);
122578064Sume	if (!dp) {
122678064Sume		/* DAD wasn't started yet */
122778064Sume		return;
122878064Sume	}
122978064Sume
123078064Sume	nd6_dad_stoptimer(dp);
123178064Sume
1232181803Sbz	TAILQ_REMOVE(&V_dadq, (struct dadq *)dp, dad_list);
123378064Sume	free(dp, M_IP6NDP);
123478064Sume	dp = NULL;
123578064Sume	IFAFREE(ifa);
123678064Sume}
123778064Sume
123853541Sshinstatic void
1239171259Sdelphijnd6_dad_timer(struct ifaddr *ifa)
124053541Sshin{
1241183550Szec	CURVNET_SET(dp->dad_vnet);
1242183550Szec	INIT_VNET_INET6(curvnet);
124353541Sshin	int s;
124453541Sshin	struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa;
124553541Sshin	struct dadq *dp;
1246165118Sbz	char ip6buf[INET6_ADDRSTRLEN];
124753541Sshin
124895023Ssuz	s = splnet();		/* XXX */
124953541Sshin
125053541Sshin	/* Sanity check */
125153541Sshin	if (ia == NULL) {
125262587Sitojun		log(LOG_ERR, "nd6_dad_timer: called with null parameter\n");
125353541Sshin		goto done;
125453541Sshin	}
125553541Sshin	dp = nd6_dad_find(ifa);
125653541Sshin	if (dp == NULL) {
125762587Sitojun		log(LOG_ERR, "nd6_dad_timer: DAD structure not found\n");
125853541Sshin		goto done;
125953541Sshin	}
126053541Sshin	if (ia->ia6_flags & IN6_IFF_DUPLICATED) {
126162587Sitojun		log(LOG_ERR, "nd6_dad_timer: called with duplicated address "
126253541Sshin			"%s(%s)\n",
1263165118Sbz			ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr),
126453541Sshin			ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???");
126553541Sshin		goto done;
126653541Sshin	}
126753541Sshin	if ((ia->ia6_flags & IN6_IFF_TENTATIVE) == 0) {
126862587Sitojun		log(LOG_ERR, "nd6_dad_timer: called with non-tentative address "
126953541Sshin			"%s(%s)\n",
1270165118Sbz			ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr),
127153541Sshin			ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???");
127253541Sshin		goto done;
127353541Sshin	}
127453541Sshin
127562587Sitojun	/* timeouted with IFF_{RUNNING,UP} check */
1276181803Sbz	if (dp->dad_ns_tcount > V_dad_maxtry) {
127778064Sume		nd6log((LOG_INFO, "%s: could not run DAD, driver problem?\n",
1278120941Sume		    if_name(ifa->ifa_ifp)));
127962587Sitojun
1280181803Sbz		TAILQ_REMOVE(&V_dadq, (struct dadq *)dp, dad_list);
128162587Sitojun		free(dp, M_IP6NDP);
128262587Sitojun		dp = NULL;
128362587Sitojun		IFAFREE(ifa);
128462587Sitojun		goto done;
128562587Sitojun	}
128662587Sitojun
128753541Sshin	/* Need more checks? */
128853541Sshin	if (dp->dad_ns_ocount < dp->dad_count) {
128953541Sshin		/*
129053541Sshin		 * We have more NS to go.  Send NS packet for DAD.
129153541Sshin		 */
129262587Sitojun		nd6_dad_ns_output(dp, ifa);
1293120941Sume		nd6_dad_starttimer(dp,
1294151539Ssuz		    (long)ND_IFINFO(ifa->ifa_ifp)->retrans * hz / 1000);
129553541Sshin	} else {
129653541Sshin		/*
129753541Sshin		 * We have transmitted sufficient number of DAD packets.
129853541Sshin		 * See what we've got.
129953541Sshin		 */
130053541Sshin		int duplicate;
130153541Sshin
130253541Sshin		duplicate = 0;
130353541Sshin
130453541Sshin		if (dp->dad_na_icount) {
130553541Sshin			/*
130653541Sshin			 * the check is in nd6_dad_na_input(),
130753541Sshin			 * but just in case
130853541Sshin			 */
130953541Sshin			duplicate++;
131053541Sshin		}
131153541Sshin
131253541Sshin		if (dp->dad_ns_icount) {
131353541Sshin			/* We've seen NS, means DAD has failed. */
131453541Sshin			duplicate++;
131553541Sshin		}
131653541Sshin
131753541Sshin		if (duplicate) {
131853541Sshin			/* (*dp) will be freed in nd6_dad_duplicated() */
131953541Sshin			dp = NULL;
132053541Sshin			nd6_dad_duplicated(ifa);
132153541Sshin		} else {
132253541Sshin			/*
132353541Sshin			 * We are done with DAD.  No NA came, no NS came.
1324148987Sume			 * No duplicate address found.
132553541Sshin			 */
132653541Sshin			ia->ia6_flags &= ~IN6_IFF_TENTATIVE;
132753541Sshin
132878064Sume			nd6log((LOG_DEBUG,
132962587Sitojun			    "%s: DAD complete for %s - no duplicates found\n",
133062587Sitojun			    if_name(ifa->ifa_ifp),
1331165118Sbz			    ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr)));
133253541Sshin
1333181803Sbz			TAILQ_REMOVE(&V_dadq, (struct dadq *)dp, dad_list);
133453541Sshin			free(dp, M_IP6NDP);
133553541Sshin			dp = NULL;
133662587Sitojun			IFAFREE(ifa);
133753541Sshin		}
133853541Sshin	}
133953541Sshin
134053541Sshindone:
134153541Sshin	splx(s);
1342183550Szec	CURVNET_RESTORE();
134353541Sshin}
134453541Sshin
134553541Sshinvoid
1346171259Sdelphijnd6_dad_duplicated(struct ifaddr *ifa)
134753541Sshin{
1348183550Szec	INIT_VNET_INET6(curvnet);
134953541Sshin	struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa;
1350151477Ssuz	struct ifnet *ifp;
135153541Sshin	struct dadq *dp;
1352165118Sbz	char ip6buf[INET6_ADDRSTRLEN];
135353541Sshin
135453541Sshin	dp = nd6_dad_find(ifa);
135553541Sshin	if (dp == NULL) {
135662587Sitojun		log(LOG_ERR, "nd6_dad_duplicated: DAD structure not found\n");
135753541Sshin		return;
135853541Sshin	}
135953541Sshin
136078064Sume	log(LOG_ERR, "%s: DAD detected duplicate IPv6 address %s: "
136178064Sume	    "NS in/out=%d/%d, NA in=%d\n",
1362165118Sbz	    if_name(ifa->ifa_ifp), ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr),
136378064Sume	    dp->dad_ns_icount, dp->dad_ns_ocount, dp->dad_na_icount);
136453541Sshin
136553541Sshin	ia->ia6_flags &= ~IN6_IFF_TENTATIVE;
136653541Sshin	ia->ia6_flags |= IN6_IFF_DUPLICATED;
136753541Sshin
1368148987Sume	/* We are done with DAD, with duplicate address found. (failure) */
136978064Sume	nd6_dad_stoptimer(dp);
137053541Sshin
1371151477Ssuz	ifp = ifa->ifa_ifp;
137262587Sitojun	log(LOG_ERR, "%s: DAD complete for %s - duplicate found\n",
1373165118Sbz	    if_name(ifp), ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr));
137462587Sitojun	log(LOG_ERR, "%s: manual intervention required\n",
1375151477Ssuz	    if_name(ifp));
137653541Sshin
1377151477Ssuz	/*
1378151477Ssuz	 * If the address is a link-local address formed from an interface
1379151477Ssuz	 * identifier based on the hardware address which is supposed to be
1380151477Ssuz	 * uniquely assigned (e.g., EUI-64 for an Ethernet interface), IP
1381151477Ssuz	 * operation on the interface SHOULD be disabled.
1382151477Ssuz	 * [rfc2462bis-03 Section 5.4.5]
1383151477Ssuz	 */
1384151477Ssuz	if (IN6_IS_ADDR_LINKLOCAL(&ia->ia_addr.sin6_addr)) {
1385151477Ssuz		struct in6_addr in6;
1386151477Ssuz
1387151477Ssuz		/*
1388151477Ssuz		 * To avoid over-reaction, we only apply this logic when we are
1389151477Ssuz		 * very sure that hardware addresses are supposed to be unique.
1390151477Ssuz		 */
1391151477Ssuz		switch (ifp->if_type) {
1392151477Ssuz		case IFT_ETHER:
1393151477Ssuz		case IFT_FDDI:
1394151477Ssuz		case IFT_ATM:
1395151477Ssuz		case IFT_IEEE1394:
1396151477Ssuz#ifdef IFT_IEEE80211
1397151477Ssuz		case IFT_IEEE80211:
1398151477Ssuz#endif
1399151477Ssuz			in6 = ia->ia_addr.sin6_addr;
1400151477Ssuz			if (in6_get_hw_ifid(ifp, &in6) == 0 &&
1401151477Ssuz			    IN6_ARE_ADDR_EQUAL(&ia->ia_addr.sin6_addr, &in6)) {
1402151477Ssuz				ND_IFINFO(ifp)->flags |= ND6_IFF_IFDISABLED;
1403151477Ssuz				log(LOG_ERR, "%s: possible hardware address "
1404151477Ssuz				    "duplication detected, disable IPv6\n",
1405151477Ssuz				    if_name(ifp));
1406151477Ssuz			}
1407151477Ssuz			break;
1408151477Ssuz		}
1409151477Ssuz	}
1410151477Ssuz
1411181803Sbz	TAILQ_REMOVE(&V_dadq, (struct dadq *)dp, dad_list);
141253541Sshin	free(dp, M_IP6NDP);
141353541Sshin	dp = NULL;
141462587Sitojun	IFAFREE(ifa);
141553541Sshin}
141653541Sshin
141762587Sitojunstatic void
1418171259Sdelphijnd6_dad_ns_output(struct dadq *dp, struct ifaddr *ifa)
141962587Sitojun{
142062587Sitojun	struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa;
142162587Sitojun	struct ifnet *ifp = ifa->ifa_ifp;
142262587Sitojun
142362587Sitojun	dp->dad_ns_tcount++;
142462587Sitojun	if ((ifp->if_flags & IFF_UP) == 0) {
142562587Sitojun		return;
142662587Sitojun	}
1427148887Srwatson	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
142862587Sitojun		return;
142962587Sitojun	}
143062587Sitojun
143162587Sitojun	dp->dad_ns_ocount++;
143262587Sitojun	nd6_ns_output(ifp, NULL, &ia->ia_addr.sin6_addr, NULL, 1);
143362587Sitojun}
143462587Sitojun
143562587Sitojunstatic void
1436171259Sdelphijnd6_dad_ns_input(struct ifaddr *ifa)
143753541Sshin{
1438183550Szec	INIT_VNET_INET6(curvnet);
143953541Sshin	struct in6_ifaddr *ia;
144053541Sshin	struct ifnet *ifp;
144178064Sume	const struct in6_addr *taddr6;
144253541Sshin	struct dadq *dp;
144353541Sshin	int duplicate;
144453541Sshin
1445151465Ssuz	if (ifa == NULL)
144653541Sshin		panic("ifa == NULL in nd6_dad_ns_input");
144753541Sshin
144853541Sshin	ia = (struct in6_ifaddr *)ifa;
144953541Sshin	ifp = ifa->ifa_ifp;
145053541Sshin	taddr6 = &ia->ia_addr.sin6_addr;
145153541Sshin	duplicate = 0;
145253541Sshin	dp = nd6_dad_find(ifa);
145353541Sshin
145453541Sshin	/* Quickhack - completely ignore DAD NS packets */
1455181803Sbz	if (V_dad_ignore_ns) {
1456165118Sbz		char ip6buf[INET6_ADDRSTRLEN];
145778064Sume		nd6log((LOG_INFO,
145878064Sume		    "nd6_dad_ns_input: ignoring DAD NS packet for "
1459165118Sbz		    "address %s(%s)\n", ip6_sprintf(ip6buf, taddr6),
146078064Sume		    if_name(ifa->ifa_ifp)));
146153541Sshin		return;
146253541Sshin	}
146353541Sshin
146453541Sshin	/*
146553541Sshin	 * if I'm yet to start DAD, someone else started using this address
146653541Sshin	 * first.  I have a duplicate and you win.
146753541Sshin	 */
1468151465Ssuz	if (dp == NULL || dp->dad_ns_ocount == 0)
146953541Sshin		duplicate++;
147053541Sshin
147153541Sshin	/* XXX more checks for loopback situation - see nd6_dad_timer too */
147253541Sshin
147353541Sshin	if (duplicate) {
147453541Sshin		dp = NULL;	/* will be freed in nd6_dad_duplicated() */
147553541Sshin		nd6_dad_duplicated(ifa);
147653541Sshin	} else {
147753541Sshin		/*
147853541Sshin		 * not sure if I got a duplicate.
147953541Sshin		 * increment ns count and see what happens.
148053541Sshin		 */
148153541Sshin		if (dp)
148253541Sshin			dp->dad_ns_icount++;
148353541Sshin	}
148453541Sshin}
148553541Sshin
148662587Sitojunstatic void
1487171259Sdelphijnd6_dad_na_input(struct ifaddr *ifa)
148853541Sshin{
148953541Sshin	struct dadq *dp;
149053541Sshin
1491151465Ssuz	if (ifa == NULL)
149253541Sshin		panic("ifa == NULL in nd6_dad_na_input");
149353541Sshin
149453541Sshin	dp = nd6_dad_find(ifa);
149553541Sshin	if (dp)
149653541Sshin		dp->dad_na_icount++;
149753541Sshin
149853541Sshin	/* remove the address. */
149953541Sshin	nd6_dad_duplicated(ifa);
150053541Sshin}
1501