1/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley 2 3 This program is free software; you can redistribute it and/or modify 4 it under the terms of the GNU General Public License as published by 5 the Free Software Foundation; version 2 dated June, 1991, or 6 (at your option) version 3 dated 29 June, 2007. 7 8 This program is distributed in the hope that it will be useful, 9 but WITHOUT ANY WARRANTY; without even the implied warranty of 10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 GNU General Public License for more details. 12 13 You should have received a copy of the GNU General Public License 14 along with this program. If not, see <http://www.gnu.org/licenses/>. 15*/ 16 17#include "dnsmasq.h" 18 19#ifdef HAVE_DHCP6 20 21#include <netinet/icmp6.h> 22 23struct iface_param { 24 struct dhcp_context *current; 25 struct dhcp_relay *relay; 26 struct in6_addr fallback, relay_local, ll_addr, ula_addr; 27 int ind, addr_match; 28}; 29 30struct mac_param { 31 struct in6_addr *target; 32 unsigned char *mac; 33 unsigned int maclen; 34}; 35 36 37static int complete_context6(struct in6_addr *local, int prefix, 38 int scope, int if_index, int flags, 39 unsigned int preferred, unsigned int valid, void *vparam); 40static int find_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv); 41static int make_duid1(int index, unsigned int type, char *mac, size_t maclen, void *parm); 42 43void dhcp6_init(void) 44{ 45 int fd; 46 struct sockaddr_in6 saddr; 47#if defined(IPV6_TCLASS) && defined(IPTOS_CLASS_CS6) 48 int class = IPTOS_CLASS_CS6; 49#endif 50 int oneopt = 1; 51 52 if ((fd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP)) == -1 || 53#if defined(IPV6_TCLASS) && defined(IPTOS_CLASS_CS6) 54 setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &class, sizeof(class)) == -1 || 55#endif 56 setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &oneopt, sizeof(oneopt)) == -1 || 57 !fix_fd(fd) || 58 !set_ipv6pktinfo(fd)) 59 die (_("cannot create DHCPv6 socket: %s"), NULL, EC_BADNET); 60 61 /* When bind-interfaces is set, there might be more than one dnmsasq 62 instance binding port 547. That's OK if they serve different networks. 63 Need to set REUSEADDR|REUSEPORT to make this posible. 64 Handle the case that REUSEPORT is defined, but the kernel doesn't 65 support it. This handles the introduction of REUSEPORT on Linux. */ 66 if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND)) 67 { 68 int rc = 0; 69 70#ifdef SO_REUSEPORT 71 if ((rc = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &oneopt, sizeof(oneopt))) == -1 && 72 errno == ENOPROTOOPT) 73 rc = 0; 74#endif 75 76 if (rc != -1) 77 rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt)); 78 79 if (rc == -1) 80 die(_("failed to set SO_REUSE{ADDR|PORT} on DHCPv6 socket: %s"), NULL, EC_BADNET); 81 } 82 83 memset(&saddr, 0, sizeof(saddr)); 84#ifdef HAVE_SOCKADDR_SA_LEN 85 saddr.sin6_len = sizeof(struct sockaddr_in6); 86#endif 87 saddr.sin6_family = AF_INET6; 88 saddr.sin6_addr = in6addr_any; 89 saddr.sin6_port = htons(DHCPV6_SERVER_PORT); 90 91 if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in6))) 92 die(_("failed to bind DHCPv6 server socket: %s"), NULL, EC_BADNET); 93 94 daemon->dhcp6fd = fd; 95} 96 97void dhcp6_packet(time_t now) 98{ 99 struct dhcp_context *context; 100 struct dhcp_relay *relay; 101 struct iface_param parm; 102 struct cmsghdr *cmptr; 103 struct msghdr msg; 104 int if_index = 0; 105 union { 106 struct cmsghdr align; /* this ensures alignment */ 107 char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))]; 108 } control_u; 109 struct sockaddr_in6 from; 110 ssize_t sz; 111 struct ifreq ifr; 112 struct iname *tmp; 113 unsigned short port; 114 struct in6_addr dst_addr; 115 116 memset(&dst_addr, 0, sizeof(dst_addr)); 117 118 msg.msg_control = control_u.control6; 119 msg.msg_controllen = sizeof(control_u); 120 msg.msg_flags = 0; 121 msg.msg_name = &from; 122 msg.msg_namelen = sizeof(from); 123 msg.msg_iov = &daemon->dhcp_packet; 124 msg.msg_iovlen = 1; 125 126 if ((sz = recv_dhcp_packet(daemon->dhcp6fd, &msg)) == -1) 127 return; 128 129 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) 130 if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo) 131 { 132 union { 133 unsigned char *c; 134 struct in6_pktinfo *p; 135 } p; 136 p.c = CMSG_DATA(cmptr); 137 138 if_index = p.p->ipi6_ifindex; 139 dst_addr = p.p->ipi6_addr; 140 } 141 142 if (!indextoname(daemon->dhcp6fd, if_index, ifr.ifr_name)) 143 return; 144 145 if ((port = relay_reply6(&from, sz, ifr.ifr_name)) == 0) 146 { 147 struct dhcp_bridge *bridge, *alias; 148 149 for (tmp = daemon->if_except; tmp; tmp = tmp->next) 150 if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name)) 151 return; 152 153 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next) 154 if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name)) 155 return; 156 157 parm.current = NULL; 158 parm.relay = NULL; 159 memset(&parm.relay_local, 0, IN6ADDRSZ); 160 parm.ind = if_index; 161 parm.addr_match = 0; 162 memset(&parm.fallback, 0, IN6ADDRSZ); 163 memset(&parm.ll_addr, 0, IN6ADDRSZ); 164 memset(&parm.ula_addr, 0, IN6ADDRSZ); 165 166 /* If the interface on which the DHCPv6 request was received is 167 an alias of some other interface (as specified by the 168 --bridge-interface option), change parm.ind so that we look 169 for DHCPv6 contexts associated with the aliased interface 170 instead of with the aliasing one. */ 171 for (bridge = daemon->bridges; bridge; bridge = bridge->next) 172 { 173 for (alias = bridge->alias; alias; alias = alias->next) 174 if (wildcard_matchn(alias->iface, ifr.ifr_name, IF_NAMESIZE)) 175 { 176 parm.ind = if_nametoindex(bridge->iface); 177 if (!parm.ind) 178 { 179 my_syslog(MS_DHCP | LOG_WARNING, 180 _("unknown interface %s in bridge-interface"), 181 bridge->iface); 182 return; 183 } 184 break; 185 } 186 if (alias) 187 break; 188 } 189 190 for (context = daemon->dhcp6; context; context = context->next) 191 if (IN6_IS_ADDR_UNSPECIFIED(&context->start6) && context->prefix == 0) 192 { 193 /* wildcard context for DHCP-stateless only */ 194 parm.current = context; 195 context->current = NULL; 196 } 197 else 198 { 199 /* unlinked contexts are marked by context->current == context */ 200 context->current = context; 201 memset(&context->local6, 0, IN6ADDRSZ); 202 } 203 204 for (relay = daemon->relay6; relay; relay = relay->next) 205 relay->current = relay; 206 207 if (!iface_enumerate(AF_INET6, &parm, complete_context6)) 208 return; 209 210 if (daemon->if_names || daemon->if_addrs) 211 { 212 213 for (tmp = daemon->if_names; tmp; tmp = tmp->next) 214 if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name)) 215 break; 216 217 if (!tmp && !parm.addr_match) 218 return; 219 } 220 221 if (parm.relay) 222 { 223 /* Ignore requests sent to the ALL_SERVERS multicast address for relay when 224 we're listening there for DHCPv6 server reasons. */ 225 struct in6_addr all_servers; 226 227 inet_pton(AF_INET6, ALL_SERVERS, &all_servers); 228 229 if (!IN6_ARE_ADDR_EQUAL(&dst_addr, &all_servers)) 230 relay_upstream6(parm.relay, sz, &from.sin6_addr, from.sin6_scope_id); 231 return; 232 } 233 234 /* May have configured relay, but not DHCP server */ 235 if (!daemon->doing_dhcp6) 236 return; 237 238 lease_prune(NULL, now); /* lose any expired leases */ 239 240 port = dhcp6_reply(parm.current, if_index, ifr.ifr_name, &parm.fallback, 241 &parm.ll_addr, &parm.ula_addr, sz, &from.sin6_addr, now); 242 243 lease_update_file(now); 244 lease_update_dns(0); 245 } 246 247 /* The port in the source address of the original request should 248 be correct, but at least once client sends from the server port, 249 so we explicitly send to the client port to a client, and the 250 server port to a relay. */ 251 if (port != 0) 252 { 253 from.sin6_port = htons(port); 254 while (retry_send(sendto(daemon->dhcp6fd, daemon->outpacket.iov_base, 255 save_counter(0), 0, (struct sockaddr *)&from, 256 sizeof(from)))); 257 } 258} 259 260void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, unsigned int *maclenp, unsigned int *mactypep) 261{ 262 /* Recieving a packet from a host does not populate the neighbour 263 cache, so we send a neighbour discovery request if we can't 264 find the sender. Repeat a few times in case of packet loss. */ 265 266 struct neigh_packet neigh; 267 struct sockaddr_in6 addr; 268 struct mac_param mac_param; 269 int i; 270 271 neigh.type = ND_NEIGHBOR_SOLICIT; 272 neigh.code = 0; 273 neigh.reserved = 0; 274 neigh.target = *client; 275 /* RFC4443 section-2.3: checksum has to be zero to be calculated */ 276 neigh.checksum = 0; 277 278 memset(&addr, 0, sizeof(addr)); 279#ifdef HAVE_SOCKADDR_SA_LEN 280 addr.sin6_len = sizeof(struct sockaddr_in6); 281#endif 282 addr.sin6_family = AF_INET6; 283 addr.sin6_port = htons(IPPROTO_ICMPV6); 284 addr.sin6_addr = *client; 285 addr.sin6_scope_id = iface; 286 287 mac_param.target = client; 288 mac_param.maclen = 0; 289 mac_param.mac = mac; 290 291 for (i = 0; i < 5; i++) 292 { 293 struct timespec ts; 294 295 iface_enumerate(AF_UNSPEC, &mac_param, find_mac); 296 297 if (mac_param.maclen != 0) 298 break; 299 300 sendto(daemon->icmp6fd, &neigh, sizeof(neigh), 0, (struct sockaddr *)&addr, sizeof(addr)); 301 302 ts.tv_sec = 0; 303 ts.tv_nsec = 100000000; /* 100ms */ 304 nanosleep(&ts, NULL); 305 } 306 307 *maclenp = mac_param.maclen; 308 *mactypep = ARPHRD_ETHER; 309} 310 311static int find_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv) 312{ 313 struct mac_param *parm = parmv; 314 315 if (family == AF_INET6 && IN6_ARE_ADDR_EQUAL(parm->target, (struct in6_addr *)addrp)) 316 { 317 if (maclen <= DHCP_CHADDR_MAX) 318 { 319 parm->maclen = maclen; 320 memcpy(parm->mac, mac, maclen); 321 } 322 323 return 0; /* found, abort */ 324 } 325 326 return 1; 327} 328 329static int complete_context6(struct in6_addr *local, int prefix, 330 int scope, int if_index, int flags, unsigned int preferred, 331 unsigned int valid, void *vparam) 332{ 333 struct dhcp_context *context; 334 struct dhcp_relay *relay; 335 struct iface_param *param = vparam; 336 struct iname *tmp; 337 338 (void)scope; /* warning */ 339 340 if (if_index == param->ind) 341 { 342 if (IN6_IS_ADDR_LINKLOCAL(local)) 343 param->ll_addr = *local; 344 else if (IN6_IS_ADDR_ULA(local)) 345 param->ula_addr = *local; 346 347 if (!IN6_IS_ADDR_LOOPBACK(local) && 348 !IN6_IS_ADDR_LINKLOCAL(local) && 349 !IN6_IS_ADDR_MULTICAST(local)) 350 { 351 /* if we have --listen-address config, see if the 352 arrival interface has a matching address. */ 353 for (tmp = daemon->if_addrs; tmp; tmp = tmp->next) 354 if (tmp->addr.sa.sa_family == AF_INET6 && 355 IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, local)) 356 param->addr_match = 1; 357 358 /* Determine a globally address on the arrival interface, even 359 if we have no matching dhcp-context, because we're only 360 allocating on remote subnets via relays. This 361 is used as a default for the DNS server option. */ 362 param->fallback = *local; 363 364 for (context = daemon->dhcp6; context; context = context->next) 365 { 366 if ((context->flags & CONTEXT_DHCP) && 367 !(context->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) && 368 prefix <= context->prefix && 369 is_same_net6(local, &context->start6, context->prefix) && 370 is_same_net6(local, &context->end6, context->prefix)) 371 { 372 373 374 /* link it onto the current chain if we've not seen it before */ 375 if (context->current == context) 376 { 377 struct dhcp_context *tmp, **up; 378 379 /* use interface values only for contructed contexts */ 380 if (!(context->flags & CONTEXT_CONSTRUCTED)) 381 preferred = valid = 0xffffffff; 382 else if (flags & IFACE_DEPRECATED) 383 preferred = 0; 384 385 if (context->flags & CONTEXT_DEPRECATE) 386 preferred = 0; 387 388 /* order chain, longest preferred time first */ 389 for (up = ¶m->current, tmp = param->current; tmp; tmp = tmp->current) 390 if (tmp->preferred <= preferred) 391 break; 392 else 393 up = &tmp->current; 394 395 context->current = *up; 396 *up = context; 397 context->local6 = *local; 398 context->preferred = preferred; 399 context->valid = valid; 400 } 401 } 402 } 403 } 404 405 for (relay = daemon->relay6; relay; relay = relay->next) 406 if (IN6_ARE_ADDR_EQUAL(local, &relay->local.addr.addr6) && relay->current == relay && 407 (IN6_IS_ADDR_UNSPECIFIED(¶m->relay_local) || IN6_ARE_ADDR_EQUAL(local, ¶m->relay_local))) 408 { 409 relay->current = param->relay; 410 param->relay = relay; 411 param->relay_local = *local; 412 } 413 414 } 415 416 return 1; 417} 418 419struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct in6_addr *net, int prefix, u64 addr) 420{ 421 struct dhcp_config *config; 422 423 for (config = configs; config; config = config->next) 424 if ((config->flags & CONFIG_ADDR6) && 425 is_same_net6(&config->addr6, net, prefix) && 426 (prefix == 128 || addr6part(&config->addr6) == addr)) 427 return config; 428 429 return NULL; 430} 431 432struct dhcp_context *address6_allocate(struct dhcp_context *context, unsigned char *clid, int clid_len, int temp_addr, 433 int iaid, int serial, struct dhcp_netid *netids, int plain_range, struct in6_addr *ans) 434{ 435 /* Find a free address: exclude anything in use and anything allocated to 436 a particular hwaddr/clientid/hostname in our configuration. 437 Try to return from contexts which match netids first. 438 439 Note that we assume the address prefix lengths are 64 or greater, so we can 440 get by with 64 bit arithmetic. 441*/ 442 443 u64 start, addr; 444 struct dhcp_context *c, *d; 445 int i, pass; 446 u64 j; 447 448 /* hash hwaddr: use the SDBM hashing algorithm. This works 449 for MAC addresses, let's see how it manages with client-ids! 450 For temporary addresses, we generate a new random one each time. */ 451 if (temp_addr) 452 j = rand64(); 453 else 454 for (j = iaid, i = 0; i < clid_len; i++) 455 j += clid[i] + (j << 6) + (j << 16) - j; 456 457 for (pass = 0; pass <= plain_range ? 1 : 0; pass++) 458 for (c = context; c; c = c->current) 459 if (c->flags & (CONTEXT_DEPRECATE | CONTEXT_STATIC | CONTEXT_RA_STATELESS | CONTEXT_USED)) 460 continue; 461 else if (!match_netid(c->filter, netids, pass)) 462 continue; 463 else 464 { 465 if (!temp_addr && option_bool(OPT_CONSEC_ADDR)) 466 /* seed is largest extant lease addr in this context */ 467 start = lease_find_max_addr6(c) + serial; 468 else 469 start = addr6part(&c->start6) + ((j + c->addr_epoch) % (1 + addr6part(&c->end6) - addr6part(&c->start6))); 470 471 /* iterate until we find a free address. */ 472 addr = start; 473 474 do { 475 /* eliminate addresses in use by the server. */ 476 for (d = context; d; d = d->current) 477 if (addr == addr6part(&d->local6)) 478 break; 479 480 if (!d && 481 !lease6_find_by_addr(&c->start6, c->prefix, addr) && 482 !config_find_by_address6(daemon->dhcp_conf, &c->start6, c->prefix, addr)) 483 { 484 *ans = c->start6; 485 setaddr6part (ans, addr); 486 return c; 487 } 488 489 addr++; 490 491 if (addr == addr6part(&c->end6) + 1) 492 addr = addr6part(&c->start6); 493 494 } while (addr != start); 495 } 496 497 return NULL; 498} 499 500/* can dynamically allocate addr */ 501struct dhcp_context *address6_available(struct dhcp_context *context, 502 struct in6_addr *taddr, 503 struct dhcp_netid *netids, 504 int plain_range) 505{ 506 u64 start, end, addr = addr6part(taddr); 507 struct dhcp_context *tmp; 508 509 for (tmp = context; tmp; tmp = tmp->current) 510 { 511 start = addr6part(&tmp->start6); 512 end = addr6part(&tmp->end6); 513 514 if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_RA_STATELESS)) && 515 is_same_net6(&tmp->start6, taddr, tmp->prefix) && 516 is_same_net6(&tmp->end6, taddr, tmp->prefix) && 517 addr >= start && 518 addr <= end && 519 match_netid(tmp->filter, netids, plain_range)) 520 return tmp; 521 } 522 523 return NULL; 524} 525 526/* address OK if configured */ 527struct dhcp_context *address6_valid(struct dhcp_context *context, 528 struct in6_addr *taddr, 529 struct dhcp_netid *netids, 530 int plain_range) 531{ 532 struct dhcp_context *tmp; 533 534 for (tmp = context; tmp; tmp = tmp->current) 535 if (is_same_net6(&tmp->start6, taddr, tmp->prefix) && 536 match_netid(tmp->filter, netids, plain_range)) 537 return tmp; 538 539 return NULL; 540} 541 542int config_valid(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr) 543{ 544 if (!config || !(config->flags & CONFIG_ADDR6)) 545 return 0; 546 547 if ((config->flags & CONFIG_WILDCARD) && context->prefix == 64) 548 { 549 *addr = context->start6; 550 setaddr6part(addr, addr6part(&config->addr6)); 551 return 1; 552 } 553 554 if (is_same_net6(&context->start6, &config->addr6, context->prefix)) 555 { 556 *addr = config->addr6; 557 return 1; 558 } 559 560 return 0; 561} 562 563void make_duid(time_t now) 564{ 565 (void)now; 566 567 if (daemon->duid_config) 568 { 569 unsigned char *p; 570 571 daemon->duid = p = safe_malloc(daemon->duid_config_len + 6); 572 daemon->duid_len = daemon->duid_config_len + 6; 573 PUTSHORT(2, p); /* DUID_EN */ 574 PUTLONG(daemon->duid_enterprise, p); 575 memcpy(p, daemon->duid_config, daemon->duid_config_len); 576 } 577 else 578 { 579 time_t newnow = 0; 580 581 /* If we have no persistent lease database, or a non-stable RTC, use DUID_LL (newnow == 0) */ 582#ifndef HAVE_BROKEN_RTC 583 /* rebase epoch to 1/1/2000 */ 584 if (!option_bool(OPT_LEASE_RO) || daemon->lease_change_command) 585 newnow = now - 946684800; 586#endif 587 588 iface_enumerate(AF_LOCAL, &newnow, make_duid1); 589 590 if(!daemon->duid) 591 die("Cannot create DHCPv6 server DUID: %s", NULL, EC_MISC); 592 } 593} 594 595static int make_duid1(int index, unsigned int type, char *mac, size_t maclen, void *parm) 596{ 597 /* create DUID as specified in RFC3315. We use the MAC of the 598 first interface we find that isn't loopback or P-to-P and 599 has address-type < 256. Address types above 256 are things like 600 tunnels which don't have usable MAC addresses. */ 601 602 unsigned char *p; 603 (void)index; 604 (void)parm; 605 time_t newnow = *((time_t *)parm); 606 607 if (type >= 256) 608 return 1; 609 610 if (newnow == 0) 611 { 612 daemon->duid = p = safe_malloc(maclen + 4); 613 daemon->duid_len = maclen + 4; 614 PUTSHORT(3, p); /* DUID_LL */ 615 PUTSHORT(type, p); /* address type */ 616 } 617 else 618 { 619 daemon->duid = p = safe_malloc(maclen + 8); 620 daemon->duid_len = maclen + 8; 621 PUTSHORT(1, p); /* DUID_LLT */ 622 PUTSHORT(type, p); /* address type */ 623 PUTLONG(*((time_t *)parm), p); /* time */ 624 } 625 626 memcpy(p, mac, maclen); 627 628 return 0; 629} 630 631struct cparam { 632 time_t now; 633 int newone, newname; 634}; 635 636static int construct_worker(struct in6_addr *local, int prefix, 637 int scope, int if_index, int flags, 638 int preferred, int valid, void *vparam) 639{ 640 char ifrn_name[IFNAMSIZ]; 641 struct in6_addr start6, end6; 642 struct dhcp_context *template, *context; 643 644 (void)scope; 645 (void)flags; 646 (void)valid; 647 (void)preferred; 648 649 struct cparam *param = vparam; 650 651 if (IN6_IS_ADDR_LOOPBACK(local) || 652 IN6_IS_ADDR_LINKLOCAL(local) || 653 IN6_IS_ADDR_MULTICAST(local)) 654 return 1; 655 656 if (!(flags & IFACE_PERMANENT)) 657 return 1; 658 659 if (flags & IFACE_DEPRECATED) 660 return 1; 661 662 if (!indextoname(daemon->icmp6fd, if_index, ifrn_name)) 663 return 0; 664 665 for (template = daemon->dhcp6; template; template = template->next) 666 if (!(template->flags & CONTEXT_TEMPLATE)) 667 { 668 /* non-template entries, just fill in interface and local addresses */ 669 if (prefix <= template->prefix && 670 is_same_net6(local, &template->start6, template->prefix) && 671 is_same_net6(local, &template->end6, template->prefix)) 672 { 673 template->if_index = if_index; 674 template->local6 = *local; 675 } 676 677 } 678 else if (wildcard_match(template->template_interface, ifrn_name) && 679 template->prefix >= prefix) 680 { 681 start6 = *local; 682 setaddr6part(&start6, addr6part(&template->start6)); 683 end6 = *local; 684 setaddr6part(&end6, addr6part(&template->end6)); 685 686 for (context = daemon->dhcp6; context; context = context->next) 687 if ((context->flags & CONTEXT_CONSTRUCTED) && 688 IN6_ARE_ADDR_EQUAL(&start6, &context->start6) && 689 IN6_ARE_ADDR_EQUAL(&end6, &context->end6)) 690 { 691 int flags = context->flags; 692 context->flags &= ~(CONTEXT_GC | CONTEXT_OLD); 693 if (flags & CONTEXT_OLD) 694 { 695 /* address went, now it's back */ 696 log_context(AF_INET6, context); 697 /* fast RAs for a while */ 698 ra_start_unsolicted(param->now, context); 699 param->newone = 1; 700 /* Add address to name again */ 701 if (context->flags & CONTEXT_RA_NAME) 702 param->newname = 1; 703 } 704 break; 705 } 706 707 if (!context && (context = whine_malloc(sizeof (struct dhcp_context)))) 708 { 709 *context = *template; 710 context->start6 = start6; 711 context->end6 = end6; 712 context->flags &= ~CONTEXT_TEMPLATE; 713 context->flags |= CONTEXT_CONSTRUCTED; 714 context->if_index = if_index; 715 context->local6 = *local; 716 context->saved_valid = 0; 717 718 context->next = daemon->dhcp6; 719 daemon->dhcp6 = context; 720 721 ra_start_unsolicted(param->now, context); 722 /* we created a new one, need to call 723 lease_update_file to get periodic functions called */ 724 param->newone = 1; 725 726 /* Will need to add new putative SLAAC addresses to existing leases */ 727 if (context->flags & CONTEXT_RA_NAME) 728 param->newname = 1; 729 730 log_context(AF_INET6, context); 731 } 732 } 733 734 return 1; 735} 736 737void dhcp_construct_contexts(time_t now) 738{ 739 struct dhcp_context *context, *tmp, **up; 740 struct cparam param; 741 param.newone = 0; 742 param.newname = 0; 743 param.now = now; 744 745 for (context = daemon->dhcp6; context; context = context->next) 746 if (context->flags & CONTEXT_CONSTRUCTED) 747 context->flags |= CONTEXT_GC; 748 749 iface_enumerate(AF_INET6, ¶m, construct_worker); 750 751 for (up = &daemon->dhcp6, context = daemon->dhcp6; context; context = tmp) 752 { 753 754 tmp = context->next; 755 756 if (context->flags & CONTEXT_GC && !(context->flags & CONTEXT_OLD)) 757 { 758 if ((context->flags & CONTEXT_RA) || option_bool(OPT_RA)) 759 { 760 /* previously constructed context has gone. advertise it's demise */ 761 context->flags |= CONTEXT_OLD; 762 context->address_lost_time = now; 763 /* Apply same ceiling of configured lease time as in radv.c */ 764 if (context->saved_valid > context->lease_time) 765 context->saved_valid = context->lease_time; 766 /* maximum time is 2 hours, from RFC */ 767 if (context->saved_valid > 7200) /* 2 hours */ 768 context->saved_valid = 7200; 769 ra_start_unsolicted(now, context); 770 param.newone = 1; /* include deletion */ 771 772 if (context->flags & CONTEXT_RA_NAME) 773 param.newname = 1; 774 775 log_context(AF_INET6, context); 776 777 up = &context->next; 778 } 779 else 780 { 781 /* we were never doing RA for this, so free now */ 782 *up = context->next; 783 free(context); 784 } 785 } 786 else 787 up = &context->next; 788 } 789 790 if (param.newone) 791 { 792 if (daemon->dhcp || daemon->doing_dhcp6) 793 { 794 if (param.newname) 795 lease_update_slaac(now); 796 lease_update_file(now); 797 } 798 else 799 /* Not doing DHCP, so no lease system, manage alarms for ra only */ 800 send_alarm(periodic_ra(now), now); 801 } 802} 803 804#endif 805 806 807