]> git.ipfire.org Git - people/ms/dnsmasq.git/blob - src/dhcp6.c
Support MAC addresses in dhcp-host and dhcp-mac for DHCPv6.
[people/ms/dnsmasq.git] / src / dhcp6.c
1 /* dnsmasq is Copyright (c) 2000-2013 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
23 struct iface_param {
24 struct dhcp_context *current;
25 struct dhcp_relay *relay;
26 struct in6_addr fallback, relay_local;
27 int ind, addr_match;
28 };
29
30 struct mac_param {
31 struct in6_addr *target;
32 unsigned char mac[DHCP_CHADDR_MAX];
33 unsigned int maclen;
34 };
35
36
37 static 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);
40 static int find_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv);
41 static int make_duid1(int index, unsigned int type, char *mac, size_t maclen, void *parm);
42
43 void 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
97 void dhcp6_packet(time_t now)
98 {
99 struct dhcp_context *context;
100 struct dhcp_relay *relay;
101 struct iface_param parm;
102 struct mac_param mac_param;
103 struct cmsghdr *cmptr;
104 struct msghdr msg;
105 int if_index = 0;
106 union {
107 struct cmsghdr align; /* this ensures alignment */
108 char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
109 } control_u;
110 struct sockaddr_in6 from;
111 ssize_t sz;
112 struct ifreq ifr;
113 struct iname *tmp;
114 unsigned short port;
115 struct in6_addr dst_addr;
116
117 msg.msg_control = control_u.control6;
118 msg.msg_controllen = sizeof(control_u);
119 msg.msg_flags = 0;
120 msg.msg_name = &from;
121 msg.msg_namelen = sizeof(from);
122 msg.msg_iov = &daemon->dhcp_packet;
123 msg.msg_iovlen = 1;
124
125 if ((sz = recv_dhcp_packet(daemon->dhcp6fd, &msg)) == -1)
126 return;
127
128 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
129 if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
130 {
131 union {
132 unsigned char *c;
133 struct in6_pktinfo *p;
134 } p;
135 p.c = CMSG_DATA(cmptr);
136
137 if_index = p.p->ipi6_ifindex;
138 dst_addr = p.p->ipi6_addr;
139 }
140
141 if (!indextoname(daemon->dhcp6fd, if_index, ifr.ifr_name))
142 return;
143
144 if ((port = relay_reply6(&from, sz, ifr.ifr_name)) == 0)
145 {
146 int i;
147
148 for (tmp = daemon->if_except; tmp; tmp = tmp->next)
149 if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
150 return;
151
152 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
153 if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
154 return;
155
156 parm.current = NULL;
157 parm.relay = NULL;
158 memset(&parm.relay_local, 0, IN6ADDRSZ);
159 parm.ind = if_index;
160 parm.addr_match = 0;
161 memset(&parm.fallback, 0, IN6ADDRSZ);
162
163 for (context = daemon->dhcp6; context; context = context->next)
164 if (IN6_IS_ADDR_UNSPECIFIED(&context->start6) && context->prefix == 0)
165 {
166 /* wildcard context for DHCP-stateless only */
167 parm.current = context;
168 context->current = NULL;
169 }
170 else
171 {
172 /* unlinked contexts are marked by context->current == context */
173 context->current = context;
174 memset(&context->local6, 0, IN6ADDRSZ);
175 }
176
177 for (relay = daemon->relay6; relay; relay = relay->next)
178 relay->current = relay;
179
180 if (!iface_enumerate(AF_INET6, &parm, complete_context6))
181 return;
182
183 /* Recieving a packet from a host does not populate the neighbour
184 cache, so we send a ping to prompt neighbour discovery if we can't
185 find the sender. Repeat a few times in case of packet loss. */
186
187 for (i = 0; i < 5; i++)
188 {
189 struct timespec ts;
190 struct ping_packet *ping;
191 struct sockaddr_in6 addr;
192
193 mac_param.target = &from.sin6_addr;
194 mac_param.maclen = 0;
195
196 iface_enumerate(AF_UNSPEC, &mac_param, find_mac);
197
198 if (mac_param.maclen != 0)
199 break;
200
201 save_counter(0);
202 ping = expand(sizeof(struct ping_packet));
203 ping->type = ICMP6_ECHO_REQUEST;
204 ping->code = 0;
205 ping->identifier = 1;
206 ping->sequence_no = 1;
207
208 memset(&addr, 0, sizeof(addr));
209 #ifdef HAVE_SOCKADDR_SA_LEN
210 addr.sin6_len = sizeof(struct sockaddr_in6);
211 #endif
212 addr.sin6_family = AF_INET6;
213 addr.sin6_port = htons(IPPROTO_ICMPV6);
214 addr.sin6_addr = from.sin6_addr;
215
216 sendto(daemon->icmp6fd, daemon->outpacket.iov_base, save_counter(0), 0,
217 (struct sockaddr *)&addr, sizeof(addr));
218
219 ts.tv_sec = 0;
220 ts.tv_nsec = 100000000; /* 100ms */
221 nanosleep(&ts, NULL);
222 }
223
224 if (daemon->if_names || daemon->if_addrs)
225 {
226
227 for (tmp = daemon->if_names; tmp; tmp = tmp->next)
228 if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
229 break;
230
231 if (!tmp && !parm.addr_match)
232 return;
233 }
234
235 if (parm.relay)
236 {
237 /* Ignore requests sent to the ALL_SERVERS multicast address for relay when
238 we're listening there for DHCPv6 server reasons. */
239 struct in6_addr all_servers;
240
241 inet_pton(AF_INET6, ALL_SERVERS, &all_servers);
242
243 if (!IN6_ARE_ADDR_EQUAL(&dst_addr, &all_servers))
244 relay_upstream6(parm.relay, sz, &from.sin6_addr, from.sin6_scope_id,
245 mac_param.maclen == 0 ? NULL : &mac_param.mac[0], mac_param.maclen, ARPHRD_ETHER);
246 return;
247 }
248
249 /* May have configured relay, but not DHCP server */
250 if (!daemon->doing_dhcp6)
251 return;
252
253 lease_prune(NULL, now); /* lose any expired leases */
254
255 port = dhcp6_reply(parm.current, if_index, ifr.ifr_name, &parm.fallback,
256 sz, IN6_IS_ADDR_MULTICAST(&from.sin6_addr), now,
257 mac_param.maclen == 0 ? NULL : &mac_param.mac[0], mac_param.maclen, ARPHRD_ETHER);
258
259 lease_update_file(now);
260 lease_update_dns(0);
261 }
262
263 /* The port in the source address of the original request should
264 be correct, but at least once client sends from the server port,
265 so we explicitly send to the client port to a client, and the
266 server port to a relay. */
267 if (port != 0)
268 {
269 from.sin6_port = htons(port);
270 while (sendto(daemon->dhcp6fd, daemon->outpacket.iov_base, save_counter(0),
271 0, (struct sockaddr *)&from, sizeof(from)) == -1 &&
272 retry_send());
273 }
274 }
275
276 static int find_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv)
277 {
278 struct mac_param *parm = parmv;
279
280 if (family == AF_INET6 && IN6_ARE_ADDR_EQUAL(parm->target, addrp))
281 {
282 if (maclen <= DHCP_CHADDR_MAX)
283 {
284 parm->maclen = maclen;
285 memcpy(parm->mac, mac, maclen);
286 }
287
288 return 0; /* found, abort */
289 }
290
291 return 1;
292 }
293
294 static int complete_context6(struct in6_addr *local, int prefix,
295 int scope, int if_index, int flags, unsigned int preferred,
296 unsigned int valid, void *vparam)
297 {
298 struct dhcp_context *context;
299 struct dhcp_relay *relay;
300 struct iface_param *param = vparam;
301 struct iname *tmp;
302
303 (void)scope; /* warning */
304
305 if (if_index == param->ind)
306 {
307 if (!IN6_IS_ADDR_LOOPBACK(local) &&
308 !IN6_IS_ADDR_LINKLOCAL(local) &&
309 !IN6_IS_ADDR_MULTICAST(local))
310 {
311 /* if we have --listen-address config, see if the
312 arrival interface has a matching address. */
313 for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
314 if (tmp->addr.sa.sa_family == AF_INET6 &&
315 IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, local))
316 param->addr_match = 1;
317
318 /* Determine a globally address on the arrival interface, even
319 if we have no matching dhcp-context, because we're only
320 allocating on remote subnets via relays. This
321 is used as a default for the DNS server option. */
322 param->fallback = *local;
323
324 for (context = daemon->dhcp6; context; context = context->next)
325 {
326 if ((context->flags & CONTEXT_DHCP) &&
327 !(context->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) &&
328 prefix == context->prefix &&
329 is_same_net6(local, &context->start6, prefix) &&
330 is_same_net6(local, &context->end6, prefix))
331 {
332
333
334 /* link it onto the current chain if we've not seen it before */
335 if (context->current == context)
336 {
337 struct dhcp_context *tmp, **up;
338
339 /* use interface values only for contructed contexts */
340 if (!(context->flags & CONTEXT_CONSTRUCTED))
341 preferred = valid = 0xffffffff;
342 else if (flags & IFACE_DEPRECATED)
343 preferred = 0;
344
345 if (context->flags & CONTEXT_DEPRECATE)
346 preferred = 0;
347
348 /* order chain, longest preferred time first */
349 for (up = &param->current, tmp = param->current; tmp; tmp = tmp->current)
350 if (tmp->preferred <= preferred)
351 break;
352 else
353 up = &tmp->current;
354
355 context->current = *up;
356 *up = context;
357 context->local6 = *local;
358 context->preferred = preferred;
359 context->valid = valid;
360 }
361 }
362 }
363 }
364
365 for (relay = daemon->relay6; relay; relay = relay->next)
366 if (IN6_ARE_ADDR_EQUAL(local, &relay->local.addr.addr6) && relay->current == relay &&
367 (IN6_IS_ADDR_UNSPECIFIED(&param->relay_local) || IN6_ARE_ADDR_EQUAL(local, &param->relay_local)))
368 {
369 relay->current = param->relay;
370 param->relay = relay;
371 param->relay_local = *local;
372 }
373
374 }
375
376 return 1;
377 }
378
379 struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct in6_addr *net, int prefix, u64 addr)
380 {
381 struct dhcp_config *config;
382
383 for (config = configs; config; config = config->next)
384 if ((config->flags & CONFIG_ADDR6) &&
385 is_same_net6(&config->addr6, net, prefix) &&
386 (prefix == 128 || addr6part(&config->addr6) == addr))
387 return config;
388
389 return NULL;
390 }
391
392 struct dhcp_context *address6_allocate(struct dhcp_context *context, unsigned char *clid, int clid_len,
393 int iaid, int serial, struct dhcp_netid *netids, int plain_range, struct in6_addr *ans)
394 {
395 /* Find a free address: exclude anything in use and anything allocated to
396 a particular hwaddr/clientid/hostname in our configuration.
397 Try to return from contexts which match netids first.
398
399 Note that we assume the address prefix lengths are 64 or greater, so we can
400 get by with 64 bit arithmetic.
401 */
402
403 u64 start, addr;
404 struct dhcp_context *c, *d;
405 int i, pass;
406 u64 j;
407
408 /* hash hwaddr: use the SDBM hashing algorithm. This works
409 for MAC addresses, let's see how it manages with client-ids! */
410 for (j = iaid, i = 0; i < clid_len; i++)
411 j += clid[i] + (j << 6) + (j << 16) - j;
412
413 for (pass = 0; pass <= plain_range ? 1 : 0; pass++)
414 for (c = context; c; c = c->current)
415 if (c->flags & (CONTEXT_DEPRECATE | CONTEXT_STATIC | CONTEXT_RA_STATELESS | CONTEXT_USED))
416 continue;
417 else if (!match_netid(c->filter, netids, pass))
418 continue;
419 else
420 {
421 if (option_bool(OPT_CONSEC_ADDR))
422 /* seed is largest extant lease addr in this context */
423 start = lease_find_max_addr6(c) + serial;
424 else
425 start = addr6part(&c->start6) + ((j + c->addr_epoch) % (1 + addr6part(&c->end6) - addr6part(&c->start6)));
426
427 /* iterate until we find a free address. */
428 addr = start;
429
430 do {
431 /* eliminate addresses in use by the server. */
432 for (d = context; d; d = d->current)
433 if (addr == addr6part(&d->local6))
434 break;
435
436 if (!d &&
437 !lease6_find_by_addr(&c->start6, c->prefix, addr) &&
438 !config_find_by_address6(daemon->dhcp_conf, &c->start6, c->prefix, addr))
439 {
440 *ans = c->start6;
441 setaddr6part (ans, addr);
442 return c;
443 }
444
445 addr++;
446
447 if (addr == addr6part(&c->end6) + 1)
448 addr = addr6part(&c->start6);
449
450 } while (addr != start);
451 }
452
453 return NULL;
454 }
455
456 /* can dynamically allocate addr */
457 struct dhcp_context *address6_available(struct dhcp_context *context,
458 struct in6_addr *taddr,
459 struct dhcp_netid *netids,
460 int plain_range)
461 {
462 u64 start, end, addr = addr6part(taddr);
463 struct dhcp_context *tmp;
464
465 for (tmp = context; tmp; tmp = tmp->current)
466 {
467 start = addr6part(&tmp->start6);
468 end = addr6part(&tmp->end6);
469
470 if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_RA_STATELESS)) &&
471 is_same_net6(&tmp->start6, taddr, tmp->prefix) &&
472 is_same_net6(&tmp->end6, taddr, tmp->prefix) &&
473 addr >= start &&
474 addr <= end &&
475 match_netid(tmp->filter, netids, plain_range))
476 return tmp;
477 }
478
479 return NULL;
480 }
481
482 /* address OK if configured */
483 struct dhcp_context *address6_valid(struct dhcp_context *context,
484 struct in6_addr *taddr,
485 struct dhcp_netid *netids,
486 int plain_range)
487 {
488 struct dhcp_context *tmp;
489
490 for (tmp = context; tmp; tmp = tmp->current)
491 if (is_same_net6(&tmp->start6, taddr, tmp->prefix) &&
492 match_netid(tmp->filter, netids, plain_range))
493 return tmp;
494
495 return NULL;
496 }
497
498 int config_valid(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr)
499 {
500 if (!config || !(config->flags & CONFIG_ADDR6))
501 return 0;
502
503 if ((config->flags & CONFIG_WILDCARD) && context->prefix == 64)
504 {
505 *addr = context->start6;
506 setaddr6part(addr, addr6part(&config->addr6));
507 return 1;
508 }
509
510 if (is_same_net6(&context->start6, &config->addr6, context->prefix))
511 {
512 *addr = config->addr6;
513 return 1;
514 }
515
516 return 0;
517 }
518
519 void make_duid(time_t now)
520 {
521 if (daemon->duid_config)
522 {
523 unsigned char *p;
524
525 daemon->duid = p = safe_malloc(daemon->duid_config_len + 6);
526 daemon->duid_len = daemon->duid_config_len + 6;
527 PUTSHORT(2, p); /* DUID_EN */
528 PUTLONG(daemon->duid_enterprise, p);
529 memcpy(p, daemon->duid_config, daemon->duid_config_len);
530 }
531 else
532 {
533 /* rebase epoch to 1/1/2000 */
534 time_t newnow = now - 946684800;
535
536 iface_enumerate(AF_LOCAL, &newnow, make_duid1);
537
538 if(!daemon->duid)
539 die("Cannot create DHCPv6 server DUID: %s", NULL, EC_MISC);
540 }
541 }
542
543 static int make_duid1(int index, unsigned int type, char *mac, size_t maclen, void *parm)
544 {
545 /* create DUID as specified in RFC3315. We use the MAC of the
546 first interface we find that isn't loopback or P-to-P and
547 has address-type < 256. Address types above 256 are things like
548 tunnels which don't have usable MAC addresses. */
549
550 unsigned char *p;
551 (void)index;
552
553 if (type >= 256)
554 return 1;
555
556 #ifdef HAVE_BROKEN_RTC
557 daemon->duid = p = safe_malloc(maclen + 4);
558 daemon->duid_len = maclen + 4;
559 PUTSHORT(3, p); /* DUID_LL */
560 PUTSHORT(type, p); /* address type */
561 #else
562 daemon->duid = p = safe_malloc(maclen + 8);
563 daemon->duid_len = maclen + 8;
564 PUTSHORT(1, p); /* DUID_LLT */
565 PUTSHORT(type, p); /* address type */
566 PUTLONG(*((time_t *)parm), p); /* time */
567 #endif
568
569 memcpy(p, mac, maclen);
570
571 return 0;
572 }
573
574 struct cparam {
575 time_t now;
576 int newone, newname;
577 };
578
579 static int construct_worker(struct in6_addr *local, int prefix,
580 int scope, int if_index, int flags,
581 int preferred, int valid, void *vparam)
582 {
583 char ifrn_name[IFNAMSIZ];
584 struct in6_addr start6, end6;
585 struct dhcp_context *template, *context;
586
587 (void)scope;
588 (void)flags;
589 (void)valid;
590 (void)preferred;
591
592 struct cparam *param = vparam;
593
594 if (IN6_IS_ADDR_LOOPBACK(local) ||
595 IN6_IS_ADDR_LINKLOCAL(local) ||
596 IN6_IS_ADDR_MULTICAST(local))
597 return 1;
598
599 if (!indextoname(daemon->doing_dhcp6 ? daemon->dhcp6fd : daemon->icmp6fd, if_index, ifrn_name))
600 return 0;
601
602 for (template = daemon->dhcp6; template; template = template->next)
603 if (!(template->flags & CONTEXT_TEMPLATE))
604 {
605 /* non-template entries, just fill in interface and local addresses */
606 if (prefix == template->prefix &&
607 is_same_net6(local, &template->start6, prefix) &&
608 is_same_net6(local, &template->end6, prefix))
609 {
610 template->if_index = if_index;
611 template->local6 = *local;
612 }
613
614 }
615 else if ((addr6part(local) == addr6part(&template->start6) ||
616 addr6part(local) == addr6part(&template->end6) ||
617 (IN6_IS_ADDR_UNSPECIFIED(&template->start6) &&
618 IFACE_PERMANENT == (flags & (IFACE_PERMANENT | IFACE_DEPRECATED)))) &&
619 wildcard_match(template->template_interface, ifrn_name))
620 {
621 start6 = *local;
622 setaddr6part(&start6, addr6part(&template->start6));
623 end6 = *local;
624 setaddr6part(&end6, addr6part(&template->end6));
625
626 for (context = daemon->dhcp6; context; context = context->next)
627 if ((context->flags & CONTEXT_CONSTRUCTED) &&
628 IN6_ARE_ADDR_EQUAL(&start6, &context->start6) &&
629 IN6_ARE_ADDR_EQUAL(&end6, &context->end6))
630 {
631 int flags = context->flags;
632 context->flags &= ~(CONTEXT_GC | CONTEXT_OLD);
633 if (flags & CONTEXT_OLD)
634 {
635 /* address went, now it's back */
636 log_context(AF_INET6, context);
637 /* fast RAs for a while */
638 ra_start_unsolicted(param->now, context);
639 /* Add address to name again */
640 if (context->flags & CONTEXT_RA_NAME)
641 param->newname = 1;
642 }
643 break;
644 }
645
646 if (!context && (context = whine_malloc(sizeof (struct dhcp_context))))
647 {
648 *context = *template;
649 context->start6 = start6;
650 context->end6 = end6;
651 context->flags &= ~CONTEXT_TEMPLATE;
652 context->flags |= CONTEXT_CONSTRUCTED;
653 context->if_index = if_index;
654 context->local6 = *local;
655 context->saved_valid = 0;
656
657 context->next = daemon->dhcp6;
658 daemon->dhcp6 = context;
659
660 ra_start_unsolicted(param->now, context);
661 /* we created a new one, need to call
662 lease_update_file to get periodic functions called */
663 param->newone = 1;
664
665 /* Will need to add new putative SLAAC addresses to existing leases */
666 if (context->flags & CONTEXT_RA_NAME)
667 param->newname = 1;
668
669 log_context(AF_INET6, context);
670 }
671 }
672
673 return 1;
674 }
675
676 void dhcp_construct_contexts(time_t now)
677 {
678 struct dhcp_context *context, *tmp, **up;
679 struct cparam param;
680 param.newone = 0;
681 param.newname = 0;
682 param.now = now;
683
684 for (context = daemon->dhcp6; context; context = context->next)
685 if (context->flags & CONTEXT_CONSTRUCTED)
686 context->flags |= CONTEXT_GC;
687
688 iface_enumerate(AF_INET6, &param, construct_worker);
689
690 for (up = &daemon->dhcp6, context = daemon->dhcp6; context; context = tmp)
691 {
692
693 tmp = context->next;
694
695 if (context->flags & CONTEXT_GC && !(context->flags & CONTEXT_OLD))
696 {
697
698 if ((context->flags & (CONTEXT_RA_ONLY | CONTEXT_RA_NAME | CONTEXT_RA_STATELESS)) ||
699 option_bool(OPT_RA))
700 {
701 /* previously constructed context has gone. advertise it's demise */
702 context->flags |= CONTEXT_OLD;
703 context->address_lost_time = now;
704 /* Apply same ceiling of configured lease time as in radv.c */
705 if (context->saved_valid > context->lease_time)
706 context->saved_valid = context->lease_time;
707 /* maximum time is 2 hours, from RFC */
708 if (context->saved_valid > 7200) /* 2 hours */
709 context->saved_valid = 7200;
710 ra_start_unsolicted(now, context);
711 param.newone = 1; /* include deletion */
712
713 if (context->flags & CONTEXT_RA_NAME)
714 param.newname = 1;
715
716 log_context(AF_INET6, context);
717
718 up = &context->next;
719 }
720 else
721 {
722 /* we were never doing RA for this, so free now */
723 *up = context->next;
724 free(context);
725 }
726 }
727 else
728 up = &context->next;
729 }
730
731 if (param.newone)
732 {
733 if (daemon->dhcp || daemon->doing_dhcp6)
734 {
735 if (param.newname)
736 lease_update_slaac(now);
737 lease_update_file(now);
738 }
739 else
740 /* Not doing DHCP, so no lease system, manage alarms for ra only */
741 send_alarm(periodic_ra(now), now);
742 }
743 }
744
745 #endif
746
747