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