]> git.ipfire.org Git - people/ms/dnsmasq.git/blame - src/radv.c
indentation fix.
[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 34 struct in6_addr link_local, link_global;
c4cd95df 35 unsigned int pref_time, adv_interval;
c5ad4e79
SK
36};
37
38struct search_param {
39 time_t now; int iface;
c4cd95df 40 char name[IF_NAMESIZE+1];
c5ad4e79
SK
41};
42
1f776932 43static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *dest);
c5ad4e79 44static int add_prefixes(struct in6_addr *local, int prefix,
bad7b875 45 int scope, int if_index, int flags,
55b42f6d 46 unsigned int preferred, unsigned int valid, void *vparam);
c5ad4e79 47static int iface_search(struct in6_addr *local, int prefix,
bad7b875 48 int scope, int if_index, int flags,
1f776932 49 int prefered, int valid, void *vparam);
c5ad4e79 50static int add_lla(int index, unsigned int type, char *mac, size_t maclen, void *parm);
c4cd95df
SK
51static void new_timeout(struct dhcp_context *context, char *iface_name, time_t now);
52static unsigned int calc_lifetime(struct ra_interface *ra);
53static unsigned int calc_interval(struct ra_interface *ra);
54static unsigned int calc_prio(struct ra_interface *ra);
55static struct ra_interface *find_iface_param(char *iface);
c5ad4e79 56
c5379c1a 57static int hop_limit;
c5ad4e79
SK
58
59void ra_init(time_t now)
60{
c5ad4e79
SK
61 struct icmp6_filter filter;
62 int fd;
0e88d53f 63#if defined(IPV6_TCLASS) && defined(IPTOS_CLASS_CS6)
c5ad4e79
SK
64 int class = IPTOS_CLASS_CS6;
65#endif
66 int val = 255; /* radvd uses this value */
7b6dd880 67 socklen_t len = sizeof(int);
353ae4d2
SK
68 struct dhcp_context *context;
69
843c96b4
SK
70 /* ensure this is around even if we're not doing DHCPv6 */
71 expand_buf(&daemon->outpacket, sizeof(struct dhcp_packet));
353ae4d2
SK
72
73 /* See if we're guessing SLAAC addresses, if so we need to recieve ping replies */
1f776932 74 for (context = daemon->dhcp6; context; context = context->next)
353ae4d2
SK
75 if ((context->flags & CONTEXT_RA_NAME))
76 break;
77
89500e31
SK
78 /* Need ICMP6 socket for transmission for DHCPv6 even when not doing RA. */
79
c5ad4e79 80 ICMP6_FILTER_SETBLOCKALL(&filter);
89500e31
SK
81 if (daemon->doing_ra)
82 {
83 ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filter);
84 if (context)
85 ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filter);
86 }
c5ad4e79
SK
87
88 if ((fd = socket(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) == -1 ||
c5379c1a 89 getsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hop_limit, &len) ||
0e88d53f 90#if defined(IPV6_TCLASS) && defined(IPTOS_CLASS_CS6)
c5ad4e79
SK
91 setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &class, sizeof(class)) == -1 ||
92#endif
93 !fix_fd(fd) ||
94 !set_ipv6pktinfo(fd) ||
95 setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &val, sizeof(val)) ||
96 setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, sizeof(val)) ||
97 setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof(filter)) == -1)
98 die (_("cannot create ICMPv6 socket: %s"), NULL, EC_BADNET);
99
100 daemon->icmp6fd = fd;
101
89500e31
SK
102 if (daemon->doing_ra)
103 ra_start_unsolicted(now, NULL);
c5ad4e79
SK
104}
105
353ae4d2 106void ra_start_unsolicted(time_t now, struct dhcp_context *context)
c5ad4e79 107{
353ae4d2 108 /* init timers so that we do ra's for some/all soon. some ra_times will end up zeroed
c5ad4e79
SK
109 if it's not appropriate to advertise those contexts.
110 This gets re-called on a netlink route-change to re-do the advertisement
111 and pick up new interfaces */
6e3dba3f 112
353ae4d2 113 if (context)
1b75c1e6 114 context->ra_short_period_start = context->ra_time = now;
353ae4d2 115 else
1f776932 116 for (context = daemon->dhcp6; context; context = context->next)
6e3dba3f 117 if (!(context->flags & CONTEXT_TEMPLATE))
1b75c1e6
SK
118 {
119 context->ra_time = now + (rand16()/13000); /* range 0 - 5 */
120 /* re-do frequently for a minute or so, in case the first gets lost. */
121 context->ra_short_period_start = now;
122 }
c5ad4e79
SK
123}
124
1f776932 125void icmp6_packet(time_t now)
c5ad4e79
SK
126{
127 char interface[IF_NAMESIZE+1];
128 ssize_t sz;
129 int if_index = 0;
130 struct cmsghdr *cmptr;
131 struct msghdr msg;
132 union {
133 struct cmsghdr align; /* this ensures alignment */
134 char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
135 } control_u;
136 struct sockaddr_in6 from;
5ef33279 137 unsigned char *packet;
c5ad4e79 138 struct iname *tmp;
c5ad4e79
SK
139
140 /* Note: use outpacket for input buffer */
141 msg.msg_control = control_u.control6;
142 msg.msg_controllen = sizeof(control_u);
143 msg.msg_flags = 0;
144 msg.msg_name = &from;
145 msg.msg_namelen = sizeof(from);
146 msg.msg_iov = &daemon->outpacket;
147 msg.msg_iovlen = 1;
148
149 if ((sz = recv_dhcp_packet(daemon->icmp6fd, &msg)) == -1 || sz < 8)
150 return;
5ef33279
SK
151
152 packet = (unsigned char *)daemon->outpacket.iov_base;
c5ad4e79
SK
153
154 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
155 if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
156 {
157 union {
158 unsigned char *c;
159 struct in6_pktinfo *p;
160 } p;
161 p.c = CMSG_DATA(cmptr);
162
163 if_index = p.p->ipi6_ifindex;
164 }
165
166 if (!indextoname(daemon->icmp6fd, if_index, interface))
167 return;
168
4f7b304f 169 if (!iface_check(AF_LOCAL, NULL, interface, NULL))
c5ad4e79
SK
170 return;
171
172 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
49333cbd 173 if (tmp->name && wildcard_match(tmp->name, interface))
c5ad4e79
SK
174 return;
175
8bc4cece 176 if (packet[1] != 0)
353ae4d2 177 return;
8bc4cece 178
5ef33279
SK
179 if (packet[0] == ICMP6_ECHO_REPLY)
180 lease_ping_reply(&from.sin6_addr, packet, interface);
181 else if (packet[0] == ND_ROUTER_SOLICIT)
c5ad4e79 182 {
5ef33279
SK
183 char *mac = "";
184
185 /* look for link-layer address option for logging */
186 if (sz >= 16 && packet[8] == ICMP6_OPT_SOURCE_MAC && (packet[9] * 8) + 8 <= sz)
187 {
188 print_mac(daemon->namebuff, &packet[10], (packet[9] * 8) - 2);
189 mac = daemon->namebuff;
190 }
191
8c0b73d3
KDB
192 if (!option_bool(OPT_QUIET_RA))
193 my_syslog(MS_DHCP | LOG_INFO, "RTR-SOLICIT(%s) %s", interface, mac);
f632e567 194 /* source address may not be valid in solicit request. */
1f776932 195 send_ra(now, if_index, interface, !IN6_IS_ADDR_UNSPECIFIED(&from.sin6_addr) ? &from.sin6_addr : NULL);
c5ad4e79 196 }
c5ad4e79
SK
197}
198
1f776932 199static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *dest)
c5ad4e79
SK
200{
201 struct ra_packet *ra;
202 struct ra_param parm;
c5ad4e79 203 struct sockaddr_in6 addr;
ef1a94ab 204 struct dhcp_context *context, *tmp, **up;
18f0fb05
SK
205 struct dhcp_netid iface_id;
206 struct dhcp_opt *opt_cfg;
c4cd95df 207 struct ra_interface *ra_param = find_iface_param(iface_name);
44522920 208 int done_dns = 0, old_prefix = 0;
3b43646a
SK
209#ifdef HAVE_LINUX_NETWORK
210 FILE *f;
211#endif
c4cd95df 212
c5ad4e79
SK
213 save_counter(0);
214 ra = expand(sizeof(struct ra_packet));
215
353ae4d2 216 ra->type = ND_ROUTER_ADVERT;
c5ad4e79 217 ra->code = 0;
c5379c1a 218 ra->hop_limit = hop_limit;
c4cd95df
SK
219 ra->flags = calc_prio(ra_param);
220 ra->lifetime = htons(calc_lifetime(ra_param));
c5ad4e79
SK
221 ra->reachable_time = 0;
222 ra->retrans_time = 0;
223
224 parm.ind = iface;
225 parm.managed = 0;
30cd9666 226 parm.other = 0;
c5ad4e79
SK
227 parm.found_context = 0;
228 parm.if_name = iface_name;
229 parm.first = 1;
1f776932 230 parm.now = now;
55b42f6d 231 parm.pref_time = 0;
c4cd95df 232 parm.adv_interval = calc_interval(ra_param);
1f776932 233
18f0fb05
SK
234 /* set tag with name == interface */
235 iface_id.net = iface_name;
236 iface_id.next = NULL;
237 parm.tags = &iface_id;
c5ad4e79 238
1f776932 239 for (context = daemon->dhcp6; context; context = context->next)
18f0fb05
SK
240 {
241 context->flags &= ~CONTEXT_RA_DONE;
242 context->netid.next = &context->netid;
243 }
244
ef1a94ab 245 if (!iface_enumerate(AF_INET6, &parm, add_prefixes))
c5ad4e79
SK
246 return;
247
ef1a94ab
SK
248 /* Look for constructed contexts associated with addresses which have gone,
249 and advertise them with preferred_time == 0 RFC 6204 4.3 L-13 */
250 for (up = &daemon->dhcp6, context = daemon->dhcp6; context; context = tmp)
251 {
252 tmp = context->next;
3b43646a 253
ef1a94ab
SK
254 if (context->if_index == iface && (context->flags & CONTEXT_OLD))
255 {
256 unsigned int old = difftime(now, context->address_lost_time);
257
258 if (old > context->saved_valid)
259 {
260 /* We've advertised this enough, time to go */
261 *up = context->next;
262 free(context);
263 }
264 else
265 {
266 struct prefix_opt *opt;
267 struct in6_addr local = context->start6;
268 int do_slaac = 0;
269
44522920 270 old_prefix = 1;
ef1a94ab
SK
271
272 /* zero net part of address */
273 setaddr6part(&local, addr6part(&local) & ~((context->prefix == 64) ? (u64)-1LL : (1LLU << (128 - context->prefix)) - 1LLU));
274
275 if ((context->flags &
276 (CONTEXT_RA_ONLY | CONTEXT_RA_NAME | CONTEXT_RA_STATELESS)))
277 do_slaac = 1;
278
279 if ((opt = expand(sizeof(struct prefix_opt))))
280 {
281 opt->type = ICMP6_OPT_PREFIX;
282 opt->len = 4;
283 opt->prefix_len = context->prefix;
284 /* autonomous only if we're not doing dhcp, always set "on-link" */
285 opt->flags = do_slaac ? 0xC0 : 0x80;
286 opt->valid_lifetime = htonl(context->saved_valid - old);
287 opt->preferred_lifetime = htonl(0);
288 opt->reserved = 0;
289 opt->prefix = local;
290
291 inet_ntop(AF_INET6, &local, daemon->addrbuff, ADDRSTRLEN);
8c0b73d3
KDB
292 if (!option_bool(OPT_QUIET_RA))
293 my_syslog(MS_DHCP | LOG_INFO, "RTR-ADVERT(%s) %s old prefix", iface_name, daemon->addrbuff);
ef1a94ab
SK
294 }
295
296 up = &context->next;
297 }
298 }
299 else
300 up = &context->next;
301 }
302
44522920
SK
303 /* If we're advertising only old prefixes, set router lifetime to zero. */
304 if (old_prefix && !parm.found_context)
305 ra->lifetime = htons(0);
306
307 /* No prefixes to advertise. */
308 if (!old_prefix && !parm.found_context)
309 return;
310
3b43646a
SK
311#ifdef HAVE_LINUX_NETWORK
312 /* Note that IPv6 MTU is not necessarilly the same as the IPv4 MTU
313 available from SIOCGIFMTU */
314 sprintf(daemon->namebuff, "/proc/sys/net/ipv6/conf/%s/mtu", iface_name);
315 if ((f = fopen(daemon->namebuff, "r")))
c5ad4e79 316 {
3b43646a
SK
317 if (fgets(daemon->namebuff, MAXDNAME, f))
318 {
319 put_opt6_char(ICMP6_OPT_MTU);
320 put_opt6_char(1);
321 put_opt6_short(0);
322 put_opt6_long(atoi(daemon->namebuff));
323 }
324 fclose(f);
c5ad4e79 325 }
3b43646a 326#endif
c5ad4e79
SK
327
328 iface_enumerate(AF_LOCAL, &iface, add_lla);
18f0fb05
SK
329
330 /* RDNSS, RFC 6106, use relevant DHCP6 options */
331 (void)option_filter(parm.tags, NULL, daemon->dhcp_opts6);
c5ad4e79 332
18f0fb05
SK
333 for (opt_cfg = daemon->dhcp_opts6; opt_cfg; opt_cfg = opt_cfg->next)
334 {
335 int i;
336
337 /* netids match and not encapsulated? */
338 if (!(opt_cfg->flags & DHOPT_TAGOK))
339 continue;
340
341 if (opt_cfg->opt == OPTION6_DNS_SERVER)
342 {
343 struct in6_addr *a = (struct in6_addr *)opt_cfg->val;
806cf787 344
18f0fb05 345 done_dns = 1;
806cf787
SK
346 if (opt_cfg->len == 0 || (IN6_IS_ADDR_UNSPECIFIED(a) && parm.pref_time != 0))
347 continue;
18f0fb05
SK
348
349 put_opt6_char(ICMP6_OPT_RDNSS);
350 put_opt6_char((opt_cfg->len/8) + 1);
351 put_opt6_short(0);
806cf787 352 put_opt6_long(parm.pref_time);
18f0fb05
SK
353 /* zero means "self" */
354 for (i = 0; i < opt_cfg->len; i += IN6ADDRSZ, a++)
355 if (IN6_IS_ADDR_UNSPECIFIED(a))
55b42f6d 356 put_opt6(&parm.link_global, IN6ADDRSZ);
18f0fb05
SK
357 else
358 put_opt6(a, IN6ADDRSZ);
359 }
360
361 if (opt_cfg->opt == OPTION6_DOMAIN_SEARCH && opt_cfg->len != 0)
362 {
363 int len = ((opt_cfg->len+7)/8);
364
365 put_opt6_char(ICMP6_OPT_DNSSL);
366 put_opt6_char(len + 1);
367 put_opt6_short(0);
806cf787 368 put_opt6_long(parm.pref_time);
18f0fb05
SK
369 put_opt6(opt_cfg->val, opt_cfg->len);
370
371 /* pad */
372 for (i = opt_cfg->len; i < len * 8; i++)
373 put_opt6_char(0);
374 }
375 }
376
806cf787 377 if (daemon->port == NAMESERVER_PORT && !done_dns && parm.pref_time != 0)
18f0fb05 378 {
ab915f83 379 /* default == us, as long as we are supplying DNS service. */
18f0fb05
SK
380 put_opt6_char(ICMP6_OPT_RDNSS);
381 put_opt6_char(3);
382 put_opt6_short(0);
806cf787
SK
383 put_opt6_long(parm.pref_time);
384 put_opt6(&parm.link_local, IN6ADDRSZ);
18f0fb05 385 }
c5ad4e79
SK
386
387 /* set managed bits unless we're providing only RA on this link */
388 if (parm.managed)
30cd9666
SK
389 ra->flags |= 0x80; /* M flag, managed, */
390 if (parm.other)
391 ra->flags |= 0x40; /* O flag, other */
392
c5ad4e79
SK
393 /* decide where we're sending */
394 memset(&addr, 0, sizeof(addr));
22d904db
SK
395#ifdef HAVE_SOCKADDR_SA_LEN
396 addr.sin6_len = sizeof(struct sockaddr_in6);
397#endif
c5ad4e79
SK
398 addr.sin6_family = AF_INET6;
399 addr.sin6_port = htons(IPPROTO_ICMPV6);
400 if (dest)
401 {
353ae4d2 402 addr.sin6_addr = *dest;
c5ad4e79
SK
403 if (IN6_IS_ADDR_LINKLOCAL(dest) ||
404 IN6_IS_ADDR_MC_LINKLOCAL(dest))
405 addr.sin6_scope_id = iface;
406 }
407 else
8f3194f7
SK
408 {
409 inet_pton(AF_INET6, ALL_NODES, &addr.sin6_addr);
410 setsockopt(daemon->icmp6fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &iface, sizeof(iface));
411 }
22d904db 412
8f3194f7
SK
413 while (sendto(daemon->icmp6fd, daemon->outpacket.iov_base, save_counter(0), 0,
414 (struct sockaddr *)&addr, sizeof(addr)) == -1 && retry_send());
c5ad4e79
SK
415
416}
417
418static int add_prefixes(struct in6_addr *local, int prefix,
bad7b875 419 int scope, int if_index, int flags,
55b42f6d 420 unsigned int preferred, unsigned int valid, void *vparam)
c5ad4e79 421{
c5ad4e79 422 struct ra_param *param = vparam;
c5ad4e79
SK
423
424 (void)scope; /* warning */
ed8b68ad 425
c5ad4e79
SK
426 if (if_index == param->ind)
427 {
428 if (IN6_IS_ADDR_LINKLOCAL(local))
429 param->link_local = *local;
430 else if (!IN6_IS_ADDR_LOOPBACK(local) &&
c5ad4e79
SK
431 !IN6_IS_ADDR_MULTICAST(local))
432 {
4c82efc5 433 int real_prefix = 0;
c8257540
SK
434 int do_slaac = 0;
435 int deprecate = 0;
3bc0d932 436 int constructed = 0;
c8257540 437 unsigned int time = 0xffffffff;
1e02a859
SK
438 struct dhcp_context *context;
439
1f776932 440 for (context = daemon->dhcp6; context; context = context->next)
ef1a94ab 441 if (!(context->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) &&
4c82efc5
VG
442 prefix <= context->prefix &&
443 is_same_net6(local, &context->start6, context->prefix) &&
444 is_same_net6(local, &context->end6, context->prefix))
c5ad4e79 445 {
ef1a94ab
SK
446 context->saved_valid = valid;
447
30cd9666
SK
448 if ((context->flags &
449 (CONTEXT_RA_ONLY | CONTEXT_RA_NAME | CONTEXT_RA_STATELESS)))
450 {
451 do_slaac = 1;
4723d49d 452 if (context->flags & CONTEXT_DHCP)
05e92e5a
SK
453 {
454 param->other = 1;
455 if (!(context->flags & CONTEXT_RA_STATELESS))
456 param->managed = 1;
457 }
30cd9666 458 }
884a6dfe 459 else
0010b474
SK
460 {
461 /* don't do RA for non-ra-only unless --enable-ra is set */
462 if (!option_bool(OPT_RA))
463 continue;
464 param->managed = 1;
30cd9666 465 param->other = 1;
0010b474 466 }
1f776932 467
1ecbaaa3 468 /* find floor time, don't reduce below 3 * RA interval. */
c8257540 469 if (time > context->lease_time)
7f035f58
SK
470 {
471 time = context->lease_time;
c4cd95df
SK
472 if (time < ((unsigned int)(3 * param->adv_interval)))
473 time = 3 * param->adv_interval;
7f035f58
SK
474 }
475
c8257540
SK
476 if (context->flags & CONTEXT_DEPRECATE)
477 deprecate = 1;
3bc0d932
SK
478
479 if (context->flags & CONTEXT_CONSTRUCTED)
480 constructed = 1;
481
c8257540 482
18f0fb05
SK
483 /* collect dhcp-range tags */
484 if (context->netid.next == &context->netid && context->netid.net)
485 {
486 context->netid.next = param->tags;
487 param->tags = &context->netid;
488 }
489
5ae34bf3
SK
490 /* subsequent prefixes on the same interface
491 and subsequent instances of this prefix don't need timers.
492 Be careful not to find the same prefix twice with different
493 addresses. */
1e02a859
SK
494 if (!(context->flags & CONTEXT_RA_DONE))
495 {
5ae34bf3
SK
496 if (!param->first)
497 context->ra_time = 0;
1e02a859 498 context->flags |= CONTEXT_RA_DONE;
4c82efc5 499 real_prefix = context->prefix;
1e02a859 500 }
5ae34bf3
SK
501
502 param->first = 0;
503 param->found_context = 1;
c5ad4e79 504 }
1f776932 505
ed8b68ad 506 /* configured time is ceiling */
3bc0d932 507 if (!constructed || valid > time)
ed8b68ad
SK
508 valid = time;
509
3bc0d932 510 if (flags & IFACE_DEPRECATED)
ed8b68ad 511 preferred = 0;
1e02a859 512
3bc0d932
SK
513 if (deprecate)
514 time = 0;
515
516 /* configured time is ceiling */
517 if (!constructed || preferred > time)
518 preferred = time;
519
55b42f6d
SK
520 if (preferred > param->pref_time)
521 {
522 param->pref_time = preferred;
523 param->link_global = *local;
524 }
525
4c82efc5 526 if (real_prefix != 0)
c8257540 527 {
1e02a859
SK
528 struct prefix_opt *opt;
529
530 if ((opt = expand(sizeof(struct prefix_opt))))
531 {
5ef33279 532 /* zero net part of address */
4c82efc5 533 setaddr6part(local, addr6part(local) & ~((real_prefix == 64) ? (u64)-1LL : (1LLU << (128 - real_prefix)) - 1LLU));
5ef33279 534
1e02a859
SK
535 opt->type = ICMP6_OPT_PREFIX;
536 opt->len = 4;
4c82efc5 537 opt->prefix_len = real_prefix;
fd05f127
SK
538 /* autonomous only if we're not doing dhcp, always set "on-link" */
539 opt->flags = do_slaac ? 0xC0 : 0x80;
ed8b68ad
SK
540 opt->valid_lifetime = htonl(valid);
541 opt->preferred_lifetime = htonl(preferred);
5ef33279 542 opt->reserved = 0;
1e02a859 543 opt->prefix = *local;
1e02a859 544
5ef33279 545 inet_ntop(AF_INET6, local, daemon->addrbuff, ADDRSTRLEN);
8c0b73d3
KDB
546 if (!option_bool(OPT_QUIET_RA))
547 my_syslog(MS_DHCP | LOG_INFO, "RTR-ADVERT(%s) %s", param->if_name, daemon->addrbuff);
1e02a859 548 }
ef1a94ab 549
c8257540 550 }
c5ad4e79
SK
551 }
552 }
553 return 1;
554}
555
556static int add_lla(int index, unsigned int type, char *mac, size_t maclen, void *parm)
557{
558 (void)type;
559
560 if (index == *((int *)parm))
561 {
562 /* size is in units of 8 octets and includes type and length (2 bytes)
563 add 7 to round up */
564 int len = (maclen + 9) >> 3;
565 unsigned char *p = expand(len << 3);
566 memset(p, 0, len << 3);
567 *p++ = ICMP6_OPT_SOURCE_MAC;
568 *p++ = len;
569 memcpy(p, mac, maclen);
570
571 return 0;
572 }
573
574 return 1;
575}
576
577time_t periodic_ra(time_t now)
578{
579 struct search_param param;
580 struct dhcp_context *context;
581 time_t next_event;
c4cd95df 582
c5ad4e79 583 param.now = now;
29d28dda 584 param.iface = 0;
c5ad4e79
SK
585
586 while (1)
587 {
588 /* find overdue events, and time of first future event */
1f776932 589 for (next_event = 0, context = daemon->dhcp6; context; context = context->next)
c5ad4e79
SK
590 if (context->ra_time != 0)
591 {
7dbe9814 592 if (difftime(context->ra_time, now) <= 0.0)
c5ad4e79
SK
593 break; /* overdue */
594
7dbe9814
SK
595 if (next_event == 0 || difftime(next_event, context->ra_time) > 0.0)
596 next_event = context->ra_time;
c5ad4e79
SK
597 }
598
599 /* none overdue */
600 if (!context)
601 break;
602
c4cd95df
SK
603 if ((context->flags & CONTEXT_OLD) &&
604 context->if_index != 0 &&
dd9d9ce5 605 indextoname(daemon->icmp6fd, context->if_index, param.name))
ef1a94ab
SK
606 {
607 /* A context for an old address. We'll not find the interface by
c4cd95df
SK
608 looking for addresses, but we know it anyway, since the context is
609 constructed */
ef1a94ab 610 param.iface = context->if_index;
c4cd95df 611 new_timeout(context, param.name, now);
ef1a94ab
SK
612 }
613 else if (iface_enumerate(AF_INET6, &param, iface_search))
614 /* There's a context overdue, but we can't find an interface
615 associated with it, because it's for a subnet we dont
616 have an interface on. Probably we're doing DHCP on
617 a remote subnet via a relay. Zero the timer, since we won't
618 ever be able to send ra's and satistfy it. */
c5ad4e79 619 context->ra_time = 0;
ef1a94ab
SK
620
621 if (param.iface != 0 &&
c4cd95df 622 iface_check(AF_LOCAL, NULL, param.name, NULL))
421594f8
SK
623 {
624 struct iname *tmp;
625 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
c4cd95df 626 if (tmp->name && wildcard_match(tmp->name, param.name))
421594f8
SK
627 break;
628 if (!tmp)
c4cd95df 629 send_ra(now, param.iface, param.name, NULL);
421594f8
SK
630 }
631 }
c5ad4e79
SK
632 return next_event;
633}
421594f8 634
c5ad4e79 635static int iface_search(struct in6_addr *local, int prefix,
bad7b875 636 int scope, int if_index, int flags,
1f776932 637 int preferred, int valid, void *vparam)
c5ad4e79
SK
638{
639 struct search_param *param = vparam;
741c2952 640 struct dhcp_context *context;
c5ad4e79
SK
641
642 (void)scope;
1f776932
SK
643 (void)preferred;
644 (void)valid;
c5ad4e79 645
1f776932 646 for (context = daemon->dhcp6; context; context = context->next)
ef1a94ab 647 if (!(context->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) &&
4c82efc5
VG
648 prefix <= context->prefix &&
649 is_same_net6(local, &context->start6, context->prefix) &&
650 is_same_net6(local, &context->end6, context->prefix) &&
6e3dba3f
SK
651 context->ra_time != 0 &&
652 difftime(context->ra_time, param->now) <= 0.0)
653 {
654 /* found an interface that's overdue for RA determine new
655 timeout value and arrange for RA to be sent unless interface is
656 still doing DAD.*/
657
bad7b875 658 if (!(flags & IFACE_TENTATIVE))
6e3dba3f
SK
659 param->iface = if_index;
660
c4cd95df
SK
661 /* should never fail */
662 if (!indextoname(daemon->icmp6fd, if_index, param->name))
663 {
664 param->iface = 0;
665 return 0;
666 }
667
668 new_timeout(context, param->name, param->now);
6e3dba3f
SK
669
670 /* zero timers for other contexts on the same subnet, so they don't timeout
671 independently */
672 for (context = context->next; context; context = context->next)
4c82efc5
VG
673 if (prefix <= context->prefix &&
674 is_same_net6(local, &context->start6, context->prefix) &&
675 is_same_net6(local, &context->end6, context->prefix))
6e3dba3f
SK
676 context->ra_time = 0;
677
678 return 0; /* found, abort */
679 }
c5ad4e79
SK
680
681 return 1; /* keep searching */
682}
ef1a94ab 683
c4cd95df 684static void new_timeout(struct dhcp_context *context, char *iface_name, time_t now)
ef1a94ab 685{
c4cd95df 686 if (difftime(now, context->ra_short_period_start) < 60.0)
ef1a94ab
SK
687 /* range 5 - 20 */
688 context->ra_time = now + 5 + (rand16()/4400);
689 else
c4cd95df
SK
690 {
691 /* range 3/4 - 1 times MaxRtrAdvInterval */
692 unsigned int adv_interval = calc_interval(find_iface_param(iface_name));
693 context->ra_time = now + (3 * adv_interval)/4 + ((adv_interval * (unsigned int)rand16()) >> 18);
694 }
695}
696
697static struct ra_interface *find_iface_param(char *iface)
698{
699 struct ra_interface *ra;
700
701 for (ra = daemon->ra_interfaces; ra; ra = ra->next)
702 if (wildcard_match(ra->name, iface))
703 return ra;
704
705 return NULL;
706}
707
708static unsigned int calc_interval(struct ra_interface *ra)
709{
710 int interval = 600;
711
712 if (ra && ra->interval != 0)
713 {
714 interval = ra->interval;
715 if (interval > 1800)
716 interval = 1800;
717 else if (interval < 4)
718 interval = 4;
719 }
720
721 return (unsigned int)interval;
722}
723
724static unsigned int calc_lifetime(struct ra_interface *ra)
725{
726 int lifetime, interval = (int)calc_interval(ra);
727
728 if (!ra || ra->lifetime == -1) /* not specified */
729 lifetime = 3 * interval;
730 else
731 {
732 lifetime = ra->lifetime;
733 if (lifetime < interval && lifetime != 0)
734 lifetime = interval;
735 else if (lifetime > 9000)
736 lifetime = 9000;
737 }
738
739 return (unsigned int)lifetime;
740}
741
742static unsigned int calc_prio(struct ra_interface *ra)
743{
744 if (ra)
745 return ra->prio;
746
747 return 0;
ef1a94ab 748}
801ca9a7 749
c5ad4e79 750#endif