171333Sitojun/*	$FreeBSD: releng/10.3/usr.sbin/rtadvd/config.c 254955 2013-08-27 11:50:33Z hrs $	*/
2118968Sume/*	$KAME: config.c,v 1.84 2003/08/05 12:34:23 itojun Exp $	*/
362656Skris
455505Sshin/*
555505Sshin * Copyright (C) 1998 WIDE Project.
6224144Shrs * Copyright (C) 2011 Hiroki Sato <hrs@FreeBSD.org>
755505Sshin * All rights reserved.
8222732Shrs *
955505Sshin * Redistribution and use in source and binary forms, with or without
1055505Sshin * modification, are permitted provided that the following conditions
1155505Sshin * are met:
1255505Sshin * 1. Redistributions of source code must retain the above copyright
1355505Sshin *    notice, this list of conditions and the following disclaimer.
1455505Sshin * 2. Redistributions in binary form must reproduce the above copyright
1555505Sshin *    notice, this list of conditions and the following disclaimer in the
1655505Sshin *    documentation and/or other materials provided with the distribution.
1755505Sshin * 3. Neither the name of the project nor the names of its contributors
1855505Sshin *    may be used to endorse or promote products derived from this software
1955505Sshin *    without specific prior written permission.
20222732Shrs *
2155505Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
2255505Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2355505Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2455505Sshin * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2555505Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2655505Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2755505Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2855505Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2955505Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3055505Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3155505Sshin * SUCH DAMAGE.
3255505Sshin */
3355505Sshin
3455505Sshin#include <sys/param.h>
3555505Sshin#include <sys/ioctl.h>
3655505Sshin#include <sys/socket.h>
3755505Sshin
3855505Sshin#include <net/if.h>
3955505Sshin#include <net/if_var.h>
4055505Sshin#include <net/route.h>
4155505Sshin#include <net/if_dl.h>
4255505Sshin
4355505Sshin#include <netinet/in.h>
4455505Sshin#include <netinet/in_var.h>
4557120Sshin#include <netinet/ip6.h>
4655505Sshin#include <netinet6/ip6_var.h>
4757120Sshin#include <netinet/icmp6.h>
48151468Ssuz#include <netinet6/nd6.h>
4955505Sshin
5055505Sshin#include <arpa/inet.h>
5155505Sshin
5255505Sshin#include <stdio.h>
5355505Sshin#include <syslog.h>
5455505Sshin#include <errno.h>
55224144Shrs#include <inttypes.h>
56222732Shrs#include <netdb.h>
5755505Sshin#include <string.h>
58136764Ssuz#include <search.h>
5955505Sshin#include <stdlib.h>
60253970Shrs#include <time.h>
6155505Sshin#include <unistd.h>
6278064Sume#include <ifaddrs.h>
6355505Sshin
6455505Sshin#include "rtadvd.h"
6555505Sshin#include "advcap.h"
6655505Sshin#include "timer.h"
6755505Sshin#include "if.h"
6855505Sshin#include "config.h"
6955505Sshin
70222732Shrs/* label of tcapcode + number + domain name + zero octet */
71222732Shrsstatic char entbuf[10 + 3 + NI_MAXHOST + 1];
72222732Shrsstatic char oentbuf[10 + 3 + NI_MAXHOST + 1];
73222732Shrsstatic char abuf[DNAME_LABELENC_MAXLEN];
74222732Shrs
7598172Sumestatic time_t prefix_timo = (60 * 120);	/* 2 hours.
7698172Sume					 * XXX: should be configurable. */
7798172Sume
78173412Skevlostatic struct rtadvd_timer *prefix_timeout(void *);
79222732Shrsstatic void makeentry(char *, size_t, int, const char *);
80222732Shrsstatic size_t dname_labelenc(char *, const char *);
8155505Sshin
82222732Shrs/* Encode domain name label encoding in RFC 1035 Section 3.1 */
83222732Shrsstatic size_t
84222732Shrsdname_labelenc(char *dst, const char *src)
8555505Sshin{
86222732Shrs	char *dst_origin;
87222732Shrs	char *p;
88222732Shrs	size_t len;
8955505Sshin
90222732Shrs	dst_origin = dst;
91222732Shrs	len = strlen(src);
92222732Shrs
93222732Shrs	/* Length fields per 63 octets + '\0' (<= DNAME_LABELENC_MAXLEN) */
94222732Shrs	memset(dst, 0, len + len / 64 + 1 + 1);
95222732Shrs
96222732Shrs	syslog(LOG_DEBUG, "<%s> labelenc = %s", __func__, src);
97222732Shrs	while (src && (len = strlen(src)) != 0) {
98222732Shrs		/* Put a length field with 63 octet limitation first. */
99222732Shrs		p = strchr(src, '.');
100222732Shrs		if (p == NULL)
101222732Shrs			*dst++ = len = MIN(63, len);
102222732Shrs		else
103222732Shrs			*dst++ = len = MIN(63, p - src);
104222732Shrs		/* Copy 63 octets at most. */
105222732Shrs		memcpy(dst, src, len);
106222732Shrs		dst += len;
107222732Shrs		if (p == NULL) /* the last label */
108222732Shrs			break;
109222732Shrs		src = p + 1;
110222732Shrs	}
111222732Shrs	/* Always need a 0-length label at the tail. */
112222732Shrs	*dst++ = '\0';
113222732Shrs
114222743Shrs	syslog(LOG_DEBUG, "<%s> labellen = %td", __func__, dst - dst_origin);
115222732Shrs	return (dst - dst_origin);
116222732Shrs}
117222732Shrs
118222732Shrs#define	MUSTHAVE(var, cap)						\
11962656Skris    do {								\
120118784Sume	int64_t t;							\
12155505Sshin	if ((t = agetnum(cap)) < 0) {					\
12255505Sshin		fprintf(stderr, "rtadvd: need %s for interface %s\n",	\
12355505Sshin			cap, intface);					\
12455505Sshin		exit(1);						\
12555505Sshin	}								\
12655505Sshin	var = t;							\
12762656Skris     } while (0)
128222732Shrs
129222732Shrs#define	MAYHAVE(var, cap, def)						\
13062656Skris     do {								\
13155505Sshin	if ((var = agetnum(cap)) < 0)					\
13255505Sshin		var = def;						\
13362656Skris     } while (0)
13455505Sshin
135224144Shrsint
136224144Shrsloadconfig_index(int idx)
137224144Shrs{
138224144Shrs	char ifname[IFNAMSIZ];
139222732Shrs
140224144Shrs	syslog(LOG_DEBUG, "<%s> enter", __func__);
141224144Shrs
142224144Shrs	if (if_indextoname(idx, ifname) != NULL)
143224144Shrs		return (loadconfig_ifname(ifname));
144224144Shrs	else
145224144Shrs		return (1);
146224144Shrs}
147224144Shrs
148222732Shrsint
149224144Shrsloadconfig_ifname(char *ifname)
150222972Shrs{
151224144Shrs	struct ifinfo *ifi;
152222972Shrs
153224144Shrs	syslog(LOG_DEBUG, "<%s> enter", __func__);
154224144Shrs
155224144Shrs	update_ifinfo(&ifilist, UPDATE_IFINFO_ALL);
156224144Shrs	TAILQ_FOREACH(ifi, &ifilist, ifi_next) {
157224144Shrs		/* NULL means all IFs will be processed. */
158224144Shrs		if (ifname != NULL &&
159224144Shrs		    strcmp(ifi->ifi_ifname, ifname) != 0)
160224144Shrs			continue;
161224144Shrs
162224144Shrs		if (!ifi->ifi_persist) {
163224144Shrs			syslog(LOG_INFO,
164224144Shrs			    "<%s> %s is not a target interface.  "
165224144Shrs			    "Ignored at this moment.", __func__,
166224144Shrs			    ifi->ifi_ifname);
167224144Shrs			continue;
168224144Shrs
169224144Shrs		}
170224144Shrs		if (ifi->ifi_ifindex == 0) {
171222972Shrs			syslog(LOG_ERR,
172224144Shrs			    "<%s> %s not found.  "
173224144Shrs			    "Ignored at this moment.", __func__,
174224144Shrs			    ifi->ifi_ifname);
175222972Shrs			continue;
176222972Shrs		}
177224144Shrs		if (getconfig(ifi) == NULL) {
178222972Shrs			syslog(LOG_ERR,
179222972Shrs			    "<%s> invalid configuration for %s.  "
180224144Shrs			    "Ignored at this moment.", __func__,
181224144Shrs			    ifi->ifi_ifname);
182224144Shrs			continue;
183224144Shrs		}
184222972Shrs	}
185224144Shrs	return (0);
186224144Shrs}
187222972Shrs
188224144Shrsint
189224144Shrsrm_ifinfo_index(int idx)
190224144Shrs{
191224144Shrs	struct ifinfo *ifi;
192224144Shrs
193224144Shrs	ifi = if_indextoifinfo(idx);
194224144Shrs	if (ifi == NULL) {
195224144Shrs		syslog(LOG_ERR, "<%s>: ifinfo not found (idx=%d)",
196224144Shrs		    __func__, idx);
197224144Shrs		return (-1);
198224144Shrs	}
199224144Shrs
200224144Shrs	return (rm_ifinfo(ifi));
201224144Shrs}
202224144Shrs
203224144Shrsint
204224144Shrsrm_ifinfo(struct ifinfo *ifi)
205224144Shrs{
206224144Shrs	int error;
207224144Shrs
208224144Shrs	syslog(LOG_DEBUG, "<%s> enter (%s).", __func__, ifi->ifi_ifname);
209224144Shrs	switch (ifi->ifi_state) {
210224144Shrs	case IFI_STATE_UNCONFIGURED:
211224144Shrs		return (0);
212224144Shrs		break;
213224144Shrs	default:
214224144Shrs		ifi->ifi_state = IFI_STATE_UNCONFIGURED;
215224144Shrs		syslog(LOG_DEBUG,
216224144Shrs		    "<%s> ifname=%s marked as UNCONFIGURED.",
217224144Shrs		    __func__, ifi->ifi_ifname);
218224144Shrs
219228990Suqs		/* XXX: No MC leaving here because index is disappeared */
220224144Shrs
221224144Shrs		/* Inactivate timer */
222224144Shrs		rtadvd_remove_timer(ifi->ifi_ra_timer);
223224144Shrs		ifi->ifi_ra_timer = NULL;
224224144Shrs		break;
225224144Shrs	}
226224144Shrs
227224144Shrs	/* clean up ifi */
228224144Shrs	if (!ifi->ifi_persist) {
229224144Shrs		TAILQ_REMOVE(&ifilist, ifi, ifi_next);
230224144Shrs		syslog(LOG_DEBUG, "<%s>: ifinfo (idx=%d) removed.",
231224144Shrs		    __func__, ifi->ifi_ifindex);
232224144Shrs		free(ifi);
233224144Shrs	} else {
234224144Shrs		/* recreate an empty entry */
235224144Shrs		update_persist_ifinfo(&ifilist, ifi->ifi_ifname);
236224144Shrs		syslog(LOG_DEBUG, "<%s>: ifname=%s is persistent.",
237224144Shrs		    __func__, ifi->ifi_ifname);
238224144Shrs	}
239224144Shrs
240224144Shrs	/* clean up rai if any */
241224144Shrs	switch (ifi->ifi_state) {
242224144Shrs	case IFI_STATE_CONFIGURED:
243224144Shrs		if (ifi->ifi_rainfo != NULL) {
244224144Shrs			error = rm_rainfo(ifi->ifi_rainfo);
245224144Shrs			if (error)
246224144Shrs				return (error);
247224144Shrs			ifi->ifi_rainfo = NULL;
248224144Shrs		}
249224144Shrs		break;
250224144Shrs	case IFI_STATE_TRANSITIVE:
251224144Shrs		if (ifi->ifi_rainfo == ifi->ifi_rainfo_trans) {
252224144Shrs			if (ifi->ifi_rainfo != NULL) {
253224144Shrs				error = rm_rainfo(ifi->ifi_rainfo);
254224144Shrs				if (error)
255224144Shrs					return (error);
256224144Shrs				ifi->ifi_rainfo = NULL;
257224144Shrs				ifi->ifi_rainfo_trans = NULL;
258224144Shrs			}
259224144Shrs		} else {
260224144Shrs			if (ifi->ifi_rainfo != NULL) {
261224144Shrs				error = rm_rainfo(ifi->ifi_rainfo);
262224144Shrs				if (error)
263224144Shrs					return (error);
264224144Shrs				ifi->ifi_rainfo = NULL;
265224144Shrs			}
266224144Shrs			if (ifi->ifi_rainfo_trans != NULL) {
267224144Shrs				error = rm_rainfo(ifi->ifi_rainfo_trans);
268224144Shrs				if (error)
269224144Shrs					return (error);
270224144Shrs				ifi->ifi_rainfo_trans = NULL;
271224144Shrs			}
272224144Shrs		}
273224144Shrs	}
274224144Shrs
275224144Shrs	syslog(LOG_DEBUG, "<%s> leave (%s).", __func__, ifi->ifi_ifname);
276222972Shrs	return (0);
277222972Shrs}
278222972Shrs
279222972Shrsint
280224144Shrsrm_rainfo(struct rainfo *rai)
281222732Shrs{
282222732Shrs	struct prefix *pfx;
283222732Shrs	struct soliciter *sol;
284222732Shrs	struct rdnss *rdn;
285222732Shrs	struct rdnss_addr *rdna;
286222732Shrs	struct dnssl *dns;
287222732Shrs	struct rtinfo *rti;
288222732Shrs
289224144Shrs	syslog(LOG_DEBUG, "<%s>: enter",  __func__);
290222732Shrs
291222732Shrs	TAILQ_REMOVE(&railist, rai, rai_next);
292224144Shrs	if (rai->rai_ifinfo != NULL)
293224144Shrs		syslog(LOG_DEBUG, "<%s>: rainfo (idx=%d) removed.",
294224144Shrs		    __func__, rai->rai_ifinfo->ifi_ifindex);
295222732Shrs
296222732Shrs	if (rai->rai_ra_data != NULL)
297222732Shrs		free(rai->rai_ra_data);
298222732Shrs
299254955Shrs	while ((pfx = TAILQ_FIRST(&rai->rai_prefix)) != NULL)
300254955Shrs		delete_prefix(pfx);
301222732Shrs	while ((sol = TAILQ_FIRST(&rai->rai_soliciter)) != NULL) {
302222732Shrs		TAILQ_REMOVE(&rai->rai_soliciter, sol, sol_next);
303222732Shrs		free(sol);
304222732Shrs	}
305222732Shrs	while ((rdn = TAILQ_FIRST(&rai->rai_rdnss)) != NULL) {
306222732Shrs		TAILQ_REMOVE(&rai->rai_rdnss, rdn, rd_next);
307222732Shrs		while ((rdna = TAILQ_FIRST(&rdn->rd_list)) != NULL) {
308222732Shrs			TAILQ_REMOVE(&rdn->rd_list, rdna, ra_next);
309222732Shrs			free(rdna);
310222732Shrs		}
311222732Shrs		free(rdn);
312222732Shrs	}
313222732Shrs	while ((dns = TAILQ_FIRST(&rai->rai_dnssl)) != NULL) {
314222732Shrs		TAILQ_REMOVE(&rai->rai_dnssl, dns, dn_next);
315222732Shrs		free(dns);
316222732Shrs	}
317222732Shrs	while ((rti = TAILQ_FIRST(&rai->rai_route)) != NULL) {
318222732Shrs		TAILQ_REMOVE(&rai->rai_route, rti, rti_next);
319222732Shrs		free(rti);
320222732Shrs	}
321222732Shrs	free(rai);
322224144Shrs	syslog(LOG_DEBUG, "<%s>: leave",  __func__);
323224144Shrs
324222732Shrs	return (0);
325222732Shrs}
326222732Shrs
327224144Shrsstruct ifinfo *
328224144Shrsgetconfig(struct ifinfo *ifi)
329222732Shrs{
330222732Shrs	int stat, i;
331224144Shrs	int error;
332222732Shrs	char tbuf[BUFSIZ];
333222732Shrs	struct rainfo *rai;
334222972Shrs	struct rainfo *rai_old;
335224144Shrs	int32_t val;
336222732Shrs	int64_t val64;
337222732Shrs	char buf[BUFSIZ];
338222732Shrs	char *bp = buf;
339222732Shrs	char *addr, *flagstr;
340222732Shrs
341224144Shrs	if (ifi == NULL)	/* if does not exist */
342224144Shrs		return (NULL);
343224144Shrs
344224144Shrs	if (ifi->ifi_state == IFI_STATE_TRANSITIVE &&
345224144Shrs	    ifi->ifi_rainfo == NULL) {
346224144Shrs		syslog(LOG_INFO, "<%s> %s is shutting down.  Skipped.",
347224144Shrs		    __func__, ifi->ifi_ifname);
348224144Shrs		return (NULL);
349222732Shrs	}
350222732Shrs
351224144Shrs	if ((stat = agetent(tbuf, ifi->ifi_ifname)) <= 0) {
35255505Sshin		memset(tbuf, 0, sizeof(tbuf));
35355505Sshin		syslog(LOG_INFO,
354222732Shrs		    "<%s> %s isn't defined in the configuration file"
355222732Shrs		    " or the configuration file doesn't exist."
356222732Shrs		    " Treat it as default",
357224144Shrs		     __func__, ifi->ifi_ifname);
35855505Sshin	}
35955505Sshin
360222820Shrs	ELM_MALLOC(rai, exit(1));
361222732Shrs	TAILQ_INIT(&rai->rai_prefix);
362222732Shrs	TAILQ_INIT(&rai->rai_route);
363222732Shrs	TAILQ_INIT(&rai->rai_rdnss);
364222732Shrs	TAILQ_INIT(&rai->rai_dnssl);
365222732Shrs	TAILQ_INIT(&rai->rai_soliciter);
366224144Shrs	rai->rai_ifinfo = ifi;
36755505Sshin
368222732Shrs	/* gather on-link prefixes from the network interfaces. */
369222732Shrs	if (agetflag("noifprefix"))
370222732Shrs		rai->rai_advifprefix = 0;
371222732Shrs	else
372222732Shrs		rai->rai_advifprefix = 1;
37378064Sume
37455505Sshin	/* get interface information */
37555505Sshin	if (agetflag("nolladdr"))
376222732Shrs		rai->rai_advlinkopt = 0;
37755505Sshin	else
378222732Shrs		rai->rai_advlinkopt = 1;
379222732Shrs	if (rai->rai_advlinkopt) {
380224144Shrs		if (ifi->ifi_sdl.sdl_type == 0) {
38155505Sshin			syslog(LOG_ERR,
382222732Shrs			    "<%s> can't get information of %s",
383224144Shrs			    __func__, ifi->ifi_ifname);
384222972Shrs			goto getconfig_free_rai;
38555505Sshin		}
38655505Sshin	}
38755505Sshin
38855505Sshin	/*
38955505Sshin	 * set router configuration variables.
39055505Sshin	 */
39155505Sshin	MAYHAVE(val, "maxinterval", DEF_MAXRTRADVINTERVAL);
39255505Sshin	if (val < MIN_MAXINTERVAL || val > MAX_MAXINTERVAL) {
39355505Sshin		syslog(LOG_ERR,
394224144Shrs		    "<%s> maxinterval (%" PRIu32 ") on %s is invalid "
395222732Shrs		    "(must be between %u and %u)", __func__, val,
396224144Shrs		    ifi->ifi_ifname, MIN_MAXINTERVAL, MAX_MAXINTERVAL);
397222972Shrs		goto getconfig_free_rai;
39855505Sshin	}
399224144Shrs	rai->rai_maxinterval = (uint16_t)val;
400222732Shrs
401222732Shrs	MAYHAVE(val, "mininterval", rai->rai_maxinterval/3);
402224144Shrs	if ((uint16_t)val < MIN_MININTERVAL ||
403224144Shrs	    (uint16_t)val > (rai->rai_maxinterval * 3) / 4) {
40455505Sshin		syslog(LOG_ERR,
405224144Shrs		    "<%s> mininterval (%" PRIu32 ") on %s is invalid "
406222732Shrs		    "(must be between %d and %d)",
407224144Shrs		    __func__, val, ifi->ifi_ifname, MIN_MININTERVAL,
408222732Shrs		    (rai->rai_maxinterval * 3) / 4);
409222972Shrs		goto getconfig_free_rai;
41055505Sshin	}
411224144Shrs	rai->rai_mininterval = (uint16_t)val;
41255505Sshin
41355505Sshin	MAYHAVE(val, "chlim", DEF_ADVCURHOPLIMIT);
414222732Shrs	rai->rai_hoplimit = val & 0xff;
41555505Sshin
416118968Sume	if ((flagstr = (char *)agetstr("raflags", &bp))) {
417118968Sume		val = 0;
418118968Sume		if (strchr(flagstr, 'm'))
419118968Sume			val |= ND_RA_FLAG_MANAGED;
420118968Sume		if (strchr(flagstr, 'o'))
421118968Sume			val |= ND_RA_FLAG_OTHER;
422118968Sume		if (strchr(flagstr, 'h'))
423118968Sume			val |= ND_RA_FLAG_RTPREF_HIGH;
424118968Sume		if (strchr(flagstr, 'l')) {
425118968Sume			if ((val & ND_RA_FLAG_RTPREF_HIGH)) {
426118968Sume				syslog(LOG_ERR, "<%s> the \'h\' and \'l\'"
427118968Sume				    " router flags are exclusive", __func__);
428222972Shrs				goto getconfig_free_rai;
429118968Sume			}
430118968Sume			val |= ND_RA_FLAG_RTPREF_LOW;
431118968Sume		}
432222732Shrs	} else
433118968Sume		MAYHAVE(val, "raflags", 0);
434222732Shrs
435222732Shrs	rai->rai_managedflg = val & ND_RA_FLAG_MANAGED;
436222732Shrs	rai->rai_otherflg = val & ND_RA_FLAG_OTHER;
43778064Sume#ifndef ND_RA_FLAG_RTPREF_MASK
43878064Sume#define ND_RA_FLAG_RTPREF_MASK	0x18 /* 00011000 */
43978064Sume#define ND_RA_FLAG_RTPREF_RSV	0x10 /* 00010000 */
44078064Sume#endif
441222732Shrs	rai->rai_rtpref = val & ND_RA_FLAG_RTPREF_MASK;
442222732Shrs	if (rai->rai_rtpref == ND_RA_FLAG_RTPREF_RSV) {
443118968Sume		syslog(LOG_ERR, "<%s> invalid router preference (%02x) on %s",
444224144Shrs		    __func__, rai->rai_rtpref, ifi->ifi_ifname);
445222972Shrs		goto getconfig_free_rai;
44678064Sume	}
44755505Sshin
448222732Shrs	MAYHAVE(val, "rltime", rai->rai_maxinterval * 3);
449224144Shrs	if ((uint16_t)val && ((uint16_t)val < rai->rai_maxinterval ||
450224144Shrs	    (uint16_t)val > MAXROUTERLIFETIME)) {
45155505Sshin		syslog(LOG_ERR,
452224144Shrs		    "<%s> router lifetime (%" PRIu32 ") on %s is invalid "
453222732Shrs		    "(must be 0 or between %d and %d)",
454224144Shrs		    __func__, val, ifi->ifi_ifname, rai->rai_maxinterval,
455222732Shrs		    MAXROUTERLIFETIME);
456222972Shrs		goto getconfig_free_rai;
45755505Sshin	}
458222732Shrs	rai->rai_lifetime = val & 0xffff;
45955505Sshin
46055505Sshin	MAYHAVE(val, "rtime", DEF_ADVREACHABLETIME);
461118968Sume	if (val < 0 || val > MAXREACHABLETIME) {
46255505Sshin		syslog(LOG_ERR,
463224144Shrs		    "<%s> reachable time (%" PRIu32 ") on %s is invalid "
464222732Shrs		    "(must be no greater than %d)",
465224144Shrs		    __func__, val, ifi->ifi_ifname, MAXREACHABLETIME);
466222972Shrs		goto getconfig_free_rai;
46755505Sshin	}
468224144Shrs	rai->rai_reachabletime = (uint32_t)val;
46955505Sshin
47078064Sume	MAYHAVE(val64, "retrans", DEF_ADVRETRANSTIMER);
47178064Sume	if (val64 < 0 || val64 > 0xffffffff) {
472224144Shrs		syslog(LOG_ERR, "<%s> retrans time (%" PRIu64 ") on %s out of range",
473224144Shrs		    __func__, val64, ifi->ifi_ifname);
474222972Shrs		goto getconfig_free_rai;
47555505Sshin	}
476224144Shrs	rai->rai_retranstimer = (uint32_t)val64;
47755505Sshin
478118968Sume	if (agetnum("hapref") != -1 || agetnum("hatime") != -1) {
47978064Sume		syslog(LOG_ERR,
480222732Shrs		    "<%s> mobile-ip6 configuration not supported",
481222732Shrs		    __func__);
482222972Shrs		goto getconfig_free_rai;
48378064Sume	}
48455505Sshin	/* prefix information */
48578064Sume
48678064Sume	/*
487118664Sume	 * This is an implementation specific parameter to consider
48878064Sume	 * link propagation delays and poorly synchronized clocks when
48978064Sume	 * checking consistency of advertised lifetimes.
49078064Sume	 */
49178064Sume	MAYHAVE(val, "clockskew", 0);
492222732Shrs	rai->rai_clockskew = val;
49378064Sume
494222732Shrs	rai->rai_pfxs = 0;
495118968Sume	for (i = -1; i < MAXPREFIX; i++) {
496118968Sume		struct prefix *pfx;
497118968Sume
498118968Sume		makeentry(entbuf, sizeof(entbuf), i, "addr");
499118968Sume		addr = (char *)agetstr(entbuf, &bp);
500118968Sume		if (addr == NULL)
501118968Sume			continue;
502118968Sume
503118968Sume		/* allocate memory to store prefix information */
504222732Shrs		ELM_MALLOC(pfx, exit(1));
505222820Shrs		pfx->pfx_rainfo = rai;
506222732Shrs		pfx->pfx_origin = PREFIX_FROM_CONFIG;
50771437Sume
508222732Shrs		if (inet_pton(AF_INET6, addr, &pfx->pfx_prefix) != 1) {
509118968Sume			syslog(LOG_ERR,
510222732Shrs			    "<%s> inet_pton failed for %s",
511222732Shrs			    __func__, addr);
512222972Shrs			goto getconfig_free_pfx;
513118968Sume		}
514222732Shrs		if (IN6_IS_ADDR_MULTICAST(&pfx->pfx_prefix)) {
515118968Sume			syslog(LOG_ERR,
516222732Shrs			    "<%s> multicast prefix (%s) must "
517222732Shrs			    "not be advertised on %s",
518224144Shrs			    __func__, addr, ifi->ifi_ifname);
519222972Shrs			goto getconfig_free_pfx;
520118968Sume		}
521222732Shrs		if (IN6_IS_ADDR_LINKLOCAL(&pfx->pfx_prefix))
522118968Sume			syslog(LOG_NOTICE,
523222732Shrs			    "<%s> link-local prefix (%s) will be"
524222732Shrs			    " advertised on %s",
525224144Shrs			    __func__, addr, ifi->ifi_ifname);
52662656Skris
527118968Sume		makeentry(entbuf, sizeof(entbuf), i, "prefixlen");
528118968Sume		MAYHAVE(val, entbuf, 64);
529118968Sume		if (val < 0 || val > 128) {
530224144Shrs			syslog(LOG_ERR, "<%s> prefixlen (%" PRIu32 ") for %s "
531222732Shrs			    "on %s out of range",
532224144Shrs			    __func__, val, addr, ifi->ifi_ifname);
533222972Shrs			goto getconfig_free_pfx;
534118968Sume		}
535222732Shrs		pfx->pfx_prefixlen = (int)val;
53655505Sshin
537118968Sume		makeentry(entbuf, sizeof(entbuf), i, "pinfoflags");
538118968Sume		if ((flagstr = (char *)agetstr(entbuf, &bp))) {
539118968Sume			val = 0;
540118968Sume			if (strchr(flagstr, 'l'))
541118968Sume				val |= ND_OPT_PI_FLAG_ONLINK;
542118968Sume			if (strchr(flagstr, 'a'))
543118968Sume				val |= ND_OPT_PI_FLAG_AUTO;
544118968Sume		} else {
545118968Sume			MAYHAVE(val, entbuf,
546118968Sume			    (ND_OPT_PI_FLAG_ONLINK|ND_OPT_PI_FLAG_AUTO));
547118968Sume		}
548222732Shrs		pfx->pfx_onlinkflg = val & ND_OPT_PI_FLAG_ONLINK;
549222732Shrs		pfx->pfx_autoconfflg = val & ND_OPT_PI_FLAG_AUTO;
55055505Sshin
551118968Sume		makeentry(entbuf, sizeof(entbuf), i, "vltime");
552118968Sume		MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME);
553118968Sume		if (val64 < 0 || val64 > 0xffffffff) {
554224144Shrs			syslog(LOG_ERR, "<%s> vltime (%" PRIu64 ") for "
555118968Sume			    "%s/%d on %s is out of range",
556224144Shrs			    __func__, val64,
557224144Shrs			    addr, pfx->pfx_prefixlen, ifi->ifi_ifname);
558222972Shrs			goto getconfig_free_pfx;
559118968Sume		}
560224144Shrs		pfx->pfx_validlifetime = (uint32_t)val64;
56155505Sshin
562118968Sume		makeentry(entbuf, sizeof(entbuf), i, "vltimedecr");
563118968Sume		if (agetflag(entbuf)) {
564253970Shrs			struct timespec now;
565253970Shrs
566253970Shrs			clock_gettime(CLOCK_MONOTONIC_FAST, &now);
567222732Shrs			pfx->pfx_vltimeexpire =
568222732Shrs				now.tv_sec + pfx->pfx_validlifetime;
569118968Sume		}
57078064Sume
571118968Sume		makeentry(entbuf, sizeof(entbuf), i, "pltime");
572118968Sume		MAYHAVE(val64, entbuf, DEF_ADVPREFERREDLIFETIME);
573118968Sume		if (val64 < 0 || val64 > 0xffffffff) {
574118968Sume			syslog(LOG_ERR,
575224144Shrs			    "<%s> pltime (%" PRIu64 ") for %s/%d on %s "
576118968Sume			    "is out of range",
577224144Shrs			    __func__, val64,
578224144Shrs			    addr, pfx->pfx_prefixlen, ifi->ifi_ifname);
579222972Shrs			goto getconfig_free_pfx;
580118968Sume		}
581224144Shrs		pfx->pfx_preflifetime = (uint32_t)val64;
58255505Sshin
583118968Sume		makeentry(entbuf, sizeof(entbuf), i, "pltimedecr");
584118968Sume		if (agetflag(entbuf)) {
585253970Shrs			struct timespec now;
586253970Shrs
587253970Shrs			clock_gettime(CLOCK_MONOTONIC_FAST, &now);
588222732Shrs			pfx->pfx_pltimeexpire =
589222732Shrs			    now.tv_sec + pfx->pfx_preflifetime;
59055505Sshin		}
591222820Shrs		/* link into chain */
592222820Shrs		TAILQ_INSERT_TAIL(&rai->rai_prefix, pfx, pfx_next);
593222820Shrs		rai->rai_pfxs++;
594222972Shrs		continue;
595222972Shrsgetconfig_free_pfx:
596222972Shrs		free(pfx);
59755505Sshin	}
598222732Shrs	if (rai->rai_advifprefix && rai->rai_pfxs == 0)
599222732Shrs		get_prefix(rai);
60055505Sshin
601224144Shrs	MAYHAVE(val64, "mtu", 0);
602224144Shrs	if (val < 0 || val64 > 0xffffffff) {
60355505Sshin		syslog(LOG_ERR,
604224144Shrs		    "<%s> mtu (%" PRIu64 ") on %s out of range",
605224144Shrs		    __func__, val64, ifi->ifi_ifname);
606222972Shrs		goto getconfig_free_rai;
60755505Sshin	}
608224144Shrs	rai->rai_linkmtu = (uint32_t)val64;
609222732Shrs	if (rai->rai_linkmtu == 0) {
61055505Sshin		char *mtustr;
61155505Sshin
61255505Sshin		if ((mtustr = (char *)agetstr("mtu", &bp)) &&
61355505Sshin		    strcmp(mtustr, "auto") == 0)
614224144Shrs			rai->rai_linkmtu = ifi->ifi_phymtu;
61555505Sshin	}
616222732Shrs	else if (rai->rai_linkmtu < IPV6_MMTU ||
617224144Shrs	    rai->rai_linkmtu > ifi->ifi_phymtu) {
61855505Sshin		syslog(LOG_ERR,
619224144Shrs		    "<%s> advertised link mtu (%" PRIu32 ") on %s is invalid (must "
620222732Shrs		    "be between least MTU (%d) and physical link MTU (%d)",
621224144Shrs		    __func__, rai->rai_linkmtu, ifi->ifi_ifname,
622224144Shrs		    IPV6_MMTU, ifi->ifi_phymtu);
623222972Shrs		goto getconfig_free_rai;
62455505Sshin	}
62555505Sshin
626151468Ssuz#ifdef SIOCSIFINFO_IN6
627151468Ssuz	{
628151468Ssuz		struct in6_ndireq ndi;
629151468Ssuz		int s;
630151468Ssuz
631151468Ssuz		if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
632151468Ssuz			syslog(LOG_ERR, "<%s> socket: %s", __func__,
633222732Shrs			    strerror(errno));
634151468Ssuz			exit(1);
635151468Ssuz		}
636151468Ssuz		memset(&ndi, 0, sizeof(ndi));
637224144Shrs		strncpy(ndi.ifname, ifi->ifi_ifname, sizeof(ndi.ifname));
638222732Shrs		if (ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&ndi) < 0)
639151468Ssuz			syslog(LOG_INFO, "<%s> ioctl:SIOCGIFINFO_IN6 at %s: %s",
640224144Shrs			    __func__, ifi->ifi_ifname, strerror(errno));
641151468Ssuz
642151468Ssuz		/* reflect the RA info to the host variables in kernel */
643222732Shrs		ndi.ndi.chlim = rai->rai_hoplimit;
644222732Shrs		ndi.ndi.retrans = rai->rai_retranstimer;
645222732Shrs		ndi.ndi.basereachable = rai->rai_reachabletime;
646222732Shrs		if (ioctl(s, SIOCSIFINFO_IN6, (caddr_t)&ndi) < 0)
647151468Ssuz			syslog(LOG_INFO, "<%s> ioctl:SIOCSIFINFO_IN6 at %s: %s",
648224144Shrs			    __func__, ifi->ifi_ifname, strerror(errno));
649222732Shrs
650151468Ssuz		close(s);
651151468Ssuz	}
652151468Ssuz#endif
653151468Ssuz
65478064Sume	/* route information */
655222732Shrs	rai->rai_routes = 0;
656118968Sume	for (i = -1; i < MAXROUTE; i++) {
65778064Sume		struct rtinfo *rti;
65878064Sume
659118968Sume		makeentry(entbuf, sizeof(entbuf), i, "rtprefix");
660118968Sume		addr = (char *)agetstr(entbuf, &bp);
661118968Sume		if (addr == NULL) {
662118968Sume			makeentry(oentbuf, sizeof(oentbuf), i, "rtrprefix");
663118968Sume			addr = (char *)agetstr(oentbuf, &bp);
664222732Shrs			if (addr)
665118968Sume				fprintf(stderr, "%s was obsoleted.  Use %s.\n",
666222732Shrs				    oentbuf, entbuf);
667118968Sume		}
668118968Sume		if (addr == NULL)
669118968Sume			continue;
670118968Sume
67178064Sume		/* allocate memory to store prefix information */
672222732Shrs		ELM_MALLOC(rti, exit(1));
67378064Sume
674222732Shrs		if (inet_pton(AF_INET6, addr, &rti->rti_prefix) != 1) {
675118968Sume			syslog(LOG_ERR, "<%s> inet_pton failed for %s",
676222732Shrs			    __func__, addr);
677222972Shrs			goto getconfig_free_rti;
67878064Sume		}
67978064Sume#if 0
68078064Sume		/*
68178064Sume		 * XXX: currently there's no restriction in route information
682118968Sume		 * prefix according to
683118968Sume		 * draft-ietf-ipngwg-router-selection-00.txt.
684118968Sume		 * However, I think the similar restriction be necessary.
68578064Sume		 */
68678064Sume		MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME);
68778064Sume		if (IN6_IS_ADDR_MULTICAST(&rti->prefix)) {
68878064Sume			syslog(LOG_ERR,
689222732Shrs			    "<%s> multicast route (%s) must "
690222732Shrs			    "not be advertised on %s",
691224144Shrs			    __func__, addr, ifi->ifi_ifname);
692222972Shrs			goto getconfig_free_rti;
69378064Sume		}
69478064Sume		if (IN6_IS_ADDR_LINKLOCAL(&rti->prefix)) {
69578064Sume			syslog(LOG_NOTICE,
696222732Shrs			    "<%s> link-local route (%s) will "
697222732Shrs			    "be advertised on %s",
698224144Shrs			    __func__, addr, ifi->ifi_ifname);
699222972Shrs			goto getconfig_free_rti;
70078064Sume		}
70178064Sume#endif
702118968Sume
703118968Sume		makeentry(entbuf, sizeof(entbuf), i, "rtplen");
704118968Sume		/* XXX: 256 is a magic number for compatibility check. */
705118968Sume		MAYHAVE(val, entbuf, 256);
706118968Sume		if (val == 256) {
707118968Sume			makeentry(oentbuf, sizeof(oentbuf), i, "rtrplen");
708118968Sume			MAYHAVE(val, oentbuf, 256);
709222732Shrs			if (val != 256)
710118968Sume				fprintf(stderr, "%s was obsoleted.  Use %s.\n",
711222732Shrs				    oentbuf, entbuf);
712222732Shrs			else
713118968Sume				val = 64;
714118968Sume		}
715118968Sume		if (val < 0 || val > 128) {
716224144Shrs			syslog(LOG_ERR, "<%s> prefixlen (%" PRIu32 ") for %s on %s "
717222732Shrs			    "out of range",
718224144Shrs			    __func__, val, addr, ifi->ifi_ifname);
719222972Shrs			goto getconfig_free_rti;
720118968Sume		}
721222732Shrs		rti->rti_prefixlen = (int)val;
722118968Sume
723118968Sume		makeentry(entbuf, sizeof(entbuf), i, "rtflags");
724118968Sume		if ((flagstr = (char *)agetstr(entbuf, &bp))) {
725118968Sume			val = 0;
726118968Sume			if (strchr(flagstr, 'h'))
727118968Sume				val |= ND_RA_FLAG_RTPREF_HIGH;
728118968Sume			if (strchr(flagstr, 'l')) {
729118968Sume				if ((val & ND_RA_FLAG_RTPREF_HIGH)) {
730118968Sume					syslog(LOG_ERR,
731118968Sume					    "<%s> the \'h\' and \'l\' route"
732118968Sume					    " preferences are exclusive",
733118968Sume					    __func__);
734222972Shrs					goto getconfig_free_rti;
735118968Sume				}
736118968Sume				val |= ND_RA_FLAG_RTPREF_LOW;
737118968Sume			}
738118968Sume		} else
739118968Sume			MAYHAVE(val, entbuf, 256); /* XXX */
740118968Sume		if (val == 256) {
741118968Sume			makeentry(oentbuf, sizeof(oentbuf), i, "rtrflags");
742118968Sume			MAYHAVE(val, oentbuf, 256);
743118968Sume			if (val != 256) {
744118968Sume				fprintf(stderr, "%s was obsoleted.  Use %s.\n",
745222732Shrs				    oentbuf, entbuf);
746118968Sume			} else
747118968Sume				val = 0;
748118968Sume		}
749222732Shrs		rti->rti_rtpref = val & ND_RA_FLAG_RTPREF_MASK;
750222732Shrs		if (rti->rti_rtpref == ND_RA_FLAG_RTPREF_RSV) {
751118968Sume			syslog(LOG_ERR, "<%s> invalid route preference (%02x) "
752222732Shrs			    "for %s/%d on %s",
753222732Shrs			    __func__, rti->rti_rtpref, addr,
754224144Shrs			    rti->rti_prefixlen, ifi->ifi_ifname);
755222972Shrs			goto getconfig_free_rti;
756118968Sume		}
757118968Sume
758118968Sume		/*
759118968Sume		 * Since the spec does not a default value, we should make
760118968Sume		 * this entry mandatory.  However, FreeBSD 4.4 has shipped
761118968Sume		 * with this field being optional, we use the router lifetime
762118968Sume		 * as an ad-hoc default value with a warning message.
763118968Sume		 */
764118968Sume		makeentry(entbuf, sizeof(entbuf), i, "rtltime");
765118968Sume		MAYHAVE(val64, entbuf, -1);
766118968Sume		if (val64 == -1) {
767118968Sume			makeentry(oentbuf, sizeof(oentbuf), i, "rtrltime");
768118968Sume			MAYHAVE(val64, oentbuf, -1);
769222732Shrs			if (val64 != -1)
770118968Sume				fprintf(stderr, "%s was obsoleted.  Use %s.\n",
771222732Shrs				    oentbuf, entbuf);
772222732Shrs			else {
773118968Sume				fprintf(stderr, "%s should be specified "
774224144Shrs				    "for interface %s.\n", entbuf,
775224144Shrs				    ifi->ifi_ifname);
776222732Shrs				val64 = rai->rai_lifetime;
777118968Sume			}
778118968Sume		}
779118968Sume		if (val64 < 0 || val64 > 0xffffffff) {
780224144Shrs			syslog(LOG_ERR, "<%s> route lifetime (%" PRIu64 ") for "
781118968Sume			    "%s/%d on %s out of range", __func__,
782224144Shrs			    val64, addr, rti->rti_prefixlen,
783224144Shrs			    ifi->ifi_ifname);
784222972Shrs			goto getconfig_free_rti;
785118968Sume		}
786224144Shrs		rti->rti_ltime = (uint32_t)val64;
787222972Shrs
788222972Shrs		/* link into chain */
789222972Shrs		TAILQ_INSERT_TAIL(&rai->rai_route, rti, rti_next);
790222972Shrs		rai->rai_routes++;
791222972Shrs		continue;
792222972Shrsgetconfig_free_rti:
793222972Shrs		free(rti);
79478064Sume	}
795224144Shrs
796222732Shrs	/* DNS server and DNS search list information */
797222732Shrs	for (i = -1; i < MAXRDNSSENT ; i++) {
798222732Shrs		struct rdnss *rdn;
799222732Shrs		struct rdnss_addr *rdna;
800222732Shrs		char *ap;
801222732Shrs		int c;
80278064Sume
803222732Shrs		makeentry(entbuf, sizeof(entbuf), i, "rdnss");
804222732Shrs		addr = (char *)agetstr(entbuf, &bp);
805222732Shrs		if (addr == NULL)
806222732Shrs			break;
807222732Shrs		ELM_MALLOC(rdn, exit(1));
80855505Sshin
809222732Shrs		TAILQ_INIT(&rdn->rd_list);
810222732Shrs
811222732Shrs		for (ap = addr; ap - addr < (ssize_t)strlen(addr); ap += c+1) {
812222732Shrs			c = strcspn(ap, ",");
813222732Shrs			strncpy(abuf, ap, c);
814222732Shrs			abuf[c] = '\0';
815222972Shrs			ELM_MALLOC(rdna, goto getconfig_free_rdn);
816222732Shrs			if (inet_pton(AF_INET6, abuf, &rdna->ra_dns) != 1) {
817222732Shrs				syslog(LOG_ERR, "<%s> inet_pton failed for %s",
818222732Shrs				    __func__, abuf);
819222732Shrs				free(rdna);
820222972Shrs				goto getconfig_free_rdn;
821222732Shrs			}
822222732Shrs			TAILQ_INSERT_TAIL(&rdn->rd_list, rdna, ra_next);
823222732Shrs		}
824222732Shrs
825222732Shrs		makeentry(entbuf, sizeof(entbuf), i, "rdnssltime");
826222732Shrs		MAYHAVE(val, entbuf, (rai->rai_maxinterval * 3 / 2));
827224144Shrs		if ((uint16_t)val < rai->rai_maxinterval ||
828224144Shrs		    (uint16_t)val > rai->rai_maxinterval * 2) {
829224144Shrs			syslog(LOG_ERR, "%s (%" PRIu16 ") on %s is invalid "
830222732Shrs			    "(must be between %d and %d)",
831224144Shrs			    entbuf, val, ifi->ifi_ifname, rai->rai_maxinterval,
832222732Shrs			    rai->rai_maxinterval * 2);
833222972Shrs			goto getconfig_free_rdn;
834222732Shrs		}
835222732Shrs		rdn->rd_ltime = val;
836222732Shrs
837222732Shrs		/* link into chain */
838222732Shrs		TAILQ_INSERT_TAIL(&rai->rai_rdnss, rdn, rd_next);
839222972Shrs		continue;
840222972Shrsgetconfig_free_rdn:
841222972Shrs		while ((rdna = TAILQ_FIRST(&rdn->rd_list)) != NULL) {
842222972Shrs			TAILQ_REMOVE(&rdn->rd_list, rdna, ra_next);
843222972Shrs			free(rdna);
844222972Shrs		}
845222972Shrs		free(rdn);
846222732Shrs	}
847222732Shrs
848222732Shrs	for (i = -1; i < MAXDNSSLENT ; i++) {
849222732Shrs		struct dnssl *dns;
850222732Shrs		struct dnssl_addr *dnsa;
851222732Shrs		char *ap;
852222732Shrs		int c;
853222732Shrs
854222732Shrs		makeentry(entbuf, sizeof(entbuf), i, "dnssl");
855222732Shrs		addr = (char *)agetstr(entbuf, &bp);
856222732Shrs		if (addr == NULL)
857222732Shrs			break;
858222732Shrs
859222732Shrs		ELM_MALLOC(dns, exit(1));
860222732Shrs
861222732Shrs		TAILQ_INIT(&dns->dn_list);
862222732Shrs
863222732Shrs		for (ap = addr; ap - addr < (ssize_t)strlen(addr); ap += c+1) {
864222732Shrs			c = strcspn(ap, ",");
865222732Shrs			strncpy(abuf, ap, c);
866222732Shrs			abuf[c] = '\0';
867222972Shrs			ELM_MALLOC(dnsa, goto getconfig_free_dns);
868222732Shrs			dnsa->da_len = dname_labelenc(dnsa->da_dom, abuf);
869222732Shrs			syslog(LOG_DEBUG, "<%s>: dnsa->da_len = %d", __func__,
870222732Shrs			    dnsa->da_len);
871222732Shrs			TAILQ_INSERT_TAIL(&dns->dn_list, dnsa, da_next);
872222732Shrs		}
873222732Shrs
874222732Shrs		makeentry(entbuf, sizeof(entbuf), i, "dnsslltime");
875222732Shrs		MAYHAVE(val, entbuf, (rai->rai_maxinterval * 3 / 2));
876224144Shrs		if ((uint16_t)val < rai->rai_maxinterval ||
877224144Shrs		    (uint16_t)val > rai->rai_maxinterval * 2) {
878224144Shrs			syslog(LOG_ERR, "%s (%" PRIu16 ") on %s is invalid "
879222732Shrs			    "(must be between %d and %d)",
880224144Shrs			    entbuf, val, ifi->ifi_ifname, rai->rai_maxinterval,
881222732Shrs			    rai->rai_maxinterval * 2);
882222972Shrs			goto getconfig_free_dns;
883222732Shrs		}
884222732Shrs		dns->dn_ltime = val;
885222732Shrs
886222732Shrs		/* link into chain */
887222732Shrs		TAILQ_INSERT_TAIL(&rai->rai_dnssl, dns, dn_next);
888222972Shrs		continue;
889222972Shrsgetconfig_free_dns:
890222972Shrs		while ((dnsa = TAILQ_FIRST(&dns->dn_list)) != NULL) {
891222972Shrs			TAILQ_REMOVE(&dns->dn_list, dnsa, da_next);
892222972Shrs			free(dnsa);
893222972Shrs		}
894222972Shrs		free(dns);
895222732Shrs	}
89655505Sshin	/* construct the sending packet */
897222732Shrs	make_packet(rai);
898222972Shrs
899222972Shrs	/*
900222972Shrs	 * If an entry with the same ifindex exists, remove it first.
901222972Shrs	 * Before the removal, RDNSS and DNSSL options with
902222972Shrs	 * zero-lifetime will be sent.
903222972Shrs	 */
904224144Shrs	switch (ifi->ifi_state) {
905224144Shrs	case IFI_STATE_UNCONFIGURED:
906224144Shrs		/* UNCONFIGURED -> TRANSITIVE */
907222972Shrs
908224144Shrs		error = sock_mc_join(&sock, ifi->ifi_ifindex);
909224144Shrs		if (error)
910224144Shrs			exit(1);
911222972Shrs
912224144Shrs		ifi->ifi_state = IFI_STATE_TRANSITIVE;
913224144Shrs		ifi->ifi_burstcount = MAX_INITIAL_RTR_ADVERTISEMENTS;
914224144Shrs		ifi->ifi_burstinterval = MAX_INITIAL_RTR_ADVERT_INTERVAL;
915224144Shrs
916224144Shrs		/* The same two rai mean initial burst */
917224144Shrs		ifi->ifi_rainfo = rai;
918224144Shrs		ifi->ifi_rainfo_trans = rai;
919224144Shrs		TAILQ_INSERT_TAIL(&railist, rai, rai_next);
920224144Shrs
921224144Shrs		if (ifi->ifi_ra_timer == NULL)
922224144Shrs			ifi->ifi_ra_timer = rtadvd_add_timer(ra_timeout,
923224144Shrs			    ra_timer_update, ifi, ifi);
924224144Shrs		ra_timer_update(ifi, &ifi->ifi_ra_timer->rat_tm);
925224144Shrs		rtadvd_set_timer(&ifi->ifi_ra_timer->rat_tm,
926224144Shrs		    ifi->ifi_ra_timer);
927224144Shrs
928224144Shrs		syslog(LOG_DEBUG,
929224144Shrs		    "<%s> ifname=%s marked as TRANSITIVE (initial burst).",
930224144Shrs		    __func__, ifi->ifi_ifname);
931224144Shrs		break;
932224144Shrs	case IFI_STATE_CONFIGURED:
933224144Shrs		/* CONFIGURED -> TRANSITIVE */
934224144Shrs		rai_old = ifi->ifi_rainfo;
935224144Shrs		if (rai_old == NULL) {
936224144Shrs			syslog(LOG_ERR,
937224144Shrs			    "<%s> ifi_rainfo is NULL"
938224144Shrs			    " in IFI_STATE_CONFIGURED.", __func__);
939224144Shrs			ifi = NULL;
940224144Shrs			break;
941224144Shrs		} else {
942224144Shrs			struct rdnss *rdn;
943224144Shrs			struct dnssl *dns;
944224144Shrs
945224144Shrs			rai_old->rai_lifetime = 0;
946224144Shrs			TAILQ_FOREACH(rdn, &rai_old->rai_rdnss, rd_next)
947224144Shrs			    rdn->rd_ltime = 0;
948224144Shrs			TAILQ_FOREACH(dns, &rai_old->rai_dnssl, dn_next)
949224144Shrs			    dns->dn_ltime = 0;
950224144Shrs
951224144Shrs			ifi->ifi_rainfo_trans = rai_old;
952224144Shrs			ifi->ifi_state = IFI_STATE_TRANSITIVE;
953224144Shrs			ifi->ifi_burstcount = MAX_FINAL_RTR_ADVERTISEMENTS;
954224144Shrs			ifi->ifi_burstinterval = MIN_DELAY_BETWEEN_RAS;
955224144Shrs
956224144Shrs			ra_timer_update(ifi, &ifi->ifi_ra_timer->rat_tm);
957224144Shrs			rtadvd_set_timer(&ifi->ifi_ra_timer->rat_tm,
958224144Shrs			    ifi->ifi_ra_timer);
959224144Shrs
960224144Shrs			syslog(LOG_DEBUG,
961224144Shrs			    "<%s> ifname=%s marked as TRANSITIVE"
962224144Shrs			    " (transitional burst)",
963224144Shrs			    __func__, ifi->ifi_ifname);
964222972Shrs		}
965224144Shrs		ifi->ifi_rainfo = rai;
966224144Shrs		TAILQ_INSERT_TAIL(&railist, rai, rai_next);
967224144Shrs		break;
968224144Shrs	case IFI_STATE_TRANSITIVE:
969224144Shrs		if (ifi->ifi_rainfo != NULL) {
970224144Shrs			if (ifi->ifi_rainfo == ifi->ifi_rainfo_trans) {
971224144Shrs				/* Reinitialize initial burst */
972224144Shrs				rm_rainfo(ifi->ifi_rainfo);
973224144Shrs				ifi->ifi_rainfo = rai;
974224144Shrs				ifi->ifi_rainfo_trans = rai;
975224144Shrs				ifi->ifi_burstcount =
976224144Shrs				    MAX_INITIAL_RTR_ADVERTISEMENTS;
977224144Shrs				ifi->ifi_burstinterval =
978224144Shrs				    MAX_INITIAL_RTR_ADVERT_INTERVAL;
979224144Shrs			} else {
980224144Shrs				/* Replace ifi_rainfo with the new one */
981224144Shrs				rm_rainfo(ifi->ifi_rainfo);
982224144Shrs				ifi->ifi_rainfo = rai;
983224144Shrs			}
984224144Shrs			TAILQ_INSERT_TAIL(&railist, rai, rai_next);
985224144Shrs
986224144Shrs			ra_timer_update(ifi, &ifi->ifi_ra_timer->rat_tm);
987224144Shrs			rtadvd_set_timer(&ifi->ifi_ra_timer->rat_tm,
988224144Shrs			    ifi->ifi_ra_timer);
989224144Shrs		} else {
990224144Shrs			/* XXX: NOTREACHED.  Being shut down. */
991224144Shrs			syslog(LOG_ERR,
992224144Shrs			    "<%s> %s is shutting down.  Skipped.",
993224144Shrs			    __func__, ifi->ifi_ifname);
994224144Shrs			rm_rainfo(rai);
995224144Shrs
996224144Shrs			return (NULL);
997224144Shrs		}
998224144Shrs		break;
999222972Shrs	}
100055505Sshin
1001224144Shrs	return (ifi);
1002222732Shrs
1003222972Shrsgetconfig_free_rai:
1004222972Shrs	free(rai);
1005224144Shrs	return (NULL);
100655505Sshin}
100755505Sshin
1008118968Sumevoid
100955505Sshinget_prefix(struct rainfo *rai)
101055505Sshin{
101178064Sume	struct ifaddrs *ifap, *ifa;
1012222732Shrs	struct prefix *pfx;
101378064Sume	struct in6_addr *a;
1014224144Shrs	struct ifinfo *ifi;
1015224144Shrs	char *p, *ep, *m, *lim;
1016224144Shrs	char ntopbuf[INET6_ADDRSTRLEN];
101755505Sshin
101878064Sume	if (getifaddrs(&ifap) < 0) {
101955505Sshin		syslog(LOG_ERR,
1020222732Shrs		    "<%s> can't get interface addresses",
1021222732Shrs		    __func__);
102255505Sshin		exit(1);
102355505Sshin	}
1024224144Shrs	ifi = rai->rai_ifinfo;
1025118664Sume
102678064Sume	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
102797719Sume		int plen;
102897719Sume
1029224144Shrs		if (strcmp(ifa->ifa_name, ifi->ifi_ifname) != 0)
103078064Sume			continue;
103178064Sume		if (ifa->ifa_addr->sa_family != AF_INET6)
103278064Sume			continue;
103378064Sume		a = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
103478064Sume		if (IN6_IS_ADDR_LINKLOCAL(a))
103578064Sume			continue;
1036224144Shrs
103797719Sume		/* get prefix length */
1038224144Shrs		m = (char *)&((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr;
1039224144Shrs		lim = (char *)(ifa->ifa_netmask) + ifa->ifa_netmask->sa_len;
104097719Sume		plen = prefixlen(m, lim);
1041118968Sume		if (plen <= 0 || plen > 128) {
104297719Sume			syslog(LOG_ERR, "<%s> failed to get prefixlen "
1043222732Shrs			    "or prefix is invalid",
1044222732Shrs			    __func__);
104597719Sume			exit(1);
104697719Sume		}
1047118968Sume		if (plen == 128)	/* XXX */
1048118968Sume			continue;
104997719Sume		if (find_prefix(rai, a, plen)) {
105097719Sume			/* ignore a duplicated prefix. */
105197719Sume			continue;
105297719Sume		}
105397719Sume
105455505Sshin		/* allocate memory to store prefix info. */
1055222732Shrs		ELM_MALLOC(pfx, exit(1));
105655505Sshin
105778064Sume		/* set prefix, sweep bits outside of prefixlen */
1058222732Shrs		pfx->pfx_prefixlen = plen;
1059222732Shrs		memcpy(&pfx->pfx_prefix, a, sizeof(*a));
1060224144Shrs		p = (char *)&pfx->pfx_prefix;
1061224144Shrs		ep = (char *)(&pfx->pfx_prefix + 1);
1062157047Ssuz		while (m < lim && p < ep)
106378064Sume			*p++ &= *m++;
106478064Sume		while (p < ep)
106578064Sume			*p++ = 0x00;
1066222732Shrs	        if (!inet_ntop(AF_INET6, &pfx->pfx_prefix, ntopbuf,
106778064Sume	            sizeof(ntopbuf))) {
1068118660Sume			syslog(LOG_ERR, "<%s> inet_ntop failed", __func__);
106978064Sume			exit(1);
107078064Sume		}
107155505Sshin		syslog(LOG_DEBUG,
1072222732Shrs		    "<%s> add %s/%d to prefix list on %s",
1073224144Shrs		    __func__, ntopbuf, pfx->pfx_prefixlen, ifi->ifi_ifname);
107455505Sshin
107555505Sshin		/* set other fields with protocol defaults */
1076222732Shrs		pfx->pfx_validlifetime = DEF_ADVVALIDLIFETIME;
1077222732Shrs		pfx->pfx_preflifetime = DEF_ADVPREFERREDLIFETIME;
1078222732Shrs		pfx->pfx_onlinkflg = 1;
1079222732Shrs		pfx->pfx_autoconfflg = 1;
1080222732Shrs		pfx->pfx_origin = PREFIX_FROM_KERNEL;
1081222732Shrs		pfx->pfx_rainfo = rai;
108255505Sshin
108355505Sshin		/* link into chain */
1084222732Shrs		TAILQ_INSERT_TAIL(&rai->rai_prefix, pfx, pfx_next);
108555505Sshin
108655505Sshin		/* counter increment */
1087222732Shrs		rai->rai_pfxs++;
108855505Sshin	}
108955505Sshin
109078064Sume	freeifaddrs(ifap);
109155505Sshin}
109255505Sshin
109355505Sshinstatic void
1094222732Shrsmakeentry(char *buf, size_t len, int id, const char *string)
109555505Sshin{
109697709Sume
1097118968Sume	if (id < 0)
1098118968Sume		strlcpy(buf, string, len);
1099118968Sume	else
1100118968Sume		snprintf(buf, len, "%s%d", string, id);
110155505Sshin}
110255505Sshin
110355505Sshin/*
110455505Sshin * Add a prefix to the list of specified interface and reconstruct
110555505Sshin * the outgoing packet.
110655505Sshin * The prefix must not be in the list.
1107152538Ssuz * XXX: other parameters of the prefix (e.g. lifetime) should be
110855505Sshin * able to be specified.
110955505Sshin */
111055505Sshinstatic void
111155505Sshinadd_prefix(struct rainfo *rai, struct in6_prefixreq *ipr)
111255505Sshin{
1113222732Shrs	struct prefix *pfx;
1114224144Shrs	struct ifinfo *ifi;
1115224144Shrs	char ntopbuf[INET6_ADDRSTRLEN];
111655505Sshin
1117224144Shrs	ifi = rai->rai_ifinfo;
1118222732Shrs	ELM_MALLOC(pfx, return);
1119222732Shrs	pfx->pfx_prefix = ipr->ipr_prefix.sin6_addr;
1120222732Shrs	pfx->pfx_prefixlen = ipr->ipr_plen;
1121222732Shrs	pfx->pfx_validlifetime = ipr->ipr_vltime;
1122222732Shrs	pfx->pfx_preflifetime = ipr->ipr_pltime;
1123222732Shrs	pfx->pfx_onlinkflg = ipr->ipr_raf_onlink;
1124222732Shrs	pfx->pfx_autoconfflg = ipr->ipr_raf_auto;
1125222732Shrs	pfx->pfx_origin = PREFIX_FROM_DYNAMIC;
1126254684Sdes	pfx->pfx_rainfo = rai;
112755505Sshin
1128222732Shrs	TAILQ_INSERT_TAIL(&rai->rai_prefix, pfx, pfx_next);
112955505Sshin
113055505Sshin	syslog(LOG_DEBUG, "<%s> new prefix %s/%d was added on %s",
1131222732Shrs	    __func__,
1132222732Shrs	    inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr, ntopbuf,
1133224144Shrs		sizeof(ntopbuf)), ipr->ipr_plen, ifi->ifi_ifname);
113455505Sshin
1135222732Shrs	rai->rai_pfxs++;
113655505Sshin}
113755505Sshin
113855505Sshin/*
113955505Sshin * Delete a prefix to the list of specified interface and reconstruct
114055505Sshin * the outgoing packet.
114162656Skris * The prefix must be in the list.
114255505Sshin */
114355505Sshinvoid
1144222732Shrsdelete_prefix(struct prefix *pfx)
114555505Sshin{
1146222732Shrs	struct rainfo *rai;
1147224144Shrs	struct ifinfo *ifi;
1148224144Shrs	char ntopbuf[INET6_ADDRSTRLEN];
114955505Sshin
1150222732Shrs	rai = pfx->pfx_rainfo;
1151224144Shrs	ifi = rai->rai_ifinfo;
1152222732Shrs	TAILQ_REMOVE(&rai->rai_prefix, pfx, pfx_next);
115355505Sshin	syslog(LOG_DEBUG, "<%s> prefix %s/%d was deleted on %s",
1154222732Shrs	    __func__,
1155222732Shrs	    inet_ntop(AF_INET6, &pfx->pfx_prefix, ntopbuf,
1156224144Shrs		sizeof(ntopbuf)), pfx->pfx_prefixlen, ifi->ifi_ifname);
1157222732Shrs	if (pfx->pfx_timer)
1158222732Shrs		rtadvd_remove_timer(pfx->pfx_timer);
1159222732Shrs	free(pfx);
1160224144Shrs
1161222732Shrs	rai->rai_pfxs--;
116255505Sshin}
116355505Sshin
116498172Sumevoid
1165222732Shrsinvalidate_prefix(struct prefix *pfx)
116698172Sume{
1167253970Shrs	struct timespec timo;
1168222732Shrs	struct rainfo *rai;
1169224144Shrs	struct ifinfo *ifi;
1170224144Shrs	char ntopbuf[INET6_ADDRSTRLEN];
117198172Sume
1172222732Shrs	rai = pfx->pfx_rainfo;
1173224144Shrs	ifi = rai->rai_ifinfo;
1174222732Shrs	if (pfx->pfx_timer) {	/* sanity check */
117598172Sume		syslog(LOG_ERR,
117698172Sume		    "<%s> assumption failure: timer already exists",
1177118660Sume		    __func__);
117898172Sume		exit(1);
117998172Sume	}
118098172Sume
118198172Sume	syslog(LOG_DEBUG, "<%s> prefix %s/%d was invalidated on %s, "
1182118660Sume	    "will expire in %ld seconds", __func__,
1183222732Shrs	    inet_ntop(AF_INET6, &pfx->pfx_prefix, ntopbuf, sizeof(ntopbuf)),
1184224144Shrs	    pfx->pfx_prefixlen, ifi->ifi_ifname, (long)prefix_timo);
118598172Sume
118698172Sume	/* set the expiration timer */
1187222732Shrs	pfx->pfx_timer = rtadvd_add_timer(prefix_timeout, NULL, pfx, NULL);
1188222732Shrs	if (pfx->pfx_timer == NULL) {
118998172Sume		syslog(LOG_ERR, "<%s> failed to add a timer for a prefix. "
1190118660Sume		    "remove the prefix", __func__);
1191222732Shrs		delete_prefix(pfx);
119298172Sume	}
119398172Sume	timo.tv_sec = prefix_timo;
1194253970Shrs	timo.tv_nsec = 0;
1195222732Shrs	rtadvd_set_timer(&timo, pfx->pfx_timer);
119698172Sume}
119798172Sume
119898172Sumestatic struct rtadvd_timer *
119998172Sumeprefix_timeout(void *arg)
120098172Sume{
120198172Sume
1202222732Shrs	delete_prefix((struct prefix *)arg);
1203222732Shrs
1204222732Shrs	return (NULL);
120598172Sume}
120698172Sume
120798172Sumevoid
1208222732Shrsupdate_prefix(struct prefix *pfx)
120998172Sume{
1210222732Shrs	struct rainfo *rai;
1211224144Shrs	struct ifinfo *ifi;
1212224144Shrs	char ntopbuf[INET6_ADDRSTRLEN];
121398172Sume
1214222732Shrs	rai = pfx->pfx_rainfo;
1215224144Shrs	ifi = rai->rai_ifinfo;
1216222732Shrs	if (pfx->pfx_timer == NULL) { /* sanity check */
121798172Sume		syslog(LOG_ERR,
121898172Sume		    "<%s> assumption failure: timer does not exist",
1219118660Sume		    __func__);
122098172Sume		exit(1);
122198172Sume	}
122298172Sume
122398172Sume	syslog(LOG_DEBUG, "<%s> prefix %s/%d was re-enabled on %s",
1224222732Shrs	    __func__, inet_ntop(AF_INET6, &pfx->pfx_prefix, ntopbuf,
1225224144Shrs		sizeof(ntopbuf)), pfx->pfx_prefixlen, ifi->ifi_ifname);
122698172Sume
122798172Sume	/* stop the expiration timer */
1228222732Shrs	rtadvd_remove_timer(pfx->pfx_timer);
1229222732Shrs	pfx->pfx_timer = NULL;
123098172Sume}
123198172Sume
123255505Sshin/*
123355505Sshin * Try to get an in6_prefixreq contents for a prefix which matches
123455505Sshin * ipr->ipr_prefix and ipr->ipr_plen and belongs to
123555505Sshin * the interface whose name is ipr->ipr_name[].
123655505Sshin */
123755505Sshinstatic int
123855505Sshininit_prefix(struct in6_prefixreq *ipr)
123955505Sshin{
124098262Sume#if 0
124155505Sshin	int s;
124255505Sshin
124355505Sshin	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
1244118660Sume		syslog(LOG_ERR, "<%s> socket: %s", __func__,
1245222732Shrs		    strerror(errno));
124655505Sshin		exit(1);
124755505Sshin	}
124855505Sshin
124955505Sshin	if (ioctl(s, SIOCGIFPREFIX_IN6, (caddr_t)ipr) < 0) {
1250118660Sume		syslog(LOG_INFO, "<%s> ioctl:SIOCGIFPREFIX %s", __func__,
1251222732Shrs		    strerror(errno));
125255505Sshin
125355505Sshin		ipr->ipr_vltime = DEF_ADVVALIDLIFETIME;
125455505Sshin		ipr->ipr_pltime = DEF_ADVPREFERREDLIFETIME;
125555505Sshin		ipr->ipr_raf_onlink = 1;
125655505Sshin		ipr->ipr_raf_auto = 1;
125755505Sshin		/* omit other field initialization */
125855505Sshin	}
125955505Sshin	else if (ipr->ipr_origin < PR_ORIG_RR) {
1260224144Shrs		char ntopbuf[INET6_ADDRSTRLEN];
126155505Sshin
126255505Sshin		syslog(LOG_WARNING, "<%s> Added prefix(%s)'s origin %d is"
1263222732Shrs		    "lower than PR_ORIG_RR(router renumbering)."
1264222732Shrs		    "This should not happen if I am router", __func__,
1265222732Shrs		    inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr, ntopbuf,
1266222732Shrs			sizeof(ntopbuf)), ipr->ipr_origin);
126757852Sshin		close(s);
1268222732Shrs		return (1);
126955505Sshin	}
127055505Sshin
127155505Sshin	close(s);
1272222732Shrs	return (0);
127398262Sume#else
127498262Sume	ipr->ipr_vltime = DEF_ADVVALIDLIFETIME;
127598262Sume	ipr->ipr_pltime = DEF_ADVPREFERREDLIFETIME;
127698262Sume	ipr->ipr_raf_onlink = 1;
127798262Sume	ipr->ipr_raf_auto = 1;
1278222732Shrs	return (0);
127998262Sume#endif
128055505Sshin}
128155505Sshin
128255505Sshinvoid
128355505Sshinmake_prefix(struct rainfo *rai, int ifindex, struct in6_addr *addr, int plen)
128455505Sshin{
128555505Sshin	struct in6_prefixreq ipr;
128655505Sshin
128755505Sshin	memset(&ipr, 0, sizeof(ipr));
128855505Sshin	if (if_indextoname(ifindex, ipr.ipr_name) == NULL) {
1289253057Shrs		syslog(LOG_ERR, "<%s> Prefix added interface No.%d doesn't "
1290222732Shrs		    "exist. This should not happen! %s", __func__,
1291222732Shrs		    ifindex, strerror(errno));
129255505Sshin		exit(1);
129355505Sshin	}
129455505Sshin	ipr.ipr_prefix.sin6_len = sizeof(ipr.ipr_prefix);
129555505Sshin	ipr.ipr_prefix.sin6_family = AF_INET6;
129655505Sshin	ipr.ipr_prefix.sin6_addr = *addr;
129755505Sshin	ipr.ipr_plen = plen;
129855505Sshin
129955505Sshin	if (init_prefix(&ipr))
130055505Sshin		return; /* init failed by some error */
130155505Sshin	add_prefix(rai, &ipr);
130255505Sshin}
130355505Sshin
130462656Skrisvoid
1305222732Shrsmake_packet(struct rainfo *rai)
130655505Sshin{
130755505Sshin	size_t packlen, lladdroptlen = 0;
130855505Sshin	char *buf;
130955505Sshin	struct nd_router_advert *ra;
131055505Sshin	struct nd_opt_prefix_info *ndopt_pi;
131155505Sshin	struct nd_opt_mtu *ndopt_mtu;
131278064Sume	struct nd_opt_route_info *ndopt_rti;
1313118968Sume	struct rtinfo *rti;
1314222732Shrs	struct nd_opt_rdnss *ndopt_rdnss;
1315222732Shrs	struct rdnss *rdn;
1316222732Shrs	struct nd_opt_dnssl *ndopt_dnssl;
1317222732Shrs	struct dnssl *dns;
1318222732Shrs	size_t len;
131955505Sshin	struct prefix *pfx;
1320224144Shrs	struct ifinfo *ifi;
132155505Sshin
1322224144Shrs	ifi = rai->rai_ifinfo;
132355505Sshin	/* calculate total length */
132455505Sshin	packlen = sizeof(struct nd_router_advert);
1325222732Shrs	if (rai->rai_advlinkopt) {
1326224144Shrs		if ((lladdroptlen = lladdropt_length(&ifi->ifi_sdl)) == 0) {
132755505Sshin			syslog(LOG_INFO,
1328222732Shrs			    "<%s> link-layer address option has"
1329222732Shrs			    " null length on %s.  Treat as not included.",
1330224144Shrs			    __func__, ifi->ifi_ifname);
1331222732Shrs			rai->rai_advlinkopt = 0;
133255505Sshin		}
133355505Sshin		packlen += lladdroptlen;
133455505Sshin	}
1335222732Shrs	if (rai->rai_pfxs)
1336222732Shrs		packlen += sizeof(struct nd_opt_prefix_info) * rai->rai_pfxs;
1337222732Shrs	if (rai->rai_linkmtu)
133855505Sshin		packlen += sizeof(struct nd_opt_mtu);
1339224144Shrs
1340222732Shrs	TAILQ_FOREACH(rti, &rai->rai_route, rti_next)
1341222732Shrs		packlen += sizeof(struct nd_opt_route_info) +
1342222732Shrs			   ((rti->rti_prefixlen + 0x3f) >> 6) * 8;
1343224144Shrs
1344222732Shrs	TAILQ_FOREACH(rdn, &rai->rai_rdnss, rd_next) {
1345222732Shrs		struct rdnss_addr *rdna;
134655505Sshin
1347222732Shrs		packlen += sizeof(struct nd_opt_rdnss);
1348222732Shrs		TAILQ_FOREACH(rdna, &rdn->rd_list, ra_next)
1349222732Shrs			packlen += sizeof(rdna->ra_dns);
1350222732Shrs	}
1351222732Shrs	TAILQ_FOREACH(dns, &rai->rai_dnssl, dn_next) {
1352222732Shrs		struct dnssl_addr *dnsa;
1353222732Shrs
1354222732Shrs		packlen += sizeof(struct nd_opt_dnssl);
1355222732Shrs		len = 0;
1356222732Shrs		TAILQ_FOREACH(dnsa, &dns->dn_list, da_next)
1357222732Shrs			len += dnsa->da_len;
1358222732Shrs
1359222732Shrs		/* A zero octet and 8 octet boundary */
1360222732Shrs		len++;
1361222732Shrs		len += (len % 8) ? 8 - len % 8 : 0;
1362222732Shrs
1363222732Shrs		packlen += len;
1364222732Shrs	}
136555505Sshin	/* allocate memory for the packet */
136655505Sshin	if ((buf = malloc(packlen)) == NULL) {
136755505Sshin		syslog(LOG_ERR,
1368222732Shrs		    "<%s> can't get enough memory for an RA packet",
1369222732Shrs		    __func__);
137055505Sshin		exit(1);
137155505Sshin	}
1372222732Shrs	memset(buf, 0, packlen);
1373222732Shrs	if (rai->rai_ra_data)	/* Free old data if any. */
1374222732Shrs		free(rai->rai_ra_data);
1375222732Shrs	rai->rai_ra_data = buf;
137655505Sshin	/* XXX: what if packlen > 576? */
1377222732Shrs	rai->rai_ra_datalen = packlen;
137855505Sshin
137955505Sshin	/*
138055505Sshin	 * construct the packet
138155505Sshin	 */
138255505Sshin	ra = (struct nd_router_advert *)buf;
138355505Sshin	ra->nd_ra_type = ND_ROUTER_ADVERT;
138455505Sshin	ra->nd_ra_code = 0;
138555505Sshin	ra->nd_ra_cksum = 0;
1386224144Shrs	ra->nd_ra_curhoplimit = (uint8_t)(0xff & rai->rai_hoplimit);
138778064Sume	ra->nd_ra_flags_reserved = 0; /* just in case */
138878064Sume	/*
138978064Sume	 * XXX: the router preference field, which is a 2-bit field, should be
139078064Sume	 * initialized before other fields.
139178064Sume	 */
1392222732Shrs	ra->nd_ra_flags_reserved = 0xff & rai->rai_rtpref;
139355505Sshin	ra->nd_ra_flags_reserved |=
1394222732Shrs		rai->rai_managedflg ? ND_RA_FLAG_MANAGED : 0;
139555505Sshin	ra->nd_ra_flags_reserved |=
1396222732Shrs		rai->rai_otherflg ? ND_RA_FLAG_OTHER : 0;
1397222732Shrs	ra->nd_ra_router_lifetime = htons(rai->rai_lifetime);
1398222732Shrs	ra->nd_ra_reachable = htonl(rai->rai_reachabletime);
1399222732Shrs	ra->nd_ra_retransmit = htonl(rai->rai_retranstimer);
140055505Sshin	buf += sizeof(*ra);
140155505Sshin
1402222732Shrs	if (rai->rai_advlinkopt) {
1403224144Shrs		lladdropt_fill(&ifi->ifi_sdl, (struct nd_opt_hdr *)buf);
140455505Sshin		buf += lladdroptlen;
140555505Sshin	}
140655505Sshin
1407222732Shrs	if (rai->rai_linkmtu) {
140855505Sshin		ndopt_mtu = (struct nd_opt_mtu *)buf;
140955505Sshin		ndopt_mtu->nd_opt_mtu_type = ND_OPT_MTU;
141055505Sshin		ndopt_mtu->nd_opt_mtu_len = 1;
141155505Sshin		ndopt_mtu->nd_opt_mtu_reserved = 0;
1412222732Shrs		ndopt_mtu->nd_opt_mtu_mtu = htonl(rai->rai_linkmtu);
141355505Sshin		buf += sizeof(struct nd_opt_mtu);
141455505Sshin	}
141555505Sshin
1416222732Shrs	TAILQ_FOREACH(pfx, &rai->rai_prefix, pfx_next) {
1417224144Shrs		uint32_t vltime, pltime;
1418253970Shrs		struct timespec now;
141978064Sume
142055505Sshin		ndopt_pi = (struct nd_opt_prefix_info *)buf;
142155505Sshin		ndopt_pi->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION;
142255505Sshin		ndopt_pi->nd_opt_pi_len = 4;
1423222732Shrs		ndopt_pi->nd_opt_pi_prefix_len = pfx->pfx_prefixlen;
142455505Sshin		ndopt_pi->nd_opt_pi_flags_reserved = 0;
1425222732Shrs		if (pfx->pfx_onlinkflg)
142655505Sshin			ndopt_pi->nd_opt_pi_flags_reserved |=
142755505Sshin				ND_OPT_PI_FLAG_ONLINK;
1428222732Shrs		if (pfx->pfx_autoconfflg)
142955505Sshin			ndopt_pi->nd_opt_pi_flags_reserved |=
143055505Sshin				ND_OPT_PI_FLAG_AUTO;
1431222732Shrs		if (pfx->pfx_timer)
143298172Sume			vltime = 0;
143398172Sume		else {
1434222732Shrs			if (pfx->pfx_vltimeexpire || pfx->pfx_pltimeexpire)
1435253970Shrs				clock_gettime(CLOCK_MONOTONIC_FAST, &now);
1436222732Shrs			if (pfx->pfx_vltimeexpire == 0)
1437222732Shrs				vltime = pfx->pfx_validlifetime;
143898172Sume			else
1439224144Shrs				vltime = ((time_t)pfx->pfx_vltimeexpire > now.tv_sec) ?
1440222732Shrs				    pfx->pfx_vltimeexpire - now.tv_sec : 0;
144198172Sume		}
1442222732Shrs		if (pfx->pfx_timer)
144398172Sume			pltime = 0;
144498172Sume		else {
1445222732Shrs			if (pfx->pfx_pltimeexpire == 0)
1446222732Shrs				pltime = pfx->pfx_preflifetime;
144798172Sume			else
1448224144Shrs				pltime = ((time_t)pfx->pfx_pltimeexpire > now.tv_sec) ?
1449222732Shrs				    pfx->pfx_pltimeexpire - now.tv_sec : 0;
145098172Sume		}
145178064Sume		if (vltime < pltime) {
145278064Sume			/*
145378064Sume			 * this can happen if vltime is decrement but pltime
145478064Sume			 * is not.
145578064Sume			 */
145678064Sume			pltime = vltime;
145778064Sume		}
145878064Sume		ndopt_pi->nd_opt_pi_valid_time = htonl(vltime);
145978064Sume		ndopt_pi->nd_opt_pi_preferred_time = htonl(pltime);
146055505Sshin		ndopt_pi->nd_opt_pi_reserved2 = 0;
1461222732Shrs		ndopt_pi->nd_opt_pi_prefix = pfx->pfx_prefix;
146255505Sshin
146355505Sshin		buf += sizeof(struct nd_opt_prefix_info);
146455505Sshin	}
146555505Sshin
1466222732Shrs	TAILQ_FOREACH(rti, &rai->rai_route, rti_next) {
1467224144Shrs		uint8_t psize = (rti->rti_prefixlen + 0x3f) >> 6;
146878064Sume
146978064Sume		ndopt_rti = (struct nd_opt_route_info *)buf;
147078064Sume		ndopt_rti->nd_opt_rti_type = ND_OPT_ROUTE_INFO;
147178064Sume		ndopt_rti->nd_opt_rti_len = 1 + psize;
1472222732Shrs		ndopt_rti->nd_opt_rti_prefixlen = rti->rti_prefixlen;
1473222732Shrs		ndopt_rti->nd_opt_rti_flags = 0xff & rti->rti_rtpref;
1474222732Shrs		ndopt_rti->nd_opt_rti_lifetime = htonl(rti->rti_ltime);
1475222732Shrs		memcpy(ndopt_rti + 1, &rti->rti_prefix, psize * 8);
147678064Sume		buf += sizeof(struct nd_opt_route_info) + psize * 8;
147778064Sume	}
1478224144Shrs
1479222732Shrs	TAILQ_FOREACH(rdn, &rai->rai_rdnss, rd_next) {
1480222732Shrs		struct rdnss_addr *rdna;
148178064Sume
1482222732Shrs		ndopt_rdnss = (struct nd_opt_rdnss *)buf;
1483222732Shrs		ndopt_rdnss->nd_opt_rdnss_type = ND_OPT_RDNSS;
1484222732Shrs		ndopt_rdnss->nd_opt_rdnss_len = 0;
1485222732Shrs		ndopt_rdnss->nd_opt_rdnss_reserved = 0;
1486222732Shrs		ndopt_rdnss->nd_opt_rdnss_lifetime = htonl(rdn->rd_ltime);
1487222732Shrs		buf += sizeof(struct nd_opt_rdnss);
148878064Sume
1489222732Shrs		TAILQ_FOREACH(rdna, &rdn->rd_list, ra_next) {
1490222732Shrs			memcpy(buf, &rdna->ra_dns, sizeof(rdna->ra_dns));
1491222732Shrs			buf += sizeof(rdna->ra_dns);
1492222732Shrs		}
1493222732Shrs		/* Length field should be in 8 octets */
1494222732Shrs		ndopt_rdnss->nd_opt_rdnss_len = (buf - (char *)ndopt_rdnss) / 8;
149578064Sume
1496222732Shrs		syslog(LOG_DEBUG, "<%s>: nd_opt_dnss_len = %d", __func__,
1497222732Shrs		    ndopt_rdnss->nd_opt_rdnss_len);
149878064Sume	}
1499224144Shrs
1500222732Shrs	TAILQ_FOREACH(dns, &rai->rai_dnssl, dn_next) {
1501222732Shrs		struct dnssl_addr *dnsa;
1502222732Shrs
1503222732Shrs		ndopt_dnssl = (struct nd_opt_dnssl *)buf;
1504222732Shrs		ndopt_dnssl->nd_opt_dnssl_type = ND_OPT_DNSSL;
1505222732Shrs		ndopt_dnssl->nd_opt_dnssl_len = 0;
1506222732Shrs		ndopt_dnssl->nd_opt_dnssl_reserved = 0;
1507222732Shrs		ndopt_dnssl->nd_opt_dnssl_lifetime = htonl(dns->dn_ltime);
1508222732Shrs		buf += sizeof(*ndopt_dnssl);
1509222732Shrs
1510222732Shrs		TAILQ_FOREACH(dnsa, &dns->dn_list, da_next) {
1511222732Shrs			memcpy(buf, dnsa->da_dom, dnsa->da_len);
1512222732Shrs			buf += dnsa->da_len;
1513222732Shrs		}
1514222732Shrs
1515222732Shrs		/* A zero octet after encoded DNS server list. */
1516222732Shrs		*buf++ = '\0';
1517222732Shrs
1518222732Shrs		/* Padding to next 8 octets boundary */
1519222732Shrs		len = buf - (char *)ndopt_dnssl;
1520222732Shrs		len += (len % 8) ? 8 - len % 8 : 0;
1521222732Shrs
1522222732Shrs		/* Length field must be in 8 octets */
1523222732Shrs		ndopt_dnssl->nd_opt_dnssl_len = len / 8;
1524222732Shrs
1525222732Shrs		syslog(LOG_DEBUG, "<%s>: nd_opt_dnssl_len = %d", __func__,
1526222732Shrs		    ndopt_dnssl->nd_opt_dnssl_len);
1527222732Shrs	}
1528222732Shrs	return;
152978064Sume}
1530