]> git.ipfire.org Git - people/ms/dnsmasq.git/blame - src/radv.c
code-size tweak
[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 */
161 for (context = daemon->dhcp6; context; context = context->next)
162 if (!context->interface || strcmp(context->interface, interface) == 0)
163 break;
164
5ef33279 165 if (!context || packet[1] != 0)
353ae4d2
SK
166 return;
167
5ef33279
SK
168 if (packet[0] == ICMP6_ECHO_REPLY)
169 lease_ping_reply(&from.sin6_addr, packet, interface);
170 else if (packet[0] == ND_ROUTER_SOLICIT)
c5ad4e79 171 {
5ef33279
SK
172 char *mac = "";
173
174 /* look for link-layer address option for logging */
175 if (sz >= 16 && packet[8] == ICMP6_OPT_SOURCE_MAC && (packet[9] * 8) + 8 <= sz)
176 {
177 print_mac(daemon->namebuff, &packet[10], (packet[9] * 8) - 2);
178 mac = daemon->namebuff;
179 }
180
181 my_syslog(MS_DHCP | LOG_INFO, "RTR-SOLICIT(%s) %s", interface, mac);
182 send_ra(if_index, interface, &from.sin6_addr);
c5ad4e79 183 }
c5ad4e79
SK
184}
185
186static void send_ra(int iface, char *iface_name, struct in6_addr *dest)
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;
196
c5ad4e79
SK
197 save_counter(0);
198 ra = expand(sizeof(struct ra_packet));
199
353ae4d2 200 ra->type = ND_ROUTER_ADVERT;
c5ad4e79 201 ra->code = 0;
c5379c1a 202 ra->hop_limit = hop_limit;
884a6dfe 203 ra->flags = 0x00;
c5ad4e79
SK
204 ra->lifetime = htons(1800); /* AdvDefaultLifetime*/
205 ra->reachable_time = 0;
206 ra->retrans_time = 0;
207
208 parm.ind = iface;
209 parm.managed = 0;
30cd9666 210 parm.other = 0;
c5ad4e79
SK
211 parm.found_context = 0;
212 parm.if_name = iface_name;
213 parm.first = 1;
214
18f0fb05
SK
215 /* set tag with name == interface */
216 iface_id.net = iface_name;
217 iface_id.next = NULL;
218 parm.tags = &iface_id;
c5ad4e79 219
18f0fb05
SK
220 for (context = daemon->ra_contexts; context; context = context->next)
221 {
222 context->flags &= ~CONTEXT_RA_DONE;
223 context->netid.next = &context->netid;
224 }
225
c5ad4e79
SK
226 if (!iface_enumerate(AF_INET6, &parm, add_prefixes) ||
227 !parm.found_context)
228 return;
229
230 strncpy(ifr.ifr_name, iface_name, IF_NAMESIZE);
231
232 if (ioctl(daemon->icmp6fd, SIOCGIFMTU, &ifr) != -1)
233 {
234 put_opt6_char(ICMP6_OPT_MTU);
235 put_opt6_char(1);
236 put_opt6_short(0);
237 put_opt6_long(ifr.ifr_mtu);
238 }
239
240 iface_enumerate(AF_LOCAL, &iface, add_lla);
18f0fb05
SK
241
242 /* RDNSS, RFC 6106, use relevant DHCP6 options */
243 (void)option_filter(parm.tags, NULL, daemon->dhcp_opts6);
c5ad4e79 244
18f0fb05
SK
245 for (opt_cfg = daemon->dhcp_opts6; opt_cfg; opt_cfg = opt_cfg->next)
246 {
247 int i;
248
249 /* netids match and not encapsulated? */
250 if (!(opt_cfg->flags & DHOPT_TAGOK))
251 continue;
252
253 if (opt_cfg->opt == OPTION6_DNS_SERVER)
254 {
255 struct in6_addr *a = (struct in6_addr *)opt_cfg->val;
c5ad4e79 256
18f0fb05
SK
257 done_dns = 1;
258 if (opt_cfg->len == 0)
259 continue;
260
261 put_opt6_char(ICMP6_OPT_RDNSS);
262 put_opt6_char((opt_cfg->len/8) + 1);
263 put_opt6_short(0);
264 put_opt6_long(1800); /* lifetime - twice RA retransmit */
265 /* zero means "self" */
266 for (i = 0; i < opt_cfg->len; i += IN6ADDRSZ, a++)
267 if (IN6_IS_ADDR_UNSPECIFIED(a))
268 put_opt6(&parm.link_local, IN6ADDRSZ);
269 else
270 put_opt6(a, IN6ADDRSZ);
271 }
272
273 if (opt_cfg->opt == OPTION6_DOMAIN_SEARCH && opt_cfg->len != 0)
274 {
275 int len = ((opt_cfg->len+7)/8);
276
277 put_opt6_char(ICMP6_OPT_DNSSL);
278 put_opt6_char(len + 1);
279 put_opt6_short(0);
280 put_opt6_long(1800); /* lifetime - twice RA retransmit */
281 put_opt6(opt_cfg->val, opt_cfg->len);
282
283 /* pad */
284 for (i = opt_cfg->len; i < len * 8; i++)
285 put_opt6_char(0);
286 }
287 }
288
289 if (!done_dns)
290 {
291 /* default == us. */
292 put_opt6_char(ICMP6_OPT_RDNSS);
293 put_opt6_char(3);
294 put_opt6_short(0);
295 put_opt6_long(1800); /* lifetime - twice RA retransmit */
296 put_opt6(&parm.link_local, IN6ADDRSZ);
297 }
c5ad4e79
SK
298
299 /* set managed bits unless we're providing only RA on this link */
300 if (parm.managed)
30cd9666
SK
301 ra->flags |= 0x80; /* M flag, managed, */
302 if (parm.other)
303 ra->flags |= 0x40; /* O flag, other */
304
c5ad4e79
SK
305 /* decide where we're sending */
306 memset(&addr, 0, sizeof(addr));
22d904db
SK
307#ifdef HAVE_SOCKADDR_SA_LEN
308 addr.sin6_len = sizeof(struct sockaddr_in6);
309#endif
c5ad4e79
SK
310 addr.sin6_family = AF_INET6;
311 addr.sin6_port = htons(IPPROTO_ICMPV6);
312 if (dest)
313 {
353ae4d2 314 addr.sin6_addr = *dest;
c5ad4e79
SK
315 if (IN6_IS_ADDR_LINKLOCAL(dest) ||
316 IN6_IS_ADDR_MC_LINKLOCAL(dest))
317 addr.sin6_scope_id = iface;
318 }
319 else
22d904db
SK
320 inet_pton(AF_INET6, ALL_HOSTS, &addr.sin6_addr);
321
c5ad4e79 322 send_from(daemon->icmp6fd, 0, daemon->outpacket.iov_base, save_counter(0),
50303b19 323 (union mysockaddr *)&addr, (struct all_addr *)&parm.link_local, iface);
c5ad4e79
SK
324
325}
326
327static int add_prefixes(struct in6_addr *local, int prefix,
328 int scope, int if_index, int dad, void *vparam)
329{
c5ad4e79 330 struct ra_param *param = vparam;
c5ad4e79
SK
331
332 (void)scope; /* warning */
333 (void)dad;
334
335 if (if_index == param->ind)
336 {
337 if (IN6_IS_ADDR_LINKLOCAL(local))
338 param->link_local = *local;
339 else if (!IN6_IS_ADDR_LOOPBACK(local) &&
340 !IN6_IS_ADDR_LINKLOCAL(local) &&
341 !IN6_IS_ADDR_MULTICAST(local))
342 {
1e02a859 343 int do_prefix = 0;
c8257540
SK
344 int do_slaac = 0;
345 int deprecate = 0;
346 unsigned int time = 0xffffffff;
1e02a859
SK
347 struct dhcp_context *context;
348
c5ad4e79
SK
349 for (context = daemon->ra_contexts; context; context = context->next)
350 if (prefix == context->prefix &&
351 is_same_net6(local, &context->start6, prefix) &&
352 is_same_net6(local, &context->end6, prefix))
353 {
30cd9666
SK
354 if ((context->flags &
355 (CONTEXT_RA_ONLY | CONTEXT_RA_NAME | CONTEXT_RA_STATELESS)))
356 {
357 do_slaac = 1;
4723d49d 358 if (context->flags & CONTEXT_DHCP)
05e92e5a
SK
359 {
360 param->other = 1;
361 if (!(context->flags & CONTEXT_RA_STATELESS))
362 param->managed = 1;
363 }
30cd9666 364 }
884a6dfe 365 else
0010b474
SK
366 {
367 /* don't do RA for non-ra-only unless --enable-ra is set */
368 if (!option_bool(OPT_RA))
369 continue;
370 param->managed = 1;
30cd9666 371 param->other = 1;
0010b474
SK
372 }
373
c8257540
SK
374 /* find floor time */
375 if (time > context->lease_time)
376 time = context->lease_time;
c5ad4e79 377
c8257540
SK
378 if (context->flags & CONTEXT_DEPRECATE)
379 deprecate = 1;
380
381 /* subsequent prefixes on the same interface
382 and subsequent instances of this prefix don't need timers */
c5ad4e79
SK
383 if (!param->first)
384 context->ra_time = 0;
385 param->first = 0;
386 param->found_context = 1;
18f0fb05
SK
387
388 /* collect dhcp-range tags */
389 if (context->netid.next == &context->netid && context->netid.net)
390 {
391 context->netid.next = param->tags;
392 param->tags = &context->netid;
393 }
394
1e02a859
SK
395 if (!(context->flags & CONTEXT_RA_DONE))
396 {
397 context->flags |= CONTEXT_RA_DONE;
398 do_prefix = 1;
399 }
c5ad4e79 400 }
1e02a859
SK
401
402 if (do_prefix)
c8257540 403 {
1e02a859
SK
404 struct prefix_opt *opt;
405
406 if ((opt = expand(sizeof(struct prefix_opt))))
407 {
5ef33279
SK
408 /* zero net part of address */
409 setaddr6part(local, addr6part(local) & ~((prefix == 64) ? (u64)-1LL : (1LLU << (128 - prefix)) - 1LLU));
410
1e02a859
SK
411 /* lifetimes must be min 2 hrs, by RFC 2462 */
412 if (time < 7200)
413 time = 7200;
414
415 opt->type = ICMP6_OPT_PREFIX;
416 opt->len = 4;
417 opt->prefix_len = prefix;
5ef33279 418 /* autonomous only if we're not doing dhcp */
1e02a859
SK
419 opt->flags = do_slaac ? 0x40 : 0x00;
420 opt->valid_lifetime = htonl(time);
421 opt->preferred_lifetime = htonl(deprecate ? 0 : time);
5ef33279 422 opt->reserved = 0;
1e02a859 423 opt->prefix = *local;
1e02a859 424
5ef33279 425 inet_ntop(AF_INET6, local, daemon->addrbuff, ADDRSTRLEN);
1e02a859
SK
426 my_syslog(MS_DHCP | LOG_INFO, "RTR-ADVERT(%s) %s", param->if_name, daemon->addrbuff);
427 }
c8257540 428 }
c5ad4e79
SK
429 }
430 }
431 return 1;
432}
433
434static int add_lla(int index, unsigned int type, char *mac, size_t maclen, void *parm)
435{
436 (void)type;
437
438 if (index == *((int *)parm))
439 {
440 /* size is in units of 8 octets and includes type and length (2 bytes)
441 add 7 to round up */
442 int len = (maclen + 9) >> 3;
443 unsigned char *p = expand(len << 3);
444 memset(p, 0, len << 3);
445 *p++ = ICMP6_OPT_SOURCE_MAC;
446 *p++ = len;
447 memcpy(p, mac, maclen);
448
449 return 0;
450 }
451
452 return 1;
453}
454
455time_t periodic_ra(time_t now)
456{
457 struct search_param param;
458 struct dhcp_context *context;
459 time_t next_event;
460 char interface[IF_NAMESIZE+1];
461
462 param.now = now;
463
464 while (1)
465 {
466 /* find overdue events, and time of first future event */
467 for (next_event = 0, context = daemon->ra_contexts; context; context = context->next)
468 if (context->ra_time != 0)
469 {
7dbe9814 470 if (difftime(context->ra_time, now) <= 0.0)
c5ad4e79
SK
471 break; /* overdue */
472
7dbe9814
SK
473 if (next_event == 0 || difftime(next_event, context->ra_time) > 0.0)
474 next_event = context->ra_time;
c5ad4e79
SK
475 }
476
477 /* none overdue */
478 if (!context)
479 break;
480
481 /* There's a context overdue, but we can't find an interface
482 associated with it, because it's for a subnet we dont
483 have an interface on. Probably we're doing DHCP on
484 a remote subnet via a relay. Zero the timer, since we won't
485 ever be able to send ra's and satistfy it. */
486 if (iface_enumerate(AF_INET6, &param, iface_search))
487 context->ra_time = 0;
488 else if (indextoname(daemon->icmp6fd, param.iface, interface))
489 send_ra(param.iface, interface, NULL);
490 }
491
492 return next_event;
493}
494
495static int iface_search(struct in6_addr *local, int prefix,
496 int scope, int if_index, int dad, void *vparam)
497{
498 struct search_param *param = vparam;
741c2952 499 struct dhcp_context *context;
c5ad4e79
SK
500
501 (void)scope;
502 (void)dad;
503
504 for (context = daemon->ra_contexts; context; context = context->next)
505 if (prefix == context->prefix &&
506 is_same_net6(local, &context->start6, prefix) &&
507 is_same_net6(local, &context->end6, prefix))
353ae4d2 508 if (context->ra_time != 0 && difftime(context->ra_time, param->now) <= 0.0)
c5ad4e79
SK
509 {
510 /* found an interface that's overdue for RA determine new
511 timeout value and zap other contexts on the same interface
512 so they don't timeout independently .*/
513 param->iface = if_index;
514
515 if (difftime(param->now, ra_short_period_start) < 60.0)
516 /* range 5 - 20 */
517 context->ra_time = param->now + 5 + (rand16()/4400);
518 else
519 /* range 450 - 600 */
520 context->ra_time = param->now + 450 + (rand16()/440);
521
522 return 0; /* found, abort */
523 }
524
525 return 1; /* keep searching */
526}
527
801ca9a7 528
c5ad4e79 529#endif