Dnsmasq v2.68 rc4
[tomato.git] / release / src / router / dnsmasq / src / dhcp6.c
blobf5cbffd4916627ece7063ab37347300027dafed0
1 /* dnsmasq is Copyright (c) 2000-2013 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;
27 int ind, addr_match;
30 struct mac_param {
31 struct in6_addr *target;
32 unsigned char *mac;
33 unsigned int maclen;
37 static int complete_context6(struct in6_addr *local, int prefix,
38 int scope, int if_index, int flags,
39 unsigned int preferred, unsigned int valid, void *vparam);
40 static int find_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv);
41 static int make_duid1(int index, unsigned int type, char *mac, size_t maclen, void *parm);
43 void dhcp6_init(void)
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;
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);
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))
68 int rc = 0;
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
76 if (rc != -1)
77 rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt));
79 if (rc == -1)
80 die(_("failed to set SO_REUSE{ADDR|PORT} on DHCPv6 socket: %s"), NULL, EC_BADNET);
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);
91 if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in6)))
92 die(_("failed to bind DHCPv6 server socket: %s"), NULL, EC_BADNET);
94 daemon->dhcp6fd = fd;
97 void dhcp6_packet(time_t now)
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;
116 memset(&dst_addr, 0, sizeof(dst_addr));
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;
126 if ((sz = recv_dhcp_packet(daemon->dhcp6fd, &msg)) == -1)
127 return;
129 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
130 if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
132 union {
133 unsigned char *c;
134 struct in6_pktinfo *p;
135 } p;
136 p.c = CMSG_DATA(cmptr);
138 if_index = p.p->ipi6_ifindex;
139 dst_addr = p.p->ipi6_addr;
142 if (!indextoname(daemon->dhcp6fd, if_index, ifr.ifr_name))
143 return;
145 if ((port = relay_reply6(&from, sz, ifr.ifr_name)) == 0)
147 for (tmp = daemon->if_except; tmp; tmp = tmp->next)
148 if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
149 return;
151 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
152 if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
153 return;
155 parm.current = NULL;
156 parm.relay = NULL;
157 memset(&parm.relay_local, 0, IN6ADDRSZ);
158 parm.ind = if_index;
159 parm.addr_match = 0;
160 memset(&parm.fallback, 0, IN6ADDRSZ);
162 for (context = daemon->dhcp6; context; context = context->next)
163 if (IN6_IS_ADDR_UNSPECIFIED(&context->start6) && context->prefix == 0)
165 /* wildcard context for DHCP-stateless only */
166 parm.current = context;
167 context->current = NULL;
169 else
171 /* unlinked contexts are marked by context->current == context */
172 context->current = context;
173 memset(&context->local6, 0, IN6ADDRSZ);
176 for (relay = daemon->relay6; relay; relay = relay->next)
177 relay->current = relay;
179 if (!iface_enumerate(AF_INET6, &parm, complete_context6))
180 return;
182 if (daemon->if_names || daemon->if_addrs)
185 for (tmp = daemon->if_names; tmp; tmp = tmp->next)
186 if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
187 break;
189 if (!tmp && !parm.addr_match)
190 return;
193 if (parm.relay)
195 /* Ignore requests sent to the ALL_SERVERS multicast address for relay when
196 we're listening there for DHCPv6 server reasons. */
197 struct in6_addr all_servers;
199 inet_pton(AF_INET6, ALL_SERVERS, &all_servers);
201 if (!IN6_ARE_ADDR_EQUAL(&dst_addr, &all_servers))
202 relay_upstream6(parm.relay, sz, &from.sin6_addr, from.sin6_scope_id);
203 return;
206 /* May have configured relay, but not DHCP server */
207 if (!daemon->doing_dhcp6)
208 return;
210 lease_prune(NULL, now); /* lose any expired leases */
212 port = dhcp6_reply(parm.current, if_index, ifr.ifr_name, &parm.fallback,
213 sz, &from.sin6_addr, now);
215 lease_update_file(now);
216 lease_update_dns(0);
219 /* The port in the source address of the original request should
220 be correct, but at least once client sends from the server port,
221 so we explicitly send to the client port to a client, and the
222 server port to a relay. */
223 if (port != 0)
225 from.sin6_port = htons(port);
226 while (sendto(daemon->dhcp6fd, daemon->outpacket.iov_base, save_counter(0),
227 0, (struct sockaddr *)&from, sizeof(from)) == -1 &&
228 retry_send());
232 void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, unsigned int *maclenp, unsigned int *mactypep)
234 /* Recieving a packet from a host does not populate the neighbour
235 cache, so we send a neighbour discovery request if we can't
236 find the sender. Repeat a few times in case of packet loss. */
238 struct neigh_packet neigh;
239 struct sockaddr_in6 addr;
240 struct mac_param mac_param;
241 int i;
243 neigh.type = ND_NEIGHBOR_SOLICIT;
244 neigh.code = 0;
245 neigh.reserved = 0;
246 neigh.target = *client;
248 memset(&addr, 0, sizeof(addr));
249 #ifdef HAVE_SOCKADDR_SA_LEN
250 addr.sin6_len = sizeof(struct sockaddr_in6);
251 #endif
252 addr.sin6_family = AF_INET6;
253 addr.sin6_port = htons(IPPROTO_ICMPV6);
254 addr.sin6_addr = *client;
255 addr.sin6_scope_id = iface;
257 mac_param.target = client;
258 mac_param.maclen = 0;
259 mac_param.mac = mac;
261 for (i = 0; i < 5; i++)
263 struct timespec ts;
265 iface_enumerate(AF_UNSPEC, &mac_param, find_mac);
267 if (mac_param.maclen != 0)
268 break;
270 sendto(daemon->icmp6fd, &neigh, sizeof(neigh), 0, (struct sockaddr *)&addr, sizeof(addr));
272 ts.tv_sec = 0;
273 ts.tv_nsec = 100000000; /* 100ms */
274 nanosleep(&ts, NULL);
277 *maclenp = mac_param.maclen;
278 *mactypep = ARPHRD_ETHER;
281 static int find_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv)
283 struct mac_param *parm = parmv;
285 if (family == AF_INET6 && IN6_ARE_ADDR_EQUAL(parm->target, (struct in6_addr *)addrp))
287 if (maclen <= DHCP_CHADDR_MAX)
289 parm->maclen = maclen;
290 memcpy(parm->mac, mac, maclen);
293 return 0; /* found, abort */
296 return 1;
299 static int complete_context6(struct in6_addr *local, int prefix,
300 int scope, int if_index, int flags, unsigned int preferred,
301 unsigned int valid, void *vparam)
303 struct dhcp_context *context;
304 struct dhcp_relay *relay;
305 struct iface_param *param = vparam;
306 struct iname *tmp;
308 (void)scope; /* warning */
310 if (if_index == param->ind)
312 if (!IN6_IS_ADDR_LOOPBACK(local) &&
313 !IN6_IS_ADDR_LINKLOCAL(local) &&
314 !IN6_IS_ADDR_MULTICAST(local))
316 /* if we have --listen-address config, see if the
317 arrival interface has a matching address. */
318 for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
319 if (tmp->addr.sa.sa_family == AF_INET6 &&
320 IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, local))
321 param->addr_match = 1;
323 /* Determine a globally address on the arrival interface, even
324 if we have no matching dhcp-context, because we're only
325 allocating on remote subnets via relays. This
326 is used as a default for the DNS server option. */
327 param->fallback = *local;
329 for (context = daemon->dhcp6; context; context = context->next)
331 if ((context->flags & CONTEXT_DHCP) &&
332 !(context->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) &&
333 #ifdef HAVE_TOMATO
334 prefix <= context->prefix &&
335 #else
336 prefix == context->prefix &&
337 #endif
338 is_same_net6(local, &context->start6, prefix) &&
339 is_same_net6(local, &context->end6, prefix))
343 /* link it onto the current chain if we've not seen it before */
344 if (context->current == context)
346 struct dhcp_context *tmp, **up;
348 /* use interface values only for contructed contexts */
349 if (!(context->flags & CONTEXT_CONSTRUCTED))
350 preferred = valid = 0xffffffff;
351 else if (flags & IFACE_DEPRECATED)
352 preferred = 0;
354 if (context->flags & CONTEXT_DEPRECATE)
355 preferred = 0;
357 /* order chain, longest preferred time first */
358 for (up = &param->current, tmp = param->current; tmp; tmp = tmp->current)
359 if (tmp->preferred <= preferred)
360 break;
361 else
362 up = &tmp->current;
364 context->current = *up;
365 *up = context;
366 context->local6 = *local;
367 context->preferred = preferred;
368 context->valid = valid;
374 for (relay = daemon->relay6; relay; relay = relay->next)
375 if (IN6_ARE_ADDR_EQUAL(local, &relay->local.addr.addr6) && relay->current == relay &&
376 (IN6_IS_ADDR_UNSPECIFIED(&param->relay_local) || IN6_ARE_ADDR_EQUAL(local, &param->relay_local)))
378 relay->current = param->relay;
379 param->relay = relay;
380 param->relay_local = *local;
385 return 1;
388 struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct in6_addr *net, int prefix, u64 addr)
390 struct dhcp_config *config;
392 for (config = configs; config; config = config->next)
393 if ((config->flags & CONFIG_ADDR6) &&
394 is_same_net6(&config->addr6, net, prefix) &&
395 (prefix == 128 || addr6part(&config->addr6) == addr))
396 return config;
398 return NULL;
401 struct dhcp_context *address6_allocate(struct dhcp_context *context, unsigned char *clid, int clid_len, int temp_addr,
402 int iaid, int serial, struct dhcp_netid *netids, int plain_range, struct in6_addr *ans)
404 /* Find a free address: exclude anything in use and anything allocated to
405 a particular hwaddr/clientid/hostname in our configuration.
406 Try to return from contexts which match netids first.
408 Note that we assume the address prefix lengths are 64 or greater, so we can
409 get by with 64 bit arithmetic.
412 u64 start, addr;
413 struct dhcp_context *c, *d;
414 int i, pass;
415 u64 j;
417 /* hash hwaddr: use the SDBM hashing algorithm. This works
418 for MAC addresses, let's see how it manages with client-ids!
419 For temporary addresses, we generate a new random one each time. */
420 if (temp_addr)
421 j = rand64();
422 else
423 for (j = iaid, i = 0; i < clid_len; i++)
424 j += clid[i] + (j << 6) + (j << 16) - j;
426 for (pass = 0; pass <= plain_range ? 1 : 0; pass++)
427 for (c = context; c; c = c->current)
428 if (c->flags & (CONTEXT_DEPRECATE | CONTEXT_STATIC | CONTEXT_RA_STATELESS | CONTEXT_USED))
429 continue;
430 else if (!match_netid(c->filter, netids, pass))
431 continue;
432 else
434 if (!temp_addr && option_bool(OPT_CONSEC_ADDR))
435 /* seed is largest extant lease addr in this context */
436 start = lease_find_max_addr6(c) + serial;
437 else
438 start = addr6part(&c->start6) + ((j + c->addr_epoch) % (1 + addr6part(&c->end6) - addr6part(&c->start6)));
440 /* iterate until we find a free address. */
441 addr = start;
443 do {
444 /* eliminate addresses in use by the server. */
445 for (d = context; d; d = d->current)
446 if (addr == addr6part(&d->local6))
447 break;
449 if (!d &&
450 !lease6_find_by_addr(&c->start6, c->prefix, addr) &&
451 !config_find_by_address6(daemon->dhcp_conf, &c->start6, c->prefix, addr))
453 *ans = c->start6;
454 setaddr6part (ans, addr);
455 return c;
458 addr++;
460 if (addr == addr6part(&c->end6) + 1)
461 addr = addr6part(&c->start6);
463 } while (addr != start);
466 return NULL;
469 /* can dynamically allocate addr */
470 struct dhcp_context *address6_available(struct dhcp_context *context,
471 struct in6_addr *taddr,
472 struct dhcp_netid *netids,
473 int plain_range)
475 u64 start, end, addr = addr6part(taddr);
476 struct dhcp_context *tmp;
478 for (tmp = context; tmp; tmp = tmp->current)
480 start = addr6part(&tmp->start6);
481 end = addr6part(&tmp->end6);
483 if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_RA_STATELESS)) &&
484 is_same_net6(&tmp->start6, taddr, tmp->prefix) &&
485 is_same_net6(&tmp->end6, taddr, tmp->prefix) &&
486 addr >= start &&
487 addr <= end &&
488 match_netid(tmp->filter, netids, plain_range))
489 return tmp;
492 return NULL;
495 /* address OK if configured */
496 struct dhcp_context *address6_valid(struct dhcp_context *context,
497 struct in6_addr *taddr,
498 struct dhcp_netid *netids,
499 int plain_range)
501 struct dhcp_context *tmp;
503 for (tmp = context; tmp; tmp = tmp->current)
504 if (is_same_net6(&tmp->start6, taddr, tmp->prefix) &&
505 match_netid(tmp->filter, netids, plain_range))
506 return tmp;
508 return NULL;
511 int config_valid(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr)
513 if (!config || !(config->flags & CONFIG_ADDR6))
514 return 0;
516 if ((config->flags & CONFIG_WILDCARD) && context->prefix == 64)
518 *addr = context->start6;
519 setaddr6part(addr, addr6part(&config->addr6));
520 return 1;
523 if (is_same_net6(&context->start6, &config->addr6, context->prefix))
525 *addr = config->addr6;
526 return 1;
529 return 0;
532 void make_duid(time_t now)
534 (void)now;
536 if (daemon->duid_config)
538 unsigned char *p;
540 daemon->duid = p = safe_malloc(daemon->duid_config_len + 6);
541 daemon->duid_len = daemon->duid_config_len + 6;
542 PUTSHORT(2, p); /* DUID_EN */
543 PUTLONG(daemon->duid_enterprise, p);
544 memcpy(p, daemon->duid_config, daemon->duid_config_len);
546 else
548 time_t newnow = 0;
550 /* If we have no persistent lease database, or a non-stable RTC, use DUID_LL (newnow == 0) */
551 #ifndef HAVE_BROKEN_RTC
552 /* rebase epoch to 1/1/2000 */
553 if (!option_bool(OPT_LEASE_RO) || daemon->lease_change_command)
554 newnow = now - 946684800;
555 #endif
557 iface_enumerate(AF_LOCAL, &newnow, make_duid1);
559 if(!daemon->duid)
560 die("Cannot create DHCPv6 server DUID: %s", NULL, EC_MISC);
564 static int make_duid1(int index, unsigned int type, char *mac, size_t maclen, void *parm)
566 /* create DUID as specified in RFC3315. We use the MAC of the
567 first interface we find that isn't loopback or P-to-P and
568 has address-type < 256. Address types above 256 are things like
569 tunnels which don't have usable MAC addresses. */
571 unsigned char *p;
572 (void)index;
573 (void)parm;
574 time_t newnow = *((time_t *)parm);
576 if (type >= 256)
577 return 1;
579 if (newnow == 0)
581 daemon->duid = p = safe_malloc(maclen + 4);
582 daemon->duid_len = maclen + 4;
583 PUTSHORT(3, p); /* DUID_LL */
584 PUTSHORT(type, p); /* address type */
586 else
588 daemon->duid = p = safe_malloc(maclen + 8);
589 daemon->duid_len = maclen + 8;
590 PUTSHORT(1, p); /* DUID_LLT */
591 PUTSHORT(type, p); /* address type */
592 PUTLONG(*((time_t *)parm), p); /* time */
595 memcpy(p, mac, maclen);
597 return 0;
600 struct cparam {
601 time_t now;
602 int newone, newname;
605 static int construct_worker(struct in6_addr *local, int prefix,
606 int scope, int if_index, int flags,
607 int preferred, int valid, void *vparam)
609 char ifrn_name[IFNAMSIZ];
610 struct in6_addr start6, end6;
611 struct dhcp_context *template, *context;
613 (void)scope;
614 (void)flags;
615 (void)valid;
616 (void)preferred;
618 struct cparam *param = vparam;
620 if (IN6_IS_ADDR_LOOPBACK(local) ||
621 IN6_IS_ADDR_LINKLOCAL(local) ||
622 IN6_IS_ADDR_MULTICAST(local))
623 return 1;
625 if (!(flags & IFACE_PERMANENT))
626 return 1;
628 if (flags & IFACE_DEPRECATED)
629 return 1;
631 if (!indextoname(daemon->icmp6fd, if_index, ifrn_name))
632 return 0;
634 for (template = daemon->dhcp6; template; template = template->next)
635 if (!(template->flags & CONTEXT_TEMPLATE))
637 /* non-template entries, just fill in interface and local addresses */
638 #ifdef HAVE_TOMATO
639 if (prefix <= template->prefix &&
640 #else
641 if (prefix == template->prefix &&
642 #endif
643 is_same_net6(local, &template->start6, prefix) &&
644 is_same_net6(local, &template->end6, prefix))
646 template->if_index = if_index;
647 template->local6 = *local;
651 else if (wildcard_match(template->template_interface, ifrn_name) &&
652 #ifdef HAVE_TOMATO
653 template->prefix >= prefix)
654 #else
655 template->prefix == prefix)
656 #endif
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))
736 if ((context->flags & (CONTEXT_RA_ONLY | CONTEXT_RA_NAME | CONTEXT_RA_STATELESS)) ||
737 option_bool(OPT_RA))
739 /* previously constructed context has gone. advertise it's demise */
740 context->flags |= CONTEXT_OLD;
741 context->address_lost_time = now;
742 /* Apply same ceiling of configured lease time as in radv.c */
743 if (context->saved_valid > context->lease_time)
744 context->saved_valid = context->lease_time;
745 /* maximum time is 2 hours, from RFC */
746 if (context->saved_valid > 7200) /* 2 hours */
747 context->saved_valid = 7200;
748 ra_start_unsolicted(now, context);
749 param.newone = 1; /* include deletion */
751 if (context->flags & CONTEXT_RA_NAME)
752 param.newname = 1;
754 log_context(AF_INET6, context);
756 up = &context->next;
758 else
760 /* we were never doing RA for this, so free now */
761 *up = context->next;
762 free(context);
765 else
766 up = &context->next;
769 if (param.newone)
771 if (daemon->dhcp || daemon->doing_dhcp6)
773 if (param.newname)
774 lease_update_slaac(now);
775 lease_update_file(now);
777 else
778 /* Not doing DHCP, so no lease system, manage alarms for ra only */
779 send_alarm(periodic_ra(now), now);
783 #endif