]>
git.ipfire.org Git - people/ms/dnsmasq.git/blob - src/radv.c
1 /* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
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.
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.
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/>.
18 /* NB. This code may be called during a DHCPv4 or transaction which is in ping-wait
19 It therefore cannot use any DHCP buffer resources except outpacket, which is
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 */
27 #include <netinet/icmp6.h>
30 int ind
, managed
, other
, found_context
, first
;
32 struct dhcp_netid
*tags
;
33 struct in6_addr link_local
;
37 time_t now
; int iface
;
40 static void send_ra(int iface
, char *iface_name
, struct in6_addr
*dest
);
41 static int add_prefixes(struct in6_addr
*local
, int prefix
,
42 int scope
, int if_index
, int dad
, void *vparam
);
43 static int iface_search(struct in6_addr
*local
, int prefix
,
44 int scope
, int if_index
, int dad
, void *vparam
);
45 static int add_lla(int index
, unsigned int type
, char *mac
, size_t maclen
, void *parm
);
48 static time_t ra_short_period_start
;
50 void ra_init(time_t now
)
52 struct icmp6_filter filter
;
54 #if defined(IPV6_TCLASS) && defined(IPTOS_CLASS_CS6)
55 int class = IPTOS_CLASS_CS6
;
57 int val
= 255; /* radvd uses this value */
58 socklen_t len
= sizeof(int);
59 struct dhcp_context
*context
;
61 /* ensure this is around even if we're not doing DHCPv6 */
62 expand_buf(&daemon
->outpacket
, sizeof(struct dhcp_packet
));
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
))
69 ICMP6_FILTER_SETBLOCKALL(&filter
);
70 ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT
, &filter
);
72 ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY
, &filter
);
74 if ((fd
= socket(PF_INET6
, SOCK_RAW
, IPPROTO_ICMPV6
)) == -1 ||
75 getsockopt(fd
, IPPROTO_IPV6
, IPV6_UNICAST_HOPS
, &hop_limit
, &len
) ||
76 #if defined(IPV6_TCLASS) && defined(IPTOS_CLASS_CS6)
77 setsockopt(fd
, IPPROTO_IPV6
, IPV6_TCLASS
, &class, sizeof(class)) == -1 ||
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
);
88 ra_start_unsolicted(now
, NULL
);
91 void ra_start_unsolicted(time_t now
, struct dhcp_context
*context
)
93 /* init timers so that we do ra's for some/all soon. some ra_times will end up zeroed
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 */
99 context
->ra_time
= now
;
101 for (context
= daemon
->ra_contexts
; context
; context
= context
->next
)
102 context
->ra_time
= now
+ (rand16()/13000); /* range 0 - 5 */
104 /* re-do frequently for a minute or so, in case the first gets lost. */
105 ra_short_period_start
= now
;
108 void icmp6_packet(void)
110 char interface
[IF_NAMESIZE
+1];
113 struct cmsghdr
*cmptr
;
116 struct cmsghdr align
; /* this ensures alignment */
117 char control6
[CMSG_SPACE(sizeof(struct in6_pktinfo
))];
119 struct sockaddr_in6 from
;
120 unsigned char *packet
;
123 /* Note: use outpacket for input buffer */
124 msg
.msg_control
= control_u
.control6
;
125 msg
.msg_controllen
= sizeof(control_u
);
127 msg
.msg_name
= &from
;
128 msg
.msg_namelen
= sizeof(from
);
129 msg
.msg_iov
= &daemon
->outpacket
;
132 if ((sz
= recv_dhcp_packet(daemon
->icmp6fd
, &msg
)) == -1 || sz
< 8)
135 packet
= (unsigned char *)daemon
->outpacket
.iov_base
;
137 for (cmptr
= CMSG_FIRSTHDR(&msg
); cmptr
; cmptr
= CMSG_NXTHDR(&msg
, cmptr
))
138 if (cmptr
->cmsg_level
== IPPROTO_IPV6
&& cmptr
->cmsg_type
== daemon
->v6pktinfo
)
142 struct in6_pktinfo
*p
;
144 p
.c
= CMSG_DATA(cmptr
);
146 if_index
= p
.p
->ipi6_ifindex
;
149 if (!indextoname(daemon
->icmp6fd
, if_index
, interface
))
152 if (!iface_check(AF_LOCAL
, NULL
, interface
))
155 for (tmp
= daemon
->dhcp_except
; tmp
; tmp
= tmp
->next
)
156 if (tmp
->name
&& (strcmp(tmp
->name
, interface
) == 0))
162 if (packet
[0] == ICMP6_ECHO_REPLY
)
163 lease_ping_reply(&from
.sin6_addr
, packet
, interface
);
164 else if (packet
[0] == ND_ROUTER_SOLICIT
)
168 /* look for link-layer address option for logging */
169 if (sz
>= 16 && packet
[8] == ICMP6_OPT_SOURCE_MAC
&& (packet
[9] * 8) + 8 <= sz
)
171 print_mac(daemon
->namebuff
, &packet
[10], (packet
[9] * 8) - 2);
172 mac
= daemon
->namebuff
;
175 my_syslog(MS_DHCP
| LOG_INFO
, "RTR-SOLICIT(%s) %s", interface
, mac
);
176 /* source address may not be valid in solicit request. */
177 send_ra(if_index
, interface
, !IN6_IS_ADDR_UNSPECIFIED(&from
.sin6_addr
) ? &from
.sin6_addr
: NULL
);
181 static void send_ra(int iface
, char *iface_name
, struct in6_addr
*dest
)
183 struct ra_packet
*ra
;
184 struct ra_param parm
;
186 struct sockaddr_in6 addr
;
187 struct dhcp_context
*context
;
188 struct dhcp_netid iface_id
;
189 struct dhcp_opt
*opt_cfg
;
193 ra
= expand(sizeof(struct ra_packet
));
195 ra
->type
= ND_ROUTER_ADVERT
;
197 ra
->hop_limit
= hop_limit
;
199 ra
->lifetime
= htons(1800); /* AdvDefaultLifetime*/
200 ra
->reachable_time
= 0;
201 ra
->retrans_time
= 0;
206 parm
.found_context
= 0;
207 parm
.if_name
= iface_name
;
210 /* set tag with name == interface */
211 iface_id
.net
= iface_name
;
212 iface_id
.next
= NULL
;
213 parm
.tags
= &iface_id
;
215 for (context
= daemon
->ra_contexts
; context
; context
= context
->next
)
217 context
->flags
&= ~CONTEXT_RA_DONE
;
218 context
->netid
.next
= &context
->netid
;
221 if (!iface_enumerate(AF_INET6
, &parm
, add_prefixes
) ||
225 strncpy(ifr
.ifr_name
, iface_name
, IF_NAMESIZE
);
227 if (ioctl(daemon
->icmp6fd
, SIOCGIFMTU
, &ifr
) != -1)
229 put_opt6_char(ICMP6_OPT_MTU
);
232 put_opt6_long(ifr
.ifr_mtu
);
235 iface_enumerate(AF_LOCAL
, &iface
, add_lla
);
237 /* RDNSS, RFC 6106, use relevant DHCP6 options */
238 (void)option_filter(parm
.tags
, NULL
, daemon
->dhcp_opts6
);
240 for (opt_cfg
= daemon
->dhcp_opts6
; opt_cfg
; opt_cfg
= opt_cfg
->next
)
244 /* netids match and not encapsulated? */
245 if (!(opt_cfg
->flags
& DHOPT_TAGOK
))
248 if (opt_cfg
->opt
== OPTION6_DNS_SERVER
)
250 struct in6_addr
*a
= (struct in6_addr
*)opt_cfg
->val
;
253 if (opt_cfg
->len
== 0)
256 put_opt6_char(ICMP6_OPT_RDNSS
);
257 put_opt6_char((opt_cfg
->len
/8) + 1);
259 put_opt6_long(1800); /* lifetime - twice RA retransmit */
260 /* zero means "self" */
261 for (i
= 0; i
< opt_cfg
->len
; i
+= IN6ADDRSZ
, a
++)
262 if (IN6_IS_ADDR_UNSPECIFIED(a
))
263 put_opt6(&parm
.link_local
, IN6ADDRSZ
);
265 put_opt6(a
, IN6ADDRSZ
);
268 if (opt_cfg
->opt
== OPTION6_DOMAIN_SEARCH
&& opt_cfg
->len
!= 0)
270 int len
= ((opt_cfg
->len
+7)/8);
272 put_opt6_char(ICMP6_OPT_DNSSL
);
273 put_opt6_char(len
+ 1);
275 put_opt6_long(1800); /* lifetime - twice RA retransmit */
276 put_opt6(opt_cfg
->val
, opt_cfg
->len
);
279 for (i
= opt_cfg
->len
; i
< len
* 8; i
++)
287 put_opt6_char(ICMP6_OPT_RDNSS
);
290 put_opt6_long(1800); /* lifetime - twice RA retransmit */
291 put_opt6(&parm
.link_local
, IN6ADDRSZ
);
294 /* set managed bits unless we're providing only RA on this link */
296 ra
->flags
|= 0x80; /* M flag, managed, */
298 ra
->flags
|= 0x40; /* O flag, other */
300 /* decide where we're sending */
301 memset(&addr
, 0, sizeof(addr
));
302 #ifdef HAVE_SOCKADDR_SA_LEN
303 addr
.sin6_len
= sizeof(struct sockaddr_in6
);
305 addr
.sin6_family
= AF_INET6
;
306 addr
.sin6_port
= htons(IPPROTO_ICMPV6
);
309 addr
.sin6_addr
= *dest
;
310 if (IN6_IS_ADDR_LINKLOCAL(dest
) ||
311 IN6_IS_ADDR_MC_LINKLOCAL(dest
))
312 addr
.sin6_scope_id
= iface
;
315 inet_pton(AF_INET6
, ALL_NODES
, &addr
.sin6_addr
);
317 send_from(daemon
->icmp6fd
, 0, daemon
->outpacket
.iov_base
, save_counter(0),
318 (union mysockaddr
*)&addr
, (struct all_addr
*)&parm
.link_local
, iface
);
322 static int add_prefixes(struct in6_addr
*local
, int prefix
,
323 int scope
, int if_index
, int dad
, void *vparam
)
325 struct ra_param
*param
= vparam
;
327 (void)scope
; /* warning */
330 if (if_index
== param
->ind
)
332 if (IN6_IS_ADDR_LINKLOCAL(local
))
333 param
->link_local
= *local
;
334 else if (!IN6_IS_ADDR_LOOPBACK(local
) &&
335 !IN6_IS_ADDR_LINKLOCAL(local
) &&
336 !IN6_IS_ADDR_MULTICAST(local
))
341 unsigned int time
= 0xffffffff;
342 struct dhcp_context
*context
;
344 for (context
= daemon
->ra_contexts
; context
; context
= context
->next
)
345 if (prefix
== context
->prefix
&&
346 is_same_net6(local
, &context
->start6
, prefix
) &&
347 is_same_net6(local
, &context
->end6
, prefix
))
349 if ((context
->flags
&
350 (CONTEXT_RA_ONLY
| CONTEXT_RA_NAME
| CONTEXT_RA_STATELESS
)))
353 if (context
->flags
& CONTEXT_DHCP
)
356 if (!(context
->flags
& CONTEXT_RA_STATELESS
))
362 /* don't do RA for non-ra-only unless --enable-ra is set */
363 if (!option_bool(OPT_RA
))
369 /* find floor time */
370 if (time
> context
->lease_time
)
371 time
= context
->lease_time
;
373 if (context
->flags
& CONTEXT_DEPRECATE
)
376 /* collect dhcp-range tags */
377 if (context
->netid
.next
== &context
->netid
&& context
->netid
.net
)
379 context
->netid
.next
= param
->tags
;
380 param
->tags
= &context
->netid
;
383 /* subsequent prefixes on the same interface
384 and subsequent instances of this prefix don't need timers.
385 Be careful not to find the same prefix twice with different
387 if (!(context
->flags
& CONTEXT_RA_DONE
))
390 context
->ra_time
= 0;
391 context
->flags
|= CONTEXT_RA_DONE
;
396 param
->found_context
= 1;
401 struct prefix_opt
*opt
;
403 if ((opt
= expand(sizeof(struct prefix_opt
))))
405 /* zero net part of address */
406 setaddr6part(local
, addr6part(local
) & ~((prefix
== 64) ? (u64
)-1LL : (1LLU << (128 - prefix
)) - 1LLU));
408 /* lifetimes must be min 2 hrs, by RFC 2462 */
412 opt
->type
= ICMP6_OPT_PREFIX
;
414 opt
->prefix_len
= prefix
;
415 /* autonomous only if we're not doing dhcp, always set "on-link" */
416 opt
->flags
= do_slaac
? 0xC0 : 0x80;
417 opt
->valid_lifetime
= htonl(time
);
418 opt
->preferred_lifetime
= htonl(deprecate
? 0 : time
);
420 opt
->prefix
= *local
;
422 inet_ntop(AF_INET6
, local
, daemon
->addrbuff
, ADDRSTRLEN
);
423 my_syslog(MS_DHCP
| LOG_INFO
, "RTR-ADVERT(%s) %s", param
->if_name
, daemon
->addrbuff
);
431 static int add_lla(int index
, unsigned int type
, char *mac
, size_t maclen
, void *parm
)
435 if (index
== *((int *)parm
))
437 /* size is in units of 8 octets and includes type and length (2 bytes)
439 int len
= (maclen
+ 9) >> 3;
440 unsigned char *p
= expand(len
<< 3);
441 memset(p
, 0, len
<< 3);
442 *p
++ = ICMP6_OPT_SOURCE_MAC
;
444 memcpy(p
, mac
, maclen
);
452 time_t periodic_ra(time_t now
)
454 struct search_param param
;
455 struct dhcp_context
*context
;
457 char interface
[IF_NAMESIZE
+1];
464 /* find overdue events, and time of first future event */
465 for (next_event
= 0, context
= daemon
->ra_contexts
; context
; context
= context
->next
)
466 if (context
->ra_time
!= 0)
468 if (difftime(context
->ra_time
, now
) <= 0.0)
471 if (next_event
== 0 || difftime(next_event
, context
->ra_time
) > 0.0)
472 next_event
= context
->ra_time
;
479 /* There's a context overdue, but we can't find an interface
480 associated with it, because it's for a subnet we dont
481 have an interface on. Probably we're doing DHCP on
482 a remote subnet via a relay. Zero the timer, since we won't
483 ever be able to send ra's and satistfy it. */
484 if (iface_enumerate(AF_INET6
, ¶m
, iface_search
))
485 context
->ra_time
= 0;
486 else if (param
.iface
!= 0 &&
487 indextoname(daemon
->icmp6fd
, param
.iface
, interface
) &&
488 iface_check(AF_LOCAL
, NULL
, interface
))
491 for (tmp
= daemon
->dhcp_except
; tmp
; tmp
= tmp
->next
)
492 if (tmp
->name
&& (strcmp(tmp
->name
, interface
) == 0))
495 send_ra(param
.iface
, interface
, NULL
);
501 static int iface_search(struct in6_addr
*local
, int prefix
,
502 int scope
, int if_index
, int dad
, void *vparam
)
504 struct search_param
*param
= vparam
;
505 struct dhcp_context
*context
;
510 for (context
= daemon
->ra_contexts
; context
; context
= context
->next
)
511 if (prefix
== context
->prefix
&&
512 is_same_net6(local
, &context
->start6
, prefix
) &&
513 is_same_net6(local
, &context
->end6
, prefix
))
514 if (context
->ra_time
!= 0 && difftime(context
->ra_time
, param
->now
) <= 0.0)
516 /* found an interface that's overdue for RA determine new
517 timeout value and arrange for RA to be sent unless interface is
521 param
->iface
= if_index
;
523 if (difftime(param
->now
, ra_short_period_start
) < 60.0)
525 context
->ra_time
= param
->now
+ 5 + (rand16()/4400);
527 /* range 450 - 600 */
528 context
->ra_time
= param
->now
+ 450 + (rand16()/440);
530 /* zero timers for other contexts on the same subnet, so they don't timeout
532 for (context
= context
->next
; context
; context
= context
->next
)
533 if (prefix
== context
->prefix
&&
534 is_same_net6(local
, &context
->start6
, prefix
) &&
535 is_same_net6(local
, &context
->end6
, prefix
))
536 context
->ra_time
= 0;
538 return 0; /* found, abort */
541 return 1; /* keep searching */