xref: /netbsd-src/sys/netinet6/nd6.c (revision 5a9ce3a7bf83e158cd6c1eb8f71cec8041d3630c)
1*5a9ce3a7Sknakahara /*	$NetBSD: nd6.c,v 1.282 2024/04/11 07:34:37 knakahara Exp $	*/
26d8d0d63Sitojun /*	$KAME: nd6.c,v 1.279 2002/06/08 11:16:51 itojun Exp $	*/
3cd3a345eSthorpej 
474d3c214Sitojun /*
574d3c214Sitojun  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
674d3c214Sitojun  * All rights reserved.
774d3c214Sitojun  *
874d3c214Sitojun  * Redistribution and use in source and binary forms, with or without
974d3c214Sitojun  * modification, are permitted provided that the following conditions
1074d3c214Sitojun  * are met:
1174d3c214Sitojun  * 1. Redistributions of source code must retain the above copyright
1274d3c214Sitojun  *    notice, this list of conditions and the following disclaimer.
1374d3c214Sitojun  * 2. Redistributions in binary form must reproduce the above copyright
1474d3c214Sitojun  *    notice, this list of conditions and the following disclaimer in the
1574d3c214Sitojun  *    documentation and/or other materials provided with the distribution.
1674d3c214Sitojun  * 3. Neither the name of the project nor the names of its contributors
1774d3c214Sitojun  *    may be used to endorse or promote products derived from this software
1874d3c214Sitojun  *    without specific prior written permission.
1974d3c214Sitojun  *
2074d3c214Sitojun  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
2174d3c214Sitojun  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2274d3c214Sitojun  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2374d3c214Sitojun  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2474d3c214Sitojun  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2574d3c214Sitojun  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2674d3c214Sitojun  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2774d3c214Sitojun  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2874d3c214Sitojun  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2974d3c214Sitojun  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3074d3c214Sitojun  * SUCH DAMAGE.
3174d3c214Sitojun  */
3274d3c214Sitojun 
334f2ad952Slukem #include <sys/cdefs.h>
34*5a9ce3a7Sknakahara __KERNEL_RCSID(0, "$NetBSD: nd6.c,v 1.282 2024/04/11 07:34:37 knakahara Exp $");
3536d424c9Sozaki-r 
361c4a50f1Spooka #ifdef _KERNEL_OPT
37b05648aaSroy #include "opt_compat_netbsd.h"
3836d424c9Sozaki-r #include "opt_net_mpsafe.h"
391c4a50f1Spooka #endif
404e6aca94Sitojun 
4115d73271Sroy #include "bridge.h"
4215d73271Sroy #include "carp.h"
434f2ad952Slukem 
4474d3c214Sitojun #include <sys/param.h>
4574d3c214Sitojun #include <sys/systm.h>
46fc96443dSthorpej #include <sys/callout.h>
4766d96cc0Sozaki-r #include <sys/kmem.h>
4874d3c214Sitojun #include <sys/mbuf.h>
4974d3c214Sitojun #include <sys/socket.h>
5015e29e98Sad #include <sys/socketvar.h>
5174d3c214Sitojun #include <sys/sockio.h>
5274d3c214Sitojun #include <sys/time.h>
5374d3c214Sitojun #include <sys/kernel.h>
5474d3c214Sitojun #include <sys/errno.h>
5574d3c214Sitojun #include <sys/ioctl.h>
5674d3c214Sitojun #include <sys/syslog.h>
5774d3c214Sitojun #include <sys/queue.h>
583afd44cfStls #include <sys/cprng.h>
59dca032f9Sozaki-r #include <sys/workqueue.h>
604be362dbSpgoyette #include <sys/compat_stub.h>
6174d3c214Sitojun 
6274d3c214Sitojun #include <net/if.h>
6374d3c214Sitojun #include <net/if_dl.h>
64ecd5b23eSozaki-r #include <net/if_llatbl.h>
6574d3c214Sitojun #include <net/if_types.h>
66f456857bSroy #include <net/nd.h>
6774d3c214Sitojun #include <net/route.h>
68ed45b704Sitojun #include <net/if_ether.h>
69ed45b704Sitojun #include <net/if_arc.h>
7074d3c214Sitojun 
7174d3c214Sitojun #include <netinet/in.h>
7274d3c214Sitojun #include <netinet6/in6_var.h>
7390736ab6Sitojun #include <netinet/ip6.h>
7474d3c214Sitojun #include <netinet6/ip6_var.h>
7578678b13Srpaulo #include <netinet6/scope6_var.h>
7674d3c214Sitojun #include <netinet6/nd6.h>
7703980252Sroy #include <netinet6/in6_ifattach.h>
7890736ab6Sitojun #include <netinet/icmp6.h>
790dd41b37Sthorpej #include <netinet6/icmp6_private.h>
8074d3c214Sitojun 
81b05648aaSroy #include <compat/netinet6/in6_var.h>
82b05648aaSroy #include <compat/netinet6/nd6.h>
83b05648aaSroy 
8474d3c214Sitojun #define ND6_SLOWTIMER_INTERVAL (60 * 60) /* 1 hour */
8574d3c214Sitojun #define ND6_RECALC_REACHTM_INTERVAL (60 * 120) /* 2 hours */
8674d3c214Sitojun 
8774d3c214Sitojun /* timer values */
8874d3c214Sitojun int	nd6_prune	= 1;	/* walk list every 1 seconds */
8974d3c214Sitojun int	nd6_useloopback = 1;	/* use loopback interface for local traffic */
9074d3c214Sitojun 
91ea861f01Sitojun /* preventing too many loops in ND option parsing */
92ea861f01Sitojun int nd6_maxndopt = 10;	/* max # of ND options allowed */
93ea861f01Sitojun 
9422b473e0Sitojun #ifdef ND6_DEBUG
9522b473e0Sitojun int nd6_debug = 1;
9622b473e0Sitojun #else
9722b473e0Sitojun int nd6_debug = 0;
9822b473e0Sitojun #endif
9922b473e0Sitojun 
1003adf4b3bSozaki-r krwlock_t nd6_lock __cacheline_aligned;
1013adf4b3bSozaki-r 
10274d3c214Sitojun int nd6_recalc_reachtm_interval = ND6_RECALC_REACHTM_INTERVAL;
10374d3c214Sitojun 
104122b86e2Sdyoung static void nd6_slowtimo(void *);
10509973b35Sozaki-r static void nd6_free(struct llentry *, int);
106f456857bSroy static bool nd6_nud_enabled(struct ifnet *);
107f456857bSroy static unsigned int nd6_llinfo_reachable(struct ifnet *);
108f456857bSroy static unsigned int nd6_llinfo_retrans(struct ifnet *);
10920c15691Sroy static union l3addr *nd6_llinfo_holdsrc(struct llentry *, union l3addr *);
11020c15691Sroy static void nd6_llinfo_output(struct ifnet *, const union l3addr *,
11120c15691Sroy     const union l3addr *, const uint8_t *, const union l3addr *);
11220c15691Sroy static void nd6_llinfo_missed(struct ifnet *, const union l3addr *,
113e53a363eSroy     int16_t, struct mbuf *);
114acdecad0Sozaki-r static void nd6_timer(void *);
115dca032f9Sozaki-r static void nd6_timer_work(struct work *, void *);
1169d79cf8cSozaki-r static struct nd_opt_hdr *nd6_option(union nd_opts *);
11774d3c214Sitojun 
118acdecad0Sozaki-r static callout_t nd6_slowtimo_ch;
119acdecad0Sozaki-r static callout_t nd6_timer_ch;
120dca032f9Sozaki-r static struct workqueue	*nd6_timer_wq;
121dca032f9Sozaki-r static struct work	nd6_timer_wk;
122fc96443dSthorpej 
123f456857bSroy struct nd_domain nd6_nd_domain = {
124f456857bSroy 	.nd_family = AF_INET6,
125f456857bSroy 	.nd_delay = 5,		/* delay first probe time 5 second */
126f456857bSroy 	.nd_mmaxtries = 3,	/* maximum unicast query */
127f456857bSroy 	.nd_umaxtries = 3,	/* maximum multicast query */
128e53a363eSroy 	.nd_retransmultiple = BACKOFF_MULTIPLE,
129e53a363eSroy 	.nd_maxretrans = MAX_RETRANS_TIMER,
130f456857bSroy 	.nd_maxnudhint = 0,	/* max # of subsequent upper layer hints */
131f456857bSroy 	.nd_maxqueuelen = 1,	/* max # of packets in unresolved ND entries */
132f456857bSroy 	.nd_nud_enabled = nd6_nud_enabled,
133f456857bSroy 	.nd_reachable = nd6_llinfo_reachable,
134f456857bSroy 	.nd_retrans = nd6_llinfo_retrans,
135f456857bSroy 	.nd_holdsrc = nd6_llinfo_holdsrc,
136f456857bSroy 	.nd_output = nd6_llinfo_output,
137f456857bSroy 	.nd_missed = nd6_llinfo_missed,
138f456857bSroy 	.nd_free = nd6_free,
139f456857bSroy };
140f456857bSroy 
141b1934809Sthorpej MALLOC_DEFINE(M_IP6NDP, "NDP", "IPv6 Neighbour Discovery");
142b1934809Sthorpej 
14374d3c214Sitojun void
nd6_init(void)144833cc399Sdyoung nd6_init(void)
14574d3c214Sitojun {
146dca032f9Sozaki-r 	int error;
147ea861f01Sitojun 
148f456857bSroy 	nd_attach_domain(&nd6_nd_domain);
149042d1b5fSozaki-r 	nd6_nbr_init();
150042d1b5fSozaki-r 
1513adf4b3bSozaki-r 	rw_init(&nd6_lock);
1523adf4b3bSozaki-r 
15315e29e98Sad 	callout_init(&nd6_slowtimo_ch, CALLOUT_MPSAFE);
15415e29e98Sad 	callout_init(&nd6_timer_ch, CALLOUT_MPSAFE);
15588ab7da9Sad 
156dca032f9Sozaki-r 	error = workqueue_create(&nd6_timer_wq, "nd6_timer",
157dca032f9Sozaki-r 	    nd6_timer_work, NULL, PRI_SOFTNET, IPL_SOFTNET, WQ_MPSAFE);
158dca032f9Sozaki-r 	if (error)
159dca032f9Sozaki-r 		panic("%s: workqueue_create failed (%d)\n", __func__, error);
160dca032f9Sozaki-r 
16174d3c214Sitojun 	/* start timer */
162fc96443dSthorpej 	callout_reset(&nd6_slowtimo_ch, ND6_SLOWTIMER_INTERVAL * hz,
163fc96443dSthorpej 	    nd6_slowtimo, NULL);
164acdecad0Sozaki-r 	callout_reset(&nd6_timer_ch, hz, nd6_timer, NULL);
16574d3c214Sitojun }
16674d3c214Sitojun 
167b05648aaSroy struct nd_kifinfo *
nd6_ifattach(struct ifnet * ifp)168833cc399Sdyoung nd6_ifattach(struct ifnet *ifp)
16974d3c214Sitojun {
170b05648aaSroy 	struct nd_kifinfo *nd;
17174d3c214Sitojun 
17266d96cc0Sozaki-r 	nd = kmem_zalloc(sizeof(*nd), KM_SLEEP);
17374d3c214Sitojun 
1745c1df51dSitojun 	nd->chlim = IPV6_DEFHLIM;
1755c1df51dSitojun 	nd->basereachable = REACHABLE_TIME;
1765c1df51dSitojun 	nd->reachable = ND_COMPUTE_RTIME(nd->basereachable);
1775c1df51dSitojun 	nd->retrans = RETRANS_TIMER;
17803980252Sroy 
179b05648aaSroy 	nd->flags = ND6_IFF_PERFORMNUD;
18087fc46bcSitojun 
18103980252Sroy 	/* A loopback interface always has ND6_IFF_AUTO_LINKLOCAL.
18203980252Sroy 	 * A bridge interface should not have ND6_IFF_AUTO_LINKLOCAL
183f0a7346dSsnj 	 * because one of its members should. */
18403980252Sroy 	if ((ip6_auto_linklocal && ifp->if_type != IFT_BRIDGE) ||
18503980252Sroy 	    (ifp->if_flags & IFF_LOOPBACK))
18603980252Sroy 		nd->flags |= ND6_IFF_AUTO_LINKLOCAL;
18703980252Sroy 
1885c1df51dSitojun 	return nd;
18974d3c214Sitojun }
19074d3c214Sitojun 
19174d3c214Sitojun void
nd6_ifdetach(struct ifnet * ifp,struct in6_ifextra * ext)19294a27aa4Smartin nd6_ifdetach(struct ifnet *ifp, struct in6_ifextra *ext)
19374d3c214Sitojun {
1945c1df51dSitojun 
195834995eeSozaki-r 	/* Ensure all IPv6 addresses are purged before calling nd6_purge */
196834995eeSozaki-r 	if_purgeaddrs(ifp, AF_INET6, in6_purgeaddr);
19794a27aa4Smartin 	nd6_purge(ifp, ext);
198b05648aaSroy 	kmem_free(ext->nd_ifinfo, sizeof(struct nd_kifinfo));
19974d3c214Sitojun }
20074d3c214Sitojun 
20174d3c214Sitojun void
nd6_option_init(void * opt,int icmp6len,union nd_opts * ndopts)202833cc399Sdyoung nd6_option_init(void *opt, int icmp6len, union nd_opts *ndopts)
20374d3c214Sitojun {
2045c1df51dSitojun 
205ee1bfcb3Sdyoung 	memset(ndopts, 0, sizeof(*ndopts));
20674d3c214Sitojun 	ndopts->nd_opts_search = (struct nd_opt_hdr *)opt;
20774d3c214Sitojun 	ndopts->nd_opts_last
20874d3c214Sitojun 		= (struct nd_opt_hdr *)(((u_char *)opt) + icmp6len);
20974d3c214Sitojun 
21074d3c214Sitojun 	if (icmp6len == 0) {
21174d3c214Sitojun 		ndopts->nd_opts_done = 1;
21274d3c214Sitojun 		ndopts->nd_opts_search = NULL;
21374d3c214Sitojun 	}
21474d3c214Sitojun }
21574d3c214Sitojun 
21674d3c214Sitojun /*
21774d3c214Sitojun  * Take one ND option.
21874d3c214Sitojun  */
2199d79cf8cSozaki-r static struct nd_opt_hdr *
nd6_option(union nd_opts * ndopts)220833cc399Sdyoung nd6_option(union nd_opts *ndopts)
22174d3c214Sitojun {
22274d3c214Sitojun 	struct nd_opt_hdr *nd_opt;
22374d3c214Sitojun 	int olen;
22474d3c214Sitojun 
225452d01ddSozaki-r 	KASSERT(ndopts != NULL);
226452d01ddSozaki-r 	KASSERT(ndopts->nd_opts_last != NULL);
227452d01ddSozaki-r 
2288c2379fdSrpaulo 	if (ndopts->nd_opts_search == NULL)
22974d3c214Sitojun 		return NULL;
23074d3c214Sitojun 	if (ndopts->nd_opts_done)
23174d3c214Sitojun 		return NULL;
23274d3c214Sitojun 
23374d3c214Sitojun 	nd_opt = ndopts->nd_opts_search;
23474d3c214Sitojun 
235e1e31656Sitojun 	/* make sure nd_opt_len is inside the buffer */
23653524e44Schristos 	if ((void *)&nd_opt->nd_opt_len >= (void *)ndopts->nd_opts_last) {
237ee1bfcb3Sdyoung 		memset(ndopts, 0, sizeof(*ndopts));
238e1e31656Sitojun 		return NULL;
239e1e31656Sitojun 	}
240e1e31656Sitojun 
24174d3c214Sitojun 	olen = nd_opt->nd_opt_len << 3;
24274d3c214Sitojun 	if (olen == 0) {
24374d3c214Sitojun 		/*
24474d3c214Sitojun 		 * Message validation requires that all included
24574d3c214Sitojun 		 * options have a length that is greater than zero.
24674d3c214Sitojun 		 */
247ee1bfcb3Sdyoung 		memset(ndopts, 0, sizeof(*ndopts));
24874d3c214Sitojun 		return NULL;
24974d3c214Sitojun 	}
25074d3c214Sitojun 
25153524e44Schristos 	ndopts->nd_opts_search = (struct nd_opt_hdr *)((char *)nd_opt + olen);
252e1e31656Sitojun 	if (ndopts->nd_opts_search > ndopts->nd_opts_last) {
253e1e31656Sitojun 		/* option overruns the end of buffer, invalid */
254ee1bfcb3Sdyoung 		memset(ndopts, 0, sizeof(*ndopts));
255e1e31656Sitojun 		return NULL;
256e1e31656Sitojun 	} else if (ndopts->nd_opts_search == ndopts->nd_opts_last) {
257e1e31656Sitojun 		/* reached the end of options chain */
25874d3c214Sitojun 		ndopts->nd_opts_done = 1;
25974d3c214Sitojun 		ndopts->nd_opts_search = NULL;
26074d3c214Sitojun 	}
26174d3c214Sitojun 	return nd_opt;
26274d3c214Sitojun }
26374d3c214Sitojun 
26474d3c214Sitojun /*
26574d3c214Sitojun  * Parse multiple ND options.
26674d3c214Sitojun  * This function is much easier to use, for ND routines that do not need
26774d3c214Sitojun  * multiple options of the same type.
26874d3c214Sitojun  */
26974d3c214Sitojun int
nd6_options(union nd_opts * ndopts)270833cc399Sdyoung nd6_options(union nd_opts *ndopts)
27174d3c214Sitojun {
27274d3c214Sitojun 	struct nd_opt_hdr *nd_opt;
27374d3c214Sitojun 	int i = 0;
27474d3c214Sitojun 
275452d01ddSozaki-r 	KASSERT(ndopts != NULL);
276452d01ddSozaki-r 	KASSERT(ndopts->nd_opts_last != NULL);
277452d01ddSozaki-r 
2788c2379fdSrpaulo 	if (ndopts->nd_opts_search == NULL)
27974d3c214Sitojun 		return 0;
28074d3c214Sitojun 
28174d3c214Sitojun 	while (1) {
28274d3c214Sitojun 		nd_opt = nd6_option(ndopts);
2838c2379fdSrpaulo 		if (nd_opt == NULL && ndopts->nd_opts_last == NULL) {
28474d3c214Sitojun 			/*
28574d3c214Sitojun 			 * Message validation requires that all included
28674d3c214Sitojun 			 * options have a length that is greater than zero.
28774d3c214Sitojun 			 */
2880dd41b37Sthorpej 			ICMP6_STATINC(ICMP6_STAT_ND_BADOPT);
289ee1bfcb3Sdyoung 			memset(ndopts, 0, sizeof(*ndopts));
29074d3c214Sitojun 			return -1;
29174d3c214Sitojun 		}
29274d3c214Sitojun 
2938c2379fdSrpaulo 		if (nd_opt == NULL)
29474d3c214Sitojun 			goto skip1;
29574d3c214Sitojun 
29674d3c214Sitojun 		switch (nd_opt->nd_opt_type) {
29774d3c214Sitojun 		case ND_OPT_SOURCE_LINKADDR:
29874d3c214Sitojun 		case ND_OPT_TARGET_LINKADDR:
29974d3c214Sitojun 		case ND_OPT_MTU:
30074d3c214Sitojun 		case ND_OPT_REDIRECTED_HEADER:
30189bda1e3Sroy 		case ND_OPT_NONCE:
30274d3c214Sitojun 			if (ndopts->nd_opt_array[nd_opt->nd_opt_type]) {
303103bd8dfSozaki-r 				nd6log(LOG_INFO,
30422b473e0Sitojun 				    "duplicated ND6 option found (type=%d)\n",
305103bd8dfSozaki-r 				    nd_opt->nd_opt_type);
30674d3c214Sitojun 				/* XXX bark? */
30774d3c214Sitojun 			} else {
30874d3c214Sitojun 				ndopts->nd_opt_array[nd_opt->nd_opt_type]
30974d3c214Sitojun 					= nd_opt;
31074d3c214Sitojun 			}
31174d3c214Sitojun 			break;
31274d3c214Sitojun 		case ND_OPT_PREFIX_INFORMATION:
31374d3c214Sitojun 			if (ndopts->nd_opt_array[nd_opt->nd_opt_type] == 0) {
31474d3c214Sitojun 				ndopts->nd_opt_array[nd_opt->nd_opt_type]
31574d3c214Sitojun 					= nd_opt;
31674d3c214Sitojun 			}
31774d3c214Sitojun 			ndopts->nd_opts_pi_end =
31874d3c214Sitojun 				(struct nd_opt_prefix_info *)nd_opt;
31974d3c214Sitojun 			break;
32074d3c214Sitojun 		default:
32174d3c214Sitojun 			/*
32274d3c214Sitojun 			 * Unknown options must be silently ignored,
3231665d5e9Schristos 			 * to accommodate future extension to the protocol.
32474d3c214Sitojun 			 */
325103bd8dfSozaki-r 			nd6log(LOG_DEBUG,
32674d3c214Sitojun 			    "nd6_options: unsupported option %d - "
327103bd8dfSozaki-r 			    "option ignored\n", nd_opt->nd_opt_type);
32874d3c214Sitojun 		}
32974d3c214Sitojun 
33074d3c214Sitojun skip1:
33174d3c214Sitojun 		i++;
332ea861f01Sitojun 		if (i > nd6_maxndopt) {
3330dd41b37Sthorpej 			ICMP6_STATINC(ICMP6_STAT_ND_TOOMANYOPT);
334103bd8dfSozaki-r 			nd6log(LOG_INFO, "too many loop in nd opt\n");
33574d3c214Sitojun 			break;
33674d3c214Sitojun 		}
33774d3c214Sitojun 
33874d3c214Sitojun 		if (ndopts->nd_opts_done)
33974d3c214Sitojun 			break;
34074d3c214Sitojun 	}
34174d3c214Sitojun 
34274d3c214Sitojun 	return 0;
34374d3c214Sitojun }
34474d3c214Sitojun 
34574d3c214Sitojun /*
3465d81659aSozaki-r  * Gets source address of the first packet in hold queue
3475d81659aSozaki-r  * and stores it in @src.
3485d81659aSozaki-r  * Returns pointer to @src (if hold queue is not empty) or NULL.
3495d81659aSozaki-r  */
3505d81659aSozaki-r static struct in6_addr *
nd6_llinfo_get_holdsrc(struct llentry * ln,struct in6_addr * src)351ecd5b23eSozaki-r nd6_llinfo_get_holdsrc(struct llentry *ln, struct in6_addr *src)
3525d81659aSozaki-r {
3535d81659aSozaki-r 	struct ip6_hdr *hip6;
3545d81659aSozaki-r 
3555d81659aSozaki-r 	if (ln == NULL || ln->ln_hold == NULL)
3565d81659aSozaki-r 		return NULL;
3575d81659aSozaki-r 
3585d81659aSozaki-r 	/*
3595d81659aSozaki-r 	 * assuming every packet in ln_hold has the same IP header
3605d81659aSozaki-r 	 */
3615d81659aSozaki-r 	hip6 = mtod(ln->ln_hold, struct ip6_hdr *);
3625d81659aSozaki-r 	/* XXX pullup? */
3635d81659aSozaki-r 	if (sizeof(*hip6) < ln->ln_hold->m_len)
3645d81659aSozaki-r 		*src = hip6->ip6_src;
3655d81659aSozaki-r 	else
3665d81659aSozaki-r 		src = NULL;
3675d81659aSozaki-r 
3685d81659aSozaki-r 	return src;
3695d81659aSozaki-r }
3705d81659aSozaki-r 
37120c15691Sroy static union l3addr *
nd6_llinfo_holdsrc(struct llentry * ln,union l3addr * src)37220c15691Sroy nd6_llinfo_holdsrc(struct llentry *ln, union l3addr *src)
37374d3c214Sitojun {
37474d3c214Sitojun 
37520c15691Sroy 	if (nd6_llinfo_get_holdsrc(ln, &src->addr6) == NULL)
376f456857bSroy 		return NULL;
377f456857bSroy 	return src;
37874d3c214Sitojun }
3792cadb8caSitojun 
380f456857bSroy static void
nd6_llinfo_output(struct ifnet * ifp,const union l3addr * daddr,const union l3addr * taddr,__unused const uint8_t * tlladdr,const union l3addr * hsrc)38120c15691Sroy nd6_llinfo_output(struct ifnet *ifp, const union l3addr *daddr,
38220c15691Sroy     const union l3addr *taddr, __unused const uint8_t *tlladdr,
38320c15691Sroy     const union l3addr *hsrc)
384f456857bSroy {
3851231d107Sozaki-r 
386f421410cSnia 	nd6_ns_output(ifp,
387f421410cSnia 	    daddr != NULL ? &daddr->addr6 : NULL,
388f421410cSnia 	    taddr != NULL ? &taddr->addr6 : NULL,
389f421410cSnia 	    hsrc != NULL ? &hsrc->addr6 : NULL, NULL);
390ca19b576Sroy }
391ca19b576Sroy 
392f456857bSroy static bool
nd6_nud_enabled(struct ifnet * ifp)393f456857bSroy nd6_nud_enabled(struct ifnet *ifp)
394f456857bSroy {
395f456857bSroy 	struct nd_kifinfo *ndi = ND_IFINFO(ifp);
3966530896dSroy 
397f456857bSroy 	return ndi->flags & ND6_IFF_PERFORMNUD;
3983f765c21Sozaki-r }
399ca19b576Sroy 
400f456857bSroy static unsigned int
nd6_llinfo_reachable(struct ifnet * ifp)401f456857bSroy nd6_llinfo_reachable(struct ifnet *ifp)
402f456857bSroy {
403f456857bSroy 	struct nd_kifinfo *ndi = ND_IFINFO(ifp);
404ca19b576Sroy 
405f456857bSroy 	return ndi->reachable;
40674d3c214Sitojun }
4072cadb8caSitojun 
408f456857bSroy static unsigned int
nd6_llinfo_retrans(struct ifnet * ifp)409f456857bSroy nd6_llinfo_retrans(struct ifnet *ifp)
410f456857bSroy {
411f456857bSroy 	struct nd_kifinfo *ndi = ND_IFINFO(ifp);
4125d81659aSozaki-r 
413f456857bSroy 	return ndi->retrans;
4147cdf5bbeSozaki-r }
4157cdf5bbeSozaki-r 
416f456857bSroy static void
nd6_llinfo_missed(struct ifnet * ifp,const union l3addr * taddr,int16_t type,struct mbuf * m)417e53a363eSroy nd6_llinfo_missed(struct ifnet *ifp, const union l3addr *taddr,
418e53a363eSroy     int16_t type, struct mbuf *m)
419f456857bSroy {
4206530896dSroy 	struct in6_addr mdaddr6 = zeroin6_addr;
421f456857bSroy 	struct sockaddr_in6 dsin6, tsin6;
4226530896dSroy 	struct sockaddr *sa;
4236530896dSroy 
424e53a363eSroy 	if (m != NULL) {
425e53a363eSroy 		if (type == ND_LLINFO_PROBE) {
426e53a363eSroy 			struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
427e53a363eSroy 
428e53a363eSroy 			/* XXX pullup? */
429e53a363eSroy 			if (sizeof(*ip6) < m->m_len)
430e53a363eSroy 				mdaddr6 = ip6->ip6_src;
431e53a363eSroy 			m_freem(m);
432e53a363eSroy 		} else
43354af335eSchristos 			icmp6_error2(m, ICMP6_DST_UNREACH,
43454af335eSchristos 			    ICMP6_DST_UNREACH_ADDR, 0, ifp, &mdaddr6);
435e53a363eSroy 	}
4366530896dSroy 	if (!IN6_IS_ADDR_UNSPECIFIED(&mdaddr6)) {
4376530896dSroy 		sockaddr_in6_init(&dsin6, &mdaddr6, 0, 0, 0);
4386530896dSroy 		sa = sin6tosa(&dsin6);
4396530896dSroy 	} else
4406530896dSroy 		sa = NULL;
441f456857bSroy 
44220c15691Sroy 	sockaddr_in6_init(&tsin6, &taddr->addr6, 0, 0, 0);
4436530896dSroy 	rt_clonedmsg(RTM_MISS, sa, sin6tosa(&tsin6), NULL, ifp);
44454af335eSchristos }
44574d3c214Sitojun 
4462cadb8caSitojun /*
4472cadb8caSitojun  * ND6 timer routine to expire default route list and prefix list
4482cadb8caSitojun  */
449acdecad0Sozaki-r static void
nd6_timer_work(struct work * wk,void * arg)450dca032f9Sozaki-r nd6_timer_work(struct work *wk, void *arg)
4512cadb8caSitojun {
4522cadb8caSitojun 	struct in6_ifaddr *ia6, *nia6;
453a403cbd4Sozaki-r 	int s, bound;
454a403cbd4Sozaki-r 	struct psref psref;
4552cadb8caSitojun 
4562cadb8caSitojun 	callout_reset(&nd6_timer_ch, nd6_prune * hz,
4572cadb8caSitojun 	    nd6_timer, NULL);
4582cadb8caSitojun 
459cead3b88Sozaki-r 	SOFTNET_KERNEL_LOCK_UNLESS_NET_MPSAFE();
46015e29e98Sad 
461b05648aaSroy 	/* expire interface addresses */
462a403cbd4Sozaki-r 	bound = curlwp_bind();
463a403cbd4Sozaki-r 	s = pserialize_read_enter();
464a403cbd4Sozaki-r 	for (ia6 = IN6_ADDRLIST_READER_FIRST(); ia6; ia6 = nia6) {
465a403cbd4Sozaki-r 		nia6 = IN6_ADDRLIST_READER_NEXT(ia6);
466a403cbd4Sozaki-r 
467a403cbd4Sozaki-r 		ia6_acquire(ia6, &psref);
468a403cbd4Sozaki-r 		pserialize_read_exit(s);
469a403cbd4Sozaki-r 
47074d3c214Sitojun 		/* check address lifetime */
4716d8d0d63Sitojun 		if (IFA6_IS_INVALID(ia6)) {
472bde7231eSozaki-r 			struct ifnet *ifp;
4738c2379fdSrpaulo 
474bde7231eSozaki-r 			ifp = ia6->ia_ifa.ifa_ifp;
475bde7231eSozaki-r 			IFNET_LOCK(ifp);
476bde7231eSozaki-r 			/*
477bde7231eSozaki-r 			 * Need to take the lock first to prevent if_detach
478bde7231eSozaki-r 			 * from running in6_purgeaddr concurrently.
479bde7231eSozaki-r 			 */
480bde7231eSozaki-r 			if (!if_is_deactivated(ifp)) {
481a403cbd4Sozaki-r 				ia6_release(ia6, &psref);
4828c2379fdSrpaulo 				in6_purgeaddr(&ia6->ia_ifa);
483bde7231eSozaki-r 			} else {
484bde7231eSozaki-r 				/*
485bde7231eSozaki-r 				 * ifp is being destroyed, ia6 will be destroyed
486bde7231eSozaki-r 				 * by if_detach.
487bde7231eSozaki-r 				 */
488bde7231eSozaki-r 				ia6_release(ia6, &psref);
489bde7231eSozaki-r 			}
490a403cbd4Sozaki-r 			ia6 = NULL;
491bde7231eSozaki-r 			IFNET_UNLOCK(ifp);
4928c2379fdSrpaulo 		} else if (IFA6_IS_DEPRECATED(ia6)) {
4938c2379fdSrpaulo 			int oldflags = ia6->ia6_flags;
4948c2379fdSrpaulo 
495a34d7284Sroy 			if ((oldflags & IN6_IFF_DEPRECATED) == 0) {
49674d3c214Sitojun 				ia6->ia6_flags |= IN6_IFF_DEPRECATED;
4974af7afb8Sroy 				rt_addrmsg(RTM_NEWADDR, (struct ifaddr *)ia6);
498a34d7284Sroy 			}
4996d8d0d63Sitojun 		} else {
5006d8d0d63Sitojun 			/*
5016d8d0d63Sitojun 			 * A new RA might have made a deprecated address
5026d8d0d63Sitojun 			 * preferred.
5036d8d0d63Sitojun 			 */
504a34d7284Sroy 			if (ia6->ia6_flags & IN6_IFF_DEPRECATED) {
5056d8d0d63Sitojun 				ia6->ia6_flags &= ~IN6_IFF_DEPRECATED;
5064af7afb8Sroy 				rt_addrmsg(RTM_NEWADDR, (struct ifaddr *)ia6);
507a34d7284Sroy 			}
50874d3c214Sitojun 		}
509a403cbd4Sozaki-r 		s = pserialize_read_enter();
510a403cbd4Sozaki-r 		ia6_release(ia6, &psref);
51174d3c214Sitojun 	}
512a403cbd4Sozaki-r 	pserialize_read_exit(s);
513a403cbd4Sozaki-r 	curlwp_bindx(bound);
51474d3c214Sitojun 
515cead3b88Sozaki-r 	SOFTNET_KERNEL_UNLOCK_UNLESS_NET_MPSAFE();
51674d3c214Sitojun }
51774d3c214Sitojun 
518dca032f9Sozaki-r static void
nd6_timer(void * ignored_arg)519dca032f9Sozaki-r nd6_timer(void *ignored_arg)
520dca032f9Sozaki-r {
521dca032f9Sozaki-r 
522dca032f9Sozaki-r 	workqueue_enqueue(nd6_timer_wq, &nd6_timer_wk, NULL);
523dca032f9Sozaki-r }
524dca032f9Sozaki-r 
525f91ee608Sitojun /*
526f91ee608Sitojun  * Nuke neighbor cache/prefix/default router management table, right before
527f91ee608Sitojun  * ifp goes away.
528f91ee608Sitojun  */
529f91ee608Sitojun void
nd6_purge(struct ifnet * ifp,struct in6_ifextra * ext)53094a27aa4Smartin nd6_purge(struct ifnet *ifp, struct in6_ifextra *ext)
531f91ee608Sitojun {
532f91ee608Sitojun 
533f91ee608Sitojun 	/*
53494a27aa4Smartin 	 * During detach, the ND info might be already removed, but
53594a27aa4Smartin 	 * then is explitly passed as argument.
53694a27aa4Smartin 	 * Otherwise get it from ifp->if_afdata.
53794a27aa4Smartin 	 */
53894a27aa4Smartin 	if (ext == NULL)
53994a27aa4Smartin 		ext = ifp->if_afdata[AF_INET6];
54094a27aa4Smartin 	if (ext == NULL)
54194a27aa4Smartin 		return;
54294a27aa4Smartin 
543f91ee608Sitojun 	/*
544ecd5b23eSozaki-r 	 * We may not need to nuke the neighbor cache entries here
545ecd5b23eSozaki-r 	 * because the neighbor cache is kept in if_afdata[AF_INET6].
546ecd5b23eSozaki-r 	 * nd6_purge() is invoked by in6_ifdetach() which is called
547ecd5b23eSozaki-r 	 * from if_detach() where everything gets purged. However
548ecd5b23eSozaki-r 	 * in6_ifdetach is directly called from vlan(4), so we still
549ecd5b23eSozaki-r 	 * need to purge entries here.
550f91ee608Sitojun 	 */
551ecd5b23eSozaki-r 	if (ext->lltable != NULL)
552ecd5b23eSozaki-r 		lltable_purge_entries(ext->lltable);
553f91ee608Sitojun }
554f91ee608Sitojun 
55509973b35Sozaki-r struct llentry *
nd6_lookup(const struct in6_addr * addr6,const struct ifnet * ifp,bool wlock)55609973b35Sozaki-r nd6_lookup(const struct in6_addr *addr6, const struct ifnet *ifp, bool wlock)
55774d3c214Sitojun {
55874d3c214Sitojun 	struct sockaddr_in6 sin6;
55909973b35Sozaki-r 	struct llentry *ln;
56074d3c214Sitojun 
561c44fbd16Sdyoung 	sockaddr_in6_init(&sin6, addr6, 0, 0, 0);
56230272756Sitojun 
56309973b35Sozaki-r 	IF_AFDATA_RLOCK(ifp);
56409973b35Sozaki-r 	ln = lla_lookup(LLTABLE6(ifp), wlock ? LLE_EXCLUSIVE : 0,
56509973b35Sozaki-r 	    sin6tosa(&sin6));
56609973b35Sozaki-r 	IF_AFDATA_RUNLOCK(ifp);
56774d3c214Sitojun 
56809973b35Sozaki-r 	return ln;
56974d3c214Sitojun }
57074d3c214Sitojun 
57109973b35Sozaki-r struct llentry *
nd6_create(const struct in6_addr * addr6,const struct ifnet * ifp)57209973b35Sozaki-r nd6_create(const struct in6_addr *addr6, const struct ifnet *ifp)
573b122449bSroy {
57409973b35Sozaki-r 	struct sockaddr_in6 sin6;
57509973b35Sozaki-r 	struct llentry *ln;
5761c27f64dSozaki-r 	struct rtentry *rt;
577b122449bSroy 
57809973b35Sozaki-r 	sockaddr_in6_init(&sin6, addr6, 0, 0, 0);
5791c27f64dSozaki-r 	rt = rtalloc1(sin6tosa(&sin6), 0);
58009973b35Sozaki-r 
58109973b35Sozaki-r 	IF_AFDATA_WLOCK(ifp);
5821c27f64dSozaki-r 	ln = lla_create(LLTABLE6(ifp), LLE_EXCLUSIVE, sin6tosa(&sin6), rt);
58309973b35Sozaki-r 	IF_AFDATA_WUNLOCK(ifp);
58409973b35Sozaki-r 
5851c27f64dSozaki-r 	if (rt != NULL)
5861c27f64dSozaki-r 		rt_unref(rt);
58709973b35Sozaki-r 	if (ln != NULL)
588f456857bSroy 		ln->ln_state = ND_LLINFO_NOSTATE;
58909973b35Sozaki-r 
59009973b35Sozaki-r 	return ln;
59109973b35Sozaki-r }
59209973b35Sozaki-r 
59309973b35Sozaki-r /*
59409973b35Sozaki-r  * Test whether a given IPv6 address is a neighbor or not, ignoring
59509973b35Sozaki-r  * the actual neighbor cache.  The neighbor cache is ignored in order
59609973b35Sozaki-r  * to not reenter the routing code from within itself.
59709973b35Sozaki-r  */
59809973b35Sozaki-r static int
nd6_is_new_addr_neighbor(const struct sockaddr_in6 * addr,struct ifnet * ifp)59909973b35Sozaki-r nd6_is_new_addr_neighbor(const struct sockaddr_in6 *addr, struct ifnet *ifp)
60009973b35Sozaki-r {
60109973b35Sozaki-r 	struct ifaddr *dstaddr;
602a403cbd4Sozaki-r 	int s;
60309973b35Sozaki-r 
60409973b35Sozaki-r 	/*
60509973b35Sozaki-r 	 * A link-local address is always a neighbor.
60609973b35Sozaki-r 	 * XXX: a link does not necessarily specify a single interface.
60709973b35Sozaki-r 	 */
60809973b35Sozaki-r 	if (IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr)) {
60909973b35Sozaki-r 		struct sockaddr_in6 sin6_copy;
61009973b35Sozaki-r 		u_int32_t zone;
61109973b35Sozaki-r 
61209973b35Sozaki-r 		/*
61309973b35Sozaki-r 		 * We need sin6_copy since sa6_recoverscope() may modify the
61409973b35Sozaki-r 		 * content (XXX).
61509973b35Sozaki-r 		 */
61609973b35Sozaki-r 		sin6_copy = *addr;
61709973b35Sozaki-r 		if (sa6_recoverscope(&sin6_copy))
61809973b35Sozaki-r 			return 0; /* XXX: should be impossible */
61909973b35Sozaki-r 		if (in6_setscope(&sin6_copy.sin6_addr, ifp, &zone))
62009973b35Sozaki-r 			return 0;
62109973b35Sozaki-r 		if (sin6_copy.sin6_scope_id == zone)
62209973b35Sozaki-r 			return 1;
62309973b35Sozaki-r 		else
62409973b35Sozaki-r 			return 0;
62509973b35Sozaki-r 	}
62609973b35Sozaki-r 
62709973b35Sozaki-r 	/*
62809973b35Sozaki-r 	 * If the address is assigned on the node of the other side of
62909973b35Sozaki-r 	 * a p2p interface, the address should be a neighbor.
63009973b35Sozaki-r 	 */
631a403cbd4Sozaki-r 	s = pserialize_read_enter();
6328759207cSozaki-r 	dstaddr = ifa_ifwithdstaddr(sin6tocsa(addr));
63309973b35Sozaki-r 	if (dstaddr != NULL) {
63409973b35Sozaki-r 		if (dstaddr->ifa_ifp == ifp) {
635a403cbd4Sozaki-r 			pserialize_read_exit(s);
63609973b35Sozaki-r 			return 1;
63709973b35Sozaki-r 		}
63809973b35Sozaki-r 	}
639a403cbd4Sozaki-r 	pserialize_read_exit(s);
64009973b35Sozaki-r 
64109973b35Sozaki-r 	return 0;
642b122449bSroy }
643b122449bSroy 
64474d3c214Sitojun /*
645dcc13cddSitojun  * Detect if a given IPv6 address identifies a neighbor on a given link.
646dcc13cddSitojun  * XXX: should take care of the destination of a p2p link?
647dcc13cddSitojun  */
648dcc13cddSitojun int
nd6_is_addr_neighbor(const struct sockaddr_in6 * addr,struct ifnet * ifp)6495493f188Sdyoung nd6_is_addr_neighbor(const struct sockaddr_in6 *addr, struct ifnet *ifp)
650dcc13cddSitojun {
65109973b35Sozaki-r 	struct llentry *ln;
6529eae87d0Sozaki-r 	struct rtentry *rt;
653dcc13cddSitojun 
6544f1c7f0bSitojun 	/*
6554f1c7f0bSitojun 	 * A link-local address is always a neighbor.
6567dcf45fbSitojun 	 * XXX: a link does not necessarily specify a single interface.
6574f1c7f0bSitojun 	 */
65878678b13Srpaulo 	if (IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr)) {
65978678b13Srpaulo 		struct sockaddr_in6 sin6_copy;
66078678b13Srpaulo 		u_int32_t zone;
66178678b13Srpaulo 
66278678b13Srpaulo 		/*
66378678b13Srpaulo 		 * We need sin6_copy since sa6_recoverscope() may modify the
66478678b13Srpaulo 		 * content (XXX).
66578678b13Srpaulo 		 */
66678678b13Srpaulo 		sin6_copy = *addr;
66778678b13Srpaulo 		if (sa6_recoverscope(&sin6_copy))
668833cc399Sdyoung 			return 0; /* XXX: should be impossible */
66978678b13Srpaulo 		if (in6_setscope(&sin6_copy.sin6_addr, ifp, &zone))
670833cc399Sdyoung 			return 0;
67178678b13Srpaulo 		if (sin6_copy.sin6_scope_id == zone)
672833cc399Sdyoung 			return 1;
67378678b13Srpaulo 		else
674833cc399Sdyoung 			return 0;
67578678b13Srpaulo 	}
676dcc13cddSitojun 
67709973b35Sozaki-r 	if (nd6_is_new_addr_neighbor(addr, ifp))
67809973b35Sozaki-r 		return 1;
67909973b35Sozaki-r 
680dcc13cddSitojun 	/*
68109973b35Sozaki-r 	 * Even if the address matches none of our addresses, it might be
68209973b35Sozaki-r 	 * in the neighbor cache or a connected route.
683dcc13cddSitojun 	 */
68409973b35Sozaki-r 	ln = nd6_lookup(&addr->sin6_addr, ifp, false);
68509973b35Sozaki-r 	if (ln != NULL) {
68609973b35Sozaki-r 		LLE_RUNLOCK(ln);
68709973b35Sozaki-r 		return 1;
68809973b35Sozaki-r 	}
68909973b35Sozaki-r 
69009973b35Sozaki-r 	rt = rtalloc1(sin6tocsa(addr), 0);
69109973b35Sozaki-r 	if (rt == NULL)
69209973b35Sozaki-r 		return 0;
69309973b35Sozaki-r 
69409973b35Sozaki-r 	if ((rt->rt_flags & RTF_CONNECTED) && (rt->rt_ifp == ifp
69509973b35Sozaki-r #if NBRIDGE > 0
69609973b35Sozaki-r 	    || rt->rt_ifp->if_bridge == ifp->if_bridge
69709973b35Sozaki-r #endif
69809973b35Sozaki-r #if NCARP > 0
69909973b35Sozaki-r 	    || (ifp->if_type == IFT_CARP && rt->rt_ifp == ifp->if_carpdev) ||
70009973b35Sozaki-r 	    (rt->rt_ifp->if_type == IFT_CARP && rt->rt_ifp->if_carpdev == ifp)||
70109973b35Sozaki-r 	    (ifp->if_type == IFT_CARP && rt->rt_ifp->if_type == IFT_CARP &&
70209973b35Sozaki-r 	    rt->rt_ifp->if_carpdev == ifp->if_carpdev)
70309973b35Sozaki-r #endif
70409973b35Sozaki-r 	    )) {
7056fb88806Sozaki-r 		rt_unref(rt);
706833cc399Sdyoung 		return 1;
7079eae87d0Sozaki-r 	}
7086fb88806Sozaki-r 	rt_unref(rt);
709dcc13cddSitojun 
710833cc399Sdyoung 	return 0;
711dcc13cddSitojun }
712dcc13cddSitojun 
713dcc13cddSitojun /*
71474d3c214Sitojun  * Free an nd6 llinfo entry.
7151990d680Sitojun  * Since the function would cause significant changes in the kernel, DO NOT
7161990d680Sitojun  * make it global, unless you have a strong reason for the change, and are sure
7171990d680Sitojun  * that the change is safe.
71874d3c214Sitojun  */
719ecd5b23eSozaki-r static void
nd6_free(struct llentry * ln,int gc)72009973b35Sozaki-r nd6_free(struct llentry *ln, int gc)
72174d3c214Sitojun {
72209973b35Sozaki-r 	struct ifnet *ifp;
72374d3c214Sitojun 
724ecd5b23eSozaki-r 	KASSERT(ln != NULL);
725ecd5b23eSozaki-r 	LLE_WLOCK_ASSERT(ln);
726ecd5b23eSozaki-r 
7271450d6e6Sitojun 	/*
728b05648aaSroy 	 * If the reason for the deletion is just garbage collection,
729b05648aaSroy 	 * and the neighbor is an active router, do not delete it.
730b05648aaSroy 	 * Instead, reset the GC timer using the router's lifetime.
731b05648aaSroy 	 * XXX: the check for ln_state should be redundant,
7321990d680Sitojun 	 *      but we intentionally keep it just in case.
7331990d680Sitojun 	 */
734b05648aaSroy 	if (!ip6_forwarding && ln->ln_router &&
735f456857bSroy 	    ln->ln_state == ND_LLINFO_STALE && gc)
736b05648aaSroy 	{
737f456857bSroy 		nd_set_timer(ln, ND_TIMER_EXPIRE);
7389c1d1242Sozaki-r 		LLE_WUNLOCK(ln);
739ecd5b23eSozaki-r 		return;
7401990d680Sitojun 	}
7411990d680Sitojun 
742b05648aaSroy 	ifp = ln->lle_tbl->llt_ifp;
74374d3c214Sitojun 
744ca19b576Sroy 	if (ln->la_flags & LLE_VALID || gc) {
745ca19b576Sroy 		struct sockaddr_in6 sin6;
746ca19b576Sroy 		const char *lladdr;
747ca19b576Sroy 
748b05648aaSroy 		sockaddr_in6_init(&sin6, &ln->r_l3addr.addr6, 0, 0, 0);
749ca19b576Sroy 		lladdr = ln->la_flags & LLE_VALID ?
750ca19b576Sroy 		    (const char *)&ln->ll_addr : NULL;
7515ff17943Sroy 		rt_clonedmsg(RTM_DELETE, NULL, sin6tosa(&sin6), lladdr, ifp);
752ca19b576Sroy 	}
753ece8d087Sroy 
754a1d89972Sitojun 	/*
75509973b35Sozaki-r 	 * Save to unlock. We still hold an extra reference and will not
75609973b35Sozaki-r 	 * free(9) in llentry_free() if someone else holds one as well.
757a1d89972Sitojun 	 */
75809973b35Sozaki-r 	LLE_WUNLOCK(ln);
75909973b35Sozaki-r 	IF_AFDATA_LOCK(ifp);
76009973b35Sozaki-r 	LLE_WLOCK(ln);
76109973b35Sozaki-r 
7626261537bSozaki-r 	lltable_free_entry(LLTABLE6(ifp), ln);
76309973b35Sozaki-r 
76409973b35Sozaki-r 	IF_AFDATA_UNLOCK(ifp);
76574d3c214Sitojun }
76674d3c214Sitojun 
76774d3c214Sitojun /*
76874d3c214Sitojun  * Upper-layer reachability hint for Neighbor Unreachability Detection.
76974d3c214Sitojun  *
770eb35daf5Srpaulo  * XXX cost-effective methods?
77174d3c214Sitojun  */
77274d3c214Sitojun void
nd6_nud_hint(struct rtentry * rt)77331cbc4a7Sozaki-r nd6_nud_hint(struct rtentry *rt)
77474d3c214Sitojun {
775ecd5b23eSozaki-r 	struct llentry *ln;
77609973b35Sozaki-r 	struct ifnet *ifp;
77774d3c214Sitojun 
778fcda92b6Sozaki-r 	if (rt == NULL)
77974d3c214Sitojun 		return;
78074d3c214Sitojun 
78109973b35Sozaki-r 	ifp = rt->rt_ifp;
78209973b35Sozaki-r 	ln = nd6_lookup(&(satocsin6(rt_getkey(rt)))->sin6_addr, ifp, true);
783f456857bSroy 	nd_nud_hint(ln);
78474d3c214Sitojun }
78574d3c214Sitojun 
786ab06ed12Sozaki-r struct gc_args {
787ab06ed12Sozaki-r 	int gc_entries;
788ab06ed12Sozaki-r 	const struct in6_addr *skip_in6;
789ab06ed12Sozaki-r };
790ab06ed12Sozaki-r 
791ecd5b23eSozaki-r static int
nd6_purge_entry(struct lltable * llt,struct llentry * ln,void * farg)792ecd5b23eSozaki-r nd6_purge_entry(struct lltable *llt, struct llentry *ln, void *farg)
793ecd5b23eSozaki-r {
794ab06ed12Sozaki-r 	struct gc_args *args = farg;
795ab06ed12Sozaki-r 	int *n = &args->gc_entries;
796ab06ed12Sozaki-r 	const struct in6_addr *skip_in6 = args->skip_in6;
797ecd5b23eSozaki-r 
798ecd5b23eSozaki-r 	if (*n <= 0)
799ecd5b23eSozaki-r 		return 0;
800ecd5b23eSozaki-r 
801f456857bSroy 	if (ND_IS_LLINFO_PERMANENT(ln))
802ecd5b23eSozaki-r 		return 0;
803ecd5b23eSozaki-r 
804ab06ed12Sozaki-r 	if (IN6_ARE_ADDR_EQUAL(&ln->r_l3addr.addr6, skip_in6))
805ab06ed12Sozaki-r 		return 0;
806ab06ed12Sozaki-r 
807ecd5b23eSozaki-r 	LLE_WLOCK(ln);
808f456857bSroy 	if (ln->ln_state > ND_LLINFO_INCOMPLETE)
809f456857bSroy 		ln->ln_state = ND_LLINFO_STALE;
810ecd5b23eSozaki-r 	else
811f456857bSroy 		ln->ln_state = ND_LLINFO_PURGE;
812f456857bSroy 	nd_set_timer(ln, ND_TIMER_IMMEDIATE);
813ecd5b23eSozaki-r 	LLE_WUNLOCK(ln);
814ecd5b23eSozaki-r 
815ecd5b23eSozaki-r 	(*n)--;
816ecd5b23eSozaki-r 	return 0;
817ecd5b23eSozaki-r }
818ecd5b23eSozaki-r 
819ecd5b23eSozaki-r static void
nd6_gc_neighbors(struct lltable * llt,const struct in6_addr * in6)820ab06ed12Sozaki-r nd6_gc_neighbors(struct lltable *llt, const struct in6_addr *in6)
821ecd5b23eSozaki-r {
822ecd5b23eSozaki-r 
823ecd5b23eSozaki-r 	if (ip6_neighborgcthresh >= 0 &&
824ecd5b23eSozaki-r 	    lltable_get_entry_count(llt) >= ip6_neighborgcthresh) {
825ab06ed12Sozaki-r 		struct gc_args gc_args = {10, in6};
826ecd5b23eSozaki-r 		/*
827ecd5b23eSozaki-r 		 * XXX entries that are "less recently used" should be
828ecd5b23eSozaki-r 		 * freed first.
829ecd5b23eSozaki-r 		 */
830ab06ed12Sozaki-r 		lltable_foreach_lle(llt, nd6_purge_entry, &gc_args);
831ecd5b23eSozaki-r 	}
832ecd5b23eSozaki-r }
833ecd5b23eSozaki-r 
83474d3c214Sitojun void
nd6_rtrequest(int req,struct rtentry * rt,const struct rt_addrinfo * info)835cf969cfaSdyoung nd6_rtrequest(int req, struct rtentry *rt, const struct rt_addrinfo *info)
83674d3c214Sitojun {
83774d3c214Sitojun 	struct sockaddr *gate = rt->rt_gateway;
83874d3c214Sitojun 	struct ifnet *ifp = rt->rt_ifp;
839b3fc2963Sdyoung 	uint8_t namelen = strlen(ifp->if_xname), addrlen = ifp->if_addrlen;
84074d3c214Sitojun 	struct ifaddr *ifa;
84174d3c214Sitojun 
8425d7712e0Sjoerg 	RT_DPRINTF("rt_getkey(rt) = %p\n", rt_getkey(rt));
84308e6f222Sdyoung 
844de87fe67Sdyoung 	if (req == RTM_LLINFO_UPD) {
845de87fe67Sdyoung 		int rc;
846de87fe67Sdyoung 		struct in6_addr *in6;
847de87fe67Sdyoung 		struct in6_addr in6_all;
848de87fe67Sdyoung 		int anycast;
849de87fe67Sdyoung 
850de87fe67Sdyoung 		if ((ifa = info->rti_ifa) == NULL)
851de87fe67Sdyoung 			return;
852de87fe67Sdyoung 
853de87fe67Sdyoung 		in6 = &ifatoia6(ifa)->ia_addr.sin6_addr;
854de87fe67Sdyoung 		anycast = ifatoia6(ifa)->ia6_flags & IN6_IFF_ANYCAST;
855de87fe67Sdyoung 
856de87fe67Sdyoung 		in6_all = in6addr_linklocal_allnodes;
857de87fe67Sdyoung 		if ((rc = in6_setscope(&in6_all, ifa->ifa_ifp, NULL)) != 0) {
858de87fe67Sdyoung 			log(LOG_ERR, "%s: failed to set scope %s "
859de87fe67Sdyoung 			    "(errno=%d)\n", __func__, if_name(ifp), rc);
860de87fe67Sdyoung 			return;
861de87fe67Sdyoung 		}
862de87fe67Sdyoung 
863de87fe67Sdyoung 		/* XXX don't set Override for proxy addresses */
864de87fe67Sdyoung 		nd6_na_output(ifa->ifa_ifp, &in6_all, in6,
865de87fe67Sdyoung 		    (anycast ? 0 : ND_NA_FLAG_OVERRIDE)
866de87fe67Sdyoung #if 0
867de87fe67Sdyoung 		    | (ip6_forwarding ? ND_NA_FLAG_ROUTER : 0)
868de87fe67Sdyoung #endif
869de87fe67Sdyoung 		    , 1, NULL);
870de87fe67Sdyoung 		return;
871de87fe67Sdyoung 	}
872de87fe67Sdyoung 
873ba3b1f72Sroy 	if ((rt->rt_flags & RTF_GATEWAY) != 0) {
874ba3b1f72Sroy 		if (req != RTM_ADD)
87574d3c214Sitojun 			return;
876ba3b1f72Sroy 		/*
877ba3b1f72Sroy 		 * linklayers with particular MTU limitation.
878ba3b1f72Sroy 		 */
879ba3b1f72Sroy 		switch(ifp->if_type) {
880ba3b1f72Sroy #if NARCNET > 0
881ba3b1f72Sroy 		case IFT_ARCNET:
882ba3b1f72Sroy 			if (rt->rt_rmx.rmx_mtu > ARC_PHDS_MAXMTU) /* RFC2497 */
883ba3b1f72Sroy 				rt->rt_rmx.rmx_mtu = ARC_PHDS_MAXMTU;
884ba3b1f72Sroy 			break;
885ba3b1f72Sroy #endif
886ba3b1f72Sroy 		}
887ba3b1f72Sroy 		return;
888ba3b1f72Sroy 	}
88974d3c214Sitojun 
8901990d680Sitojun 	if (nd6_need_cache(ifp) == 0 && (rt->rt_flags & RTF_HOST) == 0) {
8915d7712e0Sjoerg 		RT_DPRINTF("rt_getkey(rt) = %p\n", rt_getkey(rt));
8921990d680Sitojun 		/*
8931990d680Sitojun 		 * This is probably an interface direct route for a link
8941990d680Sitojun 		 * which does not need neighbor caches (e.g. fe80::%lo0/64).
8951990d680Sitojun 		 * We do not need special treatment below for such a route.
8961990d680Sitojun 		 * Moreover, the RTF_LLINFO flag which would be set below
8971990d680Sitojun 		 * would annoy the ndp(8) command.
8981990d680Sitojun 		 */
8991990d680Sitojun 		return;
9001990d680Sitojun 	}
9011990d680Sitojun 
90274d3c214Sitojun 	switch (req) {
903a403cbd4Sozaki-r 	case RTM_ADD: {
904b9e3a5a1Sozaki-r 		struct psref psref;
905a403cbd4Sozaki-r 
9065d7712e0Sjoerg 		RT_DPRINTF("rt_getkey(rt) = %p\n", rt_getkey(rt));
90774d3c214Sitojun 		/*
90874d3c214Sitojun 		 * There is no backward compatibility :)
90974d3c214Sitojun 		 *
91074d3c214Sitojun 		 * if ((rt->rt_flags & RTF_HOST) == 0 &&
91174d3c214Sitojun 		 *     SIN(rt_mask(rt))->sin_addr.s_addr != 0xffffffff)
91274d3c214Sitojun 		 *	   rt->rt_flags |= RTF_CLONING;
91374d3c214Sitojun 		 */
91409973b35Sozaki-r 		/* XXX should move to route.c? */
91509973b35Sozaki-r 		if (rt->rt_flags & (RTF_CONNECTED | RTF_LOCAL)) {
91688399b68Sdyoung 			union {
91788399b68Sdyoung 				struct sockaddr sa;
91888399b68Sdyoung 				struct sockaddr_dl sdl;
91988399b68Sdyoung 				struct sockaddr_storage ss;
92088399b68Sdyoung 			} u;
92174d3c214Sitojun 			/*
922adb5d5afSitojun 			 * Case 1: This route should come from a route to
923adb5d5afSitojun 			 * interface (RTF_CLONING case) or the route should be
924adb5d5afSitojun 			 * treated as on-link but is currently not
925ee1bfcb3Sdyoung 			 * (RTF_LLINFO && ln == NULL case).
92674d3c214Sitojun 			 */
927c0d48690Sdyoung 			if (sockaddr_dl_init(&u.sdl, sizeof(u.ss),
92888399b68Sdyoung 			    ifp->if_index, ifp->if_type,
929c0d48690Sdyoung 			    NULL, namelen, NULL, addrlen) == NULL) {
930c0d48690Sdyoung 				printf("%s.%d: sockaddr_dl_init(, %zu, ) "
931c0d48690Sdyoung 				    "failed on %s\n", __func__, __LINE__,
932c0d48690Sdyoung 				    sizeof(u.ss), if_name(ifp));
933c0d48690Sdyoung 			}
93488399b68Sdyoung 			rt_setgate(rt, &u.sa);
935b3fc2963Sdyoung 			gate = rt->rt_gateway;
9365d7712e0Sjoerg 			RT_DPRINTF("rt_getkey(rt) = %p\n", rt_getkey(rt));
9370c74cec6Sozaki-r 			if (gate == NULL) {
9380c74cec6Sozaki-r 				log(LOG_ERR,
9390c74cec6Sozaki-r 				    "%s: rt_setgate failed on %s\n", __func__,
9400c74cec6Sozaki-r 				    if_name(ifp));
9410c74cec6Sozaki-r 				break;
9420c74cec6Sozaki-r 			}
94309973b35Sozaki-r 
9445d7712e0Sjoerg 			RT_DPRINTF("rt_getkey(rt) = %p\n", rt_getkey(rt));
94509973b35Sozaki-r 			if ((rt->rt_flags & RTF_CONNECTED) != 0)
94674d3c214Sitojun 				break;
94774d3c214Sitojun 		}
9485d7712e0Sjoerg 		RT_DPRINTF("rt_getkey(rt) = %p\n", rt_getkey(rt));
9491450d6e6Sitojun 		/*
9501450d6e6Sitojun 		 * In IPv4 code, we try to annonuce new RTF_ANNOUNCE entry here.
9511450d6e6Sitojun 		 * We don't do that here since llinfo is not ready yet.
9521450d6e6Sitojun 		 *
9531450d6e6Sitojun 		 * There are also couple of other things to be discussed:
9541450d6e6Sitojun 		 * - unsolicited NA code needs improvement beforehand
9551450d6e6Sitojun 		 * - RFC2461 says we MAY send multicast unsolicited NA
9561450d6e6Sitojun 		 *   (7.2.6 paragraph 4), however, it also says that we
9571450d6e6Sitojun 		 *   SHOULD provide a mechanism to prevent multicast NA storm.
9581450d6e6Sitojun 		 *   we don't have anything like it right now.
959e1f4f779Sitojun 		 *   note that the mechanism needs a mutual agreement
9601450d6e6Sitojun 		 *   between proxies, which means that we need to implement
961e1f4f779Sitojun 		 *   a new protocol, or a new kludge.
962e1f4f779Sitojun 		 * - from RFC2461 6.2.4, host MUST NOT send an unsolicited NA.
9631450d6e6Sitojun 		 *   we need to check ip6forwarding before sending it.
9641450d6e6Sitojun 		 *   (or should we allow proxy ND configuration only for
9651450d6e6Sitojun 		 *   routers?  there's no mention about proxy ND from hosts)
9661450d6e6Sitojun 		 */
9671450d6e6Sitojun #if 0
9681450d6e6Sitojun 		/* XXX it does not work */
96974d3c214Sitojun 		if (rt->rt_flags & RTF_ANNOUNCE)
97074d3c214Sitojun 			nd6_na_output(ifp,
97127de4861Sdyoung 			      &satocsin6(rt_getkey(rt))->sin6_addr,
97227de4861Sdyoung 			      &satocsin6(rt_getkey(rt))->sin6_addr,
97374d3c214Sitojun 			      ip6_forwarding ? ND_NA_FLAG_ROUTER : 0,
9741450d6e6Sitojun 			      1, NULL);
9751450d6e6Sitojun #endif
97609973b35Sozaki-r 
9776d8d0d63Sitojun 		if ((ifp->if_flags & (IFF_POINTOPOINT | IFF_LOOPBACK)) == 0) {
9785d7712e0Sjoerg 			RT_DPRINTF("rt_getkey(rt) = %p\n", rt_getkey(rt));
9793e342405Sitojun 			/*
9803e342405Sitojun 			 * Address resolution isn't necessary for a point to
9813e342405Sitojun 			 * point link, so we can skip this test for a p2p link.
9823e342405Sitojun 			 */
98374d3c214Sitojun 			if (gate->sa_family != AF_LINK ||
984b3fc2963Sdyoung 			    gate->sa_len <
985b3fc2963Sdyoung 			    sockaddr_dl_measure(namelen, addrlen)) {
9863e342405Sitojun 				log(LOG_DEBUG,
98722b473e0Sitojun 				    "nd6_rtrequest: bad gateway value: %s\n",
98822b473e0Sitojun 				    if_name(ifp));
98974d3c214Sitojun 				break;
99074d3c214Sitojun 			}
99127de4861Sdyoung 			satosdl(gate)->sdl_type = ifp->if_type;
99227de4861Sdyoung 			satosdl(gate)->sdl_index = ifp->if_index;
9935d7712e0Sjoerg 			RT_DPRINTF("rt_getkey(rt) = %p\n", rt_getkey(rt));
9943e342405Sitojun 		}
9955d7712e0Sjoerg 		RT_DPRINTF("rt_getkey(rt) = %p\n", rt_getkey(rt));
99674d3c214Sitojun 
997443eb0a2Schristos 		/*
998d4c71b34Sozaki-r 		 * When called from rt_ifa_addlocal, we cannot depend on that
999d4c71b34Sozaki-r 		 * the address (rt_getkey(rt)) exits in the address list of the
1000d4c71b34Sozaki-r 		 * interface. So check RTF_LOCAL instead.
1001d4c71b34Sozaki-r 		 */
1002d4c71b34Sozaki-r 		if (rt->rt_flags & RTF_LOCAL) {
1003d4c71b34Sozaki-r 			if (nd6_useloopback)
1004d4c71b34Sozaki-r 				rt->rt_ifp = lo0ifp;	/* XXX */
1005d4c71b34Sozaki-r 			break;
1006d4c71b34Sozaki-r 		}
1007d4c71b34Sozaki-r 
1008d4c71b34Sozaki-r 		/*
100908e6f222Sdyoung 		 * check if rt_getkey(rt) is an address assigned
101074d3c214Sitojun 		 * to the interface.
101174d3c214Sitojun 		 */
1012b9e3a5a1Sozaki-r 		ifa = (struct ifaddr *)in6ifa_ifpwithaddr_psref(ifp,
1013b9e3a5a1Sozaki-r 		    &satocsin6(rt_getkey(rt))->sin6_addr, &psref);
1014ee1bfcb3Sdyoung 		if (ifa != NULL) {
101574d3c214Sitojun 			if (nd6_useloopback) {
1016894d037bSozaki-r 				rt->rt_ifp = lo0ifp;	/* XXX */
101774d3c214Sitojun 				/*
101874d3c214Sitojun 				 * Make sure rt_ifa be equal to the ifaddr
101974d3c214Sitojun 				 * corresponding to the address.
102074d3c214Sitojun 				 * We need this because when we refer
102174d3c214Sitojun 				 * rt_ifa->ia6_flags in ip6_input, we assume
102274d3c214Sitojun 				 * that the rt_ifa points to the address instead
102374d3c214Sitojun 				 * of the loopback address.
102474d3c214Sitojun 				 */
1025973496efSozaki-r 				if (!ISSET(info->rti_flags, RTF_DONTCHANGEIFA)
1026973496efSozaki-r 				    && ifa != rt->rt_ifa)
1027a25eaedeSdyoung 					rt_replace_ifa(rt, ifa);
102874d3c214Sitojun 			}
10291450d6e6Sitojun 		} else if (rt->rt_flags & RTF_ANNOUNCE) {
10301450d6e6Sitojun 			/* join solicited node multicast for proxy ND */
10311450d6e6Sitojun 			if (ifp->if_flags & IFF_MULTICAST) {
10321450d6e6Sitojun 				struct in6_addr llsol;
10331450d6e6Sitojun 				int error;
10341450d6e6Sitojun 
103527de4861Sdyoung 				llsol = satocsin6(rt_getkey(rt))->sin6_addr;
103678678b13Srpaulo 				llsol.s6_addr32[0] = htonl(0xff020000);
10371450d6e6Sitojun 				llsol.s6_addr32[1] = 0;
10381450d6e6Sitojun 				llsol.s6_addr32[2] = htonl(1);
10391450d6e6Sitojun 				llsol.s6_addr8[12] = 0xff;
104078678b13Srpaulo 				if (in6_setscope(&llsol, ifp, NULL))
104109973b35Sozaki-r 					goto out;
10428c2379fdSrpaulo 				if (!in6_addmulti(&llsol, ifp, &error, 0)) {
104328f4c24cSryo 					char ip6buf[INET6_ADDRSTRLEN];
1044103bd8dfSozaki-r 					nd6log(LOG_ERR, "%s: failed to join "
10451990d680Sitojun 					    "%s (errno=%d)\n", if_name(ifp),
104635561f6bSchristos 					    IN6_PRINT(ip6buf, &llsol), error);
1047fc644273Sitojun 				}
10481450d6e6Sitojun 			}
104974d3c214Sitojun 		}
105009973b35Sozaki-r 	out:
1051b9e3a5a1Sozaki-r 		ifa_release(ifa, &psref);
1052ecd5b23eSozaki-r 		/*
1053ecd5b23eSozaki-r 		 * If we have too many cache entries, initiate immediate
1054ecd5b23eSozaki-r 		 * purging for some entries.
1055ecd5b23eSozaki-r 		 */
105609973b35Sozaki-r 		if (rt->rt_ifp != NULL)
1057ab06ed12Sozaki-r 			nd6_gc_neighbors(LLTABLE6(rt->rt_ifp), NULL);
105874d3c214Sitojun 		break;
1059a403cbd4Sozaki-r 	    }
106074d3c214Sitojun 
106174d3c214Sitojun 	case RTM_DELETE:
10621450d6e6Sitojun 		/* leave from solicited node multicast for proxy ND */
10631450d6e6Sitojun 		if ((rt->rt_flags & RTF_ANNOUNCE) != 0 &&
10641450d6e6Sitojun 		    (ifp->if_flags & IFF_MULTICAST) != 0) {
10651450d6e6Sitojun 			struct in6_addr llsol;
10661450d6e6Sitojun 
106727de4861Sdyoung 			llsol = satocsin6(rt_getkey(rt))->sin6_addr;
106878678b13Srpaulo 			llsol.s6_addr32[0] = htonl(0xff020000);
10691450d6e6Sitojun 			llsol.s6_addr32[1] = 0;
10701450d6e6Sitojun 			llsol.s6_addr32[2] = htonl(1);
10711450d6e6Sitojun 			llsol.s6_addr8[12] = 0xff;
10725e90b86bSozaki-r 			if (in6_setscope(&llsol, ifp, NULL) == 0)
10735e90b86bSozaki-r 				in6_lookup_and_delete_multi(&llsol, ifp);
10741450d6e6Sitojun 		}
1075ecd5b23eSozaki-r 		break;
107674d3c214Sitojun 	}
107774d3c214Sitojun }
107874d3c214Sitojun 
1079b05648aaSroy static void
nd6_setifflags(struct ifnet * ifp,uint32_t flags)1080b05648aaSroy nd6_setifflags(struct ifnet *ifp, uint32_t flags)
108174d3c214Sitojun {
1082b05648aaSroy 	struct nd_kifinfo *ndi = ND_IFINFO(ifp);
1083263486c9Sroy 	struct ifaddr *ifa;
1084263486c9Sroy 	struct in6_ifaddr *ia;
10853adf4b3bSozaki-r 	int s;
1086263486c9Sroy 
1087b05648aaSroy 	if (ndi->flags & ND6_IFF_IFDISABLED && !(flags & ND6_IFF_IFDISABLED)) {
1088263486c9Sroy 		/*
1089263486c9Sroy 		 * If the interface is marked as ND6_IFF_IFDISABLED and
1090263486c9Sroy 		 * has a link-local address with IN6_IFF_DUPLICATED,
1091263486c9Sroy 		 * do not clear ND6_IFF_IFDISABLED.
1092263486c9Sroy 		 * See RFC 4862, section 5.4.5.
1093263486c9Sroy 		 */
1094b05648aaSroy 		bool duplicated_linklocal = false;
1095263486c9Sroy 
1096a403cbd4Sozaki-r 		s = pserialize_read_enter();
10979e4c2bdaSozaki-r 		IFADDR_READER_FOREACH(ifa, ifp) {
1098263486c9Sroy 			if (ifa->ifa_addr->sa_family != AF_INET6)
1099263486c9Sroy 				continue;
1100263486c9Sroy 			ia = (struct in6_ifaddr *)ifa;
1101263486c9Sroy 			if ((ia->ia6_flags & IN6_IFF_DUPLICATED) &&
1102263486c9Sroy 			    IN6_IS_ADDR_LINKLOCAL(IA6_IN6(ia)))
1103263486c9Sroy 			{
1104b05648aaSroy 				duplicated_linklocal = true;
1105263486c9Sroy 				break;
1106263486c9Sroy 			}
1107263486c9Sroy 		}
1108a403cbd4Sozaki-r 		pserialize_read_exit(s);
1109263486c9Sroy 
1110263486c9Sroy 		if (duplicated_linklocal) {
1111b05648aaSroy 			flags |= ND6_IFF_IFDISABLED;
111245108122Schristos 			log(LOG_ERR, "%s: Cannot enable an interface"
1113263486c9Sroy 			    " with a link-local address marked"
111445108122Schristos 			    " duplicate.\n", if_name(ifp));
1115263486c9Sroy 		} else {
1116b05648aaSroy 			ndi->flags &= ~ND6_IFF_IFDISABLED;
1117263486c9Sroy 			if (ifp->if_flags & IFF_UP)
1118263486c9Sroy 				in6_if_up(ifp);
1119263486c9Sroy 		}
1120b05648aaSroy 	} else if (!(ndi->flags & ND6_IFF_IFDISABLED) &&
1121b05648aaSroy 	    (flags & ND6_IFF_IFDISABLED))
1122b05648aaSroy 	{
1123b05648aaSroy 		struct psref psref;
1124a403cbd4Sozaki-r 		int bound = curlwp_bind();
1125b05648aaSroy 
1126263486c9Sroy 		/* Mark all IPv6 addresses as tentative. */
1127263486c9Sroy 
1128b05648aaSroy 		ndi->flags |= ND6_IFF_IFDISABLED;
1129a403cbd4Sozaki-r 		s = pserialize_read_enter();
11309e4c2bdaSozaki-r 		IFADDR_READER_FOREACH(ifa, ifp) {
1131263486c9Sroy 			if (ifa->ifa_addr->sa_family != AF_INET6)
1132263486c9Sroy 				continue;
1133a403cbd4Sozaki-r 			ifa_acquire(ifa, &psref);
1134a403cbd4Sozaki-r 			pserialize_read_exit(s);
1135a403cbd4Sozaki-r 
1136263486c9Sroy 			nd6_dad_stop(ifa);
1137a403cbd4Sozaki-r 
1138263486c9Sroy 			ia = (struct in6_ifaddr *)ifa;
1139263486c9Sroy 			ia->ia6_flags |= IN6_IFF_TENTATIVE;
1140a403cbd4Sozaki-r 
1141a403cbd4Sozaki-r 			s = pserialize_read_enter();
1142a403cbd4Sozaki-r 			ifa_release(ifa, &psref);
1143263486c9Sroy 		}
1144a403cbd4Sozaki-r 		pserialize_read_exit(s);
1145a403cbd4Sozaki-r 		curlwp_bindx(bound);
1146263486c9Sroy 	}
1147263486c9Sroy 
1148b05648aaSroy 	if (flags & ND6_IFF_AUTO_LINKLOCAL) {
1149b05648aaSroy 		if (!(ndi->flags & ND6_IFF_AUTO_LINKLOCAL)) {
115003980252Sroy 			/* auto_linklocal 0->1 transition */
115103980252Sroy 
1152b05648aaSroy 			ndi->flags |= ND6_IFF_AUTO_LINKLOCAL;
115303980252Sroy 			in6_ifattach(ifp, NULL);
1154b05648aaSroy 		} else if (!(flags & ND6_IFF_IFDISABLED) &&
115503980252Sroy 		    ifp->if_flags & IFF_UP)
115603980252Sroy 		{
115703980252Sroy 			/*
115803980252Sroy 			 * When the IF already has
115903980252Sroy 			 * ND6_IFF_AUTO_LINKLOCAL, no link-local
116003980252Sroy 			 * address is assigned, and IFF_UP, try to
116103980252Sroy 			 * assign one.
116203980252Sroy 			 */
1163b05648aaSroy 			bool haslinklocal = 0;
116403980252Sroy 
1165a403cbd4Sozaki-r 			s = pserialize_read_enter();
11669e4c2bdaSozaki-r 			IFADDR_READER_FOREACH(ifa, ifp) {
116703980252Sroy 				if (ifa->ifa_addr->sa_family !=AF_INET6)
116803980252Sroy 					continue;
116903980252Sroy 				ia = (struct in6_ifaddr *)ifa;
117003980252Sroy 				if (IN6_IS_ADDR_LINKLOCAL(IA6_IN6(ia))){
1171b05648aaSroy 					haslinklocal = true;
117203980252Sroy 					break;
117303980252Sroy 				}
117403980252Sroy 			}
1175a403cbd4Sozaki-r 			pserialize_read_exit(s);
117603980252Sroy 			if (!haslinklocal)
117703980252Sroy 				in6_ifattach(ifp, NULL);
117803980252Sroy 		}
117903980252Sroy 	}
1180b05648aaSroy 
1181b05648aaSroy 	ndi->flags = flags;
118203980252Sroy }
1183b05648aaSroy 
1184b05648aaSroy int
nd6_ioctl(u_long cmd,void * data,struct ifnet * ifp)1185b05648aaSroy nd6_ioctl(u_long cmd, void *data, struct ifnet *ifp)
1186b05648aaSroy {
1187b05648aaSroy #ifdef OSIOCGIFINFO_IN6_90
1188b05648aaSroy 	struct in6_ndireq90 *ondi = (struct in6_ndireq90 *)data;
1189b05648aaSroy 	struct in6_ndifreq90 *ndif = (struct in6_ndifreq90 *)data;
1190b05648aaSroy #define OND	ondi->ndi
1191b05648aaSroy #endif
1192b05648aaSroy 	struct in6_ndireq *ndi = (struct in6_ndireq *)data;
1193b05648aaSroy 	struct in6_nbrinfo *nbi = (struct in6_nbrinfo *)data;
1194b05648aaSroy 	struct nd_kifinfo *ifndi = ND_IFINFO(ifp);
1195b05648aaSroy 	int error = 0;
1196b05648aaSroy #define ND     ndi->ndi
1197b05648aaSroy 
1198b05648aaSroy 	switch (cmd) {
1199b05648aaSroy #ifdef OSIOCSRTRFLUSH_IN6
1200b05648aaSroy 	case OSIOCGDRLST_IN6:		/* FALLTHROUGH */
1201b05648aaSroy 	case OSIOCGPRLST_IN6:		/* FALLTHROUGH */
1202b05648aaSroy 	case OSIOCSNDFLUSH_IN6:		/* FALLTHROUGH */
1203b05648aaSroy 	case OSIOCSPFXFLUSH_IN6:	/* FALLTHROUGH */
1204b05648aaSroy 	case OSIOCSRTRFLUSH_IN6:	/* FALLTHROUGH */
1205b05648aaSroy 		break;
1206b05648aaSroy 	case OSIOCGDEFIFACE_IN6:
1207b05648aaSroy 		ndif->ifindex = 0;
1208b05648aaSroy 		break;
1209b05648aaSroy 	case OSIOCSDEFIFACE_IN6:
1210b05648aaSroy 		error = ENOTSUP;
1211b05648aaSroy 		break;
1212b05648aaSroy #endif
1213b05648aaSroy #ifdef OSIOCGIFINFO_IN6
1214b05648aaSroy 	case OSIOCGIFINFO_IN6:		/* FALLTHROUGH */
1215b05648aaSroy #endif
1216b05648aaSroy #ifdef OSIOCGIFINFO_IN6_90
1217b05648aaSroy 	case OSIOCGIFINFO_IN6_90:
1218b05648aaSroy 		memset(&OND, 0, sizeof(OND));
1219b05648aaSroy 		OND.initialized = 1;
1220b05648aaSroy 		OND.chlim = ifndi->chlim;
1221b05648aaSroy 		OND.basereachable = ifndi->basereachable;
1222b05648aaSroy 		OND.retrans = ifndi->retrans;
1223b05648aaSroy 		OND.flags = ifndi->flags;
1224b05648aaSroy 		break;
1225b05648aaSroy 	case OSIOCSIFINFO_IN6_90:
12269f959b7cSmsaitoh 		/* Allow userland to set Neighbor Unreachability Detection
1227b05648aaSroy 		 * timers. */
1228b05648aaSroy 		if (OND.chlim != 0)
1229b05648aaSroy 			ifndi->chlim = OND.chlim;
1230b05648aaSroy 		if (OND.basereachable != 0 &&
1231b05648aaSroy 		    OND.basereachable != ifndi->basereachable)
1232b05648aaSroy 		{
1233b05648aaSroy 			ifndi->basereachable = OND.basereachable;
1234b05648aaSroy 			ifndi->reachable = ND_COMPUTE_RTIME(OND.basereachable);
1235b05648aaSroy 		}
1236b05648aaSroy 		if (OND.retrans != 0)
1237b05648aaSroy 			ifndi->retrans = OND.retrans;
1238b05648aaSroy 		/* Retain the old behaviour .... */
1239b05648aaSroy 		/* FALLTHROUGH */
1240b05648aaSroy 	case OSIOCSIFINFO_FLAGS_90:
1241b05648aaSroy 		nd6_setifflags(ifp, OND.flags);
1242b05648aaSroy 		break;
1243b05648aaSroy #undef OND
1244b05648aaSroy #endif
1245b05648aaSroy 	case SIOCGIFINFO_IN6:
1246b05648aaSroy 		ND.chlim = ifndi->chlim;
1247b05648aaSroy 		ND.basereachable = ifndi->basereachable;
1248b05648aaSroy 		ND.retrans = ifndi->retrans;
1249b05648aaSroy 		ND.flags = ifndi->flags;
1250b05648aaSroy 		break;
1251b05648aaSroy 	case SIOCSIFINFO_IN6:
12529f959b7cSmsaitoh 		/* Allow userland to set Neighbor Unreachability Detection
1253b05648aaSroy 		 * timers. */
1254b05648aaSroy 		if (ND.chlim != 0)
1255b05648aaSroy 			ifndi->chlim = ND.chlim;
1256b05648aaSroy 		if (ND.basereachable != 0 &&
1257b05648aaSroy 		    ND.basereachable != ifndi->basereachable)
1258b05648aaSroy 		{
1259b05648aaSroy 			ifndi->basereachable = ND.basereachable;
1260b05648aaSroy 			ifndi->reachable = ND_COMPUTE_RTIME(ND.basereachable);
1261b05648aaSroy 		}
1262b05648aaSroy 		if (ND.retrans != 0)
1263b05648aaSroy 			ifndi->retrans = ND.retrans;
1264b05648aaSroy 		break;
1265b05648aaSroy 	case SIOCSIFINFO_FLAGS:
1266b05648aaSroy 		nd6_setifflags(ifp, ND.flags);
12673e342405Sitojun 		break;
12688c2379fdSrpaulo #undef ND
126974d3c214Sitojun 	case SIOCGNBRINFO_IN6:
127074d3c214Sitojun 	{
1271ecd5b23eSozaki-r 		struct llentry *ln;
1272ea861f01Sitojun 		struct in6_addr nb_addr = nbi->addr; /* make local for safety */
127374d3c214Sitojun 
127478678b13Srpaulo 		if ((error = in6_setscope(&nb_addr, ifp, NULL)) != 0)
1275833cc399Sdyoung 			return error;
1276ea861f01Sitojun 
127709973b35Sozaki-r 		ln = nd6_lookup(&nb_addr, ifp, false);
12789eae87d0Sozaki-r 		if (ln == NULL) {
127974d3c214Sitojun 			error = EINVAL;
128074d3c214Sitojun 			break;
128174d3c214Sitojun 		}
128274d3c214Sitojun 		nbi->state = ln->ln_state;
128374d3c214Sitojun 		nbi->asked = ln->ln_asked;
128474d3c214Sitojun 		nbi->isrouter = ln->ln_router;
128555140c19Sozaki-r 		nbi->expire = ln->ln_expire ?
128655140c19Sozaki-r 		    time_mono_to_wall(ln->ln_expire) : 0;
128709973b35Sozaki-r 		LLE_RUNLOCK(ln);
128874d3c214Sitojun 
128974d3c214Sitojun 		break;
129074d3c214Sitojun 	}
129174d3c214Sitojun 	}
1292833cc399Sdyoung 	return error;
129374d3c214Sitojun }
129474d3c214Sitojun 
12951db31a59Sdyoung void
nd6_llinfo_release_pkts(struct llentry * ln,struct ifnet * ifp)129609973b35Sozaki-r nd6_llinfo_release_pkts(struct llentry *ln, struct ifnet *ifp)
12971db31a59Sdyoung {
12981db31a59Sdyoung 	struct mbuf *m_hold, *m_hold_next;
129909973b35Sozaki-r 	struct sockaddr_in6 sin6;
13001db31a59Sdyoung 
130109973b35Sozaki-r 	LLE_WLOCK_ASSERT(ln);
130209973b35Sozaki-r 
130309973b35Sozaki-r 	sockaddr_in6_init(&sin6, &ln->r_l3addr.addr6, 0, 0, 0);
130409973b35Sozaki-r 
130509973b35Sozaki-r 	m_hold = ln->la_hold, ln->la_hold = NULL, ln->la_numheld = 0;
130609973b35Sozaki-r 
1307de6f3b09Sozaki-r 	LLE_ADDREF(ln);
130809973b35Sozaki-r 	LLE_WUNLOCK(ln);
130909973b35Sozaki-r 	for (; m_hold != NULL; m_hold = m_hold_next) {
13101db31a59Sdyoung 		m_hold_next = m_hold->m_nextpkt;
13111db31a59Sdyoung 		m_hold->m_nextpkt = NULL;
13121db31a59Sdyoung 
13131db31a59Sdyoung 		/*
13141db31a59Sdyoung 		 * we assume ifp is not a p2p here, so
13151db31a59Sdyoung 		 * just set the 2nd argument as the
13161db31a59Sdyoung 		 * 1st one.
13171db31a59Sdyoung 		 */
13183f909d17Sozaki-r 		ip6_if_output(ifp, ifp, m_hold, &sin6, NULL);
13191db31a59Sdyoung 	}
132009973b35Sozaki-r 	LLE_WLOCK(ln);
1321de6f3b09Sozaki-r 	LLE_REMREF(ln);
13221db31a59Sdyoung }
13231db31a59Sdyoung 
132474d3c214Sitojun /*
132574d3c214Sitojun  * Create neighbor cache entry and cache link-layer address,
132674d3c214Sitojun  * on reception of inbound ND6 packets.  (RS/RA/NS/redirect)
132774d3c214Sitojun  */
13289eae87d0Sozaki-r void
nd6_cache_lladdr(struct ifnet * ifp,struct in6_addr * from,char * lladdr,int lladdrlen,int type,int code)13294d595fd7Schristos nd6_cache_lladdr(
13304d595fd7Schristos     struct ifnet *ifp,
13314d595fd7Schristos     struct in6_addr *from,
13324d595fd7Schristos     char *lladdr,
1333168cd830Schristos     int lladdrlen,
13344d595fd7Schristos     int type,	/* ICMP6 type */
13354d595fd7Schristos     int code	/* type dependent information */
13364d595fd7Schristos )
133774d3c214Sitojun {
1338ecd5b23eSozaki-r 	struct llentry *ln = NULL;
133974d3c214Sitojun 	int is_newentry;
134074d3c214Sitojun 	int do_update;
134174d3c214Sitojun 	int olladdr;
134274d3c214Sitojun 	int llchange;
134374d3c214Sitojun 	int newstate = 0;
134474d3c214Sitojun 
1345452d01ddSozaki-r 	KASSERT(ifp != NULL);
1346452d01ddSozaki-r 	KASSERT(from != NULL);
134774d3c214Sitojun 
134874d3c214Sitojun 	/* nothing must be updated for unspecified address */
134974d3c214Sitojun 	if (IN6_IS_ADDR_UNSPECIFIED(from))
13509eae87d0Sozaki-r 		return;
135174d3c214Sitojun 
135274d3c214Sitojun 	/*
135374d3c214Sitojun 	 * Validation about ifp->if_addrlen and lladdrlen must be done in
135474d3c214Sitojun 	 * the caller.
135574d3c214Sitojun 	 *
135674d3c214Sitojun 	 * XXX If the link does not have link-layer adderss, what should
135774d3c214Sitojun 	 * we do? (ifp->if_addrlen == 0)
135874d3c214Sitojun 	 * Spec says nothing in sections for RA, RS and NA.  There's small
135974d3c214Sitojun 	 * description on it in NS section (RFC 2461 7.2.3).
136074d3c214Sitojun 	 */
136174d3c214Sitojun 
136209973b35Sozaki-r 	ln = nd6_lookup(from, ifp, true);
136309973b35Sozaki-r 	if (ln == NULL) {
136474d3c214Sitojun #if 0
136574d3c214Sitojun 		/* nothing must be done if there's no lladdr */
136674d3c214Sitojun 		if (!lladdr || !lladdrlen)
136774d3c214Sitojun 			return NULL;
136874d3c214Sitojun #endif
136974d3c214Sitojun 
137009973b35Sozaki-r 		ln = nd6_create(from, ifp);
137174d3c214Sitojun 		is_newentry = 1;
137227a0af58Sitojun 	} else {
137327a0af58Sitojun 		/* do nothing if static ndp is set */
137409973b35Sozaki-r 		if (ln->la_flags & LLE_STATIC) {
137509973b35Sozaki-r 			LLE_WUNLOCK(ln);
13769eae87d0Sozaki-r 			return;
13779eae87d0Sozaki-r 		}
137874d3c214Sitojun 		is_newentry = 0;
137927a0af58Sitojun 	}
138074d3c214Sitojun 
13818c2379fdSrpaulo 	if (ln == NULL)
138209973b35Sozaki-r 		return;
138374d3c214Sitojun 
138409973b35Sozaki-r 	olladdr = (ln->la_flags & LLE_VALID) ? 1 : 0;
138574d3c214Sitojun 	if (olladdr && lladdr) {
138609973b35Sozaki-r 		llchange = memcmp(lladdr, &ln->ll_addr, ifp->if_addrlen);
138774d3c214Sitojun 	} else
138874d3c214Sitojun 		llchange = 0;
138974d3c214Sitojun 
139074d3c214Sitojun 	/*
139174d3c214Sitojun 	 * newentry olladdr  lladdr  llchange	(*=record)
139274d3c214Sitojun 	 *	0	n	n	--	(1)
139374d3c214Sitojun 	 *	0	y	n	--	(2)
139474d3c214Sitojun 	 *	0	n	y	--	(3) * STALE
139574d3c214Sitojun 	 *	0	y	y	n	(4) *
139674d3c214Sitojun 	 *	0	y	y	y	(5) * STALE
139774d3c214Sitojun 	 *	1	--	n	--	(6)   NOSTATE(= PASSIVE)
139874d3c214Sitojun 	 *	1	--	y	--	(7) * STALE
139974d3c214Sitojun 	 */
140074d3c214Sitojun 
140174d3c214Sitojun 	if (lladdr) {		/* (3-5) and (7) */
140274d3c214Sitojun 		/*
140374d3c214Sitojun 		 * Record source link-layer address
140474d3c214Sitojun 		 * XXX is it dependent to ifp->if_type?
140574d3c214Sitojun 		 */
140609973b35Sozaki-r 		memcpy(&ln->ll_addr, lladdr, ifp->if_addrlen);
140709973b35Sozaki-r 		ln->la_flags |= LLE_VALID;
140874d3c214Sitojun 	}
140974d3c214Sitojun 
141074d3c214Sitojun 	if (!is_newentry) {
14116d8d0d63Sitojun 		if ((!olladdr && lladdr) ||		/* (3) */
14126d8d0d63Sitojun 		    (olladdr && lladdr && llchange)) {	/* (5) */
141374d3c214Sitojun 			do_update = 1;
1414f456857bSroy 			newstate = ND_LLINFO_STALE;
141574d3c214Sitojun 		} else					/* (1-2,4) */
141674d3c214Sitojun 			do_update = 0;
141774d3c214Sitojun 	} else {
141874d3c214Sitojun 		do_update = 1;
14198c2379fdSrpaulo 		if (lladdr == NULL)			/* (6) */
1420f456857bSroy 			newstate = ND_LLINFO_NOSTATE;
142174d3c214Sitojun 		else					/* (7) */
1422f456857bSroy 			newstate = ND_LLINFO_STALE;
142374d3c214Sitojun 	}
142474d3c214Sitojun 
142574d3c214Sitojun 	if (do_update) {
142674d3c214Sitojun 		/*
142774d3c214Sitojun 		 * Update the state of the neighbor cache.
142874d3c214Sitojun 		 */
142974d3c214Sitojun 		ln->ln_state = newstate;
143074d3c214Sitojun 
1431f456857bSroy 		if (ln->ln_state == ND_LLINFO_STALE) {
14323e898c92Sitojun 			/*
14333e898c92Sitojun 			 * XXX: since nd6_output() below will cause
14343e898c92Sitojun 			 * state tansition to DELAY and reset the timer,
14353e898c92Sitojun 			 * we must set the timer now, although it is actually
14363e898c92Sitojun 			 * meaningless.
14373e898c92Sitojun 			 */
1438f456857bSroy 			nd_set_timer(ln, ND_TIMER_GC);
14393e898c92Sitojun 
144009973b35Sozaki-r 			nd6_llinfo_release_pkts(ln, ifp);
1441f456857bSroy 		} else if (ln->ln_state == ND_LLINFO_INCOMPLETE) {
144274d3c214Sitojun 			/* probe right away */
1443f456857bSroy 			nd_set_timer(ln, ND_TIMER_IMMEDIATE);
144474d3c214Sitojun 		}
144574d3c214Sitojun 	}
144674d3c214Sitojun 
144774d3c214Sitojun 	/*
144874d3c214Sitojun 	 * ICMP6 type dependent behavior.
144974d3c214Sitojun 	 *
145074d3c214Sitojun 	 * NS: clear IsRouter if new entry
145174d3c214Sitojun 	 * RS: clear IsRouter
145274d3c214Sitojun 	 * RA: set IsRouter if there's lladdr
145374d3c214Sitojun 	 * redir: clear IsRouter if new entry
145474d3c214Sitojun 	 *
145574d3c214Sitojun 	 * RA case, (1):
145674d3c214Sitojun 	 * The spec says that we must set IsRouter in the following cases:
145774d3c214Sitojun 	 * - If lladdr exist, set IsRouter.  This means (1-5).
145874d3c214Sitojun 	 * - If it is old entry (!newentry), set IsRouter.  This means (7).
145974d3c214Sitojun 	 * So, based on the spec, in (1-5) and (7) cases we must set IsRouter.
1460016e2181Sandvar 	 * A question arises for (1) case.  (1) case has no lladdr in the
146174d3c214Sitojun 	 * neighbor cache, this is similar to (6).
146274d3c214Sitojun 	 * This case is rare but we figured that we MUST NOT set IsRouter.
146374d3c214Sitojun 	 *
146474d3c214Sitojun 	 * newentry olladdr  lladdr  llchange	    NS  RS  RA	redir
146570ada095Sitojun 	 *							D R
146670ada095Sitojun 	 *	0	n	n	--	(1)	c   ?     s
146770ada095Sitojun 	 *	0	y	n	--	(2)	c   s     s
146870ada095Sitojun 	 *	0	n	y	--	(3)	c   s     s
146970ada095Sitojun 	 *	0	y	y	n	(4)	c   s     s
147070ada095Sitojun 	 *	0	y	y	y	(5)	c   s     s
147170ada095Sitojun 	 *	1	--	n	--	(6) c	c 	c s
147270ada095Sitojun 	 *	1	--	y	--	(7) c	c   s	c s
147374d3c214Sitojun 	 *
147474d3c214Sitojun 	 *					(c=clear s=set)
147574d3c214Sitojun 	 */
147674d3c214Sitojun 	switch (type & 0xff) {
147774d3c214Sitojun 	case ND_NEIGHBOR_SOLICIT:
147874d3c214Sitojun 		/*
147974d3c214Sitojun 		 * New entry must have is_router flag cleared.
148074d3c214Sitojun 		 */
148174d3c214Sitojun 		if (is_newentry)	/* (6-7) */
148274d3c214Sitojun 			ln->ln_router = 0;
148374d3c214Sitojun 		break;
148470ada095Sitojun 	case ND_REDIRECT:
148570ada095Sitojun 		/*
148670ada095Sitojun 		 * If the icmp is a redirect to a better router, always set the
148770ada095Sitojun 		 * is_router flag.  Otherwise, if the entry is newly created,
148870ada095Sitojun 		 * clear the flag.  [RFC 2461, sec 8.3]
148970ada095Sitojun 		 */
149070ada095Sitojun 		if (code == ND_REDIRECT_ROUTER)
149170ada095Sitojun 			ln->ln_router = 1;
149270ada095Sitojun 		else if (is_newentry) /* (6-7) */
149370ada095Sitojun 			ln->ln_router = 0;
149470ada095Sitojun 		break;
149574d3c214Sitojun 	case ND_ROUTER_SOLICIT:
149674d3c214Sitojun 		/*
149774d3c214Sitojun 		 * is_router flag must always be cleared.
149874d3c214Sitojun 		 */
149974d3c214Sitojun 		ln->ln_router = 0;
150074d3c214Sitojun 		break;
150174d3c214Sitojun 	case ND_ROUTER_ADVERT:
150274d3c214Sitojun 		/*
150374d3c214Sitojun 		 * Mark an entry with lladdr as a router.
150474d3c214Sitojun 		 */
15056d8d0d63Sitojun 		if ((!is_newentry && (olladdr || lladdr)) ||	/* (2-5) */
15066d8d0d63Sitojun 		    (is_newentry && lladdr)) {			/* (7) */
150774d3c214Sitojun 			ln->ln_router = 1;
150874d3c214Sitojun 		}
150974d3c214Sitojun 		break;
151074d3c214Sitojun 	}
151174d3c214Sitojun 
1512ca19b576Sroy 	if (do_update && lladdr != NULL) {
1513ece8d087Sroy 		struct sockaddr_in6 sin6;
1514ece8d087Sroy 
1515ece8d087Sroy 		sockaddr_in6_init(&sin6, from, 0, 0, 0);
1516ece8d087Sroy 		rt_clonedmsg(is_newentry ? RTM_ADD : RTM_CHANGE,
15175ff17943Sroy 		    NULL, sin6tosa(&sin6), lladdr, ifp);
1518ece8d087Sroy 	}
151909973b35Sozaki-r 
1520b05648aaSroy 	if (ln != NULL)
152109973b35Sozaki-r 		LLE_WUNLOCK(ln);
152209973b35Sozaki-r 
152309973b35Sozaki-r 	/*
152409973b35Sozaki-r 	 * If we have too many cache entries, initiate immediate
152509973b35Sozaki-r 	 * purging for some entries.
152609973b35Sozaki-r 	 */
152709973b35Sozaki-r 	if (is_newentry)
1528ab06ed12Sozaki-r 		nd6_gc_neighbors(LLTABLE6(ifp), &ln->r_l3addr.addr6);
152974d3c214Sitojun }
153074d3c214Sitojun 
153174d3c214Sitojun static void
nd6_slowtimo(void * ignored_arg)1532168cd830Schristos nd6_slowtimo(void *ignored_arg)
153374d3c214Sitojun {
1534b05648aaSroy 	struct nd_kifinfo *ndi;
15355c1df51dSitojun 	struct ifnet *ifp;
15367295f80bSriastradh 	struct psref psref;
1537040205aeSozaki-r 	int s;
153874d3c214Sitojun 
1539cead3b88Sozaki-r 	SOFTNET_KERNEL_LOCK_UNLESS_NET_MPSAFE();
1540fc96443dSthorpej 	callout_reset(&nd6_slowtimo_ch, ND6_SLOWTIMER_INTERVAL * hz,
1541fc96443dSthorpej 	    nd6_slowtimo, NULL);
1542040205aeSozaki-r 
1543040205aeSozaki-r 	s = pserialize_read_enter();
1544040205aeSozaki-r 	IFNET_READER_FOREACH(ifp) {
1545b05648aaSroy 		ndi = ND_IFINFO(ifp);
1546b05648aaSroy 		if (ndi->basereachable && /* already initialized */
1547b05648aaSroy 		    (ndi->recalctm -= ND6_SLOWTIMER_INTERVAL) <= 0) {
15487295f80bSriastradh 			if_acquire(ifp, &psref);
15497295f80bSriastradh 			pserialize_read_exit(s);
155074d3c214Sitojun 			/*
155174d3c214Sitojun 			 * Since reachable time rarely changes by router
155274d3c214Sitojun 			 * advertisements, we SHOULD insure that a new random
155374d3c214Sitojun 			 * value gets recomputed at least once every few hours.
155474d3c214Sitojun 			 * (RFC 2461, 6.3.4)
155574d3c214Sitojun 			 */
1556b05648aaSroy 			ndi->recalctm = nd6_recalc_reachtm_interval;
1557b05648aaSroy 			ndi->reachable = ND_COMPUTE_RTIME(ndi->basereachable);
15587295f80bSriastradh 			s = pserialize_read_enter();
15597295f80bSriastradh 			if_release(ifp, &psref);
156074d3c214Sitojun 		}
156174d3c214Sitojun 	}
1562040205aeSozaki-r 	pserialize_read_exit(s);
1563040205aeSozaki-r 
1564cead3b88Sozaki-r 	SOFTNET_KERNEL_UNLOCK_UNLESS_NET_MPSAFE();
156574d3c214Sitojun }
156674d3c214Sitojun 
15673f909d17Sozaki-r /*
15683f909d17Sozaki-r  * Return 0 if a neighbor cache is found. Return EWOULDBLOCK if a cache is not
15693f909d17Sozaki-r  * found and trying to resolve a neighbor; in this case the mbuf is queued in
15703f909d17Sozaki-r  * the list. Otherwise return errno after freeing the mbuf.
15713f909d17Sozaki-r  */
15729e0f6c5eSozaki-r int
nd6_resolve(struct ifnet * ifp,const struct rtentry * rt,struct mbuf * m,const struct sockaddr * _dst,uint8_t * lldst,size_t dstsize)15733f909d17Sozaki-r nd6_resolve(struct ifnet *ifp, const struct rtentry *rt, struct mbuf *m,
15743f909d17Sozaki-r     const struct sockaddr *_dst, uint8_t *lldst, size_t dstsize)
1575c325d0caSozaki-r {
15769e0f6c5eSozaki-r 	struct llentry *ln = NULL;
15779e0f6c5eSozaki-r 	bool created = false;
15783f909d17Sozaki-r 	const struct sockaddr_in6 *dst = satocsin6(_dst);
1579ca7497cbSroy 	int error;
1580b05648aaSroy 	struct nd_kifinfo *ndi = ND_IFINFO(ifp);
15819e0f6c5eSozaki-r 
15823f909d17Sozaki-r 	/* discard the packet if IPv6 operation is disabled on the interface */
1583b05648aaSroy 	if (ndi->flags & ND6_IFF_IFDISABLED) {
15849e0f6c5eSozaki-r 		m_freem(m);
15853f909d17Sozaki-r 		return ENETDOWN; /* better error? */
15869e0f6c5eSozaki-r 	}
158730a93491Sozaki-r 
158874d3c214Sitojun 	/*
158974d3c214Sitojun 	 * Address resolution or Neighbor Unreachability Detection
159074d3c214Sitojun 	 * for the next hop.
159174d3c214Sitojun 	 * At this point, the destination of the packet must be a unicast
159274d3c214Sitojun 	 * or an anycast address(i.e. not a multicast).
159374d3c214Sitojun 	 */
159474d3c214Sitojun 
159574d3c214Sitojun 	/* Look up the neighbor cache for the nexthop */
15963f909d17Sozaki-r 	ln = nd6_lookup(&dst->sin6_addr, ifp, false);
15973f909d17Sozaki-r 
15980ec6d93dSozaki-r 	if (ln != NULL && (ln->la_flags & LLE_VALID) != 0 &&
1599f456857bSroy 	    ln->ln_state == ND_LLINFO_REACHABLE) {
16003f909d17Sozaki-r 		/* Fast path */
16013f909d17Sozaki-r 		memcpy(lldst, &ln->ll_addr, MIN(dstsize, ifp->if_addrlen));
16023f909d17Sozaki-r 		LLE_RUNLOCK(ln);
16033f909d17Sozaki-r 		return 0;
16043f909d17Sozaki-r 	}
16053f909d17Sozaki-r 	if (ln != NULL)
16063f909d17Sozaki-r 		LLE_RUNLOCK(ln);
16073f909d17Sozaki-r 
16083f909d17Sozaki-r 	/* Slow path */
160909973b35Sozaki-r 	ln = nd6_lookup(&dst->sin6_addr, ifp, true);
16103f909d17Sozaki-r 	if (ln == NULL && nd6_is_addr_neighbor(dst, ifp))  {
16114f1c7f0bSitojun 		/*
16124f1c7f0bSitojun 		 * Since nd6_is_addr_neighbor() internally calls nd6_lookup(),
16134f1c7f0bSitojun 		 * the condition below is not very efficient.  But we believe
16144f1c7f0bSitojun 		 * it is tolerable, because this should be a rare case.
16154f1c7f0bSitojun 		 */
161609973b35Sozaki-r 		ln = nd6_create(&dst->sin6_addr, ifp);
161709973b35Sozaki-r 		if (ln == NULL) {
161828f4c24cSryo 			char ip6buf[INET6_ADDRSTRLEN];
16194f1c7f0bSitojun 			log(LOG_DEBUG,
16203f909d17Sozaki-r 			    "%s: can't allocate llinfo for %s "
16213f909d17Sozaki-r 			    "(ln=%p, rt=%p)\n", __func__,
162235561f6bSchristos 			    IN6_PRINT(ip6buf, &dst->sin6_addr), ln, rt);
16233f909d17Sozaki-r 			m_freem(m);
16243f909d17Sozaki-r 			return ENOBUFS;
162574d3c214Sitojun 		}
16263f909d17Sozaki-r 		created = true;
16274f1c7f0bSitojun 	}
16284f1c7f0bSitojun 
16296bf0e671Sozaki-r 	if (ln == NULL) {
16306bf0e671Sozaki-r 		m_freem(m);
16316bf0e671Sozaki-r 		return ENETDOWN; /* better error? */
16326bf0e671Sozaki-r 	}
16336bf0e671Sozaki-r 
1634f456857bSroy 	error = nd_resolve(ln, rt, m, lldst, dstsize);
163509973b35Sozaki-r 
163609973b35Sozaki-r 	if (created)
1637ab06ed12Sozaki-r 		nd6_gc_neighbors(LLTABLE6(ifp), &dst->sin6_addr);
16389eae87d0Sozaki-r 
1639ca7497cbSroy 	return error;
164009973b35Sozaki-r }
164174d3c214Sitojun 
164274d3c214Sitojun int
nd6_need_cache(struct ifnet * ifp)1643833cc399Sdyoung nd6_need_cache(struct ifnet *ifp)
16441990d680Sitojun {
16451990d680Sitojun 	/*
16461990d680Sitojun 	 * XXX: we currently do not make neighbor cache on any interface
16474541d682Sthorpej 	 * other than ARCnet, Ethernet, and GIF.
16481990d680Sitojun 	 *
16491990d680Sitojun 	 * RFC2893 says:
16501990d680Sitojun 	 * - unidirectional tunnels needs no ND
16511990d680Sitojun 	 */
16521990d680Sitojun 	switch (ifp->if_type) {
16531990d680Sitojun 	case IFT_ARCNET:
16541990d680Sitojun 	case IFT_ETHER:
16551990d680Sitojun 	case IFT_IEEE1394:
16564876c304Sliamjfoy 	case IFT_CARP:
16571990d680Sitojun 	case IFT_GIF:		/* XXX need more cases? */
1658*5a9ce3a7Sknakahara 	case IFT_IPSEC:
16598c2379fdSrpaulo 	case IFT_PPP:
16608c2379fdSrpaulo 	case IFT_TUNNEL:
1661833cc399Sdyoung 		return 1;
16621990d680Sitojun 	default:
1663833cc399Sdyoung 		return 0;
16641990d680Sitojun 	}
16651990d680Sitojun }
16661990d680Sitojun 
16676d8d0d63Sitojun int
nd6_sysctl(int name,void * oldp,size_t * oldlenp,void * newp,size_t newlen)16684d595fd7Schristos nd6_sysctl(
16694d595fd7Schristos     int name,
16704d595fd7Schristos     void *oldp,	/* syscall arg, need copyout */
16714d595fd7Schristos     size_t *oldlenp,
16724d595fd7Schristos     void *newp,	/* syscall arg, need copyin */
1673168cd830Schristos     size_t newlen
16744d595fd7Schristos )
16756d8d0d63Sitojun {
16764be362dbSpgoyette 	int error;
16776d8d0d63Sitojun 
16786d8d0d63Sitojun 	if (newp)
16796d8d0d63Sitojun 		return EPERM;
16803bf8ebadSpgoyette 
16816d8d0d63Sitojun 	switch (name) {
16824be362dbSpgoyette 
16834be362dbSpgoyette /* call the nd6 compat_90 hook to validate the nd6-related names */
1684b05648aaSroy 	case OICMPV6CTL_ND6_DRLIST: /* FALLTHROUGH */
1685b05648aaSroy 	case OICMPV6CTL_ND6_PRLIST:
16864be362dbSpgoyette 		MODULE_HOOK_CALL(net_inet6_nd_90_hook, (name), ENOPROTOOPT,
16874be362dbSpgoyette 		    error);
16884be362dbSpgoyette 		if (error == 0)
1689b05648aaSroy 			*oldlenp = 0;
16904be362dbSpgoyette 		return error;
16914be362dbSpgoyette 
16928c2379fdSrpaulo 	case ICMPV6CTL_ND6_MAXQLEN:
1693ce7078edSpgoyette 		return 0;
16946d8d0d63Sitojun 	default:
1695ce7078edSpgoyette 		return ENOPROTOOPT;
16966d8d0d63Sitojun 	}
1697091c448cSozaki-r }
1698