1 /* 2 * dhcpcd - DHCP client daemon 3 * Copyright (c) 2006-2012 Roy Marples <roy (at) marples.name> 4 * All rights reserved 5 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/param.h> 29 #include <sys/socket.h> 30 #include <net/if.h> 31 #include <netinet/in.h> 32 #include <netinet/ip6.h> 33 #include <netinet/icmp6.h> 34 35 #include <errno.h> 36 #include <stddef.h> 37 #include <stdlib.h> 38 #include <string.h> 39 #include <syslog.h> 40 41 #ifdef __linux__ 42 # define _LINUX_IN6_H 43 # include <linux/ipv6.h> 44 #endif 45 46 #define ELOOP_QUEUE 1 47 #include "bind.h" 48 #include "common.h" 49 #include "configure.h" 50 #include "dhcpcd.h" 51 #include "eloop.h" 52 #include "ipv6rs.h" 53 54 #define ALLROUTERS "ff02::2" 55 #define HOPLIMIT 255 56 57 #define ROUNDUP8(a) (1 + (((a) - 1) | 7)) 58 59 #define RTR_SOLICITATION_INTERVAL 4 /* seconds */ 60 #define MAX_RTR_SOLICITATIONS 3 /* times */ 61 62 #ifndef ND_OPT_RDNSS 63 #define ND_OPT_RDNSS 25 64 struct nd_opt_rdnss { /* RDNSS option RFC 6106 */ 65 uint8_t nd_opt_rdnss_type; 66 uint8_t nd_opt_rdnss_len; 67 uint16_t nd_opt_rdnss_reserved; 68 uint32_t nd_opt_rdnss_lifetime; 69 /* followed by list of IP prefixes */ 70 } _packed; 71 #endif 72 73 #ifndef ND_OPT_DNSSL 74 #define ND_OPT_DNSSL 31 75 struct nd_opt_dnssl { /* DNSSL option RFC 6106 */ 76 uint8_t nd_opt_dnssl_type; 77 uint8_t nd_opt_dnssl_len; 78 uint16_t nd_opt_dnssl_reserved; 79 uint32_t nd_opt_dnssl_lifetime; 80 /* followed by list of DNS servers */ 81 } _packed; 82 #endif 83 84 static int sock; 85 static struct sockaddr_in6 allrouters, from; 86 static struct msghdr sndhdr; 87 static struct iovec sndiov[2]; 88 static unsigned char *sndbuf; 89 static struct msghdr rcvhdr; 90 static struct iovec rcviov[2]; 91 static unsigned char *rcvbuf; 92 static unsigned char ansbuf[1500]; 93 static char ntopbuf[INET6_ADDRSTRLEN]; 94 95 #if DEBUG_MEMORY 96 static void 97 ipv6rs_cleanup(void) 98 { 99 100 free(sndbuf); 101 free(rcvbuf); 102 } 103 #endif 104 105 int 106 ipv6rs_open(void) 107 { 108 int on; 109 int len; 110 struct icmp6_filter filt; 111 112 memset(&allrouters, 0, sizeof(allrouters)); 113 allrouters.sin6_family = AF_INET6; 114 #ifdef SIN6_LEN 115 allrouters.sin6_len = sizeof(allrouters); 116 #endif 117 if (inet_pton(AF_INET6, ALLROUTERS, &allrouters.sin6_addr.s6_addr) != 1) 118 return -1; 119 sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); 120 if (sock == -1) 121 return -1; 122 on = 1; 123 if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, 124 &on, sizeof(on)) == -1) 125 return -1; 126 127 on = 1; 128 if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, 129 &on, sizeof(on)) == -1) 130 return -1; 131 132 ICMP6_FILTER_SETBLOCKALL(&filt); 133 ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt); 134 if (setsockopt(sock, IPPROTO_ICMPV6, ICMP6_FILTER, 135 &filt, sizeof(filt)) == -1) 136 return -1; 137 138 #if DEBUG_MEMORY 139 atexit(ipv6rs_cleanup); 140 #endif 141 142 len = CMSG_SPACE(sizeof(struct in6_pktinfo)) + CMSG_SPACE(sizeof(int)); 143 sndbuf = xzalloc(len); 144 if (sndbuf == NULL) 145 return -1; 146 sndhdr.msg_namelen = sizeof(struct sockaddr_in6); 147 sndhdr.msg_iov = sndiov; 148 sndhdr.msg_iovlen = 1; 149 sndhdr.msg_control = sndbuf; 150 sndhdr.msg_controllen = len; 151 rcvbuf = xzalloc(len); 152 if (rcvbuf == NULL) 153 return -1; 154 rcvhdr.msg_name = &from; 155 rcvhdr.msg_namelen = sizeof(from); 156 rcvhdr.msg_iov = rcviov; 157 rcvhdr.msg_iovlen = 1; 158 rcvhdr.msg_control = rcvbuf; 159 rcvhdr.msg_controllen = len; 160 rcviov[0].iov_base = ansbuf; 161 rcviov[0].iov_len = sizeof(ansbuf); 162 return sock; 163 } 164 165 static int 166 ipv6rs_makeprobe(struct interface *ifp) 167 { 168 struct nd_router_solicit *rs; 169 struct nd_opt_hdr *nd; 170 171 free(ifp->rs); 172 ifp->rslen = sizeof(*rs) + ROUNDUP8(ifp->hwlen + 2); 173 ifp->rs = xzalloc(ifp->rslen); 174 if (ifp->rs == NULL) 175 return -1; 176 rs = (struct nd_router_solicit *)ifp->rs; 177 rs->nd_rs_type = ND_ROUTER_SOLICIT; 178 rs->nd_rs_code = 0; 179 rs->nd_rs_cksum = 0; 180 rs->nd_rs_reserved = 0; 181 nd = (struct nd_opt_hdr *)(ifp->rs + sizeof(*rs)); 182 nd->nd_opt_type = ND_OPT_SOURCE_LINKADDR; 183 nd->nd_opt_len = (ROUNDUP8(ifp->hwlen + 2)) >> 3; 184 memcpy(nd + 1, ifp->hwaddr, ifp->hwlen); 185 return 0; 186 } 187 188 static void 189 ipv6rs_sendprobe(void *arg) 190 { 191 struct interface *ifp = arg; 192 struct sockaddr_in6 dst; 193 struct cmsghdr *cm; 194 struct in6_pktinfo pi; 195 int hoplimit = HOPLIMIT; 196 197 dst = allrouters; 198 //dst.sin6_scope_id = ifp->linkid; 199 200 ipv6rs_makeprobe(ifp); 201 sndhdr.msg_name = (caddr_t)&dst; 202 sndhdr.msg_iov[0].iov_base = ifp->rs; 203 sndhdr.msg_iov[0].iov_len = ifp->rslen; 204 205 /* Set the outbound interface */ 206 cm = CMSG_FIRSTHDR(&sndhdr); 207 cm->cmsg_level = IPPROTO_IPV6; 208 cm->cmsg_type = IPV6_PKTINFO; 209 cm->cmsg_len = CMSG_LEN(sizeof(pi)); 210 memset(&pi, 0, sizeof(pi)); 211 pi.ipi6_ifindex = if_nametoindex(ifp->name); 212 memcpy(CMSG_DATA(cm), &pi, sizeof(pi)); 213 214 /* Hop limit */ 215 cm = CMSG_NXTHDR(&sndhdr, cm); 216 cm->cmsg_level = IPPROTO_IPV6; 217 cm->cmsg_type = IPV6_HOPLIMIT; 218 cm->cmsg_len = CMSG_LEN(sizeof(hoplimit)); 219 memcpy(CMSG_DATA(cm), &hoplimit, sizeof(hoplimit)); 220 221 syslog(LOG_INFO, "%s: sending IPv6 Router Solicitation", ifp->name); 222 if (sendmsg(sock, &sndhdr, 0) == -1) 223 syslog(LOG_ERR, "%s: sendmsg: %m", ifp->name); 224 225 if (ifp->rsprobes++ < MAX_RTR_SOLICITATIONS) 226 add_timeout_sec(RTR_SOLICITATION_INTERVAL, 227 ipv6rs_sendprobe, ifp); 228 else 229 syslog(LOG_INFO, "%s: no IPv6 Routers available", ifp->name); 230 } 231 232 static void 233 ipv6rs_sort(struct interface *ifp) 234 { 235 struct ra *rap, *sorted, *ran, *rat; 236 237 if (ifp->ras == NULL || ifp->ras->next == NULL) 238 return; 239 240 /* Sort our RA's - most recent first */ 241 sorted = ifp->ras; 242 ifp->ras = ifp->ras->next; 243 sorted->next = NULL; 244 for (rap = ifp->ras; rap && (ran = rap->next, 1); rap = ran) { 245 /* Are we the new head? */ 246 if (timercmp(&rap->received, &sorted->received, <)) { 247 rap->next = sorted; 248 sorted = rap; 249 continue; 250 } 251 /* Do we fit in the middle? */ 252 for (rat = sorted; rat->next; rat = rat->next) { 253 if (timercmp(&rap->received, &rat->next->received, <)) { 254 rap->next = rat->next; 255 rat->next = rap; 256 break; 257 } 258 } 259 /* We must be at the end */ 260 if (!rat->next) { 261 rat->next = rap; 262 rap->next = NULL; 263 } 264 } 265 } 266 267 void 268 ipv6rs_handledata(_unused void *arg) 269 { 270 ssize_t len, l, n, olen; 271 struct cmsghdr *cm; 272 int hoplimit; 273 struct in6_pktinfo pkt; 274 struct icmp6_hdr *icp; 275 struct interface *ifp; 276 const char *sfrom; 277 struct nd_router_advert *nd_ra; 278 struct nd_opt_prefix_info *pi; 279 struct nd_opt_mtu *mtu; 280 struct nd_opt_rdnss *rdnss; 281 struct nd_opt_dnssl *dnssl; 282 uint32_t lifetime; 283 uint8_t *p, *op; 284 struct in6_addr addr; 285 char buf[INET6_ADDRSTRLEN]; 286 const char *cbp; 287 struct ra *rap; 288 struct nd_opt_hdr *ndo; 289 struct ra_opt *rao, *raol; 290 char *opt; 291 struct timeval expire; 292 int has_dns; 293 294 len = recvmsg(sock, &rcvhdr, 0); 295 if (len == -1) { 296 syslog(LOG_ERR, "recvmsg: %m"); 297 return; 298 } 299 sfrom = inet_ntop(AF_INET6, &from.sin6_addr, 300 ntopbuf, INET6_ADDRSTRLEN); 301 if ((size_t)len < sizeof(struct nd_router_advert)) { 302 syslog(LOG_ERR, "IPv6 RA packet too short from %s", sfrom); 303 return; 304 } 305 306 pkt.ipi6_ifindex = hoplimit = 0; 307 for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&rcvhdr); 308 cm; 309 cm = (struct cmsghdr *)CMSG_NXTHDR(&rcvhdr, cm)) 310 { 311 if (cm->cmsg_level != IPPROTO_IPV6) 312 continue; 313 switch(cm->cmsg_type) { 314 case IPV6_PKTINFO: 315 if (cm->cmsg_len == CMSG_LEN(sizeof(pkt))) 316 memcpy(&pkt, CMSG_DATA(cm), sizeof(pkt)); 317 break; 318 case IPV6_HOPLIMIT: 319 if (cm->cmsg_len == CMSG_LEN(sizeof(int))) 320 memcpy(&hoplimit, CMSG_DATA(cm), sizeof(int)); 321 break; 322 } 323 } 324 325 if (pkt.ipi6_ifindex == 0 || hoplimit == 0) { 326 syslog(LOG_ERR, 327 "IPv6 RA did not contain index or hop limit from %s", 328 sfrom); 329 return; 330 } 331 332 icp = (struct icmp6_hdr *)rcvhdr.msg_iov[0].iov_base; 333 if (icp->icmp6_type != ND_ROUTER_ADVERT || 334 icp->icmp6_code != 0) 335 { 336 syslog(LOG_ERR, "invalid IPv6 type or code from %s", sfrom); 337 return; 338 } 339 340 if (!IN6_IS_ADDR_LINKLOCAL(&from.sin6_addr)) { 341 syslog(LOG_ERR, "RA recieved from non local IPv6 address %s", 342 sfrom); 343 return; 344 } 345 346 for (ifp = ifaces; ifp; ifp = ifp->next) 347 if (if_nametoindex(ifp->name) == (unsigned int)pkt.ipi6_ifindex) 348 break; 349 if (ifp == NULL) { 350 syslog(LOG_ERR,"received RA for unexpected interface from %s", 351 sfrom); 352 return; 353 } 354 for (rap = ifp->ras; rap; rap = rap->next) { 355 if (memcmp(rap->from.s6_addr, from.sin6_addr.s6_addr, 356 sizeof(rap->from.s6_addr)) == 0) 357 break; 358 } 359 360 /* We don't want to spam the log with the fact we got an RA every 361 * 30 seconds or so, so only spam the log if it's different. */ 362 if (options & DHCPCD_DEBUG || rap == NULL || 363 (rap->expired || rap->data_len != len || 364 memcmp(rap->data, (unsigned char *)icp, rap->data_len) != 0)) 365 { 366 if (rap) { 367 free(rap->data); 368 rap->data_len = 0; 369 } 370 syslog(LOG_INFO, "%s: Router Advertisement from %s", 371 ifp->name, sfrom); 372 } 373 374 if (rap == NULL) { 375 rap = xmalloc(sizeof(*rap)); 376 rap->next = ifp->ras; 377 rap->options = NULL; 378 ifp->ras = rap; 379 memcpy(rap->from.s6_addr, from.sin6_addr.s6_addr, 380 sizeof(rap->from.s6_addr)); 381 strlcpy(rap->sfrom, sfrom, sizeof(rap->sfrom)); 382 rap->data_len = 0; 383 } 384 if (rap->data_len == 0) { 385 rap->data = xmalloc(len); 386 memcpy(rap->data, icp, len); 387 rap->data_len = len; 388 } 389 390 get_monotonic(&rap->received); 391 nd_ra = (struct nd_router_advert *)icp; 392 rap->lifetime = ntohs(nd_ra->nd_ra_router_lifetime); 393 rap->expired = 0; 394 395 len -= sizeof(struct nd_router_advert); 396 p = ((uint8_t *)icp) + sizeof(struct nd_router_advert); 397 olen = 0; 398 lifetime = ~0U; 399 has_dns = 0; 400 for (olen = 0; len > 0; p += olen, len -= olen) { 401 if ((size_t)len < sizeof(struct nd_opt_hdr)) { 402 syslog(LOG_ERR, "%s: Short option", ifp->name); 403 break; 404 } 405 ndo = (struct nd_opt_hdr *)p; 406 olen = ndo->nd_opt_len * 8 ; 407 if (olen == 0) { 408 syslog(LOG_ERR, "%s: zero length option", ifp->name); 409 break; 410 } 411 if (olen > len) { 412 syslog(LOG_ERR, 413 "%s: Option length exceeds message", ifp->name); 414 break; 415 } 416 417 opt = NULL; 418 switch (ndo->nd_opt_type) { 419 case ND_OPT_PREFIX_INFORMATION: 420 pi = (struct nd_opt_prefix_info *)ndo; 421 if (pi->nd_opt_pi_len != 4) { 422 syslog(LOG_ERR, 423 "%s: invalid option len for prefix", 424 ifp->name); 425 break; 426 } 427 if (pi->nd_opt_pi_prefix_len > 128) { 428 syslog(LOG_ERR, "%s: invalid prefix len", 429 ifp->name); 430 break; 431 } 432 if (IN6_IS_ADDR_MULTICAST(&pi->nd_opt_pi_prefix) || 433 IN6_IS_ADDR_LINKLOCAL(&pi->nd_opt_pi_prefix)) 434 { 435 syslog(LOG_ERR, 436 "%s: invalid prefix in RA", ifp->name); 437 break; 438 } 439 opt = xstrdup(inet_ntop(AF_INET6, 440 pi->nd_opt_pi_prefix.s6_addr, 441 ntopbuf, INET6_ADDRSTRLEN)); 442 if (opt) { 443 rap->prefix_len = pi->nd_opt_pi_prefix_len; 444 rap->prefix_vltime = 445 ntohl(pi->nd_opt_pi_valid_time); 446 rap->prefix_pltime = 447 ntohl(pi->nd_opt_pi_preferred_time); 448 } 449 break; 450 451 case ND_OPT_MTU: 452 mtu = (struct nd_opt_mtu *)p; 453 snprintf(buf, sizeof(buf), "%d", 454 ntohl(mtu->nd_opt_mtu_mtu)); 455 opt = xstrdup(buf); 456 break; 457 458 case ND_OPT_RDNSS: 459 rdnss = (struct nd_opt_rdnss *)p; 460 lifetime = ntohl(rdnss->nd_opt_rdnss_lifetime); 461 op = (uint8_t *)ndo; 462 op += offsetof(struct nd_opt_rdnss, 463 nd_opt_rdnss_lifetime); 464 op += sizeof(rdnss->nd_opt_rdnss_lifetime); 465 l = 0; 466 for (n = ndo->nd_opt_len - 1; n > 1; n -= 2) { 467 memcpy(&addr.s6_addr, op, sizeof(addr.s6_addr)); 468 cbp = inet_ntop(AF_INET6, &addr, 469 ntopbuf, INET6_ADDRSTRLEN); 470 if (cbp == NULL) { 471 syslog(LOG_ERR, 472 "%s: invalid RDNSS address", 473 ifp->name); 474 } else { 475 if (opt) { 476 l = strlen(opt); 477 opt = xrealloc(opt, 478 l + strlen(cbp) + 2); 479 opt[l] = ' '; 480 strcpy(opt + l + 1, cbp); 481 } else 482 opt = xstrdup(cbp); 483 if (lifetime > 0) 484 has_dns = 1; 485 } 486 op += sizeof(addr.s6_addr); 487 } 488 break; 489 490 case ND_OPT_DNSSL: 491 dnssl = (struct nd_opt_dnssl *)p; 492 lifetime = ntohl(dnssl->nd_opt_dnssl_lifetime); 493 op = p + offsetof(struct nd_opt_dnssl, 494 nd_opt_dnssl_lifetime); 495 op += sizeof(dnssl->nd_opt_dnssl_lifetime); 496 n = (dnssl->nd_opt_dnssl_len - 1) * 8; 497 l = decode_rfc3397(NULL, 0, n, op); 498 if (l < 1) { 499 syslog(LOG_ERR, "%s: invalid DNSSL option", 500 ifp->name); 501 } else { 502 opt = xmalloc(l); 503 decode_rfc3397(opt, l, n, op); 504 } 505 break; 506 } 507 508 if (opt == NULL) 509 continue; 510 for (raol = NULL, rao = rap->options; 511 rao; 512 raol = rao, rao = rao->next) 513 { 514 if (rao->type == ndo->nd_opt_type && 515 strcmp(rao->option, opt) == 0) 516 break; 517 } 518 if (lifetime == 0) { 519 if (rao) { 520 if (raol) 521 raol->next = rao->next; 522 else 523 rap->options = rao->next; 524 free(rao->option); 525 free(rao); 526 } 527 continue; 528 } 529 530 if (rao == NULL) { 531 rao = xmalloc(sizeof(*rao)); 532 rao->next = rap->options; 533 rap->options = rao; 534 rao->type = ndo->nd_opt_type; 535 rao->option = opt; 536 } else 537 free(opt); 538 if (lifetime == ~0U) 539 timerclear(&rao->expire); 540 else { 541 expire.tv_sec = lifetime; 542 expire.tv_usec = 0; 543 timeradd(&rap->received, &expire, &rao->expire); 544 } 545 } 546 547 ipv6rs_sort(ifp); 548 run_script_reason(ifp, options & DHCPCD_TEST ? "TEST" : "ROUTERADVERT"); 549 if (options & DHCPCD_TEST) 550 exit(EXIT_SUCCESS); 551 552 /* If we don't require RDNSS then set has_dns = 1 so we fork */ 553 if (!(ifp->state->options->options & DHCPCD_IPV6RA_REQRDNSS)) 554 has_dns = 1; 555 556 if (has_dns) 557 delete_q_timeout(0, handle_exit_timeout, NULL); 558 delete_timeout(NULL, ifp); 559 ipv6rs_expire(ifp); 560 if (has_dns) 561 daemonise(); 562 else if (options & DHCPCD_DAEMONISE && !(options & DHCPCD_DAEMONISED)) 563 syslog(LOG_WARNING, 564 "%s: did not fork due to an absent RDNSS option in the RA", 565 ifp->name); 566 } 567 568 ssize_t 569 ipv6rs_env(char **env, const char *prefix, const struct interface *ifp) 570 { 571 ssize_t l; 572 struct timeval now; 573 const struct ra *rap; 574 const struct ra_opt *rao; 575 int i; 576 char buffer[32], buffer2[32]; 577 const char *optn; 578 579 l = 0; 580 get_monotonic(&now); 581 for (rap = ifp->ras, i = 1; rap; rap = rap->next, i++) { 582 if (env) { 583 snprintf(buffer, sizeof(buffer), 584 "ra%d_from", i); 585 setvar(&env, prefix, buffer, rap->sfrom); 586 } 587 l++; 588 589 for (rao = rap->options; rao; rao = rao->next) { 590 if (rao->option == NULL) 591 continue; 592 if (env == NULL) { 593 switch (rao->type) { 594 case ND_OPT_PREFIX_INFORMATION: 595 l += 4; 596 break; 597 default: 598 l++; 599 } 600 continue; 601 } 602 switch (rao->type) { 603 case ND_OPT_PREFIX_INFORMATION: 604 optn = "prefix"; 605 break; 606 case ND_OPT_MTU: 607 optn = "mtu"; 608 break; 609 case ND_OPT_RDNSS: 610 optn = "rdnss"; 611 break; 612 case ND_OPT_DNSSL: 613 optn = "dnssl"; 614 break; 615 default: 616 continue; 617 } 618 snprintf(buffer, sizeof(buffer), "ra%d_%s", i, optn); 619 setvar(&env, prefix, buffer, rao->option); 620 l++; 621 switch (rao->type) { 622 case ND_OPT_PREFIX_INFORMATION: 623 snprintf(buffer, sizeof(buffer), 624 "ra%d_prefix_len", i); 625 snprintf(buffer2, sizeof(buffer2), 626 "%d", rap->prefix_len); 627 setvar(&env, prefix, buffer, buffer2); 628 629 snprintf(buffer, sizeof(buffer), 630 "ra%d_prefix_vltime", i); 631 snprintf(buffer2, sizeof(buffer2), 632 "%d", rap->prefix_vltime); 633 setvar(&env, prefix, buffer, buffer2); 634 635 snprintf(buffer, sizeof(buffer), 636 "ra%d_prefix_pltime", i); 637 snprintf(buffer2, sizeof(buffer2), 638 "%d", rap->prefix_pltime); 639 setvar(&env, prefix, buffer, buffer2); 640 l += 3; 641 break; 642 } 643 644 } 645 } 646 647 if (env) 648 setvard(&env, prefix, "ra_count", i - 1); 649 l++; 650 return l; 651 } 652 653 static void 654 ipv6rs_free_opts(struct ra *rap) 655 { 656 struct ra_opt *rao, *raon; 657 658 for (rao = rap->options; rao && (raon = rao->next, 1); rao = raon) { 659 free(rao->option); 660 free(rao); 661 } 662 } 663 664 void 665 ipv6rs_free(struct interface *ifp) 666 { 667 struct ra *rap, *ran; 668 669 free(ifp->rs); 670 ifp->rs = NULL; 671 for (rap = ifp->ras; rap && (ran = rap->next, 1); rap = ran) { 672 ipv6rs_free_opts(rap); 673 free(rap->data); 674 free(rap); 675 } 676 ifp->ras = NULL; 677 } 678 679 void 680 ipv6rs_expire(void *arg) 681 { 682 struct interface *ifp; 683 struct ra *rap, *ran, *ral; 684 struct ra_opt *rao, *raol, *raon; 685 struct timeval now, lt, expire, next; 686 int expired; 687 uint32_t expire_secs; 688 689 ifp = arg; 690 get_monotonic(&now); 691 expired = 0; 692 expire_secs = ~0U; 693 timerclear(&next); 694 695 for (rap = ifp->ras, ral = NULL; 696 rap && (ran = rap->next, 1); 697 ral = rap, rap = ran) 698 { 699 lt.tv_sec = rap->lifetime; 700 lt.tv_usec = 0; 701 timeradd(&rap->received, <, &expire); 702 if (timercmp(&now, &expire, >)) { 703 syslog(LOG_INFO, "%s: %s: expired Router Advertisement", 704 ifp->name, rap->sfrom); 705 rap->expired = expired = 1; 706 if (ral) 707 ral->next = ran; 708 else 709 ifp->ras = ran; 710 ipv6rs_free_opts(rap); 711 free(rap); 712 continue; 713 } 714 timersub(&expire, &now, <); 715 if (!timerisset(&next) || timercmp(&next, <, >)) 716 next = lt; 717 718 for (rao = rap->options, raol = NULL; 719 rao && (raon = rao->next); 720 raol = rao, rao = raon) 721 { 722 if (!timerisset(&rao->expire)) 723 continue; 724 if (timercmp(&now, &rao->expire, >)) { 725 syslog(LOG_INFO, 726 "%s: %s: expired option %d", 727 ifp->name, rap->sfrom, rao->type); 728 rap->expired = expired = 1; 729 if (raol) 730 raol = raon; 731 else 732 rap->options = raon; 733 continue; 734 } 735 timersub(&rao->expire, &now, <); 736 if (!timerisset(&next) || timercmp(&next, <, >)) 737 next = lt; 738 } 739 } 740 741 if (timerisset(&next)) 742 add_timeout_tv(&next, ipv6rs_expire, ifp); 743 if (expired) 744 run_script_reason(ifp, "ROUTERADVERT"); 745 } 746 747 int 748 ipv6rs_start(struct interface *ifp) 749 { 750 751 delete_timeout(NULL, ifp); 752 753 /* Always make a new probe as the underlying hardware 754 * address could have changed. */ 755 ipv6rs_makeprobe(ifp); 756 if (ifp->rs == NULL) 757 return -1; 758 759 ifp->rsprobes = 0; 760 ipv6rs_sendprobe(ifp); 761 return 0; 762 } 763