config.c revision 113325
1151497Sru/*	$FreeBSD: head/usr.sbin/rtadvd/config.c 113325 2003-04-10 07:31:34Z suz $	*/
2151497Sru/*	$KAME: config.c,v 1.37 2001/05/25 07:34:00 itojun Exp $	*/
3151497Sru
4151497Sru/*
5151497Sru * Copyright (C) 1998 WIDE Project.
6151497Sru * All rights reserved.
7151497Sru *
8151497Sru * Redistribution and use in source and binary forms, with or without
9151497Sru * modification, are permitted provided that the following conditions
10151497Sru * are met:
11151497Sru * 1. Redistributions of source code must retain the above copyright
12151497Sru *    notice, this list of conditions and the following disclaimer.
13151497Sru * 2. Redistributions in binary form must reproduce the above copyright
14151497Sru *    notice, this list of conditions and the following disclaimer in the
15151497Sru *    documentation and/or other materials provided with the distribution.
16151497Sru * 3. Neither the name of the project nor the names of its contributors
17151497Sru *    may be used to endorse or promote products derived from this software
18151497Sru *    without specific prior written permission.
19151497Sru *
20151497Sru * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21151497Sru * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22151497Sru * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23151497Sru * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24151497Sru * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25151497Sru * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26151497Sru * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27151497Sru * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28151497Sru * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29151497Sru * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30151497Sru * SUCH DAMAGE.
31151497Sru */
32151497Sru
33151497Sru#include <sys/param.h>
34151497Sru#include <sys/ioctl.h>
35151497Sru#include <sys/socket.h>
36151497Sru#include <sys/time.h>
37151497Sru#include <sys/sysctl.h>
38151497Sru
39151497Sru#include <net/if.h>
40151497Sru#if defined(__FreeBSD__) && __FreeBSD__ >= 3
41151497Sru#include <net/if_var.h>
42151497Sru#endif /* __FreeBSD__ >= 3 */
43151497Sru#include <net/route.h>
44151497Sru#include <net/if_dl.h>
45151497Sru
46151497Sru#include <netinet/in.h>
47151497Sru#include <netinet/in_var.h>
48151497Sru#include <netinet/ip6.h>
49151497Sru#include <netinet6/ip6_var.h>
50151497Sru#include <netinet/icmp6.h>
51151497Sru#ifdef MIP6
52151497Sru#include <netinet6/mip6.h>
53151497Sru#endif
54151497Sru
55151497Sru#include <arpa/inet.h>
56151497Sru
57151497Sru#include <stdio.h>
58151497Sru#include <syslog.h>
59151497Sru#include <errno.h>
60151497Sru#include <string.h>
61151497Sru#include <stdlib.h>
62151497Sru#if defined(__NetBSD__) || defined(__OpenBSD__)
63151497Sru#include <search.h>
64151497Sru#endif
65151497Sru#include <unistd.h>
66151497Sru#include <ifaddrs.h>
67151497Sru
68151497Sru#include "rtadvd.h"
69151497Sru#include "advcap.h"
70151497Sru#include "timer.h"
71151497Sru#include "if.h"
72151497Sru#include "config.h"
73151497Sru
74151497Srustatic time_t prefix_timo = (60 * 120);	/* 2 hours.
75151497Sru					 * XXX: should be configurable. */
76151497Sruextern struct rainfo *ralist;
77151497Sru
78151497Srustatic struct rtadvd_timer *prefix_timeout __P((void *));
79151497Srustatic void makeentry __P((char *, size_t, int, char *, int));
80151497Srustatic void get_prefix __P((struct rainfo *));
81151497Srustatic int getinet6sysctl __P((int));
82151497Sru
83151497Sruvoid
84151497Srugetconfig(intface)
85151497Sru	char *intface;
86151497Sru{
87151497Sru	int stat, pfxs, i;
88151497Sru	char tbuf[BUFSIZ];
89151497Sru	struct rainfo *tmp;
90151497Sru	long val;
91151497Sru	long long val64;
92151497Sru	char buf[BUFSIZ];
93151497Sru	char *bp = buf;
94151497Sru	char *addr;
95151497Sru	static int forwarding = -1;
96151497Sru
97151497Sru#define MUSTHAVE(var, cap)	\
98151497Sru    do {								\
99151497Sru	int t;								\
100151497Sru	if ((t = agetnum(cap)) < 0) {					\
101151497Sru		fprintf(stderr, "rtadvd: need %s for interface %s\n",	\
102151497Sru			cap, intface);					\
103151497Sru		exit(1);						\
104151497Sru	}								\
105151497Sru	var = t;							\
106151497Sru     } while (0)
107151497Sru#define MAYHAVE(var, cap, def)	\
108151497Sru     do {								\
109151497Sru	if ((var = agetnum(cap)) < 0)					\
110151497Sru		var = def;						\
111151497Sru     } while (0)
112151497Sru
113151497Sru	if ((stat = agetent(tbuf, intface)) <= 0) {
114151497Sru		memset(tbuf, 0, sizeof(tbuf));
115151497Sru		syslog(LOG_INFO,
116151497Sru		       "<%s> %s isn't defined in the configuration file"
117151497Sru		       " or the configuration file doesn't exist."
118151497Sru		       " Treat it as default",
119151497Sru		        __FUNCTION__, intface);
120151497Sru	}
121151497Sru
122151497Sru	tmp = (struct rainfo *)malloc(sizeof(*ralist));
123151497Sru	memset(tmp, 0, sizeof(*tmp));
124151497Sru	tmp->prefix.next = tmp->prefix.prev = &tmp->prefix;
125151497Sru	tmp->route.next = tmp->route.prev = &tmp->route;
126151497Sru
127151497Sru	/* check if we are allowed to forward packets (if not determined) */
128151497Sru	if (forwarding < 0) {
129151497Sru		if ((forwarding = getinet6sysctl(IPV6CTL_FORWARDING)) < 0)
130151497Sru			exit(1);
131151497Sru	}
132151497Sru
133151497Sru	/* get interface information */
134151497Sru	if (agetflag("nolladdr"))
135151497Sru		tmp->advlinkopt = 0;
136151497Sru	else
137151497Sru		tmp->advlinkopt = 1;
138151497Sru	if (tmp->advlinkopt) {
139151497Sru		if ((tmp->sdl = if_nametosdl(intface)) == NULL) {
140151497Sru			syslog(LOG_ERR,
141151497Sru			       "<%s> can't get information of %s",
142151497Sru			       __FUNCTION__, intface);
143151497Sru			exit(1);
144151497Sru		}
145151497Sru		tmp->ifindex = tmp->sdl->sdl_index;
146151497Sru	} else
147151497Sru		tmp->ifindex = if_nametoindex(intface);
148151497Sru	strncpy(tmp->ifname, intface, sizeof(tmp->ifname));
149151497Sru	if ((tmp->phymtu = if_getmtu(intface)) == 0) {
150151497Sru		tmp->phymtu = IPV6_MMTU;
151151497Sru		syslog(LOG_WARNING,
152151497Sru		       "<%s> can't get interface mtu of %s. Treat as %d",
153151497Sru		       __FUNCTION__, intface, IPV6_MMTU);
154151497Sru	}
155151497Sru
156151497Sru	/*
157151497Sru	 * set router configuration variables.
158151497Sru	 */
159151497Sru	MAYHAVE(val, "maxinterval", DEF_MAXRTRADVINTERVAL);
160151497Sru	if (val < MIN_MAXINTERVAL || val > MAX_MAXINTERVAL) {
161151497Sru		syslog(LOG_ERR,
162151497Sru		       "<%s> maxinterval must be between %e and %u",
163151497Sru		       __FUNCTION__, MIN_MAXINTERVAL, MAX_MAXINTERVAL);
164151497Sru		exit(1);
165151497Sru	}
166151497Sru	tmp->maxinterval = (u_int)val;
167151497Sru	MAYHAVE(val, "mininterval", tmp->maxinterval/3);
168151497Sru	if (val < MIN_MININTERVAL || val > (tmp->maxinterval * 3) / 4) {
169151497Sru		syslog(LOG_ERR,
170151497Sru		       "<%s> mininterval must be between %e and %d",
171151497Sru		       __FUNCTION__,
172151497Sru		       MIN_MININTERVAL,
173151497Sru		       (tmp->maxinterval * 3) / 4);
174151497Sru		exit(1);
175151497Sru	}
176151497Sru	tmp->mininterval = (u_int)val;
177151497Sru
178151497Sru	MAYHAVE(val, "chlim", DEF_ADVCURHOPLIMIT);
179151497Sru	tmp->hoplimit = val & 0xff;
180151497Sru
181151497Sru	MAYHAVE(val, "raflags", 0);
182151497Sru	tmp->managedflg = val & ND_RA_FLAG_MANAGED;
183151497Sru	tmp->otherflg = val & ND_RA_FLAG_OTHER;
184151497Sru#ifdef MIP6
185151497Sru	if (mobileip6)
186151497Sru		tmp->haflg = val & ND_RA_FLAG_HA;
187151497Sru#endif
188151497Sru#ifndef ND_RA_FLAG_RTPREF_MASK
189151497Sru#define ND_RA_FLAG_RTPREF_MASK	0x18 /* 00011000 */
190151497Sru#define ND_RA_FLAG_RTPREF_RSV	0x10 /* 00010000 */
191151497Sru#endif
192151497Sru	tmp->rtpref = val & ND_RA_FLAG_RTPREF_MASK;
193151497Sru	if (tmp->rtpref == ND_RA_FLAG_RTPREF_RSV) {
194151497Sru		syslog(LOG_ERR, "<%s> invalid router preference on %s",
195151497Sru		       __FUNCTION__, intface);
196151497Sru		exit(1);
197151497Sru	}
198151497Sru
199151497Sru	MAYHAVE(val, "rltime", tmp->maxinterval * 3);
200151497Sru	if (val && (val < tmp->maxinterval || val > MAXROUTERLIFETIME)) {
201151497Sru		syslog(LOG_ERR,
202151497Sru		       "<%s> router lifetime on %s must be 0 or"
203151497Sru		       " between %d and %d",
204151497Sru		       __FUNCTION__, intface,
205151497Sru		       tmp->maxinterval, MAXROUTERLIFETIME);
206151497Sru		exit(1);
207151497Sru	}
208151497Sru	/*
209151497Sru	 * Basically, hosts MUST NOT send Router Advertisement messages at any
210151497Sru	 * time (RFC 2461, Section 6.2.3). However, it would sometimes be
211151497Sru	 * useful to allow hosts to advertise some parameters such as prefix
212151497Sru	 * information and link MTU. Thus, we allow hosts to invoke rtadvd
213151497Sru	 * only when router lifetime (on every advertising interface) is
214151497Sru	 * explicitly set zero. (see also the above section)
215151497Sru	 */
216151497Sru	if (val && forwarding == 0) {
217151497Sru		syslog(LOG_WARNING,
218151497Sru		       "<%s> non zero router lifetime is specified for %s, "
219151497Sru		       "which must not be allowed for hosts.",
220151497Sru		       __FUNCTION__, intface);
221151497Sru		exit(1);
222151497Sru	}
223151497Sru	tmp->lifetime = val & 0xffff;
224151497Sru
225151497Sru	MAYHAVE(val, "rtime", DEF_ADVREACHABLETIME);
226151497Sru	if (val > MAXREACHABLETIME) {
227151497Sru		syslog(LOG_ERR,
228151497Sru		       "<%s> reachable time must be no greater than %d",
229151497Sru		       __FUNCTION__, MAXREACHABLETIME);
230151497Sru		exit(1);
231151497Sru	}
232151497Sru	tmp->reachabletime = (u_int32_t)val;
233151497Sru
234151497Sru	MAYHAVE(val64, "retrans", DEF_ADVRETRANSTIMER);
235151497Sru	if (val64 < 0 || val64 > 0xffffffff) {
236151497Sru		syslog(LOG_ERR,
237151497Sru		       "<%s> retrans time out of range", __FUNCTION__);
238151497Sru		exit(1);
239151497Sru	}
240151497Sru	tmp->retranstimer = (u_int32_t)val64;
241151497Sru
242151497Sru#ifndef MIP6
243151497Sru	if (agetstr("hapref", &bp) || agetstr("hatime", &bp)) {
244151497Sru		syslog(LOG_ERR,
245151497Sru		       "<%s> mobile-ip6 configuration not supported",
246151497Sru		       __FUNCTION__);
247151497Sru		exit(1);
248151497Sru	}
249151497Sru#else
250151497Sru	if (!mobileip6) {
251151497Sru		if (agetstr("hapref", &bp) || agetstr("hatime", &bp)) {
252151497Sru			syslog(LOG_ERR,
253151497Sru			       "<%s> mobile-ip6 configuration without "
254151497Sru			       "proper command line option",
255151497Sru			       __FUNCTION__);
256151497Sru			exit(1);
257151497Sru		}
258151497Sru	} else {
259151497Sru		tmp->hapref = 0;
260151497Sru		if ((val = agetnum("hapref")) >= 0)
261151497Sru			tmp->hapref = (int16_t)val;
262151497Sru		if (tmp->hapref != 0) {
263151497Sru			tmp->hatime = 0;
264151497Sru			MUSTHAVE(val, "hatime");
265151497Sru			tmp->hatime = (u_int16_t)val;
266151497Sru			if (tmp->hatime <= 0) {
267151497Sru				syslog(LOG_ERR,
268151497Sru				       "<%s> home agent lifetime must be greater than 0",
269151497Sru				       __FUNCTION__);
270151497Sru				exit(1);
271151497Sru			}
272151497Sru		}
273151497Sru	}
274151497Sru#endif
275151497Sru
276151497Sru	/* prefix information */
277151497Sru
278151497Sru	/*
279151497Sru	 * This is an implementation specific parameter to consinder
280151497Sru	 * link propagation delays and poorly synchronized clocks when
281151497Sru	 * checking consistency of advertised lifetimes.
282151497Sru	 */
283151497Sru	MAYHAVE(val, "clockskew", 0);
284151497Sru	tmp->clockskew = val;
285151497Sru
286151497Sru	if ((pfxs = agetnum("addrs")) < 0) {
287151497Sru		/* auto configure prefix information */
288151497Sru		if (agetstr("addr", &bp) || agetstr("addr1", &bp)) {
289151497Sru			syslog(LOG_ERR,
290151497Sru			       "<%s> conflicting prefix configuration for %s: "
291151497Sru			       "automatic and manual config at the same time",
292151497Sru			       __FUNCTION__, intface);
293151497Sru			exit(1);
294151497Sru		}
295151497Sru		get_prefix(tmp);
296151497Sru	}
297151497Sru	else {
298151497Sru		tmp->pfxs = pfxs;
299151497Sru		for (i = 0; i < pfxs; i++) {
300151497Sru			struct prefix *pfx;
301151497Sru			char entbuf[256];
302151497Sru			int added = (pfxs > 1) ? 1 : 0;
303151497Sru
304151497Sru			/* allocate memory to store prefix information */
305151497Sru			if ((pfx = malloc(sizeof(struct prefix))) == NULL) {
306151497Sru				syslog(LOG_ERR,
307151497Sru				       "<%s> can't allocate enough memory",
308151497Sru				       __FUNCTION__);
309151497Sru				exit(1);
310151497Sru			}
311151497Sru			memset(pfx, 0, sizeof(*pfx));
312151497Sru
313151497Sru			/* link into chain */
314151497Sru			insque(pfx, &tmp->prefix);
315151497Sru			pfx->rainfo = tmp;
316151497Sru
317151497Sru			pfx->origin = PREFIX_FROM_CONFIG;
318151497Sru
319151497Sru			makeentry(entbuf, sizeof(entbuf), i, "prefixlen",
320151497Sru			    added);
321151497Sru			MAYHAVE(val, entbuf, 64);
322151497Sru			if (val < 0 || val > 128) {
323151497Sru				syslog(LOG_ERR,
324151497Sru				       "<%s> prefixlen out of range",
325151497Sru				       __FUNCTION__);
326151497Sru				exit(1);
327151497Sru			}
328151497Sru			pfx->prefixlen = (int)val;
329151497Sru
330151497Sru			makeentry(entbuf, sizeof(entbuf), i, "pinfoflags",
331151497Sru			    added);
332151497Sru#ifdef MIP6
333151497Sru			if (mobileip6)
334151497Sru			{
335151497Sru				MAYHAVE(val, entbuf,
336151497Sru				    (ND_OPT_PI_FLAG_ONLINK|ND_OPT_PI_FLAG_AUTO|
337151497Sru					 ND_OPT_PI_FLAG_ROUTER));
338151497Sru			} else
339151497Sru#endif
340151497Sru			{
341151497Sru				MAYHAVE(val, entbuf,
342151497Sru				    (ND_OPT_PI_FLAG_ONLINK|ND_OPT_PI_FLAG_AUTO));
343151497Sru			}
344151497Sru			pfx->onlinkflg = val & ND_OPT_PI_FLAG_ONLINK;
345151497Sru			pfx->autoconfflg = val & ND_OPT_PI_FLAG_AUTO;
346151497Sru#ifdef MIP6
347151497Sru			pfx->routeraddr = val & ND_OPT_PI_FLAG_ROUTER;
348151497Sru#endif
349151497Sru
350151497Sru			makeentry(entbuf, sizeof(entbuf), i, "vltime", added);
351151497Sru			MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME);
352151497Sru			if (val64 < 0 || val64 > 0xffffffff) {
353151497Sru				syslog(LOG_ERR,
354151497Sru				       "<%s> vltime out of range",
355151497Sru				       __FUNCTION__);
356151497Sru				exit(1);
357151497Sru			}
358151497Sru			pfx->validlifetime = (u_int32_t)val64;
359151497Sru
360151497Sru			makeentry(entbuf, sizeof(entbuf), i, "vltimedecr",
361151497Sru			    added);
362151497Sru			if (agetflag(entbuf)) {
363151497Sru				struct timeval now;
364151497Sru				gettimeofday(&now, 0);
365151497Sru				pfx->vltimeexpire =
366151497Sru					now.tv_sec + pfx->validlifetime;
367151497Sru			}
368151497Sru
369151497Sru			makeentry(entbuf, sizeof(entbuf), i, "pltime", added);
370151497Sru			MAYHAVE(val64, entbuf, DEF_ADVPREFERREDLIFETIME);
371151497Sru			if (val64 < 0 || val64 > 0xffffffff) {
372151497Sru				syslog(LOG_ERR,
373151497Sru				       "<%s> pltime out of range",
374151497Sru				       __FUNCTION__);
375151497Sru				exit(1);
376151497Sru			}
377151497Sru			pfx->preflifetime = (u_int32_t)val64;
378151497Sru
379151497Sru			makeentry(entbuf, sizeof(entbuf), i, "pltimedecr",
380151497Sru			    added);
381151497Sru			if (agetflag(entbuf)) {
382151497Sru				struct timeval now;
383151497Sru				gettimeofday(&now, 0);
384151497Sru				pfx->pltimeexpire =
385151497Sru					now.tv_sec + pfx->preflifetime;
386151497Sru			}
387151497Sru
388151497Sru			makeentry(entbuf, sizeof(entbuf), i, "addr", added);
389151497Sru			addr = (char *)agetstr(entbuf, &bp);
390151497Sru			if (addr == NULL) {
391151497Sru				syslog(LOG_ERR,
392151497Sru				       "<%s> need %s as a prefix for "
393151497Sru				       "interface %s",
394151497Sru				       __FUNCTION__, entbuf, intface);
395151497Sru				exit(1);
396151497Sru			}
397151497Sru			if (inet_pton(AF_INET6, addr,
398151497Sru				      &pfx->prefix) != 1) {
399151497Sru				syslog(LOG_ERR,
400151497Sru				       "<%s> inet_pton failed for %s",
401151497Sru				       __FUNCTION__, addr);
402151497Sru				exit(1);
403151497Sru			}
404151497Sru			if (IN6_IS_ADDR_MULTICAST(&pfx->prefix)) {
405151497Sru				syslog(LOG_ERR,
406151497Sru				       "<%s> multicast prefix(%s) must "
407151497Sru				       "not be advertised (IF=%s)",
408151497Sru				       __FUNCTION__, addr, intface);
409151497Sru				exit(1);
410151497Sru			}
411151497Sru			if (IN6_IS_ADDR_LINKLOCAL(&pfx->prefix))
412151497Sru				syslog(LOG_NOTICE,
413151497Sru				       "<%s> link-local prefix(%s) will be"
414151497Sru				       " advertised on %s",
415151497Sru				       __FUNCTION__, addr, intface);
416151497Sru		}
417151497Sru	}
418151497Sru
419151497Sru	MAYHAVE(val, "mtu", 0);
420151497Sru	if (val < 0 || val > 0xffffffff) {
421151497Sru		syslog(LOG_ERR,
422151497Sru		       "<%s> mtu out of range", __FUNCTION__);
423151497Sru		exit(1);
424151497Sru	}
425151497Sru	tmp->linkmtu = (u_int32_t)val;
426151497Sru	if (tmp->linkmtu == 0) {
427151497Sru		char *mtustr;
428151497Sru
429151497Sru		if ((mtustr = (char *)agetstr("mtu", &bp)) &&
430151497Sru		    strcmp(mtustr, "auto") == 0)
431151497Sru			tmp->linkmtu = tmp->phymtu;
432151497Sru	}
433151497Sru	else if (tmp->linkmtu < IPV6_MMTU || tmp->linkmtu > tmp->phymtu) {
434151497Sru		syslog(LOG_ERR,
435151497Sru		       "<%s> advertised link mtu must be between"
436151497Sru		       " least MTU and physical link MTU",
437151497Sru		       __FUNCTION__);
438151497Sru		exit(1);
439151497Sru	}
440151497Sru
441151497Sru	/* route information */
442151497Sru
443151497Sru	MAYHAVE(val, "routes", 0);
444151497Sru	if (val < 0 || val > 0xffffffff) {
445151497Sru		syslog(LOG_ERR,
446151497Sru		       "<%s> number of route information improper", __FUNCTION__);
447151497Sru		exit(1);
448151497Sru	}
449151497Sru	tmp->routes = val;
450151497Sru	for (i = 0; i < tmp->routes; i++) {
451151497Sru		struct rtinfo *rti;
452151497Sru		char entbuf[256];
453151497Sru		int added = (tmp->routes > 1) ? 1 : 0;
454151497Sru
455151497Sru		/* allocate memory to store prefix information */
456151497Sru		if ((rti = malloc(sizeof(struct rtinfo))) == NULL) {
457151497Sru			syslog(LOG_ERR,
458151497Sru			       "<%s> can't allocate enough memory",
459151497Sru			       __FUNCTION__);
460151497Sru			exit(1);
461151497Sru		}
462151497Sru		memset(rti, 0, sizeof(*rti));
463151497Sru
464151497Sru		/* link into chain */
465151497Sru		insque(rti, &tmp->route);
466151497Sru
467151497Sru		makeentry(entbuf, sizeof(entbuf), i, "rtrplen", added);
468151497Sru		MAYHAVE(val, entbuf, 64);
469151497Sru		if (val < 0 || val > 128) {
470151497Sru			syslog(LOG_ERR,
471151497Sru			       "<%s> prefixlen out of range",
472151497Sru			       __FUNCTION__);
473151497Sru			exit(1);
474151497Sru		}
475151497Sru		rti->prefixlen = (int)val;
476151497Sru
477151497Sru		makeentry(entbuf, sizeof(entbuf), i, "rtrflags", added);
478151497Sru		MAYHAVE(val, entbuf, 0);
479151497Sru		rti->rtpref = val & ND_RA_FLAG_RTPREF_MASK;
480151497Sru		if (rti->rtpref == ND_RA_FLAG_RTPREF_RSV) {
481151497Sru			syslog(LOG_ERR, "<%s> invalid route preference",
482151497Sru			       __FUNCTION__);
483151497Sru			exit(1);
484151497Sru		}
485151497Sru
486151497Sru		makeentry(entbuf, sizeof(entbuf), i, "rtrltime", added);
487151497Sru		/*
488151497Sru		 * XXX: since default value of route lifetime is not defined in
489151497Sru		 * draft-draves-route-selection-01.txt, I took the default
490151497Sru		 * value of valid lifetime of prefix as its default.
491151497Sru		 * It need be much considered.
492151497Sru		 */
493151497Sru		MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME);
494151497Sru		if (val64 < 0 || val64 > 0xffffffff) {
495151497Sru			syslog(LOG_ERR,
496151497Sru			       "<%s> rtrltime out of range",
497151497Sru			       __FUNCTION__);
498151497Sru			exit(1);
499151497Sru		}
500151497Sru		rti->ltime = (u_int32_t)val64;
501151497Sru
502151497Sru		makeentry(entbuf, sizeof(entbuf), i, "rtrprefix", added);
503151497Sru		addr = (char *)agetstr(entbuf, &bp);
504151497Sru		if (addr == NULL) {
505151497Sru			syslog(LOG_ERR,
506151497Sru			       "<%s> need %s as a route for "
507151497Sru			       "interface %s",
508151497Sru			       __FUNCTION__, entbuf, intface);
509151497Sru			exit(1);
510151497Sru		}
511151497Sru		if (inet_pton(AF_INET6, addr, &rti->prefix) != 1) {
512151497Sru			syslog(LOG_ERR,
513151497Sru			       "<%s> inet_pton failed for %s",
514151497Sru			       __FUNCTION__, addr);
515151497Sru			exit(1);
516151497Sru		}
517151497Sru#if 0
518151497Sru		/*
519151497Sru		 * XXX: currently there's no restriction in route information
520151497Sru		 * prefix according to draft-draves-route-selection-01.txt,
521151497Sru		 * however I think the similar restriction be necessary.
522151497Sru		 */
523151497Sru		MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME);
524151497Sru		if (IN6_IS_ADDR_MULTICAST(&rti->prefix)) {
525151497Sru			syslog(LOG_ERR,
526151497Sru			       "<%s> multicast route (%s) must "
527151497Sru			       "not be advertised (IF=%s)",
528151497Sru			       __FUNCTION__, addr, intface);
529151497Sru			exit(1);
530151497Sru		}
531151497Sru		if (IN6_IS_ADDR_LINKLOCAL(&rti->prefix)) {
532151497Sru			syslog(LOG_NOTICE,
533151497Sru			       "<%s> link-local route (%s) must "
534151497Sru			       "not be advertised on %s",
535151497Sru			       __FUNCTION__, addr, intface);
536151497Sru			exit(1);
537151497Sru		}
538151497Sru#endif
539151497Sru	}
540151497Sru
541151497Sru	/* okey */
542151497Sru	tmp->next = ralist;
543151497Sru	ralist = tmp;
544151497Sru
545151497Sru	/* construct the sending packet */
546151497Sru	make_packet(tmp);
547151497Sru
548151497Sru	/* set timer */
549151497Sru	tmp->timer = rtadvd_add_timer(ra_timeout, ra_timer_update,
550151497Sru				      tmp, tmp);
551151497Sru	ra_timer_update((void *)tmp, &tmp->timer->tm);
552151497Sru	rtadvd_set_timer(&tmp->timer->tm, tmp->timer);
553151497Sru}
554151497Sru
555151497Srustatic void
556151497Sruget_prefix(struct rainfo *rai)
557151497Sru{
558151497Sru	struct ifaddrs *ifap, *ifa;
559151497Sru	struct prefix *pp;
560151497Sru	struct in6_addr *a;
561151497Sru	u_char *p, *ep, *m, *lim;
562151497Sru	u_char ntopbuf[INET6_ADDRSTRLEN];
563151497Sru
564151497Sru	if (getifaddrs(&ifap) < 0) {
565151497Sru		syslog(LOG_ERR,
566151497Sru		       "<%s> can't get interface addresses",
567151497Sru		       __FUNCTION__);
568151497Sru		exit(1);
569151497Sru	}
570151497Sru	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
571151497Sru		int plen;
572151497Sru
573151497Sru		if (strcmp(ifa->ifa_name, rai->ifname) != 0)
574151497Sru			continue;
575151497Sru		if (ifa->ifa_addr->sa_family != AF_INET6)
576151497Sru			continue;
577151497Sru		a = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
578151497Sru		if (IN6_IS_ADDR_LINKLOCAL(a))
579151497Sru			continue;
580151497Sru
581151497Sru		/* get prefix length */
582151497Sru		m = (u_char *)&((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr;
583151497Sru		lim = (u_char *)(ifa->ifa_netmask) + ifa->ifa_netmask->sa_len;
584151497Sru		plen = prefixlen(m, lim);
585151497Sru		if (plen < 0 || plen > 128) {
586151497Sru			syslog(LOG_ERR, "<%s> failed to get prefixlen "
587151497Sru			       "or prefix is invalid",
588151497Sru			       __FUNCTION__);
589151497Sru			exit(1);
590151497Sru		}
591151497Sru		if (find_prefix(rai, a, plen)) {
592151497Sru			/* ignore a duplicated prefix. */
593151497Sru			continue;
594151497Sru		}
595151497Sru
596151497Sru		/* allocate memory to store prefix info. */
597151497Sru		if ((pp = malloc(sizeof(*pp))) == NULL) {
598151497Sru			syslog(LOG_ERR,
599151497Sru			       "<%s> can't get allocate buffer for prefix",
600151497Sru			       __FUNCTION__);
601151497Sru			exit(1);
602151497Sru		}
603151497Sru		memset(pp, 0, sizeof(*pp));
604151497Sru
605151497Sru		/* set prefix, sweep bits outside of prefixlen */
606151497Sru		pp->prefixlen = plen;
607151497Sru		memcpy(&pp->prefix, a, sizeof(*a));
608151497Sru		p = (u_char *)&pp->prefix;
609151497Sru		ep = (u_char *)(&pp->prefix + 1);
610151497Sru		while (m < lim)
611151497Sru			*p++ &= *m++;
612151497Sru		while (p < ep)
613151497Sru			*p++ = 0x00;
614151497Sru
615151497Sru	        if (!inet_ntop(AF_INET6, &pp->prefix, ntopbuf,
616151497Sru	            sizeof(ntopbuf))) {
617151497Sru			syslog(LOG_ERR, "<%s> inet_ntop failed", __FUNCTION__);
618151497Sru			exit(1);
619151497Sru		}
620151497Sru		syslog(LOG_DEBUG,
621151497Sru		       "<%s> add %s/%d to prefix list on %s",
622151497Sru		       __FUNCTION__, ntopbuf, pp->prefixlen, rai->ifname);
623151497Sru
624151497Sru		/* set other fields with protocol defaults */
625151497Sru		pp->validlifetime = DEF_ADVVALIDLIFETIME;
626151497Sru		pp->preflifetime = DEF_ADVPREFERREDLIFETIME;
627151497Sru		pp->onlinkflg = 1;
628151497Sru		pp->autoconfflg = 1;
629151497Sru		pp->origin = PREFIX_FROM_KERNEL;
630151497Sru
631151497Sru		/* link into chain */
632151497Sru		insque(pp, &rai->prefix);
633151497Sru		pp->rainfo = rai;
634151497Sru
635151497Sru		/* counter increment */
636151497Sru		rai->pfxs++;
637151497Sru	}
638151497Sru
639151497Sru	freeifaddrs(ifap);
640151497Sru}
641151497Sru
642151497Srustatic void
643151497Srumakeentry(buf, len, id, string, add)
644151497Sru	char *buf;
645151497Sru	size_t len;
646151497Sru	int id;
647151497Sru	char *string;
648151497Sru	int add;
649151497Sru{
650151497Sru	char *ep = buf + len;
651151497Sru
652151497Sru	strcpy(buf, string);
653151497Sru	if (add) {
654151497Sru		char *cp;
655151497Sru
656151497Sru		cp = (char *)index(buf, '\0');
657151497Sru		snprintf(cp, ep - cp, "%d", id);
658151497Sru	}
659151497Sru}
660151497Sru
661151497Sru/*
662151497Sru * Add a prefix to the list of specified interface and reconstruct
663151497Sru * the outgoing packet.
664151497Sru * The prefix must not be in the list.
665151497Sru * XXX: other parameter of the prefix(e.g. lifetime) shoule be
666151497Sru * able to be specified.
667151497Sru */
668151497Srustatic void
669151497Sruadd_prefix(struct rainfo *rai, struct in6_prefixreq *ipr)
670151497Sru{
671151497Sru	struct prefix *prefix;
672151497Sru	u_char ntopbuf[INET6_ADDRSTRLEN];
673151497Sru
674151497Sru	if ((prefix = malloc(sizeof(*prefix))) == NULL) {
675151497Sru		syslog(LOG_ERR, "<%s> memory allocation failed",
676151497Sru		       __FUNCTION__);
677151497Sru		return;		/* XXX: error or exit? */
678151497Sru	}
679151497Sru	memset(prefix, 0, sizeof(*prefix));
680151497Sru	prefix->prefix = ipr->ipr_prefix.sin6_addr;
681151497Sru	prefix->prefixlen = ipr->ipr_plen;
682151497Sru	prefix->validlifetime = ipr->ipr_vltime;
683151497Sru	prefix->preflifetime = ipr->ipr_pltime;
684151497Sru	prefix->onlinkflg = ipr->ipr_raf_onlink;
685151497Sru	prefix->autoconfflg = ipr->ipr_raf_auto;
686151497Sru	prefix->origin = PREFIX_FROM_DYNAMIC;
687151497Sru
688151497Sru	insque(prefix, &rai->prefix);
689151497Sru	prefix->rainfo = rai;
690151497Sru
691151497Sru	syslog(LOG_DEBUG, "<%s> new prefix %s/%d was added on %s",
692151497Sru	       __FUNCTION__, inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr,
693151497Sru				       ntopbuf, INET6_ADDRSTRLEN),
694151497Sru	       ipr->ipr_plen, rai->ifname);
695151497Sru
696151497Sru	/* free the previous packet */
697151497Sru	free(rai->ra_data);
698151497Sru	rai->ra_data = NULL;
699151497Sru
700151497Sru	/* reconstruct the packet */
701151497Sru	rai->pfxs++;
702151497Sru	make_packet(rai);
703151497Sru
704151497Sru	/*
705151497Sru	 * reset the timer so that the new prefix will be advertised quickly.
706151497Sru	 */
707151497Sru	rai->initcounter = 0;
708151497Sru	ra_timer_update((void *)rai, &rai->timer->tm);
709151497Sru	rtadvd_set_timer(&rai->timer->tm, rai->timer);
710151497Sru}
711151497Sru
712151497Sru/*
713151497Sru * Delete a prefix to the list of specified interface and reconstruct
714151497Sru * the outgoing packet.
715151497Sru * The prefix must be in the list.
716151497Sru */
717151497Sruvoid
718151497Srudelete_prefix(struct prefix *prefix)
719151497Sru{
720151497Sru	u_char ntopbuf[INET6_ADDRSTRLEN];
721151497Sru	struct rainfo *rai = prefix->rainfo;
722151497Sru
723151497Sru	remque(prefix);
724151497Sru	syslog(LOG_DEBUG, "<%s> prefix %s/%d was deleted on %s",
725151497Sru	       __FUNCTION__, inet_ntop(AF_INET6, &prefix->prefix,
726151497Sru				       ntopbuf, INET6_ADDRSTRLEN),
727151497Sru	       prefix->prefixlen, rai->ifname);
728151497Sru	if (prefix->timer)
729151497Sru		rtadvd_remove_timer(&prefix->timer);
730151497Sru	free(prefix);
731151497Sru	rai->pfxs--;
732151497Sru}
733151497Sru
734151497Sruvoid
735151497Sruinvalidate_prefix(struct prefix *prefix)
736151497Sru{
737151497Sru	u_char ntopbuf[INET6_ADDRSTRLEN];
738151497Sru	struct timeval timo;
739151497Sru	struct rainfo *rai = prefix->rainfo;
740151497Sru
741151497Sru	if (prefix->timer) {	/* sanity check */
742151497Sru		syslog(LOG_ERR,
743151497Sru		    "<%s> assumption failure: timer already exists",
744151497Sru		    __FUNCTION__);
745151497Sru		exit(1);
746151497Sru	}
747151497Sru
748151497Sru	syslog(LOG_DEBUG, "<%s> prefix %s/%d was invalidated on %s, "
749151497Sru	    "will expire in %ld seconds", __FUNCTION__,
750151497Sru	    inet_ntop(AF_INET6, &prefix->prefix, ntopbuf, INET6_ADDRSTRLEN),
751151497Sru	    prefix->prefixlen, rai->ifname, (long)prefix_timo);
752151497Sru
753151497Sru	/* set the expiration timer */
754151497Sru	prefix->timer = rtadvd_add_timer(prefix_timeout, NULL, prefix, NULL);
755151497Sru	if (prefix->timer == NULL) {
756151497Sru		syslog(LOG_ERR, "<%s> failed to add a timer for a prefix. "
757151497Sru		    "remove the prefix", __FUNCTION__);
758151497Sru		delete_prefix(prefix);
759151497Sru	}
760151497Sru	timo.tv_sec = prefix_timo;
761151497Sru	timo.tv_usec = 0;
762151497Sru	rtadvd_set_timer(&timo, prefix->timer);
763151497Sru}
764151497Sru
765151497Srustatic struct rtadvd_timer *
766151497Sruprefix_timeout(void *arg)
767151497Sru{
768151497Sru	struct prefix *prefix = (struct prefix *)arg;
769151497Sru
770151497Sru	delete_prefix(prefix);
771151497Sru
772151497Sru	return(NULL);
773151497Sru}
774151497Sru
775151497Sruvoid
776151497Sruupdate_prefix(struct prefix * prefix)
777151497Sru{
778151497Sru	u_char ntopbuf[INET6_ADDRSTRLEN];
779151497Sru	struct rainfo *rai = prefix->rainfo;
780151497Sru
781151497Sru	if (prefix->timer == NULL) { /* sanity check */
782151497Sru		syslog(LOG_ERR,
783151497Sru		    "<%s> assumption failure: timer does not exist",
784151497Sru		    __FUNCTION__);
785151497Sru		exit(1);
786151497Sru	}
787151497Sru
788151497Sru	syslog(LOG_DEBUG, "<%s> prefix %s/%d was re-enabled on %s",
789151497Sru	    __FUNCTION__, inet_ntop(AF_INET6, &prefix->prefix, ntopbuf,
790151497Sru	    INET6_ADDRSTRLEN), prefix->prefixlen, rai->ifname);
791151497Sru
792151497Sru	/* stop the expiration timer */
793151497Sru	rtadvd_remove_timer(&prefix->timer);
794151497Sru}
795151497Sru
796151497Sru/*
797151497Sru * Try to get an in6_prefixreq contents for a prefix which matches
798151497Sru * ipr->ipr_prefix and ipr->ipr_plen and belongs to
799151497Sru * the interface whose name is ipr->ipr_name[].
800151497Sru */
801151497Srustatic int
802151497Sruinit_prefix(struct in6_prefixreq *ipr)
803151497Sru{
804151497Sru#if 0
805151497Sru	int s;
806151497Sru
807151497Sru	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
808151497Sru		syslog(LOG_ERR, "<%s> socket: %s", __FUNCTION__,
809151497Sru		       strerror(errno));
810151497Sru		exit(1);
811151497Sru	}
812151497Sru
813151497Sru	if (ioctl(s, SIOCGIFPREFIX_IN6, (caddr_t)ipr) < 0) {
814151497Sru		syslog(LOG_INFO, "<%s> ioctl:SIOCGIFPREFIX %s", __FUNCTION__,
815151497Sru		       strerror(errno));
816151497Sru
817151497Sru		ipr->ipr_vltime = DEF_ADVVALIDLIFETIME;
818151497Sru		ipr->ipr_pltime = DEF_ADVPREFERREDLIFETIME;
819151497Sru		ipr->ipr_raf_onlink = 1;
820151497Sru		ipr->ipr_raf_auto = 1;
821151497Sru		/* omit other field initialization */
822151497Sru	}
823151497Sru	else if (ipr->ipr_origin < PR_ORIG_RR) {
824151497Sru		u_char ntopbuf[INET6_ADDRSTRLEN];
825151497Sru
826151497Sru		syslog(LOG_WARNING, "<%s> Added prefix(%s)'s origin %d is"
827151497Sru		       "lower than PR_ORIG_RR(router renumbering)."
828151497Sru		       "This should not happen if I am router", __FUNCTION__,
829151497Sru		       inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr, ntopbuf,
830151497Sru				 sizeof(ntopbuf)), ipr->ipr_origin);
831151497Sru		close(s);
832151497Sru		return 1;
833151497Sru	}
834151497Sru
835151497Sru	close(s);
836151497Sru	return 0;
837151497Sru#else
838151497Sru	ipr->ipr_vltime = DEF_ADVVALIDLIFETIME;
839151497Sru	ipr->ipr_pltime = DEF_ADVPREFERREDLIFETIME;
840151497Sru	ipr->ipr_raf_onlink = 1;
841151497Sru	ipr->ipr_raf_auto = 1;
842151497Sru        return 0;
843151497Sru#endif
844151497Sru}
845151497Sru
846151497Sruvoid
847151497Srumake_prefix(struct rainfo *rai, int ifindex, struct in6_addr *addr, int plen)
848151497Sru{
849151497Sru	struct in6_prefixreq ipr;
850151497Sru
851151497Sru	memset(&ipr, 0, sizeof(ipr));
852151497Sru	if (if_indextoname(ifindex, ipr.ipr_name) == NULL) {
853151497Sru		syslog(LOG_ERR, "<%s> Prefix added interface No.%d doesn't"
854151497Sru		       "exist. This should not happen! %s", __FUNCTION__,
855151497Sru		       ifindex, strerror(errno));
856151497Sru		exit(1);
857151497Sru	}
858151497Sru	ipr.ipr_prefix.sin6_len = sizeof(ipr.ipr_prefix);
859151497Sru	ipr.ipr_prefix.sin6_family = AF_INET6;
860151497Sru	ipr.ipr_prefix.sin6_addr = *addr;
861151497Sru	ipr.ipr_plen = plen;
862151497Sru
863151497Sru	if (init_prefix(&ipr))
864151497Sru		return; /* init failed by some error */
865151497Sru	add_prefix(rai, &ipr);
866151497Sru}
867151497Sru
868151497Sruvoid
869151497Srumake_packet(struct rainfo *rainfo)
870151497Sru{
871151497Sru	size_t packlen, lladdroptlen = 0;
872151497Sru	char *buf;
873151497Sru	struct nd_router_advert *ra;
874151497Sru	struct nd_opt_prefix_info *ndopt_pi;
875151497Sru	struct nd_opt_mtu *ndopt_mtu;
876151497Sru#ifdef MIP6
877151497Sru	struct nd_opt_advinterval *ndopt_advint;
878151497Sru	struct nd_opt_homeagent_info *ndopt_hai;
879151497Sru#endif
880151497Sru	struct nd_opt_route_info *ndopt_rti;
881151497Sru	struct prefix *pfx;
882151497Sru	struct rtinfo *rti;
883151497Sru
884151497Sru	/* calculate total length */
885151497Sru	packlen = sizeof(struct nd_router_advert);
886151497Sru	if (rainfo->advlinkopt) {
887151497Sru		if ((lladdroptlen = lladdropt_length(rainfo->sdl)) == 0) {
888151497Sru			syslog(LOG_INFO,
889151497Sru			       "<%s> link-layer address option has"
890151497Sru			       " null length on %s."
891151497Sru			       " Treat as not included.",
892151497Sru			       __FUNCTION__, rainfo->ifname);
893151497Sru			rainfo->advlinkopt = 0;
894151497Sru		}
895151497Sru		packlen += lladdroptlen;
896151497Sru	}
897151497Sru	if (rainfo->pfxs)
898151497Sru		packlen += sizeof(struct nd_opt_prefix_info) * rainfo->pfxs;
899151497Sru	if (rainfo->linkmtu)
900151497Sru		packlen += sizeof(struct nd_opt_mtu);
901151497Sru#ifdef MIP6
902151497Sru	if (mobileip6 && rainfo->maxinterval)
903151497Sru		packlen += sizeof(struct nd_opt_advinterval);
904151497Sru	if (mobileip6 && rainfo->hatime)
905151497Sru		packlen += sizeof(struct nd_opt_homeagent_info);
906151497Sru#endif
907151497Sru#ifdef ND_OPT_ROUTE_INFO
908151497Sru	for (rti = rainfo->route.next; rti != &rainfo->route; rti = rti->next)
909151497Sru		packlen += sizeof(struct nd_opt_route_info) +
910151497Sru			   ((rti->prefixlen + 0x3f) >> 6) * 8;
911151497Sru#endif
912151497Sru
913151497Sru	/* allocate memory for the packet */
914151497Sru	if ((buf = malloc(packlen)) == NULL) {
915151497Sru		syslog(LOG_ERR,
916151497Sru		       "<%s> can't get enough memory for an RA packet",
917151497Sru		       __FUNCTION__);
918151497Sru		exit(1);
919151497Sru	}
920151497Sru	if (rainfo->ra_data) {
921151497Sru		/* free the previous packet */
922151497Sru		free(rainfo->ra_data);
923151497Sru		rainfo->ra_data = NULL;
924151497Sru	}
925151497Sru	rainfo->ra_data = buf;
926151497Sru	/* XXX: what if packlen > 576? */
927151497Sru	rainfo->ra_datalen = packlen;
928151497Sru
929151497Sru	/*
930151497Sru	 * construct the packet
931151497Sru	 */
932151497Sru	ra = (struct nd_router_advert *)buf;
933151497Sru	ra->nd_ra_type = ND_ROUTER_ADVERT;
934151497Sru	ra->nd_ra_code = 0;
935151497Sru	ra->nd_ra_cksum = 0;
936151497Sru	ra->nd_ra_curhoplimit = (u_int8_t)(0xff & rainfo->hoplimit);
937151497Sru	ra->nd_ra_flags_reserved = 0; /* just in case */
938151497Sru	/*
939151497Sru	 * XXX: the router preference field, which is a 2-bit field, should be
940151497Sru	 * initialized before other fields.
941151497Sru	 */
942151497Sru	ra->nd_ra_flags_reserved = 0xff & rainfo->rtpref;
943151497Sru	ra->nd_ra_flags_reserved |=
944151497Sru		rainfo->managedflg ? ND_RA_FLAG_MANAGED : 0;
945151497Sru	ra->nd_ra_flags_reserved |=
946151497Sru		rainfo->otherflg ? ND_RA_FLAG_OTHER : 0;
947151497Sru#ifdef MIP6
948151497Sru	ra->nd_ra_flags_reserved |=
949151497Sru		rainfo->haflg ? ND_RA_FLAG_HA : 0;
950151497Sru#endif
951151497Sru	ra->nd_ra_router_lifetime = htons(rainfo->lifetime);
952151497Sru	ra->nd_ra_reachable = htonl(rainfo->reachabletime);
953151497Sru	ra->nd_ra_retransmit = htonl(rainfo->retranstimer);
954151497Sru	buf += sizeof(*ra);
955151497Sru
956151497Sru	if (rainfo->advlinkopt) {
957151497Sru		lladdropt_fill(rainfo->sdl, (struct nd_opt_hdr *)buf);
958151497Sru		buf += lladdroptlen;
959151497Sru	}
960151497Sru
961151497Sru	if (rainfo->linkmtu) {
962151497Sru		ndopt_mtu = (struct nd_opt_mtu *)buf;
963151497Sru		ndopt_mtu->nd_opt_mtu_type = ND_OPT_MTU;
964151497Sru		ndopt_mtu->nd_opt_mtu_len = 1;
965151497Sru		ndopt_mtu->nd_opt_mtu_reserved = 0;
966151497Sru		ndopt_mtu->nd_opt_mtu_mtu = htonl(rainfo->linkmtu);
967151497Sru		buf += sizeof(struct nd_opt_mtu);
968151497Sru	}
969151497Sru
970151497Sru#ifdef MIP6
971151497Sru	if (mobileip6 && rainfo->maxinterval) {
972151497Sru		ndopt_advint = (struct nd_opt_advinterval *)buf;
973151497Sru		ndopt_advint->nd_opt_adv_type = ND_OPT_ADVINTERVAL;
974151497Sru		ndopt_advint->nd_opt_adv_len = 1;
975151497Sru		ndopt_advint->nd_opt_adv_reserved = 0;
976151497Sru		ndopt_advint->nd_opt_adv_interval = htonl(rainfo->maxinterval *
977151497Sru							  1000);
978151497Sru		buf += sizeof(struct nd_opt_advinterval);
979151497Sru	}
980151497Sru#endif
981151497Sru
982151497Sru#ifdef MIP6
983151497Sru	if (rainfo->hatime) {
984151497Sru		ndopt_hai = (struct nd_opt_homeagent_info *)buf;
985151497Sru		ndopt_hai->nd_opt_hai_type = ND_OPT_HOMEAGENT_INFO;
986151497Sru		ndopt_hai->nd_opt_hai_len = 1;
987151497Sru		ndopt_hai->nd_opt_hai_reserved = 0;
988151497Sru		ndopt_hai->nd_opt_hai_preference = htons(rainfo->hapref);
989151497Sru		ndopt_hai->nd_opt_hai_lifetime = htons(rainfo->hatime);
990151497Sru		buf += sizeof(struct nd_opt_homeagent_info);
991151497Sru	}
992151497Sru#endif
993151497Sru
994151497Sru	for (pfx = rainfo->prefix.next;
995151497Sru	     pfx != &rainfo->prefix; pfx = pfx->next) {
996151497Sru		u_int32_t vltime, pltime;
997151497Sru		struct timeval now;
998151497Sru
999151497Sru		ndopt_pi = (struct nd_opt_prefix_info *)buf;
1000151497Sru		ndopt_pi->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION;
1001151497Sru		ndopt_pi->nd_opt_pi_len = 4;
1002151497Sru		ndopt_pi->nd_opt_pi_prefix_len = pfx->prefixlen;
1003151497Sru		ndopt_pi->nd_opt_pi_flags_reserved = 0;
1004151497Sru		if (pfx->onlinkflg)
1005151497Sru			ndopt_pi->nd_opt_pi_flags_reserved |=
1006151497Sru				ND_OPT_PI_FLAG_ONLINK;
1007151497Sru		if (pfx->autoconfflg)
1008151497Sru			ndopt_pi->nd_opt_pi_flags_reserved |=
1009151497Sru				ND_OPT_PI_FLAG_AUTO;
1010151497Sru#ifdef MIP6
1011151497Sru		if (pfx->routeraddr)
1012151497Sru			ndopt_pi->nd_opt_pi_flags_reserved |=
1013151497Sru				ND_OPT_PI_FLAG_ROUTER;
1014151497Sru#endif
1015151497Sru		if (pfx->timer)
1016151497Sru			vltime = 0;
1017151497Sru		else {
1018151497Sru			if (pfx->vltimeexpire || pfx->pltimeexpire)
1019151497Sru				gettimeofday(&now, NULL);
1020151497Sru			if (pfx->vltimeexpire == 0)
1021151497Sru				vltime = pfx->validlifetime;
1022151497Sru			else
1023151497Sru				vltime = (pfx->vltimeexpire > now.tv_sec) ?
1024151497Sru				    pfx->vltimeexpire - now.tv_sec : 0;
1025151497Sru		}
1026151497Sru		if (pfx->timer)
1027151497Sru			pltime = 0;
1028151497Sru		else {
1029151497Sru			if (pfx->pltimeexpire == 0)
1030151497Sru				pltime = pfx->preflifetime;
1031151497Sru			else
1032151497Sru				pltime = (pfx->pltimeexpire > now.tv_sec) ?
1033151497Sru				    pfx->pltimeexpire - now.tv_sec : 0;
1034151497Sru		}
1035151497Sru		if (vltime < pltime) {
1036151497Sru			/*
1037151497Sru			 * this can happen if vltime is decrement but pltime
1038151497Sru			 * is not.
1039151497Sru			 */
1040151497Sru			pltime = vltime;
1041151497Sru		}
1042151497Sru		ndopt_pi->nd_opt_pi_valid_time = htonl(vltime);
1043151497Sru		ndopt_pi->nd_opt_pi_preferred_time = htonl(pltime);
1044151497Sru		ndopt_pi->nd_opt_pi_reserved2 = 0;
1045151497Sru		ndopt_pi->nd_opt_pi_prefix = pfx->prefix;
1046151497Sru
1047151497Sru		buf += sizeof(struct nd_opt_prefix_info);
1048151497Sru	}
1049151497Sru
1050151497Sru#ifdef ND_OPT_ROUTE_INFO
1051151497Sru	for (rti = rainfo->route.next; rti != &rainfo->route; rti = rti->next) {
1052151497Sru		u_int8_t psize = (rti->prefixlen + 0x3f) >> 6;
1053151497Sru
1054151497Sru		ndopt_rti = (struct nd_opt_route_info *)buf;
1055151497Sru		ndopt_rti->nd_opt_rti_type = ND_OPT_ROUTE_INFO;
1056151497Sru		ndopt_rti->nd_opt_rti_len = 1 + psize;
1057151497Sru		ndopt_rti->nd_opt_rti_prefixlen = rti->prefixlen;
1058151497Sru		ndopt_rti->nd_opt_rti_flags = 0xff & rti->rtpref;
1059151497Sru		ndopt_rti->nd_opt_rti_lifetime = htonl(rti->ltime);
1060151497Sru		memcpy(ndopt_rti + 1, &rti->prefix, psize * 8);
1061151497Sru		buf += sizeof(struct nd_opt_route_info) + psize * 8;
1062151497Sru	}
1063151497Sru#endif
1064151497Sru
1065151497Sru	return;
1066151497Sru}
1067151497Sru
1068151497Srustatic int
1069151497Srugetinet6sysctl(int code)
1070151497Sru{
1071151497Sru	int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 };
1072151497Sru	int value;
1073151497Sru	size_t size;
1074151497Sru
1075151497Sru	mib[3] = code;
1076151497Sru	size = sizeof(value);
1077151497Sru	if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), &value, &size, NULL, 0)
1078151497Sru	    < 0) {
1079151497Sru		syslog(LOG_ERR, "<%s>: failed to get ip6 sysctl(%d): %s",
1080151497Sru		       __FUNCTION__, code,
1081151497Sru		       strerror(errno));
1082151497Sru		return(-1);
1083151497Sru	}
1084151497Sru	else
1085151497Sru		return(value);
1086151497Sru}
1087151497Sru