xref: /freebsd/usr.sbin/rtadvd/config.c (revision b52b9d56d4e96089873a75f9e29062eec19fabba)
1 /*	$FreeBSD$	*/
2 /*	$KAME: config.c,v 1.37 2001/05/25 07:34:00 itojun 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 #if defined(__FreeBSD__) && __FreeBSD__ >= 3
41 #include <net/if_var.h>
42 #endif /* __FreeBSD__ >= 3 */
43 #include <net/route.h>
44 #include <net/if_dl.h>
45 
46 #include <netinet/in.h>
47 #include <netinet/in_var.h>
48 #include <netinet/ip6.h>
49 #include <netinet6/ip6_var.h>
50 #include <netinet/icmp6.h>
51 #ifdef MIP6
52 #include <netinet6/mip6.h>
53 #endif
54 
55 #include <arpa/inet.h>
56 
57 #include <stdio.h>
58 #include <syslog.h>
59 #include <errno.h>
60 #include <string.h>
61 #include <stdlib.h>
62 #if defined(__NetBSD__) || defined(__OpenBSD__)
63 #include <search.h>
64 #endif
65 #include <unistd.h>
66 #include <ifaddrs.h>
67 
68 #include "rtadvd.h"
69 #include "advcap.h"
70 #include "timer.h"
71 #include "if.h"
72 #include "config.h"
73 
74 static time_t prefix_timo = (60 * 120);	/* 2 hours.
75 					 * XXX: should be configurable. */
76 extern struct rainfo *ralist;
77 
78 static struct rtadvd_timer *prefix_timeout __P((void *));
79 static void makeentry __P((char *, size_t, int, char *, int));
80 static void get_prefix __P((struct rainfo *));
81 static int getinet6sysctl __P((int));
82 
83 void
84 getconfig(intface)
85 	char *intface;
86 {
87 	int stat, pfxs, i;
88 	char tbuf[BUFSIZ];
89 	struct rainfo *tmp;
90 	long val;
91 	long long val64;
92 	char buf[BUFSIZ];
93 	char *bp = buf;
94 	char *addr;
95 	static int forwarding = -1;
96 
97 #define MUSTHAVE(var, cap)	\
98     do {								\
99 	int t;								\
100 	if ((t = agetnum(cap)) < 0) {					\
101 		fprintf(stderr, "rtadvd: need %s for interface %s\n",	\
102 			cap, intface);					\
103 		exit(1);						\
104 	}								\
105 	var = t;							\
106      } while (0)
107 #define MAYHAVE(var, cap, def)	\
108      do {								\
109 	if ((var = agetnum(cap)) < 0)					\
110 		var = def;						\
111      } while (0)
112 
113 	if ((stat = agetent(tbuf, intface)) <= 0) {
114 		memset(tbuf, 0, sizeof(tbuf));
115 		syslog(LOG_INFO,
116 		       "<%s> %s isn't defined in the configuration file"
117 		       " or the configuration file doesn't exist."
118 		       " Treat it as default",
119 		        __FUNCTION__, intface);
120 	}
121 
122 	tmp = (struct rainfo *)malloc(sizeof(*ralist));
123 	memset(tmp, 0, sizeof(*tmp));
124 	tmp->prefix.next = tmp->prefix.prev = &tmp->prefix;
125 	tmp->route.next = tmp->route.prev = &tmp->route;
126 
127 	/* check if we are allowed to forward packets (if not determined) */
128 	if (forwarding < 0) {
129 		if ((forwarding = getinet6sysctl(IPV6CTL_FORWARDING)) < 0)
130 			exit(1);
131 	}
132 
133 	/* get interface information */
134 	if (agetflag("nolladdr"))
135 		tmp->advlinkopt = 0;
136 	else
137 		tmp->advlinkopt = 1;
138 	if (tmp->advlinkopt) {
139 		if ((tmp->sdl = if_nametosdl(intface)) == NULL) {
140 			syslog(LOG_ERR,
141 			       "<%s> can't get information of %s",
142 			       __FUNCTION__, intface);
143 			exit(1);
144 		}
145 		tmp->ifindex = tmp->sdl->sdl_index;
146 	} else
147 		tmp->ifindex = if_nametoindex(intface);
148 	strncpy(tmp->ifname, intface, sizeof(tmp->ifname));
149 	if ((tmp->phymtu = if_getmtu(intface)) == 0) {
150 		tmp->phymtu = IPV6_MMTU;
151 		syslog(LOG_WARNING,
152 		       "<%s> can't get interface mtu of %s. Treat as %d",
153 		       __FUNCTION__, intface, IPV6_MMTU);
154 	}
155 
156 	/*
157 	 * set router configuration variables.
158 	 */
159 	MAYHAVE(val, "maxinterval", DEF_MAXRTRADVINTERVAL);
160 	if (val < MIN_MAXINTERVAL || val > MAX_MAXINTERVAL) {
161 		syslog(LOG_ERR,
162 		       "<%s> maxinterval must be between %e and %u",
163 		       __FUNCTION__, MIN_MAXINTERVAL, MAX_MAXINTERVAL);
164 		exit(1);
165 	}
166 	tmp->maxinterval = (u_int)val;
167 	MAYHAVE(val, "mininterval", tmp->maxinterval/3);
168 	if (val < MIN_MININTERVAL || val > (tmp->maxinterval * 3) / 4) {
169 		syslog(LOG_ERR,
170 		       "<%s> mininterval must be between %e and %d",
171 		       __FUNCTION__,
172 		       MIN_MININTERVAL,
173 		       (tmp->maxinterval * 3) / 4);
174 		exit(1);
175 	}
176 	tmp->mininterval = (u_int)val;
177 
178 	MAYHAVE(val, "chlim", DEF_ADVCURHOPLIMIT);
179 	tmp->hoplimit = val & 0xff;
180 
181 	MAYHAVE(val, "raflags", 0);
182 	tmp->managedflg = val & ND_RA_FLAG_MANAGED;
183 	tmp->otherflg = val & ND_RA_FLAG_OTHER;
184 #ifdef MIP6
185 	if (mobileip6)
186 		tmp->haflg = val & ND_RA_FLAG_HA;
187 #endif
188 #ifndef ND_RA_FLAG_RTPREF_MASK
189 #define ND_RA_FLAG_RTPREF_MASK	0x18 /* 00011000 */
190 #define ND_RA_FLAG_RTPREF_RSV	0x10 /* 00010000 */
191 #endif
192 	tmp->rtpref = val & ND_RA_FLAG_RTPREF_MASK;
193 	if (tmp->rtpref == ND_RA_FLAG_RTPREF_RSV) {
194 		syslog(LOG_ERR, "<%s> invalid router preference on %s",
195 		       __FUNCTION__, intface);
196 		exit(1);
197 	}
198 
199 	MAYHAVE(val, "rltime", tmp->maxinterval * 3);
200 	if (val && (val < tmp->maxinterval || val > MAXROUTERLIFETIME)) {
201 		syslog(LOG_ERR,
202 		       "<%s> router lifetime on %s must be 0 or"
203 		       " between %d and %d",
204 		       __FUNCTION__, intface,
205 		       tmp->maxinterval, MAXROUTERLIFETIME);
206 		exit(1);
207 	}
208 	/*
209 	 * Basically, hosts MUST NOT send Router Advertisement messages at any
210 	 * time (RFC 2461, Section 6.2.3). However, it would sometimes be
211 	 * useful to allow hosts to advertise some parameters such as prefix
212 	 * information and link MTU. Thus, we allow hosts to invoke rtadvd
213 	 * only when router lifetime (on every advertising interface) is
214 	 * explicitly set zero. (see also the above section)
215 	 */
216 	if (val && forwarding == 0) {
217 		syslog(LOG_WARNING,
218 		       "<%s> non zero router lifetime is specified for %s, "
219 		       "which must not be allowed for hosts.",
220 		       __FUNCTION__, intface);
221 		exit(1);
222 	}
223 	tmp->lifetime = val & 0xffff;
224 
225 	MAYHAVE(val, "rtime", DEF_ADVREACHABLETIME);
226 	if (val > MAXREACHABLETIME) {
227 		syslog(LOG_ERR,
228 		       "<%s> reachable time must be no greater than %d",
229 		       __FUNCTION__, MAXREACHABLETIME);
230 		exit(1);
231 	}
232 	tmp->reachabletime = (u_int32_t)val;
233 
234 	MAYHAVE(val64, "retrans", DEF_ADVRETRANSTIMER);
235 	if (val64 < 0 || val64 > 0xffffffff) {
236 		syslog(LOG_ERR,
237 		       "<%s> retrans time out of range", __FUNCTION__);
238 		exit(1);
239 	}
240 	tmp->retranstimer = (u_int32_t)val64;
241 
242 #ifndef MIP6
243 	if (agetstr("hapref", &bp) || agetstr("hatime", &bp)) {
244 		syslog(LOG_ERR,
245 		       "<%s> mobile-ip6 configuration not supported",
246 		       __FUNCTION__);
247 		exit(1);
248 	}
249 #else
250 	if (!mobileip6) {
251 		if (agetstr("hapref", &bp) || agetstr("hatime", &bp)) {
252 			syslog(LOG_ERR,
253 			       "<%s> mobile-ip6 configuration without "
254 			       "proper command line option",
255 			       __FUNCTION__);
256 			exit(1);
257 		}
258 	} else {
259 		tmp->hapref = 0;
260 		if ((val = agetnum("hapref")) >= 0)
261 			tmp->hapref = (int16_t)val;
262 		if (tmp->hapref != 0) {
263 			tmp->hatime = 0;
264 			MUSTHAVE(val, "hatime");
265 			tmp->hatime = (u_int16_t)val;
266 			if (tmp->hatime <= 0) {
267 				syslog(LOG_ERR,
268 				       "<%s> home agent lifetime must be greater than 0",
269 				       __FUNCTION__);
270 				exit(1);
271 			}
272 		}
273 	}
274 #endif
275 
276 	/* prefix information */
277 
278 	/*
279 	 * This is an implementation specific parameter to consinder
280 	 * link propagation delays and poorly synchronized clocks when
281 	 * checking consistency of advertised lifetimes.
282 	 */
283 	MAYHAVE(val, "clockskew", 0);
284 	tmp->clockskew = val;
285 
286 	if ((pfxs = agetnum("addrs")) < 0) {
287 		/* auto configure prefix information */
288 		if (agetstr("addr", &bp) || agetstr("addr1", &bp)) {
289 			syslog(LOG_ERR,
290 			       "<%s> conflicting prefix configuration for %s: "
291 			       "automatic and manual config at the same time",
292 			       __FUNCTION__, intface);
293 			exit(1);
294 		}
295 		get_prefix(tmp);
296 	}
297 	else {
298 		tmp->pfxs = pfxs;
299 		for (i = 0; i < pfxs; i++) {
300 			struct prefix *pfx;
301 			char entbuf[256];
302 			int added = (pfxs > 1) ? 1 : 0;
303 
304 			/* allocate memory to store prefix information */
305 			if ((pfx = malloc(sizeof(struct prefix))) == NULL) {
306 				syslog(LOG_ERR,
307 				       "<%s> can't allocate enough memory",
308 				       __FUNCTION__);
309 				exit(1);
310 			}
311 			memset(pfx, 0, sizeof(*pfx));
312 
313 			/* link into chain */
314 			insque(pfx, &tmp->prefix);
315 			pfx->rainfo = tmp;
316 
317 			pfx->origin = PREFIX_FROM_CONFIG;
318 
319 			makeentry(entbuf, sizeof(entbuf), i, "prefixlen",
320 			    added);
321 			MAYHAVE(val, entbuf, 64);
322 			if (val < 0 || val > 128) {
323 				syslog(LOG_ERR,
324 				       "<%s> prefixlen out of range",
325 				       __FUNCTION__);
326 				exit(1);
327 			}
328 			pfx->prefixlen = (int)val;
329 
330 			makeentry(entbuf, sizeof(entbuf), i, "pinfoflags",
331 			    added);
332 #ifdef MIP6
333 			if (mobileip6)
334 			{
335 				MAYHAVE(val, entbuf,
336 				    (ND_OPT_PI_FLAG_ONLINK|ND_OPT_PI_FLAG_AUTO|
337 					 ND_OPT_PI_FLAG_ROUTER));
338 			} else
339 #endif
340 			{
341 				MAYHAVE(val, entbuf,
342 				    (ND_OPT_PI_FLAG_ONLINK|ND_OPT_PI_FLAG_AUTO));
343 			}
344 			pfx->onlinkflg = val & ND_OPT_PI_FLAG_ONLINK;
345 			pfx->autoconfflg = val & ND_OPT_PI_FLAG_AUTO;
346 #ifdef MIP6
347 			pfx->routeraddr = val & ND_OPT_PI_FLAG_ROUTER;
348 #endif
349 
350 			makeentry(entbuf, sizeof(entbuf), i, "vltime", added);
351 			MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME);
352 			if (val64 < 0 || val64 > 0xffffffff) {
353 				syslog(LOG_ERR,
354 				       "<%s> vltime out of range",
355 				       __FUNCTION__);
356 				exit(1);
357 			}
358 			pfx->validlifetime = (u_int32_t)val64;
359 
360 			makeentry(entbuf, sizeof(entbuf), i, "vltimedecr",
361 			    added);
362 			if (agetflag(entbuf)) {
363 				struct timeval now;
364 				gettimeofday(&now, 0);
365 				pfx->vltimeexpire =
366 					now.tv_sec + pfx->validlifetime;
367 			}
368 
369 			makeentry(entbuf, sizeof(entbuf), i, "pltime", added);
370 			MAYHAVE(val64, entbuf, DEF_ADVPREFERREDLIFETIME);
371 			if (val64 < 0 || val64 > 0xffffffff) {
372 				syslog(LOG_ERR,
373 				       "<%s> pltime out of range",
374 				       __FUNCTION__);
375 				exit(1);
376 			}
377 			pfx->preflifetime = (u_int32_t)val64;
378 
379 			makeentry(entbuf, sizeof(entbuf), i, "pltimedecr",
380 			    added);
381 			if (agetflag(entbuf)) {
382 				struct timeval now;
383 				gettimeofday(&now, 0);
384 				pfx->pltimeexpire =
385 					now.tv_sec + pfx->preflifetime;
386 			}
387 
388 			makeentry(entbuf, sizeof(entbuf), i, "addr", added);
389 			addr = (char *)agetstr(entbuf, &bp);
390 			if (addr == NULL) {
391 				syslog(LOG_ERR,
392 				       "<%s> need %s as an prefix for "
393 				       "interface %s",
394 				       __FUNCTION__, entbuf, intface);
395 				exit(1);
396 			}
397 			if (inet_pton(AF_INET6, addr,
398 				      &pfx->prefix) != 1) {
399 				syslog(LOG_ERR,
400 				       "<%s> inet_pton failed for %s",
401 				       __FUNCTION__, addr);
402 				exit(1);
403 			}
404 			if (IN6_IS_ADDR_MULTICAST(&pfx->prefix)) {
405 				syslog(LOG_ERR,
406 				       "<%s> multicast prefix(%s) must "
407 				       "not be advertised (IF=%s)",
408 				       __FUNCTION__, addr, intface);
409 				exit(1);
410 			}
411 			if (IN6_IS_ADDR_LINKLOCAL(&pfx->prefix))
412 				syslog(LOG_NOTICE,
413 				       "<%s> link-local prefix(%s) will be"
414 				       " advertised on %s",
415 				       __FUNCTION__, addr, intface);
416 		}
417 	}
418 
419 	MAYHAVE(val, "mtu", 0);
420 	if (val < 0 || val > 0xffffffff) {
421 		syslog(LOG_ERR,
422 		       "<%s> mtu out of range", __FUNCTION__);
423 		exit(1);
424 	}
425 	tmp->linkmtu = (u_int32_t)val;
426 	if (tmp->linkmtu == 0) {
427 		char *mtustr;
428 
429 		if ((mtustr = (char *)agetstr("mtu", &bp)) &&
430 		    strcmp(mtustr, "auto") == 0)
431 			tmp->linkmtu = tmp->phymtu;
432 	}
433 	else if (tmp->linkmtu < IPV6_MMTU || tmp->linkmtu > tmp->phymtu) {
434 		syslog(LOG_ERR,
435 		       "<%s> advertised link mtu must be between"
436 		       " least MTU and physical link MTU",
437 		       __FUNCTION__);
438 		exit(1);
439 	}
440 
441 	/* route information */
442 
443 	MAYHAVE(val, "routes", 0);
444 	if (val < 0 || val > 0xffffffff) {
445 		syslog(LOG_ERR,
446 		       "<%s> number of route information improper", __FUNCTION__);
447 		exit(1);
448 	}
449 	tmp->routes = val;
450 	for (i = 0; i < tmp->routes; i++) {
451 		struct rtinfo *rti;
452 		char entbuf[256];
453 		int added = (tmp->routes > 1) ? 1 : 0;
454 
455 		/* allocate memory to store prefix information */
456 		if ((rti = malloc(sizeof(struct rtinfo))) == NULL) {
457 			syslog(LOG_ERR,
458 			       "<%s> can't allocate enough memory",
459 			       __FUNCTION__);
460 			exit(1);
461 		}
462 		memset(rti, 0, sizeof(*rti));
463 
464 		/* link into chain */
465 		insque(rti, &tmp->route);
466 
467 		makeentry(entbuf, sizeof(entbuf), i, "rtrplen", added);
468 		MAYHAVE(val, entbuf, 64);
469 		if (val < 0 || val > 128) {
470 			syslog(LOG_ERR,
471 			       "<%s> prefixlen out of range",
472 			       __FUNCTION__);
473 			exit(1);
474 		}
475 		rti->prefixlen = (int)val;
476 
477 		makeentry(entbuf, sizeof(entbuf), i, "rtrflags", added);
478 		MAYHAVE(val, entbuf, 0);
479 		rti->rtpref = val & ND_RA_FLAG_RTPREF_MASK;
480 		if (rti->rtpref == ND_RA_FLAG_RTPREF_RSV) {
481 			syslog(LOG_ERR, "<%s> invalid router preference",
482 			       __FUNCTION__);
483 			exit(1);
484 		}
485 
486 		makeentry(entbuf, sizeof(entbuf), i, "rtrltime", added);
487 		/*
488 		 * XXX: since default value of route lifetime is not defined in
489 		 * draft-draves-route-selection-01.txt, I took the default
490 		 * value of valid lifetime of prefix as its default.
491 		 * It need be much considered.
492 		 */
493 		MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME);
494 		if (val64 < 0 || val64 > 0xffffffff) {
495 			syslog(LOG_ERR,
496 			       "<%s> rtrltime out of range",
497 			       __FUNCTION__);
498 			exit(1);
499 		}
500 		rti->ltime = (u_int32_t)val64;
501 
502 		makeentry(entbuf, sizeof(entbuf), i, "rtrprefix", added);
503 		addr = (char *)agetstr(entbuf, &bp);
504 		if (addr == NULL) {
505 			syslog(LOG_ERR,
506 			       "<%s> need %s as an route for "
507 			       "interface %s",
508 			       __FUNCTION__, entbuf, intface);
509 			exit(1);
510 		}
511 		if (inet_pton(AF_INET6, addr, &rti->prefix) != 1) {
512 			syslog(LOG_ERR,
513 			       "<%s> inet_pton failed for %s",
514 			       __FUNCTION__, addr);
515 			exit(1);
516 		}
517 #if 0
518 		/*
519 		 * XXX: currently there's no restriction in route information
520 		 * prefix according to draft-draves-route-selection-01.txt,
521 		 * however I think the similar restriction be necessary.
522 		 */
523 		MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME);
524 		if (IN6_IS_ADDR_MULTICAST(&rti->prefix)) {
525 			syslog(LOG_ERR,
526 			       "<%s> multicast route (%s) must "
527 			       "not be advertised (IF=%s)",
528 			       __FUNCTION__, addr, intface);
529 			exit(1);
530 		}
531 		if (IN6_IS_ADDR_LINKLOCAL(&rti->prefix)) {
532 			syslog(LOG_NOTICE,
533 			       "<%s> link-local route (%s) must "
534 			       "not be advertised on %s",
535 			       __FUNCTION__, addr, intface);
536 			exit(1);
537 		}
538 #endif
539 	}
540 
541 	/* okey */
542 	tmp->next = ralist;
543 	ralist = tmp;
544 
545 	/* construct the sending packet */
546 	make_packet(tmp);
547 
548 	/* set timer */
549 	tmp->timer = rtadvd_add_timer(ra_timeout, ra_timer_update,
550 				      tmp, tmp);
551 	ra_timer_update((void *)tmp, &tmp->timer->tm);
552 	rtadvd_set_timer(&tmp->timer->tm, tmp->timer);
553 }
554 
555 static void
556 get_prefix(struct rainfo *rai)
557 {
558 	struct ifaddrs *ifap, *ifa;
559 	struct prefix *pp;
560 	struct in6_addr *a;
561 	u_char *p, *ep, *m, *lim;
562 	u_char ntopbuf[INET6_ADDRSTRLEN];
563 
564 	if (getifaddrs(&ifap) < 0) {
565 		syslog(LOG_ERR,
566 		       "<%s> can't get interface addresses",
567 		       __FUNCTION__);
568 		exit(1);
569 	}
570 	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
571 		int plen;
572 
573 		if (strcmp(ifa->ifa_name, rai->ifname) != 0)
574 			continue;
575 		if (ifa->ifa_addr->sa_family != AF_INET6)
576 			continue;
577 		a = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
578 		if (IN6_IS_ADDR_LINKLOCAL(a))
579 			continue;
580 
581 		/* get prefix length */
582 		m = (u_char *)&((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr;
583 		lim = (u_char *)(ifa->ifa_netmask) + ifa->ifa_netmask->sa_len;
584 		plen = prefixlen(m, lim);
585 		if (plen < 0 || plen > 128) {
586 			syslog(LOG_ERR, "<%s> failed to get prefixlen "
587 			       "or prefix is invalid",
588 			       __FUNCTION__);
589 			exit(1);
590 		}
591 		if (find_prefix(rai, a, plen)) {
592 			/* ignore a duplicated prefix. */
593 			continue;
594 		}
595 
596 		/* allocate memory to store prefix info. */
597 		if ((pp = malloc(sizeof(*pp))) == NULL) {
598 			syslog(LOG_ERR,
599 			       "<%s> can't get allocate buffer for prefix",
600 			       __FUNCTION__);
601 			exit(1);
602 		}
603 		memset(pp, 0, sizeof(*pp));
604 
605 		/* set prefix, sweep bits outside of prefixlen */
606 		pp->prefixlen = plen;
607 		memcpy(&pp->prefix, a, sizeof(*a));
608 		p = (u_char *)&pp->prefix;
609 		ep = (u_char *)(&pp->prefix + 1);
610 		while (m < lim)
611 			*p++ &= *m++;
612 		while (p < ep)
613 			*p++ = 0x00;
614 
615 	        if (!inet_ntop(AF_INET6, &pp->prefix, ntopbuf,
616 	            sizeof(ntopbuf))) {
617 			syslog(LOG_ERR, "<%s> inet_ntop failed", __FUNCTION__);
618 			exit(1);
619 		}
620 		syslog(LOG_DEBUG,
621 		       "<%s> add %s/%d to prefix list on %s",
622 		       __FUNCTION__, ntopbuf, pp->prefixlen, rai->ifname);
623 
624 		/* set other fields with protocol defaults */
625 		pp->validlifetime = DEF_ADVVALIDLIFETIME;
626 		pp->preflifetime = DEF_ADVPREFERREDLIFETIME;
627 		pp->onlinkflg = 1;
628 		pp->autoconfflg = 1;
629 		pp->origin = PREFIX_FROM_KERNEL;
630 
631 		/* link into chain */
632 		insque(pp, &rai->prefix);
633 
634 		/* counter increment */
635 		rai->pfxs++;
636 	}
637 
638 	freeifaddrs(ifap);
639 }
640 
641 static void
642 makeentry(buf, len, id, string, add)
643 	char *buf;
644 	size_t len;
645 	int id;
646 	char *string;
647 	int add;
648 {
649 	char *ep = buf + len;
650 
651 	strcpy(buf, string);
652 	if (add) {
653 		char *cp;
654 
655 		cp = (char *)index(buf, '\0');
656 		snprintf(cp, ep - cp, "%d", id);
657 	}
658 }
659 
660 /*
661  * Add a prefix to the list of specified interface and reconstruct
662  * the outgoing packet.
663  * The prefix must not be in the list.
664  * XXX: other parameter of the prefix(e.g. lifetime) shoule be
665  * able to be specified.
666  */
667 static void
668 add_prefix(struct rainfo *rai, struct in6_prefixreq *ipr)
669 {
670 	struct prefix *prefix;
671 	u_char ntopbuf[INET6_ADDRSTRLEN];
672 
673 	if ((prefix = malloc(sizeof(*prefix))) == NULL) {
674 		syslog(LOG_ERR, "<%s> memory allocation failed",
675 		       __FUNCTION__);
676 		return;		/* XXX: error or exit? */
677 	}
678 	memset(prefix, 0, sizeof(*prefix));
679 	prefix->prefix = ipr->ipr_prefix.sin6_addr;
680 	prefix->prefixlen = ipr->ipr_plen;
681 	prefix->validlifetime = ipr->ipr_vltime;
682 	prefix->preflifetime = ipr->ipr_pltime;
683 	prefix->onlinkflg = ipr->ipr_raf_onlink;
684 	prefix->autoconfflg = ipr->ipr_raf_auto;
685 	prefix->origin = PREFIX_FROM_DYNAMIC;
686 
687 	insque(prefix, &rai->prefix);
688 	prefix->rainfo = rai;
689 
690 	syslog(LOG_DEBUG, "<%s> new prefix %s/%d was added on %s",
691 	       __FUNCTION__, inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr,
692 				       ntopbuf, INET6_ADDRSTRLEN),
693 	       ipr->ipr_plen, rai->ifname);
694 
695 	/* free the previous packet */
696 	free(rai->ra_data);
697 	rai->ra_data = NULL;
698 
699 	/* reconstruct the packet */
700 	rai->pfxs++;
701 	make_packet(rai);
702 
703 	/*
704 	 * reset the timer so that the new prefix will be advertised quickly.
705 	 */
706 	rai->initcounter = 0;
707 	ra_timer_update((void *)rai, &rai->timer->tm);
708 	rtadvd_set_timer(&rai->timer->tm, rai->timer);
709 }
710 
711 /*
712  * Delete a prefix to the list of specified interface and reconstruct
713  * the outgoing packet.
714  * The prefix must be in the list.
715  */
716 void
717 delete_prefix(struct prefix *prefix)
718 {
719 	u_char ntopbuf[INET6_ADDRSTRLEN];
720 	struct rainfo *rai = prefix->rainfo;
721 
722 	remque(prefix);
723 	syslog(LOG_DEBUG, "<%s> prefix %s/%d was deleted on %s",
724 	       __FUNCTION__, inet_ntop(AF_INET6, &prefix->prefix,
725 				       ntopbuf, INET6_ADDRSTRLEN),
726 	       prefix->prefixlen, rai->ifname);
727 	if (prefix->timer)
728 		rtadvd_remove_timer(&prefix->timer);
729 	free(prefix);
730 	rai->pfxs--;
731 }
732 
733 void
734 invalidate_prefix(struct prefix *prefix)
735 {
736 	u_char ntopbuf[INET6_ADDRSTRLEN];
737 	struct timeval timo;
738 	struct rainfo *rai = prefix->rainfo;
739 
740 	if (prefix->timer) {	/* sanity check */
741 		syslog(LOG_ERR,
742 		    "<%s> assumption failure: timer already exists",
743 		    __FUNCTION__);
744 		exit(1);
745 	}
746 
747 	syslog(LOG_DEBUG, "<%s> prefix %s/%d was invalidated on %s, "
748 	    "will expire in %ld seconds", __FUNCTION__,
749 	    inet_ntop(AF_INET6, &prefix->prefix, ntopbuf, INET6_ADDRSTRLEN),
750 	    prefix->prefixlen, rai->ifname, (long)prefix_timo);
751 
752 	/* set the expiration timer */
753 	prefix->timer = rtadvd_add_timer(prefix_timeout, NULL, prefix, NULL);
754 	if (prefix->timer == NULL) {
755 		syslog(LOG_ERR, "<%s> failed to add a timer for a prefix. "
756 		    "remove the prefix", __FUNCTION__);
757 		delete_prefix(prefix);
758 	}
759 	timo.tv_sec = prefix_timo;
760 	timo.tv_usec = 0;
761 	rtadvd_set_timer(&timo, prefix->timer);
762 }
763 
764 static struct rtadvd_timer *
765 prefix_timeout(void *arg)
766 {
767 	struct prefix *prefix = (struct prefix *)arg;
768 
769 	delete_prefix(prefix);
770 
771 	return(NULL);
772 }
773 
774 void
775 update_prefix(struct prefix * prefix)
776 {
777 	u_char ntopbuf[INET6_ADDRSTRLEN];
778 	struct rainfo *rai = prefix->rainfo;
779 
780 	if (prefix->timer == NULL) { /* sanity check */
781 		syslog(LOG_ERR,
782 		    "<%s> assumption failure: timer does not exist",
783 		    __FUNCTION__);
784 		exit(1);
785 	}
786 
787 	syslog(LOG_DEBUG, "<%s> prefix %s/%d was re-enabled on %s",
788 	    __FUNCTION__, inet_ntop(AF_INET6, &prefix->prefix, ntopbuf,
789 	    INET6_ADDRSTRLEN), prefix->prefixlen, rai->ifname);
790 
791 	/* stop the expiration timer */
792 	rtadvd_remove_timer(&prefix->timer);
793 }
794 
795 /*
796  * Try to get an in6_prefixreq contents for a prefix which matches
797  * ipr->ipr_prefix and ipr->ipr_plen and belongs to
798  * the interface whose name is ipr->ipr_name[].
799  */
800 static int
801 init_prefix(struct in6_prefixreq *ipr)
802 {
803 #if 0
804 	int s;
805 
806 	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
807 		syslog(LOG_ERR, "<%s> socket: %s", __FUNCTION__,
808 		       strerror(errno));
809 		exit(1);
810 	}
811 
812 	if (ioctl(s, SIOCGIFPREFIX_IN6, (caddr_t)ipr) < 0) {
813 		syslog(LOG_INFO, "<%s> ioctl:SIOCGIFPREFIX %s", __FUNCTION__,
814 		       strerror(errno));
815 
816 		ipr->ipr_vltime = DEF_ADVVALIDLIFETIME;
817 		ipr->ipr_pltime = DEF_ADVPREFERREDLIFETIME;
818 		ipr->ipr_raf_onlink = 1;
819 		ipr->ipr_raf_auto = 1;
820 		/* omit other field initialization */
821 	}
822 	else if (ipr->ipr_origin < PR_ORIG_RR) {
823 		u_char ntopbuf[INET6_ADDRSTRLEN];
824 
825 		syslog(LOG_WARNING, "<%s> Added prefix(%s)'s origin %d is"
826 		       "lower than PR_ORIG_RR(router renumbering)."
827 		       "This should not happen if I am router", __FUNCTION__,
828 		       inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr, ntopbuf,
829 				 sizeof(ntopbuf)), ipr->ipr_origin);
830 		close(s);
831 		return 1;
832 	}
833 
834 	close(s);
835 	return 0;
836 #else
837 	ipr->ipr_vltime = DEF_ADVVALIDLIFETIME;
838 	ipr->ipr_pltime = DEF_ADVPREFERREDLIFETIME;
839 	ipr->ipr_raf_onlink = 1;
840 	ipr->ipr_raf_auto = 1;
841         return 0;
842 #endif
843 }
844 
845 void
846 make_prefix(struct rainfo *rai, int ifindex, struct in6_addr *addr, int plen)
847 {
848 	struct in6_prefixreq ipr;
849 
850 	memset(&ipr, 0, sizeof(ipr));
851 	if (if_indextoname(ifindex, ipr.ipr_name) == NULL) {
852 		syslog(LOG_ERR, "<%s> Prefix added interface No.%d doesn't"
853 		       "exist. This should not happen! %s", __FUNCTION__,
854 		       ifindex, strerror(errno));
855 		exit(1);
856 	}
857 	ipr.ipr_prefix.sin6_len = sizeof(ipr.ipr_prefix);
858 	ipr.ipr_prefix.sin6_family = AF_INET6;
859 	ipr.ipr_prefix.sin6_addr = *addr;
860 	ipr.ipr_plen = plen;
861 
862 	if (init_prefix(&ipr))
863 		return; /* init failed by some error */
864 	add_prefix(rai, &ipr);
865 }
866 
867 void
868 make_packet(struct rainfo *rainfo)
869 {
870 	size_t packlen, lladdroptlen = 0;
871 	char *buf;
872 	struct nd_router_advert *ra;
873 	struct nd_opt_prefix_info *ndopt_pi;
874 	struct nd_opt_mtu *ndopt_mtu;
875 #ifdef MIP6
876 	struct nd_opt_advinterval *ndopt_advint;
877 	struct nd_opt_homeagent_info *ndopt_hai;
878 #endif
879 	struct nd_opt_route_info *ndopt_rti;
880 	struct prefix *pfx;
881 	struct rtinfo *rti;
882 
883 	/* calculate total length */
884 	packlen = sizeof(struct nd_router_advert);
885 	if (rainfo->advlinkopt) {
886 		if ((lladdroptlen = lladdropt_length(rainfo->sdl)) == 0) {
887 			syslog(LOG_INFO,
888 			       "<%s> link-layer address option has"
889 			       " null length on %s."
890 			       " Treat as not included.",
891 			       __FUNCTION__, rainfo->ifname);
892 			rainfo->advlinkopt = 0;
893 		}
894 		packlen += lladdroptlen;
895 	}
896 	if (rainfo->pfxs)
897 		packlen += sizeof(struct nd_opt_prefix_info) * rainfo->pfxs;
898 	if (rainfo->linkmtu)
899 		packlen += sizeof(struct nd_opt_mtu);
900 #ifdef MIP6
901 	if (mobileip6 && rainfo->maxinterval)
902 		packlen += sizeof(struct nd_opt_advinterval);
903 	if (mobileip6 && rainfo->hatime)
904 		packlen += sizeof(struct nd_opt_homeagent_info);
905 #endif
906 #ifdef ND_OPT_ROUTE_INFO
907 	for (rti = rainfo->route.next; rti != &rainfo->route; rti = rti->next)
908 		packlen += sizeof(struct nd_opt_route_info) +
909 			   ((rti->prefixlen + 0x3f) >> 6) * 8;
910 #endif
911 
912 	/* allocate memory for the packet */
913 	if ((buf = malloc(packlen)) == NULL) {
914 		syslog(LOG_ERR,
915 		       "<%s> can't get enough memory for an RA packet",
916 		       __FUNCTION__);
917 		exit(1);
918 	}
919 	if (rainfo->ra_data) {
920 		/* free the previous packet */
921 		free(rainfo->ra_data);
922 		rainfo->ra_data = NULL;
923 	}
924 	rainfo->ra_data = buf;
925 	/* XXX: what if packlen > 576? */
926 	rainfo->ra_datalen = packlen;
927 
928 	/*
929 	 * construct the packet
930 	 */
931 	ra = (struct nd_router_advert *)buf;
932 	ra->nd_ra_type = ND_ROUTER_ADVERT;
933 	ra->nd_ra_code = 0;
934 	ra->nd_ra_cksum = 0;
935 	ra->nd_ra_curhoplimit = (u_int8_t)(0xff & rainfo->hoplimit);
936 	ra->nd_ra_flags_reserved = 0; /* just in case */
937 	/*
938 	 * XXX: the router preference field, which is a 2-bit field, should be
939 	 * initialized before other fields.
940 	 */
941 	ra->nd_ra_flags_reserved = 0xff & rainfo->rtpref;
942 	ra->nd_ra_flags_reserved |=
943 		rainfo->managedflg ? ND_RA_FLAG_MANAGED : 0;
944 	ra->nd_ra_flags_reserved |=
945 		rainfo->otherflg ? ND_RA_FLAG_OTHER : 0;
946 #ifdef MIP6
947 	ra->nd_ra_flags_reserved |=
948 		rainfo->haflg ? ND_RA_FLAG_HA : 0;
949 #endif
950 	ra->nd_ra_router_lifetime = htons(rainfo->lifetime);
951 	ra->nd_ra_reachable = htonl(rainfo->reachabletime);
952 	ra->nd_ra_retransmit = htonl(rainfo->retranstimer);
953 	buf += sizeof(*ra);
954 
955 	if (rainfo->advlinkopt) {
956 		lladdropt_fill(rainfo->sdl, (struct nd_opt_hdr *)buf);
957 		buf += lladdroptlen;
958 	}
959 
960 	if (rainfo->linkmtu) {
961 		ndopt_mtu = (struct nd_opt_mtu *)buf;
962 		ndopt_mtu->nd_opt_mtu_type = ND_OPT_MTU;
963 		ndopt_mtu->nd_opt_mtu_len = 1;
964 		ndopt_mtu->nd_opt_mtu_reserved = 0;
965 		ndopt_mtu->nd_opt_mtu_mtu = htonl(rainfo->linkmtu);
966 		buf += sizeof(struct nd_opt_mtu);
967 	}
968 
969 #ifdef MIP6
970 	if (mobileip6 && rainfo->maxinterval) {
971 		ndopt_advint = (struct nd_opt_advinterval *)buf;
972 		ndopt_advint->nd_opt_adv_type = ND_OPT_ADVINTERVAL;
973 		ndopt_advint->nd_opt_adv_len = 1;
974 		ndopt_advint->nd_opt_adv_reserved = 0;
975 		ndopt_advint->nd_opt_adv_interval = htonl(rainfo->maxinterval *
976 							  1000);
977 		buf += sizeof(struct nd_opt_advinterval);
978 	}
979 #endif
980 
981 #ifdef MIP6
982 	if (rainfo->hatime) {
983 		ndopt_hai = (struct nd_opt_homeagent_info *)buf;
984 		ndopt_hai->nd_opt_hai_type = ND_OPT_HOMEAGENT_INFO;
985 		ndopt_hai->nd_opt_hai_len = 1;
986 		ndopt_hai->nd_opt_hai_reserved = 0;
987 		ndopt_hai->nd_opt_hai_preference = htons(rainfo->hapref);
988 		ndopt_hai->nd_opt_hai_lifetime = htons(rainfo->hatime);
989 		buf += sizeof(struct nd_opt_homeagent_info);
990 	}
991 #endif
992 
993 	for (pfx = rainfo->prefix.next;
994 	     pfx != &rainfo->prefix; pfx = pfx->next) {
995 		u_int32_t vltime, pltime;
996 		struct timeval now;
997 
998 		ndopt_pi = (struct nd_opt_prefix_info *)buf;
999 		ndopt_pi->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION;
1000 		ndopt_pi->nd_opt_pi_len = 4;
1001 		ndopt_pi->nd_opt_pi_prefix_len = pfx->prefixlen;
1002 		ndopt_pi->nd_opt_pi_flags_reserved = 0;
1003 		if (pfx->onlinkflg)
1004 			ndopt_pi->nd_opt_pi_flags_reserved |=
1005 				ND_OPT_PI_FLAG_ONLINK;
1006 		if (pfx->autoconfflg)
1007 			ndopt_pi->nd_opt_pi_flags_reserved |=
1008 				ND_OPT_PI_FLAG_AUTO;
1009 #ifdef MIP6
1010 		if (pfx->routeraddr)
1011 			ndopt_pi->nd_opt_pi_flags_reserved |=
1012 				ND_OPT_PI_FLAG_ROUTER;
1013 #endif
1014 		if (pfx->timer)
1015 			vltime = 0;
1016 		else {
1017 			if (pfx->vltimeexpire || pfx->pltimeexpire)
1018 				gettimeofday(&now, NULL);
1019 			if (pfx->vltimeexpire == 0)
1020 				vltime = pfx->validlifetime;
1021 			else
1022 				vltime = (pfx->vltimeexpire > now.tv_sec) ?
1023 				    pfx->vltimeexpire - now.tv_sec : 0;
1024 		}
1025 		if (pfx->timer)
1026 			pltime = 0;
1027 		else {
1028 			if (pfx->pltimeexpire == 0)
1029 				pltime = pfx->preflifetime;
1030 			else
1031 				pltime = (pfx->pltimeexpire > now.tv_sec) ?
1032 				    pfx->pltimeexpire - now.tv_sec : 0;
1033 		}
1034 		if (vltime < pltime) {
1035 			/*
1036 			 * this can happen if vltime is decrement but pltime
1037 			 * is not.
1038 			 */
1039 			pltime = vltime;
1040 		}
1041 		ndopt_pi->nd_opt_pi_valid_time = htonl(vltime);
1042 		ndopt_pi->nd_opt_pi_preferred_time = htonl(pltime);
1043 		ndopt_pi->nd_opt_pi_reserved2 = 0;
1044 		ndopt_pi->nd_opt_pi_prefix = pfx->prefix;
1045 
1046 		buf += sizeof(struct nd_opt_prefix_info);
1047 	}
1048 
1049 #ifdef ND_OPT_ROUTE_INFO
1050 	for (rti = rainfo->route.next; rti != &rainfo->route; rti = rti->next) {
1051 		u_int8_t psize = (rti->prefixlen + 0x3f) >> 6;
1052 
1053 		ndopt_rti = (struct nd_opt_route_info *)buf;
1054 		ndopt_rti->nd_opt_rti_type = ND_OPT_ROUTE_INFO;
1055 		ndopt_rti->nd_opt_rti_len = 1 + psize;
1056 		ndopt_rti->nd_opt_rti_prefixlen = rti->prefixlen;
1057 		ndopt_rti->nd_opt_rti_flags = 0xff & rti->rtpref;
1058 		ndopt_rti->nd_opt_rti_lifetime = rti->ltime;
1059 		memcpy(ndopt_rti + 1, &rti->prefix, psize * 8);
1060 		buf += sizeof(struct nd_opt_route_info) + psize * 8;
1061 	}
1062 #endif
1063 
1064 	return;
1065 }
1066 
1067 static int
1068 getinet6sysctl(int code)
1069 {
1070 	int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 };
1071 	int value;
1072 	size_t size;
1073 
1074 	mib[3] = code;
1075 	size = sizeof(value);
1076 	if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), &value, &size, NULL, 0)
1077 	    < 0) {
1078 		syslog(LOG_ERR, "<%s>: failed to get ip6 sysctl(%d): %s",
1079 		       __FUNCTION__, code,
1080 		       strerror(errno));
1081 		return(-1);
1082 	}
1083 	else
1084 		return(value);
1085 }
1086