Home | History | Annotate | Line # | Download | only in rtadvd
config.c revision 1.29
      1 /*	$NetBSD: config.c,v 1.29 2012/12/11 16:37:23 roy Exp $	*/
      2 /*	$KAME: config.c,v 1.93 2005/10/17 14:40:02 suz Exp $	*/
      3 
      4 /*
      5  * Copyright (C) 1998 WIDE Project.
      6  * All rights reserved.
      7  *
      8  * Redistribution and use in source and binary forms, with or without
      9  * modification, are permitted provided that the following conditions
     10  * are met:
     11  * 1. Redistributions of source code must retain the above copyright
     12  *    notice, this list of conditions and the following disclaimer.
     13  * 2. Redistributions in binary form must reproduce the above copyright
     14  *    notice, this list of conditions and the following disclaimer in the
     15  *    documentation and/or other materials provided with the distribution.
     16  * 3. Neither the name of the project nor the names of its contributors
     17  *    may be used to endorse or promote products derived from this software
     18  *    without specific prior written permission.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
     21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
     24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     30  * SUCH DAMAGE.
     31  */
     32 
     33 #include <sys/param.h>
     34 #include <sys/ioctl.h>
     35 #include <sys/socket.h>
     36 #include <sys/time.h>
     37 #include <sys/sysctl.h>
     38 
     39 #include <net/if.h>
     40 #include <net/route.h>
     41 #include <net/if_dl.h>
     42 
     43 #include <netinet/in.h>
     44 #include <netinet/in_var.h>
     45 #include <netinet/ip6.h>
     46 #include <netinet6/ip6_var.h>
     47 #include <netinet/icmp6.h>
     48 #include <netinet6/nd6.h>
     49 
     50 #include <arpa/inet.h>
     51 
     52 #include <stdio.h>
     53 #include <syslog.h>
     54 #include <errno.h>
     55 #include <string.h>
     56 #include <stdlib.h>
     57 #include <search.h>
     58 #include <unistd.h>
     59 #include <ifaddrs.h>
     60 
     61 #include "rtadvd.h"
     62 #include "advcap.h"
     63 #include "timer.h"
     64 #include "if.h"
     65 #include "config.h"
     66 
     67 static time_t prefix_timo = (60 * 120);	/* 2 hours.
     68 					 * XXX: should be configurable. */
     69 static struct rtadvd_timer *prefix_timeout(void *);
     70 static void makeentry(char *, size_t, int, const char *);
     71 static int getinet6sysctl(int);
     72 
     73 static size_t
     74 encode_domain(char *dst, const char *src)
     75 {
     76 	ssize_t len;
     77 	char *odst, *p;
     78 
     79 	odst = dst;
     80 	while (src && (len = strlen(src)) != 0) {
     81 		p = strchr(src, '.');
     82 		*dst++ = len = MIN(63, p == NULL ? len : p - src);
     83 		memcpy(dst, src, len);
     84 		dst += len;
     85 		if (p == NULL)
     86 			break;
     87 		src = p + 1;
     88 	}
     89 	*dst++ = '\0';
     90 
     91 	return dst - odst;
     92 }
     93 
     94 void
     95 getconfig(const char *intface)
     96 {
     97 	int stat, c, i;
     98 	char tbuf[BUFSIZ];
     99 	struct rainfo *tmp;
    100 	int32_t val;
    101 	int64_t val64;
    102 	char buf[BUFSIZ];
    103 	char *bp = buf;
    104 	char *addr, *flagstr, *ap;
    105 	static int forwarding = -1;
    106 	char entbuf[256], abuf[256];
    107 
    108 #define MUSTHAVE(var, cap)	\
    109     do {								\
    110 	int64_t t;							\
    111 	if ((t = agetnum(cap)) < 0) {					\
    112 		fprintf(stderr, "rtadvd: need %s for interface %s\n",	\
    113 			cap, intface);					\
    114 		exit(1);						\
    115 	}								\
    116 	var = t;							\
    117      } while (0)
    118 #define MAYHAVE(var, cap, def)	\
    119      do {								\
    120 	if ((var = agetnum(cap)) < 0)					\
    121 		var = def;						\
    122      } while (0)
    123 #define	ELM_MALLOC(p,error_action)					\
    124 	do {								\
    125 		p = calloc(1, sizeof(*p));				\
    126 		if (p == NULL) {					\
    127 			syslog(LOG_ERR, "<%s> calloc failed: %m",	\
    128 			    __func__);					\
    129 			error_action;					\
    130 		}							\
    131 	} while(/*CONSTCOND*/0)
    132 
    133 
    134 	if ((stat = agetent(tbuf, intface)) <= 0) {
    135 		memset(tbuf, 0, sizeof(tbuf));
    136 		syslog(LOG_INFO,
    137 		       "<%s> %s isn't defined in the configuration file"
    138 		       " or the configuration file doesn't exist."
    139 		       " Treat it as default",
    140 		        __func__, intface);
    141 	}
    142 
    143 	ELM_MALLOC(tmp, exit(1));
    144 
    145 	/* check if we are allowed to forward packets (if not determined) */
    146 	if (forwarding < 0) {
    147 		if ((forwarding = getinet6sysctl(IPV6CTL_FORWARDING)) < 0)
    148 			exit(1);
    149 	}
    150 
    151 	/* get interface information */
    152 	if (agetflag("nolladdr"))
    153 		tmp->advlinkopt = 0;
    154 	else
    155 		tmp->advlinkopt = 1;
    156 	if (tmp->advlinkopt) {
    157 		if ((tmp->sdl = if_nametosdl(intface)) == NULL) {
    158 			syslog(LOG_ERR,
    159 			       "<%s> can't get information of %s",
    160 			       __func__, intface);
    161 			exit(1);
    162 		}
    163 		tmp->ifindex = tmp->sdl->sdl_index;
    164 	} else
    165 		tmp->ifindex = if_nametoindex(intface);
    166 	strlcpy(tmp->ifname, intface, sizeof(tmp->ifname));
    167 	if ((tmp->phymtu = if_getmtu(intface)) == 0) {
    168 		tmp->phymtu = IPV6_MMTU;
    169 		syslog(LOG_WARNING,
    170 		       "<%s> can't get interface mtu of %s. Treat as %d",
    171 		       __func__, intface, IPV6_MMTU);
    172 	}
    173 
    174 	/*
    175 	 * set router configuration variables.
    176 	 */
    177 	MAYHAVE(val, "maxinterval", DEF_MAXRTRADVINTERVAL);
    178 	if (val < MIN_MAXINTERVAL || val > MAX_MAXINTERVAL) {
    179 		syslog(LOG_ERR,
    180 		       "<%s> maxinterval (%d) on %s is invalid "
    181 		       "(must be between %u and %u)", __func__, val,
    182 		       intface, MIN_MAXINTERVAL, MAX_MAXINTERVAL);
    183 		exit(1);
    184 	}
    185 	tmp->maxinterval = val;
    186 	MAYHAVE(val, "mininterval", tmp->maxinterval/3);
    187 	if (val < MIN_MININTERVAL || val > (tmp->maxinterval * 3) / 4) {
    188 		syslog(LOG_ERR,
    189 		       "<%s> mininterval (%d) on %s is invalid "
    190 		       "(must be between %u and %d)",
    191 		       __func__, val, intface, MIN_MININTERVAL,
    192 		       (tmp->maxinterval * 3) / 4);
    193 		exit(1);
    194 	}
    195 	tmp->mininterval = val;
    196 
    197 	MAYHAVE(val, "chlim", DEF_ADVCURHOPLIMIT);
    198 	tmp->hoplimit = val & 0xff;
    199 
    200 	if ((flagstr = (char *)agetstr("raflags", &bp))) {
    201 		val = 0;
    202 		if (strchr(flagstr, 'm'))
    203 			val |= ND_RA_FLAG_MANAGED;
    204 		if (strchr(flagstr, 'o'))
    205 			val |= ND_RA_FLAG_OTHER;
    206 		if (strchr(flagstr, 'h'))
    207 			val |= ND_RA_FLAG_RTPREF_HIGH;
    208 		if (strchr(flagstr, 'l')) {
    209 			if ((val & ND_RA_FLAG_RTPREF_HIGH)) {
    210 				syslog(LOG_ERR, "<%s> the \'h\' and \'l\'"
    211 				    " router flags are exclusive", __func__);
    212 				exit(1);
    213 			}
    214 			val |= ND_RA_FLAG_RTPREF_LOW;
    215 		}
    216 	} else {
    217 		MAYHAVE(val, "raflags", 0);
    218 	}
    219 	tmp->managedflg = val & ND_RA_FLAG_MANAGED;
    220 	tmp->otherflg = val & ND_RA_FLAG_OTHER;
    221 #ifndef ND_RA_FLAG_RTPREF_MASK
    222 #define ND_RA_FLAG_RTPREF_MASK	0x18 /* 00011000 */
    223 #define ND_RA_FLAG_RTPREF_RSV	0x10 /* 00010000 */
    224 #endif
    225 	tmp->rtpref = val & ND_RA_FLAG_RTPREF_MASK;
    226 	if (tmp->rtpref == ND_RA_FLAG_RTPREF_RSV) {
    227 		syslog(LOG_ERR, "<%s> invalid router preference (%02x) on %s",
    228 		       __func__, tmp->rtpref, intface);
    229 		exit(1);
    230 	}
    231 
    232 	MAYHAVE(val, "rltime", tmp->maxinterval * 3);
    233 	if (val && (val < tmp->maxinterval || val > MAXROUTERLIFETIME)) {
    234 		syslog(LOG_ERR,
    235 		       "<%s> router lifetime (%d) on %s is invalid "
    236 		       "(must be 0 or between %d and %d)",
    237 		       __func__, val, intface,
    238 		       tmp->maxinterval, MAXROUTERLIFETIME);
    239 		exit(1);
    240 	}
    241 	/*
    242 	 * Basically, hosts MUST NOT send Router Advertisement messages at any
    243 	 * time (RFC 2461, Section 6.2.3). However, it would sometimes be
    244 	 * useful to allow hosts to advertise some parameters such as prefix
    245 	 * information and link MTU. Thus, we allow hosts to invoke rtadvd
    246 	 * only when router lifetime (on every advertising interface) is
    247 	 * explicitly set zero. (see also the above section)
    248 	 */
    249 	if (val && forwarding == 0) {
    250 		syslog(LOG_ERR,
    251 		       "<%s> non zero router lifetime is specified for %s, "
    252 		       "which must not be allowed for hosts.  you must "
    253 		       "change router lifetime or enable IPv6 forwarding.",
    254 		       __func__, intface);
    255 		exit(1);
    256 	}
    257 	tmp->lifetime = val & 0xffff;
    258 
    259 	MAYHAVE(val, "rtime", DEF_ADVREACHABLETIME);
    260 	if (val < 0 || val > MAXREACHABLETIME) {
    261 		syslog(LOG_ERR,
    262 		       "<%s> reachable time (%d) on %s is invalid "
    263 		       "(must be no greater than %d)",
    264 		       __func__, val, intface, MAXREACHABLETIME);
    265 		exit(1);
    266 	}
    267 	tmp->reachabletime = (uint32_t)val;
    268 
    269 	MAYHAVE(val64, "retrans", DEF_ADVRETRANSTIMER);
    270 	if (val64 < 0 || val64 > 0xffffffff) {
    271 		syslog(LOG_ERR, "<%s> retrans time (%lld) on %s out of range",
    272 		       __func__, (long long)val64, intface);
    273 		exit(1);
    274 	}
    275 	tmp->retranstimer = (uint32_t)val64;
    276 
    277 	if (agetnum("hapref") != -1 || agetnum("hatime") != -1) {
    278 		syslog(LOG_ERR,
    279 		       "<%s> mobile-ip6 configuration not supported",
    280 		       __func__);
    281 		exit(1);
    282 	}
    283 	/* prefix information */
    284 
    285 	/*
    286 	 * This is an implementation specific parameter to consider
    287 	 * link propagation delays and poorly synchronized clocks when
    288 	 * checking consistency of advertised lifetimes.
    289 	 */
    290 	MAYHAVE(val, "clockskew", 0);
    291 	tmp->clockskew = val;
    292 
    293 	tmp->pfxs = 0;
    294 	TAILQ_INIT(&tmp->prefix);
    295 	for (i = -1; i < MAXPREFIX; i++) {
    296 		struct prefix *pfx;
    297 
    298 		makeentry(entbuf, sizeof(entbuf), i, "addr");
    299 		addr = (char *)agetstr(entbuf, &bp);
    300 		if (addr == NULL)
    301 			continue;
    302 
    303 		/* allocate memory to store prefix information */
    304 		if ((pfx = calloc(1, sizeof(*pfx))) == NULL) {
    305 			syslog(LOG_ERR,
    306 			       "<%s> can't allocate memory: %m",
    307 			       __func__);
    308 			exit(1);
    309 		}
    310 
    311 		TAILQ_INSERT_TAIL(&tmp->prefix, pfx, next);
    312 		tmp->pfxs++;
    313 		pfx->rainfo = tmp;
    314 
    315 		pfx->origin = PREFIX_FROM_CONFIG;
    316 
    317 		if (inet_pton(AF_INET6, addr, &pfx->prefix) != 1) {
    318 			syslog(LOG_ERR,
    319 			       "<%s> inet_pton failed for %s",
    320 			       __func__, addr);
    321 			exit(1);
    322 		}
    323 		if (IN6_IS_ADDR_MULTICAST(&pfx->prefix)) {
    324 			syslog(LOG_ERR,
    325 			       "<%s> multicast prefix (%s) must "
    326 			       "not be advertised on %s",
    327 			       __func__, addr, intface);
    328 			exit(1);
    329 		}
    330 		if (IN6_IS_ADDR_LINKLOCAL(&pfx->prefix))
    331 			syslog(LOG_NOTICE,
    332 			       "<%s> link-local prefix (%s) will be"
    333 			       " advertised on %s",
    334 			       __func__, addr, intface);
    335 
    336 		makeentry(entbuf, sizeof(entbuf), i, "prefixlen");
    337 		MAYHAVE(val, entbuf, 64);
    338 		if (val < 0 || val > 128) {
    339 			syslog(LOG_ERR, "<%s> prefixlen (%d) for %s "
    340 			       "on %s out of range",
    341 			       __func__, val, addr, intface);
    342 			exit(1);
    343 		}
    344 		pfx->prefixlen = (int)val;
    345 
    346 		makeentry(entbuf, sizeof(entbuf), i, "pinfoflags");
    347 		if ((flagstr = (char *)agetstr(entbuf, &bp))) {
    348 			val = 0;
    349 			if (strchr(flagstr, 'l'))
    350 				val |= ND_OPT_PI_FLAG_ONLINK;
    351 			if (strchr(flagstr, 'a'))
    352 				val |= ND_OPT_PI_FLAG_AUTO;
    353 		} else {
    354 			MAYHAVE(val, entbuf,
    355 			    (ND_OPT_PI_FLAG_ONLINK|ND_OPT_PI_FLAG_AUTO));
    356 		}
    357 		pfx->onlinkflg = val & ND_OPT_PI_FLAG_ONLINK;
    358 		pfx->autoconfflg = val & ND_OPT_PI_FLAG_AUTO;
    359 
    360 		makeentry(entbuf, sizeof(entbuf), i, "vltime");
    361 		MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME);
    362 		if (val64 < 0 || val64 > 0xffffffff) {
    363 			syslog(LOG_ERR, "<%s> vltime (%lld) for "
    364 			    "%s/%d on %s is out of range",
    365 			    __func__, (long long)val64,
    366 			    addr, pfx->prefixlen, intface);
    367 			exit(1);
    368 		}
    369 		pfx->validlifetime = (uint32_t)val64;
    370 
    371 		makeentry(entbuf, sizeof(entbuf), i, "vltimedecr");
    372 		if (agetflag(entbuf)) {
    373 			struct timeval now;
    374 			gettimeofday(&now, 0);
    375 			pfx->vltimeexpire =
    376 				now.tv_sec + pfx->validlifetime;
    377 		}
    378 
    379 		makeentry(entbuf, sizeof(entbuf), i, "pltime");
    380 		MAYHAVE(val64, entbuf, DEF_ADVPREFERREDLIFETIME);
    381 		if (val64 < 0 || val64 > 0xffffffff) {
    382 			syslog(LOG_ERR,
    383 			    "<%s> pltime (%lld) for %s/%d on %s "
    384 			    "is out of range",
    385 			    __func__, (long long)val64,
    386 			    addr, pfx->prefixlen, intface);
    387 			exit(1);
    388 		}
    389 		pfx->preflifetime = (uint32_t)val64;
    390 
    391 		makeentry(entbuf, sizeof(entbuf), i, "pltimedecr");
    392 		if (agetflag(entbuf)) {
    393 			struct timeval now;
    394 			gettimeofday(&now, 0);
    395 			pfx->pltimeexpire =
    396 				now.tv_sec + pfx->preflifetime;
    397 		}
    398 	}
    399 	if (TAILQ_FIRST(&tmp->prefix) == NULL && !agetflag("noifprefix"))
    400 		get_prefix(tmp);
    401 
    402 	MAYHAVE(val64, "mtu", 0);
    403 	if (val64 < 0 || val64 > 0xffffffff) {
    404 		syslog(LOG_ERR,
    405 		       "<%s> mtu (%" PRIi64 ") on %s out of range",
    406 		       __func__, val64, intface);
    407 		exit(1);
    408 	}
    409 	tmp->linkmtu = (uint32_t)val64;
    410 	if (tmp->linkmtu == 0) {
    411 		char *mtustr;
    412 
    413 		if ((mtustr = (char *)agetstr("mtu", &bp)) &&
    414 		    strcmp(mtustr, "auto") == 0)
    415 			tmp->linkmtu = tmp->phymtu;
    416 	}
    417 	else if (tmp->linkmtu < IPV6_MMTU || tmp->linkmtu > tmp->phymtu) {
    418 		syslog(LOG_ERR,
    419 		       "<%s> advertised link mtu (%d) on %s is invalid (must "
    420 		       "be between least MTU (%d) and physical link MTU (%d)",
    421 		       __func__, tmp->linkmtu, intface,
    422 		       IPV6_MMTU, tmp->phymtu);
    423 		exit(1);
    424 	}
    425 
    426 #ifdef SIOCSIFINFO_IN6
    427 	{
    428 		struct in6_ndireq ndi;
    429 		int s;
    430 
    431 		if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
    432 			syslog(LOG_ERR, "<%s> socket: %m", __func__);
    433 			exit(1);
    434 		}
    435 		memset(&ndi, 0, sizeof(ndi));
    436 		strncpy(ndi.ifname, intface, IFNAMSIZ);
    437 		if (ioctl(s, SIOCGIFINFO_IN6, &ndi) < 0) {
    438 			syslog(LOG_INFO, "<%s> ioctl:SIOCGIFINFO_IN6 at %s: %m",
    439 			     __func__, intface);
    440 		}
    441 
    442 		/* reflect the RA info to the host variables in kernel */
    443 		ndi.ndi.chlim = tmp->hoplimit;
    444 		ndi.ndi.retrans = tmp->retranstimer;
    445 		ndi.ndi.basereachable = tmp->reachabletime;
    446 		if (ioctl(s, SIOCSIFINFO_IN6, &ndi) < 0) {
    447 			syslog(LOG_INFO, "<%s> ioctl:SIOCSIFINFO_IN6 at %s: %m",
    448 			     __func__, intface);
    449 		}
    450 		close(s);
    451 	}
    452 #endif
    453 
    454 	/* route information */
    455 	TAILQ_INIT(&tmp->route);
    456 	for (i = -1; i < MAXROUTE; i++) {
    457 		struct rtinfo *rti;
    458 		char oentbuf[256];
    459 
    460 		makeentry(entbuf, sizeof(entbuf), i, "rtprefix");
    461 		addr = (char *)agetstr(entbuf, &bp);
    462 		if (addr == NULL) {
    463 			makeentry(oentbuf, sizeof(oentbuf), i, "rtrprefix");
    464 			addr = (char *)agetstr(oentbuf, &bp);
    465 			if (addr) {
    466 				fprintf(stderr, "%s was obsoleted.  Use %s.\n",
    467 					oentbuf, entbuf);
    468 			}
    469 		}
    470 		if (addr == NULL)
    471 			continue;
    472 
    473 		ELM_MALLOC(rti, exit(1));
    474 		memset(rti, 0, sizeof(*rti));
    475 
    476 		/* link into chain */
    477 		TAILQ_INSERT_TAIL(&tmp->route, rti, next);
    478 
    479 		if (inet_pton(AF_INET6, addr, &rti->prefix) != 1) {
    480 			syslog(LOG_ERR, "<%s> inet_pton failed for %s",
    481 			       __func__, addr);
    482 			exit(1);
    483 		}
    484 #if 0
    485 		/*
    486 		 * XXX: currently there's no restriction in route information
    487 		 * prefix according to
    488 		 * draft-ietf-ipngwg-router-selection-00.txt.
    489 		 * However, I think the similar restriction be necessary.
    490 		 */
    491 		MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME);
    492 		if (IN6_IS_ADDR_MULTICAST(&rti->prefix)) {
    493 			syslog(LOG_ERR,
    494 			       "<%s> multicast route (%s) must "
    495 			       "not be advertised on %s",
    496 			       __func__, addr, intface);
    497 			exit(1);
    498 		}
    499 		if (IN6_IS_ADDR_LINKLOCAL(&rti->prefix)) {
    500 			syslog(LOG_NOTICE,
    501 			       "<%s> link-local route (%s) will "
    502 			       "be advertised on %s",
    503 			       __func__, addr, intface);
    504 			exit(1);
    505 		}
    506 #endif
    507 
    508 		makeentry(entbuf, sizeof(entbuf), i, "rtplen");
    509 		/* XXX: 256 is a magic number for compatibility check. */
    510 		MAYHAVE(val, entbuf, 256);
    511 		if (val == 256) {
    512 			makeentry(oentbuf, sizeof(oentbuf), i, "rtrplen");
    513 			MAYHAVE(val, oentbuf, 256);
    514 			if (val != 256) {
    515 				fprintf(stderr, "%s was obsoleted.  Use %s.\n",
    516 					oentbuf, entbuf);
    517 			} else
    518 				val = 64;
    519 		}
    520 		if (val < 0 || val > 128) {
    521 			syslog(LOG_ERR, "<%s> prefixlen (%d) for %s on %s "
    522 			       "out of range",
    523 			       __func__, val, addr, intface);
    524 			exit(1);
    525 		}
    526 		rti->prefixlen = (int)val;
    527 
    528 		makeentry(entbuf, sizeof(entbuf), i, "rtflags");
    529 		if ((flagstr = (char *)agetstr(entbuf, &bp))) {
    530 			val = 0;
    531 			if (strchr(flagstr, 'h'))
    532 				val |= ND_RA_FLAG_RTPREF_HIGH;
    533 			if (strchr(flagstr, 'l')) {
    534 				if ((val & ND_RA_FLAG_RTPREF_HIGH)) {
    535 					syslog(LOG_ERR,
    536 					    "<%s> the \'h\' and \'l\' route"
    537 					    " preferences are exclusive",
    538 					    __func__);
    539 					exit(1);
    540 				}
    541 				val |= ND_RA_FLAG_RTPREF_LOW;
    542 			}
    543 		} else
    544 			MAYHAVE(val, entbuf, 256); /* XXX */
    545 		if (val == 256) {
    546 			makeentry(oentbuf, sizeof(oentbuf), i, "rtrflags");
    547 			MAYHAVE(val, oentbuf, 256);
    548 			if (val != 256) {
    549 				fprintf(stderr, "%s was obsoleted.  Use %s.\n",
    550 					oentbuf, entbuf);
    551 			} else
    552 				val = 0;
    553 		}
    554 		rti->rtpref = val & ND_RA_FLAG_RTPREF_MASK;
    555 		if (rti->rtpref == ND_RA_FLAG_RTPREF_RSV) {
    556 			syslog(LOG_ERR, "<%s> invalid route preference (%02x) "
    557 			       "for %s/%d on %s",
    558 			       __func__, rti->rtpref, addr,
    559 			       rti->prefixlen, intface);
    560 			exit(1);
    561 		}
    562 
    563 		/*
    564 		 * Since the spec does not a default value, we should make
    565 		 * this entry mandatory.  However, FreeBSD 4.4 has shipped
    566 		 * with this field being optional, we use the router lifetime
    567 		 * as an ad-hoc default value with a warning message.
    568 		 */
    569 		makeentry(entbuf, sizeof(entbuf), i, "rtltime");
    570 		MAYHAVE(val64, entbuf, -1);
    571 		if (val64 == -1) {
    572 			makeentry(oentbuf, sizeof(oentbuf), i, "rtrltime");
    573 			MAYHAVE(val64, oentbuf, -1);
    574 			if (val64 != -1) {
    575 				fprintf(stderr, "%s was obsoleted.  Use %s.\n",
    576 					oentbuf, entbuf);
    577 			} else {
    578 				fprintf(stderr, "%s should be specified "
    579 					"for interface %s.\n",
    580 					entbuf, intface);
    581 				val64 = tmp->lifetime;
    582 			}
    583 		}
    584 		if (val64 < 0 || val64 > 0xffffffff) {
    585 			syslog(LOG_ERR, "<%s> route lifetime (%lld) for "
    586 			    "%s/%d on %s out of range", __func__,
    587 			    (long long)val64, addr, rti->prefixlen, intface);
    588 			exit(1);
    589 		}
    590 		rti->ltime = (uint32_t)val64;
    591 	}
    592 
    593 	/* RDNSS */
    594 	TAILQ_INIT(&tmp->rdnss);
    595 	for (i = -1; i < MAXRDNSS; i++) {
    596 		struct rdnss *rdnss;
    597 		struct rdnss_addr *rdnsa;
    598 
    599 		makeentry(entbuf, sizeof(entbuf), i, "rdnss");
    600 		addr = (char *)agetstr(entbuf, &bp);
    601 		if (addr == NULL)
    602 			continue;
    603 
    604 		ELM_MALLOC(rdnss, exit(1));
    605 		TAILQ_INIT(&rdnss->list);
    606 
    607 		for (ap = addr; ap - addr < (ssize_t)strlen(addr); ap += c+1) {
    608 			c = strcspn(ap, ",");
    609 			strncpy(abuf, ap, c);
    610 			abuf[c] = '\0';
    611 			ELM_MALLOC(rdnsa, exit(1));
    612 			if (inet_pton(AF_INET6, abuf, &rdnsa->addr) != 1) {
    613 				syslog(LOG_ERR, "<%s> inet_pton failed for %s",
    614 			           __func__, addr);
    615 				exit(1);
    616 			}
    617 			TAILQ_INSERT_TAIL(&rdnss->list, rdnsa, next);
    618 		}
    619 
    620 		makeentry(entbuf, sizeof(entbuf), i, "rdnssltime");
    621 		MAYHAVE(val64, entbuf, tmp->maxinterval * 3 / 2);
    622 		if (val64 < tmp->maxinterval ||
    623 		    val64 > tmp->maxinterval * 2)
    624 		{
    625 			syslog(LOG_ERR, "<%s> %s (%lld) on %s is invalid",
    626 		    	     __func__, entbuf, (long long)val64, intface);
    627 			exit(1);
    628 		}
    629 		rdnss->lifetime = (uint32_t)val64;
    630 
    631 		TAILQ_INSERT_TAIL(&tmp->rdnss, rdnss, next);
    632 	}
    633 
    634 	/* DNSSL */
    635 	TAILQ_INIT(&tmp->dnssl);
    636 	for (i = -1; i < MAXDNSSL; i++) {
    637 		struct dnssl *dnssl;
    638 		struct dnssl_domain *dnsd;
    639 
    640 		makeentry(entbuf, sizeof(entbuf), i, "dnssl");
    641 		addr = (char *)agetstr(entbuf, &bp);
    642 		if (addr == NULL)
    643 			continue;
    644 
    645 		ELM_MALLOC(dnssl, exit(1));
    646 		TAILQ_INIT(&dnssl->list);
    647 
    648 		for (ap = addr; ap - addr < (ssize_t)strlen(addr); ap += c+1) {
    649 			c = strcspn(ap, ",");
    650 			strncpy(abuf, ap, c);
    651 			abuf[c] = '\0';
    652 			ELM_MALLOC(dnsd, exit(1));
    653 			dnsd->len = encode_domain(dnsd->domain, abuf);
    654 			TAILQ_INSERT_TAIL(&dnssl->list, dnsd, next);
    655 		}
    656 
    657 		makeentry(entbuf, sizeof(entbuf), i, "dnsslltime");
    658 		MAYHAVE(val64, entbuf, tmp->maxinterval * 3 / 2);
    659 		if (val64 < tmp->maxinterval ||
    660 		    val64 > tmp->maxinterval * 2)
    661 		{
    662 			syslog(LOG_ERR, "<%s> %s (%lld) on %s is invalid",
    663 		    	     __func__, entbuf, (long long)val64, intface);
    664 			exit(1);
    665 		}
    666 		dnssl->lifetime = (uint32_t)val64;
    667 
    668 		TAILQ_INSERT_TAIL(&tmp->dnssl, dnssl, next);
    669 	}
    670 
    671 	/* okey */
    672 	TAILQ_INSERT_TAIL(&ralist, tmp, next);
    673 
    674 	/* construct the sending packet */
    675 	make_packet(tmp);
    676 
    677 	/* set timer */
    678 	tmp->timer = rtadvd_add_timer(ra_timeout, ra_timer_update,
    679 				      tmp, tmp);
    680 	ra_timer_update((void *)tmp, &tmp->timer->tm);
    681 	rtadvd_set_timer(&tmp->timer->tm, tmp->timer);
    682 }
    683 
    684 void
    685 get_prefix(struct rainfo *rai)
    686 {
    687 	struct ifaddrs *ifap, *ifa;
    688 	struct prefix *pp;
    689 	struct in6_addr *a;
    690 	unsigned char *p, *ep, *m, *lim;
    691 	char ntopbuf[INET6_ADDRSTRLEN];
    692 
    693 	if (getifaddrs(&ifap) < 0) {
    694 		syslog(LOG_ERR,
    695 		       "<%s> can't get interface addresses",
    696 		       __func__);
    697 		exit(1);
    698 	}
    699 
    700 	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
    701 		int plen;
    702 
    703 		if (strcmp(ifa->ifa_name, rai->ifname) != 0)
    704 			continue;
    705 		if (ifa->ifa_addr->sa_family != AF_INET6)
    706 			continue;
    707 		a = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
    708 		if (IN6_IS_ADDR_LINKLOCAL(a))
    709 			continue;
    710 		/* get prefix length */
    711 		m = (unsigned char *)&((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr;
    712 		lim = (unsigned char *)(ifa->ifa_netmask) + ifa->ifa_netmask->sa_len;
    713 		plen = prefixlen(m, lim);
    714 		if (plen <= 0 || plen > 128) {
    715 			syslog(LOG_ERR, "<%s> failed to get prefixlen "
    716 			       "or prefix is invalid",
    717 			       __func__);
    718 			exit(1);
    719 		}
    720 		if (plen == 128)	/* XXX */
    721 			continue;
    722 		if (find_prefix(rai, a, plen)) {
    723 			/* ignore a duplicated prefix. */
    724 			continue;
    725 		}
    726 
    727 		/* allocate memory to store prefix info. */
    728 		if ((pp = calloc(1, sizeof(*pp))) == NULL) {
    729 			syslog(LOG_ERR,
    730 			       "<%s> can't get allocate buffer for prefix",
    731 			       __func__);
    732 			exit(1);
    733 		}
    734 
    735 		/* set prefix, sweep bits outside of prefixlen */
    736 		pp->prefixlen = plen;
    737 		memcpy(&pp->prefix, a, sizeof(*a));
    738 		if (1)
    739 		{
    740 			p = (unsigned char *)&pp->prefix;
    741 			ep = (unsigned char *)(&pp->prefix + 1);
    742 			while (m < lim && p < ep)
    743 				*p++ &= *m++;
    744 			while (p < ep)
    745 				*p++ = 0x00;
    746 		}
    747 	        if (!inet_ntop(AF_INET6, &pp->prefix, ntopbuf,
    748 	            sizeof(ntopbuf))) {
    749 			syslog(LOG_ERR, "<%s> inet_ntop failed", __func__);
    750 			exit(1);
    751 		}
    752 		syslog(LOG_DEBUG,
    753 		       "<%s> add %s/%d to prefix list on %s",
    754 		       __func__, ntopbuf, pp->prefixlen, rai->ifname);
    755 
    756 		/* set other fields with protocol defaults */
    757 		pp->validlifetime = DEF_ADVVALIDLIFETIME;
    758 		pp->preflifetime = DEF_ADVPREFERREDLIFETIME;
    759 		pp->onlinkflg = 1;
    760 		pp->autoconfflg = 1;
    761 		pp->origin = PREFIX_FROM_KERNEL;
    762 		pp->rainfo = rai;
    763 
    764 		/* link into chain */
    765 		TAILQ_INSERT_TAIL(&rai->prefix, pp, next);
    766 		rai->pfxs++;
    767 	}
    768 
    769 	freeifaddrs(ifap);
    770 }
    771 
    772 static void
    773 makeentry(char *buf, size_t len, int id, const char *string)
    774 {
    775 
    776 	if (id < 0)
    777 		strlcpy(buf, string, len);
    778 	else
    779 		snprintf(buf, len, "%s%d", string, id);
    780 }
    781 
    782 /*
    783  * Add a prefix to the list of specified interface and reconstruct
    784  * the outgoing packet.
    785  * The prefix must not be in the list.
    786  * XXX: other parameters of the prefix(e.g. lifetime) should be
    787  * able to be specified.
    788  */
    789 static void
    790 add_prefix(struct rainfo *rai, struct in6_prefixreq *ipr)
    791 {
    792 	struct prefix *prefix;
    793 	char ntopbuf[INET6_ADDRSTRLEN];
    794 
    795 	if ((prefix = calloc(1, sizeof(*prefix))) == NULL) {
    796 		syslog(LOG_ERR, "<%s> memory allocation failed",
    797 		       __func__);
    798 		return;		/* XXX: error or exit? */
    799 	}
    800 	prefix->prefix = ipr->ipr_prefix.sin6_addr;
    801 	prefix->prefixlen = ipr->ipr_plen;
    802 	prefix->validlifetime = ipr->ipr_vltime;
    803 	prefix->preflifetime = ipr->ipr_pltime;
    804 	prefix->onlinkflg = ipr->ipr_raf_onlink;
    805 	prefix->autoconfflg = ipr->ipr_raf_auto;
    806 	prefix->origin = PREFIX_FROM_DYNAMIC;
    807 
    808 	prefix->rainfo = rai;
    809 	TAILQ_INSERT_TAIL(&rai->prefix, prefix, next);
    810 	rai->pfxs++;
    811 
    812 	syslog(LOG_DEBUG, "<%s> new prefix %s/%d was added on %s",
    813 	       __func__, inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr,
    814 				       ntopbuf, INET6_ADDRSTRLEN),
    815 	       ipr->ipr_plen, rai->ifname);
    816 
    817 	/* free the previous packet */
    818 	free(rai->ra_data);
    819 	rai->ra_data = NULL;
    820 
    821 	/* reconstruct the packet */
    822 	make_packet(rai);
    823 }
    824 
    825 /*
    826  * Delete a prefix to the list of specified interface and reconstruct
    827  * the outgoing packet.
    828  * The prefix must be in the list.
    829  */
    830 void
    831 delete_prefix(struct prefix *prefix)
    832 {
    833 	char ntopbuf[INET6_ADDRSTRLEN];
    834 	struct rainfo *rai = prefix->rainfo;
    835 
    836 	TAILQ_REMOVE(&rai->prefix, prefix, next);
    837 	rai->pfxs--;
    838 	syslog(LOG_DEBUG, "<%s> prefix %s/%d was deleted on %s",
    839 	       __func__, inet_ntop(AF_INET6, &prefix->prefix,
    840 				       ntopbuf, INET6_ADDRSTRLEN),
    841 	       prefix->prefixlen, rai->ifname);
    842 	if (prefix->timer)
    843 		rtadvd_remove_timer(&prefix->timer);
    844 	free(prefix);
    845 }
    846 
    847 void
    848 invalidate_prefix(struct prefix *prefix)
    849 {
    850 	char ntopbuf[INET6_ADDRSTRLEN];
    851 	struct timeval timo;
    852 	struct rainfo *rai = prefix->rainfo;
    853 
    854 	if (prefix->timer) {	/* sanity check */
    855 		syslog(LOG_ERR,
    856 		    "<%s> assumption failure: timer already exists",
    857 		    __func__);
    858 		exit(1);
    859 	}
    860 
    861 	syslog(LOG_DEBUG, "<%s> prefix %s/%d was invalidated on %s, "
    862 	    "will expire in %ld seconds", __func__,
    863 	    inet_ntop(AF_INET6, &prefix->prefix, ntopbuf, INET6_ADDRSTRLEN),
    864 	    prefix->prefixlen, rai->ifname, (long)prefix_timo);
    865 
    866 	/* set the expiration timer */
    867 	prefix->timer = rtadvd_add_timer(prefix_timeout, NULL, prefix, NULL);
    868 	if (prefix->timer == NULL) {
    869 		syslog(LOG_ERR, "<%s> failed to add a timer for a prefix. "
    870 		    "remove the prefix", __func__);
    871 		delete_prefix(prefix);
    872 	}
    873 	timo.tv_sec = prefix_timo;
    874 	timo.tv_usec = 0;
    875 	rtadvd_set_timer(&timo, prefix->timer);
    876 }
    877 
    878 static struct rtadvd_timer *
    879 prefix_timeout(void *arg)
    880 {
    881 	struct prefix *prefix = (struct prefix *)arg;
    882 
    883 	delete_prefix(prefix);
    884 
    885 	return(NULL);
    886 }
    887 
    888 void
    889 update_prefix(struct prefix * prefix)
    890 {
    891 	char ntopbuf[INET6_ADDRSTRLEN];
    892 	struct rainfo *rai = prefix->rainfo;
    893 
    894 	if (prefix->timer == NULL) { /* sanity check */
    895 		syslog(LOG_ERR,
    896 		    "<%s> assumption failure: timer does not exist",
    897 		    __func__);
    898 		exit(1);
    899 	}
    900 
    901 	syslog(LOG_DEBUG, "<%s> prefix %s/%d was re-enabled on %s",
    902 	    __func__, inet_ntop(AF_INET6, &prefix->prefix, ntopbuf,
    903 	    INET6_ADDRSTRLEN), prefix->prefixlen, rai->ifname);
    904 
    905 	/* stop the expiration timer */
    906 	rtadvd_remove_timer(&prefix->timer);
    907 }
    908 
    909 /*
    910  * Try to get an in6_prefixreq contents for a prefix which matches
    911  * ipr->ipr_prefix and ipr->ipr_plen and belongs to
    912  * the interface whose name is ipr->ipr_name[].
    913  */
    914 static int
    915 init_prefix(struct in6_prefixreq *ipr)
    916 {
    917 #if 0
    918 	int s;
    919 
    920 	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
    921 		syslog(LOG_ERR, "<%s> socket: %m", __func__);
    922 		exit(1);
    923 	}
    924 
    925 	if (ioctl(s, SIOCGIFPREFIX_IN6, ipr) < 0) {
    926 		syslog(LOG_INFO, "<%s> ioctl:SIOCGIFPREFIX: %m", __func__);
    927 
    928 		ipr->ipr_vltime = DEF_ADVVALIDLIFETIME;
    929 		ipr->ipr_pltime = DEF_ADVPREFERREDLIFETIME;
    930 		ipr->ipr_raf_onlink = 1;
    931 		ipr->ipr_raf_auto = 1;
    932 		/* omit other field initialization */
    933 	}
    934 	else if (ipr->ipr_origin < PR_ORIG_RR) {
    935 		char ntopbuf[INET6_ADDRSTRLEN];
    936 
    937 		syslog(LOG_WARNING, "<%s> Added prefix(%s)'s origin %d is"
    938 		       "lower than PR_ORIG_RR(router renumbering)."
    939 		       "This should not happen if I am router", __func__,
    940 		       inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr, ntopbuf,
    941 				 sizeof(ntopbuf)), ipr->ipr_origin);
    942 		close(s);
    943 		return 1;
    944 	}
    945 
    946 	close(s);
    947 	return 0;
    948 #else
    949 	ipr->ipr_vltime = DEF_ADVVALIDLIFETIME;
    950 	ipr->ipr_pltime = DEF_ADVPREFERREDLIFETIME;
    951 	ipr->ipr_raf_onlink = 1;
    952 	ipr->ipr_raf_auto = 1;
    953 	return 0;
    954 #endif
    955 }
    956 
    957 void
    958 make_prefix(struct rainfo *rai, int ifindex, struct in6_addr *addr, int plen)
    959 {
    960 	struct in6_prefixreq ipr;
    961 
    962 	memset(&ipr, 0, sizeof(ipr));
    963 	if (if_indextoname(ifindex, ipr.ipr_name) == NULL) {
    964 		syslog(LOG_ERR, "<%s> Prefix added interface No.%d doesn't"
    965 		       "exist. This should not happen: %m", __func__,
    966 		       ifindex);
    967 		exit(1);
    968 	}
    969 	ipr.ipr_prefix.sin6_len = sizeof(ipr.ipr_prefix);
    970 	ipr.ipr_prefix.sin6_family = AF_INET6;
    971 	ipr.ipr_prefix.sin6_addr = *addr;
    972 	ipr.ipr_plen = plen;
    973 
    974 	if (init_prefix(&ipr))
    975 		return; /* init failed by some error */
    976 	add_prefix(rai, &ipr);
    977 }
    978 
    979 void
    980 make_packet(struct rainfo *rainfo)
    981 {
    982 	size_t packlen, lladdroptlen = 0;
    983 	char *buf;
    984 	struct nd_router_advert *ra;
    985 	struct nd_opt_prefix_info *ndopt_pi;
    986 	struct nd_opt_mtu *ndopt_mtu;
    987 	struct prefix *pfx;
    988 	struct nd_opt_route_info *ndopt_rti;
    989 	struct rtinfo *rti;
    990 	struct nd_opt_rdnss *ndopt_rdnss;
    991 	struct rdnss *rdns;
    992 	struct rdnss_addr *rdnsa;
    993 	struct nd_opt_dnssl *ndopt_dnssl;
    994 	struct dnssl *dnsl;
    995 	struct dnssl_domain *dnsd;
    996 	size_t len, plen;
    997 
    998 	/* calculate total length */
    999 	packlen = sizeof(struct nd_router_advert);
   1000 	if (rainfo->advlinkopt) {
   1001 		if ((lladdroptlen = lladdropt_length(rainfo->sdl)) == 0) {
   1002 			syslog(LOG_INFO,
   1003 			       "<%s> link-layer address option has"
   1004 			       " null length on %s.  Treat as not included.",
   1005 			       __func__, rainfo->ifname);
   1006 			rainfo->advlinkopt = 0;
   1007 		}
   1008 		packlen += lladdroptlen;
   1009 	}
   1010 	if (TAILQ_FIRST(&rainfo->prefix) != NULL)
   1011 		packlen += sizeof(struct nd_opt_prefix_info) * rainfo->pfxs;
   1012 	if (rainfo->linkmtu)
   1013 		packlen += sizeof(struct nd_opt_mtu);
   1014 	TAILQ_FOREACH(rti, &rainfo->route, next)
   1015 		packlen += sizeof(struct nd_opt_route_info) +
   1016 			   ((rti->prefixlen + 0x3f) >> 6) * 8;
   1017 
   1018 	TAILQ_FOREACH(rdns, &rainfo->rdnss, next) {
   1019 		packlen += sizeof(struct nd_opt_rdnss);
   1020 		TAILQ_FOREACH(rdnsa, &rdns->list, next)
   1021 			packlen += sizeof(rdnsa->addr);
   1022 	}
   1023 	TAILQ_FOREACH(dnsl, &rainfo->dnssl, next) {
   1024 		packlen += sizeof(struct nd_opt_dnssl);
   1025 		len = 0;
   1026 		TAILQ_FOREACH(dnsd, &dnsl->list, next)
   1027 			len += dnsd->len;
   1028 		len += len % 8 ? 8 - len % 8 : 0;
   1029 		packlen += len;
   1030 	}
   1031 
   1032 	/* allocate memory for the packet */
   1033 	if ((buf = realloc(rainfo->ra_data, packlen)) == NULL) {
   1034 		syslog(LOG_ERR,
   1035 		       "<%s> can't get enough memory for an RA packet %m",
   1036 		       __func__);
   1037 		exit(1);
   1038 	}
   1039 	rainfo->ra_data = buf;
   1040 	/* XXX: what if packlen > 576? */
   1041 	rainfo->ra_datalen = packlen;
   1042 #define CHECKLEN(size) \
   1043 	do { \
   1044 		if (buf + size > rainfo->ra_data + packlen) { \
   1045 			syslog(LOG_ERR, \
   1046 			    "<%s, %d> RA packet does not fit in %zu",\
   1047 			    __func__, __LINE__, packlen); \
   1048 			exit(1); \
   1049 		} \
   1050 	} while (/*CONSTCOND*/0)
   1051 	/*
   1052 	 * construct the packet
   1053 	 */
   1054 	CHECKLEN(sizeof(*ra));
   1055 	ra = (struct nd_router_advert *)buf;
   1056 	ra->nd_ra_type = ND_ROUTER_ADVERT;
   1057 	ra->nd_ra_code = 0;
   1058 	ra->nd_ra_cksum = 0;
   1059 	ra->nd_ra_curhoplimit = (uint8_t)(0xff & rainfo->hoplimit);
   1060 	ra->nd_ra_flags_reserved = 0; /* just in case */
   1061 	/*
   1062 	 * XXX: the router preference field, which is a 2-bit field, should be
   1063 	 * initialized before other fields.
   1064 	 */
   1065 	ra->nd_ra_flags_reserved = 0xff & rainfo->rtpref;
   1066 	ra->nd_ra_flags_reserved |=
   1067 		rainfo->managedflg ? ND_RA_FLAG_MANAGED : 0;
   1068 	ra->nd_ra_flags_reserved |=
   1069 		rainfo->otherflg ? ND_RA_FLAG_OTHER : 0;
   1070 	ra->nd_ra_router_lifetime = htons(rainfo->lifetime);
   1071 	ra->nd_ra_reachable = htonl(rainfo->reachabletime);
   1072 	ra->nd_ra_retransmit = htonl(rainfo->retranstimer);
   1073 	buf += sizeof(*ra);
   1074 
   1075 	if (rainfo->advlinkopt) {
   1076 		CHECKLEN(sizeof(struct nd_opt_hdr));
   1077 		lladdropt_fill(rainfo->sdl, (struct nd_opt_hdr *)buf);
   1078 		buf += lladdroptlen;
   1079 	}
   1080 
   1081 	if (rainfo->linkmtu) {
   1082 		CHECKLEN(sizeof(*ndopt_mtu));
   1083 		ndopt_mtu = (struct nd_opt_mtu *)buf;
   1084 		ndopt_mtu->nd_opt_mtu_type = ND_OPT_MTU;
   1085 		ndopt_mtu->nd_opt_mtu_len = 1;
   1086 		ndopt_mtu->nd_opt_mtu_reserved = 0;
   1087 		ndopt_mtu->nd_opt_mtu_mtu = htonl(rainfo->linkmtu);
   1088 		buf += sizeof(struct nd_opt_mtu);
   1089 	}
   1090 
   1091 	TAILQ_FOREACH(pfx, &rainfo->prefix, next) {
   1092 		uint32_t vltime, pltime;
   1093 		struct timeval now;
   1094 
   1095 		CHECKLEN(sizeof(*ndopt_pi));
   1096 		ndopt_pi = (struct nd_opt_prefix_info *)buf;
   1097 		ndopt_pi->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION;
   1098 		ndopt_pi->nd_opt_pi_len = 4;
   1099 		ndopt_pi->nd_opt_pi_prefix_len = pfx->prefixlen;
   1100 		ndopt_pi->nd_opt_pi_flags_reserved = 0;
   1101 		if (pfx->onlinkflg)
   1102 			ndopt_pi->nd_opt_pi_flags_reserved |=
   1103 				ND_OPT_PI_FLAG_ONLINK;
   1104 		if (pfx->autoconfflg)
   1105 			ndopt_pi->nd_opt_pi_flags_reserved |=
   1106 				ND_OPT_PI_FLAG_AUTO;
   1107 		if (pfx->timer)
   1108 			vltime = 0;
   1109 		else {
   1110 			if (pfx->vltimeexpire || pfx->pltimeexpire)
   1111 				gettimeofday(&now, NULL);
   1112 			if (pfx->vltimeexpire == 0)
   1113 				vltime = pfx->validlifetime;
   1114 			else
   1115 				vltime = (pfx->vltimeexpire > now.tv_sec) ?
   1116 				    pfx->vltimeexpire - now.tv_sec : 0;
   1117 		}
   1118 		if (pfx->timer)
   1119 			pltime = 0;
   1120 		else {
   1121 			if (pfx->pltimeexpire == 0)
   1122 				pltime = pfx->preflifetime;
   1123 			else
   1124 				pltime = (pfx->pltimeexpire > now.tv_sec) ?
   1125 				    pfx->pltimeexpire - now.tv_sec : 0;
   1126 		}
   1127 		if (vltime < pltime) {
   1128 			/*
   1129 			 * this can happen if vltime is decrement but pltime
   1130 			 * is not.
   1131 			 */
   1132 			pltime = vltime;
   1133 		}
   1134 		ndopt_pi->nd_opt_pi_valid_time = htonl(vltime);
   1135 		ndopt_pi->nd_opt_pi_preferred_time = htonl(pltime);
   1136 		ndopt_pi->nd_opt_pi_reserved2 = 0;
   1137 		ndopt_pi->nd_opt_pi_prefix = pfx->prefix;
   1138 
   1139 		buf += sizeof(struct nd_opt_prefix_info);
   1140 	}
   1141 
   1142 	TAILQ_FOREACH(rti, &rainfo->route, next) {
   1143 		uint8_t psize = (rti->prefixlen + 0x3f) >> 6;
   1144 
   1145 		CHECKLEN(sizeof(*ndopt_rti));
   1146 		ndopt_rti = (struct nd_opt_route_info *)buf;
   1147 		ndopt_rti->nd_opt_rti_type = ND_OPT_ROUTE_INFO;
   1148 		ndopt_rti->nd_opt_rti_len = 1 + psize;
   1149 		ndopt_rti->nd_opt_rti_prefixlen = rti->prefixlen;
   1150 		ndopt_rti->nd_opt_rti_flags = 0xff & rti->rtpref;
   1151 		ndopt_rti->nd_opt_rti_lifetime = htonl(rti->ltime);
   1152 		memcpy(ndopt_rti + 1, &rti->prefix, psize * 8);
   1153 		buf += sizeof(struct nd_opt_route_info) + psize * 8;
   1154 	}
   1155 
   1156 	TAILQ_FOREACH(rdns, &rainfo->rdnss, next) {
   1157 		CHECKLEN(sizeof(*ndopt_rdnss));
   1158 		ndopt_rdnss = (struct nd_opt_rdnss *)buf;
   1159 		ndopt_rdnss->nd_opt_rdnss_type = ND_OPT_RDNSS;
   1160 		ndopt_rdnss->nd_opt_rdnss_len = 1;
   1161 		ndopt_rdnss->nd_opt_rdnss_reserved = 0;
   1162 		ndopt_rdnss->nd_opt_rdnss_lifetime = htonl(rdns->lifetime);
   1163 		buf += sizeof(*ndopt_rdnss);
   1164 
   1165 		TAILQ_FOREACH(rdnsa, &rdns->list, next) {
   1166 			CHECKLEN(sizeof(rdnsa->addr));
   1167 			memcpy(buf, &rdnsa->addr, sizeof(rdnsa->addr));
   1168 			ndopt_rdnss->nd_opt_rdnss_len += 2;
   1169 			buf += sizeof(rdnsa->addr);
   1170 		}
   1171 	}
   1172 
   1173 	TAILQ_FOREACH(dnsl, &rainfo->dnssl, next) {
   1174 		CHECKLEN(sizeof(*ndopt_dnssl));
   1175 		ndopt_dnssl = (struct nd_opt_dnssl *)buf;
   1176 		ndopt_dnssl->nd_opt_dnssl_type = ND_OPT_DNSSL;
   1177 		ndopt_dnssl->nd_opt_dnssl_len = 0;
   1178 		ndopt_dnssl->nd_opt_dnssl_reserved = 0;
   1179 		ndopt_dnssl->nd_opt_dnssl_lifetime = htonl(dnsl->lifetime);
   1180 		buf += sizeof(*ndopt_dnssl);
   1181 
   1182 		TAILQ_FOREACH(dnsd, &dnsl->list, next) {
   1183 			CHECKLEN(dnsd->len);
   1184 			memcpy(buf, dnsd->domain, dnsd->len);
   1185 			buf += dnsd->len;
   1186 		}
   1187 		/* Ensure our length is padded correctly */
   1188 		len = buf - (char *)ndopt_dnssl;
   1189 		plen = len % 8 ? 8 - len % 8 : 0;
   1190 		CHECKLEN(plen);
   1191 		memset(buf, 0, plen);
   1192 		buf += plen;
   1193 		ndopt_dnssl->nd_opt_dnssl_len = (len + plen) / 8;
   1194 	}
   1195 	memset(buf, 0, packlen - (buf - rainfo->ra_data));
   1196 }
   1197 
   1198 static int
   1199 getinet6sysctl(int code)
   1200 {
   1201 	int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 };
   1202 	int value;
   1203 	size_t size;
   1204 
   1205 	mib[3] = code;
   1206 	size = sizeof(value);
   1207 	if (sysctl(mib, __arraycount(mib), &value, &size, NULL, 0)
   1208 	    < 0) {
   1209 		syslog(LOG_ERR, "<%s>: failed to get ip6 sysctl(%d): %m",
   1210 		       __func__, code);
   1211 		return -1;
   1212 	}
   1213 	else
   1214 		return value;
   1215 }
   1216