xref: /freebsd/sys/netinet6/nd6_rtr.c (revision 82cd038d51e2fa970be77f4d59c56d3452fedec0)
182cd038dSYoshinobu Inoue /*
282cd038dSYoshinobu Inoue  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
382cd038dSYoshinobu Inoue  * All rights reserved.
482cd038dSYoshinobu Inoue  *
582cd038dSYoshinobu Inoue  * Redistribution and use in source and binary forms, with or without
682cd038dSYoshinobu Inoue  * modification, are permitted provided that the following conditions
782cd038dSYoshinobu Inoue  * are met:
882cd038dSYoshinobu Inoue  * 1. Redistributions of source code must retain the above copyright
982cd038dSYoshinobu Inoue  *    notice, this list of conditions and the following disclaimer.
1082cd038dSYoshinobu Inoue  * 2. Redistributions in binary form must reproduce the above copyright
1182cd038dSYoshinobu Inoue  *    notice, this list of conditions and the following disclaimer in the
1282cd038dSYoshinobu Inoue  *    documentation and/or other materials provided with the distribution.
1382cd038dSYoshinobu Inoue  * 3. Neither the name of the project nor the names of its contributors
1482cd038dSYoshinobu Inoue  *    may be used to endorse or promote products derived from this software
1582cd038dSYoshinobu Inoue  *    without specific prior written permission.
1682cd038dSYoshinobu Inoue  *
1782cd038dSYoshinobu Inoue  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
1882cd038dSYoshinobu Inoue  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1982cd038dSYoshinobu Inoue  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2082cd038dSYoshinobu Inoue  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2182cd038dSYoshinobu Inoue  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2282cd038dSYoshinobu Inoue  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2382cd038dSYoshinobu Inoue  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2482cd038dSYoshinobu Inoue  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2582cd038dSYoshinobu Inoue  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2682cd038dSYoshinobu Inoue  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2782cd038dSYoshinobu Inoue  * SUCH DAMAGE.
2882cd038dSYoshinobu Inoue  *
2982cd038dSYoshinobu Inoue  * $FreeBSD$
3082cd038dSYoshinobu Inoue  */
3182cd038dSYoshinobu Inoue 
3282cd038dSYoshinobu Inoue #include <sys/param.h>
3382cd038dSYoshinobu Inoue #include <sys/systm.h>
3482cd038dSYoshinobu Inoue #include <sys/malloc.h>
3582cd038dSYoshinobu Inoue #include <sys/mbuf.h>
3682cd038dSYoshinobu Inoue #include <sys/socket.h>
3782cd038dSYoshinobu Inoue #include <sys/sockio.h>
3882cd038dSYoshinobu Inoue #include <sys/time.h>
3982cd038dSYoshinobu Inoue #include <sys/kernel.h>
4082cd038dSYoshinobu Inoue #include <sys/errno.h>
4182cd038dSYoshinobu Inoue #include <sys/syslog.h>
4282cd038dSYoshinobu Inoue 
4382cd038dSYoshinobu Inoue #include <net/if.h>
4482cd038dSYoshinobu Inoue #include <net/if_types.h>
4582cd038dSYoshinobu Inoue #include <net/if_dl.h>
4682cd038dSYoshinobu Inoue #include <net/route.h>
4782cd038dSYoshinobu Inoue #include <net/radix.h>
4882cd038dSYoshinobu Inoue 
4982cd038dSYoshinobu Inoue #include <netinet/in.h>
5082cd038dSYoshinobu Inoue #include <netinet6/in6_var.h>
5182cd038dSYoshinobu Inoue #include <netinet6/ip6.h>
5282cd038dSYoshinobu Inoue #include <netinet6/ip6_var.h>
5382cd038dSYoshinobu Inoue #include <netinet6/nd6.h>
5482cd038dSYoshinobu Inoue #include <netinet6/icmp6.h>
5582cd038dSYoshinobu Inoue 
5682cd038dSYoshinobu Inoue #include <net/net_osdep.h>
5782cd038dSYoshinobu Inoue 
5882cd038dSYoshinobu Inoue #define	SDL(s)	((struct sockaddr_dl *)s)
5982cd038dSYoshinobu Inoue 
6082cd038dSYoshinobu Inoue static struct	nd_defrouter *defrtrlist_update __P((struct nd_defrouter *));
6182cd038dSYoshinobu Inoue static int	prelist_add __P((struct nd_prefix *, struct nd_defrouter *));
6282cd038dSYoshinobu Inoue static struct	nd_prefix *prefix_lookup __P((struct nd_prefix *));
6382cd038dSYoshinobu Inoue static struct	in6_ifaddr *in6_ifadd __P((struct ifnet *, struct in6_addr *,
6482cd038dSYoshinobu Inoue 					   struct in6_addr *, int));
6582cd038dSYoshinobu Inoue static struct	nd_pfxrouter *pfxrtr_lookup __P((struct nd_prefix *,
6682cd038dSYoshinobu Inoue 						 struct nd_defrouter *));
6782cd038dSYoshinobu Inoue static void	pfxrtr_add __P((struct nd_prefix *, struct nd_defrouter *));
6882cd038dSYoshinobu Inoue static void	pfxrtr_del __P((struct nd_pfxrouter *));
6982cd038dSYoshinobu Inoue static void	pfxlist_onlink_check __P((void));
7082cd038dSYoshinobu Inoue static void	nd6_detach_prefix __P((struct nd_prefix *));
7182cd038dSYoshinobu Inoue static void	nd6_attach_prefix __P((struct nd_prefix *));
7282cd038dSYoshinobu Inoue 
7382cd038dSYoshinobu Inoue static void	in6_init_address_ltimes __P((struct nd_prefix *ndpr,
7482cd038dSYoshinobu Inoue 					     struct in6_addrlifetime *lt6));
7582cd038dSYoshinobu Inoue 
7682cd038dSYoshinobu Inoue static int	rt6_deleteroute __P((struct radix_node *, void *));
7782cd038dSYoshinobu Inoue 
7882cd038dSYoshinobu Inoue extern int	nd6_recalc_reachtm_interval;
7982cd038dSYoshinobu Inoue 
8082cd038dSYoshinobu Inoue /*
8182cd038dSYoshinobu Inoue  * Receive Router Solicitation Message - just for routers.
8282cd038dSYoshinobu Inoue  * Router solicitation/advertisement is mostly managed by userland program
8382cd038dSYoshinobu Inoue  * (rtadvd) so here we have no function like nd6_ra_output().
8482cd038dSYoshinobu Inoue  *
8582cd038dSYoshinobu Inoue  * Based on RFC 2461
8682cd038dSYoshinobu Inoue  */
8782cd038dSYoshinobu Inoue void
8882cd038dSYoshinobu Inoue nd6_rs_input(m, off, icmp6len)
8982cd038dSYoshinobu Inoue 	struct	mbuf *m;
9082cd038dSYoshinobu Inoue 	int off, icmp6len;
9182cd038dSYoshinobu Inoue {
9282cd038dSYoshinobu Inoue 	struct ifnet *ifp = m->m_pkthdr.rcvif;
9382cd038dSYoshinobu Inoue 	struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
9482cd038dSYoshinobu Inoue 	struct nd_router_solicit *nd_rs
9582cd038dSYoshinobu Inoue 		= (struct nd_router_solicit *)((caddr_t)ip6 + off);
9682cd038dSYoshinobu Inoue 	struct in6_addr saddr6 = ip6->ip6_src;
9782cd038dSYoshinobu Inoue 	char *lladdr = NULL;
9882cd038dSYoshinobu Inoue 	int lladdrlen = 0;
9982cd038dSYoshinobu Inoue 	union nd_opts ndopts;
10082cd038dSYoshinobu Inoue 
10182cd038dSYoshinobu Inoue 	/* If I'm not a router, ignore it. */
10282cd038dSYoshinobu Inoue 	if (ip6_accept_rtadv != 0 || ip6_forwarding != 1)
10382cd038dSYoshinobu Inoue 		return;
10482cd038dSYoshinobu Inoue 
10582cd038dSYoshinobu Inoue 	/* Sanity checks */
10682cd038dSYoshinobu Inoue 	if (ip6->ip6_hlim != 255) {
10782cd038dSYoshinobu Inoue 		log(LOG_ERR,
10882cd038dSYoshinobu Inoue 		    "nd6_rs_input: invalid hlim %d\n", ip6->ip6_hlim);
10982cd038dSYoshinobu Inoue 		return;
11082cd038dSYoshinobu Inoue 	}
11182cd038dSYoshinobu Inoue 
11282cd038dSYoshinobu Inoue 	/*
11382cd038dSYoshinobu Inoue 	 * Don't update the neighbor cache, if src = ::.
11482cd038dSYoshinobu Inoue 	 * This indicates that the src has no IP address assigned yet.
11582cd038dSYoshinobu Inoue 	 */
11682cd038dSYoshinobu Inoue 	if (IN6_IS_ADDR_UNSPECIFIED(&saddr6))
11782cd038dSYoshinobu Inoue 		return;
11882cd038dSYoshinobu Inoue 
11982cd038dSYoshinobu Inoue 	icmp6len -= sizeof(*nd_rs);
12082cd038dSYoshinobu Inoue 	nd6_option_init(nd_rs + 1, icmp6len, &ndopts);
12182cd038dSYoshinobu Inoue 	if (nd6_options(&ndopts) < 0) {
12282cd038dSYoshinobu Inoue 		log(LOG_INFO, "nd6_rs_input: invalid ND option, ignored\n");
12382cd038dSYoshinobu Inoue 		return;
12482cd038dSYoshinobu Inoue 	}
12582cd038dSYoshinobu Inoue 
12682cd038dSYoshinobu Inoue 	if (ndopts.nd_opts_src_lladdr) {
12782cd038dSYoshinobu Inoue 		lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1);
12882cd038dSYoshinobu Inoue 		lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3;
12982cd038dSYoshinobu Inoue 	}
13082cd038dSYoshinobu Inoue 
13182cd038dSYoshinobu Inoue 	if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) {
13282cd038dSYoshinobu Inoue 		log(LOG_INFO,
13382cd038dSYoshinobu Inoue 		    "nd6_rs_input: lladdrlen mismatch for %s "
13482cd038dSYoshinobu Inoue 		    "(if %d, RS packet %d)\n",
13582cd038dSYoshinobu Inoue 			ip6_sprintf(&saddr6), ifp->if_addrlen, lladdrlen - 2);
13682cd038dSYoshinobu Inoue 	}
13782cd038dSYoshinobu Inoue 
13882cd038dSYoshinobu Inoue 	nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_ROUTER_SOLICIT, 0);
13982cd038dSYoshinobu Inoue }
14082cd038dSYoshinobu Inoue 
14182cd038dSYoshinobu Inoue /*
14282cd038dSYoshinobu Inoue  * Receive Router Advertisement Message.
14382cd038dSYoshinobu Inoue  *
14482cd038dSYoshinobu Inoue  * Based on RFC 2461
14582cd038dSYoshinobu Inoue  * TODO: on-link bit on prefix information
14682cd038dSYoshinobu Inoue  * TODO: ND_RA_FLAG_{OTHER,MANAGED} processing
14782cd038dSYoshinobu Inoue  */
14882cd038dSYoshinobu Inoue void
14982cd038dSYoshinobu Inoue nd6_ra_input(m, off, icmp6len)
15082cd038dSYoshinobu Inoue 	struct	mbuf *m;
15182cd038dSYoshinobu Inoue 	int off, icmp6len;
15282cd038dSYoshinobu Inoue {
15382cd038dSYoshinobu Inoue 	struct ifnet *ifp = m->m_pkthdr.rcvif;
15482cd038dSYoshinobu Inoue 	struct nd_ifinfo *ndi = &nd_ifinfo[ifp->if_index];
15582cd038dSYoshinobu Inoue 	struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
15682cd038dSYoshinobu Inoue 	struct nd_router_advert *nd_ra =
15782cd038dSYoshinobu Inoue 		(struct nd_router_advert *)((caddr_t)ip6 + off);
15882cd038dSYoshinobu Inoue 	struct in6_addr saddr6 = ip6->ip6_src;
15982cd038dSYoshinobu Inoue 	union nd_opts ndopts;
16082cd038dSYoshinobu Inoue 	struct nd_defrouter *dr;
16182cd038dSYoshinobu Inoue 
16282cd038dSYoshinobu Inoue 	if (ip6_accept_rtadv == 0)
16382cd038dSYoshinobu Inoue 		return;
16482cd038dSYoshinobu Inoue 
16582cd038dSYoshinobu Inoue 	if (ip6->ip6_hlim != 255) {
16682cd038dSYoshinobu Inoue 		log(LOG_ERR,
16782cd038dSYoshinobu Inoue 		    "nd6_ra_input: invalid hlim %d\n", ip6->ip6_hlim);
16882cd038dSYoshinobu Inoue 		return;
16982cd038dSYoshinobu Inoue 	}
17082cd038dSYoshinobu Inoue 
17182cd038dSYoshinobu Inoue 	if (!IN6_IS_ADDR_LINKLOCAL(&saddr6)) {
17282cd038dSYoshinobu Inoue 		log(LOG_ERR,
17382cd038dSYoshinobu Inoue 		    "nd6_ra_input: src %s is not link-local\n",
17482cd038dSYoshinobu Inoue 		    ip6_sprintf(&saddr6));
17582cd038dSYoshinobu Inoue 		return;
17682cd038dSYoshinobu Inoue 	}
17782cd038dSYoshinobu Inoue 
17882cd038dSYoshinobu Inoue 	icmp6len -= sizeof(*nd_ra);
17982cd038dSYoshinobu Inoue 	nd6_option_init(nd_ra + 1, icmp6len, &ndopts);
18082cd038dSYoshinobu Inoue 	if (nd6_options(&ndopts) < 0) {
18182cd038dSYoshinobu Inoue 		log(LOG_INFO, "nd6_ra_input: invalid ND option, ignored\n");
18282cd038dSYoshinobu Inoue 		return;
18382cd038dSYoshinobu Inoue 	}
18482cd038dSYoshinobu Inoue 
18582cd038dSYoshinobu Inoue     {
18682cd038dSYoshinobu Inoue 	struct nd_defrouter dr0;
18782cd038dSYoshinobu Inoue 	u_int32_t advreachable = nd_ra->nd_ra_reachable;
18882cd038dSYoshinobu Inoue 
18982cd038dSYoshinobu Inoue 	dr0.rtaddr = saddr6;
19082cd038dSYoshinobu Inoue 	dr0.flags  = nd_ra->nd_ra_flags_reserved;
19182cd038dSYoshinobu Inoue 	dr0.rtlifetime = ntohs(nd_ra->nd_ra_router_lifetime);
19282cd038dSYoshinobu Inoue 	dr0.expire = time_second + dr0.rtlifetime;
19382cd038dSYoshinobu Inoue 	dr0.ifp = ifp;
19482cd038dSYoshinobu Inoue 	/* unspecified or not? (RFC 2461 6.3.4) */
19582cd038dSYoshinobu Inoue 	if (advreachable) {
19682cd038dSYoshinobu Inoue 		NTOHL(advreachable);
19782cd038dSYoshinobu Inoue 		if (advreachable <= MAX_REACHABLE_TIME &&
19882cd038dSYoshinobu Inoue 		    ndi->basereachable != advreachable) {
19982cd038dSYoshinobu Inoue 			ndi->basereachable = advreachable;
20082cd038dSYoshinobu Inoue 			ndi->reachable = ND_COMPUTE_RTIME(ndi->basereachable);
20182cd038dSYoshinobu Inoue 			ndi->recalctm = nd6_recalc_reachtm_interval; /* reset */
20282cd038dSYoshinobu Inoue 		}
20382cd038dSYoshinobu Inoue 	}
20482cd038dSYoshinobu Inoue 	if (nd_ra->nd_ra_retransmit)
20582cd038dSYoshinobu Inoue 		ndi->retrans = ntohl(nd_ra->nd_ra_retransmit);
20682cd038dSYoshinobu Inoue 	if (nd_ra->nd_ra_curhoplimit)
20782cd038dSYoshinobu Inoue 		ndi->chlim = nd_ra->nd_ra_curhoplimit;
20882cd038dSYoshinobu Inoue 	dr = defrtrlist_update(&dr0);
20982cd038dSYoshinobu Inoue     }
21082cd038dSYoshinobu Inoue 
21182cd038dSYoshinobu Inoue 	/*
21282cd038dSYoshinobu Inoue 	 * prefix
21382cd038dSYoshinobu Inoue 	 */
21482cd038dSYoshinobu Inoue 	if (ndopts.nd_opts_pi) {
21582cd038dSYoshinobu Inoue 		struct nd_opt_hdr *pt;
21682cd038dSYoshinobu Inoue 		struct nd_opt_prefix_info *pi;
21782cd038dSYoshinobu Inoue 		struct nd_prefix pr;
21882cd038dSYoshinobu Inoue 
21982cd038dSYoshinobu Inoue 		for (pt = (struct nd_opt_hdr *)ndopts.nd_opts_pi;
22082cd038dSYoshinobu Inoue 		     pt <= (struct nd_opt_hdr *)ndopts.nd_opts_pi_end;
22182cd038dSYoshinobu Inoue 		     pt = (struct nd_opt_hdr *)((caddr_t)pt +
22282cd038dSYoshinobu Inoue 						(pt->nd_opt_len << 3))) {
22382cd038dSYoshinobu Inoue 			if (pt->nd_opt_type != ND_OPT_PREFIX_INFORMATION)
22482cd038dSYoshinobu Inoue 				continue;
22582cd038dSYoshinobu Inoue 			pi = (struct nd_opt_prefix_info *)pt;
22682cd038dSYoshinobu Inoue 
22782cd038dSYoshinobu Inoue 			if (pi->nd_opt_pi_len != 4) {
22882cd038dSYoshinobu Inoue 				log(LOG_INFO, "nd6_ra_input: invalid option "
22982cd038dSYoshinobu Inoue 					"len %d for prefix information option, "
23082cd038dSYoshinobu Inoue 					"ignored\n", pi->nd_opt_pi_len);
23182cd038dSYoshinobu Inoue 				continue;
23282cd038dSYoshinobu Inoue 			}
23382cd038dSYoshinobu Inoue 
23482cd038dSYoshinobu Inoue 			if (128 < pi->nd_opt_pi_prefix_len) {
23582cd038dSYoshinobu Inoue 				log(LOG_INFO, "nd6_ra_input: invalid prefix "
23682cd038dSYoshinobu Inoue 					"len %d for prefix information option, "
23782cd038dSYoshinobu Inoue 					"ignored\n", pi->nd_opt_pi_prefix_len);
23882cd038dSYoshinobu Inoue 				continue;
23982cd038dSYoshinobu Inoue 			}
24082cd038dSYoshinobu Inoue 
24182cd038dSYoshinobu Inoue 			if (IN6_IS_ADDR_MULTICAST(&pi->nd_opt_pi_prefix)
24282cd038dSYoshinobu Inoue 			 || IN6_IS_ADDR_LINKLOCAL(&pi->nd_opt_pi_prefix)) {
24382cd038dSYoshinobu Inoue 				log(LOG_INFO, "nd6_ra_input: invalid prefix "
24482cd038dSYoshinobu Inoue 					"%s, ignored\n",
24582cd038dSYoshinobu Inoue 					ip6_sprintf(&pi->nd_opt_pi_prefix));
24682cd038dSYoshinobu Inoue 				continue;
24782cd038dSYoshinobu Inoue 			}
24882cd038dSYoshinobu Inoue 
24982cd038dSYoshinobu Inoue 			/* aggregatable unicast address, rfc2374 */
25082cd038dSYoshinobu Inoue 			if ((pi->nd_opt_pi_prefix.s6_addr8[0] & 0xe0) == 0x20
25182cd038dSYoshinobu Inoue 			 && pi->nd_opt_pi_prefix_len != 64) {
25282cd038dSYoshinobu Inoue 				log(LOG_INFO, "nd6_ra_input: invalid prefixlen "
25382cd038dSYoshinobu Inoue 					"%d for rfc2374 prefix %s, ignored\n",
25482cd038dSYoshinobu Inoue 					pi->nd_opt_pi_prefix_len,
25582cd038dSYoshinobu Inoue 					ip6_sprintf(&pi->nd_opt_pi_prefix));
25682cd038dSYoshinobu Inoue 				continue;
25782cd038dSYoshinobu Inoue 			}
25882cd038dSYoshinobu Inoue 
25982cd038dSYoshinobu Inoue 			bzero(&pr, sizeof(pr));
26082cd038dSYoshinobu Inoue 			pr.ndpr_prefix.sin6_family = AF_INET6;
26182cd038dSYoshinobu Inoue 			pr.ndpr_prefix.sin6_len = sizeof(pr.ndpr_prefix);
26282cd038dSYoshinobu Inoue 			pr.ndpr_prefix.sin6_addr = pi->nd_opt_pi_prefix;
26382cd038dSYoshinobu Inoue 			pr.ndpr_ifp = (struct ifnet *)m->m_pkthdr.rcvif;
26482cd038dSYoshinobu Inoue 
26582cd038dSYoshinobu Inoue 			pr.ndpr_raf_onlink = (pi->nd_opt_pi_flags_reserved &
26682cd038dSYoshinobu Inoue 					      ND_OPT_PI_FLAG_ONLINK) ? 1 : 0;
26782cd038dSYoshinobu Inoue 			pr.ndpr_raf_auto = (pi->nd_opt_pi_flags_reserved &
26882cd038dSYoshinobu Inoue 					    ND_OPT_PI_FLAG_AUTO) ? 1 : 0;
26982cd038dSYoshinobu Inoue 			pr.ndpr_plen = pi->nd_opt_pi_prefix_len;
27082cd038dSYoshinobu Inoue 			pr.ndpr_vltime = ntohl(pi->nd_opt_pi_valid_time);
27182cd038dSYoshinobu Inoue 			pr.ndpr_pltime =
27282cd038dSYoshinobu Inoue 				ntohl(pi->nd_opt_pi_preferred_time);
27382cd038dSYoshinobu Inoue 
27482cd038dSYoshinobu Inoue 			if (in6_init_prefix_ltimes(&pr))
27582cd038dSYoshinobu Inoue 				continue; /* prefix lifetime init failed */
27682cd038dSYoshinobu Inoue 
27782cd038dSYoshinobu Inoue 			(void)prelist_update(&pr, dr, m);
27882cd038dSYoshinobu Inoue 		}
27982cd038dSYoshinobu Inoue 	}
28082cd038dSYoshinobu Inoue 
28182cd038dSYoshinobu Inoue 	/*
28282cd038dSYoshinobu Inoue 	 * MTU
28382cd038dSYoshinobu Inoue 	 */
28482cd038dSYoshinobu Inoue 	if (ndopts.nd_opts_mtu && ndopts.nd_opts_mtu->nd_opt_mtu_len == 1) {
28582cd038dSYoshinobu Inoue 		u_int32_t mtu = ntohl(ndopts.nd_opts_mtu->nd_opt_mtu_mtu);
28682cd038dSYoshinobu Inoue 
28782cd038dSYoshinobu Inoue 		/* lower bound */
28882cd038dSYoshinobu Inoue 		if (mtu < IPV6_MMTU) {
28982cd038dSYoshinobu Inoue 			log(LOG_INFO, "nd6_ra_input: bogus mtu option "
29082cd038dSYoshinobu Inoue 			    "mtu=%d sent from %s, ignoring\n",
29182cd038dSYoshinobu Inoue 			    mtu, ip6_sprintf(&ip6->ip6_src));
29282cd038dSYoshinobu Inoue 			goto skip;
29382cd038dSYoshinobu Inoue 		}
29482cd038dSYoshinobu Inoue 
29582cd038dSYoshinobu Inoue 		/* upper bound */
29682cd038dSYoshinobu Inoue 		if (ndi->maxmtu) {
29782cd038dSYoshinobu Inoue 			if (mtu <= ndi->maxmtu) {
29882cd038dSYoshinobu Inoue 				int change = (ndi->linkmtu != mtu);
29982cd038dSYoshinobu Inoue 
30082cd038dSYoshinobu Inoue 				ndi->linkmtu = mtu;
30182cd038dSYoshinobu Inoue 				if (change) /* in6_maxmtu may change */
30282cd038dSYoshinobu Inoue 					in6_setmaxmtu();
30382cd038dSYoshinobu Inoue 			} else {
30482cd038dSYoshinobu Inoue 				log(LOG_INFO, "nd6_ra_input: bogus mtu "
30582cd038dSYoshinobu Inoue 				    "mtu=%d sent from %s; "
30682cd038dSYoshinobu Inoue 				    "exceeds maxmtu %d, ignoring\n",
30782cd038dSYoshinobu Inoue 				    mtu, ip6_sprintf(&ip6->ip6_src),
30882cd038dSYoshinobu Inoue 				    ndi->maxmtu);
30982cd038dSYoshinobu Inoue 			}
31082cd038dSYoshinobu Inoue 		} else {
31182cd038dSYoshinobu Inoue 			log(LOG_INFO, "nd6_ra_input: mtu option "
31282cd038dSYoshinobu Inoue 			    "mtu=%d sent from %s; maxmtu unknown, "
31382cd038dSYoshinobu Inoue 			    "ignoring\n",
31482cd038dSYoshinobu Inoue 			    mtu, ip6_sprintf(&ip6->ip6_src));
31582cd038dSYoshinobu Inoue 		}
31682cd038dSYoshinobu Inoue 	}
31782cd038dSYoshinobu Inoue 
31882cd038dSYoshinobu Inoue  skip:
31982cd038dSYoshinobu Inoue 
32082cd038dSYoshinobu Inoue 	/*
32182cd038dSYoshinobu Inoue 	 * Src linkaddress
32282cd038dSYoshinobu Inoue 	 */
32382cd038dSYoshinobu Inoue     {
32482cd038dSYoshinobu Inoue 	char *lladdr = NULL;
32582cd038dSYoshinobu Inoue 	int lladdrlen = 0;
32682cd038dSYoshinobu Inoue 
32782cd038dSYoshinobu Inoue 	if (ndopts.nd_opts_src_lladdr) {
32882cd038dSYoshinobu Inoue 		lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1);
32982cd038dSYoshinobu Inoue 		lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3;
33082cd038dSYoshinobu Inoue 	}
33182cd038dSYoshinobu Inoue 
33282cd038dSYoshinobu Inoue 	if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) {
33382cd038dSYoshinobu Inoue 		log(LOG_INFO,
33482cd038dSYoshinobu Inoue 		    "nd6_ra_input: lladdrlen mismatch for %s "
33582cd038dSYoshinobu Inoue 		    "(if %d, RA packet %d)\n",
33682cd038dSYoshinobu Inoue 			ip6_sprintf(&saddr6), ifp->if_addrlen, lladdrlen - 2);
33782cd038dSYoshinobu Inoue 	}
33882cd038dSYoshinobu Inoue 
33982cd038dSYoshinobu Inoue 	nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_ROUTER_ADVERT, 0);
34082cd038dSYoshinobu Inoue     }
34182cd038dSYoshinobu Inoue }
34282cd038dSYoshinobu Inoue 
34382cd038dSYoshinobu Inoue /*
34482cd038dSYoshinobu Inoue  * default router list proccessing sub routines
34582cd038dSYoshinobu Inoue  */
34682cd038dSYoshinobu Inoue void
34782cd038dSYoshinobu Inoue defrouter_addreq(new)
34882cd038dSYoshinobu Inoue 	struct nd_defrouter *new;
34982cd038dSYoshinobu Inoue {
35082cd038dSYoshinobu Inoue 	struct sockaddr_in6 def, mask, gate;
35182cd038dSYoshinobu Inoue 	int s;
35282cd038dSYoshinobu Inoue 
35382cd038dSYoshinobu Inoue 	Bzero(&def, sizeof(def));
35482cd038dSYoshinobu Inoue 	Bzero(&mask, sizeof(mask));
35582cd038dSYoshinobu Inoue 	Bzero(&gate, sizeof(gate));
35682cd038dSYoshinobu Inoue 
35782cd038dSYoshinobu Inoue 	def.sin6_len = mask.sin6_len = gate.sin6_len
35882cd038dSYoshinobu Inoue 		= sizeof(struct sockaddr_in6);
35982cd038dSYoshinobu Inoue 	def.sin6_family = mask.sin6_family = gate.sin6_family = AF_INET6;
36082cd038dSYoshinobu Inoue 	gate.sin6_addr = new->rtaddr;
36182cd038dSYoshinobu Inoue 
36282cd038dSYoshinobu Inoue 	s = splnet();
36382cd038dSYoshinobu Inoue 	(void)rtrequest(RTM_ADD, (struct sockaddr *)&def,
36482cd038dSYoshinobu Inoue 		(struct sockaddr *)&gate, (struct sockaddr *)&mask,
36582cd038dSYoshinobu Inoue 		RTF_GATEWAY, NULL);
36682cd038dSYoshinobu Inoue 	splx(s);
36782cd038dSYoshinobu Inoue 	return;
36882cd038dSYoshinobu Inoue }
36982cd038dSYoshinobu Inoue 
37082cd038dSYoshinobu Inoue struct nd_defrouter *
37182cd038dSYoshinobu Inoue defrouter_lookup(addr, ifp)
37282cd038dSYoshinobu Inoue 	struct in6_addr *addr;
37382cd038dSYoshinobu Inoue 	struct ifnet *ifp;
37482cd038dSYoshinobu Inoue {
37582cd038dSYoshinobu Inoue 	struct nd_defrouter *dr;
37682cd038dSYoshinobu Inoue 
37782cd038dSYoshinobu Inoue 	for(dr = nd_defrouter.lh_first; dr; dr = dr->dr_next)
37882cd038dSYoshinobu Inoue 		if (dr->ifp == ifp && IN6_ARE_ADDR_EQUAL(addr, &dr->rtaddr))
37982cd038dSYoshinobu Inoue 			return(dr);
38082cd038dSYoshinobu Inoue 
38182cd038dSYoshinobu Inoue 	return(NULL);		/* search failed */
38282cd038dSYoshinobu Inoue }
38382cd038dSYoshinobu Inoue 
38482cd038dSYoshinobu Inoue void
38582cd038dSYoshinobu Inoue defrouter_delreq(dr, dofree)
38682cd038dSYoshinobu Inoue 	struct nd_defrouter *dr;
38782cd038dSYoshinobu Inoue 	int dofree;
38882cd038dSYoshinobu Inoue {
38982cd038dSYoshinobu Inoue 	struct sockaddr_in6 def, mask, gate;
39082cd038dSYoshinobu Inoue 
39182cd038dSYoshinobu Inoue 	Bzero(&def, sizeof(def));
39282cd038dSYoshinobu Inoue 	Bzero(&mask, sizeof(mask));
39382cd038dSYoshinobu Inoue 	Bzero(&gate, sizeof(gate));
39482cd038dSYoshinobu Inoue 
39582cd038dSYoshinobu Inoue 	def.sin6_len = mask.sin6_len = gate.sin6_len
39682cd038dSYoshinobu Inoue 		= sizeof(struct sockaddr_in6);
39782cd038dSYoshinobu Inoue 	def.sin6_family = mask.sin6_family = gate.sin6_family = AF_INET6;
39882cd038dSYoshinobu Inoue 	gate.sin6_addr = dr->rtaddr;
39982cd038dSYoshinobu Inoue 
40082cd038dSYoshinobu Inoue 	rtrequest(RTM_DELETE, (struct sockaddr *)&def,
40182cd038dSYoshinobu Inoue 		  (struct sockaddr *)&gate,
40282cd038dSYoshinobu Inoue 		  (struct sockaddr *)&mask,
40382cd038dSYoshinobu Inoue 		  RTF_GATEWAY, (struct rtentry **)0);
40482cd038dSYoshinobu Inoue 
40582cd038dSYoshinobu Inoue 	if (dofree)
40682cd038dSYoshinobu Inoue 		free(dr, M_IP6NDP);
40782cd038dSYoshinobu Inoue 
40882cd038dSYoshinobu Inoue 	if (nd_defrouter.lh_first)
40982cd038dSYoshinobu Inoue 		defrouter_addreq(nd_defrouter.lh_first);
41082cd038dSYoshinobu Inoue 
41182cd038dSYoshinobu Inoue 	/*
41282cd038dSYoshinobu Inoue 	 * xxx update the Destination Cache entries for all
41382cd038dSYoshinobu Inoue 	 * destinations using that neighbor as a router (7.2.5)
41482cd038dSYoshinobu Inoue 	 */
41582cd038dSYoshinobu Inoue }
41682cd038dSYoshinobu Inoue 
41782cd038dSYoshinobu Inoue void
41882cd038dSYoshinobu Inoue defrtrlist_del(dr)
41982cd038dSYoshinobu Inoue 	struct nd_defrouter *dr;
42082cd038dSYoshinobu Inoue {
42182cd038dSYoshinobu Inoue 	struct nd_defrouter *deldr = NULL;
42282cd038dSYoshinobu Inoue 	struct nd_prefix *pr;
42382cd038dSYoshinobu Inoue 
42482cd038dSYoshinobu Inoue 	/*
42582cd038dSYoshinobu Inoue 	 * Flush all the routing table entries that use the router
42682cd038dSYoshinobu Inoue 	 * as a next hop.
42782cd038dSYoshinobu Inoue 	 */
42882cd038dSYoshinobu Inoue 	if (!ip6_forwarding && ip6_accept_rtadv) {
42982cd038dSYoshinobu Inoue 		/* above is a good condition? */
43082cd038dSYoshinobu Inoue 		rt6_flush(&dr->rtaddr, dr->ifp);
43182cd038dSYoshinobu Inoue 	}
43282cd038dSYoshinobu Inoue 
43382cd038dSYoshinobu Inoue 	if (dr == nd_defrouter.lh_first)
43482cd038dSYoshinobu Inoue 		deldr = dr;	/* The router is primary. */
43582cd038dSYoshinobu Inoue 
43682cd038dSYoshinobu Inoue 	LIST_REMOVE(dr, dr_entry);
43782cd038dSYoshinobu Inoue 
43882cd038dSYoshinobu Inoue 	/*
43982cd038dSYoshinobu Inoue 	 * Also delete all the pointers to the router in each prefix lists.
44082cd038dSYoshinobu Inoue 	 */
44182cd038dSYoshinobu Inoue 	for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
44282cd038dSYoshinobu Inoue 		struct nd_pfxrouter *pfxrtr;
44382cd038dSYoshinobu Inoue 		if ((pfxrtr = pfxrtr_lookup(pr, dr)) != NULL)
44482cd038dSYoshinobu Inoue 			pfxrtr_del(pfxrtr);
44582cd038dSYoshinobu Inoue 	}
44682cd038dSYoshinobu Inoue 	pfxlist_onlink_check();
44782cd038dSYoshinobu Inoue 
44882cd038dSYoshinobu Inoue 	/*
44982cd038dSYoshinobu Inoue 	 * If the router is the primary one, delete the default route
45082cd038dSYoshinobu Inoue 	 * entry in the routing table.
45182cd038dSYoshinobu Inoue 	 */
45282cd038dSYoshinobu Inoue 	if (deldr)
45382cd038dSYoshinobu Inoue 		defrouter_delreq(deldr, 0);
45482cd038dSYoshinobu Inoue 	free(dr, M_IP6NDP);
45582cd038dSYoshinobu Inoue }
45682cd038dSYoshinobu Inoue 
45782cd038dSYoshinobu Inoue static struct nd_defrouter *
45882cd038dSYoshinobu Inoue defrtrlist_update(new)
45982cd038dSYoshinobu Inoue 	struct nd_defrouter *new;
46082cd038dSYoshinobu Inoue {
46182cd038dSYoshinobu Inoue 	struct nd_defrouter *dr, *n;
46282cd038dSYoshinobu Inoue 	int s = splnet();
46382cd038dSYoshinobu Inoue 
46482cd038dSYoshinobu Inoue 	if ((dr = defrouter_lookup(&new->rtaddr, new->ifp)) != NULL) {
46582cd038dSYoshinobu Inoue 		/* entry exists */
46682cd038dSYoshinobu Inoue 		if (new->rtlifetime == 0) {
46782cd038dSYoshinobu Inoue 			defrtrlist_del(dr);
46882cd038dSYoshinobu Inoue 			dr = NULL;
46982cd038dSYoshinobu Inoue 		} else {
47082cd038dSYoshinobu Inoue 			/* override */
47182cd038dSYoshinobu Inoue 			dr->flags = new->flags; /* xxx flag check */
47282cd038dSYoshinobu Inoue 			dr->rtlifetime = new->rtlifetime;
47382cd038dSYoshinobu Inoue 			dr->expire = new->expire;
47482cd038dSYoshinobu Inoue 		}
47582cd038dSYoshinobu Inoue 		splx(s);
47682cd038dSYoshinobu Inoue 		return(dr);
47782cd038dSYoshinobu Inoue 	}
47882cd038dSYoshinobu Inoue 
47982cd038dSYoshinobu Inoue 	/* entry does not exist */
48082cd038dSYoshinobu Inoue 	if (new->rtlifetime == 0) {
48182cd038dSYoshinobu Inoue 		splx(s);
48282cd038dSYoshinobu Inoue 		return(NULL);
48382cd038dSYoshinobu Inoue 	}
48482cd038dSYoshinobu Inoue 
48582cd038dSYoshinobu Inoue 	n = (struct nd_defrouter *)malloc(sizeof(*n), M_IP6NDP, M_NOWAIT);
48682cd038dSYoshinobu Inoue 	if (n == NULL) {
48782cd038dSYoshinobu Inoue 		splx(s);
48882cd038dSYoshinobu Inoue 		return(NULL);
48982cd038dSYoshinobu Inoue 	}
49082cd038dSYoshinobu Inoue 	bzero(n, sizeof(*n));
49182cd038dSYoshinobu Inoue 	*n = *new;
49282cd038dSYoshinobu Inoue 	if (nd_defrouter.lh_first == NULL) {
49382cd038dSYoshinobu Inoue 		LIST_INSERT_HEAD(&nd_defrouter, n, dr_entry);
49482cd038dSYoshinobu Inoue 		defrouter_addreq(n);
49582cd038dSYoshinobu Inoue 	} else {
49682cd038dSYoshinobu Inoue 		LIST_INSERT_AFTER(nd_defrouter.lh_first, n, dr_entry);
49782cd038dSYoshinobu Inoue 		defrouter_addreq(n);
49882cd038dSYoshinobu Inoue 	}
49982cd038dSYoshinobu Inoue 	splx(s);
50082cd038dSYoshinobu Inoue 
50182cd038dSYoshinobu Inoue 	return(n);
50282cd038dSYoshinobu Inoue }
50382cd038dSYoshinobu Inoue 
50482cd038dSYoshinobu Inoue static struct nd_pfxrouter *
50582cd038dSYoshinobu Inoue pfxrtr_lookup(pr, dr)
50682cd038dSYoshinobu Inoue 	struct nd_prefix *pr;
50782cd038dSYoshinobu Inoue 	struct nd_defrouter *dr;
50882cd038dSYoshinobu Inoue {
50982cd038dSYoshinobu Inoue 	struct nd_pfxrouter *search;
51082cd038dSYoshinobu Inoue 
51182cd038dSYoshinobu Inoue 	for (search = pr->ndpr_advrtrs.lh_first; search; search = search->pfr_next) {
51282cd038dSYoshinobu Inoue 		if (search->router == dr)
51382cd038dSYoshinobu Inoue 			break;
51482cd038dSYoshinobu Inoue 	}
51582cd038dSYoshinobu Inoue 
51682cd038dSYoshinobu Inoue 	return(search);
51782cd038dSYoshinobu Inoue }
51882cd038dSYoshinobu Inoue 
51982cd038dSYoshinobu Inoue static void
52082cd038dSYoshinobu Inoue pfxrtr_add(pr, dr)
52182cd038dSYoshinobu Inoue 	struct nd_prefix *pr;
52282cd038dSYoshinobu Inoue 	struct nd_defrouter *dr;
52382cd038dSYoshinobu Inoue {
52482cd038dSYoshinobu Inoue 	struct nd_pfxrouter *new;
52582cd038dSYoshinobu Inoue 
52682cd038dSYoshinobu Inoue 	new = (struct nd_pfxrouter *)malloc(sizeof(*new), M_IP6NDP, M_NOWAIT);
52782cd038dSYoshinobu Inoue 	if (new == NULL)
52882cd038dSYoshinobu Inoue 		return;
52982cd038dSYoshinobu Inoue 	bzero(new, sizeof(*new));
53082cd038dSYoshinobu Inoue 	new->router = dr;
53182cd038dSYoshinobu Inoue 
53282cd038dSYoshinobu Inoue 	LIST_INSERT_HEAD(&pr->ndpr_advrtrs, new, pfr_entry);
53382cd038dSYoshinobu Inoue 
53482cd038dSYoshinobu Inoue 	pfxlist_onlink_check();
53582cd038dSYoshinobu Inoue }
53682cd038dSYoshinobu Inoue 
53782cd038dSYoshinobu Inoue static void
53882cd038dSYoshinobu Inoue pfxrtr_del(pfr)
53982cd038dSYoshinobu Inoue 	struct nd_pfxrouter *pfr;
54082cd038dSYoshinobu Inoue {
54182cd038dSYoshinobu Inoue 	LIST_REMOVE(pfr, pfr_entry);
54282cd038dSYoshinobu Inoue 	free(pfr, M_IP6NDP);
54382cd038dSYoshinobu Inoue }
54482cd038dSYoshinobu Inoue 
54582cd038dSYoshinobu Inoue static struct nd_prefix *
54682cd038dSYoshinobu Inoue prefix_lookup(pr)
54782cd038dSYoshinobu Inoue 	struct nd_prefix *pr;
54882cd038dSYoshinobu Inoue {
54982cd038dSYoshinobu Inoue 	struct nd_prefix *search;
55082cd038dSYoshinobu Inoue 
55182cd038dSYoshinobu Inoue 	for (search = nd_prefix.lh_first; search; search = search->ndpr_next) {
55282cd038dSYoshinobu Inoue 		if (pr->ndpr_ifp == search->ndpr_ifp &&
55382cd038dSYoshinobu Inoue 		    pr->ndpr_plen == search->ndpr_plen &&
55482cd038dSYoshinobu Inoue 		    in6_are_prefix_equal(&pr->ndpr_prefix.sin6_addr,
55582cd038dSYoshinobu Inoue 					 &search->ndpr_prefix.sin6_addr,
55682cd038dSYoshinobu Inoue 					 pr->ndpr_plen)
55782cd038dSYoshinobu Inoue 		    ) {
55882cd038dSYoshinobu Inoue 			break;
55982cd038dSYoshinobu Inoue 		}
56082cd038dSYoshinobu Inoue 	}
56182cd038dSYoshinobu Inoue 
56282cd038dSYoshinobu Inoue 	return(search);
56382cd038dSYoshinobu Inoue }
56482cd038dSYoshinobu Inoue 
56582cd038dSYoshinobu Inoue static int
56682cd038dSYoshinobu Inoue prelist_add(pr, dr)
56782cd038dSYoshinobu Inoue 	struct nd_prefix *pr;
56882cd038dSYoshinobu Inoue 	struct nd_defrouter *dr;
56982cd038dSYoshinobu Inoue {
57082cd038dSYoshinobu Inoue 	struct nd_prefix *new;
57182cd038dSYoshinobu Inoue 	int i, s;
57282cd038dSYoshinobu Inoue 
57382cd038dSYoshinobu Inoue 	new = (struct nd_prefix *)malloc(sizeof(*new), M_IP6NDP, M_NOWAIT);
57482cd038dSYoshinobu Inoue 	if (new == NULL)
57582cd038dSYoshinobu Inoue 		return ENOMEM;
57682cd038dSYoshinobu Inoue 	bzero(new, sizeof(*new));
57782cd038dSYoshinobu Inoue 	*new = *pr;
57882cd038dSYoshinobu Inoue 
57982cd038dSYoshinobu Inoue 	/* initilization */
58082cd038dSYoshinobu Inoue 	new->ndpr_statef_onlink = pr->ndpr_statef_onlink;
58182cd038dSYoshinobu Inoue 	LIST_INIT(&new->ndpr_advrtrs);
58282cd038dSYoshinobu Inoue 	in6_prefixlen2mask(&new->ndpr_mask, new->ndpr_plen);
58382cd038dSYoshinobu Inoue 	/* make prefix in the canonical form */
58482cd038dSYoshinobu Inoue 	for (i = 0; i < 4; i++)
58582cd038dSYoshinobu Inoue 		new->ndpr_prefix.sin6_addr.s6_addr32[i] &=
58682cd038dSYoshinobu Inoue 			new->ndpr_mask.s6_addr32[i];
58782cd038dSYoshinobu Inoue 
58882cd038dSYoshinobu Inoue 	/* xxx ND_OPT_PI_FLAG_ONLINK processing */
58982cd038dSYoshinobu Inoue 
59082cd038dSYoshinobu Inoue 	s = splnet();
59182cd038dSYoshinobu Inoue 	/* link ndpr_entry to nd_prefix list */
59282cd038dSYoshinobu Inoue 	LIST_INSERT_HEAD(&nd_prefix, new, ndpr_entry);
59382cd038dSYoshinobu Inoue 	splx(s);
59482cd038dSYoshinobu Inoue 
59582cd038dSYoshinobu Inoue 	if (dr)
59682cd038dSYoshinobu Inoue 		pfxrtr_add(new, dr);
59782cd038dSYoshinobu Inoue 
59882cd038dSYoshinobu Inoue 	return 0;
59982cd038dSYoshinobu Inoue }
60082cd038dSYoshinobu Inoue 
60182cd038dSYoshinobu Inoue void
60282cd038dSYoshinobu Inoue prelist_remove(pr)
60382cd038dSYoshinobu Inoue 	struct nd_prefix *pr;
60482cd038dSYoshinobu Inoue {
60582cd038dSYoshinobu Inoue 	struct nd_pfxrouter *pfr, *next;
60682cd038dSYoshinobu Inoue 	int s;
60782cd038dSYoshinobu Inoue 
60882cd038dSYoshinobu Inoue 	s = splnet();
60982cd038dSYoshinobu Inoue 	/* unlink ndpr_entry from nd_prefix list */
61082cd038dSYoshinobu Inoue 	LIST_REMOVE(pr, ndpr_entry);
61182cd038dSYoshinobu Inoue 	splx(s);
61282cd038dSYoshinobu Inoue 
61382cd038dSYoshinobu Inoue 	/* free list of routers that adversed the prefix */
61482cd038dSYoshinobu Inoue 	for (pfr = pr->ndpr_advrtrs.lh_first; pfr; pfr = next) {
61582cd038dSYoshinobu Inoue 		next = pfr->pfr_next;
61682cd038dSYoshinobu Inoue 
61782cd038dSYoshinobu Inoue 		free(pfr, M_IP6NDP);
61882cd038dSYoshinobu Inoue 	}
61982cd038dSYoshinobu Inoue 	free(pr, M_IP6NDP);
62082cd038dSYoshinobu Inoue 
62182cd038dSYoshinobu Inoue 	pfxlist_onlink_check();
62282cd038dSYoshinobu Inoue }
62382cd038dSYoshinobu Inoue 
62482cd038dSYoshinobu Inoue /*
62582cd038dSYoshinobu Inoue  * NOTE: We set address lifetime to keep
62682cd038dSYoshinobu Inoue  *	address lifetime <= prefix lifetime
62782cd038dSYoshinobu Inoue  * invariant.  This is to simplify on-link determination code.
62882cd038dSYoshinobu Inoue  * If onlink determination is udated, this routine may have to be updated too.
62982cd038dSYoshinobu Inoue  */
63082cd038dSYoshinobu Inoue int
63182cd038dSYoshinobu Inoue prelist_update(new, dr, m)
63282cd038dSYoshinobu Inoue 	struct nd_prefix *new;
63382cd038dSYoshinobu Inoue 	struct nd_defrouter *dr; /* may be NULL */
63482cd038dSYoshinobu Inoue 	struct mbuf *m;
63582cd038dSYoshinobu Inoue {
63682cd038dSYoshinobu Inoue 	struct in6_ifaddr *ia6 = NULL;
63782cd038dSYoshinobu Inoue 	struct nd_prefix *pr;
63882cd038dSYoshinobu Inoue 	int s = splnet();
63982cd038dSYoshinobu Inoue 	int error = 0;
64082cd038dSYoshinobu Inoue 	int auth;
64182cd038dSYoshinobu Inoue 	struct in6_addrlifetime *lt6;
64282cd038dSYoshinobu Inoue 
64382cd038dSYoshinobu Inoue 	auth = 0;
64482cd038dSYoshinobu Inoue 	if (m) {
64582cd038dSYoshinobu Inoue 		/*
64682cd038dSYoshinobu Inoue 		 * Authenticity for NA consists authentication for
64782cd038dSYoshinobu Inoue 		 * both IP header and IP datagrams, doesn't it ?
64882cd038dSYoshinobu Inoue 		 */
64982cd038dSYoshinobu Inoue #if defined(M_AUTHIPHDR) && defined(M_AUTHIPDGM)
65082cd038dSYoshinobu Inoue 		auth = (m->m_flags & M_AUTHIPHDR
65182cd038dSYoshinobu Inoue 		     && m->m_flags & M_AUTHIPDGM) ? 1 : 0;
65282cd038dSYoshinobu Inoue #endif
65382cd038dSYoshinobu Inoue 	}
65482cd038dSYoshinobu Inoue 
65582cd038dSYoshinobu Inoue 	if ((pr = prefix_lookup(new)) != NULL) {
65682cd038dSYoshinobu Inoue 		if (pr->ndpr_ifp != new->ndpr_ifp) {
65782cd038dSYoshinobu Inoue 			error = EADDRNOTAVAIL;
65882cd038dSYoshinobu Inoue 			goto end;
65982cd038dSYoshinobu Inoue 		}
66082cd038dSYoshinobu Inoue 		/* update prefix information */
66182cd038dSYoshinobu Inoue 		pr->ndpr_flags = new->ndpr_flags;
66282cd038dSYoshinobu Inoue 		pr->ndpr_vltime = new->ndpr_vltime;
66382cd038dSYoshinobu Inoue 		pr->ndpr_pltime = new->ndpr_pltime;
66482cd038dSYoshinobu Inoue 		pr->ndpr_preferred = new->ndpr_preferred;
66582cd038dSYoshinobu Inoue 		pr->ndpr_expire = new->ndpr_expire;
66682cd038dSYoshinobu Inoue 
66782cd038dSYoshinobu Inoue 		/*
66882cd038dSYoshinobu Inoue 		 * RFC 2462 5.5.3 (d) or (e)
66982cd038dSYoshinobu Inoue 		 * We got a prefix which we have seen in the past.
67082cd038dSYoshinobu Inoue 		 */
67182cd038dSYoshinobu Inoue 		if (!new->ndpr_raf_auto)
67282cd038dSYoshinobu Inoue 			goto noautoconf1;
67382cd038dSYoshinobu Inoue 
67482cd038dSYoshinobu Inoue 		if (IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr))
67582cd038dSYoshinobu Inoue 			ia6 = NULL;
67682cd038dSYoshinobu Inoue 		else
67782cd038dSYoshinobu Inoue 			ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr);
67882cd038dSYoshinobu Inoue 
67982cd038dSYoshinobu Inoue 		if (ia6 == NULL) {
68082cd038dSYoshinobu Inoue 			/*
68182cd038dSYoshinobu Inoue 			 * Special case:
68282cd038dSYoshinobu Inoue 			 * (1) We have seen the prefix advertised before, but
68382cd038dSYoshinobu Inoue 			 * we have never performed autoconfig for this prefix.
68482cd038dSYoshinobu Inoue 			 * This is because Autonomous bit was 0 previously, or
68582cd038dSYoshinobu Inoue 			 * autoconfig failed due to some other reasons.
68682cd038dSYoshinobu Inoue 			 * (2) We have seen the prefix advertised before and
68782cd038dSYoshinobu Inoue 			 * we have performed autoconfig in the past, but
68882cd038dSYoshinobu Inoue 			 * we seem to have no interface address right now.
68982cd038dSYoshinobu Inoue 			 * This is because the interface address have expired.
69082cd038dSYoshinobu Inoue 			 *
69182cd038dSYoshinobu Inoue 			 * This prefix is fresh, with respect to autoconfig
69282cd038dSYoshinobu Inoue 			 * process.
69382cd038dSYoshinobu Inoue 			 *
69482cd038dSYoshinobu Inoue 			 * Add an address based on RFC 2462 5.5.3 (d).
69582cd038dSYoshinobu Inoue 			 */
69682cd038dSYoshinobu Inoue 			ia6 = in6_ifadd(pr->ndpr_ifp,
69782cd038dSYoshinobu Inoue 				&pr->ndpr_prefix.sin6_addr, &pr->ndpr_addr,
69882cd038dSYoshinobu Inoue 				new->ndpr_plen);
69982cd038dSYoshinobu Inoue 			if (!ia6) {
70082cd038dSYoshinobu Inoue 				error = EADDRNOTAVAIL;
70182cd038dSYoshinobu Inoue 				log(LOG_ERR, "prelist_update: failed to add a "
70282cd038dSYoshinobu Inoue 				    "new address\n");
70382cd038dSYoshinobu Inoue 				goto noautoconf1;
70482cd038dSYoshinobu Inoue 			}
70582cd038dSYoshinobu Inoue 
70682cd038dSYoshinobu Inoue 			lt6 = &ia6->ia6_lifetime;
70782cd038dSYoshinobu Inoue 
70882cd038dSYoshinobu Inoue 			/* address lifetime <= prefix lifetime */
70982cd038dSYoshinobu Inoue 			lt6->ia6t_vltime = new->ndpr_vltime;
71082cd038dSYoshinobu Inoue 			lt6->ia6t_pltime = new->ndpr_pltime;
71182cd038dSYoshinobu Inoue 			in6_init_address_ltimes(new, lt6);
71282cd038dSYoshinobu Inoue 		} else {
71382cd038dSYoshinobu Inoue #define	TWOHOUR		(120*60)
71482cd038dSYoshinobu Inoue 			/*
71582cd038dSYoshinobu Inoue 			 * We have seen the prefix before, and we have added
71682cd038dSYoshinobu Inoue 			 * interface address in the past.  We still have
71782cd038dSYoshinobu Inoue 			 * the interface address assigned.
71882cd038dSYoshinobu Inoue 			 *
71982cd038dSYoshinobu Inoue 			 * update address lifetime based on RFC 2462
72082cd038dSYoshinobu Inoue 			 * 5.5.3 (e).
72182cd038dSYoshinobu Inoue 			 */
72282cd038dSYoshinobu Inoue 			int update = 0;
72382cd038dSYoshinobu Inoue 
72482cd038dSYoshinobu Inoue 			lt6 = &ia6->ia6_lifetime;
72582cd038dSYoshinobu Inoue 
72682cd038dSYoshinobu Inoue 			/*
72782cd038dSYoshinobu Inoue 			 * update to RFC 2462 5.5.3 (e) from Jim Bound,
72882cd038dSYoshinobu Inoue 			 * (ipng 6712)
72982cd038dSYoshinobu Inoue 			 */
73082cd038dSYoshinobu Inoue 			if (TWOHOUR < new->ndpr_vltime
73182cd038dSYoshinobu Inoue 			 || lt6->ia6t_vltime < new->ndpr_vltime) {
73282cd038dSYoshinobu Inoue 				lt6->ia6t_vltime = new->ndpr_vltime;
73382cd038dSYoshinobu Inoue 				update++;
73482cd038dSYoshinobu Inoue 			} else if (auth) {
73582cd038dSYoshinobu Inoue 				lt6->ia6t_vltime = new->ndpr_vltime;
73682cd038dSYoshinobu Inoue 				update++;
73782cd038dSYoshinobu Inoue 			}
73882cd038dSYoshinobu Inoue 
73982cd038dSYoshinobu Inoue 			/* jim bound rule is not imposed for pref lifetime */
74082cd038dSYoshinobu Inoue 			lt6->ia6t_pltime = new->ndpr_pltime;
74182cd038dSYoshinobu Inoue 
74282cd038dSYoshinobu Inoue 			update++;
74382cd038dSYoshinobu Inoue 			if (update)
74482cd038dSYoshinobu Inoue 				in6_init_address_ltimes(new, lt6);
74582cd038dSYoshinobu Inoue 		}
74682cd038dSYoshinobu Inoue 
74782cd038dSYoshinobu Inoue  noautoconf1:
74882cd038dSYoshinobu Inoue 
74982cd038dSYoshinobu Inoue 		if (dr && pfxrtr_lookup(pr, dr) == NULL)
75082cd038dSYoshinobu Inoue 			pfxrtr_add(pr, dr);
75182cd038dSYoshinobu Inoue 	} else {
75282cd038dSYoshinobu Inoue 		int error_tmp;
75382cd038dSYoshinobu Inoue 
75482cd038dSYoshinobu Inoue 		if (new->ndpr_vltime == 0) goto end;
75582cd038dSYoshinobu Inoue 
75682cd038dSYoshinobu Inoue 		bzero(&new->ndpr_addr, sizeof(struct in6_addr));
75782cd038dSYoshinobu Inoue 
75882cd038dSYoshinobu Inoue 		/*
75982cd038dSYoshinobu Inoue 		 * RFC 2462 5.5.3 (d)
76082cd038dSYoshinobu Inoue 		 * We got a fresh prefix.  Perform some sanity checks
76182cd038dSYoshinobu Inoue 		 * and add an interface address by appending interface ID
76282cd038dSYoshinobu Inoue 		 * to the advertised prefix.
76382cd038dSYoshinobu Inoue 		 */
76482cd038dSYoshinobu Inoue 		if (!new->ndpr_raf_auto)
76582cd038dSYoshinobu Inoue 			goto noautoconf2;
76682cd038dSYoshinobu Inoue 
76782cd038dSYoshinobu Inoue 		ia6 = in6_ifadd(new->ndpr_ifp, &new->ndpr_prefix.sin6_addr,
76882cd038dSYoshinobu Inoue 			  &new->ndpr_addr, new->ndpr_plen);
76982cd038dSYoshinobu Inoue 		if (!ia6) {
77082cd038dSYoshinobu Inoue 			error = EADDRNOTAVAIL;
77182cd038dSYoshinobu Inoue 			log(LOG_ERR, "prelist_update: "
77282cd038dSYoshinobu Inoue 				"failed to add a new address\n");
77382cd038dSYoshinobu Inoue 			goto noautoconf2;
77482cd038dSYoshinobu Inoue 		}
77582cd038dSYoshinobu Inoue 		/* set onlink bit if an interface route is configured */
77682cd038dSYoshinobu Inoue 		new->ndpr_statef_onlink = (ia6->ia_flags & IFA_ROUTE) ? 1 : 0;
77782cd038dSYoshinobu Inoue 
77882cd038dSYoshinobu Inoue 		lt6 = &ia6->ia6_lifetime;
77982cd038dSYoshinobu Inoue 
78082cd038dSYoshinobu Inoue 		/* address lifetime <= prefix lifetime */
78182cd038dSYoshinobu Inoue 		lt6->ia6t_vltime = new->ndpr_vltime;
78282cd038dSYoshinobu Inoue 		lt6->ia6t_pltime = new->ndpr_pltime;
78382cd038dSYoshinobu Inoue 		in6_init_address_ltimes(new, lt6);
78482cd038dSYoshinobu Inoue 
78582cd038dSYoshinobu Inoue  noautoconf2:
78682cd038dSYoshinobu Inoue 		error_tmp = prelist_add(new, dr);
78782cd038dSYoshinobu Inoue 		error = error_tmp ? error_tmp : error;
78882cd038dSYoshinobu Inoue 	}
78982cd038dSYoshinobu Inoue 
79082cd038dSYoshinobu Inoue  end:
79182cd038dSYoshinobu Inoue 	splx(s);
79282cd038dSYoshinobu Inoue 	return error;
79382cd038dSYoshinobu Inoue }
79482cd038dSYoshinobu Inoue 
79582cd038dSYoshinobu Inoue /*
79682cd038dSYoshinobu Inoue  * Check if each prefix in the prefix list has at least one available router
79782cd038dSYoshinobu Inoue  * that advertised the prefix.
79882cd038dSYoshinobu Inoue  * If the check fails, the prefix may be off-link because, for example,
79982cd038dSYoshinobu Inoue  * we have moved from the network but the lifetime of the prefix has not
80082cd038dSYoshinobu Inoue  * been expired yet. So we should not use the prefix if there is another
80182cd038dSYoshinobu Inoue  * prefix that has an available router.
80282cd038dSYoshinobu Inoue  * But if there is no prefix that has an availble router, we still regards
80382cd038dSYoshinobu Inoue  * all the prefixes as on-link. This is because we can't tell if all the
80482cd038dSYoshinobu Inoue  * routers are simply dead or if we really moved from the network and there
80582cd038dSYoshinobu Inoue  * is no router around us.
80682cd038dSYoshinobu Inoue  */
80782cd038dSYoshinobu Inoue static void
80882cd038dSYoshinobu Inoue pfxlist_onlink_check()
80982cd038dSYoshinobu Inoue {
81082cd038dSYoshinobu Inoue 	struct nd_prefix *pr;
81182cd038dSYoshinobu Inoue 
81282cd038dSYoshinobu Inoue 	for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next)
81382cd038dSYoshinobu Inoue 		if (pr->ndpr_advrtrs.lh_first) /* pr has an available router */
81482cd038dSYoshinobu Inoue 			break;
81582cd038dSYoshinobu Inoue 
81682cd038dSYoshinobu Inoue 	if (pr) {
81782cd038dSYoshinobu Inoue 		/*
81882cd038dSYoshinobu Inoue 		 * There is at least one prefix that has a router. First,
81982cd038dSYoshinobu Inoue 		 * detach prefixes which has no advertising router and then
82082cd038dSYoshinobu Inoue 		 * attach other prefixes. The order is important since an
82182cd038dSYoshinobu Inoue 		 * attached prefix and a detached prefix may have a same
82282cd038dSYoshinobu Inoue 		 * interface route.
82382cd038dSYoshinobu Inoue 		 */
82482cd038dSYoshinobu Inoue 		for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
82582cd038dSYoshinobu Inoue 			if (pr->ndpr_advrtrs.lh_first == NULL &&
82682cd038dSYoshinobu Inoue 			    pr->ndpr_statef_onlink) {
82782cd038dSYoshinobu Inoue 				pr->ndpr_statef_onlink = 0;
82882cd038dSYoshinobu Inoue 				nd6_detach_prefix(pr);
82982cd038dSYoshinobu Inoue 			}
83082cd038dSYoshinobu Inoue 		}
83182cd038dSYoshinobu Inoue 		for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
83282cd038dSYoshinobu Inoue 			if (pr->ndpr_advrtrs.lh_first &&
83382cd038dSYoshinobu Inoue 				 pr->ndpr_statef_onlink == 0)
83482cd038dSYoshinobu Inoue 				nd6_attach_prefix(pr);
83582cd038dSYoshinobu Inoue 		}
83682cd038dSYoshinobu Inoue 	} else {
83782cd038dSYoshinobu Inoue 		/* there is no prefix that has a router */
83882cd038dSYoshinobu Inoue 		for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next)
83982cd038dSYoshinobu Inoue 			if (pr->ndpr_statef_onlink == 0)
84082cd038dSYoshinobu Inoue 				nd6_attach_prefix(pr);
84182cd038dSYoshinobu Inoue 	}
84282cd038dSYoshinobu Inoue }
84382cd038dSYoshinobu Inoue 
84482cd038dSYoshinobu Inoue static void
84582cd038dSYoshinobu Inoue nd6_detach_prefix(pr)
84682cd038dSYoshinobu Inoue 	struct nd_prefix *pr;
84782cd038dSYoshinobu Inoue {
84882cd038dSYoshinobu Inoue 	struct in6_ifaddr *ia6;
84982cd038dSYoshinobu Inoue 	struct sockaddr_in6 sa6, mask6;
85082cd038dSYoshinobu Inoue 
85182cd038dSYoshinobu Inoue 	/*
85282cd038dSYoshinobu Inoue 	 * Delete the interface route associated with the prefix.
85382cd038dSYoshinobu Inoue 	 */
85482cd038dSYoshinobu Inoue 	bzero(&sa6, sizeof(sa6));
85582cd038dSYoshinobu Inoue 	sa6.sin6_family = AF_INET6;
85682cd038dSYoshinobu Inoue 	sa6.sin6_len = sizeof(sa6);
85782cd038dSYoshinobu Inoue 	bcopy(&pr->ndpr_prefix.sin6_addr, &sa6.sin6_addr,
85882cd038dSYoshinobu Inoue 	      sizeof(struct in6_addr));
85982cd038dSYoshinobu Inoue 	bzero(&mask6, sizeof(mask6));
86082cd038dSYoshinobu Inoue 	mask6.sin6_family = AF_INET6;
86182cd038dSYoshinobu Inoue 	mask6.sin6_len = sizeof(sa6);
86282cd038dSYoshinobu Inoue 	bcopy(&pr->ndpr_mask, &mask6.sin6_addr, sizeof(struct in6_addr));
86382cd038dSYoshinobu Inoue 	{
86482cd038dSYoshinobu Inoue 		int e;
86582cd038dSYoshinobu Inoue 
86682cd038dSYoshinobu Inoue 		e = rtrequest(RTM_DELETE, (struct sockaddr *)&sa6, NULL,
86782cd038dSYoshinobu Inoue 			      (struct sockaddr *)&mask6, 0, NULL);
86882cd038dSYoshinobu Inoue 		if (e) {
86982cd038dSYoshinobu Inoue 			log(LOG_ERR,
87082cd038dSYoshinobu Inoue 			    "nd6_detach_prefix: failed to delete route: "
87182cd038dSYoshinobu Inoue 			    "%s/%d (errno = %d)\n",
87282cd038dSYoshinobu Inoue 			    ip6_sprintf(&sa6.sin6_addr),
87382cd038dSYoshinobu Inoue 			    pr->ndpr_plen,
87482cd038dSYoshinobu Inoue 			    e);
87582cd038dSYoshinobu Inoue 		}
87682cd038dSYoshinobu Inoue 	}
87782cd038dSYoshinobu Inoue 
87882cd038dSYoshinobu Inoue 	/*
87982cd038dSYoshinobu Inoue 	 * Mark the address derived from the prefix detached so that
88082cd038dSYoshinobu Inoue 	 * it won't be used as a source address for a new connection.
88182cd038dSYoshinobu Inoue 	 */
88282cd038dSYoshinobu Inoue 	if (IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr))
88382cd038dSYoshinobu Inoue 		ia6 = NULL;
88482cd038dSYoshinobu Inoue 	else
88582cd038dSYoshinobu Inoue 		ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr);
88682cd038dSYoshinobu Inoue 	if (ia6)
88782cd038dSYoshinobu Inoue 		ia6->ia6_flags |= IN6_IFF_DETACHED;
88882cd038dSYoshinobu Inoue }
88982cd038dSYoshinobu Inoue 
89082cd038dSYoshinobu Inoue static void
89182cd038dSYoshinobu Inoue nd6_attach_prefix(pr)
89282cd038dSYoshinobu Inoue 	struct nd_prefix *pr;
89382cd038dSYoshinobu Inoue {
89482cd038dSYoshinobu Inoue 	struct ifaddr *ifa;
89582cd038dSYoshinobu Inoue 	struct in6_ifaddr *ia6;
89682cd038dSYoshinobu Inoue 
89782cd038dSYoshinobu Inoue 	/*
89882cd038dSYoshinobu Inoue 	 * Add the interface route associated with the prefix(if necessary)
89982cd038dSYoshinobu Inoue 	 * Should we consider if the L bit is set in pr->ndpr_flags?
90082cd038dSYoshinobu Inoue 	 */
90182cd038dSYoshinobu Inoue 	ifa = ifaof_ifpforaddr((struct sockaddr *)&pr->ndpr_prefix,
90282cd038dSYoshinobu Inoue 			       pr->ndpr_ifp);
90382cd038dSYoshinobu Inoue 	if (ifa == NULL) {
90482cd038dSYoshinobu Inoue 		log(LOG_ERR,
90582cd038dSYoshinobu Inoue 		    "nd6_attach_prefix: failed to find any ifaddr"
90682cd038dSYoshinobu Inoue 		    " to add route for a prefix(%s/%d)\n",
90782cd038dSYoshinobu Inoue 		    ip6_sprintf(&pr->ndpr_addr), pr->ndpr_plen);
90882cd038dSYoshinobu Inoue 	} else {
90982cd038dSYoshinobu Inoue 		int e;
91082cd038dSYoshinobu Inoue 		struct sockaddr_in6 mask6;
91182cd038dSYoshinobu Inoue 
91282cd038dSYoshinobu Inoue 		bzero(&mask6, sizeof(mask6));
91382cd038dSYoshinobu Inoue 		mask6.sin6_family = AF_INET6;
91482cd038dSYoshinobu Inoue 		mask6.sin6_len = sizeof(mask6);
91582cd038dSYoshinobu Inoue 		mask6.sin6_addr = pr->ndpr_mask;
91682cd038dSYoshinobu Inoue 		e = rtrequest(RTM_ADD, (struct sockaddr *)&pr->ndpr_prefix,
91782cd038dSYoshinobu Inoue 			      ifa->ifa_addr, (struct sockaddr *)&mask6,
91882cd038dSYoshinobu Inoue 			      ifa->ifa_flags, NULL);
91982cd038dSYoshinobu Inoue 		if (e == 0)
92082cd038dSYoshinobu Inoue 			pr->ndpr_statef_onlink = 1;
92182cd038dSYoshinobu Inoue 		else {
92282cd038dSYoshinobu Inoue 			log(LOG_ERR,
92382cd038dSYoshinobu Inoue 			    "nd6_attach_prefix: failed to add route for"
92482cd038dSYoshinobu Inoue 			    " a prefix(%s/%d), errno = %d\n",
92582cd038dSYoshinobu Inoue 			    ip6_sprintf(&pr->ndpr_addr), pr->ndpr_plen, e);
92682cd038dSYoshinobu Inoue 		}
92782cd038dSYoshinobu Inoue 	}
92882cd038dSYoshinobu Inoue 
92982cd038dSYoshinobu Inoue 	/*
93082cd038dSYoshinobu Inoue 	 * Now the address derived from the prefix can be used as a source
93182cd038dSYoshinobu Inoue 	 * for a new connection, so clear the detached flag.
93282cd038dSYoshinobu Inoue 	 */
93382cd038dSYoshinobu Inoue 	if (IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr))
93482cd038dSYoshinobu Inoue 		ia6 = NULL;
93582cd038dSYoshinobu Inoue 	else
93682cd038dSYoshinobu Inoue 		ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr);
93782cd038dSYoshinobu Inoue 	if (ia6) {
93882cd038dSYoshinobu Inoue 		ia6->ia6_flags &= ~IN6_IFF_DETACHED;
93982cd038dSYoshinobu Inoue 		if (pr->ndpr_statef_onlink)
94082cd038dSYoshinobu Inoue 			ia6->ia_flags |= IFA_ROUTE;
94182cd038dSYoshinobu Inoue 	}
94282cd038dSYoshinobu Inoue }
94382cd038dSYoshinobu Inoue 
94482cd038dSYoshinobu Inoue static struct in6_ifaddr *
94582cd038dSYoshinobu Inoue in6_ifadd(ifp, in6, addr, prefixlen)
94682cd038dSYoshinobu Inoue 	struct ifnet *ifp;
94782cd038dSYoshinobu Inoue 	struct in6_addr *in6;
94882cd038dSYoshinobu Inoue 	struct in6_addr *addr;
94982cd038dSYoshinobu Inoue 	int prefixlen;	/* prefix len of the new prefix in "in6" */
95082cd038dSYoshinobu Inoue {
95182cd038dSYoshinobu Inoue 	struct ifaddr *ifa;
95282cd038dSYoshinobu Inoue 	struct in6_ifaddr *ia, *ib, *oia;
95382cd038dSYoshinobu Inoue 	int s, error;
95482cd038dSYoshinobu Inoue 	struct in6_addr mask;
95582cd038dSYoshinobu Inoue 
95682cd038dSYoshinobu Inoue 	in6_len2mask(&mask, prefixlen);
95782cd038dSYoshinobu Inoue 
95882cd038dSYoshinobu Inoue 	/* find link-local address (will be interface ID) */
95982cd038dSYoshinobu Inoue 	ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp);
96082cd038dSYoshinobu Inoue 	if (ifa)
96182cd038dSYoshinobu Inoue 		ib = (struct in6_ifaddr *)ifa;
96282cd038dSYoshinobu Inoue 	else
96382cd038dSYoshinobu Inoue 		return NULL;
96482cd038dSYoshinobu Inoue 
96582cd038dSYoshinobu Inoue 	/* prefixlen + ifidlen must be equal to 128 */
96682cd038dSYoshinobu Inoue 	if (prefixlen != in6_mask2len(&ib->ia_prefixmask.sin6_addr)) {
96782cd038dSYoshinobu Inoue 		log(LOG_ERR, "in6_ifadd: wrong prefixlen for %s"
96882cd038dSYoshinobu Inoue 			"(prefix=%d ifid=%d)\n", if_name(ifp),
96982cd038dSYoshinobu Inoue 			prefixlen,
97082cd038dSYoshinobu Inoue 			128 - in6_mask2len(&ib->ia_prefixmask.sin6_addr));
97182cd038dSYoshinobu Inoue 		return NULL;
97282cd038dSYoshinobu Inoue 	}
97382cd038dSYoshinobu Inoue 
97482cd038dSYoshinobu Inoue 	/* make ifaddr */
97582cd038dSYoshinobu Inoue 	ia = (struct in6_ifaddr *)malloc(sizeof(*ia), M_IFADDR, M_DONTWAIT);
97682cd038dSYoshinobu Inoue 	if (ia == NULL) {
97782cd038dSYoshinobu Inoue 		printf("ENOBUFS in in6_ifadd %d\n", __LINE__);
97882cd038dSYoshinobu Inoue 		return NULL;
97982cd038dSYoshinobu Inoue 	}
98082cd038dSYoshinobu Inoue 
98182cd038dSYoshinobu Inoue 	bzero((caddr_t)ia, sizeof(*ia));
98282cd038dSYoshinobu Inoue 	ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr;
98382cd038dSYoshinobu Inoue 	ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr;
98482cd038dSYoshinobu Inoue 	ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask;
98582cd038dSYoshinobu Inoue 	ia->ia_ifp = ifp;
98682cd038dSYoshinobu Inoue 
98782cd038dSYoshinobu Inoue 	/* link to in6_ifaddr */
98882cd038dSYoshinobu Inoue 	if ((oia = in6_ifaddr) != NULL) {
98982cd038dSYoshinobu Inoue 		for( ; oia->ia_next; oia = oia->ia_next)
99082cd038dSYoshinobu Inoue 			continue;
99182cd038dSYoshinobu Inoue 		oia->ia_next = ia;
99282cd038dSYoshinobu Inoue 	} else
99382cd038dSYoshinobu Inoue 		in6_ifaddr = ia;
99482cd038dSYoshinobu Inoue 
99582cd038dSYoshinobu Inoue 	/* link to if_addrlist */
99682cd038dSYoshinobu Inoue 	if (ifp->if_addrlist.tqh_first != NULL) {
99782cd038dSYoshinobu Inoue 		TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)ia,
99882cd038dSYoshinobu Inoue 			ifa_list);
99982cd038dSYoshinobu Inoue 	}
100082cd038dSYoshinobu Inoue 
100182cd038dSYoshinobu Inoue 	/* new address */
100282cd038dSYoshinobu Inoue 	ia->ia_addr.sin6_len = sizeof(struct sockaddr_in6);
100382cd038dSYoshinobu Inoue 	ia->ia_addr.sin6_family = AF_INET6;
100482cd038dSYoshinobu Inoue 	/* prefix */
100582cd038dSYoshinobu Inoue 	bcopy(in6, &ia->ia_addr.sin6_addr, sizeof(ia->ia_addr.sin6_addr));
100682cd038dSYoshinobu Inoue 	ia->ia_addr.sin6_addr.s6_addr32[0] &= mask.s6_addr32[0];
100782cd038dSYoshinobu Inoue 	ia->ia_addr.sin6_addr.s6_addr32[1] &= mask.s6_addr32[1];
100882cd038dSYoshinobu Inoue 	ia->ia_addr.sin6_addr.s6_addr32[2] &= mask.s6_addr32[2];
100982cd038dSYoshinobu Inoue 	ia->ia_addr.sin6_addr.s6_addr32[3] &= mask.s6_addr32[3];
101082cd038dSYoshinobu Inoue 	/* interface ID */
101182cd038dSYoshinobu Inoue 	ia->ia_addr.sin6_addr.s6_addr32[0]
101282cd038dSYoshinobu Inoue 		|= (ib->ia_addr.sin6_addr.s6_addr32[0] & ~mask.s6_addr32[0]);
101382cd038dSYoshinobu Inoue 	ia->ia_addr.sin6_addr.s6_addr32[1]
101482cd038dSYoshinobu Inoue 		|= (ib->ia_addr.sin6_addr.s6_addr32[1] & ~mask.s6_addr32[1]);
101582cd038dSYoshinobu Inoue 	ia->ia_addr.sin6_addr.s6_addr32[2]
101682cd038dSYoshinobu Inoue 		|= (ib->ia_addr.sin6_addr.s6_addr32[2] & ~mask.s6_addr32[2]);
101782cd038dSYoshinobu Inoue 	ia->ia_addr.sin6_addr.s6_addr32[3]
101882cd038dSYoshinobu Inoue 		|= (ib->ia_addr.sin6_addr.s6_addr32[3] & ~mask.s6_addr32[3]);
101982cd038dSYoshinobu Inoue 
102082cd038dSYoshinobu Inoue 	/* new prefix */
102182cd038dSYoshinobu Inoue 	ia->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6);
102282cd038dSYoshinobu Inoue 	ia->ia_prefixmask.sin6_family = AF_INET6;
102382cd038dSYoshinobu Inoue 	bcopy(&mask, &ia->ia_prefixmask.sin6_addr,
102482cd038dSYoshinobu Inoue 		sizeof(ia->ia_prefixmask.sin6_addr));
102582cd038dSYoshinobu Inoue 
102682cd038dSYoshinobu Inoue 	/* same routine */
102782cd038dSYoshinobu Inoue 	ia->ia_ifa.ifa_rtrequest =
102882cd038dSYoshinobu Inoue 		(ifp->if_type == IFT_PPP) ? nd6_p2p_rtrequest : nd6_rtrequest;
102982cd038dSYoshinobu Inoue 	ia->ia_ifa.ifa_flags |= RTF_CLONING;
103082cd038dSYoshinobu Inoue 	ia->ia_ifa.ifa_metric = ifp->if_metric;
103182cd038dSYoshinobu Inoue 
103282cd038dSYoshinobu Inoue 	/* add interface route */
103382cd038dSYoshinobu Inoue 	if ((error = rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_UP|RTF_CLONING))) {
103482cd038dSYoshinobu Inoue 		log(LOG_NOTICE, "in6_ifadd: failed to add an interface route "
103582cd038dSYoshinobu Inoue 		    "for %s/%d on %s, errno = %d\n",
103682cd038dSYoshinobu Inoue 		    ip6_sprintf(&ia->ia_addr.sin6_addr), prefixlen,
103782cd038dSYoshinobu Inoue 		    if_name(ifp), error);
103882cd038dSYoshinobu Inoue 	} else
103982cd038dSYoshinobu Inoue 		ia->ia_flags |= IFA_ROUTE;
104082cd038dSYoshinobu Inoue 
104182cd038dSYoshinobu Inoue 	*addr = ia->ia_addr.sin6_addr;
104282cd038dSYoshinobu Inoue 
104382cd038dSYoshinobu Inoue 	if (ifp->if_flags & IFF_MULTICAST) {
104482cd038dSYoshinobu Inoue 		int error;	/* not used */
104582cd038dSYoshinobu Inoue 		struct in6_addr sol6;
104682cd038dSYoshinobu Inoue 
104782cd038dSYoshinobu Inoue 		/* join solicited node multicast address */
104882cd038dSYoshinobu Inoue 		bzero(&sol6, sizeof(sol6));
104982cd038dSYoshinobu Inoue 		sol6.s6_addr16[0] = htons(0xff02);
105082cd038dSYoshinobu Inoue 		sol6.s6_addr16[1] = htons(ifp->if_index);
105182cd038dSYoshinobu Inoue 		sol6.s6_addr32[1] = 0;
105282cd038dSYoshinobu Inoue 		sol6.s6_addr32[2] = htonl(1);
105382cd038dSYoshinobu Inoue 		sol6.s6_addr32[3] = ia->ia_addr.sin6_addr.s6_addr32[3];
105482cd038dSYoshinobu Inoue 		sol6.s6_addr8[12] = 0xff;
105582cd038dSYoshinobu Inoue 		(void)in6_addmulti(&sol6, ifp, &error);
105682cd038dSYoshinobu Inoue 	}
105782cd038dSYoshinobu Inoue 
105882cd038dSYoshinobu Inoue 	ia->ia6_flags |= IN6_IFF_TENTATIVE;
105982cd038dSYoshinobu Inoue 
106082cd038dSYoshinobu Inoue 	/*
106182cd038dSYoshinobu Inoue 	 * To make the interface up. Only AF_INET6 in ia is used...
106282cd038dSYoshinobu Inoue 	 */
106382cd038dSYoshinobu Inoue 	s = splimp();
106482cd038dSYoshinobu Inoue 	if (ifp->if_ioctl && (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia)) {
106582cd038dSYoshinobu Inoue 		splx(s);
106682cd038dSYoshinobu Inoue 		return NULL;
106782cd038dSYoshinobu Inoue 	}
106882cd038dSYoshinobu Inoue 	splx(s);
106982cd038dSYoshinobu Inoue 
107082cd038dSYoshinobu Inoue 	/* Perform DAD, if needed. */
107182cd038dSYoshinobu Inoue 	nd6_dad_start((struct ifaddr *)ia, NULL);
107282cd038dSYoshinobu Inoue 
107382cd038dSYoshinobu Inoue 	return ia;
107482cd038dSYoshinobu Inoue }
107582cd038dSYoshinobu Inoue 
107682cd038dSYoshinobu Inoue int
107782cd038dSYoshinobu Inoue in6_ifdel(ifp, in6)
107882cd038dSYoshinobu Inoue 	struct ifnet *ifp;
107982cd038dSYoshinobu Inoue 	struct in6_addr *in6;
108082cd038dSYoshinobu Inoue {
108182cd038dSYoshinobu Inoue 	struct in6_ifaddr *ia = (struct in6_ifaddr *)NULL;
108282cd038dSYoshinobu Inoue 	struct in6_ifaddr *oia = (struct in6_ifaddr *)NULL;
108382cd038dSYoshinobu Inoue 
108482cd038dSYoshinobu Inoue 	if (!ifp)
108582cd038dSYoshinobu Inoue 		return -1;
108682cd038dSYoshinobu Inoue 
108782cd038dSYoshinobu Inoue 	ia = in6ifa_ifpwithaddr(ifp, in6);
108882cd038dSYoshinobu Inoue 	if (!ia)
108982cd038dSYoshinobu Inoue 		return -1;
109082cd038dSYoshinobu Inoue 
109182cd038dSYoshinobu Inoue 	if (ifp->if_flags & IFF_MULTICAST) {
109282cd038dSYoshinobu Inoue 		/*
109382cd038dSYoshinobu Inoue 		 * delete solicited multicast addr for deleting host id
109482cd038dSYoshinobu Inoue 		 */
109582cd038dSYoshinobu Inoue 		struct in6_multi *in6m;
109682cd038dSYoshinobu Inoue 		struct in6_addr llsol;
109782cd038dSYoshinobu Inoue 		bzero(&llsol, sizeof(struct in6_addr));
109882cd038dSYoshinobu Inoue 		llsol.s6_addr16[0] = htons(0xff02);
109982cd038dSYoshinobu Inoue 		llsol.s6_addr16[1] = htons(ifp->if_index);
110082cd038dSYoshinobu Inoue 		llsol.s6_addr32[1] = 0;
110182cd038dSYoshinobu Inoue 		llsol.s6_addr32[2] = htonl(1);
110282cd038dSYoshinobu Inoue 		llsol.s6_addr32[3] =
110382cd038dSYoshinobu Inoue 				ia->ia_addr.sin6_addr.s6_addr32[3];
110482cd038dSYoshinobu Inoue 		llsol.s6_addr8[12] = 0xff;
110582cd038dSYoshinobu Inoue 
110682cd038dSYoshinobu Inoue 		IN6_LOOKUP_MULTI(llsol, ifp, in6m);
110782cd038dSYoshinobu Inoue 		if (in6m)
110882cd038dSYoshinobu Inoue 			in6_delmulti(in6m);
110982cd038dSYoshinobu Inoue 	}
111082cd038dSYoshinobu Inoue 
111182cd038dSYoshinobu Inoue 	if (ia->ia_flags & IFA_ROUTE) {
111282cd038dSYoshinobu Inoue 		rtinit(&(ia->ia_ifa), (int)RTM_DELETE, 0);
111382cd038dSYoshinobu Inoue 		ia->ia_flags &= ~IFA_ROUTE;
111482cd038dSYoshinobu Inoue 	}
111582cd038dSYoshinobu Inoue 
111682cd038dSYoshinobu Inoue 	TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list);
111782cd038dSYoshinobu Inoue 
111882cd038dSYoshinobu Inoue 	/* lladdr is never deleted */
111982cd038dSYoshinobu Inoue 	oia = ia;
112082cd038dSYoshinobu Inoue 	if (oia == (ia = in6_ifaddr))
112182cd038dSYoshinobu Inoue 		in6_ifaddr = ia->ia_next;
112282cd038dSYoshinobu Inoue 	else {
112382cd038dSYoshinobu Inoue 		while (ia->ia_next && (ia->ia_next != oia))
112482cd038dSYoshinobu Inoue 			ia = ia->ia_next;
112582cd038dSYoshinobu Inoue 		if (ia->ia_next)
112682cd038dSYoshinobu Inoue 			ia->ia_next = oia->ia_next;
112782cd038dSYoshinobu Inoue 		else
112882cd038dSYoshinobu Inoue 			return -1;
112982cd038dSYoshinobu Inoue 	}
113082cd038dSYoshinobu Inoue 
113182cd038dSYoshinobu Inoue 	IFAFREE((&oia->ia_ifa));
113282cd038dSYoshinobu Inoue /* xxx
113382cd038dSYoshinobu Inoue 	rtrequest(RTM_DELETE,
113482cd038dSYoshinobu Inoue 		  (struct sockaddr *)&ia->ia_addr,
113582cd038dSYoshinobu Inoue 		  (struct sockaddr *)0
113682cd038dSYoshinobu Inoue 		  (struct sockaddr *)&ia->ia_prefixmask,
113782cd038dSYoshinobu Inoue 		  RTF_UP|RTF_CLONING,
113882cd038dSYoshinobu Inoue 		  (struct rtentry **)0);
113982cd038dSYoshinobu Inoue */
114082cd038dSYoshinobu Inoue 	return 0;
114182cd038dSYoshinobu Inoue }
114282cd038dSYoshinobu Inoue 
114382cd038dSYoshinobu Inoue int
114482cd038dSYoshinobu Inoue in6_init_prefix_ltimes(struct nd_prefix *ndpr)
114582cd038dSYoshinobu Inoue {
114682cd038dSYoshinobu Inoue 
114782cd038dSYoshinobu Inoue 	/* check if preferred lifetime > valid lifetime */
114882cd038dSYoshinobu Inoue 	if (ndpr->ndpr_pltime > ndpr->ndpr_vltime) {
114982cd038dSYoshinobu Inoue 		log(LOG_INFO, "in6_init_prefix_ltimes: preferred lifetime"
115082cd038dSYoshinobu Inoue 		    "(%d) is greater than valid lifetime(%d)\n",
115182cd038dSYoshinobu Inoue 		    (u_int)ndpr->ndpr_pltime, (u_int)ndpr->ndpr_vltime);
115282cd038dSYoshinobu Inoue 		return (EINVAL);
115382cd038dSYoshinobu Inoue 	}
115482cd038dSYoshinobu Inoue 	if (ndpr->ndpr_pltime == ND6_INFINITE_LIFETIME)
115582cd038dSYoshinobu Inoue 		ndpr->ndpr_preferred = 0;
115682cd038dSYoshinobu Inoue 	else
115782cd038dSYoshinobu Inoue 		ndpr->ndpr_preferred = time_second + ndpr->ndpr_pltime;
115882cd038dSYoshinobu Inoue 	if (ndpr->ndpr_vltime == ND6_INFINITE_LIFETIME)
115982cd038dSYoshinobu Inoue 		ndpr->ndpr_expire = 0;
116082cd038dSYoshinobu Inoue 	else
116182cd038dSYoshinobu Inoue 		ndpr->ndpr_expire = time_second + ndpr->ndpr_vltime;
116282cd038dSYoshinobu Inoue 
116382cd038dSYoshinobu Inoue 	return 0;
116482cd038dSYoshinobu Inoue }
116582cd038dSYoshinobu Inoue 
116682cd038dSYoshinobu Inoue static void
116782cd038dSYoshinobu Inoue in6_init_address_ltimes(struct nd_prefix *new, struct in6_addrlifetime *lt6)
116882cd038dSYoshinobu Inoue {
116982cd038dSYoshinobu Inoue 
117082cd038dSYoshinobu Inoue 	/* init ia6t_expire */
117182cd038dSYoshinobu Inoue 	if (lt6->ia6t_vltime == ND6_INFINITE_LIFETIME)
117282cd038dSYoshinobu Inoue 		lt6->ia6t_expire = 0;
117382cd038dSYoshinobu Inoue 	else {
117482cd038dSYoshinobu Inoue 		lt6->ia6t_expire = time_second;
117582cd038dSYoshinobu Inoue 		lt6->ia6t_expire += lt6->ia6t_vltime;
117682cd038dSYoshinobu Inoue 	}
117782cd038dSYoshinobu Inoue 	/* init ia6t_preferred */
117882cd038dSYoshinobu Inoue 	if (lt6->ia6t_pltime == ND6_INFINITE_LIFETIME)
117982cd038dSYoshinobu Inoue 		lt6->ia6t_preferred = 0;
118082cd038dSYoshinobu Inoue 	else {
118182cd038dSYoshinobu Inoue 		lt6->ia6t_preferred = time_second;
118282cd038dSYoshinobu Inoue 		lt6->ia6t_preferred += lt6->ia6t_pltime;
118382cd038dSYoshinobu Inoue 	}
118482cd038dSYoshinobu Inoue 	/* ensure addr lifetime <= prefix lifetime */
118582cd038dSYoshinobu Inoue 	if (new->ndpr_expire && lt6->ia6t_expire &&
118682cd038dSYoshinobu Inoue 	    new->ndpr_expire < lt6->ia6t_expire)
118782cd038dSYoshinobu Inoue 		lt6->ia6t_expire = new->ndpr_expire;
118882cd038dSYoshinobu Inoue 	if (new->ndpr_preferred && lt6->ia6t_preferred
118982cd038dSYoshinobu Inoue 	    && new->ndpr_preferred < lt6->ia6t_preferred)
119082cd038dSYoshinobu Inoue 		lt6->ia6t_preferred = new->ndpr_preferred;
119182cd038dSYoshinobu Inoue }
119282cd038dSYoshinobu Inoue 
119382cd038dSYoshinobu Inoue /*
119482cd038dSYoshinobu Inoue  * Delete all the routing table entries that use the specified gateway.
119582cd038dSYoshinobu Inoue  * XXX: this function causes search through all entries of routing table, so
119682cd038dSYoshinobu Inoue  * it shouldn't be called when acting as a router.
119782cd038dSYoshinobu Inoue  */
119882cd038dSYoshinobu Inoue void
119982cd038dSYoshinobu Inoue rt6_flush(gateway, ifp)
120082cd038dSYoshinobu Inoue     struct in6_addr *gateway;
120182cd038dSYoshinobu Inoue     struct ifnet *ifp;
120282cd038dSYoshinobu Inoue {
120382cd038dSYoshinobu Inoue 	struct radix_node_head *rnh = rt_tables[AF_INET6];
120482cd038dSYoshinobu Inoue 	int s = splnet();
120582cd038dSYoshinobu Inoue 
120682cd038dSYoshinobu Inoue 	/* We'll care only link-local addresses */
120782cd038dSYoshinobu Inoue 	if (!IN6_IS_ADDR_LINKLOCAL(gateway)) {
120882cd038dSYoshinobu Inoue 		splx(s);
120982cd038dSYoshinobu Inoue 		return;
121082cd038dSYoshinobu Inoue 	}
121182cd038dSYoshinobu Inoue 	/* XXX: hack for KAME's link-local address kludge */
121282cd038dSYoshinobu Inoue 	gateway->s6_addr16[1] = htons(ifp->if_index);
121382cd038dSYoshinobu Inoue 
121482cd038dSYoshinobu Inoue 	rnh->rnh_walktree(rnh, rt6_deleteroute, (void *)gateway);
121582cd038dSYoshinobu Inoue 	splx(s);
121682cd038dSYoshinobu Inoue }
121782cd038dSYoshinobu Inoue 
121882cd038dSYoshinobu Inoue static int
121982cd038dSYoshinobu Inoue rt6_deleteroute(rn, arg)
122082cd038dSYoshinobu Inoue 	struct radix_node *rn;
122182cd038dSYoshinobu Inoue 	void *arg;
122282cd038dSYoshinobu Inoue {
122382cd038dSYoshinobu Inoue #define SIN6(s)	((struct sockaddr_in6 *)s)
122482cd038dSYoshinobu Inoue 	struct rtentry *rt = (struct rtentry *)rn;
122582cd038dSYoshinobu Inoue 	struct in6_addr *gate = (struct in6_addr *)arg;
122682cd038dSYoshinobu Inoue 
122782cd038dSYoshinobu Inoue 	if (rt->rt_gateway == NULL || rt->rt_gateway->sa_family != AF_INET6)
122882cd038dSYoshinobu Inoue 		return(0);
122982cd038dSYoshinobu Inoue 
123082cd038dSYoshinobu Inoue 	if (!IN6_ARE_ADDR_EQUAL(gate, &SIN6(rt->rt_gateway)->sin6_addr))
123182cd038dSYoshinobu Inoue 		return(0);
123282cd038dSYoshinobu Inoue 
123382cd038dSYoshinobu Inoue 	/*
123482cd038dSYoshinobu Inoue 	 * We delete only host route. This means, in particular, we don't
123582cd038dSYoshinobu Inoue 	 * delete default route.
123682cd038dSYoshinobu Inoue 	 */
123782cd038dSYoshinobu Inoue 	if ((rt->rt_flags & RTF_HOST) == 0)
123882cd038dSYoshinobu Inoue 		return(0);
123982cd038dSYoshinobu Inoue 
124082cd038dSYoshinobu Inoue 	return(rtrequest(RTM_DELETE, rt_key(rt),
124182cd038dSYoshinobu Inoue 			 rt->rt_gateway, rt_mask(rt), rt->rt_flags, 0));
124282cd038dSYoshinobu Inoue #undef SIN6
124382cd038dSYoshinobu Inoue }
1244