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