]>
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 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. */
26 #include <netinet/icmp6.h>
29 int ind
, managed
, found_context
, first
;
31 struct in6_addr link_local
;
35 time_t now
; int iface
;
38 static void send_ra(int iface
, char *iface_name
, struct in6_addr
*dest
);
39 static int add_prefixes(struct in6_addr
*local
, int prefix
,
40 int scope
, int if_index
, int dad
, void *vparam
);
41 static int iface_search(struct in6_addr
*local
, int prefix
,
42 int scope
, int if_index
, int dad
, void *vparam
);
43 static int add_lla(int index
, unsigned int type
, char *mac
, size_t maclen
, void *parm
);
46 static time_t ra_short_period_start
;
48 void ra_init(time_t now
)
50 struct dhcp_context
*context
;
51 struct icmp6_filter filter
;
53 #if defined(IP_TOS) && defined(IPTOS_CLASS_CS6)
54 int class = IPTOS_CLASS_CS6
;
56 int val
= 255; /* radvd uses this value */
57 size_t len
= sizeof(int);
59 ICMP6_FILTER_SETBLOCKALL(&filter
);
60 ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT
, &filter
);
61 ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT
, &filter
);
63 if ((fd
= socket(PF_INET6
, SOCK_RAW
, IPPROTO_ICMPV6
)) == -1 ||
64 getsockopt(fd
, IPPROTO_IPV6
, IPV6_UNICAST_HOPS
, &hop_limit
, &len
) ||
65 #if defined(IP_TOS) && defined(IPTOS_CLASS_CS6)
66 setsockopt(fd
, IPPROTO_IPV6
, IPV6_TCLASS
, &class, sizeof(class)) == -1 ||
69 !set_ipv6pktinfo(fd
) ||
70 setsockopt(fd
, IPPROTO_IPV6
, IPV6_UNICAST_HOPS
, &val
, sizeof(val
)) ||
71 setsockopt(fd
, IPPROTO_IPV6
, IPV6_MULTICAST_HOPS
, &val
, sizeof(val
)) ||
72 setsockopt(fd
, IPPROTO_ICMPV6
, ICMP6_FILTER
, &filter
, sizeof(filter
)) == -1)
73 die (_("cannot create ICMPv6 socket: %s"), NULL
, EC_BADNET
);
77 /* link the DHCP6 contexts to the ra-only ones so we can traverse them all
78 from ->ra_contexts, but only the non-ra-onlies from ->dhcp6 */
79 if (!daemon
->ra_contexts
)
80 daemon
->ra_contexts
= daemon
->dhcp6
;
83 for (context
= daemon
->ra_contexts
; context
->next
; context
= context
->next
);
84 context
->next
= daemon
->dhcp6
;
88 die(_("cannot do router advertisement unless DHCPv6 is enabled"), NULL
, EC_BADCONF
);
90 ra_start_unsolicted(now
);
93 void ra_start_unsolicted(time_t now
)
95 struct dhcp_context
*context
;
97 /* init timers so that we do ra's for all soon. some ra_times will end up zeroed
98 if it's not appropriate to advertise those contexts.
99 This gets re-called on a netlink route-change to re-do the advertisement
100 and pick up new interfaces */
103 for (context
= daemon
->ra_contexts
; context
; context
= context
->next
)
104 context
->ra_time
= now
+ (rand16()/13000);
106 /* re-do frequently for a minute or so, in case the first gets lost. */
107 ra_short_period_start
= now
;
110 void icmp6_packet(void)
112 char interface
[IF_NAMESIZE
+1];
115 struct cmsghdr
*cmptr
;
118 struct cmsghdr align
; /* this ensures alignment */
119 char control6
[CMSG_SPACE(sizeof(struct in6_pktinfo
))];
121 struct sockaddr_in6 from
;
125 struct dhcp_context
*context
;
127 /* Note: use outpacket for input buffer */
128 msg
.msg_control
= control_u
.control6
;
129 msg
.msg_controllen
= sizeof(control_u
);
131 msg
.msg_name
= &from
;
132 msg
.msg_namelen
= sizeof(from
);
133 msg
.msg_iov
= &daemon
->outpacket
;
136 if ((sz
= recv_dhcp_packet(daemon
->icmp6fd
, &msg
)) == -1 || sz
< 8)
139 for (cmptr
= CMSG_FIRSTHDR(&msg
); cmptr
; cmptr
= CMSG_NXTHDR(&msg
, cmptr
))
140 if (cmptr
->cmsg_level
== IPPROTO_IPV6
&& cmptr
->cmsg_type
== daemon
->v6pktinfo
)
144 struct in6_pktinfo
*p
;
146 p
.c
= CMSG_DATA(cmptr
);
148 if_index
= p
.p
->ipi6_ifindex
;
151 if (!indextoname(daemon
->icmp6fd
, if_index
, interface
))
154 if (!iface_check(AF_LOCAL
, NULL
, interface
))
157 for (tmp
= daemon
->dhcp_except
; tmp
; tmp
= tmp
->next
)
158 if (tmp
->name
&& (strcmp(tmp
->name
, interface
) == 0))
161 /* weird libvirt-inspired access control */
162 for (context
= daemon
->dhcp6
; context
; context
= context
->next
)
163 if (!context
->interface
|| strcmp(context
->interface
, interface
) == 0)
169 p
= (unsigned char *)daemon
->outpacket
.iov_base
;
171 if (p
[0] != ICMP6_ROUTER_SOLICIT
|| p
[1] != 0)
174 /* look for link-layer address option for logging */
175 if (sz
>= 16 && p
[8] == ICMP6_OPT_SOURCE_MAC
&& (p
[9] * 8) + 8 <= sz
)
177 print_mac(daemon
->namebuff
, &p
[10], (p
[9] * 8) - 2);
178 mac
= daemon
->namebuff
;
181 my_syslog(MS_DHCP
| LOG_INFO
, "RTR-SOLICIT(%s) %s", interface
, mac
);
183 send_ra(if_index
, interface
, &from
.sin6_addr
);
186 static void send_ra(int iface
, char *iface_name
, struct in6_addr
*dest
)
188 struct ra_packet
*ra
;
189 struct ra_param parm
;
191 struct sockaddr_in6 addr
;
192 struct dhcp_context
*context
;
195 ra
= expand(sizeof(struct ra_packet
));
197 ra
->type
= ICMP6_ROUTER_ADVERT
;
199 ra
->hop_limit
= hop_limit
;
201 ra
->lifetime
= htons(1800); /* AdvDefaultLifetime*/
202 ra
->reachable_time
= 0;
203 ra
->retrans_time
= 0;
207 parm
.found_context
= 0;
208 parm
.if_name
= iface_name
;
211 for (context
= daemon
->ra_contexts
; context
; context
= context
->next
)
212 context
->flags
&= ~CONTEXT_RA_DONE
;
214 if (!iface_enumerate(AF_INET6
, &parm
, add_prefixes
) ||
218 strncpy(ifr
.ifr_name
, iface_name
, IF_NAMESIZE
);
220 if (ioctl(daemon
->icmp6fd
, SIOCGIFMTU
, &ifr
) != -1)
222 put_opt6_char(ICMP6_OPT_MTU
);
225 put_opt6_long(ifr
.ifr_mtu
);
228 iface_enumerate(AF_LOCAL
, &iface
, add_lla
);
230 /* RDNSS, RFC 6106 */
231 put_opt6_char(ICMP6_OPT_RDNSS
);
234 put_opt6_long(1800); /* lifetime - twice RA retransmit */
235 put_opt6(&parm
.link_local
, IN6ADDRSZ
);
238 /* set managed bits unless we're providing only RA on this link */
242 /* decide where we're sending */
243 memset(&addr
, 0, sizeof(addr
));
244 #ifdef HAVE_SOCKADDR_SA_LEN
245 addr
.sin6_len
= sizeof(struct sockaddr_in6
);
247 addr
.sin6_family
= AF_INET6
;
248 addr
.sin6_port
= htons(IPPROTO_ICMPV6
);
251 memcpy(&addr
.sin6_addr
, dest
, sizeof(struct in6_addr
));
252 if (IN6_IS_ADDR_LINKLOCAL(dest
) ||
253 IN6_IS_ADDR_MC_LINKLOCAL(dest
))
254 addr
.sin6_scope_id
= iface
;
257 inet_pton(AF_INET6
, ALL_HOSTS
, &addr
.sin6_addr
);
259 send_from(daemon
->icmp6fd
, 0, daemon
->outpacket
.iov_base
, save_counter(0),
260 (union mysockaddr
*)&addr
, (struct all_addr
*)&parm
.link_local
, iface
);
264 static int add_prefixes(struct in6_addr
*local
, int prefix
,
265 int scope
, int if_index
, int dad
, void *vparam
)
267 struct dhcp_context
*context
, *tmp
;
268 struct ra_param
*param
= vparam
;
269 struct prefix_opt
*opt
;
271 (void)scope
; /* warning */
274 if (if_index
== param
->ind
)
276 if (IN6_IS_ADDR_LINKLOCAL(local
))
277 param
->link_local
= *local
;
278 else if (!IN6_IS_ADDR_LOOPBACK(local
) &&
279 !IN6_IS_ADDR_LINKLOCAL(local
) &&
280 !IN6_IS_ADDR_MULTICAST(local
))
282 for (context
= daemon
->ra_contexts
; context
; context
= context
->next
)
283 if (prefix
== context
->prefix
&&
284 is_same_net6(local
, &context
->start6
, prefix
) &&
285 is_same_net6(local
, &context
->end6
, prefix
))
287 if (!(context
->flags
& CONTEXT_RA_ONLY
))
290 if (context
->flags
& CONTEXT_RA_DONE
)
293 /* subsequent prefixes on the same interface don't need timers */
295 context
->ra_time
= 0;
297 param
->found_context
= 1;
298 context
->flags
|= CONTEXT_RA_DONE
;
300 /* mark this subnet and duplicates: as done. */
301 for (tmp
= context
->next
; tmp
; tmp
= tmp
->next
)
302 if (tmp
->prefix
== prefix
&&
303 is_same_net6(local
, &tmp
->start6
, prefix
) &&
304 is_same_net6(local
, &tmp
->end6
, prefix
))
306 tmp
->flags
|= CONTEXT_RA_DONE
;
307 context
->ra_time
= 0;
310 if ((opt
= expand(sizeof(struct prefix_opt
))))
312 u64 addrpart
= addr6part(&context
->start6
);
313 u64 mask
= (prefix
== 64) ? (u64
)-1LL : (1LLU << (128 - prefix
)) - 1LLU;
314 unsigned int time
= context
->lease_time
;
316 /* lifetimes must be min 2 hrs, by RFC 2462 */
320 opt
->type
= ICMP6_OPT_PREFIX
;
322 opt
->prefix_len
= prefix
;
323 /* autonomous only is we're not doing dhcp */
324 opt
->flags
= (context
->flags
& CONTEXT_RA_ONLY
) ? 0xc0 : 0x00;
325 opt
->valid_lifetime
= opt
->preferred_lifetime
= htonl(time
);
328 opt
->prefix
= context
->start6
;
329 setaddr6part(&opt
->prefix
, addrpart
& ~mask
);
331 inet_ntop(AF_INET6
, &opt
->prefix
, daemon
->addrbuff
, ADDRSTRLEN
);
332 my_syslog(MS_DHCP
| LOG_INFO
, "RTR-ADVERT(%s) %s", param
->if_name
, daemon
->addrbuff
);
340 static int add_lla(int index
, unsigned int type
, char *mac
, size_t maclen
, void *parm
)
344 if (index
== *((int *)parm
))
346 /* size is in units of 8 octets and includes type and length (2 bytes)
348 int len
= (maclen
+ 9) >> 3;
349 unsigned char *p
= expand(len
<< 3);
350 memset(p
, 0, len
<< 3);
351 *p
++ = ICMP6_OPT_SOURCE_MAC
;
353 memcpy(p
, mac
, maclen
);
361 time_t periodic_ra(time_t now
)
363 struct search_param param
;
364 struct dhcp_context
*context
;
366 char interface
[IF_NAMESIZE
+1];
372 /* find overdue events, and time of first future event */
373 for (next_event
= 0, context
= daemon
->ra_contexts
; context
; context
= context
->next
)
374 if (context
->ra_time
!= 0)
376 if (difftime(context
->ra_time
, now
) < 0.0)
379 if (next_event
== 0 || difftime(next_event
, context
->ra_time
+ 2) > 0.0)
380 next_event
= context
->ra_time
+ 2;
387 /* There's a context overdue, but we can't find an interface
388 associated with it, because it's for a subnet we dont
389 have an interface on. Probably we're doing DHCP on
390 a remote subnet via a relay. Zero the timer, since we won't
391 ever be able to send ra's and satistfy it. */
392 if (iface_enumerate(AF_INET6
, ¶m
, iface_search
))
393 context
->ra_time
= 0;
394 else if (indextoname(daemon
->icmp6fd
, param
.iface
, interface
))
395 send_ra(param
.iface
, interface
, NULL
);
401 static int iface_search(struct in6_addr
*local
, int prefix
,
402 int scope
, int if_index
, int dad
, void *vparam
)
404 struct search_param
*param
= vparam
;
405 struct dhcp_context
*context
;
410 for (context
= daemon
->ra_contexts
; context
; context
= context
->next
)
411 if (prefix
== context
->prefix
&&
412 is_same_net6(local
, &context
->start6
, prefix
) &&
413 is_same_net6(local
, &context
->end6
, prefix
))
414 if (context
->ra_time
!= 0 && difftime(context
->ra_time
, param
->now
) < 0.0)
416 /* found an interface that's overdue for RA determine new
417 timeout value and zap other contexts on the same interface
418 so they don't timeout independently .*/
419 param
->iface
= if_index
;
421 if (difftime(param
->now
, ra_short_period_start
) < 60.0)
423 context
->ra_time
= param
->now
+ 5 + (rand16()/4400);
425 /* range 450 - 600 */
426 context
->ra_time
= param
->now
+ 450 + (rand16()/440);
428 return 0; /* found, abort */
431 return 1; /* keep searching */