config.c revision 97709
14Srgrimes/*	$FreeBSD: head/usr.sbin/rtadvd/config.c 97709 2002-06-01 16:50:21Z ume $	*/
24Srgrimes/*	$KAME: config.c,v 1.37 2001/05/25 07:34:00 itojun Exp $	*/
34Srgrimes
44Srgrimes/*
54Srgrimes * Copyright (C) 1998 WIDE Project.
64Srgrimes * All rights reserved.
74Srgrimes *
84Srgrimes * Redistribution and use in source and binary forms, with or without
94Srgrimes * modification, are permitted provided that the following conditions
104Srgrimes * are met:
114Srgrimes * 1. Redistributions of source code must retain the above copyright
124Srgrimes *    notice, this list of conditions and the following disclaimer.
134Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
144Srgrimes *    notice, this list of conditions and the following disclaimer in the
154Srgrimes *    documentation and/or other materials provided with the distribution.
164Srgrimes * 3. Neither the name of the project nor the names of its contributors
174Srgrimes *    may be used to endorse or promote products derived from this software
184Srgrimes *    without specific prior written permission.
194Srgrimes *
204Srgrimes * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
214Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
224Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
234Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
244Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
254Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
264Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
274Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
284Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
294Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
304Srgrimes * SUCH DAMAGE.
314Srgrimes */
324Srgrimes
334Srgrimes#include <sys/param.h>
344Srgrimes#include <sys/ioctl.h>
354Srgrimes#include <sys/socket.h>
36619Srgrimes#include <sys/time.h>
3750477Speter#include <sys/sysctl.h>
384Srgrimes
394Srgrimes#include <net/if.h>
403185Ssos#if defined(__FreeBSD__) && __FreeBSD__ >= 3
4119173Sbde#include <net/if_var.h>
4219173Sbde#endif /* __FreeBSD__ >= 3 */
4319173Sbde#include <net/route.h>
4419173Sbde#include <net/if_dl.h>
453185Ssos
463185Ssos#include <netinet/in.h>
473185Ssos#include <netinet/in_var.h>
483185Ssos#include <netinet/ip6.h>
492913Sache#include <netinet6/ip6_var.h>
502913Sache#include <netinet/icmp6.h>
5116299Spst#ifdef MIP6
5233929Sphk#include <netinet6/mip6.h>
5313228Swollman#endif
542056Swollman
552056Swollman#include <arpa/inet.h>
5661994Smsmith
5767356Sjhb#include <stdio.h>
5867356Sjhb#include <syslog.h>
5965557Sjasone#include <errno.h>
602056Swollman#include <string.h>
6158377Sphk#include <stdlib.h>
622056Swollman#if defined(__NetBSD__) || defined(__OpenBSD__)
6331253Sbde#include <search.h>
6431253Sbde#endif
6531253Sbde#include <unistd.h>
6615508Sbde#include <ifaddrs.h>
6749558Sphk
6815508Sbde#include "rtadvd.h"
694180Sbde#include "advcap.h"
7015508Sbde#include "timer.h"
7115508Sbde#include "if.h"
7230805Sbde#include "config.h"
732056Swollman
7428551Sbdestatic void makeentry __P((char *, size_t, int, char *, int));
7532054Sphkstatic void get_prefix __P((struct rainfo *));
7647588Sbdestatic int getinet6sysctl __P((int));
7730805Sbde
7830805Sbdeextern struct rainfo *ralist;
7930805Sbde
8028921Sfsmpvoid
8126949Sfsmpgetconfig(intface)
8228921Sfsmp	char *intface;
8332054Sphk{
8415508Sbde	int stat, pfxs, i;
852056Swollman	char tbuf[BUFSIZ];
862056Swollman	struct rainfo *tmp;
8747642Sdfr	long val;
8861994Smsmith	long long val64;
892056Swollman	char buf[BUFSIZ];
904Srgrimes	char *bp = buf;
9145897Speter	char *addr;
9228487Sfsmp	static int forwarding = -1;
9350823Smdodd
9450823Smdodd#define MUSTHAVE(var, cap)	\
9550823Smdodd    do {								\
9650823Smdodd	int t;								\
9750823Smdodd	if ((t = agetnum(cap)) < 0) {					\
9834571Stegge		fprintf(stderr, "rtadvd: need %s for interface %s\n",	\
9934571Stegge			cap, intface);					\
10034058Stegge		exit(1);						\
10134058Stegge	}								\
10234571Stegge	var = t;							\
10334571Stegge     } while (0)
10434571Stegge#define MAYHAVE(var, cap, def)	\
10528921Sfsmp     do {								\
1062873Sbde	if ((var = agetnum(cap)) < 0)					\
1072873Sbde		var = def;						\
1082873Sbde     } while (0)
1092873Sbde
1102873Sbde	if ((stat = agetent(tbuf, intface)) <= 0) {
1112913Sache		memset(tbuf, 0, sizeof(tbuf));
1122873Sbde		syslog(LOG_INFO,
11315508Sbde		       "<%s> %s isn't defined in the configuration file"
1144Srgrimes		       " or the configuration file doesn't exist."
1154180Sbde		       " Treat it as default",
1164180Sbde		        __FUNCTION__, intface);
1174180Sbde	}
1184180Sbde
1194180Sbde	tmp = (struct rainfo *)malloc(sizeof(*ralist));
1204180Sbde	memset(tmp, 0, sizeof(*tmp));
1214180Sbde	tmp->prefix.next = tmp->prefix.prev = &tmp->prefix;
1224180Sbde	tmp->route.next = tmp->route.prev = &tmp->route;
1234180Sbde
1244180Sbde	/* check if we are allowed to forward packets (if not determined) */
12517236Sjoerg	if (forwarding < 0) {
12617231Sjoerg		if ((forwarding = getinet6sysctl(IPV6CTL_FORWARDING)) < 0)
12733690Sphk			exit(1);
1284180Sbde	}
12917231Sjoerg
1304180Sbde	/* get interface information */
13141787Smckay	if (agetflag("nolladdr"))
13247588Sbde		tmp->advlinkopt = 0;
13315045Sache	else
13432052Sphk		tmp->advlinkopt = 1;
13533690Sphk	if (tmp->advlinkopt) {
13633690Sphk		if ((tmp->sdl = if_nametosdl(intface)) == NULL) {
13733690Sphk			syslog(LOG_ERR,
13832052Sphk			       "<%s> can't get information of %s",
13932052Sphk			       __FUNCTION__, intface);
14032005Sphk			exit(1);
14147592Sphk		}
14241787Smckay		tmp->ifindex = tmp->sdl->sdl_index;
14367356Sjhb	} else
1441390Ssos		tmp->ifindex = if_nametoindex(intface);
1454180Sbde	strncpy(tmp->ifname, intface, sizeof(tmp->ifname));
1464180Sbde	if ((tmp->phymtu = if_getmtu(intface)) == 0) {
14719173Sbde		tmp->phymtu = IPV6_MMTU;
14833690Sphk		syslog(LOG_WARNING,
14933690Sphk		       "<%s> can't get interface mtu of %s. Treat as %d",
15033690Sphk		       __FUNCTION__, intface, IPV6_MMTU);
1514180Sbde	}
1524180Sbde
1534180Sbde	/*
15454890Speter	 * set router configuration variables.
1554180Sbde	 */
1564180Sbde	MAYHAVE(val, "maxinterval", DEF_MAXRTRADVINTERVAL);
15719173Sbde	if (val < MIN_MAXINTERVAL || val > MAX_MAXINTERVAL) {
15819173Sbde		syslog(LOG_ERR,
1594180Sbde		       "<%s> maxinterval must be between %e and %u",
16015345Snate		       __FUNCTION__, MIN_MAXINTERVAL, MAX_MAXINTERVAL);
16133690Sphk		exit(1);
16217231Sjoerg	}
16317231Sjoerg	tmp->maxinterval = (u_int)val;
16417236Sjoerg	MAYHAVE(val, "mininterval", tmp->maxinterval/3);
16517236Sjoerg	if (val < MIN_MININTERVAL || val > (tmp->maxinterval * 3) / 4) {
16617236Sjoerg		syslog(LOG_ERR,
16717236Sjoerg		       "<%s> mininterval must be between %e and %d",
16817231Sjoerg		       __FUNCTION__,
16917231Sjoerg		       MIN_MININTERVAL,
17017231Sjoerg		       (tmp->maxinterval * 3) / 4);
17119173Sbde		exit(1);
17233690Sphk	}
1734180Sbde	tmp->mininterval = (u_int)val;
17436719Sphk
17536719Sphk	MAYHAVE(val, "chlim", DEF_ADVCURHOPLIMIT);
17621783Sbde	tmp->hoplimit = val & 0xff;
17717353Sbde
17840610Sphk	MAYHAVE(val, "raflags", 0);
17933690Sphk	tmp->managedflg = val & ND_RA_FLAG_MANAGED;
18036741Sphk	tmp->otherflg = val & ND_RA_FLAG_OTHER;
18136198Sphk#ifdef MIP6
18233690Sphk	if (mobileip6)
18333690Sphk		tmp->haflg = val & ND_RA_FLAG_HA;
18433690Sphk#endif
18533690Sphk#ifndef ND_RA_FLAG_RTPREF_MASK
18633690Sphk#define ND_RA_FLAG_RTPREF_MASK	0x18 /* 00011000 */
18740610Sphk#define ND_RA_FLAG_RTPREF_RSV	0x10 /* 00010000 */
18833690Sphk#endif
18940610Sphk	tmp->rtpref = val & ND_RA_FLAG_RTPREF_MASK;
19033690Sphk	if (tmp->rtpref == ND_RA_FLAG_RTPREF_RSV) {
19136741Sphk		syslog(LOG_ERR, "<%s> invalid router preference on %s",
19236198Sphk		       __FUNCTION__, intface);
19333690Sphk		exit(1);
19433690Sphk	}
19533690Sphk
19633690Sphk	MAYHAVE(val, "rltime", tmp->maxinterval * 3);
19733690Sphk	if (val && (val < tmp->maxinterval || val > MAXROUTERLIFETIME)) {
19840610Sphk		syslog(LOG_ERR,
19933690Sphk		       "<%s> router lifetime on %s must be 0 or"
20012724Sphk		       " between %d and %d",
2013185Ssos		       __FUNCTION__, intface,
2022074Swollman		       tmp->maxinterval, MAXROUTERLIFETIME);
20365557Sjasone		exit(1);
20439503Sbde	}
20566716Sjhb	/*
20647588Sbde	 * Basically, hosts MUST NOT send Router Advertisement messages at any
20739503Sbde	 * time (RFC 2461, Section 6.2.3). However, it would sometimes be
20847588Sbde	 * useful to allow hosts to advertise some parameters such as prefix
20939503Sbde	 * information and link MTU. Thus, we allow hosts to invoke rtadvd
21039503Sbde	 * only when router lifetime (on every advertising interface) is
21139503Sbde	 * explicitly set zero. (see also the above section)
21247588Sbde	 */
21366716Sjhb	if (val && forwarding == 0) {
21439503Sbde		syslog(LOG_WARNING,
2151549Srgrimes		       "<%s> non zero router lifetime is specified for %s, "
2161442Ssos		       "which must not be allowed for hosts.",
21717236Sjoerg		       __FUNCTION__, intface);
21817231Sjoerg		exit(1);
2198448Sbde	}
2201442Ssos	tmp->lifetime = val & 0xffff;
22117236Sjoerg
22217231Sjoerg	MAYHAVE(val, "rtime", DEF_ADVREACHABLETIME);
2234180Sbde	if (val > MAXREACHABLETIME) {
2244180Sbde		syslog(LOG_ERR,
22533309Sbde		       "<%s> reachable time must be no greater than %d",
2261549Srgrimes		       __FUNCTION__, MAXREACHABLETIME);
2278448Sbde		exit(1);
2281390Ssos	}
2291442Ssos	tmp->reachabletime = (u_int32_t)val;
23017236Sjoerg
23117231Sjoerg	MAYHAVE(val64, "retrans", DEF_ADVRETRANSTIMER);
23266716Sjhb	if (val64 < 0 || val64 > 0xffffffff) {
23348889Sbde		syslog(LOG_ERR,
23448889Sbde		       "<%s> retrans time out of range", __FUNCTION__);
2354180Sbde		exit(1);
2364180Sbde	}
2374180Sbde	tmp->retranstimer = (u_int32_t)val64;
2384180Sbde
23966716Sjhb#ifndef MIP6
2401442Ssos	if (agetstr("hapref", &bp) || agetstr("hatime", &bp)) {
24117231Sjoerg		syslog(LOG_ERR,
24248889Sbde		       "<%s> mobile-ip6 configuration not supported",
2431442Ssos		       __FUNCTION__);
24417236Sjoerg		exit(1);
24517231Sjoerg	}
2464180Sbde#else
2474180Sbde	if (!mobileip6) {
24866716Sjhb		if (agetstr("hapref", &bp) || agetstr("hatime", &bp)) {
24948889Sbde			syslog(LOG_ERR,
25048889Sbde			       "<%s> mobile-ip6 configuration without "
2515291Sbde			       "proper command line option",
2524180Sbde			       __FUNCTION__);
2534180Sbde			exit(1);
2544180Sbde		}
2554180Sbde	} else {
25666716Sjhb		tmp->hapref = 0;
2574180Sbde		if ((val = agetnum("hapref")) >= 0)
25817231Sjoerg			tmp->hapref = (int16_t)val;
25917231Sjoerg		if (tmp->hapref != 0) {
26048889Sbde			tmp->hatime = 0;
26148889Sbde			MUSTHAVE(val, "hatime");
2621442Ssos			tmp->hatime = (u_int16_t)val;
2631442Ssos			if (tmp->hatime <= 0) {
2641442Ssos				syslog(LOG_ERR,
26550823Smdodd				       "<%s> home agent lifetime must be greater than 0",
26650823Smdodd				       __FUNCTION__);
26750823Smdodd				exit(1);
26850823Smdodd			}
26950823Smdodd		}
2701390Ssos	}
2711390Ssos#endif
27217231Sjoerg
27317236Sjoerg	/* prefix information */
27417231Sjoerg
2751390Ssos	/*
2764180Sbde	 * This is an implementation specific parameter to consinder
2771390Ssos	 * link propagation delays and poorly synchronized clocks when
27817231Sjoerg	 * checking consistency of advertised lifetimes.
27917231Sjoerg	 */
28017231Sjoerg	MAYHAVE(val, "clockskew", 0);
28117231Sjoerg	tmp->clockskew = val;
28217231Sjoerg
28317231Sjoerg	if ((pfxs = agetnum("addrs")) < 0) {
28417236Sjoerg		/* auto configure prefix information */
28517236Sjoerg		if (agetstr("addr", &bp) || agetstr("addr1", &bp)) {
28617236Sjoerg			syslog(LOG_ERR,
28717231Sjoerg			       "<%s> conflicting prefix configuration for %s: "
28817236Sjoerg			       "automatic and manual config at the same time",
28917236Sjoerg			       __FUNCTION__, intface);
29017236Sjoerg			exit(1);
29117236Sjoerg		}
29217236Sjoerg		get_prefix(tmp);
29317236Sjoerg	}
29417236Sjoerg	else {
29517236Sjoerg		tmp->pfxs = pfxs;
29617236Sjoerg		for (i = 0; i < pfxs; i++) {
29717236Sjoerg			struct prefix *pfx;
29817236Sjoerg			char entbuf[256];
29917236Sjoerg			int added = (pfxs > 1) ? 1 : 0;
30017236Sjoerg
30117236Sjoerg			/* allocate memory to store prefix information */
30217231Sjoerg			if ((pfx = malloc(sizeof(struct prefix))) == NULL) {
3031442Ssos				syslog(LOG_ERR,
30417231Sjoerg				       "<%s> can't allocate enough memory",
30517231Sjoerg				       __FUNCTION__);
3061390Ssos				exit(1);
3071390Ssos			}
3081390Ssos			memset(pfx, 0, sizeof(*pfx));
3091390Ssos
3101390Ssos			/* link into chain */
31117231Sjoerg			insque(pfx, &tmp->prefix);
31217231Sjoerg
31317231Sjoerg			pfx->origin = PREFIX_FROM_CONFIG;
31417231Sjoerg
31517236Sjoerg			makeentry(entbuf, sizeof(entbuf), i, "prefixlen",
31617236Sjoerg			    added);
31717236Sjoerg			MAYHAVE(val, entbuf, 64);
31817236Sjoerg			if (val < 0 || val > 128) {
31917236Sjoerg				syslog(LOG_ERR,
32017236Sjoerg				       "<%s> prefixlen out of range",
32117236Sjoerg				       __FUNCTION__);
32217236Sjoerg				exit(1);
32317236Sjoerg			}
32417236Sjoerg			pfx->prefixlen = (int)val;
32517231Sjoerg
3261390Ssos			makeentry(entbuf, sizeof(entbuf), i, "pinfoflags",
3271390Ssos			    added);
3281390Ssos#ifdef MIP6
3291390Ssos			if (mobileip6)
3301390Ssos			{
33117231Sjoerg				MAYHAVE(val, entbuf,
33217231Sjoerg				    (ND_OPT_PI_FLAG_ONLINK|ND_OPT_PI_FLAG_AUTO|
33317236Sjoerg					 ND_OPT_PI_FLAG_ROUTER));
33417236Sjoerg			} else
33517236Sjoerg#endif
33617231Sjoerg			{
33717236Sjoerg				MAYHAVE(val, entbuf,
33817236Sjoerg				    (ND_OPT_PI_FLAG_ONLINK|ND_OPT_PI_FLAG_AUTO));
33917236Sjoerg			}
34017236Sjoerg			pfx->onlinkflg = val & ND_OPT_PI_FLAG_ONLINK;
34117236Sjoerg			pfx->autoconfflg = val & ND_OPT_PI_FLAG_AUTO;
34217236Sjoerg#ifdef MIP6
34317236Sjoerg			pfx->routeraddr = val & ND_OPT_PI_FLAG_ROUTER;
34417231Sjoerg#endif
34517231Sjoerg
3461390Ssos			makeentry(entbuf, sizeof(entbuf), i, "vltime", added);
3471390Ssos			MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME);
3481390Ssos			if (val64 < 0 || val64 > 0xffffffff) {
3491390Ssos				syslog(LOG_ERR,
3501390Ssos				       "<%s> vltime out of range",
35117231Sjoerg				       __FUNCTION__);
35217231Sjoerg				exit(1);
35317231Sjoerg			}
35417231Sjoerg			pfx->validlifetime = (u_int32_t)val64;
35517236Sjoerg
35617231Sjoerg			makeentry(entbuf, sizeof(entbuf), i, "vltimedecr",
3571390Ssos			    added);
3581390Ssos			if (agetflag(entbuf)) {
3593185Ssos				struct timeval now;
3603185Ssos				gettimeofday(&now, 0);
3613185Ssos				pfx->vltimeexpire =
3623185Ssos					now.tv_sec + pfx->validlifetime;
3633185Ssos			}
3643185Ssos
3653185Ssos			makeentry(entbuf, sizeof(entbuf), i, "pltime", added);
3663185Ssos			MAYHAVE(val64, entbuf, DEF_ADVPREFERREDLIFETIME);
3673185Ssos			if (val64 < 0 || val64 > 0xffffffff) {
3683185Ssos				syslog(LOG_ERR,
3693185Ssos				       "<%s> pltime out of range",
3703185Ssos				       __FUNCTION__);
37124676Smckay				exit(1);
37224676Smckay			}
37324676Smckay			pfx->preflifetime = (u_int32_t)val64;
37424676Smckay
37524676Smckay			makeentry(entbuf, sizeof(entbuf), i, "pltimedecr",
37624676Smckay			    added);
37724676Smckay			if (agetflag(entbuf)) {
37824676Smckay				struct timeval now;
3793185Ssos				gettimeofday(&now, 0);
38012724Sphk				pfx->pltimeexpire =
3813185Ssos					now.tv_sec + pfx->preflifetime;
3823185Ssos			}
38324676Smckay
3843185Ssos			makeentry(entbuf, sizeof(entbuf), i, "addr", added);
3853185Ssos			addr = (char *)agetstr(entbuf, &bp);
3861390Ssos			if (addr == NULL) {
38718297Sbde				syslog(LOG_ERR,
3885291Sbde				       "<%s> need %s as an prefix for "
38918297Sbde				       "interface %s",
39018297Sbde				       __FUNCTION__, entbuf, intface);
39118297Sbde				exit(1);
3923185Ssos			}
3935291Sbde			if (inet_pton(AF_INET6, addr,
3945291Sbde				      &pfx->prefix) != 1) {
3955291Sbde				syslog(LOG_ERR,
3965291Sbde				       "<%s> inet_pton failed for %s",
3973185Ssos				       __FUNCTION__, addr);
39818297Sbde				exit(1);
3993185Ssos			}
4001390Ssos			if (IN6_IS_ADDR_MULTICAST(&pfx->prefix)) {
40110268Sbde				syslog(LOG_ERR,
4021390Ssos				       "<%s> multicast prefix(%s) must "
40366716Sjhb				       "not be advertised (IF=%s)",
4041390Ssos				       __FUNCTION__, addr, intface);
40566716Sjhb				exit(1);
40616428Sbde			}
40716428Sbde			if (IN6_IS_ADDR_LINKLOCAL(&pfx->prefix))
40819173Sbde				syslog(LOG_NOTICE,
40916428Sbde				       "<%s> link-local prefix(%s) will be"
4101390Ssos				       " advertised on %s",
4111390Ssos				       __FUNCTION__, addr, intface);
41216428Sbde		}
41366716Sjhb	}
4141390Ssos
4151390Ssos	MAYHAVE(val, "mtu", 0);
4161390Ssos	if (val < 0 || val > 0xffffffff) {
4172017Swollman		syslog(LOG_ERR,
4181390Ssos		       "<%s> mtu out of range", __FUNCTION__);
41915508Sbde		exit(1);
4201390Ssos	}
4211390Ssos	tmp->linkmtu = (u_int32_t)val;
4221390Ssos	if (tmp->linkmtu == 0) {
4231390Ssos		char *mtustr;
4241390Ssos
42522106Sbde		if ((mtustr = (char *)agetstr("mtu", &bp)) &&
4261390Ssos		    strcmp(mtustr, "auto") == 0)
4271390Ssos			tmp->linkmtu = tmp->phymtu;
4281390Ssos	}
4291390Ssos	else if (tmp->linkmtu < IPV6_MMTU || tmp->linkmtu > tmp->phymtu) {
4301390Ssos		syslog(LOG_ERR,
4311390Ssos		       "<%s> advertised link mtu must be between"
4321390Ssos		       " least MTU and physical link MTU",
4331390Ssos		       __FUNCTION__);
4341390Ssos		exit(1);
4351390Ssos	}
4361390Ssos
4371390Ssos	/* route information */
4381390Ssos
4391390Ssos	MAYHAVE(val, "routes", 0);
4401390Ssos	if (val < 0 || val > 0xffffffff) {
4411390Ssos		syslog(LOG_ERR,
44221783Sbde		       "<%s> number of route information improper", __FUNCTION__);
44321783Sbde		exit(1);
44421783Sbde	}
44521783Sbde	tmp->routes = val;
44621783Sbde	for (i = 0; i < tmp->routes; i++) {
44721783Sbde		struct rtinfo *rti;
44821783Sbde		char entbuf[256];
4491390Ssos		int added = (tmp->routes > 1) ? 1 : 0;
4501390Ssos
4511390Ssos		/* allocate memory to store prefix information */
4521390Ssos		if ((rti = malloc(sizeof(struct rtinfo))) == NULL) {
4531390Ssos			syslog(LOG_ERR,
4541390Ssos			       "<%s> can't allocate enough memory",
45510268Sbde			       __FUNCTION__);
45622106Sbde			exit(1);
4571390Ssos		}
45815508Sbde		memset(rti, 0, sizeof(*rti));
4591390Ssos
4601390Ssos		/* link into chain */
46122106Sbde		insque(rti, &tmp->route);
46222106Sbde
46322106Sbde		makeentry(entbuf, sizeof(entbuf), i, "rtrplen", added);
46422106Sbde		MAYHAVE(val, entbuf, 64);
46522106Sbde		if (val < 0 || val > 128) {
46622106Sbde			syslog(LOG_ERR,
46722106Sbde			       "<%s> prefixlen out of range",
46822106Sbde			       __FUNCTION__);
46922106Sbde			exit(1);
47022106Sbde		}
47122106Sbde		rti->prefixlen = (int)val;
47222106Sbde
47322106Sbde		makeentry(entbuf, sizeof(entbuf), i, "rtrflags", added);
47422106Sbde		MAYHAVE(val, entbuf, 0);
47522106Sbde		rti->rtpref = val & ND_RA_FLAG_RTPREF_MASK;
47622106Sbde		if (rti->rtpref == ND_RA_FLAG_RTPREF_RSV) {
47722106Sbde			syslog(LOG_ERR, "<%s> invalid router preference",
47822106Sbde			       __FUNCTION__);
47922106Sbde			exit(1);
4801390Ssos		}
4811390Ssos
48210268Sbde		makeentry(entbuf, sizeof(entbuf), i, "rtrltime", added);
4831390Ssos		/*
4841390Ssos		 * XXX: since default value of route lifetime is not defined in
4851390Ssos		 * draft-draves-route-selection-01.txt, I took the default
48621783Sbde		 * value of valid lifetime of prefix as its default.
4871390Ssos		 * It need be much considered.
48821783Sbde		 */
48921783Sbde		MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME);
49021783Sbde		if (val64 < 0 || val64 > 0xffffffff) {
49121783Sbde			syslog(LOG_ERR,
49221783Sbde			       "<%s> rtrltime out of range",
49321783Sbde			       __FUNCTION__);
49421783Sbde			exit(1);
49521783Sbde		}
49621783Sbde		rti->ltime = (u_int32_t)val64;
49721783Sbde
49821783Sbde		makeentry(entbuf, sizeof(entbuf), i, "rtrprefix", added);
49921783Sbde		addr = (char *)agetstr(entbuf, &bp);
5001390Ssos		if (addr == NULL) {
5011390Ssos			syslog(LOG_ERR,
5021390Ssos			       "<%s> need %s as an route for "
5031390Ssos			       "interface %s",
5041390Ssos			       __FUNCTION__, entbuf, intface);
5051390Ssos			exit(1);
5061390Ssos		}
5071390Ssos		if (inet_pton(AF_INET6, addr, &rti->prefix) != 1) {
5081390Ssos			syslog(LOG_ERR,
5093185Ssos			       "<%s> inet_pton failed for %s",
5101390Ssos			       __FUNCTION__, addr);
5111390Ssos			exit(1);
5121390Ssos		}
5131390Ssos#if 0
5141390Ssos		/*
5151390Ssos		 * XXX: currently there's no restriction in route information
5168876Srgrimes		 * prefix according to draft-draves-route-selection-01.txt,
5171390Ssos		 * however I think the similar restriction be necessary.
5181390Ssos		 */
51917231Sjoerg		MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME);
5201390Ssos		if (IN6_IS_ADDR_MULTICAST(&rti->prefix)) {
5218876Srgrimes			syslog(LOG_ERR,
52217231Sjoerg			       "<%s> multicast route (%s) must "
52317231Sjoerg			       "not be advertised (IF=%s)",
52417231Sjoerg			       __FUNCTION__, addr, intface);
52517231Sjoerg			exit(1);
52617231Sjoerg		}
52766716Sjhb		if (IN6_IS_ADDR_LINKLOCAL(&rti->prefix)) {
5281390Ssos			syslog(LOG_NOTICE,
5291390Ssos			       "<%s> link-local route (%s) must "
53066716Sjhb			       "not be advertised on %s",
5311390Ssos			       __FUNCTION__, addr, intface);
53217231Sjoerg			exit(1);
53317231Sjoerg		}
5341390Ssos#endif
5352873Sbde	}
5361390Ssos
53717231Sjoerg	/* okey */
53817231Sjoerg	tmp->next = ralist;
5391390Ssos	ralist = tmp;
5401390Ssos
5412913Sache	/* construct the sending packet */
5422913Sache	make_packet(tmp);
5432913Sache
5442913Sache	/* set timer */
54514943Sbde	tmp->timer = rtadvd_add_timer(ra_timeout, ra_timer_update,
54614943Sbde				      tmp, tmp);
54714943Sbde	ra_timer_update((void *)tmp, &tmp->timer->tm);
54814943Sbde	rtadvd_set_timer(&tmp->timer->tm, tmp->timer);
54955098Sbde}
55014943Sbde
55114943Sbdestatic void
55255098Sbdeget_prefix(struct rainfo *rai)
55314943Sbde{
55414943Sbde	struct ifaddrs *ifap, *ifa;
55514943Sbde	struct prefix *pp;
55614943Sbde	struct in6_addr *a;
55755098Sbde	u_char *p, *ep, *m, *lim;
55814943Sbde	u_char ntopbuf[INET6_ADDRSTRLEN];
55914943Sbde
56014943Sbde	if (getifaddrs(&ifap) < 0) {
56113445Sphk		syslog(LOG_ERR,
5625291Sbde		       "<%s> can't get interface addresses",
5632913Sache		       __FUNCTION__);
56455098Sbde		exit(1);
56555098Sbde	}
56655098Sbde	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
56733309Sbde		if (strcmp(ifa->ifa_name, rai->ifname) != 0)
5685291Sbde			continue;
56933309Sbde		if (ifa->ifa_addr->sa_family != AF_INET6)
5705291Sbde			continue;
57133309Sbde		a = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
57255098Sbde		if (IN6_IS_ADDR_LINKLOCAL(a))
5732913Sache			continue;
5742913Sache
57513445Sphk		/* allocate memory to store prefix info. */
5762913Sache		if ((pp = malloc(sizeof(*pp))) == NULL) {
5772913Sache			syslog(LOG_ERR,
57813445Sphk			       "<%s> can't get allocate buffer for prefix",
5792913Sache			       __FUNCTION__);
5802913Sache			exit(1);
58115508Sbde		}
58215508Sbde		memset(pp, 0, sizeof(*pp));
58315508Sbde
58448160Sgreen		/* set prefix length */
58515508Sbde		m = (u_char *)&((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr;
58615508Sbde		lim = (u_char *)(ifa->ifa_netmask) + ifa->ifa_netmask->sa_len;
58715508Sbde		pp->prefixlen = prefixlen(m, lim);
58823393Sbde		if (pp->prefixlen < 0 || pp->prefixlen > 128) {
58923393Sbde			syslog(LOG_ERR,
59015508Sbde			       "<%s> failed to get prefixlen "
59115508Sbde			       "or prefix is invalid",
59215508Sbde			       __FUNCTION__);
59315508Sbde			exit(1);
59415508Sbde		}
59515508Sbde
59615508Sbde		/* set prefix, sweep bits outside of prefixlen */
59715508Sbde		memcpy(&pp->prefix, a, sizeof(*a));
59815508Sbde		p = (u_char *)&pp->prefix;
59915508Sbde		ep = (u_char *)(&pp->prefix + 1);
60015508Sbde		while (m < lim)
60115508Sbde			*p++ &= *m++;
60215508Sbde		while (p < ep)
60315508Sbde			*p++ = 0x00;
60415508Sbde
60515508Sbde	        if (!inet_ntop(AF_INET6, &pp->prefix, ntopbuf,
60615508Sbde	            sizeof(ntopbuf))) {
60715508Sbde			syslog(LOG_ERR, "<%s> inet_ntop failed", __FUNCTION__);
60815508Sbde			exit(1);
60915508Sbde		}
61015508Sbde		syslog(LOG_DEBUG,
61115508Sbde		       "<%s> add %s/%d to prefix list on %s",
61215508Sbde		       __FUNCTION__, ntopbuf, pp->prefixlen, rai->ifname);
61315508Sbde
61415508Sbde		/* set other fields with protocol defaults */
61515508Sbde		pp->validlifetime = DEF_ADVVALIDLIFETIME;
61615508Sbde		pp->preflifetime = DEF_ADVPREFERREDLIFETIME;
61715508Sbde		pp->onlinkflg = 1;
61815508Sbde		pp->autoconfflg = 1;
61915508Sbde		pp->origin = PREFIX_FROM_KERNEL;
62015508Sbde
62115508Sbde		/* link into chain */
62232054Sphk		insque(pp, &rai->prefix);
62348160Sgreen
62448266Speter		/* counter increment */
62548266Speter		rai->pfxs++;
62615508Sbde	}
62715508Sbde
62815508Sbde	freeifaddrs(ifap);
62915508Sbde}
63015508Sbde
63115508Sbdestatic void
63215508Sbdemakeentry(buf, len, id, string, add)
63315508Sbde	char *buf;
63415508Sbde	size_t len;
63515508Sbde	int id;
63615508Sbde	char *string;
63715508Sbde	int add;
63815508Sbde{
63915508Sbde	char *ep = buf + len;
64015508Sbde
64115508Sbde	strcpy(buf, string);
64215508Sbde	if (add) {
64315508Sbde		char *cp;
64415508Sbde
64515508Sbde		cp = (char *)index(buf, '\0');
64615508Sbde		snprintf(cp, ep - cp, "%d", id);
64715508Sbde	}
64815508Sbde}
64915508Sbde
65015508Sbde/*
65115508Sbde * Add a prefix to the list of specified interface and reconstruct
65215508Sbde * the outgoing packet.
65315508Sbde * The prefix must not be in the list.
65415508Sbde * XXX: other parameter of the prefix(e.g. lifetime) shoule be
65515508Sbde * able to be specified.
65615508Sbde */
65715508Sbdestatic void
65815508Sbdeadd_prefix(struct rainfo *rai, struct in6_prefixreq *ipr)
65933690Sphk{
66048160Sgreen	struct prefix *prefix;
66133690Sphk	u_char ntopbuf[INET6_ADDRSTRLEN];
66233690Sphk
66333690Sphk	if ((prefix = malloc(sizeof(*prefix))) == NULL) {
66432005Sphk		syslog(LOG_ERR, "<%s> memory allocation failed",
66533929Sphk		       __FUNCTION__);
66615508Sbde		return;		/* XXX: error or exit? */
66715508Sbde	}
66815508Sbde	memset(prefix, 0, sizeof(*prefix));
66915508Sbde	prefix->prefix = ipr->ipr_prefix.sin6_addr;
67023393Sbde	prefix->prefixlen = ipr->ipr_plen;
67123393Sbde	prefix->validlifetime = ipr->ipr_vltime;
67223393Sbde	prefix->preflifetime = ipr->ipr_pltime;
67315508Sbde	prefix->onlinkflg = ipr->ipr_raf_onlink;
67415508Sbde	prefix->autoconfflg = ipr->ipr_raf_auto;
67515508Sbde	prefix->origin = PREFIX_FROM_DYNAMIC;
67615508Sbde
67715508Sbde	insque(prefix, &rai->prefix);
67815508Sbde
67933309Sbde	syslog(LOG_DEBUG, "<%s> new prefix %s/%d was added on %s",
68015508Sbde	       __FUNCTION__, inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr,
68166716Sjhb				       ntopbuf, INET6_ADDRSTRLEN),
68215508Sbde	       ipr->ipr_plen, rai->ifname);
68333309Sbde
68433309Sbde	/* free the previous packet */
68533309Sbde	free(rai->ra_data);
68633309Sbde	rai->ra_data = NULL;
68733309Sbde
68833309Sbde	/* reconstruct the packet */
68933309Sbde	rai->pfxs++;
69066716Sjhb	make_packet(rai);
69115508Sbde
69215508Sbde	/*
6935291Sbde	 * reset the timer so that the new prefix will be advertised quickly.
69452669Siwasaki	 */
69552669Siwasaki	rai->initcounter = 0;
69652669Siwasaki	ra_timer_update((void *)rai, &rai->timer->tm);
69752669Siwasaki	rtadvd_set_timer(&rai->timer->tm, rai->timer);
69852669Siwasaki}
69952669Siwasaki
70052669Siwasaki/*
70152669Siwasaki * Delete a prefix to the list of specified interface and reconstruct
70252669Siwasaki * the outgoing packet.
70352669Siwasaki * The prefix must be in the list.
70452669Siwasaki */
70566716Sjhbvoid
70652669Siwasakidelete_prefix(struct rainfo *rai, struct prefix *prefix)
70752669Siwasaki{
70852669Siwasaki	u_char ntopbuf[INET6_ADDRSTRLEN];
70966716Sjhb
71052669Siwasaki	remque(prefix);
71152669Siwasaki	syslog(LOG_DEBUG, "<%s> prefix %s/%d was deleted on %s",
71252669Siwasaki	       __FUNCTION__, inet_ntop(AF_INET6, &prefix->prefix,
71333690Sphk				       ntopbuf, INET6_ADDRSTRLEN),
7145291Sbde	       prefix->prefixlen, rai->ifname);
7155291Sbde	free(prefix);
7161390Ssos	rai->pfxs--;
7178876Srgrimes	make_packet(rai);
718798Swollman}
71915508Sbde
72015508Sbde/*
72132054Sphk * Try to get an in6_prefixreq contents for a prefix which matches
72232054Sphk * ipr->ipr_prefix and ipr->ipr_plen and belongs to
72332054Sphk * the interface whose name is ipr->ipr_name[].
72432054Sphk */
72532054Sphkstatic int
72615508Sbdeinit_prefix(struct in6_prefixreq *ipr)
72715508Sbde{
72815508Sbde	int s;
72916874Sbde
73015508Sbde	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
73115508Sbde		syslog(LOG_ERR, "<%s> socket: %s", __FUNCTION__,
73215508Sbde		       strerror(errno));
73315508Sbde		exit(1);
73415508Sbde	}
73518288Sbde
73615508Sbde	if (ioctl(s, SIOCGIFPREFIX_IN6, (caddr_t)ipr) < 0) {
73715508Sbde		syslog(LOG_INFO, "<%s> ioctl:SIOCGIFPREFIX %s", __FUNCTION__,
73815508Sbde		       strerror(errno));
73915508Sbde
74015508Sbde		ipr->ipr_vltime = DEF_ADVVALIDLIFETIME;
74115508Sbde		ipr->ipr_pltime = DEF_ADVPREFERREDLIFETIME;
74215508Sbde		ipr->ipr_raf_onlink = 1;
74315508Sbde		ipr->ipr_raf_auto = 1;
74415508Sbde		/* omit other field initialization */
74515508Sbde	}
74615508Sbde	else if (ipr->ipr_origin < PR_ORIG_RR) {
74715508Sbde		u_char ntopbuf[INET6_ADDRSTRLEN];
74816300Spst
74919173Sbde		syslog(LOG_WARNING, "<%s> Added prefix(%s)'s origin %d is"
75015508Sbde		       "lower than PR_ORIG_RR(router renumbering)."
75115508Sbde		       "This should not happen if I am router", __FUNCTION__,
75215508Sbde		       inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr, ntopbuf,
75315508Sbde				 sizeof(ntopbuf)), ipr->ipr_origin);
75415508Sbde		close(s);
75523393Sbde		return 1;
75623393Sbde	}
75723393Sbde
75823393Sbde	close(s);
75932005Sphk	return 0;
76015508Sbde}
76115508Sbde
76215508Sbdevoid
76340610Sphkmake_prefix(struct rainfo *rai, int ifindex, struct in6_addr *addr, int plen)
76458377Sphk{
76515508Sbde	struct in6_prefixreq ipr;
76632005Sphk
76732005Sphk	memset(&ipr, 0, sizeof(ipr));
76816300Spst	if (if_indextoname(ifindex, ipr.ipr_name) == NULL) {
76919173Sbde		syslog(LOG_ERR, "<%s> Prefix added interface No.%d doesn't"
77032005Sphk		       "exist. This should not happen! %s", __FUNCTION__,
77132005Sphk		       ifindex, strerror(errno));
77215508Sbde		exit(1);
77315508Sbde	}
77432054Sphk	ipr.ipr_prefix.sin6_len = sizeof(ipr.ipr_prefix);
77515508Sbde	ipr.ipr_prefix.sin6_family = AF_INET6;
77615508Sbde	ipr.ipr_prefix.sin6_addr = *addr;
77715508Sbde	ipr.ipr_plen = plen;
77815508Sbde
77915508Sbde	if (init_prefix(&ipr))
78048160Sgreen		return; /* init failed by some error */
78148160Sgreen	add_prefix(rai, &ipr);
78215508Sbde}
78348160Sgreen
78432054Sphkvoid
78523393Sbdemake_packet(struct rainfo *rainfo)
78633690Sphk{
78716300Spst	size_t packlen, lladdroptlen = 0;
78815508Sbde	char *buf;
78934617Sphk	struct nd_router_advert *ra;
79034617Sphk	struct nd_opt_prefix_info *ndopt_pi;
79134617Sphk	struct nd_opt_mtu *ndopt_mtu;
79234617Sphk#ifdef MIP6
79334617Sphk	struct nd_opt_advinterval *ndopt_advint;
79434617Sphk	struct nd_opt_homeagent_info *ndopt_hai;
79534617Sphk#endif
79634617Sphk	struct nd_opt_route_info *ndopt_rti;
79734617Sphk	struct prefix *pfx;
79834617Sphk	struct rtinfo *rti;
79934617Sphk
80049186Smsmith	/* calculate total length */
80149186Smsmith	packlen = sizeof(struct nd_router_advert);
80234617Sphk	if (rainfo->advlinkopt) {
80334617Sphk		if ((lladdroptlen = lladdropt_length(rainfo->sdl)) == 0) {
80434617Sphk			syslog(LOG_INFO,
80534617Sphk			       "<%s> link-layer address option has"
80649186Smsmith			       " null length on %s."
80749186Smsmith			       " Treat as not included.",
80834617Sphk			       __FUNCTION__, rainfo->ifname);
80964031Sphk			rainfo->advlinkopt = 0;
81064031Sphk		}
81164031Sphk		packlen += lladdroptlen;
81264031Sphk	}
81364031Sphk	if (rainfo->pfxs)
81464031Sphk		packlen += sizeof(struct nd_opt_prefix_info) * rainfo->pfxs;
81534617Sphk	if (rainfo->linkmtu)
81634617Sphk		packlen += sizeof(struct nd_opt_mtu);
81747592Sphk#ifdef MIP6
81840610Sphk	if (mobileip6 && rainfo->maxinterval)
81958377Sphk		packlen += sizeof(struct nd_opt_advinterval);
82033690Sphk	if (mobileip6 && rainfo->hatime)
82134617Sphk		packlen += sizeof(struct nd_opt_homeagent_info);
82234617Sphk#endif
8234Srgrimes#ifdef ND_OPT_ROUTE_INFO
8244Srgrimes	for (rti = rainfo->route.next; rti != &rainfo->route; rti = rti->next)
8252913Sache		packlen += sizeof(struct nd_opt_route_info) +
82641787Smckay			   ((rti->prefixlen + 0x3f) >> 6) * 8;
82741787Smckay#endif
8282913Sache
8292913Sache	/* allocate memory for the packet */
8303185Ssos	if ((buf = malloc(packlen)) == NULL) {
8314Srgrimes		syslog(LOG_ERR,
8322913Sache		       "<%s> can't get enough memory for an RA packet",
8332913Sache		       __FUNCTION__);
8342913Sache		exit(1);
8352913Sache	}
83633690Sphk	if (rainfo->ra_data) {
8374Srgrimes		/* free the previous packet */
83832850Sphk		free(rainfo->ra_data);
83932850Sphk		rainfo->ra_data = NULL;
84033690Sphk	}
84133690Sphk	rainfo->ra_data = buf;
84258377Sphk	/* XXX: what if packlen > 576? */
84332850Sphk	rainfo->ra_datalen = packlen;
84432850Sphk
8451390Ssos	/*
84641787Smckay	 * construct the packet
8479202Srgrimes	 */
8482913Sache	ra = (struct nd_router_advert *)buf;
8494Srgrimes	ra->nd_ra_type = ND_ROUTER_ADVERT;
85041787Smckay	ra->nd_ra_code = 0;
85141787Smckay	ra->nd_ra_cksum = 0;
85255098Sbde	ra->nd_ra_curhoplimit = (u_int8_t)(0xff & rainfo->hoplimit);
85355098Sbde	ra->nd_ra_flags_reserved = 0; /* just in case */
85455098Sbde	/*
85555098Sbde	 * XXX: the router preference field, which is a 2-bit field, should be
85655098Sbde	 * initialized before other fields.
8574Srgrimes	 */
8582913Sache	ra->nd_ra_flags_reserved = 0xff & rainfo->rtpref;
8593355Sache	ra->nd_ra_flags_reserved |=
86041787Smckay		rainfo->managedflg ? ND_RA_FLAG_MANAGED : 0;
8613355Sache	ra->nd_ra_flags_reserved |=
8623355Sache		rainfo->otherflg ? ND_RA_FLAG_OTHER : 0;
8632913Sache#ifdef MIP6
8643355Sache	ra->nd_ra_flags_reserved |=
8653355Sache		rainfo->haflg ? ND_RA_FLAG_HA : 0;
86655098Sbde#endif
86755098Sbde	ra->nd_ra_router_lifetime = htons(rainfo->lifetime);
8682913Sache	ra->nd_ra_reachable = htonl(rainfo->reachabletime);
86955098Sbde	ra->nd_ra_retransmit = htonl(rainfo->retranstimer);
87041787Smckay	buf += sizeof(*ra);
87141787Smckay
87241787Smckay	if (rainfo->advlinkopt) {
87341787Smckay		lladdropt_fill(rainfo->sdl, (struct nd_opt_hdr *)buf);
8742913Sache		buf += lladdroptlen;
87541787Smckay	}
8762913Sache
8772913Sache	if (rainfo->linkmtu) {
87841787Smckay		ndopt_mtu = (struct nd_opt_mtu *)buf;
8792913Sache		ndopt_mtu->nd_opt_mtu_type = ND_OPT_MTU;
8802913Sache		ndopt_mtu->nd_opt_mtu_len = 1;
8812913Sache		ndopt_mtu->nd_opt_mtu_reserved = 0;
8822913Sache		ndopt_mtu->nd_opt_mtu_mtu = htonl(rainfo->linkmtu);
88341787Smckay		buf += sizeof(struct nd_opt_mtu);
88441787Smckay	}
8851390Ssos
88615054Sache#ifdef MIP6
8874Srgrimes	if (mobileip6 && rainfo->maxinterval) {
88834961Sphk		ndopt_advint = (struct nd_opt_advinterval *)buf;
88933690Sphk		ndopt_advint->nd_opt_adv_type = ND_OPT_ADVINTERVAL;
89033690Sphk		ndopt_advint->nd_opt_adv_len = 1;
89133690Sphk		ndopt_advint->nd_opt_adv_reserved = 0;
89233690Sphk		ndopt_advint->nd_opt_adv_interval = htonl(rainfo->maxinterval *
89358377Sphk							  1000);
89433690Sphk		buf += sizeof(struct nd_opt_advinterval);
89555098Sbde	}
8962913Sache#endif
8972913Sache
8982913Sache#ifdef MIP6
89941787Smckay	if (rainfo->hatime) {
90041787Smckay		ndopt_hai = (struct nd_opt_homeagent_info *)buf;
9014Srgrimes		ndopt_hai->nd_opt_hai_type = ND_OPT_HOMEAGENT_INFO;
9024Srgrimes		ndopt_hai->nd_opt_hai_len = 1;
9034Srgrimes		ndopt_hai->nd_opt_hai_reserved = 0;
90441787Smckay		ndopt_hai->nd_opt_hai_preference = htons(rainfo->hapref);
9054Srgrimes		ndopt_hai->nd_opt_hai_lifetime = htons(rainfo->hatime);
9064180Sbde		buf += sizeof(struct nd_opt_homeagent_info);
9074180Sbde	}
9084Srgrimes#endif
9092913Sache
91011872Sphk	for (pfx = rainfo->prefix.next;
9114Srgrimes	     pfx != &rainfo->prefix; pfx = pfx->next) {
9123366Sache		u_int32_t vltime, pltime;
9133366Sache		struct timeval now;
9143366Sache
9152913Sache		ndopt_pi = (struct nd_opt_prefix_info *)buf;
91634961Sphk		ndopt_pi->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION;
9172913Sache		ndopt_pi->nd_opt_pi_len = 4;
9184Srgrimes		ndopt_pi->nd_opt_pi_prefix_len = pfx->prefixlen;
9195291Sbde		ndopt_pi->nd_opt_pi_flags_reserved = 0;
9202913Sache		if (pfx->onlinkflg)
9214Srgrimes			ndopt_pi->nd_opt_pi_flags_reserved |=
92241787Smckay				ND_OPT_PI_FLAG_ONLINK;
9231390Ssos		if (pfx->autoconfflg)
92415054Sache			ndopt_pi->nd_opt_pi_flags_reserved |=
9252913Sache				ND_OPT_PI_FLAG_AUTO;
92613445Sphk#ifdef MIP6
92713445Sphk		if (pfx->routeraddr)
92813445Sphk			ndopt_pi->nd_opt_pi_flags_reserved |=
9292913Sache				ND_OPT_PI_FLAG_ROUTER;
93041787Smckay#endif
9312913Sache		if (pfx->vltimeexpire || pfx->pltimeexpire)
93213350Sache			gettimeofday(&now, NULL);
93313350Sache		if (pfx->vltimeexpire == 0)
93413350Sache			vltime = pfx->validlifetime;
93513350Sache		else
9362913Sache			vltime = (pfx->vltimeexpire > now.tv_sec) ?
9372913Sache				pfx->vltimeexpire - now.tv_sec : 0;
93813453Sache		if (pfx->pltimeexpire == 0)
93913350Sache			pltime = pfx->preflifetime;
94013445Sphk		else
9413355Sache			pltime = (pfx->pltimeexpire > now.tv_sec) ?
94213402Sbde				pfx->pltimeexpire - now.tv_sec : 0;
94313402Sbde		if (vltime < pltime) {
9442913Sache			/*
94513402Sbde			 * this can happen if vltime is decrement but pltime
94613402Sbde			 * is not.
94713402Sbde			 */
94813402Sbde			pltime = vltime;
94913402Sbde		}
95013402Sbde		ndopt_pi->nd_opt_pi_valid_time = htonl(vltime);
95113402Sbde		ndopt_pi->nd_opt_pi_preferred_time = htonl(pltime);
95213402Sbde		ndopt_pi->nd_opt_pi_reserved2 = 0;
95313445Sphk		ndopt_pi->nd_opt_pi_prefix = pfx->prefix;
95413445Sphk
9552913Sache		buf += sizeof(struct nd_opt_prefix_info);
9565291Sbde	}
95715345Snate
9584Srgrimes#ifdef ND_OPT_ROUTE_INFO
9594Srgrimes	for (rti = rainfo->route.next; rti != &rainfo->route; rti = rti->next) {
96027560Sfsmp		u_int8_t psize = (rti->prefixlen + 0x3f) >> 6;
9614Srgrimes
9625291Sbde		ndopt_rti = (struct nd_opt_route_info *)buf;
9634Srgrimes		ndopt_rti->nd_opt_rti_type = ND_OPT_ROUTE_INFO;
9645291Sbde		ndopt_rti->nd_opt_rti_len = 1 + psize;
9655291Sbde		ndopt_rti->nd_opt_rti_prefixlen = rti->prefixlen;
9664Srgrimes		ndopt_rti->nd_opt_rti_flags = 0xff & rti->rtpref;
9675291Sbde		ndopt_rti->nd_opt_rti_lifetime = rti->ltime;
96826949Sfsmp		memcpy(ndopt_rti + 1, &rti->prefix, psize * 8);
96966716Sjhb		buf += sizeof(struct nd_opt_route_info) + psize * 8;
97066716Sjhb	}
97125164Speter#endif
9724Srgrimes
97315345Snate	return;
97415345Snate}
97515345Snate
97615345Snatestatic int
97715345Snategetinet6sysctl(int code)
97815345Snate{
97915345Snate	int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 };
98015345Snate	int value;
98115345Snate	size_t size;
98215345Snate
98315345Snate	mib[3] = code;
98415345Snate	size = sizeof(value);
98515345Snate	if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), &value, &size, NULL, 0)
9864Srgrimes	    < 0) {
9875291Sbde		syslog(LOG_ERR, "<%s>: failed to get ip6 sysctl(%d): %s",
98826949Sfsmp		       __FUNCTION__, code,
98927563Sfsmp		       strerror(errno));
99038888Stegge		return(-1);
99134571Stegge	}
99234571Stegge	else
99334571Stegge		return(value);
99434571Stegge}
99534571Stegge