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 = &param->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(&param->relay_local) || IN6_ARE_ADDR_EQUAL(local, &param->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, &param, 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