config.c revision 57852
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: head/usr.sbin/rtadvd/config.c 57852 2000-03-09 14:51:05Z shin $
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
63static void	makeentry __P((char *, int, char *, int));
64static void	make_packet __P((struct rainfo *));
65static void	get_prefix __P((struct rainfo *));
66
67extern struct	rainfo *ralist;
68
69void
70getconfig(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
322static void
323get_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
399static void
400makeentry(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 */
421static void
422add_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 */
467void
468delete_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 */
487static int
488init_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		close(s);
517		return 1;
518	}
519
520	close(s);
521	return 0;
522}
523
524void
525make_prefix(struct rainfo *rai, int ifindex, struct in6_addr *addr, int plen)
526{
527	struct in6_prefixreq ipr;
528
529	memset(&ipr, 0, sizeof(ipr));
530	if (if_indextoname(ifindex, ipr.ipr_name) == NULL) {
531		syslog(LOG_ERR, "<%s> Prefix added interface No.%d doesn't"
532		       "exist. This should not happen! %s", __FUNCTION__,
533		       ifindex, strerror(errno));
534		exit(1);
535	}
536	ipr.ipr_prefix.sin6_len = sizeof(ipr.ipr_prefix);
537	ipr.ipr_prefix.sin6_family = AF_INET6;
538	ipr.ipr_prefix.sin6_addr = *addr;
539	ipr.ipr_plen = plen;
540
541	if (init_prefix(&ipr))
542		return; /* init failed by some error */
543	add_prefix(rai, &ipr);
544}
545
546static void
547make_packet(struct rainfo *rainfo)
548{
549	size_t packlen, lladdroptlen = 0;
550	char *buf;
551	struct nd_router_advert *ra;
552	struct nd_opt_prefix_info *ndopt_pi;
553	struct nd_opt_mtu *ndopt_mtu;
554	struct prefix *pfx;
555
556	/* calculate total length */
557	packlen = sizeof(struct nd_router_advert);
558	if (rainfo->advlinkopt) {
559		if ((lladdroptlen = lladdropt_length(rainfo->sdl)) == 0) {
560			syslog(LOG_INFO,
561			       "<%s> link-layer address option has"
562			       " null length on %s."
563			       " Treat as not included.",
564			       __FUNCTION__, rainfo->ifname);
565			rainfo->advlinkopt = 0;
566		}
567		packlen += lladdroptlen;
568	}
569	if (rainfo->pfxs)
570		packlen += sizeof(struct nd_opt_prefix_info) * rainfo->pfxs;
571	if (rainfo->linkmtu)
572		packlen += sizeof(struct nd_opt_mtu);
573
574	/* allocate memory for the packet */
575	if ((buf = malloc(packlen)) == NULL) {
576		syslog(LOG_ERR,
577		       "<%s> can't get enough memory for an RA packet",
578		       __FUNCTION__);
579		exit(1);
580	}
581	rainfo->ra_data = buf;
582	/* XXX: what if packlen > 576? */
583	rainfo->ra_datalen = packlen;
584
585	/*
586	 * construct the packet
587	 */
588	ra = (struct nd_router_advert *)buf;
589	ra->nd_ra_type = ND_ROUTER_ADVERT;
590	ra->nd_ra_code = 0;
591	ra->nd_ra_cksum = 0;
592	ra->nd_ra_curhoplimit = (u_int8_t)(0xff & rainfo->hoplimit);
593	ra->nd_ra_flags_reserved = 0;
594	ra->nd_ra_flags_reserved |=
595		rainfo->managedflg ? ND_RA_FLAG_MANAGED : 0;
596	ra->nd_ra_flags_reserved |=
597		rainfo->otherflg ? ND_RA_FLAG_OTHER : 0;
598	ra->nd_ra_router_lifetime = htons(rainfo->lifetime);
599	ra->nd_ra_reachable = htonl(rainfo->reachabletime);
600	ra->nd_ra_retransmit = htonl(rainfo->retranstimer);
601	buf += sizeof(*ra);
602
603	if (rainfo->advlinkopt) {
604		lladdropt_fill(rainfo->sdl, (struct nd_opt_hdr *)buf);
605		buf += lladdroptlen;
606	}
607
608	if (rainfo->linkmtu) {
609		ndopt_mtu = (struct nd_opt_mtu *)buf;
610		ndopt_mtu->nd_opt_mtu_type = ND_OPT_MTU;
611		ndopt_mtu->nd_opt_mtu_len = 1;
612		ndopt_mtu->nd_opt_mtu_reserved = 0;
613		ndopt_mtu->nd_opt_mtu_mtu = ntohl(rainfo->linkmtu);
614		buf += sizeof(struct nd_opt_mtu);
615	}
616
617	for (pfx = rainfo->prefix.next;
618	     pfx != &rainfo->prefix; pfx = pfx->next) {
619		ndopt_pi = (struct nd_opt_prefix_info *)buf;
620		ndopt_pi->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION;
621		ndopt_pi->nd_opt_pi_len = 4;
622		ndopt_pi->nd_opt_pi_prefix_len = pfx->prefixlen;
623		ndopt_pi->nd_opt_pi_flags_reserved = 0;
624		if (pfx->onlinkflg)
625			ndopt_pi->nd_opt_pi_flags_reserved |=
626				ND_OPT_PI_FLAG_ONLINK;
627		if (pfx->autoconfflg)
628			ndopt_pi->nd_opt_pi_flags_reserved |=
629				ND_OPT_PI_FLAG_AUTO;
630		ndopt_pi->nd_opt_pi_valid_time = ntohl(pfx->validlifetime);
631		ndopt_pi->nd_opt_pi_preferred_time =
632			ntohl(pfx->preflifetime);
633		ndopt_pi->nd_opt_pi_reserved2 = 0;
634		ndopt_pi->nd_opt_pi_prefix = pfx->prefix;
635
636		buf += sizeof(struct nd_opt_prefix_info);
637	}
638
639	return;
640}
641