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