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