config.c revision 71437
1/*	$FreeBSD: head/usr.sbin/rtadvd/config.c 71437 2001-01-23 17:29:12Z ume $	*/
2/*	$KAME: config.c,v 1.11 2000/05/16 13:34:13 itojun Exp $	*/
3
4/*
5 * Copyright (C) 1998 WIDE Project.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the project nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#include <sys/param.h>
34#include <sys/ioctl.h>
35#include <sys/socket.h>
36#include <sys/time.h>
37
38#include <net/if.h>
39#if defined(__FreeBSD__) && __FreeBSD__ >= 3
40#include <net/if_var.h>
41#endif /* __FreeBSD__ >= 3 */
42#include <net/route.h>
43#include <net/if_dl.h>
44
45#include <netinet/in.h>
46#include <netinet/in_var.h>
47#include <netinet/ip6.h>
48#include <netinet6/ip6_var.h>
49#include <netinet/icmp6.h>
50#ifdef MIP6
51#include <netinet6/mip6.h>
52#endif
53
54#include <arpa/inet.h>
55
56#include <stdio.h>
57#include <syslog.h>
58#include <errno.h>
59#include <string.h>
60#include <stdlib.h>
61#if defined(__NetBSD__) || defined(__OpenBSD__)
62#include <search.h>
63#endif
64#include <unistd.h>
65
66#include "rtadvd.h"
67#include "advcap.h"
68#include "timer.h"
69#include "if.h"
70#include "config.h"
71
72static void makeentry __P((char *, int, char *, int));
73static void get_prefix __P((struct rainfo *));
74
75extern struct rainfo *ralist;
76
77void
78getconfig(intface)
79	char *intface;
80{
81	int stat, pfxs, i;
82	char tbuf[BUFSIZ];
83	struct rainfo *tmp;
84	long val;
85	char buf[BUFSIZ];
86	char *bp = buf;
87	char *addr;
88
89#define MUSTHAVE(var, cap)	\
90    do {								\
91	int t;								\
92	if ((t = agetnum(cap)) < 0) {					\
93		fprintf(stderr, "rtadvd: need %s for interface %s\n",	\
94			cap, intface);					\
95		exit(1);						\
96	}								\
97	var = t;							\
98     } while (0)
99#define MAYHAVE(var, cap, def)	\
100     do {								\
101	if ((var = agetnum(cap)) < 0)					\
102		var = def;						\
103     } while (0)
104
105	if ((stat = agetent(tbuf, intface)) <= 0) {
106		memset(tbuf, 0, sizeof(tbuf));
107		syslog(LOG_INFO,
108		       "<%s> %s isn't defined in the configuration file"
109		       " or the configuration file doesn't exist."
110		       " Treat it as default",
111		        __FUNCTION__, intface);
112	}
113
114	tmp = (struct rainfo *)malloc(sizeof(*ralist));
115	memset(tmp, 0, sizeof(*tmp));
116	tmp->prefix.next = tmp->prefix.prev = &tmp->prefix;
117
118	/* get interface information */
119	if (agetflag("nolladdr"))
120		tmp->advlinkopt = 0;
121	else
122		tmp->advlinkopt = 1;
123	if (tmp->advlinkopt) {
124		if ((tmp->sdl = if_nametosdl(intface)) == NULL) {
125			syslog(LOG_ERR,
126			       "<%s> can't get information of %s",
127			       __FUNCTION__, intface);
128			exit(1);
129		}
130		tmp->ifindex = tmp->sdl->sdl_index;
131	} else
132		tmp->ifindex = if_nametoindex(intface);
133	strncpy(tmp->ifname, intface, sizeof(tmp->ifname));
134	if ((tmp->phymtu = if_getmtu(intface)) == 0) {
135		tmp->phymtu = IPV6_MMTU;
136		syslog(LOG_WARNING,
137		       "<%s> can't get interface mtu of %s. Treat as %d",
138		       __FUNCTION__, intface, IPV6_MMTU);
139	}
140
141	/*
142	 * set router configuration variables.
143	 */
144	MAYHAVE(val, "maxinterval", DEF_MAXRTRADVINTERVAL);
145	if (val < MIN_MAXINTERVAL || val > MAX_MAXINTERVAL) {
146		syslog(LOG_ERR,
147		       "<%s> maxinterval must be between %e and %u",
148		       __FUNCTION__, MIN_MAXINTERVAL, MAX_MAXINTERVAL);
149		exit(1);
150	}
151	tmp->maxinterval = (u_int)val;
152	MAYHAVE(val, "mininterval", tmp->maxinterval/3);
153	if (val < MIN_MININTERVAL || val > (tmp->maxinterval * 3) / 4) {
154		syslog(LOG_ERR,
155		       "<%s> mininterval must be between %e and %d",
156		       __FUNCTION__,
157		       MIN_MININTERVAL,
158		       (tmp->maxinterval * 3) / 4);
159		exit(1);
160	}
161	tmp->mininterval = (u_int)val;
162
163	MAYHAVE(val, "chlim", DEF_ADVCURHOPLIMIT);
164	tmp->hoplimit = val & 0xff;
165
166	MAYHAVE(val, "raflags", 0);
167	tmp->managedflg= val & ND_RA_FLAG_MANAGED;
168	tmp->otherflg = val & ND_RA_FLAG_OTHER;
169#ifdef MIP6
170	if (mobileip6)
171		tmp->haflg = val & ND_RA_FLAG_HA;
172#endif
173
174	MAYHAVE(val, "rltime", tmp->maxinterval * 3);
175	if (val && (val < tmp->maxinterval || val > MAXROUTERLIFETIME)) {
176		syslog(LOG_ERR,
177		       "<%s> router lifetime on %s must be 0 or"
178		       " between %d and %d",
179		       __FUNCTION__, intface,
180		       tmp->maxinterval, MAXROUTERLIFETIME);
181		exit(1);
182	}
183	tmp->lifetime = val & 0xffff;
184
185	MAYHAVE(val, "rtime", DEF_ADVREACHABLETIME);
186	if (val > MAXREACHABLETIME) {
187		syslog(LOG_ERR,
188		       "<%s> reachable time must be no greater than %d",
189		       __FUNCTION__, MAXREACHABLETIME);
190		exit(1);
191	}
192	tmp->reachabletime = (u_int32_t)val;
193
194	MAYHAVE(val, "retrans", DEF_ADVRETRANSTIMER);
195	if (val < 0 || val > 0xffffffff) {
196		syslog(LOG_ERR,
197		       "<%s> retrans time out of range", __FUNCTION__);
198		exit(1);
199	}
200	tmp->retranstimer = (u_int32_t)val;
201
202#ifdef MIP6
203	if (!mobileip6)
204#else
205	if (1)
206#endif
207	{
208		if (agetstr("hapref", &bp) || agetstr("hatime", &bp)) {
209			syslog(LOG_ERR,
210			       "<%s> mobile-ip6 configuration without "
211			       "proper command line option",
212			       __FUNCTION__);
213			exit(1);
214		}
215	}
216#ifdef MIP6
217	else {
218		tmp->hapref = 0;
219		if ((val = agetnum("hapref")) >= 0)
220			tmp->hapref = (int16_t)val;
221		if (tmp->hapref != 0) {
222			tmp->hatime = 0;
223			MUSTHAVE(val, "hatime");
224			tmp->hatime = (u_int16_t)val;
225			if (tmp->hatime <= 0) {
226				syslog(LOG_ERR,
227				       "<%s> home agent lifetime must be greater than 0",
228				       __FUNCTION__);
229				exit(1);
230			}
231		}
232	}
233#endif
234
235	/* prefix information */
236	if ((pfxs = agetnum("addrs")) < 0) {
237		/* auto configure prefix information */
238		if (agetstr("addr", &bp) || agetstr("addr1", &bp)) {
239			syslog(LOG_ERR,
240			       "<%s> conflicting prefix configuration for %s: "
241			       "automatic and manual config at the same time",
242			       __FUNCTION__, intface);
243			exit(1);
244		}
245		get_prefix(tmp);
246	}
247	else {
248		tmp->pfxs = pfxs;
249		for (i = 0; i < pfxs; i++) {
250			struct prefix *pfx;
251			char entbuf[256];
252			int added = (pfxs > 1) ? 1 : 0;
253
254			/* allocate memory to store prefix information */
255			if ((pfx = malloc(sizeof(struct prefix))) == NULL) {
256				syslog(LOG_ERR,
257				       "<%s> can't allocate enough memory",
258				       __FUNCTION__);
259				exit(1);
260			}
261			memset(pfx, 0, sizeof(*pfx));
262
263			/* link into chain */
264			insque(pfx, &tmp->prefix);
265
266			pfx->origin = PREFIX_FROM_CONFIG;
267
268			makeentry(entbuf, i, "prefixlen", added);
269			MAYHAVE(val, entbuf, 64);
270			if (val < 0 || val > 128) {
271				syslog(LOG_ERR,
272				       "<%s> prefixlen out of range",
273				       __FUNCTION__);
274				exit(1);
275			}
276			pfx->prefixlen = (int)val;
277
278			makeentry(entbuf, i, "pinfoflags", added);
279#ifdef MIP6
280			if (mobileip6)
281			{
282				MAYHAVE(val, entbuf,
283				    (ND_OPT_PI_FLAG_ONLINK|ND_OPT_PI_FLAG_AUTO|
284					 ND_OPT_PI_FLAG_RTADDR));
285			} else
286#endif
287			{
288				MAYHAVE(val, entbuf,
289				    (ND_OPT_PI_FLAG_ONLINK|ND_OPT_PI_FLAG_AUTO));
290			}
291			pfx->onlinkflg = val & ND_OPT_PI_FLAG_ONLINK;
292			pfx->autoconfflg = val & ND_OPT_PI_FLAG_AUTO;
293#ifdef MIP6
294			if (mobileip6)
295				pfx->routeraddr = val & ND_OPT_PI_FLAG_RTADDR;
296#endif
297
298			makeentry(entbuf, i, "vltime", added);
299			MAYHAVE(val, entbuf, DEF_ADVVALIDLIFETIME);
300			if (val < 0 || val > 0xffffffff) {
301				syslog(LOG_ERR,
302				       "<%s> vltime out of range",
303				       __FUNCTION__);
304				exit(1);
305			}
306			pfx->validlifetime = (u_int32_t)val;
307
308			makeentry(entbuf, i, "pltime", added);
309			MAYHAVE(val, entbuf, DEF_ADVPREFERREDLIFETIME);
310			if (val < 0 || val > 0xffffffff) {
311				syslog(LOG_ERR,
312				       "<%s> pltime out of range",
313				       __FUNCTION__);
314				exit(1);
315			}
316			pfx->preflifetime = (u_int32_t)val;
317
318			makeentry(entbuf, i, "addr", added);
319			addr = (char *)agetstr(entbuf, &bp);
320			if (addr == NULL) {
321				syslog(LOG_ERR,
322				       "<%s> need %s as an prefix for "
323				       "interface %s",
324				       __FUNCTION__, entbuf, intface);
325				exit(1);
326			}
327			if (inet_pton(AF_INET6, addr,
328				      &pfx->prefix) != 1) {
329				syslog(LOG_ERR,
330				       "<%s> inet_pton failed for %s",
331				       __FUNCTION__, addr);
332				exit(1);
333			}
334			if (IN6_IS_ADDR_MULTICAST(&pfx->prefix)) {
335				syslog(LOG_ERR,
336				       "<%s> multicast prefix(%s) must "
337				       "not be advertised (IF=%s)",
338				       __FUNCTION__, addr, intface);
339				exit(1);
340			}
341			if (IN6_IS_ADDR_LINKLOCAL(&pfx->prefix))
342				syslog(LOG_NOTICE,
343				       "<%s> link-local prefix(%s) will be"
344				       " advertised on %s",
345				       __FUNCTION__, addr, intface);
346		}
347	}
348
349	MAYHAVE(val, "mtu", 0);
350	if (val < 0 || val > 0xffffffff) {
351		syslog(LOG_ERR,
352		       "<%s> mtu out of range", __FUNCTION__);
353		exit(1);
354	}
355	tmp->linkmtu = (u_int32_t)val;
356	if (tmp->linkmtu == 0) {
357		char *mtustr;
358
359		if ((mtustr = (char *)agetstr("mtu", &bp)) &&
360		    strcmp(mtustr, "auto") == 0)
361			tmp->linkmtu = tmp->phymtu;
362	}
363	else if (tmp->linkmtu < IPV6_MMTU || tmp->linkmtu > tmp->phymtu) {
364		syslog(LOG_ERR,
365		       "<%s> advertised link mtu must be between"
366		       " least MTU and physical link MTU",
367		       __FUNCTION__);
368		exit(1);
369	}
370
371	/* okey */
372	tmp->next = ralist;
373	ralist = tmp;
374
375	/* construct the sending packet */
376	make_packet(tmp);
377
378	/* set timer */
379	tmp->timer = rtadvd_add_timer(ra_timeout, ra_timer_update,
380				      tmp, tmp);
381	ra_timer_update((void *)tmp, &tmp->timer->tm);
382	rtadvd_set_timer(&tmp->timer->tm, tmp->timer);
383}
384
385static void
386get_prefix(struct rainfo *rai)
387{
388	size_t len;
389	u_char *buf, *lim, *next;
390	u_char ntopbuf[INET6_ADDRSTRLEN];
391
392	if ((len = rtbuf_len()) < 0) {
393		syslog(LOG_ERR,
394		       "<%s> can't get buffer length for routing info",
395		       __FUNCTION__);
396		exit(1);
397	}
398	if ((buf = malloc(len)) == NULL) {
399		syslog(LOG_ERR,
400		       "<%s> can't allocate buffer", __FUNCTION__);
401		exit(1);
402	}
403	if (get_rtinfo(buf, &len) < 0) {
404		syslog(LOG_ERR,
405		       "<%s> can't get routing inforamtion", __FUNCTION__);
406		exit(1);
407	}
408
409	lim = buf + len;
410	next = get_next_msg(buf, lim, rai->ifindex, &len,
411			    RTADV_TYPE2BITMASK(RTM_GET));
412	while (next < lim) {
413		struct prefix *pp;
414		struct in6_addr *a;
415
416		/* allocate memory to store prefix info. */
417		if ((pp = malloc(sizeof(*pp))) == NULL) {
418			syslog(LOG_ERR,
419			       "<%s> can't get allocate buffer for prefix",
420			       __FUNCTION__);
421			exit(1);
422		}
423		memset(pp, 0, sizeof(*pp));
424
425		/* set prefix and its length */
426		a = get_addr(next);
427		memcpy(&pp->prefix, a, sizeof(*a));
428		if ((pp->prefixlen = get_prefixlen(next)) < 0) {
429			syslog(LOG_ERR,
430			       "<%s> failed to get prefixlen "
431			       "or prefixl is invalid",
432			       __FUNCTION__);
433			exit(1);
434		}
435		syslog(LOG_DEBUG,
436		       "<%s> add %s/%d to prefix list on %s",
437		       __FUNCTION__,
438		       inet_ntop(AF_INET6, a, ntopbuf, INET6_ADDRSTRLEN),
439		       pp->prefixlen, rai->ifname);
440
441		/* set other fields with protocol defaults */
442		pp->validlifetime = DEF_ADVVALIDLIFETIME;
443		pp->preflifetime = DEF_ADVPREFERREDLIFETIME;
444		pp->onlinkflg = 1;
445		pp->autoconfflg = 1;
446		pp->origin = PREFIX_FROM_KERNEL;
447
448		/* link into chain */
449		insque(pp, &rai->prefix);
450
451		/* counter increment */
452		rai->pfxs++;
453
454		/* forward pointer and get next prefix(if any) */
455		next += len;
456		next = get_next_msg(next, lim, rai->ifindex,
457				    &len, RTADV_TYPE2BITMASK(RTM_GET));
458	}
459
460	free(buf);
461}
462
463static void
464makeentry(buf, id, string, add)
465    char *buf, *string;
466    int id, add;
467{
468	strcpy(buf, string);
469	if (add) {
470		char *cp;
471
472		cp = (char *)index(buf, '\0');
473		cp += sprintf(cp, "%d", id);
474		*cp = '\0';
475	}
476}
477
478/*
479 * Add a prefix to the list of specified interface and reconstruct
480 * the outgoing packet.
481 * The prefix must not be in the list.
482 * XXX: other parameter of the prefix(e.g. lifetime) shoule be
483 * able to be specified.
484 */
485static void
486add_prefix(struct rainfo *rai, struct in6_prefixreq *ipr)
487{
488	struct prefix *prefix;
489	u_char ntopbuf[INET6_ADDRSTRLEN];
490
491	if ((prefix = malloc(sizeof(*prefix))) == NULL) {
492		syslog(LOG_ERR, "<%s> memory allocation failed",
493		       __FUNCTION__);
494		return;		/* XXX: error or exit? */
495	}
496	prefix->prefix = ipr->ipr_prefix.sin6_addr;
497	prefix->prefixlen = ipr->ipr_plen;
498	prefix->validlifetime = ipr->ipr_vltime;
499	prefix->preflifetime = ipr->ipr_pltime;
500	prefix->onlinkflg = ipr->ipr_raf_onlink;
501	prefix->autoconfflg = ipr->ipr_raf_auto;
502	prefix->origin = PREFIX_FROM_DYNAMIC;
503
504	insque(prefix, &rai->prefix);
505
506	syslog(LOG_DEBUG, "<%s> new prefix %s/%d was added on %s",
507	       __FUNCTION__, inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr,
508				       ntopbuf, INET6_ADDRSTRLEN),
509	       ipr->ipr_plen, rai->ifname);
510
511	/* free the previous packet */
512	free(rai->ra_data);
513	rai->ra_data = 0;
514
515	/* reconstruct the packet */
516	rai->pfxs++;
517	make_packet(rai);
518
519	/*
520	 * reset the timer so that the new prefix will be advertised quickly.
521	 */
522	rai->initcounter = 0;
523	ra_timer_update((void *)rai, &rai->timer->tm);
524	rtadvd_set_timer(&rai->timer->tm, rai->timer);
525}
526
527/*
528 * Delete a prefix to the list of specified interface and reconstruct
529 * the outgoing packet.
530 * The prefix must be in the list.
531 */
532void
533delete_prefix(struct rainfo *rai, struct prefix *prefix)
534{
535	u_char ntopbuf[INET6_ADDRSTRLEN];
536
537	remque(prefix);
538	syslog(LOG_DEBUG, "<%s> prefix %s/%d was deleted on %s",
539	       __FUNCTION__, inet_ntop(AF_INET6, &prefix->prefix,
540				       ntopbuf, INET6_ADDRSTRLEN),
541	       prefix->prefixlen, rai->ifname);
542	free(prefix);
543	rai->pfxs--;
544	make_packet(rai);
545}
546
547/*
548 * Try to get an in6_prefixreq contents for a prefix which matches
549 * ipr->ipr_prefix and ipr->ipr_plen and belongs to
550 * the interface whose name is ipr->ipr_name[].
551 */
552static int
553init_prefix(struct in6_prefixreq *ipr)
554{
555	int s;
556
557	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
558		syslog(LOG_ERR, "<%s> socket: %s", __FUNCTION__,
559		       strerror(errno));
560		exit(1);
561	}
562
563	if (ioctl(s, SIOCGIFPREFIX_IN6, (caddr_t)ipr) < 0) {
564		syslog(LOG_INFO, "<%s> ioctl:SIOCGIFPREFIX %s", __FUNCTION__,
565		       strerror(errno));
566
567		ipr->ipr_vltime = DEF_ADVVALIDLIFETIME;
568		ipr->ipr_pltime = DEF_ADVPREFERREDLIFETIME;
569		ipr->ipr_raf_onlink = 1;
570		ipr->ipr_raf_auto = 1;
571		/* omit other field initialization */
572	}
573	else if (ipr->ipr_origin < PR_ORIG_RR) {
574		u_char ntopbuf[INET6_ADDRSTRLEN];
575
576		syslog(LOG_WARNING, "<%s> Added prefix(%s)'s origin %d is"
577		       "lower than PR_ORIG_RR(router renumbering)."
578		       "This should not happen if I am router", __FUNCTION__,
579		       inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr, ntopbuf,
580				 sizeof(ntopbuf)), ipr->ipr_origin);
581		close(s);
582		return 1;
583	}
584
585	close(s);
586	return 0;
587}
588
589void
590make_prefix(struct rainfo *rai, int ifindex, struct in6_addr *addr, int plen)
591{
592	struct in6_prefixreq ipr;
593
594	memset(&ipr, 0, sizeof(ipr));
595	if (if_indextoname(ifindex, ipr.ipr_name) == NULL) {
596		syslog(LOG_ERR, "<%s> Prefix added interface No.%d doesn't"
597		       "exist. This should not happen! %s", __FUNCTION__,
598		       ifindex, strerror(errno));
599		exit(1);
600	}
601	ipr.ipr_prefix.sin6_len = sizeof(ipr.ipr_prefix);
602	ipr.ipr_prefix.sin6_family = AF_INET6;
603	ipr.ipr_prefix.sin6_addr = *addr;
604	ipr.ipr_plen = plen;
605
606	if (init_prefix(&ipr))
607		return; /* init failed by some error */
608	add_prefix(rai, &ipr);
609}
610
611void
612make_packet(struct rainfo *rainfo)
613{
614	size_t packlen, lladdroptlen = 0;
615	char *buf;
616	struct nd_router_advert *ra;
617	struct nd_opt_prefix_info *ndopt_pi;
618	struct nd_opt_mtu *ndopt_mtu;
619#ifdef MIP6
620	struct nd_opt_advint *ndopt_advint;
621	struct nd_opt_hai *ndopt_hai;
622#endif
623	struct prefix *pfx;
624
625	/* calculate total length */
626	packlen = sizeof(struct nd_router_advert);
627	if (rainfo->advlinkopt) {
628		if ((lladdroptlen = lladdropt_length(rainfo->sdl)) == 0) {
629			syslog(LOG_INFO,
630			       "<%s> link-layer address option has"
631			       " null length on %s."
632			       " Treat as not included.",
633			       __FUNCTION__, rainfo->ifname);
634			rainfo->advlinkopt = 0;
635		}
636		packlen += lladdroptlen;
637	}
638	if (rainfo->pfxs)
639		packlen += sizeof(struct nd_opt_prefix_info) * rainfo->pfxs;
640	if (rainfo->linkmtu)
641		packlen += sizeof(struct nd_opt_mtu);
642#ifdef MIP6
643	if (mobileip6 && rainfo->maxinterval)
644		packlen += sizeof(struct nd_opt_advint);
645	if (mobileip6 && rainfo->hatime)
646		packlen += sizeof(struct nd_opt_hai);
647#endif
648
649	/* allocate memory for the packet */
650	if ((buf = malloc(packlen)) == NULL) {
651		syslog(LOG_ERR,
652		       "<%s> can't get enough memory for an RA packet",
653		       __FUNCTION__);
654		exit(1);
655	}
656	rainfo->ra_data = buf;
657	/* XXX: what if packlen > 576? */
658	rainfo->ra_datalen = packlen;
659
660	/*
661	 * construct the packet
662	 */
663	ra = (struct nd_router_advert *)buf;
664	ra->nd_ra_type = ND_ROUTER_ADVERT;
665	ra->nd_ra_code = 0;
666	ra->nd_ra_cksum = 0;
667	ra->nd_ra_curhoplimit = (u_int8_t)(0xff & rainfo->hoplimit);
668	ra->nd_ra_flags_reserved = 0;
669	ra->nd_ra_flags_reserved |=
670		rainfo->managedflg ? ND_RA_FLAG_MANAGED : 0;
671	ra->nd_ra_flags_reserved |=
672		rainfo->otherflg ? ND_RA_FLAG_OTHER : 0;
673#ifdef MIP6
674	ra->nd_ra_flags_reserved |=
675		rainfo->haflg ? ND_RA_FLAG_HA : 0;
676#endif
677	ra->nd_ra_router_lifetime = htons(rainfo->lifetime);
678	ra->nd_ra_reachable = htonl(rainfo->reachabletime);
679	ra->nd_ra_retransmit = htonl(rainfo->retranstimer);
680	buf += sizeof(*ra);
681
682	if (rainfo->advlinkopt) {
683		lladdropt_fill(rainfo->sdl, (struct nd_opt_hdr *)buf);
684		buf += lladdroptlen;
685	}
686
687	if (rainfo->linkmtu) {
688		ndopt_mtu = (struct nd_opt_mtu *)buf;
689		ndopt_mtu->nd_opt_mtu_type = ND_OPT_MTU;
690		ndopt_mtu->nd_opt_mtu_len = 1;
691		ndopt_mtu->nd_opt_mtu_reserved = 0;
692		ndopt_mtu->nd_opt_mtu_mtu = ntohl(rainfo->linkmtu);
693		buf += sizeof(struct nd_opt_mtu);
694	}
695
696#ifdef MIP6
697	if (mobileip6 && rainfo->maxinterval) {
698		ndopt_advint = (struct nd_opt_advint *)buf;
699		ndopt_advint->nd_opt_int_type = ND_OPT_ADV_INTERVAL;
700		ndopt_advint->nd_opt_int_len = 1;
701		ndopt_advint->nd_opt_int_reserved = 0;
702		ndopt_advint->nd_opt_int_interval = ntohl(rainfo->maxinterval *
703							  1000);
704		buf += sizeof(struct nd_opt_advint);
705	}
706#endif
707
708#ifdef MIP6
709	if (rainfo->hatime) {
710		ndopt_hai = (struct nd_opt_hai *)buf;
711		ndopt_hai->nd_opt_hai_type = ND_OPT_HA_INFORMATION;
712		ndopt_hai->nd_opt_hai_len = 1;
713		ndopt_hai->nd_opt_hai_reserved = 0;
714		ndopt_hai->nd_opt_hai_pref = ntohs(rainfo->hapref);
715		ndopt_hai->nd_opt_hai_lifetime = ntohs(rainfo->hatime);
716		buf += sizeof(struct nd_opt_hai);
717	}
718#endif
719
720	for (pfx = rainfo->prefix.next;
721	     pfx != &rainfo->prefix; pfx = pfx->next) {
722		ndopt_pi = (struct nd_opt_prefix_info *)buf;
723		ndopt_pi->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION;
724		ndopt_pi->nd_opt_pi_len = 4;
725		ndopt_pi->nd_opt_pi_prefix_len = pfx->prefixlen;
726		ndopt_pi->nd_opt_pi_flags_reserved = 0;
727		if (pfx->onlinkflg)
728			ndopt_pi->nd_opt_pi_flags_reserved |=
729				ND_OPT_PI_FLAG_ONLINK;
730		if (pfx->autoconfflg)
731			ndopt_pi->nd_opt_pi_flags_reserved |=
732				ND_OPT_PI_FLAG_AUTO;
733#ifdef MIP6
734		if (pfx->routeraddr)
735			ndopt_pi->nd_opt_pi_flags_reserved |=
736				ND_OPT_PI_FLAG_RTADDR;
737#endif
738		ndopt_pi->nd_opt_pi_valid_time = ntohl(pfx->validlifetime);
739		ndopt_pi->nd_opt_pi_preferred_time =
740			ntohl(pfx->preflifetime);
741		ndopt_pi->nd_opt_pi_reserved2 = 0;
742		ndopt_pi->nd_opt_pi_prefix = pfx->prefix;
743
744		buf += sizeof(struct nd_opt_prefix_info);
745	}
746
747	return;
748}
749