config.c revision 118968
171333Sitojun/*	$FreeBSD: head/usr.sbin/rtadvd/config.c 118968 2003-08-15 19:13:53Z ume $	*/
2118968Sume/*	$KAME: config.c,v 1.84 2003/08/05 12:34:23 itojun Exp $	*/
362656Skris
455505Sshin/*
555505Sshin * Copyright (C) 1998 WIDE Project.
655505Sshin * All rights reserved.
762656Skris *
855505Sshin * Redistribution and use in source and binary forms, with or without
955505Sshin * modification, are permitted provided that the following conditions
1055505Sshin * are met:
1155505Sshin * 1. Redistributions of source code must retain the above copyright
1255505Sshin *    notice, this list of conditions and the following disclaimer.
1355505Sshin * 2. Redistributions in binary form must reproduce the above copyright
1455505Sshin *    notice, this list of conditions and the following disclaimer in the
1555505Sshin *    documentation and/or other materials provided with the distribution.
1655505Sshin * 3. Neither the name of the project nor the names of its contributors
1755505Sshin *    may be used to endorse or promote products derived from this software
1855505Sshin *    without specific prior written permission.
1962656Skris *
2055505Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
2155505Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2255505Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2355505Sshin * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2455505Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2555505Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2655505Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2755505Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2855505Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2955505Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3055505Sshin * SUCH DAMAGE.
3155505Sshin */
3255505Sshin
3355505Sshin#include <sys/param.h>
3455505Sshin#include <sys/ioctl.h>
3555505Sshin#include <sys/socket.h>
3655505Sshin#include <sys/time.h>
3778064Sume#include <sys/sysctl.h>
3855505Sshin
3955505Sshin#include <net/if.h>
4055505Sshin#include <net/if_var.h>
4155505Sshin#include <net/route.h>
4255505Sshin#include <net/if_dl.h>
4355505Sshin
4455505Sshin#include <netinet/in.h>
4555505Sshin#include <netinet/in_var.h>
4657120Sshin#include <netinet/ip6.h>
4755505Sshin#include <netinet6/ip6_var.h>
4857120Sshin#include <netinet/icmp6.h>
4955505Sshin
5055505Sshin#include <arpa/inet.h>
5155505Sshin
5255505Sshin#include <stdio.h>
5355505Sshin#include <syslog.h>
5455505Sshin#include <errno.h>
5555505Sshin#include <string.h>
5655505Sshin#include <stdlib.h>
5755505Sshin#include <unistd.h>
5878064Sume#include <ifaddrs.h>
5955505Sshin
6055505Sshin#include "rtadvd.h"
6155505Sshin#include "advcap.h"
6255505Sshin#include "timer.h"
6355505Sshin#include "if.h"
6455505Sshin#include "config.h"
6555505Sshin
6698172Sumestatic time_t prefix_timo = (60 * 120);	/* 2 hours.
6798172Sume					 * XXX: should be configurable. */
6898172Sumeextern struct rainfo *ralist;
6998172Sume
7098172Sumestatic struct rtadvd_timer *prefix_timeout __P((void *));
71118968Sumestatic void makeentry __P((char *, size_t, int, char *));
7278064Sumestatic int getinet6sysctl __P((int));
7355505Sshin
7455505Sshinvoid
7555505Sshingetconfig(intface)
7655505Sshin	char *intface;
7755505Sshin{
78118968Sume	int stat, i;
7955505Sshin	char tbuf[BUFSIZ];
8055505Sshin	struct rainfo *tmp;
8155505Sshin	long val;
82118784Sume	int64_t val64;
8355505Sshin	char buf[BUFSIZ];
8455505Sshin	char *bp = buf;
85118968Sume	char *addr, *flagstr;
8678064Sume	static int forwarding = -1;
8755505Sshin
8862656Skris#define MUSTHAVE(var, cap)	\
8962656Skris    do {								\
90118784Sume	int64_t t;							\
9155505Sshin	if ((t = agetnum(cap)) < 0) {					\
9255505Sshin		fprintf(stderr, "rtadvd: need %s for interface %s\n",	\
9355505Sshin			cap, intface);					\
9455505Sshin		exit(1);						\
9555505Sshin	}								\
9655505Sshin	var = t;							\
9762656Skris     } while (0)
9862656Skris#define MAYHAVE(var, cap, def)	\
9962656Skris     do {								\
10055505Sshin	if ((var = agetnum(cap)) < 0)					\
10155505Sshin		var = def;						\
10262656Skris     } while (0)
10355505Sshin
10455505Sshin	if ((stat = agetent(tbuf, intface)) <= 0) {
10555505Sshin		memset(tbuf, 0, sizeof(tbuf));
10655505Sshin		syslog(LOG_INFO,
10755505Sshin		       "<%s> %s isn't defined in the configuration file"
10855505Sshin		       " or the configuration file doesn't exist."
10955505Sshin		       " Treat it as default",
110118660Sume		        __func__, intface);
11155505Sshin	}
11255505Sshin
11355505Sshin	tmp = (struct rainfo *)malloc(sizeof(*ralist));
114118831Sume	if (tmp == NULL) {
115118831Sume		syslog(LOG_INFO, "<%s> %s: can't allocate enough memory",
116118831Sume		    __func__, intface);
117118831Sume		exit(1);
118118831Sume	}
11955505Sshin	memset(tmp, 0, sizeof(*tmp));
12055505Sshin	tmp->prefix.next = tmp->prefix.prev = &tmp->prefix;
121118968Sume#ifdef ROUTEINFO
12278064Sume	tmp->route.next = tmp->route.prev = &tmp->route;
123118968Sume#endif
12455505Sshin
12578064Sume	/* check if we are allowed to forward packets (if not determined) */
12678064Sume	if (forwarding < 0) {
12778064Sume		if ((forwarding = getinet6sysctl(IPV6CTL_FORWARDING)) < 0)
12878064Sume			exit(1);
12978064Sume	}
13078064Sume
13155505Sshin	/* get interface information */
13255505Sshin	if (agetflag("nolladdr"))
13355505Sshin		tmp->advlinkopt = 0;
13455505Sshin	else
13555505Sshin		tmp->advlinkopt = 1;
13655505Sshin	if (tmp->advlinkopt) {
13755505Sshin		if ((tmp->sdl = if_nametosdl(intface)) == NULL) {
13855505Sshin			syslog(LOG_ERR,
13955505Sshin			       "<%s> can't get information of %s",
140118660Sume			       __func__, intface);
14155505Sshin			exit(1);
14255505Sshin		}
14355505Sshin		tmp->ifindex = tmp->sdl->sdl_index;
14455505Sshin	} else
14555505Sshin		tmp->ifindex = if_nametoindex(intface);
14655505Sshin	strncpy(tmp->ifname, intface, sizeof(tmp->ifname));
14755505Sshin	if ((tmp->phymtu = if_getmtu(intface)) == 0) {
14855505Sshin		tmp->phymtu = IPV6_MMTU;
14955505Sshin		syslog(LOG_WARNING,
15055505Sshin		       "<%s> can't get interface mtu of %s. Treat as %d",
151118660Sume		       __func__, intface, IPV6_MMTU);
15255505Sshin	}
15355505Sshin
15455505Sshin	/*
15555505Sshin	 * set router configuration variables.
15655505Sshin	 */
15755505Sshin	MAYHAVE(val, "maxinterval", DEF_MAXRTRADVINTERVAL);
15855505Sshin	if (val < MIN_MAXINTERVAL || val > MAX_MAXINTERVAL) {
15955505Sshin		syslog(LOG_ERR,
160118968Sume		       "<%s> maxinterval (%ld) on %s is invalid "
161118968Sume		       "(must be between %u and %u)", __func__, val,
162118968Sume		       intface, MIN_MAXINTERVAL, MAX_MAXINTERVAL);
16355505Sshin		exit(1);
16455505Sshin	}
16555505Sshin	tmp->maxinterval = (u_int)val;
16655505Sshin	MAYHAVE(val, "mininterval", tmp->maxinterval/3);
16755505Sshin	if (val < MIN_MININTERVAL || val > (tmp->maxinterval * 3) / 4) {
16855505Sshin		syslog(LOG_ERR,
169118968Sume		       "<%s> mininterval (%ld) on %s is invalid "
170118968Sume		       "(must be between %d and %d)",
171118968Sume		       __func__, val, intface, MIN_MININTERVAL,
17255505Sshin		       (tmp->maxinterval * 3) / 4);
17355505Sshin		exit(1);
17455505Sshin	}
17555505Sshin	tmp->mininterval = (u_int)val;
17655505Sshin
17755505Sshin	MAYHAVE(val, "chlim", DEF_ADVCURHOPLIMIT);
17855505Sshin	tmp->hoplimit = val & 0xff;
17955505Sshin
180118968Sume	if ((flagstr = (char *)agetstr("raflags", &bp))) {
181118968Sume		val = 0;
182118968Sume		if (strchr(flagstr, 'm'))
183118968Sume			val |= ND_RA_FLAG_MANAGED;
184118968Sume		if (strchr(flagstr, 'o'))
185118968Sume			val |= ND_RA_FLAG_OTHER;
186118968Sume		if (strchr(flagstr, 'h'))
187118968Sume			val |= ND_RA_FLAG_RTPREF_HIGH;
188118968Sume		if (strchr(flagstr, 'l')) {
189118968Sume			if ((val & ND_RA_FLAG_RTPREF_HIGH)) {
190118968Sume				syslog(LOG_ERR, "<%s> the \'h\' and \'l\'"
191118968Sume				    " router flags are exclusive", __func__);
192118968Sume				exit(1);
193118968Sume			}
194118968Sume			val |= ND_RA_FLAG_RTPREF_LOW;
195118968Sume		}
196118968Sume	} else {
197118968Sume		MAYHAVE(val, "raflags", 0);
198118968Sume	}
19978064Sume	tmp->managedflg = val & ND_RA_FLAG_MANAGED;
20055505Sshin	tmp->otherflg = val & ND_RA_FLAG_OTHER;
20178064Sume#ifndef ND_RA_FLAG_RTPREF_MASK
20278064Sume#define ND_RA_FLAG_RTPREF_MASK	0x18 /* 00011000 */
20378064Sume#define ND_RA_FLAG_RTPREF_RSV	0x10 /* 00010000 */
20478064Sume#endif
20578064Sume	tmp->rtpref = val & ND_RA_FLAG_RTPREF_MASK;
20678064Sume	if (tmp->rtpref == ND_RA_FLAG_RTPREF_RSV) {
207118968Sume		syslog(LOG_ERR, "<%s> invalid router preference (%02x) on %s",
208118968Sume		       __func__, tmp->rtpref, intface);
20978064Sume		exit(1);
21078064Sume	}
21155505Sshin
21255505Sshin	MAYHAVE(val, "rltime", tmp->maxinterval * 3);
21355505Sshin	if (val && (val < tmp->maxinterval || val > MAXROUTERLIFETIME)) {
21455505Sshin		syslog(LOG_ERR,
215118968Sume		       "<%s> router lifetime (%ld) on %s is invalid "
216118968Sume		       "(must be 0 or between %d and %d)",
217118968Sume		       __func__, val, intface,
218118968Sume		       tmp->maxinterval,
219118968Sume		       MAXROUTERLIFETIME);
22055505Sshin		exit(1);
22155505Sshin	}
22278064Sume	/*
22378064Sume	 * Basically, hosts MUST NOT send Router Advertisement messages at any
22478064Sume	 * time (RFC 2461, Section 6.2.3). However, it would sometimes be
22578064Sume	 * useful to allow hosts to advertise some parameters such as prefix
22678064Sume	 * information and link MTU. Thus, we allow hosts to invoke rtadvd
22778064Sume	 * only when router lifetime (on every advertising interface) is
22878064Sume	 * explicitly set zero. (see also the above section)
22978064Sume	 */
23078064Sume	if (val && forwarding == 0) {
231118968Sume		syslog(LOG_ERR,
23278064Sume		       "<%s> non zero router lifetime is specified for %s, "
233118968Sume		       "which must not be allowed for hosts.  you must "
234118968Sume		       "change router lifetime or enable IPv6 forwarding.",
235118660Sume		       __func__, intface);
23678064Sume		exit(1);
23778064Sume	}
23855505Sshin	tmp->lifetime = val & 0xffff;
23955505Sshin
24055505Sshin	MAYHAVE(val, "rtime", DEF_ADVREACHABLETIME);
241118968Sume	if (val < 0 || val > MAXREACHABLETIME) {
24255505Sshin		syslog(LOG_ERR,
243118968Sume		       "<%s> reachable time (%ld) on %s is invalid "
244118968Sume		       "(must be no greater than %d)",
245118968Sume		       __func__, val, intface, MAXREACHABLETIME);
24655505Sshin		exit(1);
24755505Sshin	}
24855505Sshin	tmp->reachabletime = (u_int32_t)val;
24955505Sshin
25078064Sume	MAYHAVE(val64, "retrans", DEF_ADVRETRANSTIMER);
25178064Sume	if (val64 < 0 || val64 > 0xffffffff) {
252118968Sume		syslog(LOG_ERR, "<%s> retrans time (%lld) on %s out of range",
253118968Sume		       __func__, (long long)val64, intface);
25455505Sshin		exit(1);
25555505Sshin	}
25678064Sume	tmp->retranstimer = (u_int32_t)val64;
25755505Sshin
258118968Sume	if (agetnum("hapref") != -1 || agetnum("hatime") != -1) {
25978064Sume		syslog(LOG_ERR,
26078064Sume		       "<%s> mobile-ip6 configuration not supported",
261118660Sume		       __func__);
26278064Sume		exit(1);
26378064Sume	}
26455505Sshin	/* prefix information */
26578064Sume
26678064Sume	/*
267118664Sume	 * This is an implementation specific parameter to consider
26878064Sume	 * link propagation delays and poorly synchronized clocks when
26978064Sume	 * checking consistency of advertised lifetimes.
27078064Sume	 */
27178064Sume	MAYHAVE(val, "clockskew", 0);
27278064Sume	tmp->clockskew = val;
27378064Sume
274118968Sume	tmp->pfxs = 0;
275118968Sume	for (i = -1; i < MAXPREFIX; i++) {
276118968Sume		struct prefix *pfx;
277118968Sume		char entbuf[256];
278118968Sume
279118968Sume		makeentry(entbuf, sizeof(entbuf), i, "addr");
280118968Sume		addr = (char *)agetstr(entbuf, &bp);
281118968Sume		if (addr == NULL)
282118968Sume			continue;
283118968Sume
284118968Sume		/* allocate memory to store prefix information */
285118968Sume		if ((pfx = malloc(sizeof(struct prefix))) == NULL) {
28655505Sshin			syslog(LOG_ERR,
287118968Sume			       "<%s> can't allocate enough memory",
288118968Sume			       __func__);
28955505Sshin			exit(1);
29055505Sshin		}
291118968Sume		memset(pfx, 0, sizeof(*pfx));
29255505Sshin
293118968Sume		/* link into chain */
294118968Sume		insque(pfx, &tmp->prefix);
295118968Sume		tmp->pfxs++;
296118968Sume		pfx->rainfo = tmp;
29771437Sume
298118968Sume		pfx->origin = PREFIX_FROM_CONFIG;
29955505Sshin
300118968Sume		if (inet_pton(AF_INET6, addr, &pfx->prefix) != 1) {
301118968Sume			syslog(LOG_ERR,
302118968Sume			       "<%s> inet_pton failed for %s",
303118968Sume			       __func__, addr);
304118968Sume			exit(1);
305118968Sume		}
306118968Sume		if (IN6_IS_ADDR_MULTICAST(&pfx->prefix)) {
307118968Sume			syslog(LOG_ERR,
308118968Sume			       "<%s> multicast prefix (%s) must "
309118968Sume			       "not be advertised on %s",
310118968Sume			       __func__, addr, intface);
311118968Sume			exit(1);
312118968Sume		}
313118968Sume		if (IN6_IS_ADDR_LINKLOCAL(&pfx->prefix))
314118968Sume			syslog(LOG_NOTICE,
315118968Sume			       "<%s> link-local prefix (%s) will be"
316118968Sume			       " advertised on %s",
317118968Sume			       __func__, addr, intface);
31862656Skris
319118968Sume		makeentry(entbuf, sizeof(entbuf), i, "prefixlen");
320118968Sume		MAYHAVE(val, entbuf, 64);
321118968Sume		if (val < 0 || val > 128) {
322118968Sume			syslog(LOG_ERR, "<%s> prefixlen (%ld) for %s "
323118968Sume			       "on %s out of range",
324118968Sume			       __func__, val, addr, intface);
325118968Sume			exit(1);
326118968Sume		}
327118968Sume		pfx->prefixlen = (int)val;
32855505Sshin
329118968Sume		makeentry(entbuf, sizeof(entbuf), i, "pinfoflags");
330118968Sume		if ((flagstr = (char *)agetstr(entbuf, &bp))) {
331118968Sume			val = 0;
332118968Sume			if (strchr(flagstr, 'l'))
333118968Sume				val |= ND_OPT_PI_FLAG_ONLINK;
334118968Sume			if (strchr(flagstr, 'a'))
335118968Sume				val |= ND_OPT_PI_FLAG_AUTO;
336118968Sume		} else {
337118968Sume			MAYHAVE(val, entbuf,
338118968Sume			    (ND_OPT_PI_FLAG_ONLINK|ND_OPT_PI_FLAG_AUTO));
339118968Sume		}
340118968Sume		pfx->onlinkflg = val & ND_OPT_PI_FLAG_ONLINK;
341118968Sume		pfx->autoconfflg = val & ND_OPT_PI_FLAG_AUTO;
34255505Sshin
343118968Sume		makeentry(entbuf, sizeof(entbuf), i, "vltime");
344118968Sume		MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME);
345118968Sume		if (val64 < 0 || val64 > 0xffffffff) {
346118968Sume			syslog(LOG_ERR, "<%s> vltime (%lld) for "
347118968Sume			    "%s/%d on %s is out of range",
348118968Sume			    __func__, (long long)val64,
349118968Sume			    addr, pfx->prefixlen, intface);
350118968Sume			exit(1);
351118968Sume		}
352118968Sume		pfx->validlifetime = (u_int32_t)val64;
35355505Sshin
354118968Sume		makeentry(entbuf, sizeof(entbuf), i, "vltimedecr");
355118968Sume		if (agetflag(entbuf)) {
356118968Sume			struct timeval now;
357118968Sume			gettimeofday(&now, 0);
358118968Sume			pfx->vltimeexpire =
359118968Sume				now.tv_sec + pfx->validlifetime;
360118968Sume		}
36178064Sume
362118968Sume		makeentry(entbuf, sizeof(entbuf), i, "pltime");
363118968Sume		MAYHAVE(val64, entbuf, DEF_ADVPREFERREDLIFETIME);
364118968Sume		if (val64 < 0 || val64 > 0xffffffff) {
365118968Sume			syslog(LOG_ERR,
366118968Sume			    "<%s> pltime (%lld) for %s/%d on %s "
367118968Sume			    "is out of range",
368118968Sume			    __func__, (long long)val64,
369118968Sume			    addr, pfx->prefixlen, intface);
370118968Sume			exit(1);
371118968Sume		}
372118968Sume		pfx->preflifetime = (u_int32_t)val64;
37355505Sshin
374118968Sume		makeentry(entbuf, sizeof(entbuf), i, "pltimedecr");
375118968Sume		if (agetflag(entbuf)) {
376118968Sume			struct timeval now;
377118968Sume			gettimeofday(&now, 0);
378118968Sume			pfx->pltimeexpire =
379118968Sume				now.tv_sec + pfx->preflifetime;
38055505Sshin		}
38155505Sshin	}
382118968Sume	if (tmp->pfxs == 0)
383118968Sume		get_prefix(tmp);
38455505Sshin
38555505Sshin	MAYHAVE(val, "mtu", 0);
38655505Sshin	if (val < 0 || val > 0xffffffff) {
38755505Sshin		syslog(LOG_ERR,
388118968Sume		       "<%s> mtu (%ld) on %s out of range",
389118968Sume		       __func__, val, intface);
39055505Sshin		exit(1);
39155505Sshin	}
39255505Sshin	tmp->linkmtu = (u_int32_t)val;
39355505Sshin	if (tmp->linkmtu == 0) {
39455505Sshin		char *mtustr;
39555505Sshin
39655505Sshin		if ((mtustr = (char *)agetstr("mtu", &bp)) &&
39755505Sshin		    strcmp(mtustr, "auto") == 0)
39855505Sshin			tmp->linkmtu = tmp->phymtu;
39955505Sshin	}
40055505Sshin	else if (tmp->linkmtu < IPV6_MMTU || tmp->linkmtu > tmp->phymtu) {
40155505Sshin		syslog(LOG_ERR,
402118968Sume		       "<%s> advertised link mtu (%lu) on %s is invalid (must "
403118968Sume		       "be between least MTU (%d) and physical link MTU (%d)",
404118968Sume		       __func__, (unsigned long)tmp->linkmtu, intface,
405118968Sume		       IPV6_MMTU, tmp->phymtu);
40655505Sshin		exit(1);
40755505Sshin	}
40855505Sshin
40978064Sume	/* route information */
410118968Sume#ifdef ROUTEINFO
411118968Sume	tmp->routes = 0;
412118968Sume	for (i = -1; i < MAXROUTE; i++) {
41378064Sume		struct rtinfo *rti;
414118968Sume		char entbuf[256], oentbuf[256];
41578064Sume
416118968Sume		makeentry(entbuf, sizeof(entbuf), i, "rtprefix");
417118968Sume		addr = (char *)agetstr(entbuf, &bp);
418118968Sume		if (addr == NULL) {
419118968Sume			makeentry(oentbuf, sizeof(oentbuf), i, "rtrprefix");
420118968Sume			addr = (char *)agetstr(oentbuf, &bp);
421118968Sume			if (addr) {
422118968Sume				fprintf(stderr, "%s was obsoleted.  Use %s.\n",
423118968Sume					oentbuf, entbuf);
424118968Sume			}
425118968Sume		}
426118968Sume		if (addr == NULL)
427118968Sume			continue;
428118968Sume
42978064Sume		/* allocate memory to store prefix information */
43078064Sume		if ((rti = malloc(sizeof(struct rtinfo))) == NULL) {
43178064Sume			syslog(LOG_ERR,
43278064Sume			       "<%s> can't allocate enough memory",
433118660Sume			       __func__);
43478064Sume			exit(1);
43578064Sume		}
43678064Sume		memset(rti, 0, sizeof(*rti));
43778064Sume
43878064Sume		/* link into chain */
43978064Sume		insque(rti, &tmp->route);
440118968Sume		tmp->routes++;
44178064Sume
44278064Sume		if (inet_pton(AF_INET6, addr, &rti->prefix) != 1) {
443118968Sume			syslog(LOG_ERR, "<%s> inet_pton failed for %s",
444118660Sume			       __func__, addr);
44578064Sume			exit(1);
44678064Sume		}
44778064Sume#if 0
44878064Sume		/*
44978064Sume		 * XXX: currently there's no restriction in route information
450118968Sume		 * prefix according to
451118968Sume		 * draft-ietf-ipngwg-router-selection-00.txt.
452118968Sume		 * However, I think the similar restriction be necessary.
45378064Sume		 */
45478064Sume		MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME);
45578064Sume		if (IN6_IS_ADDR_MULTICAST(&rti->prefix)) {
45678064Sume			syslog(LOG_ERR,
45778064Sume			       "<%s> multicast route (%s) must "
458118968Sume			       "not be advertised on %s",
459118660Sume			       __func__, addr, intface);
46078064Sume			exit(1);
46178064Sume		}
46278064Sume		if (IN6_IS_ADDR_LINKLOCAL(&rti->prefix)) {
46378064Sume			syslog(LOG_NOTICE,
464118968Sume			       "<%s> link-local route (%s) will "
465118968Sume			       "be advertised on %s",
466118660Sume			       __func__, addr, intface);
46778064Sume			exit(1);
46878064Sume		}
46978064Sume#endif
470118968Sume
471118968Sume		makeentry(entbuf, sizeof(entbuf), i, "rtplen");
472118968Sume		/* XXX: 256 is a magic number for compatibility check. */
473118968Sume		MAYHAVE(val, entbuf, 256);
474118968Sume		if (val == 256) {
475118968Sume			makeentry(oentbuf, sizeof(oentbuf), i, "rtrplen");
476118968Sume			MAYHAVE(val, oentbuf, 256);
477118968Sume			if (val != 256) {
478118968Sume				fprintf(stderr, "%s was obsoleted.  Use %s.\n",
479118968Sume					oentbuf, entbuf);
480118968Sume			} else
481118968Sume				val = 64;
482118968Sume		}
483118968Sume		if (val < 0 || val > 128) {
484118968Sume			syslog(LOG_ERR, "<%s> prefixlen (%ld) for %s on %s "
485118968Sume			       "out of range",
486118968Sume			       __func__, val, addr, intface);
487118968Sume			exit(1);
488118968Sume		}
489118968Sume		rti->prefixlen = (int)val;
490118968Sume
491118968Sume		makeentry(entbuf, sizeof(entbuf), i, "rtflags");
492118968Sume		if ((flagstr = (char *)agetstr(entbuf, &bp))) {
493118968Sume			val = 0;
494118968Sume			if (strchr(flagstr, 'h'))
495118968Sume				val |= ND_RA_FLAG_RTPREF_HIGH;
496118968Sume			if (strchr(flagstr, 'l')) {
497118968Sume				if ((val & ND_RA_FLAG_RTPREF_HIGH)) {
498118968Sume					syslog(LOG_ERR,
499118968Sume					    "<%s> the \'h\' and \'l\' route"
500118968Sume					    " preferences are exclusive",
501118968Sume					    __func__);
502118968Sume					exit(1);
503118968Sume				}
504118968Sume				val |= ND_RA_FLAG_RTPREF_LOW;
505118968Sume			}
506118968Sume		} else
507118968Sume			MAYHAVE(val, entbuf, 256); /* XXX */
508118968Sume		if (val == 256) {
509118968Sume			makeentry(oentbuf, sizeof(oentbuf), i, "rtrflags");
510118968Sume			MAYHAVE(val, oentbuf, 256);
511118968Sume			if (val != 256) {
512118968Sume				fprintf(stderr, "%s was obsoleted.  Use %s.\n",
513118968Sume					oentbuf, entbuf);
514118968Sume			} else
515118968Sume				val = 0;
516118968Sume		}
517118968Sume		rti->rtpref = val & ND_RA_FLAG_RTPREF_MASK;
518118968Sume		if (rti->rtpref == ND_RA_FLAG_RTPREF_RSV) {
519118968Sume			syslog(LOG_ERR, "<%s> invalid route preference (%02x) "
520118968Sume			       "for %s/%d on %s",
521118968Sume			       __func__, rti->rtpref, addr,
522118968Sume			       rti->prefixlen, intface);
523118968Sume			exit(1);
524118968Sume		}
525118968Sume
526118968Sume		/*
527118968Sume		 * Since the spec does not a default value, we should make
528118968Sume		 * this entry mandatory.  However, FreeBSD 4.4 has shipped
529118968Sume		 * with this field being optional, we use the router lifetime
530118968Sume		 * as an ad-hoc default value with a warning message.
531118968Sume		 */
532118968Sume		makeentry(entbuf, sizeof(entbuf), i, "rtltime");
533118968Sume		MAYHAVE(val64, entbuf, -1);
534118968Sume		if (val64 == -1) {
535118968Sume			makeentry(oentbuf, sizeof(oentbuf), i, "rtrltime");
536118968Sume			MAYHAVE(val64, oentbuf, -1);
537118968Sume			if (val64 != -1) {
538118968Sume				fprintf(stderr, "%s was obsoleted.  Use %s.\n",
539118968Sume					oentbuf, entbuf);
540118968Sume			} else {
541118968Sume				fprintf(stderr, "%s should be specified "
542118968Sume					"for interface %s.\n",
543118968Sume					entbuf, intface);
544118968Sume				val64 = tmp->lifetime;
545118968Sume			}
546118968Sume		}
547118968Sume		if (val64 < 0 || val64 > 0xffffffff) {
548118968Sume			syslog(LOG_ERR, "<%s> route lifetime (%lld) for "
549118968Sume			    "%s/%d on %s out of range", __func__,
550118968Sume			    (long long)val64, addr, rti->prefixlen, intface);
551118968Sume			exit(1);
552118968Sume		}
553118968Sume		rti->ltime = (u_int32_t)val64;
55478064Sume	}
555118968Sume#endif
55678064Sume
55755505Sshin	/* okey */
55855505Sshin	tmp->next = ralist;
55955505Sshin	ralist = tmp;
56055505Sshin
56155505Sshin	/* construct the sending packet */
56255505Sshin	make_packet(tmp);
56355505Sshin
56455505Sshin	/* set timer */
56555505Sshin	tmp->timer = rtadvd_add_timer(ra_timeout, ra_timer_update,
56655505Sshin				      tmp, tmp);
56755505Sshin	ra_timer_update((void *)tmp, &tmp->timer->tm);
56855505Sshin	rtadvd_set_timer(&tmp->timer->tm, tmp->timer);
56955505Sshin}
57055505Sshin
571118968Sumevoid
57255505Sshinget_prefix(struct rainfo *rai)
57355505Sshin{
57478064Sume	struct ifaddrs *ifap, *ifa;
57578064Sume	struct prefix *pp;
57678064Sume	struct in6_addr *a;
57778064Sume	u_char *p, *ep, *m, *lim;
57855505Sshin	u_char ntopbuf[INET6_ADDRSTRLEN];
57955505Sshin
58078064Sume	if (getifaddrs(&ifap) < 0) {
58155505Sshin		syslog(LOG_ERR,
58278064Sume		       "<%s> can't get interface addresses",
583118660Sume		       __func__);
58455505Sshin		exit(1);
58555505Sshin	}
586118664Sume
58778064Sume	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
58897719Sume		int plen;
58997719Sume
59078064Sume		if (strcmp(ifa->ifa_name, rai->ifname) != 0)
59178064Sume			continue;
59278064Sume		if (ifa->ifa_addr->sa_family != AF_INET6)
59378064Sume			continue;
59478064Sume		a = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
59578064Sume		if (IN6_IS_ADDR_LINKLOCAL(a))
59678064Sume			continue;
59797719Sume		/* get prefix length */
59897719Sume		m = (u_char *)&((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr;
59997719Sume		lim = (u_char *)(ifa->ifa_netmask) + ifa->ifa_netmask->sa_len;
60097719Sume		plen = prefixlen(m, lim);
601118968Sume		if (plen <= 0 || plen > 128) {
60297719Sume			syslog(LOG_ERR, "<%s> failed to get prefixlen "
60397719Sume			       "or prefix is invalid",
604118660Sume			       __func__);
60597719Sume			exit(1);
60697719Sume		}
607118968Sume		if (plen == 128)	/* XXX */
608118968Sume			continue;
60997719Sume		if (find_prefix(rai, a, plen)) {
61097719Sume			/* ignore a duplicated prefix. */
61197719Sume			continue;
61297719Sume		}
61397719Sume
61455505Sshin		/* allocate memory to store prefix info. */
61555505Sshin		if ((pp = malloc(sizeof(*pp))) == NULL) {
61655505Sshin			syslog(LOG_ERR,
61755505Sshin			       "<%s> can't get allocate buffer for prefix",
618118660Sume			       __func__);
61955505Sshin			exit(1);
62055505Sshin		}
62155505Sshin		memset(pp, 0, sizeof(*pp));
62255505Sshin
62378064Sume		/* set prefix, sweep bits outside of prefixlen */
62497719Sume		pp->prefixlen = plen;
62578064Sume		memcpy(&pp->prefix, a, sizeof(*a));
62678064Sume		p = (u_char *)&pp->prefix;
62778064Sume		ep = (u_char *)(&pp->prefix + 1);
62878064Sume		while (m < lim)
62978064Sume			*p++ &= *m++;
63078064Sume		while (p < ep)
63178064Sume			*p++ = 0x00;
63278064Sume	        if (!inet_ntop(AF_INET6, &pp->prefix, ntopbuf,
63378064Sume	            sizeof(ntopbuf))) {
634118660Sume			syslog(LOG_ERR, "<%s> inet_ntop failed", __func__);
63578064Sume			exit(1);
63678064Sume		}
63755505Sshin		syslog(LOG_DEBUG,
63855505Sshin		       "<%s> add %s/%d to prefix list on %s",
639118660Sume		       __func__, ntopbuf, pp->prefixlen, rai->ifname);
64055505Sshin
64155505Sshin		/* set other fields with protocol defaults */
64255505Sshin		pp->validlifetime = DEF_ADVVALIDLIFETIME;
64355505Sshin		pp->preflifetime = DEF_ADVPREFERREDLIFETIME;
64455505Sshin		pp->onlinkflg = 1;
64555505Sshin		pp->autoconfflg = 1;
64662656Skris		pp->origin = PREFIX_FROM_KERNEL;
647118968Sume		pp->rainfo = rai;
64855505Sshin
64955505Sshin		/* link into chain */
65055505Sshin		insque(pp, &rai->prefix);
65155505Sshin
65255505Sshin		/* counter increment */
65355505Sshin		rai->pfxs++;
65455505Sshin	}
65555505Sshin
65678064Sume	freeifaddrs(ifap);
65755505Sshin}
65855505Sshin
65955505Sshinstatic void
660118968Sumemakeentry(buf, len, id, string)
66197709Sume	char *buf;
66297709Sume	size_t len;
66397709Sume	int id;
66497709Sume	char *string;
66555505Sshin{
66697709Sume
667118968Sume	if (id < 0)
668118968Sume		strlcpy(buf, string, len);
669118968Sume	else
670118968Sume		snprintf(buf, len, "%s%d", string, id);
67155505Sshin}
67255505Sshin
67355505Sshin/*
67455505Sshin * Add a prefix to the list of specified interface and reconstruct
67555505Sshin * the outgoing packet.
67655505Sshin * The prefix must not be in the list.
677118968Sume * XXX: other parameters of the prefix (e.g. lifetime) shoule be
67855505Sshin * able to be specified.
67955505Sshin */
68055505Sshinstatic void
68155505Sshinadd_prefix(struct rainfo *rai, struct in6_prefixreq *ipr)
68255505Sshin{
68355505Sshin	struct prefix *prefix;
68455505Sshin	u_char ntopbuf[INET6_ADDRSTRLEN];
68555505Sshin
68655505Sshin	if ((prefix = malloc(sizeof(*prefix))) == NULL) {
68755505Sshin		syslog(LOG_ERR, "<%s> memory allocation failed",
688118660Sume		       __func__);
68955505Sshin		return;		/* XXX: error or exit? */
69055505Sshin	}
69178064Sume	memset(prefix, 0, sizeof(*prefix));
69255505Sshin	prefix->prefix = ipr->ipr_prefix.sin6_addr;
69355505Sshin	prefix->prefixlen = ipr->ipr_plen;
69455505Sshin	prefix->validlifetime = ipr->ipr_vltime;
69555505Sshin	prefix->preflifetime = ipr->ipr_pltime;
69655505Sshin	prefix->onlinkflg = ipr->ipr_raf_onlink;
69755505Sshin	prefix->autoconfflg = ipr->ipr_raf_auto;
69862656Skris	prefix->origin = PREFIX_FROM_DYNAMIC;
69955505Sshin
70055505Sshin	insque(prefix, &rai->prefix);
70198172Sume	prefix->rainfo = rai;
70255505Sshin
70355505Sshin	syslog(LOG_DEBUG, "<%s> new prefix %s/%d was added on %s",
704118660Sume	       __func__, inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr,
70555505Sshin				       ntopbuf, INET6_ADDRSTRLEN),
70655505Sshin	       ipr->ipr_plen, rai->ifname);
70755505Sshin
70855505Sshin	/* free the previous packet */
70955505Sshin	free(rai->ra_data);
71078064Sume	rai->ra_data = NULL;
71155505Sshin
71255505Sshin	/* reconstruct the packet */
71355505Sshin	rai->pfxs++;
71455505Sshin	make_packet(rai);
71555505Sshin}
71655505Sshin
71755505Sshin/*
71855505Sshin * Delete a prefix to the list of specified interface and reconstruct
71955505Sshin * the outgoing packet.
72062656Skris * The prefix must be in the list.
72155505Sshin */
72255505Sshinvoid
72398172Sumedelete_prefix(struct prefix *prefix)
72455505Sshin{
72555505Sshin	u_char ntopbuf[INET6_ADDRSTRLEN];
72698172Sume	struct rainfo *rai = prefix->rainfo;
72755505Sshin
72855505Sshin	remque(prefix);
72955505Sshin	syslog(LOG_DEBUG, "<%s> prefix %s/%d was deleted on %s",
730118660Sume	       __func__, inet_ntop(AF_INET6, &prefix->prefix,
73155505Sshin				       ntopbuf, INET6_ADDRSTRLEN),
73255505Sshin	       prefix->prefixlen, rai->ifname);
73398172Sume	if (prefix->timer)
73498172Sume		rtadvd_remove_timer(&prefix->timer);
73555505Sshin	free(prefix);
73655505Sshin	rai->pfxs--;
73755505Sshin}
73855505Sshin
73998172Sumevoid
74098172Sumeinvalidate_prefix(struct prefix *prefix)
74198172Sume{
74298172Sume	u_char ntopbuf[INET6_ADDRSTRLEN];
74398172Sume	struct timeval timo;
74498172Sume	struct rainfo *rai = prefix->rainfo;
74598172Sume
74698172Sume	if (prefix->timer) {	/* sanity check */
74798172Sume		syslog(LOG_ERR,
74898172Sume		    "<%s> assumption failure: timer already exists",
749118660Sume		    __func__);
75098172Sume		exit(1);
75198172Sume	}
75298172Sume
75398172Sume	syslog(LOG_DEBUG, "<%s> prefix %s/%d was invalidated on %s, "
754118660Sume	    "will expire in %ld seconds", __func__,
75598172Sume	    inet_ntop(AF_INET6, &prefix->prefix, ntopbuf, INET6_ADDRSTRLEN),
75698172Sume	    prefix->prefixlen, rai->ifname, (long)prefix_timo);
75798172Sume
75898172Sume	/* set the expiration timer */
75998172Sume	prefix->timer = rtadvd_add_timer(prefix_timeout, NULL, prefix, NULL);
76098172Sume	if (prefix->timer == NULL) {
76198172Sume		syslog(LOG_ERR, "<%s> failed to add a timer for a prefix. "
762118660Sume		    "remove the prefix", __func__);
76398172Sume		delete_prefix(prefix);
76498172Sume	}
76598172Sume	timo.tv_sec = prefix_timo;
76698172Sume	timo.tv_usec = 0;
76798172Sume	rtadvd_set_timer(&timo, prefix->timer);
76898172Sume}
76998172Sume
77098172Sumestatic struct rtadvd_timer *
77198172Sumeprefix_timeout(void *arg)
77298172Sume{
77398172Sume	struct prefix *prefix = (struct prefix *)arg;
77498172Sume
77598172Sume	delete_prefix(prefix);
77698172Sume
77798172Sume	return(NULL);
77898172Sume}
77998172Sume
78098172Sumevoid
78198172Sumeupdate_prefix(struct prefix * prefix)
78298172Sume{
78398172Sume	u_char ntopbuf[INET6_ADDRSTRLEN];
78498172Sume	struct rainfo *rai = prefix->rainfo;
78598172Sume
78698172Sume	if (prefix->timer == NULL) { /* sanity check */
78798172Sume		syslog(LOG_ERR,
78898172Sume		    "<%s> assumption failure: timer does not exist",
789118660Sume		    __func__);
79098172Sume		exit(1);
79198172Sume	}
79298172Sume
79398172Sume	syslog(LOG_DEBUG, "<%s> prefix %s/%d was re-enabled on %s",
794118660Sume	    __func__, inet_ntop(AF_INET6, &prefix->prefix, ntopbuf,
79598172Sume	    INET6_ADDRSTRLEN), prefix->prefixlen, rai->ifname);
79698172Sume
79798172Sume	/* stop the expiration timer */
79898172Sume	rtadvd_remove_timer(&prefix->timer);
79998172Sume}
80098172Sume
80155505Sshin/*
80255505Sshin * Try to get an in6_prefixreq contents for a prefix which matches
80355505Sshin * ipr->ipr_prefix and ipr->ipr_plen and belongs to
80455505Sshin * the interface whose name is ipr->ipr_name[].
80555505Sshin */
80655505Sshinstatic int
80755505Sshininit_prefix(struct in6_prefixreq *ipr)
80855505Sshin{
80998262Sume#if 0
81055505Sshin	int s;
81155505Sshin
81255505Sshin	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
813118660Sume		syslog(LOG_ERR, "<%s> socket: %s", __func__,
81455505Sshin		       strerror(errno));
81555505Sshin		exit(1);
81655505Sshin	}
81755505Sshin
81855505Sshin	if (ioctl(s, SIOCGIFPREFIX_IN6, (caddr_t)ipr) < 0) {
819118660Sume		syslog(LOG_INFO, "<%s> ioctl:SIOCGIFPREFIX %s", __func__,
82055505Sshin		       strerror(errno));
82155505Sshin
82255505Sshin		ipr->ipr_vltime = DEF_ADVVALIDLIFETIME;
82355505Sshin		ipr->ipr_pltime = DEF_ADVPREFERREDLIFETIME;
82455505Sshin		ipr->ipr_raf_onlink = 1;
82555505Sshin		ipr->ipr_raf_auto = 1;
82655505Sshin		/* omit other field initialization */
82755505Sshin	}
82855505Sshin	else if (ipr->ipr_origin < PR_ORIG_RR) {
82955505Sshin		u_char ntopbuf[INET6_ADDRSTRLEN];
83055505Sshin
83155505Sshin		syslog(LOG_WARNING, "<%s> Added prefix(%s)'s origin %d is"
83255505Sshin		       "lower than PR_ORIG_RR(router renumbering)."
833118660Sume		       "This should not happen if I am router", __func__,
83455505Sshin		       inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr, ntopbuf,
83555505Sshin				 sizeof(ntopbuf)), ipr->ipr_origin);
83657852Sshin		close(s);
83755505Sshin		return 1;
83855505Sshin	}
83955505Sshin
84055505Sshin	close(s);
84155505Sshin	return 0;
84298262Sume#else
84398262Sume	ipr->ipr_vltime = DEF_ADVVALIDLIFETIME;
84498262Sume	ipr->ipr_pltime = DEF_ADVPREFERREDLIFETIME;
84598262Sume	ipr->ipr_raf_onlink = 1;
84698262Sume	ipr->ipr_raf_auto = 1;
847118664Sume	return 0;
84898262Sume#endif
84955505Sshin}
85055505Sshin
85155505Sshinvoid
85255505Sshinmake_prefix(struct rainfo *rai, int ifindex, struct in6_addr *addr, int plen)
85355505Sshin{
85455505Sshin	struct in6_prefixreq ipr;
85555505Sshin
85655505Sshin	memset(&ipr, 0, sizeof(ipr));
85755505Sshin	if (if_indextoname(ifindex, ipr.ipr_name) == NULL) {
85855505Sshin		syslog(LOG_ERR, "<%s> Prefix added interface No.%d doesn't"
859118660Sume		       "exist. This should not happen! %s", __func__,
86055505Sshin		       ifindex, strerror(errno));
86155505Sshin		exit(1);
86255505Sshin	}
86355505Sshin	ipr.ipr_prefix.sin6_len = sizeof(ipr.ipr_prefix);
86455505Sshin	ipr.ipr_prefix.sin6_family = AF_INET6;
86555505Sshin	ipr.ipr_prefix.sin6_addr = *addr;
86655505Sshin	ipr.ipr_plen = plen;
86755505Sshin
86855505Sshin	if (init_prefix(&ipr))
86955505Sshin		return; /* init failed by some error */
87055505Sshin	add_prefix(rai, &ipr);
87155505Sshin}
87255505Sshin
87362656Skrisvoid
87455505Sshinmake_packet(struct rainfo *rainfo)
87555505Sshin{
87655505Sshin	size_t packlen, lladdroptlen = 0;
87755505Sshin	char *buf;
87855505Sshin	struct nd_router_advert *ra;
87955505Sshin	struct nd_opt_prefix_info *ndopt_pi;
88055505Sshin	struct nd_opt_mtu *ndopt_mtu;
881118968Sume#ifdef ROUTEINFO
88278064Sume	struct nd_opt_route_info *ndopt_rti;
883118968Sume	struct rtinfo *rti;
884118968Sume#endif
88555505Sshin	struct prefix *pfx;
88655505Sshin
88755505Sshin	/* calculate total length */
88855505Sshin	packlen = sizeof(struct nd_router_advert);
88955505Sshin	if (rainfo->advlinkopt) {
89055505Sshin		if ((lladdroptlen = lladdropt_length(rainfo->sdl)) == 0) {
89155505Sshin			syslog(LOG_INFO,
89255505Sshin			       "<%s> link-layer address option has"
893118664Sume			       " null length on %s.  Treat as not included.",
894118660Sume			       __func__, rainfo->ifname);
89555505Sshin			rainfo->advlinkopt = 0;
89655505Sshin		}
89755505Sshin		packlen += lladdroptlen;
89855505Sshin	}
89955505Sshin	if (rainfo->pfxs)
90055505Sshin		packlen += sizeof(struct nd_opt_prefix_info) * rainfo->pfxs;
90155505Sshin	if (rainfo->linkmtu)
90255505Sshin		packlen += sizeof(struct nd_opt_mtu);
903118968Sume#ifdef ROUTEINFO
90478064Sume	for (rti = rainfo->route.next; rti != &rainfo->route; rti = rti->next)
90578064Sume		packlen += sizeof(struct nd_opt_route_info) +
90678064Sume			   ((rti->prefixlen + 0x3f) >> 6) * 8;
90778064Sume#endif
90855505Sshin
90955505Sshin	/* allocate memory for the packet */
91055505Sshin	if ((buf = malloc(packlen)) == NULL) {
91155505Sshin		syslog(LOG_ERR,
91255505Sshin		       "<%s> can't get enough memory for an RA packet",
913118660Sume		       __func__);
91455505Sshin		exit(1);
91555505Sshin	}
91678064Sume	if (rainfo->ra_data) {
91778064Sume		/* free the previous packet */
91878064Sume		free(rainfo->ra_data);
91978064Sume		rainfo->ra_data = NULL;
92078064Sume	}
92155505Sshin	rainfo->ra_data = buf;
92255505Sshin	/* XXX: what if packlen > 576? */
92355505Sshin	rainfo->ra_datalen = packlen;
92455505Sshin
92555505Sshin	/*
92655505Sshin	 * construct the packet
92755505Sshin	 */
92855505Sshin	ra = (struct nd_router_advert *)buf;
92955505Sshin	ra->nd_ra_type = ND_ROUTER_ADVERT;
93055505Sshin	ra->nd_ra_code = 0;
93155505Sshin	ra->nd_ra_cksum = 0;
93255505Sshin	ra->nd_ra_curhoplimit = (u_int8_t)(0xff & rainfo->hoplimit);
93378064Sume	ra->nd_ra_flags_reserved = 0; /* just in case */
93478064Sume	/*
93578064Sume	 * XXX: the router preference field, which is a 2-bit field, should be
93678064Sume	 * initialized before other fields.
93778064Sume	 */
93878064Sume	ra->nd_ra_flags_reserved = 0xff & rainfo->rtpref;
93955505Sshin	ra->nd_ra_flags_reserved |=
94055505Sshin		rainfo->managedflg ? ND_RA_FLAG_MANAGED : 0;
94155505Sshin	ra->nd_ra_flags_reserved |=
94255505Sshin		rainfo->otherflg ? ND_RA_FLAG_OTHER : 0;
94355505Sshin	ra->nd_ra_router_lifetime = htons(rainfo->lifetime);
94455505Sshin	ra->nd_ra_reachable = htonl(rainfo->reachabletime);
94555505Sshin	ra->nd_ra_retransmit = htonl(rainfo->retranstimer);
94655505Sshin	buf += sizeof(*ra);
94755505Sshin
94855505Sshin	if (rainfo->advlinkopt) {
94955505Sshin		lladdropt_fill(rainfo->sdl, (struct nd_opt_hdr *)buf);
95055505Sshin		buf += lladdroptlen;
95155505Sshin	}
95255505Sshin
95355505Sshin	if (rainfo->linkmtu) {
95455505Sshin		ndopt_mtu = (struct nd_opt_mtu *)buf;
95555505Sshin		ndopt_mtu->nd_opt_mtu_type = ND_OPT_MTU;
95655505Sshin		ndopt_mtu->nd_opt_mtu_len = 1;
95755505Sshin		ndopt_mtu->nd_opt_mtu_reserved = 0;
95878064Sume		ndopt_mtu->nd_opt_mtu_mtu = htonl(rainfo->linkmtu);
95955505Sshin		buf += sizeof(struct nd_opt_mtu);
96055505Sshin	}
96155505Sshin
96255505Sshin	for (pfx = rainfo->prefix.next;
96355505Sshin	     pfx != &rainfo->prefix; pfx = pfx->next) {
96478064Sume		u_int32_t vltime, pltime;
96578064Sume		struct timeval now;
96678064Sume
96755505Sshin		ndopt_pi = (struct nd_opt_prefix_info *)buf;
96855505Sshin		ndopt_pi->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION;
96955505Sshin		ndopt_pi->nd_opt_pi_len = 4;
97055505Sshin		ndopt_pi->nd_opt_pi_prefix_len = pfx->prefixlen;
97155505Sshin		ndopt_pi->nd_opt_pi_flags_reserved = 0;
97255505Sshin		if (pfx->onlinkflg)
97355505Sshin			ndopt_pi->nd_opt_pi_flags_reserved |=
97455505Sshin				ND_OPT_PI_FLAG_ONLINK;
97555505Sshin		if (pfx->autoconfflg)
97655505Sshin			ndopt_pi->nd_opt_pi_flags_reserved |=
97755505Sshin				ND_OPT_PI_FLAG_AUTO;
97898172Sume		if (pfx->timer)
97998172Sume			vltime = 0;
98098172Sume		else {
98198172Sume			if (pfx->vltimeexpire || pfx->pltimeexpire)
98298172Sume				gettimeofday(&now, NULL);
98398172Sume			if (pfx->vltimeexpire == 0)
98498172Sume				vltime = pfx->validlifetime;
98598172Sume			else
98698172Sume				vltime = (pfx->vltimeexpire > now.tv_sec) ?
98798172Sume				    pfx->vltimeexpire - now.tv_sec : 0;
98898172Sume		}
98998172Sume		if (pfx->timer)
99098172Sume			pltime = 0;
99198172Sume		else {
99298172Sume			if (pfx->pltimeexpire == 0)
99398172Sume				pltime = pfx->preflifetime;
99498172Sume			else
99598172Sume				pltime = (pfx->pltimeexpire > now.tv_sec) ?
99698172Sume				    pfx->pltimeexpire - now.tv_sec : 0;
99798172Sume		}
99878064Sume		if (vltime < pltime) {
99978064Sume			/*
100078064Sume			 * this can happen if vltime is decrement but pltime
100178064Sume			 * is not.
100278064Sume			 */
100378064Sume			pltime = vltime;
100478064Sume		}
100578064Sume		ndopt_pi->nd_opt_pi_valid_time = htonl(vltime);
100678064Sume		ndopt_pi->nd_opt_pi_preferred_time = htonl(pltime);
100755505Sshin		ndopt_pi->nd_opt_pi_reserved2 = 0;
100855505Sshin		ndopt_pi->nd_opt_pi_prefix = pfx->prefix;
100955505Sshin
101055505Sshin		buf += sizeof(struct nd_opt_prefix_info);
101155505Sshin	}
101255505Sshin
1013118968Sume#ifdef ROUTEINFO
101478064Sume	for (rti = rainfo->route.next; rti != &rainfo->route; rti = rti->next) {
101578064Sume		u_int8_t psize = (rti->prefixlen + 0x3f) >> 6;
101678064Sume
101778064Sume		ndopt_rti = (struct nd_opt_route_info *)buf;
101878064Sume		ndopt_rti->nd_opt_rti_type = ND_OPT_ROUTE_INFO;
101978064Sume		ndopt_rti->nd_opt_rti_len = 1 + psize;
102078064Sume		ndopt_rti->nd_opt_rti_prefixlen = rti->prefixlen;
102178064Sume		ndopt_rti->nd_opt_rti_flags = 0xff & rti->rtpref;
1022113325Ssuz		ndopt_rti->nd_opt_rti_lifetime = htonl(rti->ltime);
102378064Sume		memcpy(ndopt_rti + 1, &rti->prefix, psize * 8);
102478064Sume		buf += sizeof(struct nd_opt_route_info) + psize * 8;
102578064Sume	}
102678064Sume#endif
102778064Sume
102855505Sshin	return;
102955505Sshin}
103078064Sume
103178064Sumestatic int
103278064Sumegetinet6sysctl(int code)
103378064Sume{
103478064Sume	int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 };
103578064Sume	int value;
103678064Sume	size_t size;
103778064Sume
103878064Sume	mib[3] = code;
103978064Sume	size = sizeof(value);
104078064Sume	if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), &value, &size, NULL, 0)
104178064Sume	    < 0) {
104278064Sume		syslog(LOG_ERR, "<%s>: failed to get ip6 sysctl(%d): %s",
1043118660Sume		       __func__, code,
104478064Sume		       strerror(errno));
104578064Sume		return(-1);
104678064Sume	}
104778064Sume	else
104878064Sume		return(value);
104978064Sume}
1050