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