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