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