xref: /netbsd/usr.sbin/rtadvd/config.c (revision c4a72b64)
1 /*	$NetBSD: config.c,v 1.19 2002/07/10 21:13:35 itojun Exp $	*/
2 /*	$KAME: config.c,v 1.62 2002/05/29 10:13:10 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 #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 
49 #include <arpa/inet.h>
50 
51 #include <stdio.h>
52 #include <syslog.h>
53 #include <errno.h>
54 #include <string.h>
55 #include <stdlib.h>
56 #include <search.h>
57 #include <unistd.h>
58 #include <ifaddrs.h>
59 
60 #include "rtadvd.h"
61 #include "advcap.h"
62 #include "timer.h"
63 #include "if.h"
64 #include "config.h"
65 
66 static void makeentry __P((char *, size_t, int, char *, int));
67 static void get_prefix __P((struct rainfo *));
68 static int getinet6sysctl __P((int));
69 
70 extern struct rainfo *ralist;
71 
72 void
73 getconfig(intface)
74 	char *intface;
75 {
76 	int stat, pfxs, i;
77 	char tbuf[BUFSIZ];
78 	struct rainfo *tmp;
79 	long val;
80 	int64_t val64;
81 	char buf[BUFSIZ];
82 	char *bp = buf;
83 	char *addr;
84 	static int forwarding = -1;
85 
86 #define MUSTHAVE(var, cap)	\
87     do {								\
88 	int64_t t;							\
89 	if ((t = agetnum(cap)) < 0) {					\
90 		fprintf(stderr, "rtadvd: need %s for interface %s\n",	\
91 			cap, intface);					\
92 		exit(1);						\
93 	}								\
94 	var = t;							\
95      } while (0)
96 #define MAYHAVE(var, cap, def)	\
97      do {								\
98 	if ((var = agetnum(cap)) < 0)					\
99 		var = def;						\
100      } while (0)
101 
102 	if ((stat = agetent(tbuf, intface)) <= 0) {
103 		memset(tbuf, 0, sizeof(tbuf));
104 		syslog(LOG_INFO,
105 		       "<%s> %s isn't defined in the configuration file"
106 		       " or the configuration file doesn't exist."
107 		       " Treat it as default",
108 		        __func__, intface);
109 	}
110 
111 	tmp = (struct rainfo *)malloc(sizeof(*ralist));
112 	if (tmp == NULL) {
113 		syslog(LOG_INFO, "<%s> %s: can't allocate enough memory",
114 		    __func__, intface);
115 		exit(1);
116 	}
117 	memset(tmp, 0, sizeof(*tmp));
118 	tmp->prefix.next = tmp->prefix.prev = &tmp->prefix;
119 
120 	/* check if we are allowed to forward packets (if not determined) */
121 	if (forwarding < 0) {
122 		if ((forwarding = getinet6sysctl(IPV6CTL_FORWARDING)) < 0)
123 			exit(1);
124 	}
125 
126 	/* get interface information */
127 	if (agetflag("nolladdr"))
128 		tmp->advlinkopt = 0;
129 	else
130 		tmp->advlinkopt = 1;
131 	if (tmp->advlinkopt) {
132 		if ((tmp->sdl = if_nametosdl(intface)) == NULL) {
133 			syslog(LOG_ERR,
134 			       "<%s> can't get information of %s",
135 			       __func__, intface);
136 			exit(1);
137 		}
138 		tmp->ifindex = tmp->sdl->sdl_index;
139 	} else
140 		tmp->ifindex = if_nametoindex(intface);
141 	strncpy(tmp->ifname, intface, sizeof(tmp->ifname));
142 	if ((tmp->phymtu = if_getmtu(intface)) == 0) {
143 		tmp->phymtu = IPV6_MMTU;
144 		syslog(LOG_WARNING,
145 		       "<%s> can't get interface mtu of %s. Treat as %d",
146 		       __func__, intface, IPV6_MMTU);
147 	}
148 
149 	/*
150 	 * set router configuration variables.
151 	 */
152 	MAYHAVE(val, "maxinterval", DEF_MAXRTRADVINTERVAL);
153 	if (val < MIN_MAXINTERVAL || val > MAX_MAXINTERVAL) {
154 		syslog(LOG_ERR,
155 		       "<%s> maxinterval (%ld) on %s is invalid "
156 		       "(must be between %e and %u)", __func__, val,
157 		       intface, MIN_MAXINTERVAL, MAX_MAXINTERVAL);
158 		exit(1);
159 	}
160 	tmp->maxinterval = (u_int)val;
161 	MAYHAVE(val, "mininterval", tmp->maxinterval/3);
162 	if (val < MIN_MININTERVAL || val > (tmp->maxinterval * 3) / 4) {
163 		syslog(LOG_ERR,
164 		       "<%s> mininterval (%ld) on %s is invalid "
165 		       "(must be between %e and %d)",
166 		       __func__, val, intface, MIN_MININTERVAL,
167 		       (tmp->maxinterval * 3) / 4);
168 		exit(1);
169 	}
170 	tmp->mininterval = (u_int)val;
171 
172 	MAYHAVE(val, "chlim", DEF_ADVCURHOPLIMIT);
173 	tmp->hoplimit = val & 0xff;
174 
175 	MAYHAVE(val, "raflags", 0);
176 	tmp->managedflg = val & ND_RA_FLAG_MANAGED;
177 	tmp->otherflg = val & ND_RA_FLAG_OTHER;
178 #ifndef ND_RA_FLAG_RTPREF_MASK
179 #define ND_RA_FLAG_RTPREF_MASK	0x18 /* 00011000 */
180 #define ND_RA_FLAG_RTPREF_RSV	0x10 /* 00010000 */
181 #endif
182 	tmp->rtpref = val & ND_RA_FLAG_RTPREF_MASK;
183 	if (tmp->rtpref == ND_RA_FLAG_RTPREF_RSV) {
184 		syslog(LOG_ERR, "<%s> invalid router preference (%02x) on %s",
185 		       __func__, tmp->rtpref, intface);
186 		exit(1);
187 	}
188 
189 	MAYHAVE(val, "rltime", tmp->maxinterval * 3);
190 	if (val && (val < tmp->maxinterval || val > MAXROUTERLIFETIME)) {
191 		syslog(LOG_ERR,
192 		       "<%s> router lifetime (%ld) on %s is invalid "
193 		       "(must be 0 or between %d and %d)",
194 		       __func__, val, intface,
195 		       tmp->maxinterval, MAXROUTERLIFETIME);
196 		exit(1);
197 	}
198 	/*
199 	 * Basically, hosts MUST NOT send Router Advertisement messages at any
200 	 * time (RFC 2461, Section 6.2.3). However, it would sometimes be
201 	 * useful to allow hosts to advertise some parameters such as prefix
202 	 * information and link MTU. Thus, we allow hosts to invoke rtadvd
203 	 * only when router lifetime (on every advertising interface) is
204 	 * explicitly set zero. (see also the above section)
205 	 */
206 	if (val && forwarding == 0) {
207 		syslog(LOG_ERR,
208 		       "<%s> non zero router lifetime is specified for %s, "
209 		       "which must not be allowed for hosts.  you must "
210 		       "change router lifetime or enable IPv6 forwarding.",
211 		       __func__, intface);
212 		exit(1);
213 	}
214 	tmp->lifetime = val & 0xffff;
215 
216 	MAYHAVE(val, "rtime", DEF_ADVREACHABLETIME);
217 	if (val < 0 || val > MAXREACHABLETIME) {
218 		syslog(LOG_ERR,
219 		       "<%s> reachable time (%ld) on %s is invalid "
220 		       "(must be no greater than %d)",
221 		       __func__, val, intface, MAXREACHABLETIME);
222 		exit(1);
223 	}
224 	tmp->reachabletime = (u_int32_t)val;
225 
226 	MAYHAVE(val64, "retrans", DEF_ADVRETRANSTIMER);
227 	if (val64 < 0 || val64 > 0xffffffff) {
228 		syslog(LOG_ERR, "<%s> retrans time (%lld) on %s out of range",
229 		       __func__, (long long)val64, intface);
230 		exit(1);
231 	}
232 	tmp->retranstimer = (u_int32_t)val64;
233 
234 	if (agetnum("hapref") != -1 || agetnum("hatime") != -1) {
235 		syslog(LOG_ERR,
236 		       "<%s> mobile-ip6 configuration not supported",
237 		       __func__);
238 		exit(1);
239 	}
240 	/* prefix information */
241 
242 	/*
243 	 * This is an implementation specific parameter to consinder
244 	 * link propagation delays and poorly synchronized clocks when
245 	 * checking consistency of advertised lifetimes.
246 	 */
247 	MAYHAVE(val, "clockskew", 0);
248 	tmp->clockskew = val;
249 
250 	if ((pfxs = agetnum("addrs")) <= 0) {
251 		/* auto configure prefix information */
252 		if (agetstr("addr", &bp) || agetstr("addr1", &bp)) {
253 			syslog(LOG_ERR,
254 			       "<%s> conflicting prefix configuration for %s: "
255 			       "automatic and manual config at the same time",
256 			       __func__, intface);
257 			exit(1);
258 		}
259 		get_prefix(tmp);
260 	} else {
261 		tmp->pfxs = pfxs;
262 		for (i = 0; i < pfxs; i++) {
263 			struct prefix *pfx;
264 			char entbuf[256];
265 			int added = (pfxs > 1) ? 1 : 0;
266 
267 			/* allocate memory to store prefix information */
268 			if ((pfx = malloc(sizeof(struct prefix))) == NULL) {
269 				syslog(LOG_ERR,
270 				       "<%s> can't allocate enough memory",
271 				       __func__);
272 				exit(1);
273 			}
274 			memset(pfx, 0, sizeof(*pfx));
275 
276 			/* link into chain */
277 			insque(pfx, &tmp->prefix);
278 
279 			pfx->origin = PREFIX_FROM_CONFIG;
280 
281 
282 			makeentry(entbuf, sizeof(entbuf), i, "addr", added);
283 			addr = (char *)agetstr(entbuf, &bp);
284 			if (addr == NULL) {
285 				syslog(LOG_ERR,
286 				       "<%s> need %s as a prefix for "
287 				       "interface %s",
288 				       __func__, entbuf, intface);
289 				exit(1);
290 			}
291 			if (inet_pton(AF_INET6, addr,
292 				      &pfx->prefix) != 1) {
293 				syslog(LOG_ERR,
294 				       "<%s> inet_pton failed for %s",
295 				       __func__, addr);
296 				exit(1);
297 			}
298 			if (IN6_IS_ADDR_MULTICAST(&pfx->prefix)) {
299 				syslog(LOG_ERR,
300 				       "<%s> multicast prefix (%s) must "
301 				       "not be advertised on %s",
302 				       __func__, addr, intface);
303 				exit(1);
304 			}
305 			if (IN6_IS_ADDR_LINKLOCAL(&pfx->prefix))
306 				syslog(LOG_NOTICE,
307 				       "<%s> link-local prefix (%s) will be"
308 				       " advertised on %s",
309 				       __func__, addr, intface);
310 
311 			makeentry(entbuf, sizeof(entbuf), i, "prefixlen",
312 			    added);
313 			MAYHAVE(val, entbuf, 64);
314 			if (val < 0 || val > 128) {
315 				syslog(LOG_ERR, "<%s> prefixlen (%ld) for %s "
316 				       "on %s out of range",
317 				       __func__, val, addr, intface);
318 				exit(1);
319 			}
320 			pfx->prefixlen = (int)val;
321 
322 			makeentry(entbuf, sizeof(entbuf), i, "pinfoflags",
323 			    added);
324 			MAYHAVE(val, entbuf,
325 				(ND_OPT_PI_FLAG_ONLINK|ND_OPT_PI_FLAG_AUTO));
326 			pfx->onlinkflg = val & ND_OPT_PI_FLAG_ONLINK;
327 			pfx->autoconfflg = val & ND_OPT_PI_FLAG_AUTO;
328 
329 			makeentry(entbuf, sizeof(entbuf), i, "vltime", added);
330 			MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME);
331 			if (val64 < 0 || val64 > 0xffffffff) {
332 				syslog(LOG_ERR, "<%s> vltime (%lld) for "
333 				    "%s/%d on %s is out of range",
334 				    __func__, (long long)val64,
335 				    addr, pfx->prefixlen, intface);
336 				exit(1);
337 			}
338 			pfx->validlifetime = (u_int32_t)val64;
339 
340 			makeentry(entbuf, sizeof(entbuf), i, "vltimedecr", added);
341 			if (agetflag(entbuf)) {
342 				struct timeval now;
343 				gettimeofday(&now, 0);
344 				pfx->vltimeexpire =
345 					now.tv_sec + pfx->validlifetime;
346 			}
347 
348 			makeentry(entbuf, sizeof(entbuf), i, "pltime", added);
349 			MAYHAVE(val64, entbuf, DEF_ADVPREFERREDLIFETIME);
350 			if (val64 < 0 || val64 > 0xffffffff) {
351 				syslog(LOG_ERR,
352 				    "<%s> pltime (%lld) for %s/%d on %s "
353 				    "is out of range",
354 				    __func__, (long long)val64,
355 				    addr, pfx->prefixlen, intface);
356 				exit(1);
357 			}
358 			pfx->preflifetime = (u_int32_t)val64;
359 
360 			makeentry(entbuf, sizeof(entbuf), i, "pltimedecr", added);
361 			if (agetflag(entbuf)) {
362 				struct timeval now;
363 				gettimeofday(&now, 0);
364 				pfx->pltimeexpire =
365 					now.tv_sec + pfx->preflifetime;
366 			}
367 		}
368 	}
369 
370 	MAYHAVE(val, "mtu", 0);
371 	if (val < 0 || val > 0xffffffff) {
372 		syslog(LOG_ERR,
373 		       "<%s> mtu (%ld) on %s out of range",
374 		       __func__, val, intface);
375 		exit(1);
376 	}
377 	tmp->linkmtu = (u_int32_t)val;
378 	if (tmp->linkmtu == 0) {
379 		char *mtustr;
380 
381 		if ((mtustr = (char *)agetstr("mtu", &bp)) &&
382 		    strcmp(mtustr, "auto") == 0)
383 			tmp->linkmtu = tmp->phymtu;
384 	}
385 	else if (tmp->linkmtu < IPV6_MMTU || tmp->linkmtu > tmp->phymtu) {
386 		syslog(LOG_ERR,
387 		       "<%s> advertised link mtu (%lu) on %s is invalid (must "
388 		       "be between least MTU (%d) and physical link MTU (%d)",
389 		       __func__, (unsigned long)tmp->linkmtu, intface,
390 		       IPV6_MMTU, tmp->phymtu);
391 		exit(1);
392 	}
393 
394 	/* route information */
395 	MAYHAVE(val, "routes", -1);
396 	if (val != -1)
397 		syslog(LOG_INFO, "route information option is not available");
398 
399 	/* okey */
400 	tmp->next = ralist;
401 	ralist = tmp;
402 
403 	/* construct the sending packet */
404 	make_packet(tmp);
405 
406 	/* set timer */
407 	tmp->timer = rtadvd_add_timer(ra_timeout, ra_timer_update,
408 				      tmp, tmp);
409 	ra_timer_update((void *)tmp, &tmp->timer->tm);
410 	rtadvd_set_timer(&tmp->timer->tm, tmp->timer);
411 }
412 
413 static void
414 get_prefix(struct rainfo *rai)
415 {
416 	struct ifaddrs *ifap, *ifa;
417 	struct prefix *pp;
418 	struct in6_addr *a;
419 	u_char *p, *ep, *m, *lim;
420 	u_char ntopbuf[INET6_ADDRSTRLEN];
421 
422 	if (getifaddrs(&ifap) < 0) {
423 		syslog(LOG_ERR,
424 		       "<%s> can't get interface addresses",
425 		       __func__);
426 		exit(1);
427 	}
428 	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
429 		int plen;
430 
431 		if (strcmp(ifa->ifa_name, rai->ifname) != 0)
432 			continue;
433 		if (ifa->ifa_addr->sa_family != AF_INET6)
434 			continue;
435 		a = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
436 		if (IN6_IS_ADDR_LINKLOCAL(a))
437 			continue;
438 		/* get prefix length */
439 		m = (u_char *)&((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr;
440 		lim = (u_char *)(ifa->ifa_netmask) + ifa->ifa_netmask->sa_len;
441 		plen = prefixlen(m, lim);
442 		if (plen <= 0 || plen > 128) {
443 			syslog(LOG_ERR, "<%s> failed to get prefixlen "
444 			       "or prefix is invalid",
445 			       __func__);
446 			exit(1);
447 		}
448 		if (plen == 128)	/* XXX */
449 			continue;
450 		if (find_prefix(rai, a, plen)) {
451 			/* ignore a duplicated prefix. */
452 			continue;
453 		}
454 
455 		/* allocate memory to store prefix info. */
456 		if ((pp = malloc(sizeof(*pp))) == NULL) {
457 			syslog(LOG_ERR,
458 			       "<%s> can't get allocate buffer for prefix",
459 			       __func__);
460 			exit(1);
461 		}
462 		memset(pp, 0, sizeof(*pp));
463 
464 		/* set prefix, sweep bits outside of prefixlen */
465 		pp->prefixlen = plen;
466 		memcpy(&pp->prefix, a, sizeof(*a));
467 		{
468 			p = (u_char *)&pp->prefix;
469 			ep = (u_char *)(&pp->prefix + 1);
470 			while (m < lim)
471 				*p++ &= *m++;
472 			while (p < ep)
473 				*p++ = 0x00;
474 		}
475 	        if (!inet_ntop(AF_INET6, &pp->prefix, ntopbuf,
476 	            sizeof(ntopbuf))) {
477 			syslog(LOG_ERR, "<%s> inet_ntop failed", __func__);
478 			exit(1);
479 		}
480 		syslog(LOG_DEBUG,
481 		       "<%s> add %s/%d to prefix list on %s",
482 		       __func__, ntopbuf, pp->prefixlen, rai->ifname);
483 
484 		/* set other fields with protocol defaults */
485 		pp->validlifetime = DEF_ADVVALIDLIFETIME;
486 		pp->preflifetime = DEF_ADVPREFERREDLIFETIME;
487 		pp->onlinkflg = 1;
488 		pp->autoconfflg = 1;
489 		pp->origin = PREFIX_FROM_KERNEL;
490 
491 		/* link into chain */
492 		insque(pp, &rai->prefix);
493 
494 		/* counter increment */
495 		rai->pfxs++;
496 	}
497 
498 	freeifaddrs(ifap);
499 }
500 
501 static void
502 makeentry(buf, len, id, string, add)
503 	char *buf;
504 	size_t len;
505 	int id;
506 	char *string;
507 	int add;
508 {
509 	char *ep = buf + len;
510 
511 	strlcpy(buf, string, len);
512 	if (add) {
513 		char *cp;
514 
515 		cp = (char *)strchr(buf, '\0');
516 		snprintf(cp, ep - cp, "%d", id);
517 	}
518 }
519 
520 /*
521  * Add a prefix to the list of specified interface and reconstruct
522  * the outgoing packet.
523  * The prefix must not be in the list.
524  * XXX: other parameter of the prefix(e.g. lifetime) shoule be
525  * able to be specified.
526  */
527 static void
528 add_prefix(struct rainfo *rai, struct in6_prefixreq *ipr)
529 {
530 	struct prefix *prefix;
531 	u_char ntopbuf[INET6_ADDRSTRLEN];
532 
533 	if ((prefix = malloc(sizeof(*prefix))) == NULL) {
534 		syslog(LOG_ERR, "<%s> memory allocation failed",
535 		       __func__);
536 		return;		/* XXX: error or exit? */
537 	}
538 	memset(prefix, 0, sizeof(*prefix));
539 	prefix->prefix = ipr->ipr_prefix.sin6_addr;
540 	prefix->prefixlen = ipr->ipr_plen;
541 	prefix->validlifetime = ipr->ipr_vltime;
542 	prefix->preflifetime = ipr->ipr_pltime;
543 	prefix->onlinkflg = ipr->ipr_raf_onlink;
544 	prefix->autoconfflg = ipr->ipr_raf_auto;
545 	prefix->origin = PREFIX_FROM_DYNAMIC;
546 
547 	insque(prefix, &rai->prefix);
548 
549 	syslog(LOG_DEBUG, "<%s> new prefix %s/%d was added on %s",
550 	       __func__, inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr,
551 				       ntopbuf, INET6_ADDRSTRLEN),
552 	       ipr->ipr_plen, rai->ifname);
553 
554 	/* free the previous packet */
555 	free(rai->ra_data);
556 	rai->ra_data = NULL;
557 
558 	/* reconstruct the packet */
559 	rai->pfxs++;
560 	make_packet(rai);
561 
562 	/*
563 	 * reset the timer so that the new prefix will be advertised quickly.
564 	 */
565 	rai->initcounter = 0;
566 	ra_timer_update((void *)rai, &rai->timer->tm);
567 	rtadvd_set_timer(&rai->timer->tm, rai->timer);
568 }
569 
570 /*
571  * Delete a prefix to the list of specified interface and reconstruct
572  * the outgoing packet.
573  * The prefix must be in the list.
574  */
575 void
576 delete_prefix(struct rainfo *rai, struct prefix *prefix)
577 {
578 	u_char ntopbuf[INET6_ADDRSTRLEN];
579 
580 	remque(prefix);
581 	syslog(LOG_DEBUG, "<%s> prefix %s/%d was deleted on %s",
582 	       __func__, inet_ntop(AF_INET6, &prefix->prefix,
583 				       ntopbuf, INET6_ADDRSTRLEN),
584 	       prefix->prefixlen, rai->ifname);
585 	free(prefix);
586 	rai->pfxs--;
587 	make_packet(rai);
588 }
589 
590 /*
591  * Try to get an in6_prefixreq contents for a prefix which matches
592  * ipr->ipr_prefix and ipr->ipr_plen and belongs to
593  * the interface whose name is ipr->ipr_name[].
594  */
595 static int
596 init_prefix(struct in6_prefixreq *ipr)
597 {
598 #if 0
599 	int s;
600 
601 	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
602 		syslog(LOG_ERR, "<%s> socket: %s", __func__,
603 		       strerror(errno));
604 		exit(1);
605 	}
606 
607 	if (ioctl(s, SIOCGIFPREFIX_IN6, (caddr_t)ipr) < 0) {
608 		syslog(LOG_INFO, "<%s> ioctl:SIOCGIFPREFIX %s", __func__,
609 		       strerror(errno));
610 
611 		ipr->ipr_vltime = DEF_ADVVALIDLIFETIME;
612 		ipr->ipr_pltime = DEF_ADVPREFERREDLIFETIME;
613 		ipr->ipr_raf_onlink = 1;
614 		ipr->ipr_raf_auto = 1;
615 		/* omit other field initialization */
616 	}
617 	else if (ipr->ipr_origin < PR_ORIG_RR) {
618 		u_char ntopbuf[INET6_ADDRSTRLEN];
619 
620 		syslog(LOG_WARNING, "<%s> Added prefix(%s)'s origin %d is"
621 		       "lower than PR_ORIG_RR(router renumbering)."
622 		       "This should not happen if I am router", __func__,
623 		       inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr, ntopbuf,
624 				 sizeof(ntopbuf)), ipr->ipr_origin);
625 		close(s);
626 		return 1;
627 	}
628 
629 	close(s);
630 	return 0;
631 #else
632 	ipr->ipr_vltime = DEF_ADVVALIDLIFETIME;
633 	ipr->ipr_pltime = DEF_ADVPREFERREDLIFETIME;
634 	ipr->ipr_raf_onlink = 1;
635 	ipr->ipr_raf_auto = 1;
636 	return 0;
637 #endif
638 }
639 
640 void
641 make_prefix(struct rainfo *rai, int ifindex, struct in6_addr *addr, int plen)
642 {
643 	struct in6_prefixreq ipr;
644 
645 	memset(&ipr, 0, sizeof(ipr));
646 	if (if_indextoname(ifindex, ipr.ipr_name) == NULL) {
647 		syslog(LOG_ERR, "<%s> Prefix added interface No.%d doesn't"
648 		       "exist. This should not happen! %s", __func__,
649 		       ifindex, strerror(errno));
650 		exit(1);
651 	}
652 	ipr.ipr_prefix.sin6_len = sizeof(ipr.ipr_prefix);
653 	ipr.ipr_prefix.sin6_family = AF_INET6;
654 	ipr.ipr_prefix.sin6_addr = *addr;
655 	ipr.ipr_plen = plen;
656 
657 	if (init_prefix(&ipr))
658 		return; /* init failed by some error */
659 	add_prefix(rai, &ipr);
660 }
661 
662 void
663 make_packet(struct rainfo *rainfo)
664 {
665 	size_t packlen, lladdroptlen = 0;
666 	char *buf;
667 	struct nd_router_advert *ra;
668 	struct nd_opt_prefix_info *ndopt_pi;
669 	struct nd_opt_mtu *ndopt_mtu;
670 	struct prefix *pfx;
671 
672 	/* calculate total length */
673 	packlen = sizeof(struct nd_router_advert);
674 	if (rainfo->advlinkopt) {
675 		if ((lladdroptlen = lladdropt_length(rainfo->sdl)) == 0) {
676 			syslog(LOG_INFO,
677 			       "<%s> link-layer address option has"
678 			       " null length on %s.  Treat as not included.",
679 			       __func__, rainfo->ifname);
680 			rainfo->advlinkopt = 0;
681 		}
682 		packlen += lladdroptlen;
683 	}
684 	if (rainfo->pfxs)
685 		packlen += sizeof(struct nd_opt_prefix_info) * rainfo->pfxs;
686 	if (rainfo->linkmtu)
687 		packlen += sizeof(struct nd_opt_mtu);
688 
689 	/* allocate memory for the packet */
690 	if ((buf = malloc(packlen)) == NULL) {
691 		syslog(LOG_ERR,
692 		       "<%s> can't get enough memory for an RA packet",
693 		       __func__);
694 		exit(1);
695 	}
696 	if (rainfo->ra_data) {
697 		/* free the previous packet */
698 		free(rainfo->ra_data);
699 		rainfo->ra_data = NULL;
700 	}
701 	rainfo->ra_data = buf;
702 	/* XXX: what if packlen > 576? */
703 	rainfo->ra_datalen = packlen;
704 
705 	/*
706 	 * construct the packet
707 	 */
708 	ra = (struct nd_router_advert *)buf;
709 	ra->nd_ra_type = ND_ROUTER_ADVERT;
710 	ra->nd_ra_code = 0;
711 	ra->nd_ra_cksum = 0;
712 	ra->nd_ra_curhoplimit = (u_int8_t)(0xff & rainfo->hoplimit);
713 	ra->nd_ra_flags_reserved = 0; /* just in case */
714 	/*
715 	 * XXX: the router preference field, which is a 2-bit field, should be
716 	 * initialized before other fields.
717 	 */
718 	ra->nd_ra_flags_reserved = 0xff & rainfo->rtpref;
719 	ra->nd_ra_flags_reserved |=
720 		rainfo->managedflg ? ND_RA_FLAG_MANAGED : 0;
721 	ra->nd_ra_flags_reserved |=
722 		rainfo->otherflg ? ND_RA_FLAG_OTHER : 0;
723 	ra->nd_ra_router_lifetime = htons(rainfo->lifetime);
724 	ra->nd_ra_reachable = htonl(rainfo->reachabletime);
725 	ra->nd_ra_retransmit = htonl(rainfo->retranstimer);
726 	buf += sizeof(*ra);
727 
728 	if (rainfo->advlinkopt) {
729 		lladdropt_fill(rainfo->sdl, (struct nd_opt_hdr *)buf);
730 		buf += lladdroptlen;
731 	}
732 
733 	if (rainfo->linkmtu) {
734 		ndopt_mtu = (struct nd_opt_mtu *)buf;
735 		ndopt_mtu->nd_opt_mtu_type = ND_OPT_MTU;
736 		ndopt_mtu->nd_opt_mtu_len = 1;
737 		ndopt_mtu->nd_opt_mtu_reserved = 0;
738 		ndopt_mtu->nd_opt_mtu_mtu = htonl(rainfo->linkmtu);
739 		buf += sizeof(struct nd_opt_mtu);
740 	}
741 
742 
743 
744 	for (pfx = rainfo->prefix.next;
745 	     pfx != &rainfo->prefix; pfx = pfx->next) {
746 		u_int32_t vltime, pltime;
747 		struct timeval now;
748 
749 		ndopt_pi = (struct nd_opt_prefix_info *)buf;
750 		ndopt_pi->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION;
751 		ndopt_pi->nd_opt_pi_len = 4;
752 		ndopt_pi->nd_opt_pi_prefix_len = pfx->prefixlen;
753 		ndopt_pi->nd_opt_pi_flags_reserved = 0;
754 		if (pfx->onlinkflg)
755 			ndopt_pi->nd_opt_pi_flags_reserved |=
756 				ND_OPT_PI_FLAG_ONLINK;
757 		if (pfx->autoconfflg)
758 			ndopt_pi->nd_opt_pi_flags_reserved |=
759 				ND_OPT_PI_FLAG_AUTO;
760 		if (pfx->vltimeexpire || pfx->pltimeexpire)
761 			gettimeofday(&now, NULL);
762 		if (pfx->vltimeexpire == 0)
763 			vltime = pfx->validlifetime;
764 		else
765 			vltime = (pfx->vltimeexpire > now.tv_sec) ?
766 				pfx->vltimeexpire - now.tv_sec : 0;
767 		if (pfx->pltimeexpire == 0)
768 			pltime = pfx->preflifetime;
769 		else
770 			pltime = (pfx->pltimeexpire > now.tv_sec) ?
771 				pfx->pltimeexpire - now.tv_sec : 0;
772 		if (vltime < pltime) {
773 			/*
774 			 * this can happen if vltime is decrement but pltime
775 			 * is not.
776 			 */
777 			pltime = vltime;
778 		}
779 		ndopt_pi->nd_opt_pi_valid_time = htonl(vltime);
780 		ndopt_pi->nd_opt_pi_preferred_time = htonl(pltime);
781 		ndopt_pi->nd_opt_pi_reserved2 = 0;
782 		ndopt_pi->nd_opt_pi_prefix = pfx->prefix;
783 
784 		buf += sizeof(struct nd_opt_prefix_info);
785 	}
786 
787 	return;
788 }
789 
790 static int
791 getinet6sysctl(int code)
792 {
793 	int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 };
794 	int value;
795 	size_t size;
796 
797 	mib[3] = code;
798 	size = sizeof(value);
799 	if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), &value, &size, NULL, 0)
800 	    < 0) {
801 		syslog(LOG_ERR, "<%s>: failed to get ip6 sysctl(%d): %s",
802 		       __func__, code,
803 		       strerror(errno));
804 		return(-1);
805 	}
806 	else
807 		return(value);
808 }
809