config.c revision 97709
176612Stshiozak/*	$FreeBSD: head/usr.sbin/rtadvd/config.c 97709 2002-06-01 16:50:21Z ume $	*/
276612Stshiozak/*	$KAME: config.c,v 1.37 2001/05/25 07:34:00 itojun Exp $	*/
376612Stshiozak
476612Stshiozak/*
576612Stshiozak * Copyright (C) 1998 WIDE Project.
676612Stshiozak * All rights reserved.
776612Stshiozak *
876612Stshiozak * Redistribution and use in source and binary forms, with or without
976612Stshiozak * modification, are permitted provided that the following conditions
1076612Stshiozak * are met:
1176612Stshiozak * 1. Redistributions of source code must retain the above copyright
1276612Stshiozak *    notice, this list of conditions and the following disclaimer.
1376612Stshiozak * 2. Redistributions in binary form must reproduce the above copyright
1476612Stshiozak *    notice, this list of conditions and the following disclaimer in the
1576612Stshiozak *    documentation and/or other materials provided with the distribution.
1676612Stshiozak * 3. Neither the name of the project nor the names of its contributors
1776612Stshiozak *    may be used to endorse or promote products derived from this software
1876612Stshiozak *    without specific prior written permission.
1976612Stshiozak *
2076612Stshiozak * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
2176612Stshiozak * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2276612Stshiozak * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2376612Stshiozak * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2476612Stshiozak * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2576612Stshiozak * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2676612Stshiozak * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2776612Stshiozak * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2876612Stshiozak * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2976612Stshiozak * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3076612Stshiozak * SUCH DAMAGE.
3176612Stshiozak */
3276612Stshiozak
3386170Sobrien#include <sys/param.h>
3476612Stshiozak#include <sys/ioctl.h>
3576612Stshiozak#include <sys/socket.h>
3676612Stshiozak#include <sys/time.h>
3776612Stshiozak#include <sys/sysctl.h>
3876612Stshiozak
3976612Stshiozak#include <net/if.h>
4076612Stshiozak#if defined(__FreeBSD__) && __FreeBSD__ >= 3
4176612Stshiozak#include <net/if_var.h>
4276612Stshiozak#endif /* __FreeBSD__ >= 3 */
4376612Stshiozak#include <net/route.h>
4476612Stshiozak#include <net/if_dl.h>
4576612Stshiozak
4676612Stshiozak#include <netinet/in.h>
4776612Stshiozak#include <netinet/in_var.h>
4876612Stshiozak#include <netinet/ip6.h>
4976612Stshiozak#include <netinet6/ip6_var.h>
5076612Stshiozak#include <netinet/icmp6.h>
5176612Stshiozak#ifdef MIP6
5276612Stshiozak#include <netinet6/mip6.h>
5376612Stshiozak#endif
5476612Stshiozak
5576612Stshiozak#include <arpa/inet.h>
5676612Stshiozak
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
74static void makeentry __P((char *, size_t, int, char *, int));
75static void get_prefix __P((struct rainfo *));
76static int getinet6sysctl __P((int));
77
78extern struct rainfo *ralist;
79
80void
81getconfig(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, sizeof(entbuf), i, "prefixlen",
316			    added);
317			MAYHAVE(val, entbuf, 64);
318			if (val < 0 || val > 128) {
319				syslog(LOG_ERR,
320				       "<%s> prefixlen out of range",
321				       __FUNCTION__);
322				exit(1);
323			}
324			pfx->prefixlen = (int)val;
325
326			makeentry(entbuf, sizeof(entbuf), i, "pinfoflags",
327			    added);
328#ifdef MIP6
329			if (mobileip6)
330			{
331				MAYHAVE(val, entbuf,
332				    (ND_OPT_PI_FLAG_ONLINK|ND_OPT_PI_FLAG_AUTO|
333					 ND_OPT_PI_FLAG_ROUTER));
334			} else
335#endif
336			{
337				MAYHAVE(val, entbuf,
338				    (ND_OPT_PI_FLAG_ONLINK|ND_OPT_PI_FLAG_AUTO));
339			}
340			pfx->onlinkflg = val & ND_OPT_PI_FLAG_ONLINK;
341			pfx->autoconfflg = val & ND_OPT_PI_FLAG_AUTO;
342#ifdef MIP6
343			pfx->routeraddr = val & ND_OPT_PI_FLAG_ROUTER;
344#endif
345
346			makeentry(entbuf, sizeof(entbuf), i, "vltime", added);
347			MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME);
348			if (val64 < 0 || val64 > 0xffffffff) {
349				syslog(LOG_ERR,
350				       "<%s> vltime out of range",
351				       __FUNCTION__);
352				exit(1);
353			}
354			pfx->validlifetime = (u_int32_t)val64;
355
356			makeentry(entbuf, sizeof(entbuf), i, "vltimedecr",
357			    added);
358			if (agetflag(entbuf)) {
359				struct timeval now;
360				gettimeofday(&now, 0);
361				pfx->vltimeexpire =
362					now.tv_sec + pfx->validlifetime;
363			}
364
365			makeentry(entbuf, sizeof(entbuf), i, "pltime", added);
366			MAYHAVE(val64, entbuf, DEF_ADVPREFERREDLIFETIME);
367			if (val64 < 0 || val64 > 0xffffffff) {
368				syslog(LOG_ERR,
369				       "<%s> pltime out of range",
370				       __FUNCTION__);
371				exit(1);
372			}
373			pfx->preflifetime = (u_int32_t)val64;
374
375			makeentry(entbuf, sizeof(entbuf), i, "pltimedecr",
376			    added);
377			if (agetflag(entbuf)) {
378				struct timeval now;
379				gettimeofday(&now, 0);
380				pfx->pltimeexpire =
381					now.tv_sec + pfx->preflifetime;
382			}
383
384			makeentry(entbuf, sizeof(entbuf), i, "addr", added);
385			addr = (char *)agetstr(entbuf, &bp);
386			if (addr == NULL) {
387				syslog(LOG_ERR,
388				       "<%s> need %s as an prefix for "
389				       "interface %s",
390				       __FUNCTION__, entbuf, intface);
391				exit(1);
392			}
393			if (inet_pton(AF_INET6, addr,
394				      &pfx->prefix) != 1) {
395				syslog(LOG_ERR,
396				       "<%s> inet_pton failed for %s",
397				       __FUNCTION__, addr);
398				exit(1);
399			}
400			if (IN6_IS_ADDR_MULTICAST(&pfx->prefix)) {
401				syslog(LOG_ERR,
402				       "<%s> multicast prefix(%s) must "
403				       "not be advertised (IF=%s)",
404				       __FUNCTION__, addr, intface);
405				exit(1);
406			}
407			if (IN6_IS_ADDR_LINKLOCAL(&pfx->prefix))
408				syslog(LOG_NOTICE,
409				       "<%s> link-local prefix(%s) will be"
410				       " advertised on %s",
411				       __FUNCTION__, addr, intface);
412		}
413	}
414
415	MAYHAVE(val, "mtu", 0);
416	if (val < 0 || val > 0xffffffff) {
417		syslog(LOG_ERR,
418		       "<%s> mtu out of range", __FUNCTION__);
419		exit(1);
420	}
421	tmp->linkmtu = (u_int32_t)val;
422	if (tmp->linkmtu == 0) {
423		char *mtustr;
424
425		if ((mtustr = (char *)agetstr("mtu", &bp)) &&
426		    strcmp(mtustr, "auto") == 0)
427			tmp->linkmtu = tmp->phymtu;
428	}
429	else if (tmp->linkmtu < IPV6_MMTU || tmp->linkmtu > tmp->phymtu) {
430		syslog(LOG_ERR,
431		       "<%s> advertised link mtu must be between"
432		       " least MTU and physical link MTU",
433		       __FUNCTION__);
434		exit(1);
435	}
436
437	/* route information */
438
439	MAYHAVE(val, "routes", 0);
440	if (val < 0 || val > 0xffffffff) {
441		syslog(LOG_ERR,
442		       "<%s> number of route information improper", __FUNCTION__);
443		exit(1);
444	}
445	tmp->routes = val;
446	for (i = 0; i < tmp->routes; i++) {
447		struct rtinfo *rti;
448		char entbuf[256];
449		int added = (tmp->routes > 1) ? 1 : 0;
450
451		/* allocate memory to store prefix information */
452		if ((rti = malloc(sizeof(struct rtinfo))) == NULL) {
453			syslog(LOG_ERR,
454			       "<%s> can't allocate enough memory",
455			       __FUNCTION__);
456			exit(1);
457		}
458		memset(rti, 0, sizeof(*rti));
459
460		/* link into chain */
461		insque(rti, &tmp->route);
462
463		makeentry(entbuf, sizeof(entbuf), i, "rtrplen", added);
464		MAYHAVE(val, entbuf, 64);
465		if (val < 0 || val > 128) {
466			syslog(LOG_ERR,
467			       "<%s> prefixlen out of range",
468			       __FUNCTION__);
469			exit(1);
470		}
471		rti->prefixlen = (int)val;
472
473		makeentry(entbuf, sizeof(entbuf), i, "rtrflags", added);
474		MAYHAVE(val, entbuf, 0);
475		rti->rtpref = val & ND_RA_FLAG_RTPREF_MASK;
476		if (rti->rtpref == ND_RA_FLAG_RTPREF_RSV) {
477			syslog(LOG_ERR, "<%s> invalid router preference",
478			       __FUNCTION__);
479			exit(1);
480		}
481
482		makeentry(entbuf, sizeof(entbuf), i, "rtrltime", added);
483		/*
484		 * XXX: since default value of route lifetime is not defined in
485		 * draft-draves-route-selection-01.txt, I took the default
486		 * value of valid lifetime of prefix as its default.
487		 * It need be much considered.
488		 */
489		MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME);
490		if (val64 < 0 || val64 > 0xffffffff) {
491			syslog(LOG_ERR,
492			       "<%s> rtrltime out of range",
493			       __FUNCTION__);
494			exit(1);
495		}
496		rti->ltime = (u_int32_t)val64;
497
498		makeentry(entbuf, sizeof(entbuf), i, "rtrprefix", added);
499		addr = (char *)agetstr(entbuf, &bp);
500		if (addr == NULL) {
501			syslog(LOG_ERR,
502			       "<%s> need %s as an route for "
503			       "interface %s",
504			       __FUNCTION__, entbuf, intface);
505			exit(1);
506		}
507		if (inet_pton(AF_INET6, addr, &rti->prefix) != 1) {
508			syslog(LOG_ERR,
509			       "<%s> inet_pton failed for %s",
510			       __FUNCTION__, addr);
511			exit(1);
512		}
513#if 0
514		/*
515		 * XXX: currently there's no restriction in route information
516		 * prefix according to draft-draves-route-selection-01.txt,
517		 * however I think the similar restriction be necessary.
518		 */
519		MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME);
520		if (IN6_IS_ADDR_MULTICAST(&rti->prefix)) {
521			syslog(LOG_ERR,
522			       "<%s> multicast route (%s) must "
523			       "not be advertised (IF=%s)",
524			       __FUNCTION__, addr, intface);
525			exit(1);
526		}
527		if (IN6_IS_ADDR_LINKLOCAL(&rti->prefix)) {
528			syslog(LOG_NOTICE,
529			       "<%s> link-local route (%s) must "
530			       "not be advertised on %s",
531			       __FUNCTION__, addr, intface);
532			exit(1);
533		}
534#endif
535	}
536
537	/* okey */
538	tmp->next = ralist;
539	ralist = tmp;
540
541	/* construct the sending packet */
542	make_packet(tmp);
543
544	/* set timer */
545	tmp->timer = rtadvd_add_timer(ra_timeout, ra_timer_update,
546				      tmp, tmp);
547	ra_timer_update((void *)tmp, &tmp->timer->tm);
548	rtadvd_set_timer(&tmp->timer->tm, tmp->timer);
549}
550
551static void
552get_prefix(struct rainfo *rai)
553{
554	struct ifaddrs *ifap, *ifa;
555	struct prefix *pp;
556	struct in6_addr *a;
557	u_char *p, *ep, *m, *lim;
558	u_char ntopbuf[INET6_ADDRSTRLEN];
559
560	if (getifaddrs(&ifap) < 0) {
561		syslog(LOG_ERR,
562		       "<%s> can't get interface addresses",
563		       __FUNCTION__);
564		exit(1);
565	}
566	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
567		if (strcmp(ifa->ifa_name, rai->ifname) != 0)
568			continue;
569		if (ifa->ifa_addr->sa_family != AF_INET6)
570			continue;
571		a = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
572		if (IN6_IS_ADDR_LINKLOCAL(a))
573			continue;
574
575		/* allocate memory to store prefix info. */
576		if ((pp = malloc(sizeof(*pp))) == NULL) {
577			syslog(LOG_ERR,
578			       "<%s> can't get allocate buffer for prefix",
579			       __FUNCTION__);
580			exit(1);
581		}
582		memset(pp, 0, sizeof(*pp));
583
584		/* set prefix length */
585		m = (u_char *)&((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr;
586		lim = (u_char *)(ifa->ifa_netmask) + ifa->ifa_netmask->sa_len;
587		pp->prefixlen = prefixlen(m, lim);
588		if (pp->prefixlen < 0 || pp->prefixlen > 128) {
589			syslog(LOG_ERR,
590			       "<%s> failed to get prefixlen "
591			       "or prefix is invalid",
592			       __FUNCTION__);
593			exit(1);
594		}
595
596		/* set prefix, sweep bits outside of prefixlen */
597		memcpy(&pp->prefix, a, sizeof(*a));
598		p = (u_char *)&pp->prefix;
599		ep = (u_char *)(&pp->prefix + 1);
600		while (m < lim)
601			*p++ &= *m++;
602		while (p < ep)
603			*p++ = 0x00;
604
605	        if (!inet_ntop(AF_INET6, &pp->prefix, ntopbuf,
606	            sizeof(ntopbuf))) {
607			syslog(LOG_ERR, "<%s> inet_ntop failed", __FUNCTION__);
608			exit(1);
609		}
610		syslog(LOG_DEBUG,
611		       "<%s> add %s/%d to prefix list on %s",
612		       __FUNCTION__, ntopbuf, pp->prefixlen, rai->ifname);
613
614		/* set other fields with protocol defaults */
615		pp->validlifetime = DEF_ADVVALIDLIFETIME;
616		pp->preflifetime = DEF_ADVPREFERREDLIFETIME;
617		pp->onlinkflg = 1;
618		pp->autoconfflg = 1;
619		pp->origin = PREFIX_FROM_KERNEL;
620
621		/* link into chain */
622		insque(pp, &rai->prefix);
623
624		/* counter increment */
625		rai->pfxs++;
626	}
627
628	freeifaddrs(ifap);
629}
630
631static void
632makeentry(buf, len, id, string, add)
633	char *buf;
634	size_t len;
635	int id;
636	char *string;
637	int add;
638{
639	char *ep = buf + len;
640
641	strcpy(buf, string);
642	if (add) {
643		char *cp;
644
645		cp = (char *)index(buf, '\0');
646		snprintf(cp, ep - cp, "%d", id);
647	}
648}
649
650/*
651 * Add a prefix to the list of specified interface and reconstruct
652 * the outgoing packet.
653 * The prefix must not be in the list.
654 * XXX: other parameter of the prefix(e.g. lifetime) shoule be
655 * able to be specified.
656 */
657static void
658add_prefix(struct rainfo *rai, struct in6_prefixreq *ipr)
659{
660	struct prefix *prefix;
661	u_char ntopbuf[INET6_ADDRSTRLEN];
662
663	if ((prefix = malloc(sizeof(*prefix))) == NULL) {
664		syslog(LOG_ERR, "<%s> memory allocation failed",
665		       __FUNCTION__);
666		return;		/* XXX: error or exit? */
667	}
668	memset(prefix, 0, sizeof(*prefix));
669	prefix->prefix = ipr->ipr_prefix.sin6_addr;
670	prefix->prefixlen = ipr->ipr_plen;
671	prefix->validlifetime = ipr->ipr_vltime;
672	prefix->preflifetime = ipr->ipr_pltime;
673	prefix->onlinkflg = ipr->ipr_raf_onlink;
674	prefix->autoconfflg = ipr->ipr_raf_auto;
675	prefix->origin = PREFIX_FROM_DYNAMIC;
676
677	insque(prefix, &rai->prefix);
678
679	syslog(LOG_DEBUG, "<%s> new prefix %s/%d was added on %s",
680	       __FUNCTION__, inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr,
681				       ntopbuf, INET6_ADDRSTRLEN),
682	       ipr->ipr_plen, rai->ifname);
683
684	/* free the previous packet */
685	free(rai->ra_data);
686	rai->ra_data = NULL;
687
688	/* reconstruct the packet */
689	rai->pfxs++;
690	make_packet(rai);
691
692	/*
693	 * reset the timer so that the new prefix will be advertised quickly.
694	 */
695	rai->initcounter = 0;
696	ra_timer_update((void *)rai, &rai->timer->tm);
697	rtadvd_set_timer(&rai->timer->tm, rai->timer);
698}
699
700/*
701 * Delete a prefix to the list of specified interface and reconstruct
702 * the outgoing packet.
703 * The prefix must be in the list.
704 */
705void
706delete_prefix(struct rainfo *rai, struct prefix *prefix)
707{
708	u_char ntopbuf[INET6_ADDRSTRLEN];
709
710	remque(prefix);
711	syslog(LOG_DEBUG, "<%s> prefix %s/%d was deleted on %s",
712	       __FUNCTION__, inet_ntop(AF_INET6, &prefix->prefix,
713				       ntopbuf, INET6_ADDRSTRLEN),
714	       prefix->prefixlen, rai->ifname);
715	free(prefix);
716	rai->pfxs--;
717	make_packet(rai);
718}
719
720/*
721 * Try to get an in6_prefixreq contents for a prefix which matches
722 * ipr->ipr_prefix and ipr->ipr_plen and belongs to
723 * the interface whose name is ipr->ipr_name[].
724 */
725static int
726init_prefix(struct in6_prefixreq *ipr)
727{
728	int s;
729
730	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
731		syslog(LOG_ERR, "<%s> socket: %s", __FUNCTION__,
732		       strerror(errno));
733		exit(1);
734	}
735
736	if (ioctl(s, SIOCGIFPREFIX_IN6, (caddr_t)ipr) < 0) {
737		syslog(LOG_INFO, "<%s> ioctl:SIOCGIFPREFIX %s", __FUNCTION__,
738		       strerror(errno));
739
740		ipr->ipr_vltime = DEF_ADVVALIDLIFETIME;
741		ipr->ipr_pltime = DEF_ADVPREFERREDLIFETIME;
742		ipr->ipr_raf_onlink = 1;
743		ipr->ipr_raf_auto = 1;
744		/* omit other field initialization */
745	}
746	else if (ipr->ipr_origin < PR_ORIG_RR) {
747		u_char ntopbuf[INET6_ADDRSTRLEN];
748
749		syslog(LOG_WARNING, "<%s> Added prefix(%s)'s origin %d is"
750		       "lower than PR_ORIG_RR(router renumbering)."
751		       "This should not happen if I am router", __FUNCTION__,
752		       inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr, ntopbuf,
753				 sizeof(ntopbuf)), ipr->ipr_origin);
754		close(s);
755		return 1;
756	}
757
758	close(s);
759	return 0;
760}
761
762void
763make_prefix(struct rainfo *rai, int ifindex, struct in6_addr *addr, int plen)
764{
765	struct in6_prefixreq ipr;
766
767	memset(&ipr, 0, sizeof(ipr));
768	if (if_indextoname(ifindex, ipr.ipr_name) == NULL) {
769		syslog(LOG_ERR, "<%s> Prefix added interface No.%d doesn't"
770		       "exist. This should not happen! %s", __FUNCTION__,
771		       ifindex, strerror(errno));
772		exit(1);
773	}
774	ipr.ipr_prefix.sin6_len = sizeof(ipr.ipr_prefix);
775	ipr.ipr_prefix.sin6_family = AF_INET6;
776	ipr.ipr_prefix.sin6_addr = *addr;
777	ipr.ipr_plen = plen;
778
779	if (init_prefix(&ipr))
780		return; /* init failed by some error */
781	add_prefix(rai, &ipr);
782}
783
784void
785make_packet(struct rainfo *rainfo)
786{
787	size_t packlen, lladdroptlen = 0;
788	char *buf;
789	struct nd_router_advert *ra;
790	struct nd_opt_prefix_info *ndopt_pi;
791	struct nd_opt_mtu *ndopt_mtu;
792#ifdef MIP6
793	struct nd_opt_advinterval *ndopt_advint;
794	struct nd_opt_homeagent_info *ndopt_hai;
795#endif
796	struct nd_opt_route_info *ndopt_rti;
797	struct prefix *pfx;
798	struct rtinfo *rti;
799
800	/* calculate total length */
801	packlen = sizeof(struct nd_router_advert);
802	if (rainfo->advlinkopt) {
803		if ((lladdroptlen = lladdropt_length(rainfo->sdl)) == 0) {
804			syslog(LOG_INFO,
805			       "<%s> link-layer address option has"
806			       " null length on %s."
807			       " Treat as not included.",
808			       __FUNCTION__, rainfo->ifname);
809			rainfo->advlinkopt = 0;
810		}
811		packlen += lladdroptlen;
812	}
813	if (rainfo->pfxs)
814		packlen += sizeof(struct nd_opt_prefix_info) * rainfo->pfxs;
815	if (rainfo->linkmtu)
816		packlen += sizeof(struct nd_opt_mtu);
817#ifdef MIP6
818	if (mobileip6 && rainfo->maxinterval)
819		packlen += sizeof(struct nd_opt_advinterval);
820	if (mobileip6 && rainfo->hatime)
821		packlen += sizeof(struct nd_opt_homeagent_info);
822#endif
823#ifdef ND_OPT_ROUTE_INFO
824	for (rti = rainfo->route.next; rti != &rainfo->route; rti = rti->next)
825		packlen += sizeof(struct nd_opt_route_info) +
826			   ((rti->prefixlen + 0x3f) >> 6) * 8;
827#endif
828
829	/* allocate memory for the packet */
830	if ((buf = malloc(packlen)) == NULL) {
831		syslog(LOG_ERR,
832		       "<%s> can't get enough memory for an RA packet",
833		       __FUNCTION__);
834		exit(1);
835	}
836	if (rainfo->ra_data) {
837		/* free the previous packet */
838		free(rainfo->ra_data);
839		rainfo->ra_data = NULL;
840	}
841	rainfo->ra_data = buf;
842	/* XXX: what if packlen > 576? */
843	rainfo->ra_datalen = packlen;
844
845	/*
846	 * construct the packet
847	 */
848	ra = (struct nd_router_advert *)buf;
849	ra->nd_ra_type = ND_ROUTER_ADVERT;
850	ra->nd_ra_code = 0;
851	ra->nd_ra_cksum = 0;
852	ra->nd_ra_curhoplimit = (u_int8_t)(0xff & rainfo->hoplimit);
853	ra->nd_ra_flags_reserved = 0; /* just in case */
854	/*
855	 * XXX: the router preference field, which is a 2-bit field, should be
856	 * initialized before other fields.
857	 */
858	ra->nd_ra_flags_reserved = 0xff & rainfo->rtpref;
859	ra->nd_ra_flags_reserved |=
860		rainfo->managedflg ? ND_RA_FLAG_MANAGED : 0;
861	ra->nd_ra_flags_reserved |=
862		rainfo->otherflg ? ND_RA_FLAG_OTHER : 0;
863#ifdef MIP6
864	ra->nd_ra_flags_reserved |=
865		rainfo->haflg ? ND_RA_FLAG_HA : 0;
866#endif
867	ra->nd_ra_router_lifetime = htons(rainfo->lifetime);
868	ra->nd_ra_reachable = htonl(rainfo->reachabletime);
869	ra->nd_ra_retransmit = htonl(rainfo->retranstimer);
870	buf += sizeof(*ra);
871
872	if (rainfo->advlinkopt) {
873		lladdropt_fill(rainfo->sdl, (struct nd_opt_hdr *)buf);
874		buf += lladdroptlen;
875	}
876
877	if (rainfo->linkmtu) {
878		ndopt_mtu = (struct nd_opt_mtu *)buf;
879		ndopt_mtu->nd_opt_mtu_type = ND_OPT_MTU;
880		ndopt_mtu->nd_opt_mtu_len = 1;
881		ndopt_mtu->nd_opt_mtu_reserved = 0;
882		ndopt_mtu->nd_opt_mtu_mtu = htonl(rainfo->linkmtu);
883		buf += sizeof(struct nd_opt_mtu);
884	}
885
886#ifdef MIP6
887	if (mobileip6 && rainfo->maxinterval) {
888		ndopt_advint = (struct nd_opt_advinterval *)buf;
889		ndopt_advint->nd_opt_adv_type = ND_OPT_ADVINTERVAL;
890		ndopt_advint->nd_opt_adv_len = 1;
891		ndopt_advint->nd_opt_adv_reserved = 0;
892		ndopt_advint->nd_opt_adv_interval = htonl(rainfo->maxinterval *
893							  1000);
894		buf += sizeof(struct nd_opt_advinterval);
895	}
896#endif
897
898#ifdef MIP6
899	if (rainfo->hatime) {
900		ndopt_hai = (struct nd_opt_homeagent_info *)buf;
901		ndopt_hai->nd_opt_hai_type = ND_OPT_HOMEAGENT_INFO;
902		ndopt_hai->nd_opt_hai_len = 1;
903		ndopt_hai->nd_opt_hai_reserved = 0;
904		ndopt_hai->nd_opt_hai_preference = htons(rainfo->hapref);
905		ndopt_hai->nd_opt_hai_lifetime = htons(rainfo->hatime);
906		buf += sizeof(struct nd_opt_homeagent_info);
907	}
908#endif
909
910	for (pfx = rainfo->prefix.next;
911	     pfx != &rainfo->prefix; pfx = pfx->next) {
912		u_int32_t vltime, pltime;
913		struct timeval now;
914
915		ndopt_pi = (struct nd_opt_prefix_info *)buf;
916		ndopt_pi->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION;
917		ndopt_pi->nd_opt_pi_len = 4;
918		ndopt_pi->nd_opt_pi_prefix_len = pfx->prefixlen;
919		ndopt_pi->nd_opt_pi_flags_reserved = 0;
920		if (pfx->onlinkflg)
921			ndopt_pi->nd_opt_pi_flags_reserved |=
922				ND_OPT_PI_FLAG_ONLINK;
923		if (pfx->autoconfflg)
924			ndopt_pi->nd_opt_pi_flags_reserved |=
925				ND_OPT_PI_FLAG_AUTO;
926#ifdef MIP6
927		if (pfx->routeraddr)
928			ndopt_pi->nd_opt_pi_flags_reserved |=
929				ND_OPT_PI_FLAG_ROUTER;
930#endif
931		if (pfx->vltimeexpire || pfx->pltimeexpire)
932			gettimeofday(&now, NULL);
933		if (pfx->vltimeexpire == 0)
934			vltime = pfx->validlifetime;
935		else
936			vltime = (pfx->vltimeexpire > now.tv_sec) ?
937				pfx->vltimeexpire - now.tv_sec : 0;
938		if (pfx->pltimeexpire == 0)
939			pltime = pfx->preflifetime;
940		else
941			pltime = (pfx->pltimeexpire > now.tv_sec) ?
942				pfx->pltimeexpire - now.tv_sec : 0;
943		if (vltime < pltime) {
944			/*
945			 * this can happen if vltime is decrement but pltime
946			 * is not.
947			 */
948			pltime = vltime;
949		}
950		ndopt_pi->nd_opt_pi_valid_time = htonl(vltime);
951		ndopt_pi->nd_opt_pi_preferred_time = htonl(pltime);
952		ndopt_pi->nd_opt_pi_reserved2 = 0;
953		ndopt_pi->nd_opt_pi_prefix = pfx->prefix;
954
955		buf += sizeof(struct nd_opt_prefix_info);
956	}
957
958#ifdef ND_OPT_ROUTE_INFO
959	for (rti = rainfo->route.next; rti != &rainfo->route; rti = rti->next) {
960		u_int8_t psize = (rti->prefixlen + 0x3f) >> 6;
961
962		ndopt_rti = (struct nd_opt_route_info *)buf;
963		ndopt_rti->nd_opt_rti_type = ND_OPT_ROUTE_INFO;
964		ndopt_rti->nd_opt_rti_len = 1 + psize;
965		ndopt_rti->nd_opt_rti_prefixlen = rti->prefixlen;
966		ndopt_rti->nd_opt_rti_flags = 0xff & rti->rtpref;
967		ndopt_rti->nd_opt_rti_lifetime = rti->ltime;
968		memcpy(ndopt_rti + 1, &rti->prefix, psize * 8);
969		buf += sizeof(struct nd_opt_route_info) + psize * 8;
970	}
971#endif
972
973	return;
974}
975
976static int
977getinet6sysctl(int code)
978{
979	int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 };
980	int value;
981	size_t size;
982
983	mib[3] = code;
984	size = sizeof(value);
985	if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), &value, &size, NULL, 0)
986	    < 0) {
987		syslog(LOG_ERR, "<%s>: failed to get ip6 sysctl(%d): %s",
988		       __FUNCTION__, code,
989		       strerror(errno));
990		return(-1);
991	}
992	else
993		return(value);
994}
995