nd6_rtr.c revision 121161
162587Sitojun/*	$FreeBSD: head/sys/netinet6/nd6_rtr.c 121161 2003-10-17 15:46:31Z ume $	*/
278064Sume/*	$KAME: nd6_rtr.c,v 1.111 2001/04/27 01:37:15 jinmei Exp $	*/
362587Sitojun
453541Sshin/*
553541Sshin * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
653541Sshin * All rights reserved.
753541Sshin *
853541Sshin * Redistribution and use in source and binary forms, with or without
953541Sshin * modification, are permitted provided that the following conditions
1053541Sshin * are met:
1153541Sshin * 1. Redistributions of source code must retain the above copyright
1253541Sshin *    notice, this list of conditions and the following disclaimer.
1353541Sshin * 2. Redistributions in binary form must reproduce the above copyright
1453541Sshin *    notice, this list of conditions and the following disclaimer in the
1553541Sshin *    documentation and/or other materials provided with the distribution.
1653541Sshin * 3. Neither the name of the project nor the names of its contributors
1753541Sshin *    may be used to endorse or promote products derived from this software
1853541Sshin *    without specific prior written permission.
1953541Sshin *
2053541Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
2153541Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2253541Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2353541Sshin * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2453541Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2553541Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2653541Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2753541Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2853541Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2953541Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3053541Sshin * SUCH DAMAGE.
3153541Sshin */
3253541Sshin
3362587Sitojun#include "opt_inet.h"
3462587Sitojun#include "opt_inet6.h"
3562587Sitojun
3653541Sshin#include <sys/param.h>
3753541Sshin#include <sys/systm.h>
3853541Sshin#include <sys/malloc.h>
3953541Sshin#include <sys/mbuf.h>
4053541Sshin#include <sys/socket.h>
4153541Sshin#include <sys/sockio.h>
4253541Sshin#include <sys/time.h>
4378064Sume#include <sys/kernel.h>
4453541Sshin#include <sys/errno.h>
4553541Sshin#include <sys/syslog.h>
4678064Sume#include <sys/queue.h>
4753541Sshin
4853541Sshin#include <net/if.h>
4953541Sshin#include <net/if_types.h>
5053541Sshin#include <net/if_dl.h>
5153541Sshin#include <net/route.h>
5253541Sshin#include <net/radix.h>
5353541Sshin
5453541Sshin#include <netinet/in.h>
5553541Sshin#include <netinet6/in6_var.h>
5678064Sume#include <netinet6/in6_ifattach.h>
5762587Sitojun#include <netinet/ip6.h>
5853541Sshin#include <netinet6/ip6_var.h>
5953541Sshin#include <netinet6/nd6.h>
6062587Sitojun#include <netinet/icmp6.h>
6162587Sitojun#include <netinet6/scope6_var.h>
6253541Sshin
6353541Sshin#include <net/net_osdep.h>
6453541Sshin
6562587Sitojun#define SDL(s)	((struct sockaddr_dl *)s)
6653541Sshin
6762587Sitojunstatic struct nd_defrouter *defrtrlist_update __P((struct nd_defrouter *));
6878064Sumestatic struct in6_ifaddr *in6_ifadd __P((struct nd_prefix *,
6978064Sume	struct in6_addr *));
7062587Sitojunstatic struct nd_pfxrouter *pfxrtr_lookup __P((struct nd_prefix *,
7178064Sume	struct nd_defrouter *));
7262587Sitojunstatic void pfxrtr_add __P((struct nd_prefix *, struct nd_defrouter *));
7362587Sitojunstatic void pfxrtr_del __P((struct nd_pfxrouter *));
7462587Sitojunstatic struct nd_pfxrouter *find_pfxlist_reachable_router
7578064Sume	__P((struct nd_prefix *));
7662587Sitojunstatic void defrouter_addifreq __P((struct ifnet *));
7778064Sumestatic void nd6_rtmsg __P((int, struct rtentry *));
7853541Sshin
79120941Sumestatic void in6_init_address_ltimes __P((struct nd_prefix *,
80120941Sume	struct in6_addrlifetime *));
8153541Sshin
8262587Sitojunstatic int rt6_deleteroute __P((struct radix_node *, void *));
8353541Sshin
8462587Sitojunextern int nd6_recalc_reachtm_interval;
8553541Sshin
8678064Sumestatic struct ifnet *nd6_defifp;
8762587Sitojunint nd6_defifindex;
8862587Sitojun
8978064Sumeint ip6_use_tempaddr = 0;
9078064Sume
9178064Sumeint ip6_desync_factor;
9278064Sumeu_int32_t ip6_temp_preferred_lifetime = DEF_TEMP_PREFERRED_LIFETIME;
9378064Sumeu_int32_t ip6_temp_valid_lifetime = DEF_TEMP_VALID_LIFETIME;
9453541Sshin/*
9578064Sume * shorter lifetimes for debugging purposes.
9678064Sumeint ip6_temp_preferred_lifetime = 800;
9778064Sumestatic int ip6_temp_valid_lifetime = 1800;
9878064Sume*/
9978064Sumeint ip6_temp_regen_advance = TEMPADDR_REGEN_ADVANCE;
10078064Sume
10178064Sume/*
10253541Sshin * Receive Router Solicitation Message - just for routers.
10353541Sshin * Router solicitation/advertisement is mostly managed by userland program
10453541Sshin * (rtadvd) so here we have no function like nd6_ra_output().
10553541Sshin *
10653541Sshin * Based on RFC 2461
10753541Sshin */
10853541Sshinvoid
10953541Sshinnd6_rs_input(m, off, icmp6len)
11053541Sshin	struct	mbuf *m;
11153541Sshin	int off, icmp6len;
11253541Sshin{
11353541Sshin	struct ifnet *ifp = m->m_pkthdr.rcvif;
11453541Sshin	struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
11562587Sitojun	struct nd_router_solicit *nd_rs;
11653541Sshin	struct in6_addr saddr6 = ip6->ip6_src;
11753541Sshin	char *lladdr = NULL;
11853541Sshin	int lladdrlen = 0;
11962587Sitojun#if 0
12062587Sitojun	struct sockaddr_dl *sdl = (struct sockaddr_dl *)NULL;
12162587Sitojun	struct llinfo_nd6 *ln = (struct llinfo_nd6 *)NULL;
12262587Sitojun	struct rtentry *rt = NULL;
12362587Sitojun	int is_newentry;
12462587Sitojun#endif
12553541Sshin	union nd_opts ndopts;
12653541Sshin
12753541Sshin	/* If I'm not a router, ignore it. */
12853541Sshin	if (ip6_accept_rtadv != 0 || ip6_forwarding != 1)
12962587Sitojun		goto freeit;
13053541Sshin
13153541Sshin	/* Sanity checks */
13253541Sshin	if (ip6->ip6_hlim != 255) {
13378064Sume		nd6log((LOG_ERR,
13478064Sume		    "nd6_rs_input: invalid hlim (%d) from %s to %s on %s\n",
13578064Sume		    ip6->ip6_hlim, ip6_sprintf(&ip6->ip6_src),
13678064Sume		    ip6_sprintf(&ip6->ip6_dst), if_name(ifp)));
13778064Sume		goto bad;
13853541Sshin	}
13953541Sshin
14053541Sshin	/*
14153541Sshin	 * Don't update the neighbor cache, if src = ::.
14253541Sshin	 * This indicates that the src has no IP address assigned yet.
14353541Sshin	 */
14453541Sshin	if (IN6_IS_ADDR_UNSPECIFIED(&saddr6))
14562587Sitojun		goto freeit;
14662587Sitojun
14762587Sitojun#ifndef PULLDOWN_TEST
14862587Sitojun	IP6_EXTHDR_CHECK(m, off, icmp6len,);
14962587Sitojun	nd_rs = (struct nd_router_solicit *)((caddr_t)ip6 + off);
15062587Sitojun#else
15162587Sitojun	IP6_EXTHDR_GET(nd_rs, struct nd_router_solicit *, m, off, icmp6len);
15262587Sitojun	if (nd_rs == NULL) {
15362587Sitojun		icmp6stat.icp6s_tooshort++;
15453541Sshin		return;
15562587Sitojun	}
15662587Sitojun#endif
15753541Sshin
15853541Sshin	icmp6len -= sizeof(*nd_rs);
15953541Sshin	nd6_option_init(nd_rs + 1, icmp6len, &ndopts);
16053541Sshin	if (nd6_options(&ndopts) < 0) {
16178064Sume		nd6log((LOG_INFO,
16278064Sume		    "nd6_rs_input: invalid ND option, ignored\n"));
16378064Sume		/* nd6_options have incremented stats */
16462587Sitojun		goto freeit;
16553541Sshin	}
16653541Sshin
16753541Sshin	if (ndopts.nd_opts_src_lladdr) {
16853541Sshin		lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1);
16953541Sshin		lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3;
17053541Sshin	}
17153541Sshin
17253541Sshin	if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) {
17378064Sume		nd6log((LOG_INFO,
17453541Sshin		    "nd6_rs_input: lladdrlen mismatch for %s "
17553541Sshin		    "(if %d, RS packet %d)\n",
176120941Sume		    ip6_sprintf(&saddr6),
177120941Sume		    ifp->if_addrlen, lladdrlen - 2));
17878064Sume		goto bad;
17953541Sshin	}
18053541Sshin
18153541Sshin	nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_ROUTER_SOLICIT, 0);
18262587Sitojun
18362587Sitojun freeit:
18462587Sitojun	m_freem(m);
18578064Sume	return;
18678064Sume
18778064Sume bad:
18878064Sume	icmp6stat.icp6s_badrs++;
18978064Sume	m_freem(m);
19053541Sshin}
19153541Sshin
19253541Sshin/*
19353541Sshin * Receive Router Advertisement Message.
19453541Sshin *
19553541Sshin * Based on RFC 2461
19653541Sshin * TODO: on-link bit on prefix information
19753541Sshin * TODO: ND_RA_FLAG_{OTHER,MANAGED} processing
19853541Sshin */
19953541Sshinvoid
20053541Sshinnd6_ra_input(m, off, icmp6len)
20153541Sshin	struct	mbuf *m;
20253541Sshin	int off, icmp6len;
20353541Sshin{
20453541Sshin	struct ifnet *ifp = m->m_pkthdr.rcvif;
205121161Sume	struct nd_ifinfo *ndi = ND_IFINFO(ifp);
20653541Sshin	struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
20762587Sitojun	struct nd_router_advert *nd_ra;
20853541Sshin	struct in6_addr saddr6 = ip6->ip6_src;
20962587Sitojun#if 0
21062587Sitojun	struct in6_addr daddr6 = ip6->ip6_dst;
21162587Sitojun	int flags; /* = nd_ra->nd_ra_flags_reserved; */
21262587Sitojun	int is_managed = ((flags & ND_RA_FLAG_MANAGED) != 0);
21362587Sitojun	int is_other = ((flags & ND_RA_FLAG_OTHER) != 0);
21462587Sitojun#endif
21553541Sshin	union nd_opts ndopts;
21653541Sshin	struct nd_defrouter *dr;
21753541Sshin
218118498Sume	/*
219118498Sume	 * We only accept RAs only when
220118498Sume	 * the system-wide variable allows the acceptance, and
221118498Sume	 * per-interface variable allows RAs on the receiving interface.
222118498Sume	 */
22353541Sshin	if (ip6_accept_rtadv == 0)
22462587Sitojun		goto freeit;
225118498Sume	if (!(ndi->flags & ND6_IFF_ACCEPT_RTADV))
226118498Sume		goto freeit;
22753541Sshin
22853541Sshin	if (ip6->ip6_hlim != 255) {
22978064Sume		nd6log((LOG_ERR,
23078064Sume		    "nd6_ra_input: invalid hlim (%d) from %s to %s on %s\n",
23178064Sume		    ip6->ip6_hlim, ip6_sprintf(&ip6->ip6_src),
23278064Sume		    ip6_sprintf(&ip6->ip6_dst), if_name(ifp)));
23378064Sume		goto bad;
23453541Sshin	}
23553541Sshin
23653541Sshin	if (!IN6_IS_ADDR_LINKLOCAL(&saddr6)) {
23778064Sume		nd6log((LOG_ERR,
23853541Sshin		    "nd6_ra_input: src %s is not link-local\n",
23978064Sume		    ip6_sprintf(&saddr6)));
24078064Sume		goto bad;
24162587Sitojun	}
24262587Sitojun
24362587Sitojun#ifndef PULLDOWN_TEST
24462587Sitojun	IP6_EXTHDR_CHECK(m, off, icmp6len,);
24562587Sitojun	nd_ra = (struct nd_router_advert *)((caddr_t)ip6 + off);
24662587Sitojun#else
24762587Sitojun	IP6_EXTHDR_GET(nd_ra, struct nd_router_advert *, m, off, icmp6len);
24862587Sitojun	if (nd_ra == NULL) {
24962587Sitojun		icmp6stat.icp6s_tooshort++;
25053541Sshin		return;
25153541Sshin	}
25262587Sitojun#endif
25353541Sshin
25453541Sshin	icmp6len -= sizeof(*nd_ra);
25553541Sshin	nd6_option_init(nd_ra + 1, icmp6len, &ndopts);
25653541Sshin	if (nd6_options(&ndopts) < 0) {
25778064Sume		nd6log((LOG_INFO,
25878064Sume		    "nd6_ra_input: invalid ND option, ignored\n"));
25978064Sume		/* nd6_options have incremented stats */
26062587Sitojun		goto freeit;
26153541Sshin	}
26253541Sshin
26353541Sshin    {
26453541Sshin	struct nd_defrouter dr0;
26553541Sshin	u_int32_t advreachable = nd_ra->nd_ra_reachable;
26653541Sshin
26753541Sshin	dr0.rtaddr = saddr6;
26853541Sshin	dr0.flags  = nd_ra->nd_ra_flags_reserved;
26953541Sshin	dr0.rtlifetime = ntohs(nd_ra->nd_ra_router_lifetime);
27053541Sshin	dr0.expire = time_second + dr0.rtlifetime;
27153541Sshin	dr0.ifp = ifp;
27262587Sitojun	dr0.advint = 0;		/* Mobile IPv6 */
27362587Sitojun	dr0.advint_expire = 0;	/* Mobile IPv6 */
27462587Sitojun	dr0.advints_lost = 0;	/* Mobile IPv6 */
27553541Sshin	/* unspecified or not? (RFC 2461 6.3.4) */
27653541Sshin	if (advreachable) {
27790868Smike		advreachable = ntohl(advreachable);
27853541Sshin		if (advreachable <= MAX_REACHABLE_TIME &&
27953541Sshin		    ndi->basereachable != advreachable) {
28053541Sshin			ndi->basereachable = advreachable;
28153541Sshin			ndi->reachable = ND_COMPUTE_RTIME(ndi->basereachable);
28253541Sshin			ndi->recalctm = nd6_recalc_reachtm_interval; /* reset */
28353541Sshin		}
28453541Sshin	}
28553541Sshin	if (nd_ra->nd_ra_retransmit)
28653541Sshin		ndi->retrans = ntohl(nd_ra->nd_ra_retransmit);
28753541Sshin	if (nd_ra->nd_ra_curhoplimit)
28853541Sshin		ndi->chlim = nd_ra->nd_ra_curhoplimit;
28953541Sshin	dr = defrtrlist_update(&dr0);
29053541Sshin    }
29153541Sshin
29253541Sshin	/*
29353541Sshin	 * prefix
29453541Sshin	 */
29553541Sshin	if (ndopts.nd_opts_pi) {
29653541Sshin		struct nd_opt_hdr *pt;
29778064Sume		struct nd_opt_prefix_info *pi = NULL;
29853541Sshin		struct nd_prefix pr;
29953541Sshin
30053541Sshin		for (pt = (struct nd_opt_hdr *)ndopts.nd_opts_pi;
30153541Sshin		     pt <= (struct nd_opt_hdr *)ndopts.nd_opts_pi_end;
30253541Sshin		     pt = (struct nd_opt_hdr *)((caddr_t)pt +
30353541Sshin						(pt->nd_opt_len << 3))) {
30453541Sshin			if (pt->nd_opt_type != ND_OPT_PREFIX_INFORMATION)
30553541Sshin				continue;
30653541Sshin			pi = (struct nd_opt_prefix_info *)pt;
30753541Sshin
30853541Sshin			if (pi->nd_opt_pi_len != 4) {
30978064Sume				nd6log((LOG_INFO,
31078064Sume				    "nd6_ra_input: invalid option "
31178064Sume				    "len %d for prefix information option, "
31278064Sume				    "ignored\n", pi->nd_opt_pi_len));
31353541Sshin				continue;
31453541Sshin			}
31553541Sshin
31653541Sshin			if (128 < pi->nd_opt_pi_prefix_len) {
31778064Sume				nd6log((LOG_INFO,
31878064Sume				    "nd6_ra_input: invalid prefix "
31978064Sume				    "len %d for prefix information option, "
32078064Sume				    "ignored\n", pi->nd_opt_pi_prefix_len));
32153541Sshin				continue;
32253541Sshin			}
32353541Sshin
32453541Sshin			if (IN6_IS_ADDR_MULTICAST(&pi->nd_opt_pi_prefix)
32553541Sshin			 || IN6_IS_ADDR_LINKLOCAL(&pi->nd_opt_pi_prefix)) {
32678064Sume				nd6log((LOG_INFO,
32778064Sume				    "nd6_ra_input: invalid prefix "
32878064Sume				    "%s, ignored\n",
32978064Sume				    ip6_sprintf(&pi->nd_opt_pi_prefix)));
33053541Sshin				continue;
33153541Sshin			}
33253541Sshin
33353541Sshin			/* aggregatable unicast address, rfc2374 */
33453541Sshin			if ((pi->nd_opt_pi_prefix.s6_addr8[0] & 0xe0) == 0x20
33553541Sshin			 && pi->nd_opt_pi_prefix_len != 64) {
33678064Sume				nd6log((LOG_INFO,
33778064Sume				    "nd6_ra_input: invalid prefixlen "
33878064Sume				    "%d for rfc2374 prefix %s, ignored\n",
33978064Sume				    pi->nd_opt_pi_prefix_len,
34078064Sume				    ip6_sprintf(&pi->nd_opt_pi_prefix)));
34153541Sshin				continue;
34253541Sshin			}
34353541Sshin
34453541Sshin			bzero(&pr, sizeof(pr));
34553541Sshin			pr.ndpr_prefix.sin6_family = AF_INET6;
34653541Sshin			pr.ndpr_prefix.sin6_len = sizeof(pr.ndpr_prefix);
34753541Sshin			pr.ndpr_prefix.sin6_addr = pi->nd_opt_pi_prefix;
34853541Sshin			pr.ndpr_ifp = (struct ifnet *)m->m_pkthdr.rcvif;
34953541Sshin
35053541Sshin			pr.ndpr_raf_onlink = (pi->nd_opt_pi_flags_reserved &
351120941Sume			    ND_OPT_PI_FLAG_ONLINK) ? 1 : 0;
35253541Sshin			pr.ndpr_raf_auto = (pi->nd_opt_pi_flags_reserved &
353120941Sume			    ND_OPT_PI_FLAG_AUTO) ? 1 : 0;
35453541Sshin			pr.ndpr_plen = pi->nd_opt_pi_prefix_len;
35553541Sshin			pr.ndpr_vltime = ntohl(pi->nd_opt_pi_valid_time);
356120941Sume			pr.ndpr_pltime = ntohl(pi->nd_opt_pi_preferred_time);
35753541Sshin			if (in6_init_prefix_ltimes(&pr))
35853541Sshin				continue; /* prefix lifetime init failed */
35953541Sshin			(void)prelist_update(&pr, dr, m);
36053541Sshin		}
36153541Sshin	}
36253541Sshin
36353541Sshin	/*
36453541Sshin	 * MTU
36553541Sshin	 */
36653541Sshin	if (ndopts.nd_opts_mtu && ndopts.nd_opts_mtu->nd_opt_mtu_len == 1) {
367120941Sume		u_int32_t mtu;
36853541Sshin
369120941Sume		mtu = ntohl(ndopts.nd_opts_mtu->nd_opt_mtu_mtu);
370120941Sume
37153541Sshin		/* lower bound */
37253541Sshin		if (mtu < IPV6_MMTU) {
37378064Sume			nd6log((LOG_INFO, "nd6_ra_input: bogus mtu option "
37453541Sshin			    "mtu=%d sent from %s, ignoring\n",
37578064Sume			    mtu, ip6_sprintf(&ip6->ip6_src)));
37653541Sshin			goto skip;
37753541Sshin		}
37853541Sshin
37953541Sshin		/* upper bound */
38053541Sshin		if (ndi->maxmtu) {
38153541Sshin			if (mtu <= ndi->maxmtu) {
38253541Sshin				int change = (ndi->linkmtu != mtu);
38353541Sshin
38453541Sshin				ndi->linkmtu = mtu;
38553541Sshin				if (change) /* in6_maxmtu may change */
38653541Sshin					in6_setmaxmtu();
38753541Sshin			} else {
38878064Sume				nd6log((LOG_INFO, "nd6_ra_input: bogus mtu "
38953541Sshin				    "mtu=%d sent from %s; "
39053541Sshin				    "exceeds maxmtu %d, ignoring\n",
39153541Sshin				    mtu, ip6_sprintf(&ip6->ip6_src),
39278064Sume				    ndi->maxmtu));
39353541Sshin			}
39453541Sshin		} else {
39578064Sume			nd6log((LOG_INFO, "nd6_ra_input: mtu option "
39653541Sshin			    "mtu=%d sent from %s; maxmtu unknown, "
39753541Sshin			    "ignoring\n",
39878064Sume			    mtu, ip6_sprintf(&ip6->ip6_src)));
39953541Sshin		}
40053541Sshin	}
40153541Sshin
40253541Sshin skip:
403120941Sume
40453541Sshin	/*
40595023Ssuz	 * Source link layer address
40653541Sshin	 */
40753541Sshin    {
40853541Sshin	char *lladdr = NULL;
40953541Sshin	int lladdrlen = 0;
410120941Sume
41153541Sshin	if (ndopts.nd_opts_src_lladdr) {
41253541Sshin		lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1);
41353541Sshin		lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3;
41453541Sshin	}
41553541Sshin
41653541Sshin	if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) {
41778064Sume		nd6log((LOG_INFO,
41853541Sshin		    "nd6_ra_input: lladdrlen mismatch for %s "
419120941Sume		    "(if %d, RA packet %d)\n", ip6_sprintf(&saddr6),
420120941Sume		    ifp->if_addrlen, lladdrlen - 2));
42178064Sume		goto bad;
42253541Sshin	}
42353541Sshin
424120941Sume	nd6_cache_lladdr(ifp, &saddr6, lladdr,
425120941Sume	    lladdrlen, ND_ROUTER_ADVERT, 0);
42662587Sitojun
42762587Sitojun	/*
42862587Sitojun	 * Installing a link-layer address might change the state of the
42962587Sitojun	 * router's neighbor cache, which might also affect our on-link
43062587Sitojun	 * detection of adveritsed prefixes.
43162587Sitojun	 */
43262587Sitojun	pfxlist_onlink_check();
43353541Sshin    }
43462587Sitojun
43578064Sume freeit:
43662587Sitojun	m_freem(m);
43778064Sume	return;
43878064Sume
43978064Sume bad:
44078064Sume	icmp6stat.icp6s_badra++;
44178064Sume	m_freem(m);
44253541Sshin}
44353541Sshin
44453541Sshin/*
44553541Sshin * default router list proccessing sub routines
44653541Sshin */
44762587Sitojun
44862587Sitojun/* tell the change to user processes watching the routing socket. */
44962587Sitojunstatic void
45078064Sumend6_rtmsg(cmd, rt)
45162587Sitojun	int cmd;
45262587Sitojun	struct rtentry *rt;
45362587Sitojun{
45462587Sitojun	struct rt_addrinfo info;
45562587Sitojun
45662587Sitojun	bzero((caddr_t)&info, sizeof(info));
45762587Sitojun	info.rti_info[RTAX_DST] = rt_key(rt);
45862587Sitojun	info.rti_info[RTAX_GATEWAY] = rt->rt_gateway;
45962587Sitojun	info.rti_info[RTAX_NETMASK] = rt_mask(rt);
46078064Sume	info.rti_info[RTAX_IFP] =
46178064Sume		(struct sockaddr *)TAILQ_FIRST(&rt->rt_ifp->if_addrlist);
46278064Sume	info.rti_info[RTAX_IFA] = rt->rt_ifa->ifa_addr;
46362587Sitojun
46462587Sitojun	rt_missmsg(cmd, &info, rt->rt_flags, 0);
46562587Sitojun}
46662587Sitojun
46753541Sshinvoid
46853541Sshindefrouter_addreq(new)
46953541Sshin	struct nd_defrouter *new;
47053541Sshin{
47153541Sshin	struct sockaddr_in6 def, mask, gate;
47262587Sitojun	struct rtentry *newrt = NULL;
47353541Sshin
47453541Sshin	Bzero(&def, sizeof(def));
47553541Sshin	Bzero(&mask, sizeof(mask));
47653541Sshin	Bzero(&gate, sizeof(gate));
47753541Sshin
478120941Sume	def.sin6_len = mask.sin6_len = gate.sin6_len =
479120941Sume	    sizeof(struct sockaddr_in6);
48053541Sshin	def.sin6_family = mask.sin6_family = gate.sin6_family = AF_INET6;
48153541Sshin	gate.sin6_addr = new->rtaddr;
48253541Sshin
48353541Sshin	(void)rtrequest(RTM_ADD, (struct sockaddr *)&def,
484120941Sume	    (struct sockaddr *)&gate, (struct sockaddr *)&mask,
485120941Sume	    RTF_GATEWAY, &newrt);
48662587Sitojun	if (newrt) {
487120727Ssam		RT_LOCK(newrt);
48878064Sume		nd6_rtmsg(RTM_ADD, newrt); /* tell user process */
48962587Sitojun		newrt->rt_refcnt--;
490120727Ssam		RT_UNLOCK(newrt);
49162587Sitojun	}
49253541Sshin	return;
49353541Sshin}
49453541Sshin
49562587Sitojun/* Add a route to a given interface as default */
49662587Sitojunvoid
49762587Sitojundefrouter_addifreq(ifp)
49862587Sitojun	struct ifnet *ifp;
49962587Sitojun{
50062587Sitojun	struct sockaddr_in6 def, mask;
50162587Sitojun	struct ifaddr *ifa;
50262587Sitojun	struct rtentry *newrt = NULL;
50362587Sitojun	int error, flags;
50462587Sitojun
50562587Sitojun	bzero(&def, sizeof(def));
50662587Sitojun	bzero(&mask, sizeof(mask));
50762587Sitojun
50862587Sitojun	def.sin6_len = mask.sin6_len = sizeof(struct sockaddr_in6);
50962587Sitojun	def.sin6_family = mask.sin6_family = AF_INET6;
51062587Sitojun
51162587Sitojun	/*
51262587Sitojun	 * Search for an ifaddr beloging to the specified interface.
51362587Sitojun	 * XXX: An IPv6 address are required to be assigned on the interface.
51462587Sitojun	 */
51562587Sitojun	if ((ifa = ifaof_ifpforaddr((struct sockaddr *)&def, ifp)) == NULL) {
51678064Sume		nd6log((LOG_ERR,	/* better error? */
51762587Sitojun		    "defrouter_addifreq: failed to find an ifaddr "
51862587Sitojun		    "to install a route to interface %s\n",
51978064Sume		    if_name(ifp)));
52062587Sitojun		return;
52162587Sitojun	}
52262587Sitojun
52362587Sitojun	flags = ifa->ifa_flags;
52478064Sume	error = rtrequest(RTM_ADD, (struct sockaddr *)&def, ifa->ifa_addr,
525120941Sume	    (struct sockaddr *)&mask, flags, &newrt);
52678064Sume	if (error != 0) {
52778064Sume		nd6log((LOG_ERR,
52862587Sitojun		    "defrouter_addifreq: failed to install a route to "
52962587Sitojun		    "interface %s (errno = %d)\n",
53078064Sume		    if_name(ifp), error));
53162587Sitojun	} else {
53262587Sitojun		if (newrt) {
533120727Ssam			RT_LOCK(newrt);
53478064Sume			nd6_rtmsg(RTM_ADD, newrt);
53562587Sitojun			newrt->rt_refcnt--;
536120727Ssam			RT_UNLOCK(newrt);
53762587Sitojun		}
53862587Sitojun	}
53962587Sitojun}
54062587Sitojun
54153541Sshinstruct nd_defrouter *
54253541Sshindefrouter_lookup(addr, ifp)
54353541Sshin	struct in6_addr *addr;
54453541Sshin	struct ifnet *ifp;
54553541Sshin{
54653541Sshin	struct nd_defrouter *dr;
54753541Sshin
54862587Sitojun	for (dr = TAILQ_FIRST(&nd_defrouter); dr;
54962587Sitojun	     dr = TAILQ_NEXT(dr, dr_entry)) {
55053541Sshin		if (dr->ifp == ifp && IN6_ARE_ADDR_EQUAL(addr, &dr->rtaddr))
551120856Sume			return (dr);
55262587Sitojun	}
55353541Sshin
554120856Sume	return (NULL);		/* search failed */
55553541Sshin}
55653541Sshin
55753541Sshinvoid
55853541Sshindefrouter_delreq(dr, dofree)
55953541Sshin	struct nd_defrouter *dr;
56053541Sshin	int dofree;
56153541Sshin{
56253541Sshin	struct sockaddr_in6 def, mask, gate;
56362587Sitojun	struct rtentry *oldrt = NULL;
56453541Sshin
56553541Sshin	Bzero(&def, sizeof(def));
56653541Sshin	Bzero(&mask, sizeof(mask));
56753541Sshin	Bzero(&gate, sizeof(gate));
56853541Sshin
569120941Sume	def.sin6_len = mask.sin6_len = gate.sin6_len =
570120941Sume	    sizeof(struct sockaddr_in6);
57153541Sshin	def.sin6_family = mask.sin6_family = gate.sin6_family = AF_INET6;
57253541Sshin	gate.sin6_addr = dr->rtaddr;
57353541Sshin
57453541Sshin	rtrequest(RTM_DELETE, (struct sockaddr *)&def,
575120941Sume	    (struct sockaddr *)&gate,
576120941Sume	    (struct sockaddr *)&mask, RTF_GATEWAY, &oldrt);
57762587Sitojun	if (oldrt) {
57878064Sume		nd6_rtmsg(RTM_DELETE, oldrt);
579108269Sru		RTFREE(oldrt);
58062587Sitojun	}
58153541Sshin
58262587Sitojun	if (dofree)		/* XXX: necessary? */
58353541Sshin		free(dr, M_IP6NDP);
58453541Sshin}
58553541Sshin
58653541Sshinvoid
58753541Sshindefrtrlist_del(dr)
58853541Sshin	struct nd_defrouter *dr;
58953541Sshin{
59053541Sshin	struct nd_defrouter *deldr = NULL;
59153541Sshin	struct nd_prefix *pr;
59253541Sshin
59353541Sshin	/*
59453541Sshin	 * Flush all the routing table entries that use the router
59553541Sshin	 * as a next hop.
59653541Sshin	 */
597120941Sume	if (!ip6_forwarding && ip6_accept_rtadv) /* XXX: better condition? */
59853541Sshin		rt6_flush(&dr->rtaddr, dr->ifp);
59953541Sshin
60062587Sitojun	if (dr == TAILQ_FIRST(&nd_defrouter))
60153541Sshin		deldr = dr;	/* The router is primary. */
60253541Sshin
60362587Sitojun	TAILQ_REMOVE(&nd_defrouter, dr, dr_entry);
60453541Sshin
60553541Sshin	/*
60653541Sshin	 * Also delete all the pointers to the router in each prefix lists.
60753541Sshin	 */
60862587Sitojun	for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
60953541Sshin		struct nd_pfxrouter *pfxrtr;
61053541Sshin		if ((pfxrtr = pfxrtr_lookup(pr, dr)) != NULL)
61153541Sshin			pfxrtr_del(pfxrtr);
61253541Sshin	}
61353541Sshin	pfxlist_onlink_check();
61453541Sshin
61553541Sshin	/*
61662587Sitojun	 * If the router is the primary one, choose a new one.
61762587Sitojun	 * Note that defrouter_select() will remove the current gateway
61862587Sitojun	 * from the routing table.
61953541Sshin	 */
62053541Sshin	if (deldr)
62162587Sitojun		defrouter_select();
62262587Sitojun
62353541Sshin	free(dr, M_IP6NDP);
62453541Sshin}
62553541Sshin
62662587Sitojun/*
62762587Sitojun * Default Router Selection according to Section 6.3.6 of RFC 2461:
62862587Sitojun * 1) Routers that are reachable or probably reachable should be
62962587Sitojun *    preferred.
63062587Sitojun * 2) When no routers on the list are known to be reachable or
63162587Sitojun *    probably reachable, routers SHOULD be selected in a round-robin
63262587Sitojun *    fashion.
63362587Sitojun * 3) If the Default Router List is empty, assume that all
63462587Sitojun *    destinations are on-link.
63562587Sitojun */
63662587Sitojunvoid
63762587Sitojundefrouter_select()
63862587Sitojun{
63962587Sitojun	int s = splnet();
64062587Sitojun	struct nd_defrouter *dr, anydr;
64162587Sitojun	struct rtentry *rt = NULL;
64262587Sitojun	struct llinfo_nd6 *ln = NULL;
64362587Sitojun
64462587Sitojun	/*
64562587Sitojun	 * Search for a (probably) reachable router from the list.
64662587Sitojun	 */
64762587Sitojun	for (dr = TAILQ_FIRST(&nd_defrouter); dr;
64862587Sitojun	     dr = TAILQ_NEXT(dr, dr_entry)) {
64962587Sitojun		if ((rt = nd6_lookup(&dr->rtaddr, 0, dr->ifp)) &&
65062587Sitojun		    (ln = (struct llinfo_nd6 *)rt->rt_llinfo) &&
65162587Sitojun		    ND6_IS_LLINFO_PROBREACH(ln)) {
65262587Sitojun			/* Got it, and move it to the head */
65362587Sitojun			TAILQ_REMOVE(&nd_defrouter, dr, dr_entry);
65462587Sitojun			TAILQ_INSERT_HEAD(&nd_defrouter, dr, dr_entry);
65562587Sitojun			break;
65662587Sitojun		}
65762587Sitojun	}
65862587Sitojun
65962587Sitojun	if ((dr = TAILQ_FIRST(&nd_defrouter))) {
66062587Sitojun		/*
66162587Sitojun		 * De-install the previous default gateway and install
66262587Sitojun		 * a new one.
66362587Sitojun		 * Note that if there is no reachable router in the list,
66462587Sitojun		 * the head entry will be used anyway.
66562587Sitojun		 * XXX: do we have to check the current routing table entry?
66662587Sitojun		 */
66762587Sitojun		bzero(&anydr, sizeof(anydr));
66862587Sitojun		defrouter_delreq(&anydr, 0);
66962587Sitojun		defrouter_addreq(dr);
67062587Sitojun	}
67162587Sitojun	else {
67262587Sitojun		/*
67362587Sitojun		 * The Default Router List is empty, so install the default
67462587Sitojun		 * route to an inteface.
67562587Sitojun		 * XXX: The specification does not say this mechanism should
67662587Sitojun		 * be restricted to hosts, but this would be not useful
67762587Sitojun		 * (even harmful) for routers.
67862587Sitojun		 */
67962587Sitojun		if (!ip6_forwarding) {
68062587Sitojun			/*
68162587Sitojun			 * De-install the current default route
68262587Sitojun			 * in advance.
68362587Sitojun			 */
68462587Sitojun			bzero(&anydr, sizeof(anydr));
68562587Sitojun			defrouter_delreq(&anydr, 0);
68662587Sitojun			if (nd6_defifp) {
68762587Sitojun				/*
68862587Sitojun				 * Install a route to the default interface
68962587Sitojun				 * as default route.
69078064Sume				 * XXX: we enable this for host only, because
69178064Sume				 * this may override a default route installed
69278064Sume				 * a user process (e.g. routing daemon) in a
69378064Sume				 * router case.
69462587Sitojun				 */
69562587Sitojun				defrouter_addifreq(nd6_defifp);
69678064Sume			} else {
69778064Sume				nd6log((LOG_INFO, "defrouter_select: "
69878064Sume				    "there's no default router and no default"
69978064Sume				    " interface\n"));
70062587Sitojun			}
70162587Sitojun		}
70262587Sitojun	}
70362587Sitojun
70462587Sitojun	splx(s);
70562587Sitojun	return;
70662587Sitojun}
70762587Sitojun
70853541Sshinstatic struct nd_defrouter *
70953541Sshindefrtrlist_update(new)
71053541Sshin	struct nd_defrouter *new;
71153541Sshin{
71253541Sshin	struct nd_defrouter *dr, *n;
71353541Sshin	int s = splnet();
71453541Sshin
71553541Sshin	if ((dr = defrouter_lookup(&new->rtaddr, new->ifp)) != NULL) {
71653541Sshin		/* entry exists */
71753541Sshin		if (new->rtlifetime == 0) {
71853541Sshin			defrtrlist_del(dr);
71953541Sshin			dr = NULL;
72053541Sshin		} else {
72153541Sshin			/* override */
72253541Sshin			dr->flags = new->flags; /* xxx flag check */
72353541Sshin			dr->rtlifetime = new->rtlifetime;
72453541Sshin			dr->expire = new->expire;
72553541Sshin		}
72653541Sshin		splx(s);
727120856Sume		return (dr);
72853541Sshin	}
72953541Sshin
73053541Sshin	/* entry does not exist */
73153541Sshin	if (new->rtlifetime == 0) {
73253541Sshin		splx(s);
733120856Sume		return (NULL);
73453541Sshin	}
73553541Sshin
73653541Sshin	n = (struct nd_defrouter *)malloc(sizeof(*n), M_IP6NDP, M_NOWAIT);
73753541Sshin	if (n == NULL) {
73853541Sshin		splx(s);
739120856Sume		return (NULL);
74053541Sshin	}
74153541Sshin	bzero(n, sizeof(*n));
74253541Sshin	*n = *new;
74362587Sitojun
74462587Sitojun	/*
74562587Sitojun	 * Insert the new router at the end of the Default Router List.
74662587Sitojun	 * If there is no other router, install it anyway. Otherwise,
74762587Sitojun	 * just continue to use the current default router.
74862587Sitojun	 */
74962587Sitojun	TAILQ_INSERT_TAIL(&nd_defrouter, n, dr_entry);
75062587Sitojun	if (TAILQ_FIRST(&nd_defrouter) == n)
75162587Sitojun		defrouter_select();
75253541Sshin	splx(s);
753120941Sume
754120856Sume	return (n);
75553541Sshin}
75653541Sshin
75753541Sshinstatic struct nd_pfxrouter *
75853541Sshinpfxrtr_lookup(pr, dr)
75953541Sshin	struct nd_prefix *pr;
76053541Sshin	struct nd_defrouter *dr;
76153541Sshin{
76253541Sshin	struct nd_pfxrouter *search;
763120941Sume
76462587Sitojun	for (search = pr->ndpr_advrtrs.lh_first; search; search = search->pfr_next) {
76553541Sshin		if (search->router == dr)
76653541Sshin			break;
76753541Sshin	}
76853541Sshin
769120856Sume	return (search);
77053541Sshin}
77153541Sshin
77253541Sshinstatic void
77353541Sshinpfxrtr_add(pr, dr)
77453541Sshin	struct nd_prefix *pr;
77553541Sshin	struct nd_defrouter *dr;
77653541Sshin{
77753541Sshin	struct nd_pfxrouter *new;
77853541Sshin
77953541Sshin	new = (struct nd_pfxrouter *)malloc(sizeof(*new), M_IP6NDP, M_NOWAIT);
78053541Sshin	if (new == NULL)
78153541Sshin		return;
78253541Sshin	bzero(new, sizeof(*new));
78353541Sshin	new->router = dr;
78453541Sshin
78553541Sshin	LIST_INSERT_HEAD(&pr->ndpr_advrtrs, new, pfr_entry);
78653541Sshin
78753541Sshin	pfxlist_onlink_check();
78853541Sshin}
78953541Sshin
79053541Sshinstatic void
79153541Sshinpfxrtr_del(pfr)
79253541Sshin	struct nd_pfxrouter *pfr;
79353541Sshin{
79453541Sshin	LIST_REMOVE(pfr, pfr_entry);
79553541Sshin	free(pfr, M_IP6NDP);
79653541Sshin}
79753541Sshin
79878064Sumestruct nd_prefix *
79978064Sumend6_prefix_lookup(pr)
80053541Sshin	struct nd_prefix *pr;
80153541Sshin{
80253541Sshin	struct nd_prefix *search;
80353541Sshin
80462587Sitojun	for (search = nd_prefix.lh_first; search; search = search->ndpr_next) {
80553541Sshin		if (pr->ndpr_ifp == search->ndpr_ifp &&
80653541Sshin		    pr->ndpr_plen == search->ndpr_plen &&
80753541Sshin		    in6_are_prefix_equal(&pr->ndpr_prefix.sin6_addr,
808120941Sume		    &search->ndpr_prefix.sin6_addr, pr->ndpr_plen)) {
80953541Sshin			break;
81053541Sshin		}
81153541Sshin	}
81253541Sshin
813120856Sume	return (search);
81453541Sshin}
81553541Sshin
81678064Sumeint
81778064Sumend6_prelist_add(pr, dr, newp)
81878064Sume	struct nd_prefix *pr, **newp;
81953541Sshin	struct nd_defrouter *dr;
82053541Sshin{
82178064Sume	struct nd_prefix *new = NULL;
82253541Sshin	int i, s;
82353541Sshin
82453541Sshin	new = (struct nd_prefix *)malloc(sizeof(*new), M_IP6NDP, M_NOWAIT);
82553541Sshin	if (new == NULL)
826120941Sume		return(ENOMEM);
82753541Sshin	bzero(new, sizeof(*new));
82853541Sshin	*new = *pr;
82978064Sume	if (newp != NULL)
83078064Sume		*newp = new;
83153541Sshin
832120941Sume	/* initialization */
83353541Sshin	LIST_INIT(&new->ndpr_advrtrs);
83453541Sshin	in6_prefixlen2mask(&new->ndpr_mask, new->ndpr_plen);
83553541Sshin	/* make prefix in the canonical form */
83653541Sshin	for (i = 0; i < 4; i++)
83753541Sshin		new->ndpr_prefix.sin6_addr.s6_addr32[i] &=
838120941Sume		    new->ndpr_mask.s6_addr32[i];
83953541Sshin
84053541Sshin	s = splnet();
84153541Sshin	/* link ndpr_entry to nd_prefix list */
84253541Sshin	LIST_INSERT_HEAD(&nd_prefix, new, ndpr_entry);
84353541Sshin	splx(s);
84453541Sshin
84578064Sume	/* ND_OPT_PI_FLAG_ONLINK processing */
84678064Sume	if (new->ndpr_raf_onlink) {
84778064Sume		int e;
84878064Sume
84978064Sume		if ((e = nd6_prefix_onlink(new)) != 0) {
85078064Sume			nd6log((LOG_ERR, "nd6_prelist_add: failed to make "
85178064Sume			    "the prefix %s/%d on-link on %s (errno=%d)\n",
85278064Sume			    ip6_sprintf(&pr->ndpr_prefix.sin6_addr),
85378064Sume			    pr->ndpr_plen, if_name(pr->ndpr_ifp), e));
85478064Sume			/* proceed anyway. XXX: is it correct? */
85578064Sume		}
85678064Sume	}
85778064Sume
858120941Sume	if (dr)
85953541Sshin		pfxrtr_add(new, dr);
86053541Sshin
86153541Sshin	return 0;
86253541Sshin}
86353541Sshin
86453541Sshinvoid
86553541Sshinprelist_remove(pr)
86653541Sshin	struct nd_prefix *pr;
86753541Sshin{
86853541Sshin	struct nd_pfxrouter *pfr, *next;
86978064Sume	int e, s;
87053541Sshin
87178064Sume	/* make sure to invalidate the prefix until it is really freed. */
87278064Sume	pr->ndpr_vltime = 0;
87378064Sume	pr->ndpr_pltime = 0;
87478064Sume#if 0
87578064Sume	/*
87678064Sume	 * Though these flags are now meaningless, we'd rather keep the value
87778064Sume	 * not to confuse users when executing "ndp -p".
87878064Sume	 */
87978064Sume	pr->ndpr_raf_onlink = 0;
88078064Sume	pr->ndpr_raf_auto = 0;
88178064Sume#endif
88278064Sume	if ((pr->ndpr_stateflags & NDPRF_ONLINK) != 0 &&
88378064Sume	    (e = nd6_prefix_offlink(pr)) != 0) {
88478064Sume		nd6log((LOG_ERR, "prelist_remove: failed to make %s/%d offlink "
88578064Sume		    "on %s, errno=%d\n",
88678064Sume		    ip6_sprintf(&pr->ndpr_prefix.sin6_addr),
88778064Sume		    pr->ndpr_plen, if_name(pr->ndpr_ifp), e));
88878064Sume		/* what should we do? */
88978064Sume	}
89078064Sume
89178064Sume	if (pr->ndpr_refcnt > 0)
89278064Sume		return;		/* notice here? */
89378064Sume
89453541Sshin	s = splnet();
89578064Sume
89653541Sshin	/* unlink ndpr_entry from nd_prefix list */
89753541Sshin	LIST_REMOVE(pr, ndpr_entry);
89853541Sshin
89953541Sshin	/* free list of routers that adversed the prefix */
90062587Sitojun	for (pfr = pr->ndpr_advrtrs.lh_first; pfr; pfr = next) {
90162587Sitojun		next = pfr->pfr_next;
90253541Sshin
90353541Sshin		free(pfr, M_IP6NDP);
90453541Sshin	}
90578064Sume	splx(s);
90678064Sume
90753541Sshin	free(pr, M_IP6NDP);
90853541Sshin
90953541Sshin	pfxlist_onlink_check();
91053541Sshin}
91153541Sshin
91253541Sshinint
91353541Sshinprelist_update(new, dr, m)
91453541Sshin	struct nd_prefix *new;
91553541Sshin	struct nd_defrouter *dr; /* may be NULL */
91653541Sshin	struct mbuf *m;
91753541Sshin{
91878064Sume	struct in6_ifaddr *ia6 = NULL, *ia6_match = NULL;
91978064Sume	struct ifaddr *ifa;
92078064Sume	struct ifnet *ifp = new->ndpr_ifp;
92153541Sshin	struct nd_prefix *pr;
92253541Sshin	int s = splnet();
92353541Sshin	int error = 0;
92478064Sume	int newprefix = 0;
92553541Sshin	int auth;
92678064Sume	struct in6_addrlifetime lt6_tmp;
92753541Sshin
92853541Sshin	auth = 0;
92953541Sshin	if (m) {
93053541Sshin		/*
93153541Sshin		 * Authenticity for NA consists authentication for
93253541Sshin		 * both IP header and IP datagrams, doesn't it ?
93353541Sshin		 */
93453541Sshin#if defined(M_AUTHIPHDR) && defined(M_AUTHIPDGM)
935120941Sume		auth = ((m->m_flags & M_AUTHIPHDR) &&
936120941Sume		    (m->m_flags & M_AUTHIPDGM));
93753541Sshin#endif
93853541Sshin	}
93953541Sshin
94078064Sume	if ((pr = nd6_prefix_lookup(new)) != NULL) {
94178064Sume		/*
94278064Sume		 * nd6_prefix_lookup() ensures that pr and new have the same
94378064Sume		 * prefix on a same interface.
94478064Sume		 */
94553541Sshin
94653541Sshin		/*
94778064Sume		 * Update prefix information.  Note that the on-link (L) bit
94878064Sume		 * and the autonomous (A) bit should NOT be changed from 1
94978064Sume		 * to 0.
95053541Sshin		 */
95178064Sume		if (new->ndpr_raf_onlink == 1)
95278064Sume			pr->ndpr_raf_onlink = 1;
95378064Sume		if (new->ndpr_raf_auto == 1)
95478064Sume			pr->ndpr_raf_auto = 1;
95578064Sume		if (new->ndpr_raf_onlink) {
95678064Sume			pr->ndpr_vltime = new->ndpr_vltime;
95778064Sume			pr->ndpr_pltime = new->ndpr_pltime;
95878064Sume			pr->ndpr_preferred = new->ndpr_preferred;
95978064Sume			pr->ndpr_expire = new->ndpr_expire;
96078064Sume		}
96153541Sshin
96278064Sume		if (new->ndpr_raf_onlink &&
96378064Sume		    (pr->ndpr_stateflags & NDPRF_ONLINK) == 0) {
96478064Sume			int e;
96553541Sshin
96678064Sume			if ((e = nd6_prefix_onlink(pr)) != 0) {
96778064Sume				nd6log((LOG_ERR,
96878064Sume				    "prelist_update: failed to make "
96978064Sume				    "the prefix %s/%d on-link on %s "
97078064Sume				    "(errno=%d)\n",
97178064Sume				    ip6_sprintf(&pr->ndpr_prefix.sin6_addr),
97278064Sume				    pr->ndpr_plen, if_name(pr->ndpr_ifp), e));
97378064Sume				/* proceed anyway. XXX: is it correct? */
97453541Sshin			}
97578064Sume		}
97653541Sshin
97778064Sume		if (dr && pfxrtr_lookup(pr, dr) == NULL)
97878064Sume			pfxrtr_add(pr, dr);
97978064Sume	} else {
98078064Sume		struct nd_prefix *newpr = NULL;
98153541Sshin
98278064Sume		newprefix = 1;
98353541Sshin
98478064Sume		if (new->ndpr_vltime == 0)
98578064Sume			goto end;
98678064Sume		if (new->ndpr_raf_onlink == 0 && new->ndpr_raf_auto == 0)
98778064Sume			goto end;
98853541Sshin
98978064Sume		bzero(&new->ndpr_addr, sizeof(struct in6_addr));
99062587Sitojun
99178064Sume		error = nd6_prelist_add(new, dr, &newpr);
99278064Sume		if (error != 0 || newpr == NULL) {
99378064Sume			nd6log((LOG_NOTICE, "prelist_update: "
99478064Sume			    "nd6_prelist_add failed for %s/%d on %s "
99578064Sume			    "errno=%d, returnpr=%p\n",
99678064Sume			    ip6_sprintf(&new->ndpr_prefix.sin6_addr),
997120941Sume			    new->ndpr_plen, if_name(new->ndpr_ifp),
998120941Sume			    error, newpr));
99978064Sume			goto end; /* we should just give up in this case. */
100078064Sume		}
100153541Sshin
100278064Sume		/*
100378064Sume		 * XXX: from the ND point of view, we can ignore a prefix
100478064Sume		 * with the on-link bit being zero.  However, we need a
100578064Sume		 * prefix structure for references from autoconfigured
1006120941Sume		 * addresses.  Thus, we explicitly make sure that the prefix
100778064Sume		 * itself expires now.
100878064Sume		 */
100978064Sume		if (newpr->ndpr_raf_onlink == 0) {
101078064Sume			newpr->ndpr_vltime = 0;
101178064Sume			newpr->ndpr_pltime = 0;
101278064Sume			in6_init_prefix_ltimes(newpr);
101353541Sshin		}
101453541Sshin
101578064Sume		pr = newpr;
101678064Sume	}
101753541Sshin
101878064Sume	/*
101978064Sume	 * Address autoconfiguration based on Section 5.5.3 of RFC 2462.
102078064Sume	 * Note that pr must be non NULL at this point.
102178064Sume	 */
102262587Sitojun
102378064Sume	/* 5.5.3 (a). Ignore the prefix without the A bit set. */
102478064Sume	if (!new->ndpr_raf_auto)
102578064Sume		goto afteraddrconf;
102662587Sitojun
102778064Sume	/*
102878064Sume	 * 5.5.3 (b). the link-local prefix should have been ignored in
102978064Sume	 * nd6_ra_input.
103078064Sume	 */
103162587Sitojun
103278064Sume	/*
103378064Sume	 * 5.5.3 (c). Consistency check on lifetimes: pltime <= vltime.
103478064Sume	 * This should have been done in nd6_ra_input.
103578064Sume	 */
103662587Sitojun
103778064Sume 	/*
103878064Sume	 * 5.5.3 (d). If the prefix advertised does not match the prefix of an
103978064Sume	 * address already in the list, and the Valid Lifetime is not 0,
104078064Sume	 * form an address.  Note that even a manually configured address
104178064Sume	 * should reject autoconfiguration of a new address.
104278064Sume	 */
1043120941Sume	TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
104478064Sume		struct in6_ifaddr *ifa6;
104578064Sume		int ifa_plen;
104678064Sume		u_int32_t storedlifetime;
104753541Sshin
104878064Sume		if (ifa->ifa_addr->sa_family != AF_INET6)
104978064Sume			continue;
105053541Sshin
105178064Sume		ifa6 = (struct in6_ifaddr *)ifa;
105253541Sshin
105353541Sshin		/*
105478064Sume		 * Spec is not clear here, but I believe we should concentrate
105578064Sume		 * on unicast (i.e. not anycast) addresses.
105678064Sume		 * XXX: other ia6_flags? detached or duplicated?
105753541Sshin		 */
105878064Sume		if ((ifa6->ia6_flags & IN6_IFF_ANYCAST) != 0)
105978064Sume			continue;
1060120941Sume
106178064Sume		ifa_plen = in6_mask2len(&ifa6->ia_prefixmask.sin6_addr, NULL);
106278064Sume		if (ifa_plen != new->ndpr_plen ||
106378064Sume		    !in6_are_prefix_equal(&ifa6->ia_addr.sin6_addr,
1064120941Sume		    &new->ndpr_prefix.sin6_addr, ifa_plen))
106578064Sume			continue;
106653541Sshin
106778064Sume		if (ia6_match == NULL) /* remember the first one */
106878064Sume			ia6_match = ifa6;
106978064Sume
107078064Sume		if ((ifa6->ia6_flags & IN6_IFF_AUTOCONF) == 0)
107178064Sume			continue;
107278064Sume
107378064Sume		/*
107478064Sume		 * An already autoconfigured address matched.  Now that we
107578064Sume		 * are sure there is at least one matched address, we can
107678064Sume		 * proceed to 5.5.3. (e): update the lifetimes according to the
107778064Sume		 * "two hours" rule and the privacy extension.
107878064Sume		 */
107978064Sume#define TWOHOUR		(120*60)
108078064Sume		lt6_tmp = ifa6->ia6_lifetime;
108178064Sume
1082112678Sume		if (lt6_tmp.ia6t_vltime == ND6_INFINITE_LIFETIME)
1083112678Sume			storedlifetime = ND6_INFINITE_LIFETIME;
1084112678Sume		else if (IFA6_IS_INVALID(ifa6))
1085112678Sume			storedlifetime = 0;
1086112678Sume		else
1087112678Sume			storedlifetime = lt6_tmp.ia6t_expire - time_second;
108878064Sume
1089112678Sume		/* when not updating, keep the current stored lifetime. */
1090112678Sume		lt6_tmp.ia6t_vltime = storedlifetime;
1091112678Sume
109278064Sume		if (TWOHOUR < new->ndpr_vltime ||
109378064Sume		    storedlifetime < new->ndpr_vltime) {
109478064Sume			lt6_tmp.ia6t_vltime = new->ndpr_vltime;
109578064Sume		} else if (storedlifetime <= TWOHOUR
109678064Sume#if 0
109778064Sume			   /*
109878064Sume			    * This condition is logically redundant, so we just
109978064Sume			    * omit it.
110078064Sume			    * See IPng 6712, 6717, and 6721.
110178064Sume			    */
110278064Sume			   && new->ndpr_vltime <= storedlifetime
110378064Sume#endif
110478064Sume			) {
110578064Sume			if (auth) {
110678064Sume				lt6_tmp.ia6t_vltime = new->ndpr_vltime;
110778064Sume			}
110878064Sume		} else {
110978064Sume			/*
111078064Sume			 * new->ndpr_vltime <= TWOHOUR &&
111178064Sume			 * TWOHOUR < storedlifetime
111278064Sume			 */
111378064Sume			lt6_tmp.ia6t_vltime = TWOHOUR;
111453541Sshin		}
111553541Sshin
111678064Sume		/* The 2 hour rule is not imposed for preferred lifetime. */
111778064Sume		lt6_tmp.ia6t_pltime = new->ndpr_pltime;
111853541Sshin
111978064Sume		in6_init_address_ltimes(pr, &lt6_tmp);
112053541Sshin
112178064Sume		/*
112278064Sume		 * When adjusting the lifetimes of an existing temporary
112378064Sume		 * address, only lower the lifetimes.
112478064Sume		 * RFC 3041 3.3. (1).
112578064Sume		 * XXX: how should we modify ia6t_[pv]ltime?
112678064Sume		 */
112778064Sume		if ((ifa6->ia6_flags & IN6_IFF_TEMPORARY) != 0) {
112878064Sume			if (lt6_tmp.ia6t_expire == 0 || /* no expire */
112978064Sume			    lt6_tmp.ia6t_expire >
113078064Sume			    ifa6->ia6_lifetime.ia6t_expire) {
113178064Sume				lt6_tmp.ia6t_expire =
1132120941Sume				    ifa6->ia6_lifetime.ia6t_expire;
113378064Sume			}
113478064Sume			if (lt6_tmp.ia6t_preferred == 0 || /* no expire */
113578064Sume			    lt6_tmp.ia6t_preferred >
113678064Sume			    ifa6->ia6_lifetime.ia6t_preferred) {
113778064Sume				lt6_tmp.ia6t_preferred =
1138120941Sume				    ifa6->ia6_lifetime.ia6t_preferred;
113978064Sume			}
114078064Sume		}
114178064Sume
114278064Sume		ifa6->ia6_lifetime = lt6_tmp;
114353541Sshin	}
114478064Sume	if (ia6_match == NULL && new->ndpr_vltime) {
114578064Sume		/*
114678064Sume		 * No address matched and the valid lifetime is non-zero.
114778064Sume		 * Create a new address.
114878064Sume		 */
114978064Sume		if ((ia6 = in6_ifadd(new, NULL)) != NULL) {
115078064Sume			/*
115178064Sume			 * note that we should use pr (not new) for reference.
115278064Sume			 */
115378064Sume			pr->ndpr_refcnt++;
115478064Sume			ia6->ia6_ndpr = pr;
115553541Sshin
115678064Sume			/*
115778064Sume			 * RFC 3041 3.3 (2).
115878064Sume			 * When a new public address is created as described
115978064Sume			 * in RFC2462, also create a new temporary address.
116078064Sume			 *
116178064Sume			 * RFC 3041 3.5.
116278064Sume			 * When an interface connects to a new link, a new
116378064Sume			 * randomized interface identifier should be generated
116478064Sume			 * immediately together with a new set of temporary
116578064Sume			 * addresses.  Thus, we specifiy 1 as the 2nd arg of
116678064Sume			 * in6_tmpifadd().
116778064Sume			 */
116878064Sume			if (ip6_use_tempaddr) {
116978064Sume				int e;
117078064Sume				if ((e = in6_tmpifadd(ia6, 1)) != 0) {
117178064Sume					nd6log((LOG_NOTICE, "prelist_update: "
117278064Sume					    "failed to create a temporary "
117378064Sume					    "address, errno=%d\n",
117478064Sume					    e));
117578064Sume				}
117678064Sume			}
117778064Sume
117878064Sume			/*
117978064Sume			 * A newly added address might affect the status
118078064Sume			 * of other addresses, so we check and update it.
118178064Sume			 * XXX: what if address duplication happens?
118278064Sume			 */
118378064Sume			pfxlist_onlink_check();
118478064Sume		} else {
118578064Sume			/* just set an error. do not bark here. */
118678064Sume			error = EADDRNOTAVAIL; /* XXX: might be unused. */
118778064Sume		}
118878064Sume	}
118978064Sume
119078064Sume  afteraddrconf:
119178064Sume
119253541Sshin end:
119353541Sshin	splx(s);
119453541Sshin	return error;
119553541Sshin}
119653541Sshin
119753541Sshin/*
119862587Sitojun * A supplement function used in the on-link detection below;
119962587Sitojun * detect if a given prefix has a (probably) reachable advertising router.
120062587Sitojun * XXX: lengthy function name...
120162587Sitojun */
120278064Sumestatic struct nd_pfxrouter *
120362587Sitojunfind_pfxlist_reachable_router(pr)
120462587Sitojun	struct nd_prefix *pr;
120562587Sitojun{
120662587Sitojun	struct nd_pfxrouter *pfxrtr;
120762587Sitojun	struct rtentry *rt;
120862587Sitojun	struct llinfo_nd6 *ln;
120962587Sitojun
121062587Sitojun	for (pfxrtr = LIST_FIRST(&pr->ndpr_advrtrs); pfxrtr;
121162587Sitojun	     pfxrtr = LIST_NEXT(pfxrtr, pfr_entry)) {
121262587Sitojun		if ((rt = nd6_lookup(&pfxrtr->router->rtaddr, 0,
1213120941Sume		    pfxrtr->router->ifp)) &&
121462587Sitojun		    (ln = (struct llinfo_nd6 *)rt->rt_llinfo) &&
121562587Sitojun		    ND6_IS_LLINFO_PROBREACH(ln))
121662587Sitojun			break;	/* found */
121762587Sitojun	}
121862587Sitojun
1219120856Sume	return (pfxrtr);
122062587Sitojun}
122162587Sitojun
122262587Sitojun/*
122353541Sshin * Check if each prefix in the prefix list has at least one available router
122478064Sume * that advertised the prefix (a router is "available" if its neighbor cache
122578064Sume * entry is reachable or probably reachable).
122662587Sitojun * If the check fails, the prefix may be off-link, because, for example,
122753541Sshin * we have moved from the network but the lifetime of the prefix has not
122878064Sume * expired yet.  So we should not use the prefix if there is another prefix
122978064Sume * that has an available router.
123078064Sume * But, if there is no prefix that has an available router, we still regards
123178064Sume * all the prefixes as on-link.  This is because we can't tell if all the
123253541Sshin * routers are simply dead or if we really moved from the network and there
123353541Sshin * is no router around us.
123453541Sshin */
123562587Sitojunvoid
123653541Sshinpfxlist_onlink_check()
123753541Sshin{
123853541Sshin	struct nd_prefix *pr;
123978064Sume	struct in6_ifaddr *ifa;
124053541Sshin
124162587Sitojun	/*
124262587Sitojun	 * Check if there is a prefix that has a reachable advertising
124362587Sitojun	 * router.
124462587Sitojun	 */
124562587Sitojun	for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
124678064Sume		if (pr->ndpr_raf_onlink && find_pfxlist_reachable_router(pr))
124753541Sshin			break;
124862587Sitojun	}
124953541Sshin
125053541Sshin	if (pr) {
125153541Sshin		/*
125262587Sitojun		 * There is at least one prefix that has a reachable router.
125378064Sume		 * Detach prefixes which have no reachable advertising
125478064Sume		 * router, and attach other prefixes.
125553541Sshin		 */
125662587Sitojun		for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
125778064Sume			/* XXX: a link-local prefix should never be detached */
125878064Sume			if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr))
125978064Sume				continue;
126078064Sume
126178064Sume			/*
126278064Sume			 * we aren't interested in prefixes without the L bit
126378064Sume			 * set.
126478064Sume			 */
126578064Sume			if (pr->ndpr_raf_onlink == 0)
126678064Sume				continue;
126778064Sume
126878064Sume			if ((pr->ndpr_stateflags & NDPRF_DETACHED) == 0 &&
126978064Sume			    find_pfxlist_reachable_router(pr) == NULL)
127078064Sume				pr->ndpr_stateflags |= NDPRF_DETACHED;
127178064Sume			if ((pr->ndpr_stateflags & NDPRF_DETACHED) != 0 &&
127278064Sume			    find_pfxlist_reachable_router(pr) != 0)
127378064Sume				pr->ndpr_stateflags &= ~NDPRF_DETACHED;
127453541Sshin		}
127578064Sume	} else {
127678064Sume		/* there is no prefix that has a reachable router */
127762587Sitojun		for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
127878064Sume			if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr))
127978064Sume				continue;
128078064Sume
128178064Sume			if (pr->ndpr_raf_onlink == 0)
128278064Sume				continue;
128378064Sume
128478064Sume			if ((pr->ndpr_stateflags & NDPRF_DETACHED) != 0)
128578064Sume				pr->ndpr_stateflags &= ~NDPRF_DETACHED;
128653541Sshin		}
128762587Sitojun	}
128878064Sume
128978064Sume	/*
129078064Sume	 * Remove each interface route associated with a (just) detached
129178064Sume	 * prefix, and reinstall the interface route for a (just) attached
129278064Sume	 * prefix.  Note that all attempt of reinstallation does not
129378064Sume	 * necessarily success, when a same prefix is shared among multiple
129478064Sume	 * interfaces.  Such cases will be handled in nd6_prefix_onlink,
129578064Sume	 * so we don't have to care about them.
129678064Sume	 */
129778064Sume	for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
129878064Sume		int e;
129978064Sume
130078064Sume		if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr))
130178064Sume			continue;
130278064Sume
130378064Sume		if (pr->ndpr_raf_onlink == 0)
130478064Sume			continue;
130578064Sume
130678064Sume		if ((pr->ndpr_stateflags & NDPRF_DETACHED) != 0 &&
130778064Sume		    (pr->ndpr_stateflags & NDPRF_ONLINK) != 0) {
130878064Sume			if ((e = nd6_prefix_offlink(pr)) != 0) {
130978064Sume				nd6log((LOG_ERR,
131078064Sume				    "pfxlist_onlink_check: failed to "
131178064Sume				    "make %s/%d offlink, errno=%d\n",
131278064Sume				    ip6_sprintf(&pr->ndpr_prefix.sin6_addr),
131378064Sume				    pr->ndpr_plen, e));
131478064Sume			}
131578064Sume		}
131678064Sume		if ((pr->ndpr_stateflags & NDPRF_DETACHED) == 0 &&
131778064Sume		    (pr->ndpr_stateflags & NDPRF_ONLINK) == 0 &&
131878064Sume		    pr->ndpr_raf_onlink) {
131978064Sume			if ((e = nd6_prefix_onlink(pr)) != 0) {
132078064Sume				nd6log((LOG_ERR,
132178064Sume				    "pfxlist_onlink_check: failed to "
132278064Sume				    "make %s/%d offlink, errno=%d\n",
132378064Sume				    ip6_sprintf(&pr->ndpr_prefix.sin6_addr),
132478064Sume				    pr->ndpr_plen, e));
132578064Sume			}
132678064Sume		}
132778064Sume	}
132878064Sume
132978064Sume	/*
133078064Sume	 * Changes on the prefix status might affect address status as well.
133178064Sume	 * Make sure that all addresses derived from an attached prefix are
133278064Sume	 * attached, and that all addresses derived from a detached prefix are
133378064Sume	 * detached.  Note, however, that a manually configured address should
133478064Sume	 * always be attached.
133578064Sume	 * The precise detection logic is same as the one for prefixes.
133678064Sume	 */
133778064Sume	for (ifa = in6_ifaddr; ifa; ifa = ifa->ia_next) {
1338120941Sume		if (!(ifa->ia6_flags & IN6_IFF_AUTOCONF))
133978064Sume			continue;
134078064Sume
134178064Sume		if (ifa->ia6_ndpr == NULL) {
134278064Sume			/*
134378064Sume			 * This can happen when we first configure the address
134478064Sume			 * (i.e. the address exists, but the prefix does not).
134578064Sume			 * XXX: complicated relationships...
134678064Sume			 */
134778064Sume			continue;
134878064Sume		}
134978064Sume
135078064Sume		if (find_pfxlist_reachable_router(ifa->ia6_ndpr))
135178064Sume			break;
135278064Sume	}
135378064Sume	if (ifa) {
135478064Sume		for (ifa = in6_ifaddr; ifa; ifa = ifa->ia_next) {
135578064Sume			if ((ifa->ia6_flags & IN6_IFF_AUTOCONF) == 0)
135678064Sume				continue;
135778064Sume
135878064Sume			if (ifa->ia6_ndpr == NULL) /* XXX: see above. */
135978064Sume				continue;
136078064Sume
136178064Sume			if (find_pfxlist_reachable_router(ifa->ia6_ndpr))
136278064Sume				ifa->ia6_flags &= ~IN6_IFF_DETACHED;
136378064Sume			else
136478064Sume				ifa->ia6_flags |= IN6_IFF_DETACHED;
136578064Sume		}
136678064Sume	}
136762587Sitojun	else {
136878064Sume		for (ifa = in6_ifaddr; ifa; ifa = ifa->ia_next) {
136978064Sume			if ((ifa->ia6_flags & IN6_IFF_AUTOCONF) == 0)
137078064Sume				continue;
137178064Sume
137278064Sume			ifa->ia6_flags &= ~IN6_IFF_DETACHED;
137378064Sume		}
137453541Sshin	}
137553541Sshin}
137653541Sshin
137778064Sumeint
137878064Sumend6_prefix_onlink(pr)
137953541Sshin	struct nd_prefix *pr;
138053541Sshin{
138178064Sume	struct ifaddr *ifa;
138278064Sume	struct ifnet *ifp = pr->ndpr_ifp;
138378064Sume	struct sockaddr_in6 mask6;
138478064Sume	struct nd_prefix *opr;
138578064Sume	u_long rtflags;
138678064Sume	int error = 0;
138778064Sume	struct rtentry *rt = NULL;
138853541Sshin
138978064Sume	/* sanity check */
139078064Sume	if ((pr->ndpr_stateflags & NDPRF_ONLINK) != 0) {
139178064Sume		nd6log((LOG_ERR,
139278064Sume		    "nd6_prefix_onlink: %s/%d is already on-link\n",
139378064Sume		    ip6_sprintf(&pr->ndpr_prefix.sin6_addr), pr->ndpr_plen);
1394120856Sume		return (EEXIST));
139578064Sume	}
139678064Sume
139753541Sshin	/*
139878064Sume	 * Add the interface route associated with the prefix.  Before
139978064Sume	 * installing the route, check if there's the same prefix on another
140078064Sume	 * interface, and the prefix has already installed the interface route.
140178064Sume	 * Although such a configuration is expected to be rare, we explicitly
140278064Sume	 * allow it.
140353541Sshin	 */
140478064Sume	for (opr = nd_prefix.lh_first; opr; opr = opr->ndpr_next) {
140578064Sume		if (opr == pr)
140678064Sume			continue;
140778064Sume
140878064Sume		if ((opr->ndpr_stateflags & NDPRF_ONLINK) == 0)
140978064Sume			continue;
141078064Sume
141178064Sume		if (opr->ndpr_plen == pr->ndpr_plen &&
141278064Sume		    in6_are_prefix_equal(&pr->ndpr_prefix.sin6_addr,
1413120941Sume		    &opr->ndpr_prefix.sin6_addr, pr->ndpr_plen))
1414120856Sume			return (0);
141578064Sume	}
141678064Sume
141778064Sume	/*
1418120941Sume	 * We prefer link-local addresses as the associated interface address.
141978064Sume	 */
142078064Sume	/* search for a link-local addr */
142178064Sume	ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp,
1422120941Sume	    IN6_IFF_NOTREADY | IN6_IFF_ANYCAST);
142378064Sume	if (ifa == NULL) {
142478064Sume		/* XXX: freebsd does not have ifa_ifwithaf */
1425120941Sume		TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
142678064Sume			if (ifa->ifa_addr->sa_family == AF_INET6)
142778064Sume				break;
142878064Sume		}
142978064Sume		/* should we care about ia6_flags? */
143078064Sume	}
143178064Sume	if (ifa == NULL) {
143278064Sume		/*
143378064Sume		 * This can still happen, when, for example, we receive an RA
143478064Sume		 * containing a prefix with the L bit set and the A bit clear,
143578064Sume		 * after removing all IPv6 addresses on the receiving
143678064Sume		 * interface.  This should, of course, be rare though.
143778064Sume		 */
143878064Sume		nd6log((LOG_NOTICE,
143978064Sume		    "nd6_prefix_onlink: failed to find any ifaddr"
144078064Sume		    " to add route for a prefix(%s/%d) on %s\n",
144178064Sume		    ip6_sprintf(&pr->ndpr_prefix.sin6_addr),
144278064Sume		    pr->ndpr_plen, if_name(ifp)));
1443120856Sume		return (0);
144478064Sume	}
144578064Sume
144678064Sume	/*
144778064Sume	 * in6_ifinit() sets nd6_rtrequest to ifa_rtrequest for all ifaddrs.
144878064Sume	 * ifa->ifa_rtrequest = nd6_rtrequest;
144978064Sume	 */
145078064Sume	bzero(&mask6, sizeof(mask6));
145178064Sume	mask6.sin6_len = sizeof(mask6);
145278064Sume	mask6.sin6_addr = pr->ndpr_mask;
145378064Sume	rtflags = ifa->ifa_flags | RTF_CLONING | RTF_UP;
145478064Sume	if (nd6_need_cache(ifp)) {
145578064Sume		/* explicitly set in case ifa_flags does not set the flag. */
145678064Sume		rtflags |= RTF_CLONING;
145778064Sume	} else {
145878064Sume		/*
145978064Sume		 * explicitly clear the cloning bit in case ifa_flags sets it.
146078064Sume		 */
146178064Sume		rtflags &= ~RTF_CLONING;
146278064Sume	}
146378064Sume	error = rtrequest(RTM_ADD, (struct sockaddr *)&pr->ndpr_prefix,
1464120941Sume	    ifa->ifa_addr, (struct sockaddr *)&mask6, rtflags, &rt);
146578064Sume	if (error == 0) {
146678064Sume		if (rt != NULL) /* this should be non NULL, though */
146778064Sume			nd6_rtmsg(RTM_ADD, rt);
146878064Sume		pr->ndpr_stateflags |= NDPRF_ONLINK;
1469120941Sume	} else {
147078064Sume		nd6log((LOG_ERR, "nd6_prefix_onlink: failed to add route for a"
147178064Sume		    " prefix (%s/%d) on %s, gw=%s, mask=%s, flags=%lx "
147278064Sume		    "errno = %d\n",
147378064Sume		    ip6_sprintf(&pr->ndpr_prefix.sin6_addr),
147478064Sume		    pr->ndpr_plen, if_name(ifp),
147578064Sume		    ip6_sprintf(&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr),
147678064Sume		    ip6_sprintf(&mask6.sin6_addr), rtflags, error));
147778064Sume	}
147878064Sume
1479120727Ssam	if (rt != NULL) {
1480120727Ssam		RT_LOCK(rt);
148178064Sume		rt->rt_refcnt--;
1482120727Ssam		RT_UNLOCK(rt);
1483120727Ssam	}
148478064Sume
1485120856Sume	return (error);
148678064Sume}
148778064Sume
148878064Sumeint
148978064Sumend6_prefix_offlink(pr)
149078064Sume	struct nd_prefix *pr;
149178064Sume{
149278064Sume	int error = 0;
149378064Sume	struct ifnet *ifp = pr->ndpr_ifp;
149478064Sume	struct nd_prefix *opr;
149578064Sume	struct sockaddr_in6 sa6, mask6;
149678064Sume	struct rtentry *rt = NULL;
149778064Sume
149878064Sume	/* sanity check */
149978064Sume	if ((pr->ndpr_stateflags & NDPRF_ONLINK) == 0) {
150078064Sume		nd6log((LOG_ERR,
150178064Sume		    "nd6_prefix_offlink: %s/%d is already off-link\n",
150278064Sume		    ip6_sprintf(&pr->ndpr_prefix.sin6_addr), pr->ndpr_plen));
1503120856Sume		return (EEXIST);
150478064Sume	}
150578064Sume
150653541Sshin	bzero(&sa6, sizeof(sa6));
150753541Sshin	sa6.sin6_family = AF_INET6;
150853541Sshin	sa6.sin6_len = sizeof(sa6);
150953541Sshin	bcopy(&pr->ndpr_prefix.sin6_addr, &sa6.sin6_addr,
1510120941Sume	    sizeof(struct in6_addr));
151153541Sshin	bzero(&mask6, sizeof(mask6));
151253541Sshin	mask6.sin6_family = AF_INET6;
151353541Sshin	mask6.sin6_len = sizeof(sa6);
151453541Sshin	bcopy(&pr->ndpr_mask, &mask6.sin6_addr, sizeof(struct in6_addr));
151578064Sume	error = rtrequest(RTM_DELETE, (struct sockaddr *)&sa6, NULL,
1516120941Sume	    (struct sockaddr *)&mask6, 0, &rt);
151778064Sume	if (error == 0) {
151878064Sume		pr->ndpr_stateflags &= ~NDPRF_ONLINK;
151953541Sshin
152078064Sume		/* report the route deletion to the routing socket. */
152178064Sume		if (rt != NULL)
152278064Sume			nd6_rtmsg(RTM_DELETE, rt);
152353541Sshin
152478064Sume		/*
152578064Sume		 * There might be the same prefix on another interface,
152678064Sume		 * the prefix which could not be on-link just because we have
152778064Sume		 * the interface route (see comments in nd6_prefix_onlink).
152878064Sume		 * If there's one, try to make the prefix on-link on the
152978064Sume		 * interface.
153078064Sume		 */
153178064Sume		for (opr = nd_prefix.lh_first; opr; opr = opr->ndpr_next) {
153278064Sume			if (opr == pr)
153378064Sume				continue;
153453541Sshin
153578064Sume			if ((opr->ndpr_stateflags & NDPRF_ONLINK) != 0)
153678064Sume				continue;
153753541Sshin
153878064Sume			/*
153978064Sume			 * KAME specific: detached prefixes should not be
154078064Sume			 * on-link.
154178064Sume			 */
154278064Sume			if ((opr->ndpr_stateflags & NDPRF_DETACHED) != 0)
154378064Sume				continue;
154478064Sume
154578064Sume			if (opr->ndpr_plen == pr->ndpr_plen &&
154678064Sume			    in6_are_prefix_equal(&pr->ndpr_prefix.sin6_addr,
1547120941Sume			    &opr->ndpr_prefix.sin6_addr, pr->ndpr_plen)) {
154878064Sume				int e;
154978064Sume
155078064Sume				if ((e = nd6_prefix_onlink(opr)) != 0) {
155178064Sume					nd6log((LOG_ERR,
155278064Sume					    "nd6_prefix_offlink: failed to "
155378064Sume					    "recover a prefix %s/%d from %s "
155478064Sume					    "to %s (errno = %d)\n",
155578064Sume					    ip6_sprintf(&opr->ndpr_prefix.sin6_addr),
155678064Sume					    opr->ndpr_plen, if_name(ifp),
155778064Sume					    if_name(opr->ndpr_ifp), e));
155878064Sume				}
155978064Sume			}
156078064Sume		}
1561120941Sume	} else {
156278064Sume		/* XXX: can we still set the NDPRF_ONLINK flag? */
156378064Sume		nd6log((LOG_ERR,
156478064Sume		    "nd6_prefix_offlink: failed to delete route: "
156578064Sume		    "%s/%d on %s (errno = %d)\n",
156678064Sume		    ip6_sprintf(&sa6.sin6_addr), pr->ndpr_plen, if_name(ifp),
156778064Sume		    error));
156878064Sume	}
156953541Sshin
1570120941Sume	if (rt != NULL) {
1571108269Sru		RTFREE(rt);
1572120941Sume	}
157353541Sshin
1574120856Sume	return (error);
157553541Sshin}
157653541Sshin
157753541Sshinstatic struct in6_ifaddr *
157878064Sumein6_ifadd(pr, ifid)
157978064Sume	struct nd_prefix *pr;
158078064Sume	struct in6_addr  *ifid;   /* Mobile IPv6 addition */
158153541Sshin{
158278064Sume	struct ifnet *ifp = pr->ndpr_ifp;
158353541Sshin	struct ifaddr *ifa;
158478064Sume	struct in6_aliasreq ifra;
158578064Sume	struct in6_ifaddr *ia, *ib;
158678064Sume	int error, plen0;
158753541Sshin	struct in6_addr mask;
158878064Sume	int prefixlen = pr->ndpr_plen;
158953541Sshin
159053541Sshin	in6_len2mask(&mask, prefixlen);
159153541Sshin
159278064Sume	/*
159378064Sume	 * find a link-local address (will be interface ID).
159478064Sume	 * Is it really mandatory? Theoretically, a global or a site-local
159578064Sume	 * address can be configured without a link-local address, if we
159678064Sume	 * have a unique interface identifier...
159778064Sume	 *
159878064Sume	 * it is not mandatory to have a link-local address, we can generate
159978064Sume	 * interface identifier on the fly.  we do this because:
160078064Sume	 * (1) it should be the easiest way to find interface identifier.
160178064Sume	 * (2) RFC2462 5.4 suggesting the use of the same interface identifier
160278064Sume	 * for multiple addresses on a single interface, and possible shortcut
160378064Sume	 * of DAD.  we omitted DAD for this reason in the past.
1604120941Sume	 * (3) a user can prevent autoconfiguration of global address
160578064Sume	 * by removing link-local address by hand (this is partly because we
1606108533Sschweikh	 * don't have other way to control the use of IPv6 on an interface.
160778064Sume	 * this has been our design choice - cf. NRL's "ifconfig auto").
160878064Sume	 * (4) it is easier to manage when an interface has addresses
160978064Sume	 * with the same interface identifier, than to have multiple addresses
161078064Sume	 * with different interface identifiers.
161178064Sume	 *
161278064Sume	 * Mobile IPv6 addition: allow for caller to specify a wished interface
161378064Sume	 * ID. This is to not break connections when moving addresses between
161478064Sume	 * interfaces.
161578064Sume	 */
1616120941Sume	ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, 0); /* 0 is OK? */
161753541Sshin	if (ifa)
161853541Sshin		ib = (struct in6_ifaddr *)ifa;
161953541Sshin	else
162053541Sshin		return NULL;
162153541Sshin
162262587Sitojun#if 0 /* don't care link local addr state, and always do DAD */
162362587Sitojun	/* if link-local address is not eligible, do not autoconfigure. */
162462587Sitojun	if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_NOTREADY) {
162562587Sitojun		printf("in6_ifadd: link-local address not ready\n");
162662587Sitojun		return NULL;
162762587Sitojun	}
162862587Sitojun#endif
162962587Sitojun
163053541Sshin	/* prefixlen + ifidlen must be equal to 128 */
163178064Sume	plen0 = in6_mask2len(&ib->ia_prefixmask.sin6_addr, NULL);
163278064Sume	if (prefixlen != plen0) {
163378064Sume		nd6log((LOG_INFO, "in6_ifadd: wrong prefixlen for %s "
163478064Sume		    "(prefix=%d ifid=%d)\n",
163578064Sume		    if_name(ifp), prefixlen, 128 - plen0));
163653541Sshin		return NULL;
163753541Sshin	}
163853541Sshin
163953541Sshin	/* make ifaddr */
164053541Sshin
164178064Sume	bzero(&ifra, sizeof(ifra));
164278064Sume	/*
164378064Sume	 * in6_update_ifa() does not use ifra_name, but we accurately set it
164478064Sume	 * for safety.
164578064Sume	 */
164678064Sume	strncpy(ifra.ifra_name, if_name(ifp), sizeof(ifra.ifra_name));
164778064Sume	ifra.ifra_addr.sin6_family = AF_INET6;
164878064Sume	ifra.ifra_addr.sin6_len = sizeof(struct sockaddr_in6);
164978064Sume	/* prefix */
165078064Sume	bcopy(&pr->ndpr_prefix.sin6_addr, &ifra.ifra_addr.sin6_addr,
1651120941Sume	    sizeof(ifra.ifra_addr.sin6_addr));
165278064Sume	ifra.ifra_addr.sin6_addr.s6_addr32[0] &= mask.s6_addr32[0];
165378064Sume	ifra.ifra_addr.sin6_addr.s6_addr32[1] &= mask.s6_addr32[1];
165478064Sume	ifra.ifra_addr.sin6_addr.s6_addr32[2] &= mask.s6_addr32[2];
165578064Sume	ifra.ifra_addr.sin6_addr.s6_addr32[3] &= mask.s6_addr32[3];
165653541Sshin
165753541Sshin	/* interface ID */
165878064Sume	if (ifid == NULL || IN6_IS_ADDR_UNSPECIFIED(ifid))
165978064Sume		ifid = &ib->ia_addr.sin6_addr;
1660120941Sume	ifra.ifra_addr.sin6_addr.s6_addr32[0] |=
1661120941Sume	    (ifid->s6_addr32[0] & ~mask.s6_addr32[0]);
1662120941Sume	ifra.ifra_addr.sin6_addr.s6_addr32[1] |=
1663120941Sume	    (ifid->s6_addr32[1] & ~mask.s6_addr32[1]);
1664120941Sume	ifra.ifra_addr.sin6_addr.s6_addr32[2] |=
1665120941Sume	    (ifid->s6_addr32[2] & ~mask.s6_addr32[2]);
1666120941Sume	ifra.ifra_addr.sin6_addr.s6_addr32[3] |=
1667120941Sume	    (ifid->s6_addr32[3] & ~mask.s6_addr32[3]);
1668120941Sume
166978064Sume	/* new prefix mask. */
167078064Sume	ifra.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6);
167178064Sume	ifra.ifra_prefixmask.sin6_family = AF_INET6;
167278064Sume	bcopy(&mask, &ifra.ifra_prefixmask.sin6_addr,
1673120941Sume	    sizeof(ifra.ifra_prefixmask.sin6_addr));
167453541Sshin
167578064Sume	/*
167678064Sume	 * lifetime.
167778064Sume	 * XXX: in6_init_address_ltimes would override these values later.
1678120941Sume	 * We should reconsider this logic.
167978064Sume	 */
168078064Sume	ifra.ifra_lifetime.ia6t_vltime = pr->ndpr_vltime;
168178064Sume	ifra.ifra_lifetime.ia6t_pltime = pr->ndpr_pltime;
168253541Sshin
168378064Sume	/* XXX: scope zone ID? */
168453541Sshin
168578064Sume	ifra.ifra_flags |= IN6_IFF_AUTOCONF; /* obey autoconf */
168678064Sume	/*
168778064Sume	 * temporarily set the nopfx flag to avoid conflict.
168878064Sume	 * XXX: we should reconsider the entire mechanism about prefix
168978064Sume	 * manipulation.
169078064Sume	 */
169178064Sume	ifra.ifra_flags |= IN6_IFF_NOPFX;
169253541Sshin
169353541Sshin	/*
169478064Sume	 * keep the new address, regardless of the result of in6_update_ifa.
169578064Sume	 * XXX: this address is now meaningless.
169678064Sume	 * We should reconsider its role.
169753541Sshin	 */
169878064Sume	pr->ndpr_addr = ifra.ifra_addr.sin6_addr;
169978064Sume
170078064Sume	/* allocate ifaddr structure, link into chain, etc. */
170178064Sume	if ((error = in6_update_ifa(ifp, &ifra, NULL)) != 0) {
170278064Sume		nd6log((LOG_ERR,
170378064Sume		    "in6_ifadd: failed to make ifaddr %s on %s (errno=%d)\n",
170478064Sume		    ip6_sprintf(&ifra.ifra_addr.sin6_addr), if_name(ifp),
170578064Sume		    error));
1706120856Sume		return (NULL);	/* ifaddr must not have been allocated. */
170753541Sshin	}
170853541Sshin
170978064Sume	ia = in6ifa_ifpwithaddr(ifp, &ifra.ifra_addr.sin6_addr);
171053541Sshin
1711120941Sume	return (ia);		/* this is always non-NULL */
171253541Sshin}
171353541Sshin
171453541Sshinint
171578064Sumein6_tmpifadd(ia0, forcegen)
171678064Sume	const struct in6_ifaddr *ia0; /* corresponding public address */
171778407Sume	int forcegen;
171853541Sshin{
171978064Sume	struct ifnet *ifp = ia0->ia_ifa.ifa_ifp;
172078064Sume	struct in6_ifaddr *newia;
172178064Sume	struct in6_aliasreq ifra;
172278064Sume	int i, error;
172378064Sume	int trylimit = 3;	/* XXX: adhoc value */
172478064Sume	u_int32_t randid[2];
172578064Sume	time_t vltime0, pltime0;
172653541Sshin
172778064Sume	bzero(&ifra, sizeof(ifra));
172878064Sume	strncpy(ifra.ifra_name, if_name(ifp), sizeof(ifra.ifra_name));
172978064Sume	ifra.ifra_addr = ia0->ia_addr;
173078064Sume	/* copy prefix mask */
173178064Sume	ifra.ifra_prefixmask = ia0->ia_prefixmask;
173278064Sume	/* clear the old IFID */
173378064Sume	for (i = 0; i < 4; i++) {
1734120941Sume		ifra.ifra_addr.sin6_addr.s6_addr32[i] &=
1735120941Sume		    ifra.ifra_prefixmask.sin6_addr.s6_addr32[i];
173678064Sume	}
173753541Sshin
173878064Sume  again:
173978064Sume	in6_get_tmpifid(ifp, (u_int8_t *)randid,
1740120941Sume	    (const u_int8_t *)&ia0->ia_addr.sin6_addr.s6_addr[8], forcegen);
1741120941Sume	ifra.ifra_addr.sin6_addr.s6_addr32[2] |=
1742120941Sume	    (randid[0] & ~(ifra.ifra_prefixmask.sin6_addr.s6_addr32[2]));
1743120941Sume	ifra.ifra_addr.sin6_addr.s6_addr32[3] |=
1744120941Sume	    (randid[1] & ~(ifra.ifra_prefixmask.sin6_addr.s6_addr32[3]));
174553541Sshin
174678064Sume	/*
174778064Sume	 * If by chance the new temporary address is the same as an address
174878064Sume	 * already assigned to the interface, generate a new randomized
174978064Sume	 * interface identifier and repeat this step.
175078064Sume	 * RFC 3041 3.3 (4).
175178064Sume	 */
175278064Sume	if (in6ifa_ifpwithaddr(ifp, &ifra.ifra_addr.sin6_addr) != NULL) {
175378064Sume		if (trylimit-- == 0) {
175478064Sume			nd6log((LOG_NOTICE, "in6_tmpifadd: failed to find "
175578064Sume			    "a unique random IFID\n"));
1756120856Sume			return (EEXIST);
175778064Sume		}
175878064Sume		forcegen = 1;
175978064Sume		goto again;
176053541Sshin	}
176153541Sshin
176278064Sume	/*
176378064Sume	 * The Valid Lifetime is the lower of the Valid Lifetime of the
176478064Sume         * public address or TEMP_VALID_LIFETIME.
176578064Sume	 * The Preferred Lifetime is the lower of the Preferred Lifetime
176678064Sume         * of the public address or TEMP_PREFERRED_LIFETIME -
176778064Sume         * DESYNC_FACTOR.
176878064Sume	 */
176978064Sume	if (ia0->ia6_lifetime.ia6t_expire != 0) {
177078064Sume		vltime0 = IFA6_IS_INVALID(ia0) ? 0 :
1771120941Sume		    (ia0->ia6_lifetime.ia6t_expire - time_second);
177278064Sume		if (vltime0 > ip6_temp_valid_lifetime)
177378064Sume			vltime0 = ip6_temp_valid_lifetime;
177478064Sume	} else
177578064Sume		vltime0 = ip6_temp_valid_lifetime;
177678064Sume	if (ia0->ia6_lifetime.ia6t_preferred != 0) {
177778064Sume		pltime0 = IFA6_IS_DEPRECATED(ia0) ? 0 :
1778120941Sume		    (ia0->ia6_lifetime.ia6t_preferred - time_second);
177978064Sume		if (pltime0 > ip6_temp_preferred_lifetime - ip6_desync_factor){
178078064Sume			pltime0 = ip6_temp_preferred_lifetime -
1781120941Sume			    ip6_desync_factor;
178278064Sume		}
178378064Sume	} else
178478064Sume		pltime0 = ip6_temp_preferred_lifetime - ip6_desync_factor;
178578064Sume	ifra.ifra_lifetime.ia6t_vltime = vltime0;
178678064Sume	ifra.ifra_lifetime.ia6t_pltime = pltime0;
178753541Sshin
178878064Sume	/*
178978064Sume	 * A temporary address is created only if this calculated Preferred
179078064Sume	 * Lifetime is greater than REGEN_ADVANCE time units.
179178064Sume	 */
179278064Sume	if (ifra.ifra_lifetime.ia6t_pltime <= ip6_temp_regen_advance)
1793120856Sume		return (0);
179453541Sshin
179578064Sume	/* XXX: scope zone ID? */
179678064Sume
179778064Sume	ifra.ifra_flags |= (IN6_IFF_AUTOCONF|IN6_IFF_TEMPORARY);
179878064Sume
179978064Sume	/* allocate ifaddr structure, link into chain, etc. */
180078064Sume	if ((error = in6_update_ifa(ifp, &ifra, NULL)) != 0)
1801120856Sume		return (error);
180278064Sume
180378064Sume	newia = in6ifa_ifpwithaddr(ifp, &ifra.ifra_addr.sin6_addr);
180478064Sume	if (newia == NULL) {	/* XXX: can it happen? */
180578064Sume		nd6log((LOG_ERR,
180678064Sume		    "in6_tmpifadd: ifa update succeeded, but we got "
180778064Sume		    "no ifaddr\n"));
1808120856Sume		return (EINVAL); /* XXX */
180953541Sshin	}
181078064Sume	newia->ia6_ndpr = ia0->ia6_ndpr;
181178064Sume	newia->ia6_ndpr->ndpr_refcnt++;
181253541Sshin
181378407Sume	/*
181478407Sume	 * A newly added address might affect the status of other addresses.
181578407Sume	 * XXX: when the temporary address is generated with a new public
181678407Sume	 * address, the onlink check is redundant.  However, it would be safe
181778407Sume	 * to do the check explicitly everywhere a new address is generated,
181878407Sume	 * and, in fact, we surely need the check when we create a new
181978407Sume	 * temporary address due to deprecation of an old temporary address.
182078407Sume	 */
182178407Sume	pfxlist_onlink_check();
182278407Sume
1823120856Sume	return (0);
1824120941Sume}
182553541Sshin
182653541Sshinint
182753541Sshinin6_init_prefix_ltimes(struct nd_prefix *ndpr)
182853541Sshin{
182978064Sume	/* check if preferred lifetime > valid lifetime.  RFC2462 5.5.3 (c) */
183053541Sshin	if (ndpr->ndpr_pltime > ndpr->ndpr_vltime) {
183178064Sume		nd6log((LOG_INFO, "in6_init_prefix_ltimes: preferred lifetime"
183253541Sshin		    "(%d) is greater than valid lifetime(%d)\n",
183378064Sume		    (u_int)ndpr->ndpr_pltime, (u_int)ndpr->ndpr_vltime));
183453541Sshin		return (EINVAL);
183553541Sshin	}
183653541Sshin	if (ndpr->ndpr_pltime == ND6_INFINITE_LIFETIME)
183753541Sshin		ndpr->ndpr_preferred = 0;
183853541Sshin	else
183953541Sshin		ndpr->ndpr_preferred = time_second + ndpr->ndpr_pltime;
184053541Sshin	if (ndpr->ndpr_vltime == ND6_INFINITE_LIFETIME)
184153541Sshin		ndpr->ndpr_expire = 0;
184253541Sshin	else
184353541Sshin		ndpr->ndpr_expire = time_second + ndpr->ndpr_vltime;
184453541Sshin
184553541Sshin	return 0;
184653541Sshin}
184753541Sshin
184853541Sshinstatic void
184978064Sumein6_init_address_ltimes(struct nd_prefix *new, struct in6_addrlifetime *lt6)
185053541Sshin{
185178064Sume	/* init ia6t_expire */
185278064Sume	if (lt6->ia6t_vltime == ND6_INFINITE_LIFETIME)
185378064Sume		lt6->ia6t_expire = 0;
185478064Sume	else {
185578064Sume		lt6->ia6t_expire = time_second;
185678064Sume		lt6->ia6t_expire += lt6->ia6t_vltime;
185753541Sshin	}
185862587Sitojun
185953541Sshin	/* init ia6t_preferred */
186053541Sshin	if (lt6->ia6t_pltime == ND6_INFINITE_LIFETIME)
186153541Sshin		lt6->ia6t_preferred = 0;
186253541Sshin	else {
186353541Sshin		lt6->ia6t_preferred = time_second;
186453541Sshin		lt6->ia6t_preferred += lt6->ia6t_pltime;
186553541Sshin	}
186653541Sshin}
186753541Sshin
186853541Sshin/*
186953541Sshin * Delete all the routing table entries that use the specified gateway.
187053541Sshin * XXX: this function causes search through all entries of routing table, so
187153541Sshin * it shouldn't be called when acting as a router.
187253541Sshin */
187353541Sshinvoid
187453541Sshinrt6_flush(gateway, ifp)
187578064Sume	struct in6_addr *gateway;
187678064Sume	struct ifnet *ifp;
187753541Sshin{
187853541Sshin	struct radix_node_head *rnh = rt_tables[AF_INET6];
187953541Sshin	int s = splnet();
188053541Sshin
188153541Sshin	/* We'll care only link-local addresses */
188253541Sshin	if (!IN6_IS_ADDR_LINKLOCAL(gateway)) {
188353541Sshin		splx(s);
188453541Sshin		return;
188553541Sshin	}
188653541Sshin	/* XXX: hack for KAME's link-local address kludge */
188753541Sshin	gateway->s6_addr16[1] = htons(ifp->if_index);
188853541Sshin
1889108250Shsu	RADIX_NODE_HEAD_LOCK(rnh);
189053541Sshin	rnh->rnh_walktree(rnh, rt6_deleteroute, (void *)gateway);
1891108250Shsu	RADIX_NODE_HEAD_UNLOCK(rnh);
189253541Sshin	splx(s);
189353541Sshin}
189453541Sshin
189553541Sshinstatic int
189653541Sshinrt6_deleteroute(rn, arg)
189753541Sshin	struct radix_node *rn;
189853541Sshin	void *arg;
189953541Sshin{
190053541Sshin#define SIN6(s)	((struct sockaddr_in6 *)s)
190153541Sshin	struct rtentry *rt = (struct rtentry *)rn;
190253541Sshin	struct in6_addr *gate = (struct in6_addr *)arg;
190353541Sshin
190453541Sshin	if (rt->rt_gateway == NULL || rt->rt_gateway->sa_family != AF_INET6)
1905120856Sume		return (0);
190653541Sshin
1907120941Sume	if (!IN6_ARE_ADDR_EQUAL(gate, &SIN6(rt->rt_gateway)->sin6_addr)) {
1908120856Sume		return (0);
1909120941Sume	}
191053541Sshin
191153541Sshin	/*
191278064Sume	 * Do not delete a static route.
191378064Sume	 * XXX: this seems to be a bit ad-hoc. Should we consider the
191478064Sume	 * 'cloned' bit instead?
191578064Sume	 */
191678064Sume	if ((rt->rt_flags & RTF_STATIC) != 0)
1917120856Sume		return (0);
191878064Sume
191978064Sume	/*
192053541Sshin	 * We delete only host route. This means, in particular, we don't
192153541Sshin	 * delete default route.
192253541Sshin	 */
192353541Sshin	if ((rt->rt_flags & RTF_HOST) == 0)
1924120856Sume		return (0);
192553541Sshin
1926120941Sume	return (rtrequest(RTM_DELETE, rt_key(rt), rt->rt_gateway,
1927120941Sume	    rt_mask(rt), rt->rt_flags, 0));
192853541Sshin#undef SIN6
192953541Sshin}
193062587Sitojun
193162587Sitojunint
193262587Sitojunnd6_setdefaultiface(ifindex)
193362587Sitojun	int ifindex;
193462587Sitojun{
193562587Sitojun	int error = 0;
193662587Sitojun
193762587Sitojun	if (ifindex < 0 || if_index < ifindex)
1938120856Sume		return (EINVAL);
193962587Sitojun
194062587Sitojun	if (nd6_defifindex != ifindex) {
194162587Sitojun		nd6_defifindex = ifindex;
194262587Sitojun		if (nd6_defifindex > 0)
194383130Sjlemon			nd6_defifp = ifnet_byindex(nd6_defifindex);
194462587Sitojun		else
194562587Sitojun			nd6_defifp = NULL;
194662587Sitojun
194762587Sitojun		/*
194862587Sitojun		 * If the Default Router List is empty, install a route
194962587Sitojun		 * to the specified interface as default or remove the default
195062587Sitojun		 * route when the default interface becomes canceled.
195162587Sitojun		 * The check for the queue is actually redundant, but
195262587Sitojun		 * we do this here to avoid re-install the default route
195362587Sitojun		 * if the list is NOT empty.
195462587Sitojun		 */
195562587Sitojun		if (TAILQ_FIRST(&nd_defrouter) == NULL)
195662587Sitojun			defrouter_select();
195762587Sitojun
195862587Sitojun		/*
195962587Sitojun		 * Our current implementation assumes one-to-one maping between
196062587Sitojun		 * interfaces and links, so it would be natural to use the
196162587Sitojun		 * default interface as the default link.
196262587Sitojun		 */
196362587Sitojun		scope6_setdefault(nd6_defifp);
196462587Sitojun	}
196562587Sitojun
1966120856Sume	return (error);
196762587Sitojun}
1968