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