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