]> git.ipfire.org Git - people/ms/dnsmasq.git/blame - src/radv.c
Make --clear-on-reload apply to DBus API too.
[people/ms/dnsmasq.git] / src / radv.c
CommitLineData
61744359 1/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
c5ad4e79
SK
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
843c96b4 18/* NB. This code may be called during a DHCPv4 or transaction which is in ping-wait
c5ad4e79 19 It therefore cannot use any DHCP buffer resources except outpacket, which is
843c96b4
SK
20 not used by DHCPv4 code. This code may also be called when DHCP 4 or 6 isn't
21 active, so we ensure that outpacket is allocated here too */
c5ad4e79
SK
22
23#include "dnsmasq.h"
c5ad4e79
SK
24
25#ifdef HAVE_DHCP6
26
22d904db
SK
27#include <netinet/icmp6.h>
28
c5ad4e79 29struct ra_param {
1f776932 30 time_t now;
30cd9666 31 int ind, managed, other, found_context, first;
c5ad4e79 32 char *if_name;
18f0fb05 33 struct dhcp_netid *tags;
55b42f6d
SK
34 struct in6_addr link_local, link_global;
35 unsigned int pref_time;
c5ad4e79
SK
36};
37
38struct search_param {
39 time_t now; int iface;
40};
41
1f776932 42static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *dest);
c5ad4e79 43static int add_prefixes(struct in6_addr *local, int prefix,
bad7b875 44 int scope, int if_index, int flags,
55b42f6d 45 unsigned int preferred, unsigned int valid, void *vparam);
c5ad4e79 46static int iface_search(struct in6_addr *local, int prefix,
bad7b875 47 int scope, int if_index, int flags,
1f776932 48 int prefered, int valid, void *vparam);
c5ad4e79
SK
49static int add_lla(int index, unsigned int type, char *mac, size_t maclen, void *parm);
50
c5379c1a 51static int hop_limit;
c5ad4e79
SK
52
53void ra_init(time_t now)
54{
c5ad4e79
SK
55 struct icmp6_filter filter;
56 int fd;
0e88d53f 57#if defined(IPV6_TCLASS) && defined(IPTOS_CLASS_CS6)
c5ad4e79
SK
58 int class = IPTOS_CLASS_CS6;
59#endif
60 int val = 255; /* radvd uses this value */
7b6dd880 61 socklen_t len = sizeof(int);
353ae4d2
SK
62 struct dhcp_context *context;
63
843c96b4
SK
64 /* ensure this is around even if we're not doing DHCPv6 */
65 expand_buf(&daemon->outpacket, sizeof(struct dhcp_packet));
353ae4d2
SK
66
67 /* See if we're guessing SLAAC addresses, if so we need to recieve ping replies */
1f776932 68 for (context = daemon->dhcp6; context; context = context->next)
353ae4d2
SK
69 if ((context->flags & CONTEXT_RA_NAME))
70 break;
71
c5ad4e79
SK
72 ICMP6_FILTER_SETBLOCKALL(&filter);
73 ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filter);
353ae4d2
SK
74 if (context)
75 ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filter);
c5ad4e79
SK
76
77 if ((fd = socket(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) == -1 ||
c5379c1a 78 getsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hop_limit, &len) ||
0e88d53f 79#if defined(IPV6_TCLASS) && defined(IPTOS_CLASS_CS6)
c5ad4e79
SK
80 setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &class, sizeof(class)) == -1 ||
81#endif
82 !fix_fd(fd) ||
83 !set_ipv6pktinfo(fd) ||
84 setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &val, sizeof(val)) ||
85 setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, sizeof(val)) ||
86 setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof(filter)) == -1)
87 die (_("cannot create ICMPv6 socket: %s"), NULL, EC_BADNET);
88
89 daemon->icmp6fd = fd;
90
353ae4d2 91 ra_start_unsolicted(now, NULL);
c5ad4e79
SK
92}
93
353ae4d2 94void ra_start_unsolicted(time_t now, struct dhcp_context *context)
c5ad4e79 95{
353ae4d2 96 /* init timers so that we do ra's for some/all soon. some ra_times will end up zeroed
c5ad4e79
SK
97 if it's not appropriate to advertise those contexts.
98 This gets re-called on a netlink route-change to re-do the advertisement
99 and pick up new interfaces */
6e3dba3f 100
353ae4d2 101 if (context)
1b75c1e6 102 context->ra_short_period_start = context->ra_time = now;
353ae4d2 103 else
1f776932 104 for (context = daemon->dhcp6; context; context = context->next)
6e3dba3f 105 if (!(context->flags & CONTEXT_TEMPLATE))
1b75c1e6
SK
106 {
107 context->ra_time = now + (rand16()/13000); /* range 0 - 5 */
108 /* re-do frequently for a minute or so, in case the first gets lost. */
109 context->ra_short_period_start = now;
110 }
c5ad4e79
SK
111}
112
1f776932 113void icmp6_packet(time_t now)
c5ad4e79
SK
114{
115 char interface[IF_NAMESIZE+1];
116 ssize_t sz;
117 int if_index = 0;
118 struct cmsghdr *cmptr;
119 struct msghdr msg;
120 union {
121 struct cmsghdr align; /* this ensures alignment */
122 char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
123 } control_u;
124 struct sockaddr_in6 from;
5ef33279 125 unsigned char *packet;
c5ad4e79 126 struct iname *tmp;
c5ad4e79
SK
127
128 /* Note: use outpacket for input buffer */
129 msg.msg_control = control_u.control6;
130 msg.msg_controllen = sizeof(control_u);
131 msg.msg_flags = 0;
132 msg.msg_name = &from;
133 msg.msg_namelen = sizeof(from);
134 msg.msg_iov = &daemon->outpacket;
135 msg.msg_iovlen = 1;
136
137 if ((sz = recv_dhcp_packet(daemon->icmp6fd, &msg)) == -1 || sz < 8)
138 return;
5ef33279
SK
139
140 packet = (unsigned char *)daemon->outpacket.iov_base;
c5ad4e79
SK
141
142 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
143 if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
144 {
145 union {
146 unsigned char *c;
147 struct in6_pktinfo *p;
148 } p;
149 p.c = CMSG_DATA(cmptr);
150
151 if_index = p.p->ipi6_ifindex;
152 }
153
154 if (!indextoname(daemon->icmp6fd, if_index, interface))
155 return;
156
4f7b304f 157 if (!iface_check(AF_LOCAL, NULL, interface, NULL))
c5ad4e79
SK
158 return;
159
160 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
49333cbd 161 if (tmp->name && wildcard_match(tmp->name, interface))
c5ad4e79
SK
162 return;
163
8bc4cece 164 if (packet[1] != 0)
353ae4d2 165 return;
8bc4cece 166
5ef33279
SK
167 if (packet[0] == ICMP6_ECHO_REPLY)
168 lease_ping_reply(&from.sin6_addr, packet, interface);
169 else if (packet[0] == ND_ROUTER_SOLICIT)
c5ad4e79 170 {
5ef33279
SK
171 char *mac = "";
172
173 /* look for link-layer address option for logging */
174 if (sz >= 16 && packet[8] == ICMP6_OPT_SOURCE_MAC && (packet[9] * 8) + 8 <= sz)
175 {
176 print_mac(daemon->namebuff, &packet[10], (packet[9] * 8) - 2);
177 mac = daemon->namebuff;
178 }
179
180 my_syslog(MS_DHCP | LOG_INFO, "RTR-SOLICIT(%s) %s", interface, mac);
f632e567 181 /* source address may not be valid in solicit request. */
1f776932 182 send_ra(now, if_index, interface, !IN6_IS_ADDR_UNSPECIFIED(&from.sin6_addr) ? &from.sin6_addr : NULL);
c5ad4e79 183 }
c5ad4e79
SK
184}
185
1f776932 186static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *dest)
c5ad4e79
SK
187{
188 struct ra_packet *ra;
189 struct ra_param parm;
190 struct ifreq ifr;
191 struct sockaddr_in6 addr;
192 struct dhcp_context *context;
18f0fb05
SK
193 struct dhcp_netid iface_id;
194 struct dhcp_opt *opt_cfg;
195 int done_dns = 0;
3b43646a
SK
196#ifdef HAVE_LINUX_NETWORK
197 FILE *f;
198#endif
199
c5ad4e79
SK
200 save_counter(0);
201 ra = expand(sizeof(struct ra_packet));
202
353ae4d2 203 ra->type = ND_ROUTER_ADVERT;
c5ad4e79 204 ra->code = 0;
c5379c1a 205 ra->hop_limit = hop_limit;
884a6dfe 206 ra->flags = 0x00;
dc9476b6 207 ra->lifetime = htons(RA_INTERVAL * 3); /* AdvDefaultLifetime * 3 */
c5ad4e79
SK
208 ra->reachable_time = 0;
209 ra->retrans_time = 0;
210
211 parm.ind = iface;
212 parm.managed = 0;
30cd9666 213 parm.other = 0;
c5ad4e79
SK
214 parm.found_context = 0;
215 parm.if_name = iface_name;
216 parm.first = 1;
1f776932 217 parm.now = now;
55b42f6d 218 parm.pref_time = 0;
1f776932 219
18f0fb05
SK
220 /* set tag with name == interface */
221 iface_id.net = iface_name;
222 iface_id.next = NULL;
223 parm.tags = &iface_id;
c5ad4e79 224
1f776932 225 for (context = daemon->dhcp6; context; context = context->next)
18f0fb05
SK
226 {
227 context->flags &= ~CONTEXT_RA_DONE;
228 context->netid.next = &context->netid;
229 }
230
c5ad4e79
SK
231 if (!iface_enumerate(AF_INET6, &parm, add_prefixes) ||
232 !parm.found_context)
233 return;
234
235 strncpy(ifr.ifr_name, iface_name, IF_NAMESIZE);
3b43646a
SK
236
237#ifdef HAVE_LINUX_NETWORK
238 /* Note that IPv6 MTU is not necessarilly the same as the IPv4 MTU
239 available from SIOCGIFMTU */
240 sprintf(daemon->namebuff, "/proc/sys/net/ipv6/conf/%s/mtu", iface_name);
241 if ((f = fopen(daemon->namebuff, "r")))
c5ad4e79 242 {
3b43646a
SK
243 if (fgets(daemon->namebuff, MAXDNAME, f))
244 {
245 put_opt6_char(ICMP6_OPT_MTU);
246 put_opt6_char(1);
247 put_opt6_short(0);
248 put_opt6_long(atoi(daemon->namebuff));
249 }
250 fclose(f);
c5ad4e79 251 }
3b43646a 252#endif
c5ad4e79
SK
253
254 iface_enumerate(AF_LOCAL, &iface, add_lla);
18f0fb05
SK
255
256 /* RDNSS, RFC 6106, use relevant DHCP6 options */
257 (void)option_filter(parm.tags, NULL, daemon->dhcp_opts6);
c5ad4e79 258
18f0fb05
SK
259 for (opt_cfg = daemon->dhcp_opts6; opt_cfg; opt_cfg = opt_cfg->next)
260 {
261 int i;
262
263 /* netids match and not encapsulated? */
264 if (!(opt_cfg->flags & DHOPT_TAGOK))
265 continue;
266
267 if (opt_cfg->opt == OPTION6_DNS_SERVER)
268 {
269 struct in6_addr *a = (struct in6_addr *)opt_cfg->val;
c5ad4e79 270
18f0fb05
SK
271 done_dns = 1;
272 if (opt_cfg->len == 0)
273 continue;
274
275 put_opt6_char(ICMP6_OPT_RDNSS);
276 put_opt6_char((opt_cfg->len/8) + 1);
277 put_opt6_short(0);
dc9476b6 278 put_opt6_long(RA_INTERVAL * 2); /* lifetime - twice RA retransmit */
18f0fb05
SK
279 /* zero means "self" */
280 for (i = 0; i < opt_cfg->len; i += IN6ADDRSZ, a++)
281 if (IN6_IS_ADDR_UNSPECIFIED(a))
55b42f6d 282 put_opt6(&parm.link_global, IN6ADDRSZ);
18f0fb05
SK
283 else
284 put_opt6(a, IN6ADDRSZ);
285 }
286
287 if (opt_cfg->opt == OPTION6_DOMAIN_SEARCH && opt_cfg->len != 0)
288 {
289 int len = ((opt_cfg->len+7)/8);
290
291 put_opt6_char(ICMP6_OPT_DNSSL);
292 put_opt6_char(len + 1);
293 put_opt6_short(0);
1ecbaaa3 294 put_opt6_long(RA_INTERVAL * 2); /* lifetime - twice RA retransmit */
18f0fb05
SK
295 put_opt6(opt_cfg->val, opt_cfg->len);
296
297 /* pad */
298 for (i = opt_cfg->len; i < len * 8; i++)
299 put_opt6_char(0);
300 }
301 }
302
ab915f83 303 if (daemon->port == NAMESERVER_PORT && !done_dns)
18f0fb05 304 {
ab915f83 305 /* default == us, as long as we are supplying DNS service. */
18f0fb05
SK
306 put_opt6_char(ICMP6_OPT_RDNSS);
307 put_opt6_char(3);
308 put_opt6_short(0);
dc9476b6 309 put_opt6_long(RA_INTERVAL * 2); /* lifetime - twice RA retransmit */
55b42f6d 310 put_opt6(&parm.link_global, IN6ADDRSZ);
18f0fb05 311 }
c5ad4e79
SK
312
313 /* set managed bits unless we're providing only RA on this link */
314 if (parm.managed)
30cd9666
SK
315 ra->flags |= 0x80; /* M flag, managed, */
316 if (parm.other)
317 ra->flags |= 0x40; /* O flag, other */
318
c5ad4e79
SK
319 /* decide where we're sending */
320 memset(&addr, 0, sizeof(addr));
22d904db
SK
321#ifdef HAVE_SOCKADDR_SA_LEN
322 addr.sin6_len = sizeof(struct sockaddr_in6);
323#endif
c5ad4e79
SK
324 addr.sin6_family = AF_INET6;
325 addr.sin6_port = htons(IPPROTO_ICMPV6);
326 if (dest)
327 {
353ae4d2 328 addr.sin6_addr = *dest;
c5ad4e79
SK
329 if (IN6_IS_ADDR_LINKLOCAL(dest) ||
330 IN6_IS_ADDR_MC_LINKLOCAL(dest))
331 addr.sin6_scope_id = iface;
332 }
333 else
f632e567 334 inet_pton(AF_INET6, ALL_NODES, &addr.sin6_addr);
22d904db 335
c5ad4e79 336 send_from(daemon->icmp6fd, 0, daemon->outpacket.iov_base, save_counter(0),
50303b19 337 (union mysockaddr *)&addr, (struct all_addr *)&parm.link_local, iface);
c5ad4e79
SK
338
339}
340
341static int add_prefixes(struct in6_addr *local, int prefix,
bad7b875 342 int scope, int if_index, int flags,
55b42f6d 343 unsigned int preferred, unsigned int valid, void *vparam)
c5ad4e79 344{
c5ad4e79 345 struct ra_param *param = vparam;
c5ad4e79
SK
346
347 (void)scope; /* warning */
ed8b68ad 348
c5ad4e79
SK
349 if (if_index == param->ind)
350 {
351 if (IN6_IS_ADDR_LINKLOCAL(local))
352 param->link_local = *local;
353 else if (!IN6_IS_ADDR_LOOPBACK(local) &&
c5ad4e79
SK
354 !IN6_IS_ADDR_MULTICAST(local))
355 {
1e02a859 356 int do_prefix = 0;
c8257540
SK
357 int do_slaac = 0;
358 int deprecate = 0;
3bc0d932 359 int constructed = 0;
c8257540 360 unsigned int time = 0xffffffff;
1e02a859
SK
361 struct dhcp_context *context;
362
1f776932 363 for (context = daemon->dhcp6; context; context = context->next)
6e3dba3f
SK
364 if (!(context->flags & CONTEXT_TEMPLATE) &&
365 prefix == context->prefix &&
c5ad4e79
SK
366 is_same_net6(local, &context->start6, prefix) &&
367 is_same_net6(local, &context->end6, prefix))
368 {
30cd9666
SK
369 if ((context->flags &
370 (CONTEXT_RA_ONLY | CONTEXT_RA_NAME | CONTEXT_RA_STATELESS)))
371 {
372 do_slaac = 1;
4723d49d 373 if (context->flags & CONTEXT_DHCP)
05e92e5a
SK
374 {
375 param->other = 1;
376 if (!(context->flags & CONTEXT_RA_STATELESS))
377 param->managed = 1;
378 }
30cd9666 379 }
884a6dfe 380 else
0010b474
SK
381 {
382 /* don't do RA for non-ra-only unless --enable-ra is set */
383 if (!option_bool(OPT_RA))
384 continue;
385 param->managed = 1;
30cd9666 386 param->other = 1;
0010b474 387 }
1f776932 388
1ecbaaa3 389 /* find floor time, don't reduce below 3 * RA interval. */
c8257540 390 if (time > context->lease_time)
7f035f58
SK
391 {
392 time = context->lease_time;
1ecbaaa3
SK
393 if (time < ((unsigned int)(3 * RA_INTERVAL)))
394 time = 3 * RA_INTERVAL;
7f035f58
SK
395 }
396
c8257540
SK
397 if (context->flags & CONTEXT_DEPRECATE)
398 deprecate = 1;
3bc0d932
SK
399
400 if (context->flags & CONTEXT_CONSTRUCTED)
401 constructed = 1;
402
c8257540 403
18f0fb05
SK
404 /* collect dhcp-range tags */
405 if (context->netid.next == &context->netid && context->netid.net)
406 {
407 context->netid.next = param->tags;
408 param->tags = &context->netid;
409 }
410
5ae34bf3
SK
411 /* subsequent prefixes on the same interface
412 and subsequent instances of this prefix don't need timers.
413 Be careful not to find the same prefix twice with different
414 addresses. */
1e02a859
SK
415 if (!(context->flags & CONTEXT_RA_DONE))
416 {
5ae34bf3
SK
417 if (!param->first)
418 context->ra_time = 0;
1e02a859
SK
419 context->flags |= CONTEXT_RA_DONE;
420 do_prefix = 1;
421 }
5ae34bf3
SK
422
423 param->first = 0;
424 param->found_context = 1;
c5ad4e79 425 }
1f776932 426
ed8b68ad 427 /* configured time is ceiling */
3bc0d932 428 if (!constructed || valid > time)
ed8b68ad
SK
429 valid = time;
430
3bc0d932 431 if (flags & IFACE_DEPRECATED)
ed8b68ad 432 preferred = 0;
1e02a859 433
3bc0d932
SK
434 if (deprecate)
435 time = 0;
436
437 /* configured time is ceiling */
438 if (!constructed || preferred > time)
439 preferred = time;
440
55b42f6d
SK
441 if (preferred > param->pref_time)
442 {
443 param->pref_time = preferred;
444 param->link_global = *local;
445 }
446
1e02a859 447 if (do_prefix)
c8257540 448 {
1e02a859
SK
449 struct prefix_opt *opt;
450
451 if ((opt = expand(sizeof(struct prefix_opt))))
452 {
5ef33279
SK
453 /* zero net part of address */
454 setaddr6part(local, addr6part(local) & ~((prefix == 64) ? (u64)-1LL : (1LLU << (128 - prefix)) - 1LLU));
455
1e02a859
SK
456 opt->type = ICMP6_OPT_PREFIX;
457 opt->len = 4;
458 opt->prefix_len = prefix;
fd05f127
SK
459 /* autonomous only if we're not doing dhcp, always set "on-link" */
460 opt->flags = do_slaac ? 0xC0 : 0x80;
ed8b68ad
SK
461 opt->valid_lifetime = htonl(valid);
462 opt->preferred_lifetime = htonl(preferred);
5ef33279 463 opt->reserved = 0;
1e02a859 464 opt->prefix = *local;
1e02a859 465
5ef33279 466 inet_ntop(AF_INET6, local, daemon->addrbuff, ADDRSTRLEN);
1e02a859
SK
467 my_syslog(MS_DHCP | LOG_INFO, "RTR-ADVERT(%s) %s", param->if_name, daemon->addrbuff);
468 }
c8257540 469 }
c5ad4e79
SK
470 }
471 }
472 return 1;
473}
474
475static int add_lla(int index, unsigned int type, char *mac, size_t maclen, void *parm)
476{
477 (void)type;
478
479 if (index == *((int *)parm))
480 {
481 /* size is in units of 8 octets and includes type and length (2 bytes)
482 add 7 to round up */
483 int len = (maclen + 9) >> 3;
484 unsigned char *p = expand(len << 3);
485 memset(p, 0, len << 3);
486 *p++ = ICMP6_OPT_SOURCE_MAC;
487 *p++ = len;
488 memcpy(p, mac, maclen);
489
490 return 0;
491 }
492
493 return 1;
494}
495
496time_t periodic_ra(time_t now)
497{
498 struct search_param param;
499 struct dhcp_context *context;
500 time_t next_event;
501 char interface[IF_NAMESIZE+1];
502
503 param.now = now;
29d28dda 504 param.iface = 0;
c5ad4e79
SK
505
506 while (1)
507 {
508 /* find overdue events, and time of first future event */
1f776932 509 for (next_event = 0, context = daemon->dhcp6; context; context = context->next)
c5ad4e79
SK
510 if (context->ra_time != 0)
511 {
7dbe9814 512 if (difftime(context->ra_time, now) <= 0.0)
c5ad4e79
SK
513 break; /* overdue */
514
7dbe9814
SK
515 if (next_event == 0 || difftime(next_event, context->ra_time) > 0.0)
516 next_event = context->ra_time;
c5ad4e79
SK
517 }
518
519 /* none overdue */
520 if (!context)
521 break;
522
523 /* There's a context overdue, but we can't find an interface
524 associated with it, because it's for a subnet we dont
525 have an interface on. Probably we're doing DHCP on
526 a remote subnet via a relay. Zero the timer, since we won't
527 ever be able to send ra's and satistfy it. */
528 if (iface_enumerate(AF_INET6, &param, iface_search))
529 context->ra_time = 0;
29d28dda
SK
530 else if (param.iface != 0 &&
531 indextoname(daemon->icmp6fd, param.iface, interface) &&
f7fe3627 532 iface_check(AF_LOCAL, NULL, interface, NULL))
421594f8
SK
533 {
534 struct iname *tmp;
535 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
49333cbd 536 if (tmp->name && wildcard_match(tmp->name, interface))
421594f8
SK
537 break;
538 if (!tmp)
1f776932 539 send_ra(now, param.iface, interface, NULL);
421594f8
SK
540 }
541 }
c5ad4e79
SK
542 return next_event;
543}
421594f8 544
c5ad4e79 545static int iface_search(struct in6_addr *local, int prefix,
bad7b875 546 int scope, int if_index, int flags,
1f776932 547 int preferred, int valid, void *vparam)
c5ad4e79
SK
548{
549 struct search_param *param = vparam;
741c2952 550 struct dhcp_context *context;
c5ad4e79
SK
551
552 (void)scope;
1f776932
SK
553 (void)preferred;
554 (void)valid;
c5ad4e79 555
1f776932 556 for (context = daemon->dhcp6; context; context = context->next)
6e3dba3f
SK
557 if (!(context->flags & CONTEXT_TEMPLATE) &&
558 prefix == context->prefix &&
c5ad4e79 559 is_same_net6(local, &context->start6, prefix) &&
6e3dba3f
SK
560 is_same_net6(local, &context->end6, prefix) &&
561 context->ra_time != 0 &&
562 difftime(context->ra_time, param->now) <= 0.0)
563 {
564 /* found an interface that's overdue for RA determine new
565 timeout value and arrange for RA to be sent unless interface is
566 still doing DAD.*/
567
bad7b875 568 if (!(flags & IFACE_TENTATIVE))
6e3dba3f
SK
569 param->iface = if_index;
570
1b75c1e6 571 if (difftime(param->now, context->ra_short_period_start) < 60.0)
6e3dba3f
SK
572 /* range 5 - 20 */
573 context->ra_time = param->now + 5 + (rand16()/4400);
574 else
55b548ae
SK
575 /* range 3/4 - 1 times RA_INTERVAL */
576 context->ra_time = param->now + (3 * RA_INTERVAL)/4 + ((RA_INTERVAL * (unsigned int)rand16()) >> 18);
6e3dba3f
SK
577
578 /* zero timers for other contexts on the same subnet, so they don't timeout
579 independently */
580 for (context = context->next; context; context = context->next)
581 if (prefix == context->prefix &&
582 is_same_net6(local, &context->start6, prefix) &&
583 is_same_net6(local, &context->end6, prefix))
584 context->ra_time = 0;
585
586 return 0; /* found, abort */
587 }
c5ad4e79
SK
588
589 return 1; /* keep searching */
590}
591
801ca9a7 592
c5ad4e79 593#endif