dnsmasq: re-apply original Tomato specific code
[tomato.git] / release / src / router / dnsmasq / src / dhcp6.c
blob56eb0dadbf96a075bf69d0401b6c38292edb3ec5
1 /* dnsmasq is Copyright (c) 2000-2016 Simon Kelley
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.
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.
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/>.
17 #include "dnsmasq.h"
19 #ifdef HAVE_DHCP6
21 #include <netinet/icmp6.h>
23 struct 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;
31 static 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);
34 static int make_duid1(int index, unsigned int type, char *mac, size_t maclen, void *parm);
36 void dhcp6_init(void)
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;
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);
54 /* When bind-interfaces is set, there might be more than one dnmsasq
55 instance binding port 547. That's OK if they serve different networks.
56 Need to set REUSEADDR|REUSEPORT to make this posible.
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))
61 int rc = 0;
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
69 if (rc != -1)
70 rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt));
72 if (rc == -1)
73 die(_("failed to set SO_REUSE{ADDR|PORT} on DHCPv6 socket: %s"), NULL, EC_BADNET);
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);
84 if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in6)))
85 die(_("failed to bind DHCPv6 server socket: %s"), NULL, EC_BADNET);
87 daemon->dhcp6fd = fd;
90 void dhcp6_packet(time_t now)
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;
109 memset(&dst_addr, 0, sizeof(dst_addr));
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;
119 if ((sz = recv_dhcp_packet(daemon->dhcp6fd, &msg)) == -1)
120 return;
122 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
123 if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
125 union {
126 unsigned char *c;
127 struct in6_pktinfo *p;
128 } p;
129 p.c = CMSG_DATA(cmptr);
131 if_index = p.p->ipi6_ifindex;
132 dst_addr = p.p->ipi6_addr;
135 if (!indextoname(daemon->dhcp6fd, if_index, ifr.ifr_name))
136 return;
138 if ((port = relay_reply6(&from, sz, ifr.ifr_name)) == 0)
140 struct dhcp_bridge *bridge, *alias;
142 for (tmp = daemon->if_except; tmp; tmp = tmp->next)
143 if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
144 return;
146 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
147 if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
148 return;
150 parm.current = NULL;
151 parm.relay = NULL;
152 memset(&parm.relay_local, 0, IN6ADDRSZ);
153 parm.ind = if_index;
154 parm.addr_match = 0;
155 memset(&parm.fallback, 0, IN6ADDRSZ);
156 memset(&parm.ll_addr, 0, IN6ADDRSZ);
157 memset(&parm.ula_addr, 0, IN6ADDRSZ);
159 /* If the interface on which the DHCPv6 request was received is
160 an alias of some other interface (as specified by the
161 --bridge-interface option), change parm.ind so that we look
162 for DHCPv6 contexts associated with the aliased interface
163 instead of with the aliasing one. */
164 for (bridge = daemon->bridges; bridge; bridge = bridge->next)
166 for (alias = bridge->alias; alias; alias = alias->next)
167 if (wildcard_matchn(alias->iface, ifr.ifr_name, IF_NAMESIZE))
169 parm.ind = if_nametoindex(bridge->iface);
170 if (!parm.ind)
172 my_syslog(MS_DHCP | LOG_WARNING,
173 _("unknown interface %s in bridge-interface"),
174 bridge->iface);
175 return;
177 break;
179 if (alias)
180 break;
183 for (context = daemon->dhcp6; context; context = context->next)
184 if (IN6_IS_ADDR_UNSPECIFIED(&context->start6) && context->prefix == 0)
186 /* wildcard context for DHCP-stateless only */
187 parm.current = context;
188 context->current = NULL;
190 else
192 /* unlinked contexts are marked by context->current == context */
193 context->current = context;
194 memset(&context->local6, 0, IN6ADDRSZ);
197 for (relay = daemon->relay6; relay; relay = relay->next)
198 relay->current = relay;
200 if (!iface_enumerate(AF_INET6, &parm, complete_context6))
201 return;
203 if (daemon->if_names || daemon->if_addrs)
206 for (tmp = daemon->if_names; tmp; tmp = tmp->next)
207 if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
208 break;
210 if (!tmp && !parm.addr_match)
211 return;
214 if (parm.relay)
216 /* Ignore requests sent to the ALL_SERVERS multicast address for relay when
217 we're listening there for DHCPv6 server reasons. */
218 struct in6_addr all_servers;
220 inet_pton(AF_INET6, ALL_SERVERS, &all_servers);
222 if (!IN6_ARE_ADDR_EQUAL(&dst_addr, &all_servers))
223 relay_upstream6(parm.relay, sz, &from.sin6_addr, from.sin6_scope_id, now);
224 return;
227 /* May have configured relay, but not DHCP server */
228 if (!daemon->doing_dhcp6)
229 return;
231 lease_prune(NULL, now); /* lose any expired leases */
233 port = dhcp6_reply(parm.current, if_index, ifr.ifr_name, &parm.fallback,
234 &parm.ll_addr, &parm.ula_addr, sz, &from.sin6_addr, now);
236 lease_update_file(now);
237 lease_update_dns(0);
240 /* The port in the source address of the original request should
241 be correct, but at least once client sends from the server port,
242 so we explicitly send to the client port to a client, and the
243 server port to a relay. */
244 if (port != 0)
246 from.sin6_port = htons(port);
247 while (retry_send(sendto(daemon->dhcp6fd, daemon->outpacket.iov_base,
248 save_counter(0), 0, (struct sockaddr *)&from,
249 sizeof(from))));
253 void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, unsigned int *maclenp, unsigned int *mactypep, time_t now)
255 /* Recieving a packet from a host does not populate the neighbour
256 cache, so we send a neighbour discovery request if we can't
257 find the sender. Repeat a few times in case of packet loss. */
259 struct neigh_packet neigh;
260 union mysockaddr addr;
261 int i, maclen;
263 neigh.type = ND_NEIGHBOR_SOLICIT;
264 neigh.code = 0;
265 neigh.reserved = 0;
266 neigh.target = *client;
267 /* RFC4443 section-2.3: checksum has to be zero to be calculated */
268 neigh.checksum = 0;
270 memset(&addr, 0, sizeof(addr));
271 #ifdef HAVE_SOCKADDR_SA_LEN
272 addr.in6.sin6_len = sizeof(struct sockaddr_in6);
273 #endif
274 addr.in6.sin6_family = AF_INET6;
275 addr.in6.sin6_port = htons(IPPROTO_ICMPV6);
276 addr.in6.sin6_addr = *client;
277 addr.in6.sin6_scope_id = iface;
279 for (i = 0; i < 5; i++)
281 struct timespec ts;
283 if ((maclen = find_mac(&addr, mac, 0, now)) != 0)
284 break;
286 sendto(daemon->icmp6fd, &neigh, sizeof(neigh), 0, &addr.sa, sizeof(addr));
288 ts.tv_sec = 0;
289 ts.tv_nsec = 100000000; /* 100ms */
290 nanosleep(&ts, NULL);
293 *maclenp = maclen;
294 *mactypep = ARPHRD_ETHER;
297 static int complete_context6(struct in6_addr *local, int prefix,
298 int scope, int if_index, int flags, unsigned int preferred,
299 unsigned int valid, void *vparam)
301 struct dhcp_context *context;
302 struct dhcp_relay *relay;
303 struct iface_param *param = vparam;
304 struct iname *tmp;
306 (void)scope; /* warning */
308 if (if_index == param->ind)
310 if (IN6_IS_ADDR_LINKLOCAL(local))
311 param->ll_addr = *local;
312 else if (IN6_IS_ADDR_ULA(local))
313 param->ula_addr = *local;
315 if (!IN6_IS_ADDR_LOOPBACK(local) &&
316 !IN6_IS_ADDR_LINKLOCAL(local) &&
317 !IN6_IS_ADDR_MULTICAST(local))
319 /* if we have --listen-address config, see if the
320 arrival interface has a matching address. */
321 for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
322 if (tmp->addr.sa.sa_family == AF_INET6 &&
323 IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, local))
324 param->addr_match = 1;
326 /* Determine a globally address on the arrival interface, even
327 if we have no matching dhcp-context, because we're only
328 allocating on remote subnets via relays. This
329 is used as a default for the DNS server option. */
330 param->fallback = *local;
332 for (context = daemon->dhcp6; context; context = context->next)
334 if ((context->flags & CONTEXT_DHCP) &&
335 !(context->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) &&
336 prefix <= context->prefix &&
337 is_same_net6(local, &context->start6, context->prefix) &&
338 is_same_net6(local, &context->end6, context->prefix))
342 /* link it onto the current chain if we've not seen it before */
343 if (context->current == context)
345 struct dhcp_context *tmp, **up;
347 /* use interface values only for contructed contexts */
348 if (!(context->flags & CONTEXT_CONSTRUCTED))
349 preferred = valid = 0xffffffff;
350 else if (flags & IFACE_DEPRECATED)
351 preferred = 0;
353 if (context->flags & CONTEXT_DEPRECATE)
354 preferred = 0;
356 /* order chain, longest preferred time first */
357 for (up = &param->current, tmp = param->current; tmp; tmp = tmp->current)
358 if (tmp->preferred <= preferred)
359 break;
360 else
361 up = &tmp->current;
363 context->current = *up;
364 *up = context;
365 context->local6 = *local;
366 context->preferred = preferred;
367 context->valid = valid;
373 for (relay = daemon->relay6; relay; relay = relay->next)
374 if (IN6_ARE_ADDR_EQUAL(local, &relay->local.addr.addr6) && relay->current == relay &&
375 (IN6_IS_ADDR_UNSPECIFIED(&param->relay_local) || IN6_ARE_ADDR_EQUAL(local, &param->relay_local)))
377 relay->current = param->relay;
378 param->relay = relay;
379 param->relay_local = *local;
384 return 1;
387 struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct in6_addr *net, int prefix, u64 addr)
389 struct dhcp_config *config;
391 for (config = configs; config; config = config->next)
392 if ((config->flags & CONFIG_ADDR6) &&
393 is_same_net6(&config->addr6, net, prefix) &&
394 (prefix == 128 || addr6part(&config->addr6) == addr))
395 return config;
397 return NULL;
400 struct dhcp_context *address6_allocate(struct dhcp_context *context, unsigned char *clid, int clid_len, int temp_addr,
401 int iaid, int serial, struct dhcp_netid *netids, int plain_range, struct in6_addr *ans)
403 /* Find a free address: exclude anything in use and anything allocated to
404 a particular hwaddr/clientid/hostname in our configuration.
405 Try to return from contexts which match netids first.
407 Note that we assume the address prefix lengths are 64 or greater, so we can
408 get by with 64 bit arithmetic.
411 u64 start, addr;
412 struct dhcp_context *c, *d;
413 int i, pass;
414 u64 j;
416 /* hash hwaddr: use the SDBM hashing algorithm. This works
417 for MAC addresses, let's see how it manages with client-ids!
418 For temporary addresses, we generate a new random one each time. */
419 if (temp_addr)
420 j = rand64();
421 else
422 for (j = iaid, i = 0; i < clid_len; i++)
423 j = clid[i] + (j << 6) + (j << 16) - j;
425 for (pass = 0; pass <= plain_range ? 1 : 0; pass++)
426 for (c = context; c; c = c->current)
427 if (c->flags & (CONTEXT_DEPRECATE | CONTEXT_STATIC | CONTEXT_RA_STATELESS | CONTEXT_USED))
428 continue;
429 else if (!match_netid(c->filter, netids, pass))
430 continue;
431 else
433 if (!temp_addr && option_bool(OPT_CONSEC_ADDR))
434 /* seed is largest extant lease addr in this context */
435 start = lease_find_max_addr6(c) + serial;
436 else
438 u64 range = 1 + addr6part(&c->end6) - addr6part(&c->start6);
439 u64 offset = j + c->addr_epoch;
441 /* don't divide by zero if range is whole 2^64 */
442 if (range != 0)
443 offset = offset % range;
445 start = addr6part(&c->start6) + offset;
448 /* iterate until we find a free address. */
449 addr = start;
451 do {
452 /* eliminate addresses in use by the server. */
453 for (d = context; d; d = d->current)
454 if (addr == addr6part(&d->local6))
455 break;
457 if (!d &&
458 !lease6_find_by_addr(&c->start6, c->prefix, addr) &&
459 !config_find_by_address6(daemon->dhcp_conf, &c->start6, c->prefix, addr))
461 *ans = c->start6;
462 setaddr6part (ans, addr);
463 return c;
466 addr++;
468 if (addr == addr6part(&c->end6) + 1)
469 addr = addr6part(&c->start6);
471 } while (addr != start);
474 return NULL;
477 /* can dynamically allocate addr */
478 struct dhcp_context *address6_available(struct dhcp_context *context,
479 struct in6_addr *taddr,
480 struct dhcp_netid *netids,
481 int plain_range)
483 u64 start, end, addr = addr6part(taddr);
484 struct dhcp_context *tmp;
486 for (tmp = context; tmp; tmp = tmp->current)
488 start = addr6part(&tmp->start6);
489 end = addr6part(&tmp->end6);
491 if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_RA_STATELESS)) &&
492 is_same_net6(&tmp->start6, taddr, tmp->prefix) &&
493 is_same_net6(&tmp->end6, taddr, tmp->prefix) &&
494 addr >= start &&
495 addr <= end &&
496 match_netid(tmp->filter, netids, plain_range))
497 return tmp;
500 return NULL;
503 /* address OK if configured */
504 struct dhcp_context *address6_valid(struct dhcp_context *context,
505 struct in6_addr *taddr,
506 struct dhcp_netid *netids,
507 int plain_range)
509 struct dhcp_context *tmp;
511 for (tmp = context; tmp; tmp = tmp->current)
512 if (is_same_net6(&tmp->start6, taddr, tmp->prefix) &&
513 match_netid(tmp->filter, netids, plain_range))
514 return tmp;
516 return NULL;
519 int config_valid(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr)
521 if (!config || !(config->flags & CONFIG_ADDR6))
522 return 0;
524 if ((config->flags & CONFIG_WILDCARD) && context->prefix == 64)
526 *addr = context->start6;
527 setaddr6part(addr, addr6part(&config->addr6));
528 return 1;
531 if (is_same_net6(&context->start6, &config->addr6, context->prefix))
533 *addr = config->addr6;
534 return 1;
537 return 0;
540 void make_duid(time_t now)
542 (void)now;
544 if (daemon->duid_config)
546 unsigned char *p;
548 daemon->duid = p = safe_malloc(daemon->duid_config_len + 6);
549 daemon->duid_len = daemon->duid_config_len + 6;
550 PUTSHORT(2, p); /* DUID_EN */
551 PUTLONG(daemon->duid_enterprise, p);
552 memcpy(p, daemon->duid_config, daemon->duid_config_len);
554 else
556 time_t newnow = 0;
558 /* If we have no persistent lease database, or a non-stable RTC, use DUID_LL (newnow == 0) */
559 #ifndef HAVE_BROKEN_RTC
560 /* rebase epoch to 1/1/2000 */
561 if (!option_bool(OPT_LEASE_RO) || daemon->lease_change_command)
562 newnow = now - 946684800;
563 #endif
565 iface_enumerate(AF_LOCAL, &newnow, make_duid1);
567 if(!daemon->duid)
568 die("Cannot create DHCPv6 server DUID: %s", NULL, EC_MISC);
572 static int make_duid1(int index, unsigned int type, char *mac, size_t maclen, void *parm)
574 /* create DUID as specified in RFC3315. We use the MAC of the
575 first interface we find that isn't loopback or P-to-P and
576 has address-type < 256. Address types above 256 are things like
577 tunnels which don't have usable MAC addresses. */
579 unsigned char *p;
580 (void)index;
581 (void)parm;
582 time_t newnow = *((time_t *)parm);
584 if (type >= 256)
585 return 1;
587 if (newnow == 0)
589 daemon->duid = p = safe_malloc(maclen + 4);
590 daemon->duid_len = maclen + 4;
591 PUTSHORT(3, p); /* DUID_LL */
592 PUTSHORT(type, p); /* address type */
594 else
596 daemon->duid = p = safe_malloc(maclen + 8);
597 daemon->duid_len = maclen + 8;
598 PUTSHORT(1, p); /* DUID_LLT */
599 PUTSHORT(type, p); /* address type */
600 PUTLONG(*((time_t *)parm), p); /* time */
603 memcpy(p, mac, maclen);
605 return 0;
608 struct cparam {
609 time_t now;
610 int newone, newname;
613 static int construct_worker(struct in6_addr *local, int prefix,
614 int scope, int if_index, int flags,
615 int preferred, int valid, void *vparam)
617 char ifrn_name[IFNAMSIZ];
618 struct in6_addr start6, end6;
619 struct dhcp_context *template, *context;
621 (void)scope;
622 (void)flags;
623 (void)valid;
624 (void)preferred;
626 struct cparam *param = vparam;
628 if (IN6_IS_ADDR_LOOPBACK(local) ||
629 IN6_IS_ADDR_LINKLOCAL(local) ||
630 IN6_IS_ADDR_MULTICAST(local))
631 return 1;
633 if (!(flags & IFACE_PERMANENT))
634 return 1;
636 if (flags & IFACE_DEPRECATED)
637 return 1;
639 if (!indextoname(daemon->icmp6fd, if_index, ifrn_name))
640 return 0;
642 for (template = daemon->dhcp6; template; template = template->next)
643 if (!(template->flags & CONTEXT_TEMPLATE))
645 /* non-template entries, just fill in interface and local addresses */
646 if (prefix <= template->prefix &&
647 is_same_net6(local, &template->start6, template->prefix) &&
648 is_same_net6(local, &template->end6, template->prefix))
650 template->if_index = if_index;
651 template->local6 = *local;
655 else if (wildcard_match(template->template_interface, ifrn_name) &&
656 template->prefix >= prefix)
658 start6 = *local;
659 setaddr6part(&start6, addr6part(&template->start6));
660 end6 = *local;
661 setaddr6part(&end6, addr6part(&template->end6));
663 for (context = daemon->dhcp6; context; context = context->next)
664 if ((context->flags & CONTEXT_CONSTRUCTED) &&
665 IN6_ARE_ADDR_EQUAL(&start6, &context->start6) &&
666 IN6_ARE_ADDR_EQUAL(&end6, &context->end6))
668 int flags = context->flags;
669 context->flags &= ~(CONTEXT_GC | CONTEXT_OLD);
670 if (flags & CONTEXT_OLD)
672 /* address went, now it's back */
673 log_context(AF_INET6, context);
674 /* fast RAs for a while */
675 ra_start_unsolicted(param->now, context);
676 param->newone = 1;
677 /* Add address to name again */
678 if (context->flags & CONTEXT_RA_NAME)
679 param->newname = 1;
681 break;
684 if (!context && (context = whine_malloc(sizeof (struct dhcp_context))))
686 *context = *template;
687 context->start6 = start6;
688 context->end6 = end6;
689 context->flags &= ~CONTEXT_TEMPLATE;
690 context->flags |= CONTEXT_CONSTRUCTED;
691 context->if_index = if_index;
692 context->local6 = *local;
693 context->saved_valid = 0;
695 context->next = daemon->dhcp6;
696 daemon->dhcp6 = context;
698 ra_start_unsolicted(param->now, context);
699 /* we created a new one, need to call
700 lease_update_file to get periodic functions called */
701 param->newone = 1;
703 /* Will need to add new putative SLAAC addresses to existing leases */
704 if (context->flags & CONTEXT_RA_NAME)
705 param->newname = 1;
707 log_context(AF_INET6, context);
711 return 1;
714 void dhcp_construct_contexts(time_t now)
716 struct dhcp_context *context, *tmp, **up;
717 struct cparam param;
718 param.newone = 0;
719 param.newname = 0;
720 param.now = now;
722 for (context = daemon->dhcp6; context; context = context->next)
723 if (context->flags & CONTEXT_CONSTRUCTED)
724 context->flags |= CONTEXT_GC;
726 iface_enumerate(AF_INET6, &param, construct_worker);
728 for (up = &daemon->dhcp6, context = daemon->dhcp6; context; context = tmp)
731 tmp = context->next;
733 if (context->flags & CONTEXT_GC && !(context->flags & CONTEXT_OLD))
735 if ((context->flags & CONTEXT_RA) || option_bool(OPT_RA))
737 /* previously constructed context has gone. advertise it's demise */
738 context->flags |= CONTEXT_OLD;
739 context->address_lost_time = now;
740 /* Apply same ceiling of configured lease time as in radv.c */
741 if (context->saved_valid > context->lease_time)
742 context->saved_valid = context->lease_time;
743 /* maximum time is 2 hours, from RFC */
744 if (context->saved_valid > 7200) /* 2 hours */
745 context->saved_valid = 7200;
746 ra_start_unsolicted(now, context);
747 param.newone = 1; /* include deletion */
749 if (context->flags & CONTEXT_RA_NAME)
750 param.newname = 1;
752 log_context(AF_INET6, context);
754 up = &context->next;
756 else
758 /* we were never doing RA for this, so free now */
759 *up = context->next;
760 free(context);
763 else
764 up = &context->next;
767 if (param.newone)
769 if (daemon->dhcp || daemon->doing_dhcp6)
771 if (param.newname)
772 lease_update_slaac(now);
773 lease_update_file(now);
775 else
776 /* Not doing DHCP, so no lease system, manage alarms for ra only */
777 send_alarm(periodic_ra(now), now);
781 #endif