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