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