blob: 2be877fea5ef74af07a27ef67e124ae5d7986b20 [file] [log] [blame]
lh9ed821d2023-04-07 01:36:19 -07001/* dnsmasq is Copyright (c) 2000-2021 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
30
31static int complete_context6(struct in6_addr *local, int prefix,
32 int scope, int if_index, int flags,
33 unsigned int preferred, unsigned int valid, void *vparam);
34static int make_duid1(int index, unsigned int type, char *mac, size_t maclen, void *parm);
35
36void dhcp6_init(void)
37{
38 int fd;
39 struct sockaddr_in6 saddr;
40#if defined(IPV6_TCLASS) && defined(IPTOS_CLASS_CS6)
41 int class = IPTOS_CLASS_CS6;
42#endif
43 int oneopt = 1;
44
45 if ((fd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP)) == -1 ||
46#if defined(IPV6_TCLASS) && defined(IPTOS_CLASS_CS6)
47 setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &class, sizeof(class)) == -1 ||
48#endif
49 setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &oneopt, sizeof(oneopt)) == -1 ||
50 !fix_fd(fd) ||
51 !set_ipv6pktinfo(fd))
52 die (_("cannot create DHCPv6 socket: %s"), NULL, EC_BADNET);
53
54 /* When bind-interfaces is set, there might be more than one dnsmasq
55 instance binding port 547. That's OK if they serve different networks.
56 Need to set REUSEADDR|REUSEPORT to make this possible.
57 Handle the case that REUSEPORT is defined, but the kernel doesn't
58 support it. This handles the introduction of REUSEPORT on Linux. */
59 if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND))
60 {
61 int rc = 0;
62
63#ifdef SO_REUSEPORT
64 if ((rc = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &oneopt, sizeof(oneopt))) == -1 &&
65 errno == ENOPROTOOPT)
66 rc = 0;
67#endif
68
69 if (rc != -1)
70 rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt));
71
72 if (rc == -1)
73 die(_("failed to set SO_REUSE{ADDR|PORT} on DHCPv6 socket: %s"), NULL, EC_BADNET);
74 }
75
76 memset(&saddr, 0, sizeof(saddr));
77#ifdef HAVE_SOCKADDR_SA_LEN
78 saddr.sin6_len = sizeof(struct sockaddr_in6);
79#endif
80 saddr.sin6_family = AF_INET6;
81 saddr.sin6_addr = in6addr_any;
82 saddr.sin6_port = htons(DHCPV6_SERVER_PORT);
83
84 if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in6)))
85 die(_("failed to bind DHCPv6 server socket: %s"), NULL, EC_BADNET);
86
87 daemon->dhcp6fd = fd;
88}
89
90void dhcp6_packet(time_t now)
91{
92 struct dhcp_context *context;
93 struct dhcp_relay *relay;
94 struct iface_param parm;
95 struct cmsghdr *cmptr;
96 struct msghdr msg;
97 int if_index = 0;
98 union {
99 struct cmsghdr align; /* this ensures alignment */
100 char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
101 } control_u;
102 struct sockaddr_in6 from;
103 ssize_t sz;
104 struct ifreq ifr;
105 struct iname *tmp;
106 unsigned short port;
107 struct in6_addr dst_addr;
108
109 memset(&dst_addr, 0, sizeof(dst_addr));
110
111 msg.msg_control = control_u.control6;
112 msg.msg_controllen = sizeof(control_u);
113 msg.msg_flags = 0;
114 msg.msg_name = &from;
115 msg.msg_namelen = sizeof(from);
116 msg.msg_iov = &daemon->dhcp_packet;
117 msg.msg_iovlen = 1;
118
119 if ((sz = recv_dhcp_packet(daemon->dhcp6fd, &msg)) == -1)
120 return;
121
122 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
123 if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
124 {
125 union {
126 unsigned char *c;
127 struct in6_pktinfo *p;
128 } p;
129 p.c = CMSG_DATA(cmptr);
130
131 if_index = p.p->ipi6_ifindex;
132 dst_addr = p.p->ipi6_addr;
133 }
134
135 if (!indextoname(daemon->dhcp6fd, if_index, ifr.ifr_name))
136 return;
137
138 if ((port = relay_reply6(&from, sz, ifr.ifr_name)) != 0)
139 {
140 from.sin6_port = htons(port);
141 while (retry_send(sendto(daemon->dhcp6fd, daemon->outpacket.iov_base,
142 save_counter(-1), 0, (struct sockaddr *)&from,
143 sizeof(from))));
144 }
145 else
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, now);
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 /* The port in the source address of the original request should
244 be correct, but at least once client sends from the server port,
245 so we explicitly send to the client port to a client, and the
246 server port to a relay. */
247 if (port != 0)
248 {
249 from.sin6_port = htons(port);
250 while (retry_send(sendto(daemon->dhcp6fd, daemon->outpacket.iov_base,
251 save_counter(-1), 0, (struct sockaddr *)&from,
252 sizeof(from))));
253 }
254
255 /* These need to be called _after_ we send DHCPv6 packet, since lease_update_file()
256 may trigger sending an RA packet, which overwrites our buffer. */
257 lease_update_file(now);
258 lease_update_dns(0);
259 }
260}
261
262void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, unsigned int *maclenp, unsigned int *mactypep, time_t now)
263{
264 /* Receiving a packet from a host does not populate the neighbour
265 cache, so we send a neighbour discovery request if we can't
266 find the sender. Repeat a few times in case of packet loss. */
267
268 struct neigh_packet neigh;
269 union mysockaddr addr;
270 int i, maclen;
271
272 neigh.type = ND_NEIGHBOR_SOLICIT;
273 neigh.code = 0;
274 neigh.reserved = 0;
275 neigh.target = *client;
276 /* RFC4443 section-2.3: checksum has to be zero to be calculated */
277 neigh.checksum = 0;
278
279 memset(&addr, 0, sizeof(addr));
280#ifdef HAVE_SOCKADDR_SA_LEN
281 addr.in6.sin6_len = sizeof(struct sockaddr_in6);
282#endif
283 addr.in6.sin6_family = AF_INET6;
284 addr.in6.sin6_port = htons(IPPROTO_ICMPV6);
285 addr.in6.sin6_addr = *client;
286 addr.in6.sin6_scope_id = iface;
287
288 for (i = 0; i < 5; i++)
289 {
290 struct timespec ts;
291
292 if ((maclen = find_mac(&addr, mac, 0, now)) != 0)
293 break;
294
295 sendto(daemon->icmp6fd, &neigh, sizeof(neigh), 0, &addr.sa, sizeof(addr));
296
297 ts.tv_sec = 0;
298 ts.tv_nsec = 100000000; /* 100ms */
299 nanosleep(&ts, NULL);
300 }
301
302 *maclenp = maclen;
303 *mactypep = ARPHRD_ETHER;
304}
305
306static int complete_context6(struct in6_addr *local, int prefix,
307 int scope, int if_index, int flags, unsigned int preferred,
308 unsigned int valid, void *vparam)
309{
310 struct dhcp_context *context;
311 struct shared_network *share;
312 struct dhcp_relay *relay;
313 struct iface_param *param = vparam;
314 struct iname *tmp;
315
316 (void)scope; /* warning */
317
318 if (if_index != param->ind)
319 return 1;
320
321 if (IN6_IS_ADDR_LINKLOCAL(local))
322 param->ll_addr = *local;
323 else if (IN6_IS_ADDR_ULA(local))
324 param->ula_addr = *local;
325
326 if (IN6_IS_ADDR_LOOPBACK(local) ||
327 IN6_IS_ADDR_LINKLOCAL(local) ||
328 IN6_IS_ADDR_MULTICAST(local))
329 return 1;
330
331 /* if we have --listen-address config, see if the
332 arrival interface has a matching address. */
333 for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
334 if (tmp->addr.sa.sa_family == AF_INET6 &&
335 IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, local))
336 param->addr_match = 1;
337
338 /* Determine a globally address on the arrival interface, even
339 if we have no matching dhcp-context, because we're only
340 allocating on remote subnets via relays. This
341 is used as a default for the DNS server option. */
342 param->fallback = *local;
343
344 for (context = daemon->dhcp6; context; context = context->next)
345 if ((context->flags & CONTEXT_DHCP) &&
346 !(context->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) &&
347 prefix <= context->prefix &&
348 context->current == context)
349 {
350 if (is_same_net6(local, &context->start6, context->prefix) &&
351 is_same_net6(local, &context->end6, context->prefix))
352 {
353 struct dhcp_context *tmp, **up;
354
355 /* use interface values only for constructed contexts */
356 if (!(context->flags & CONTEXT_CONSTRUCTED))
357 preferred = valid = 0xffffffff;
358 else if (flags & IFACE_DEPRECATED)
359 preferred = 0;
360
361 if (context->flags & CONTEXT_DEPRECATE)
362 preferred = 0;
363
364 /* order chain, longest preferred time first */
365 for (up = &param->current, tmp = param->current; tmp; tmp = tmp->current)
366 if (tmp->preferred <= preferred)
367 break;
368 else
369 up = &tmp->current;
370
371 context->current = *up;
372 *up = context;
373 context->local6 = *local;
374 context->preferred = preferred;
375 context->valid = valid;
376 }
377 else
378 {
379 for (share = daemon->shared_networks; share; share = share->next)
380 {
381 /* IPv4 shared_address - ignore */
382 if (share->shared_addr.s_addr != 0)
383 continue;
384
385 if (share->if_index != 0)
386 {
387 if (share->if_index != if_index)
388 continue;
389 }
390 else
391 {
392 if (!IN6_ARE_ADDR_EQUAL(&share->match_addr6, local))
393 continue;
394 }
395
396 if (is_same_net6(&share->shared_addr6, &context->start6, context->prefix) &&
397 is_same_net6(&share->shared_addr6, &context->end6, context->prefix))
398 {
399 context->current = param->current;
400 param->current = context;
401 context->local6 = *local;
402 context->preferred = context->flags & CONTEXT_DEPRECATE ? 0 :0xffffffff;
403 context->valid = 0xffffffff;
404 }
405 }
406 }
407 }
408
409 for (relay = daemon->relay6; relay; relay = relay->next)
410 if (IN6_ARE_ADDR_EQUAL(local, &relay->local.addr6) && relay->current == relay &&
411 (IN6_IS_ADDR_UNSPECIFIED(&param->relay_local) || IN6_ARE_ADDR_EQUAL(local, &param->relay_local)))
412 {
413 relay->current = param->relay;
414 param->relay = relay;
415 param->relay_local = *local;
416 }
417
418 return 1;
419}
420
421struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct in6_addr *net, int prefix, struct in6_addr *addr)
422{
423 struct dhcp_config *config;
424
425 for (config = configs; config; config = config->next)
426 if (config->flags & CONFIG_ADDR6)
427 {
428 struct addrlist *addr_list;
429
430 for (addr_list = config->addr6; addr_list; addr_list = addr_list->next)
431 if ((!net || is_same_net6(&addr_list->addr.addr6, net, prefix) || ((addr_list->flags & ADDRLIST_WILDCARD) && prefix == 64)) &&
432 is_same_net6(&addr_list->addr.addr6, addr, (addr_list->flags & ADDRLIST_PREFIX) ? addr_list->prefixlen : 128))
433 return config;
434 }
435
436 return NULL;
437}
438
439struct dhcp_context *address6_allocate(struct dhcp_context *context, unsigned char *clid, int clid_len, int temp_addr,
440 unsigned int iaid, int serial, struct dhcp_netid *netids, int plain_range, struct in6_addr *ans)
441{
442 /* Find a free address: exclude anything in use and anything allocated to
443 a particular hwaddr/clientid/hostname in our configuration.
444 Try to return from contexts which match netids first.
445
446 Note that we assume the address prefix lengths are 64 or greater, so we can
447 get by with 64 bit arithmetic.
448*/
449
450 u64 start, addr;
451 struct dhcp_context *c, *d;
452 int i, pass;
453 u64 j;
454
455 /* hash hwaddr: use the SDBM hashing algorithm. This works
456 for MAC addresses, let's see how it manages with client-ids!
457 For temporary addresses, we generate a new random one each time. */
458 if (temp_addr)
459 j = rand64();
460 else
461 for (j = iaid, i = 0; i < clid_len; i++)
462 j = clid[i] + (j << 6) + (j << 16) - j;
463
464 for (pass = 0; pass <= plain_range ? 1 : 0; pass++)
465 for (c = context; c; c = c->current)
466 if (c->flags & (CONTEXT_DEPRECATE | CONTEXT_STATIC | CONTEXT_RA_STATELESS | CONTEXT_USED))
467 continue;
468 else if (!match_netid(c->filter, netids, pass))
469 continue;
470 else
471 {
472 if (!temp_addr && option_bool(OPT_CONSEC_ADDR))
473 {
474 /* seed is largest extant lease addr in this context,
475 skip addresses equal to the number of addresses rejected
476 by clients. This should avoid the same client being offered the same
477 address after it has rjected it. */
478 start = lease_find_max_addr6(c) + 1 + serial + c->addr_epoch;
479 if (c->addr_epoch)
480 c->addr_epoch--;
481 }
482 else
483 {
484 u64 range = 1 + addr6part(&c->end6) - addr6part(&c->start6);
485 u64 offset = j + c->addr_epoch;
486
487 /* don't divide by zero if range is whole 2^64 */
488 if (range != 0)
489 offset = offset % range;
490
491 start = addr6part(&c->start6) + offset;
492 }
493
494 /* iterate until we find a free address. */
495 addr = start;
496
497 do {
498 /* eliminate addresses in use by the server. */
499 for (d = context; d; d = d->current)
500 if (addr == addr6part(&d->local6))
501 break;
502
503 *ans = c->start6;
504 setaddr6part (ans, addr);
505
506 if (!d &&
507 !lease6_find_by_addr(&c->start6, c->prefix, addr) &&
508 !config_find_by_address6(daemon->dhcp_conf, &c->start6, c->prefix, ans))
509 return c;
510
511 addr++;
512
513 if (addr == addr6part(&c->end6) + 1)
514 addr = addr6part(&c->start6);
515
516 } while (addr != start);
517 }
518
519 return NULL;
520}
521
522/* can dynamically allocate addr */
523struct dhcp_context *address6_available(struct dhcp_context *context,
524 struct in6_addr *taddr,
525 struct dhcp_netid *netids,
526 int plain_range)
527{
528 u64 start, end, addr = addr6part(taddr);
529 struct dhcp_context *tmp;
530
531 for (tmp = context; tmp; tmp = tmp->current)
532 {
533 start = addr6part(&tmp->start6);
534 end = addr6part(&tmp->end6);
535
536 if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_RA_STATELESS)) &&
537 is_same_net6(&tmp->start6, taddr, tmp->prefix) &&
538 is_same_net6(&tmp->end6, taddr, tmp->prefix) &&
539 addr >= start &&
540 addr <= end &&
541 match_netid(tmp->filter, netids, plain_range))
542 return tmp;
543 }
544
545 return NULL;
546}
547
548/* address OK if configured */
549struct dhcp_context *address6_valid(struct dhcp_context *context,
550 struct in6_addr *taddr,
551 struct dhcp_netid *netids,
552 int plain_range)
553{
554 struct dhcp_context *tmp;
555
556 for (tmp = context; tmp; tmp = tmp->current)
557 if (is_same_net6(&tmp->start6, taddr, tmp->prefix) &&
558 match_netid(tmp->filter, netids, plain_range))
559 return tmp;
560
561 return NULL;
562}
563
564void make_duid(time_t now)
565{
566 (void)now;
567
568 if (daemon->duid_config)
569 {
570 unsigned char *p;
571
572 daemon->duid = p = safe_malloc(daemon->duid_config_len + 6);
573 daemon->duid_len = daemon->duid_config_len + 6;
574 PUTSHORT(2, p); /* DUID_EN */
575 PUTLONG(daemon->duid_enterprise, p);
576 memcpy(p, daemon->duid_config, daemon->duid_config_len);
577 }
578 else
579 {
580 time_t newnow = 0;
581
582 /* If we have no persistent lease database, or a non-stable RTC, use DUID_LL (newnow == 0) */
583#ifndef HAVE_BROKEN_RTC
584 /* rebase epoch to 1/1/2000 */
585 if (!option_bool(OPT_LEASE_RO) || daemon->lease_change_command)
586 newnow = now - 946684800;
587#endif
588
589 iface_enumerate(AF_LOCAL, &newnow, make_duid1);
590
591 if(!daemon->duid)
592 die("Cannot create DHCPv6 server DUID: %s", NULL, EC_MISC);
593 }
594}
595
596static int make_duid1(int index, unsigned int type, char *mac, size_t maclen, void *parm)
597{
598 /* create DUID as specified in RFC3315. We use the MAC of the
599 first interface we find that isn't loopback or P-to-P and
600 has address-type < 256. Address types above 256 are things like
601 tunnels which don't have usable MAC addresses. */
602
603 unsigned char *p;
604 (void)index;
605 (void)parm;
606 time_t newnow = *((time_t *)parm);
607
608 if (type >= 256)
609 return 1;
610
611 if (newnow == 0)
612 {
613 daemon->duid = p = safe_malloc(maclen + 4);
614 daemon->duid_len = maclen + 4;
615 PUTSHORT(3, p); /* DUID_LL */
616 PUTSHORT(type, p); /* address type */
617 }
618 else
619 {
620 daemon->duid = p = safe_malloc(maclen + 8);
621 daemon->duid_len = maclen + 8;
622 PUTSHORT(1, p); /* DUID_LLT */
623 PUTSHORT(type, p); /* address type */
624 PUTLONG(*((time_t *)parm), p); /* time */
625 }
626
627 memcpy(p, mac, maclen);
628
629 return 0;
630}
631
632struct cparam {
633 time_t now;
634 int newone, newname;
635};
636
637static int construct_worker(struct in6_addr *local, int prefix,
638 int scope, int if_index, int flags,
639 int preferred, int valid, void *vparam)
640{
641 char ifrn_name[IFNAMSIZ];
642 struct in6_addr start6, end6;
643 struct dhcp_context *template, *context;
644 struct iname *tmp;
645
646 (void)scope;
647 (void)flags;
648 (void)valid;
649 (void)preferred;
650
651 struct cparam *param = vparam;
652
653 if (IN6_IS_ADDR_LOOPBACK(local) ||
654 IN6_IS_ADDR_LINKLOCAL(local) ||
655 IN6_IS_ADDR_MULTICAST(local))
656 return 1;
657
658 if (!(flags & IFACE_PERMANENT))
659 return 1;
660
661 if (flags & IFACE_DEPRECATED)
662 return 1;
663
664 /* Ignore interfaces where we're not doing RA/DHCP6 */
665 if (!indextoname(daemon->icmp6fd, if_index, ifrn_name) ||
666 !iface_check(AF_LOCAL, NULL, ifrn_name, NULL))
667 return 1;
668
669 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
670 if (tmp->name && wildcard_match(tmp->name, ifrn_name))
671 return 1;
672
673 for (template = daemon->dhcp6; template; template = template->next)
674 if (!(template->flags & (CONTEXT_TEMPLATE | CONTEXT_CONSTRUCTED)))
675 {
676 /* non-template entries, just fill in interface and local addresses */
677 if (prefix <= template->prefix &&
678 is_same_net6(local, &template->start6, template->prefix) &&
679 is_same_net6(local, &template->end6, template->prefix))
680 {
681 /* First time found, do fast RA. */
682 if (template->if_index == 0)
683 {
684 ra_start_unsolicited(param->now, template);
685 param->newone = 1;
686 }
687
688 template->if_index = if_index;
689 template->local6 = *local;
690 }
691
692 }
693 else if (wildcard_match(template->template_interface, ifrn_name) &&
694 template->prefix >= prefix)
695 {
696 start6 = *local;
697 setaddr6part(&start6, addr6part(&template->start6));
698 end6 = *local;
699 setaddr6part(&end6, addr6part(&template->end6));
700
701 for (context = daemon->dhcp6; context; context = context->next)
702 if (!(context->flags & CONTEXT_TEMPLATE) &&
703 IN6_ARE_ADDR_EQUAL(&start6, &context->start6) &&
704 IN6_ARE_ADDR_EQUAL(&end6, &context->end6))
705 {
706 /* If there's an absolute address context covering this address
707 then don't construct one as well. */
708 if (!(context->flags & CONTEXT_CONSTRUCTED))
709 break;
710
711 if (context->if_index == if_index)
712 {
713 int cflags = context->flags;
714 context->flags &= ~(CONTEXT_GC | CONTEXT_OLD);
715 if (cflags & CONTEXT_OLD)
716 {
717 /* address went, now it's back, and on the same interface */
718 log_context(AF_INET6, context);
719 /* fast RAs for a while */
720 ra_start_unsolicited(param->now, context);
721 param->newone = 1;
722 /* Add address to name again */
723 if (context->flags & CONTEXT_RA_NAME)
724 param->newname = 1;
725
726 }
727 break;
728 }
729 }
730
731 if (!context && (context = whine_malloc(sizeof (struct dhcp_context))))
732 {
733 *context = *template;
734 context->start6 = start6;
735 context->end6 = end6;
736 context->flags &= ~CONTEXT_TEMPLATE;
737 context->flags |= CONTEXT_CONSTRUCTED;
738 context->if_index = if_index;
739 context->local6 = *local;
740 context->saved_valid = 0;
741
742 context->next = daemon->dhcp6;
743 daemon->dhcp6 = context;
744
745 ra_start_unsolicited(param->now, context);
746 /* we created a new one, need to call
747 lease_update_file to get periodic functions called */
748 param->newone = 1;
749
750 /* Will need to add new putative SLAAC addresses to existing leases */
751 if (context->flags & CONTEXT_RA_NAME)
752 param->newname = 1;
753
754 log_context(AF_INET6, context);
755 }
756 }
757
758 return 1;
759}
760
761void dhcp_construct_contexts(time_t now)
762{
763 struct dhcp_context *context, *tmp, **up;
764 struct cparam param;
765 param.newone = 0;
766 param.newname = 0;
767 param.now = now;
768
769 for (context = daemon->dhcp6; context; context = context->next)
770 if (context->flags & CONTEXT_CONSTRUCTED)
771 context->flags |= CONTEXT_GC;
772
773 iface_enumerate(AF_INET6, &param, construct_worker);
774
775 for (up = &daemon->dhcp6, context = daemon->dhcp6; context; context = tmp)
776 {
777
778 tmp = context->next;
779
780 if (context->flags & CONTEXT_GC && !(context->flags & CONTEXT_OLD))
781 {
782 if ((context->flags & CONTEXT_RA) || option_bool(OPT_RA))
783 {
784 /* previously constructed context has gone. advertise it's demise */
785 context->flags |= CONTEXT_OLD;
786 context->address_lost_time = now;
787 /* Apply same ceiling of configured lease time as in radv.c */
788 if (context->saved_valid > context->lease_time)
789 context->saved_valid = context->lease_time;
790 /* maximum time is 2 hours, from RFC */
791 if (context->saved_valid > 7200) /* 2 hours */
792 context->saved_valid = 7200;
793 ra_start_unsolicited(now, context);
794 param.newone = 1; /* include deletion */
795
796 if (context->flags & CONTEXT_RA_NAME)
797 param.newname = 1;
798
799 log_context(AF_INET6, context);
800
801 up = &context->next;
802 }
803 else
804 {
805 /* we were never doing RA for this, so free now */
806 *up = context->next;
807 free(context);
808 }
809 }
810 else
811 up = &context->next;
812 }
813
814 if (param.newone)
815 {
816 if (daemon->dhcp || daemon->doing_dhcp6)
817 {
818 if (param.newname)
819 lease_update_slaac(now);
820 lease_update_file(now);
821 }
822 else
823 /* Not doing DHCP, so no lease system, manage alarms for ra only */
824 send_alarm(periodic_ra(now), now);
825 }
826}
827
828#endif /* HAVE_DHCP6 */