blob: b1f2bc125f00b6ad7903bb8974d29c3ea4b87440 [file] [log] [blame]
Simon Kelley61744352013-01-31 14:34:40 +00001/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
Simon Kelleyc5ad4e72012-02-24 16:06:20 +00002
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
Simon Kelley843c96b2012-02-27 17:42:38 +000018/* NB. This code may be called during a DHCPv4 or transaction which is in ping-wait
Simon Kelleyc5ad4e72012-02-24 16:06:20 +000019 It therefore cannot use any DHCP buffer resources except outpacket, which is
Simon Kelley843c96b2012-02-27 17:42:38 +000020 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 */
Simon Kelleyc5ad4e72012-02-24 16:06:20 +000022
23#include "dnsmasq.h"
Simon Kelleyc5ad4e72012-02-24 16:06:20 +000024
25#ifdef HAVE_DHCP6
26
Simon Kelley22d904d2012-02-26 20:13:45 +000027#include <netinet/icmp6.h>
28
Simon Kelleyc5ad4e72012-02-24 16:06:20 +000029struct ra_param {
Simon Kelley1f776932012-12-16 19:46:08 +000030 time_t now;
Simon Kelley30cd9662012-03-25 20:44:38 +010031 int ind, managed, other, found_context, first;
Simon Kelleyc5ad4e72012-02-24 16:06:20 +000032 char *if_name;
Simon Kelley18f0fb02012-03-31 21:18:55 +010033 struct dhcp_netid *tags;
Simon Kelley55b42f62012-12-21 16:53:15 +000034 struct in6_addr link_local, link_global;
35 unsigned int pref_time;
Simon Kelleyc5ad4e72012-02-24 16:06:20 +000036};
37
38struct search_param {
39 time_t now; int iface;
40};
41
Simon Kelley1f776932012-12-16 19:46:08 +000042static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *dest);
Simon Kelleyc5ad4e72012-02-24 16:06:20 +000043static int add_prefixes(struct in6_addr *local, int prefix,
Simon Kelleybad7b872012-12-20 22:00:39 +000044 int scope, int if_index, int flags,
Simon Kelley55b42f62012-12-21 16:53:15 +000045 unsigned int preferred, unsigned int valid, void *vparam);
Simon Kelleyc5ad4e72012-02-24 16:06:20 +000046static int iface_search(struct in6_addr *local, int prefix,
Simon Kelleybad7b872012-12-20 22:00:39 +000047 int scope, int if_index, int flags,
Simon Kelley1f776932012-12-16 19:46:08 +000048 int prefered, int valid, void *vparam);
Simon Kelleyc5ad4e72012-02-24 16:06:20 +000049static int add_lla(int index, unsigned int type, char *mac, size_t maclen, void *parm);
Simon Kelleyef1a94a2013-07-26 13:59:03 +010050static void new_timeout(struct dhcp_context *context, time_t now);
Simon Kelleyc5ad4e72012-02-24 16:06:20 +000051
Simon Kelleyc5379c12012-02-24 20:05:52 +000052static int hop_limit;
Simon Kelleyc5ad4e72012-02-24 16:06:20 +000053
54void ra_init(time_t now)
55{
Simon Kelleyc5ad4e72012-02-24 16:06:20 +000056 struct icmp6_filter filter;
57 int fd;
Simon Kelley0e88d532012-03-28 22:22:05 +010058#if defined(IPV6_TCLASS) && defined(IPTOS_CLASS_CS6)
Simon Kelleyc5ad4e72012-02-24 16:06:20 +000059 int class = IPTOS_CLASS_CS6;
60#endif
61 int val = 255; /* radvd uses this value */
Simon Kelley7b6dd882012-03-01 10:26:16 +000062 socklen_t len = sizeof(int);
Simon Kelley353ae4d2012-03-19 20:07:51 +000063 struct dhcp_context *context;
64
Simon Kelley843c96b2012-02-27 17:42:38 +000065 /* ensure this is around even if we're not doing DHCPv6 */
66 expand_buf(&daemon->outpacket, sizeof(struct dhcp_packet));
Simon Kelley353ae4d2012-03-19 20:07:51 +000067
68 /* See if we're guessing SLAAC addresses, if so we need to recieve ping replies */
Simon Kelley1f776932012-12-16 19:46:08 +000069 for (context = daemon->dhcp6; context; context = context->next)
Simon Kelley353ae4d2012-03-19 20:07:51 +000070 if ((context->flags & CONTEXT_RA_NAME))
71 break;
72
Simon Kelley89500e32013-09-20 16:29:20 +010073 /* Need ICMP6 socket for transmission for DHCPv6 even when not doing RA. */
74
Simon Kelleyc5ad4e72012-02-24 16:06:20 +000075 ICMP6_FILTER_SETBLOCKALL(&filter);
Simon Kelley89500e32013-09-20 16:29:20 +010076 if (daemon->doing_ra)
77 {
78 ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filter);
79 if (context)
80 ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filter);
81 }
Simon Kelleyc5ad4e72012-02-24 16:06:20 +000082
83 if ((fd = socket(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) == -1 ||
Simon Kelleyc5379c12012-02-24 20:05:52 +000084 getsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hop_limit, &len) ||
Simon Kelley0e88d532012-03-28 22:22:05 +010085#if defined(IPV6_TCLASS) && defined(IPTOS_CLASS_CS6)
Simon Kelleyc5ad4e72012-02-24 16:06:20 +000086 setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &class, sizeof(class)) == -1 ||
87#endif
88 !fix_fd(fd) ||
89 !set_ipv6pktinfo(fd) ||
90 setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &val, sizeof(val)) ||
91 setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, sizeof(val)) ||
92 setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof(filter)) == -1)
93 die (_("cannot create ICMPv6 socket: %s"), NULL, EC_BADNET);
94
95 daemon->icmp6fd = fd;
96
Simon Kelley89500e32013-09-20 16:29:20 +010097 if (daemon->doing_ra)
98 ra_start_unsolicted(now, NULL);
Simon Kelleyc5ad4e72012-02-24 16:06:20 +000099}
100
Simon Kelley353ae4d2012-03-19 20:07:51 +0000101void ra_start_unsolicted(time_t now, struct dhcp_context *context)
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000102{
Simon Kelley353ae4d2012-03-19 20:07:51 +0000103 /* init timers so that we do ra's for some/all soon. some ra_times will end up zeroed
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000104 if it's not appropriate to advertise those contexts.
105 This gets re-called on a netlink route-change to re-do the advertisement
106 and pick up new interfaces */
Simon Kelley6e3dba32012-12-16 21:52:45 +0000107
Simon Kelley353ae4d2012-03-19 20:07:51 +0000108 if (context)
Simon Kelley1b75c1e2012-12-18 19:55:25 +0000109 context->ra_short_period_start = context->ra_time = now;
Simon Kelley353ae4d2012-03-19 20:07:51 +0000110 else
Simon Kelley1f776932012-12-16 19:46:08 +0000111 for (context = daemon->dhcp6; context; context = context->next)
Simon Kelley6e3dba32012-12-16 21:52:45 +0000112 if (!(context->flags & CONTEXT_TEMPLATE))
Simon Kelley1b75c1e2012-12-18 19:55:25 +0000113 {
114 context->ra_time = now + (rand16()/13000); /* range 0 - 5 */
115 /* re-do frequently for a minute or so, in case the first gets lost. */
116 context->ra_short_period_start = now;
117 }
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000118}
119
Simon Kelley1f776932012-12-16 19:46:08 +0000120void icmp6_packet(time_t now)
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000121{
122 char interface[IF_NAMESIZE+1];
123 ssize_t sz;
124 int if_index = 0;
125 struct cmsghdr *cmptr;
126 struct msghdr msg;
127 union {
128 struct cmsghdr align; /* this ensures alignment */
129 char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
130 } control_u;
131 struct sockaddr_in6 from;
Simon Kelley5ef33272012-03-30 15:10:28 +0100132 unsigned char *packet;
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000133 struct iname *tmp;
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000134
135 /* Note: use outpacket for input buffer */
136 msg.msg_control = control_u.control6;
137 msg.msg_controllen = sizeof(control_u);
138 msg.msg_flags = 0;
139 msg.msg_name = &from;
140 msg.msg_namelen = sizeof(from);
141 msg.msg_iov = &daemon->outpacket;
142 msg.msg_iovlen = 1;
143
144 if ((sz = recv_dhcp_packet(daemon->icmp6fd, &msg)) == -1 || sz < 8)
145 return;
Simon Kelley5ef33272012-03-30 15:10:28 +0100146
147 packet = (unsigned char *)daemon->outpacket.iov_base;
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000148
149 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
150 if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
151 {
152 union {
153 unsigned char *c;
154 struct in6_pktinfo *p;
155 } p;
156 p.c = CMSG_DATA(cmptr);
157
158 if_index = p.p->ipi6_ifindex;
159 }
160
161 if (!indextoname(daemon->icmp6fd, if_index, interface))
162 return;
163
Simon Kelley4f7b3042012-11-28 21:27:02 +0000164 if (!iface_check(AF_LOCAL, NULL, interface, NULL))
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000165 return;
166
167 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
Simon Kelley49333cb2013-03-15 20:30:51 +0000168 if (tmp->name && wildcard_match(tmp->name, interface))
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000169 return;
170
Simon Kelley8bc4cec2012-07-03 21:04:11 +0100171 if (packet[1] != 0)
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000172 return;
Simon Kelley8bc4cec2012-07-03 21:04:11 +0100173
Simon Kelley5ef33272012-03-30 15:10:28 +0100174 if (packet[0] == ICMP6_ECHO_REPLY)
175 lease_ping_reply(&from.sin6_addr, packet, interface);
176 else if (packet[0] == ND_ROUTER_SOLICIT)
Simon Kelley353ae4d2012-03-19 20:07:51 +0000177 {
Simon Kelley5ef33272012-03-30 15:10:28 +0100178 char *mac = "";
179
180 /* look for link-layer address option for logging */
181 if (sz >= 16 && packet[8] == ICMP6_OPT_SOURCE_MAC && (packet[9] * 8) + 8 <= sz)
182 {
183 print_mac(daemon->namebuff, &packet[10], (packet[9] * 8) - 2);
184 mac = daemon->namebuff;
185 }
186
187 my_syslog(MS_DHCP | LOG_INFO, "RTR-SOLICIT(%s) %s", interface, mac);
Simon Kelleyf632e562012-05-12 15:05:34 +0100188 /* source address may not be valid in solicit request. */
Simon Kelley1f776932012-12-16 19:46:08 +0000189 send_ra(now, if_index, interface, !IN6_IS_ADDR_UNSPECIFIED(&from.sin6_addr) ? &from.sin6_addr : NULL);
Simon Kelley353ae4d2012-03-19 20:07:51 +0000190 }
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000191}
192
Simon Kelley1f776932012-12-16 19:46:08 +0000193static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *dest)
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000194{
195 struct ra_packet *ra;
196 struct ra_param parm;
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000197 struct sockaddr_in6 addr;
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100198 struct dhcp_context *context, *tmp, **up;
Simon Kelley18f0fb02012-03-31 21:18:55 +0100199 struct dhcp_netid iface_id;
200 struct dhcp_opt *opt_cfg;
201 int done_dns = 0;
Simon Kelley3b436462012-12-28 11:55:45 +0000202#ifdef HAVE_LINUX_NETWORK
203 FILE *f;
204#endif
205
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000206 save_counter(0);
207 ra = expand(sizeof(struct ra_packet));
208
Simon Kelley353ae4d2012-03-19 20:07:51 +0000209 ra->type = ND_ROUTER_ADVERT;
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000210 ra->code = 0;
Simon Kelleyc5379c12012-02-24 20:05:52 +0000211 ra->hop_limit = hop_limit;
Simon Kelley884a6df2012-03-20 16:20:22 +0000212 ra->flags = 0x00;
Simon Kelleydc9476b2012-12-29 22:08:26 +0000213 ra->lifetime = htons(RA_INTERVAL * 3); /* AdvDefaultLifetime * 3 */
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000214 ra->reachable_time = 0;
215 ra->retrans_time = 0;
216
217 parm.ind = iface;
218 parm.managed = 0;
Simon Kelley30cd9662012-03-25 20:44:38 +0100219 parm.other = 0;
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000220 parm.found_context = 0;
221 parm.if_name = iface_name;
222 parm.first = 1;
Simon Kelley1f776932012-12-16 19:46:08 +0000223 parm.now = now;
Simon Kelley55b42f62012-12-21 16:53:15 +0000224 parm.pref_time = 0;
Simon Kelley1f776932012-12-16 19:46:08 +0000225
Simon Kelley18f0fb02012-03-31 21:18:55 +0100226 /* set tag with name == interface */
227 iface_id.net = iface_name;
228 iface_id.next = NULL;
229 parm.tags = &iface_id;
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000230
Simon Kelley1f776932012-12-16 19:46:08 +0000231 for (context = daemon->dhcp6; context; context = context->next)
Simon Kelley18f0fb02012-03-31 21:18:55 +0100232 {
233 context->flags &= ~CONTEXT_RA_DONE;
234 context->netid.next = &context->netid;
235 }
236
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100237 if (!iface_enumerate(AF_INET6, &parm, add_prefixes))
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000238 return;
239
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100240 /* Look for constructed contexts associated with addresses which have gone,
241 and advertise them with preferred_time == 0 RFC 6204 4.3 L-13 */
242 for (up = &daemon->dhcp6, context = daemon->dhcp6; context; context = tmp)
243 {
244 tmp = context->next;
Simon Kelley3b436462012-12-28 11:55:45 +0000245
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100246 if (context->if_index == iface && (context->flags & CONTEXT_OLD))
247 {
248 unsigned int old = difftime(now, context->address_lost_time);
249
250 if (old > context->saved_valid)
251 {
252 /* We've advertised this enough, time to go */
253 *up = context->next;
254 free(context);
255 }
256 else
257 {
258 struct prefix_opt *opt;
259 struct in6_addr local = context->start6;
260 int do_slaac = 0;
261
262 parm.found_context = 1;
263
264 /* zero net part of address */
265 setaddr6part(&local, addr6part(&local) & ~((context->prefix == 64) ? (u64)-1LL : (1LLU << (128 - context->prefix)) - 1LLU));
266
267 if ((context->flags &
268 (CONTEXT_RA_ONLY | CONTEXT_RA_NAME | CONTEXT_RA_STATELESS)))
269 do_slaac = 1;
270
271 if ((opt = expand(sizeof(struct prefix_opt))))
272 {
273 opt->type = ICMP6_OPT_PREFIX;
274 opt->len = 4;
275 opt->prefix_len = context->prefix;
276 /* autonomous only if we're not doing dhcp, always set "on-link" */
277 opt->flags = do_slaac ? 0xC0 : 0x80;
278 opt->valid_lifetime = htonl(context->saved_valid - old);
279 opt->preferred_lifetime = htonl(0);
280 opt->reserved = 0;
281 opt->prefix = local;
282
283 inet_ntop(AF_INET6, &local, daemon->addrbuff, ADDRSTRLEN);
284 my_syslog(MS_DHCP | LOG_INFO, "RTR-ADVERT(%s) %s old prefix", iface_name, daemon->addrbuff);
285 }
286
287 up = &context->next;
288 }
289 }
290 else
291 up = &context->next;
292 }
293
294 if (!parm.found_context)
295 return;
296
Simon Kelley3b436462012-12-28 11:55:45 +0000297#ifdef HAVE_LINUX_NETWORK
298 /* Note that IPv6 MTU is not necessarilly the same as the IPv4 MTU
299 available from SIOCGIFMTU */
300 sprintf(daemon->namebuff, "/proc/sys/net/ipv6/conf/%s/mtu", iface_name);
301 if ((f = fopen(daemon->namebuff, "r")))
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000302 {
Simon Kelley3b436462012-12-28 11:55:45 +0000303 if (fgets(daemon->namebuff, MAXDNAME, f))
304 {
305 put_opt6_char(ICMP6_OPT_MTU);
306 put_opt6_char(1);
307 put_opt6_short(0);
308 put_opt6_long(atoi(daemon->namebuff));
309 }
310 fclose(f);
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000311 }
Simon Kelley3b436462012-12-28 11:55:45 +0000312#endif
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000313
314 iface_enumerate(AF_LOCAL, &iface, add_lla);
Simon Kelley18f0fb02012-03-31 21:18:55 +0100315
316 /* RDNSS, RFC 6106, use relevant DHCP6 options */
317 (void)option_filter(parm.tags, NULL, daemon->dhcp_opts6);
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000318
Simon Kelley18f0fb02012-03-31 21:18:55 +0100319 for (opt_cfg = daemon->dhcp_opts6; opt_cfg; opt_cfg = opt_cfg->next)
320 {
321 int i;
322
323 /* netids match and not encapsulated? */
324 if (!(opt_cfg->flags & DHOPT_TAGOK))
325 continue;
326
327 if (opt_cfg->opt == OPTION6_DNS_SERVER)
328 {
329 struct in6_addr *a = (struct in6_addr *)opt_cfg->val;
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000330
Simon Kelley18f0fb02012-03-31 21:18:55 +0100331 done_dns = 1;
332 if (opt_cfg->len == 0)
333 continue;
334
335 put_opt6_char(ICMP6_OPT_RDNSS);
336 put_opt6_char((opt_cfg->len/8) + 1);
337 put_opt6_short(0);
Simon Kelleydc9476b2012-12-29 22:08:26 +0000338 put_opt6_long(RA_INTERVAL * 2); /* lifetime - twice RA retransmit */
Simon Kelley18f0fb02012-03-31 21:18:55 +0100339 /* zero means "self" */
340 for (i = 0; i < opt_cfg->len; i += IN6ADDRSZ, a++)
341 if (IN6_IS_ADDR_UNSPECIFIED(a))
Simon Kelley55b42f62012-12-21 16:53:15 +0000342 put_opt6(&parm.link_global, IN6ADDRSZ);
Simon Kelley18f0fb02012-03-31 21:18:55 +0100343 else
344 put_opt6(a, IN6ADDRSZ);
345 }
346
347 if (opt_cfg->opt == OPTION6_DOMAIN_SEARCH && opt_cfg->len != 0)
348 {
349 int len = ((opt_cfg->len+7)/8);
350
351 put_opt6_char(ICMP6_OPT_DNSSL);
352 put_opt6_char(len + 1);
353 put_opt6_short(0);
Simon Kelley1ecbaaa2013-07-25 14:19:27 +0100354 put_opt6_long(RA_INTERVAL * 2); /* lifetime - twice RA retransmit */
Simon Kelley18f0fb02012-03-31 21:18:55 +0100355 put_opt6(opt_cfg->val, opt_cfg->len);
356
357 /* pad */
358 for (i = opt_cfg->len; i < len * 8; i++)
359 put_opt6_char(0);
360 }
361 }
362
Simon Kelleyab915f82013-04-30 10:41:28 +0100363 if (daemon->port == NAMESERVER_PORT && !done_dns)
Simon Kelley18f0fb02012-03-31 21:18:55 +0100364 {
Simon Kelleyab915f82013-04-30 10:41:28 +0100365 /* default == us, as long as we are supplying DNS service. */
Simon Kelley18f0fb02012-03-31 21:18:55 +0100366 put_opt6_char(ICMP6_OPT_RDNSS);
367 put_opt6_char(3);
368 put_opt6_short(0);
Simon Kelleydc9476b2012-12-29 22:08:26 +0000369 put_opt6_long(RA_INTERVAL * 2); /* lifetime - twice RA retransmit */
Simon Kelley55b42f62012-12-21 16:53:15 +0000370 put_opt6(&parm.link_global, IN6ADDRSZ);
Simon Kelley18f0fb02012-03-31 21:18:55 +0100371 }
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000372
373 /* set managed bits unless we're providing only RA on this link */
374 if (parm.managed)
Simon Kelley30cd9662012-03-25 20:44:38 +0100375 ra->flags |= 0x80; /* M flag, managed, */
376 if (parm.other)
377 ra->flags |= 0x40; /* O flag, other */
378
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000379 /* decide where we're sending */
380 memset(&addr, 0, sizeof(addr));
Simon Kelley22d904d2012-02-26 20:13:45 +0000381#ifdef HAVE_SOCKADDR_SA_LEN
382 addr.sin6_len = sizeof(struct sockaddr_in6);
383#endif
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000384 addr.sin6_family = AF_INET6;
385 addr.sin6_port = htons(IPPROTO_ICMPV6);
386 if (dest)
387 {
Simon Kelley353ae4d2012-03-19 20:07:51 +0000388 addr.sin6_addr = *dest;
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000389 if (IN6_IS_ADDR_LINKLOCAL(dest) ||
390 IN6_IS_ADDR_MC_LINKLOCAL(dest))
391 addr.sin6_scope_id = iface;
392 }
393 else
Simon Kelley8f3194f2013-09-30 15:04:58 +0100394 {
395 inet_pton(AF_INET6, ALL_NODES, &addr.sin6_addr);
396 setsockopt(daemon->icmp6fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &iface, sizeof(iface));
397 }
Simon Kelley22d904d2012-02-26 20:13:45 +0000398
Simon Kelley8f3194f2013-09-30 15:04:58 +0100399 while (sendto(daemon->icmp6fd, daemon->outpacket.iov_base, save_counter(0), 0,
400 (struct sockaddr *)&addr, sizeof(addr)) == -1 && retry_send());
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000401
402}
403
404static int add_prefixes(struct in6_addr *local, int prefix,
Simon Kelleybad7b872012-12-20 22:00:39 +0000405 int scope, int if_index, int flags,
Simon Kelley55b42f62012-12-21 16:53:15 +0000406 unsigned int preferred, unsigned int valid, void *vparam)
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000407{
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000408 struct ra_param *param = vparam;
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000409
410 (void)scope; /* warning */
Simon Kelleyed8b68a2012-12-21 16:23:26 +0000411
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000412 if (if_index == param->ind)
413 {
414 if (IN6_IS_ADDR_LINKLOCAL(local))
415 param->link_local = *local;
416 else if (!IN6_IS_ADDR_LOOPBACK(local) &&
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000417 !IN6_IS_ADDR_MULTICAST(local))
418 {
Simon Kelley1e02a852012-03-29 11:07:25 +0100419 int do_prefix = 0;
Simon Kelleyc8257542012-03-28 21:15:41 +0100420 int do_slaac = 0;
421 int deprecate = 0;
Simon Kelley3bc0d932012-12-28 11:31:44 +0000422 int constructed = 0;
Simon Kelleyc8257542012-03-28 21:15:41 +0100423 unsigned int time = 0xffffffff;
Simon Kelley1e02a852012-03-29 11:07:25 +0100424 struct dhcp_context *context;
425
Simon Kelley1f776932012-12-16 19:46:08 +0000426 for (context = daemon->dhcp6; context; context = context->next)
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100427 if (!(context->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) &&
Simon Kelley6e3dba32012-12-16 21:52:45 +0000428 prefix == context->prefix &&
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000429 is_same_net6(local, &context->start6, prefix) &&
430 is_same_net6(local, &context->end6, prefix))
431 {
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100432 context->saved_valid = valid;
433
Simon Kelley30cd9662012-03-25 20:44:38 +0100434 if ((context->flags &
435 (CONTEXT_RA_ONLY | CONTEXT_RA_NAME | CONTEXT_RA_STATELESS)))
436 {
437 do_slaac = 1;
Simon Kelley4723d492012-03-30 21:04:17 +0100438 if (context->flags & CONTEXT_DHCP)
Simon Kelley05e92e52012-03-30 22:24:15 +0100439 {
440 param->other = 1;
441 if (!(context->flags & CONTEXT_RA_STATELESS))
442 param->managed = 1;
443 }
Simon Kelley30cd9662012-03-25 20:44:38 +0100444 }
Simon Kelley884a6df2012-03-20 16:20:22 +0000445 else
Simon Kelley0010b472012-02-29 12:18:30 +0000446 {
447 /* don't do RA for non-ra-only unless --enable-ra is set */
448 if (!option_bool(OPT_RA))
449 continue;
450 param->managed = 1;
Simon Kelley30cd9662012-03-25 20:44:38 +0100451 param->other = 1;
Simon Kelley0010b472012-02-29 12:18:30 +0000452 }
Simon Kelley1f776932012-12-16 19:46:08 +0000453
Simon Kelley1ecbaaa2013-07-25 14:19:27 +0100454 /* find floor time, don't reduce below 3 * RA interval. */
Simon Kelleyc8257542012-03-28 21:15:41 +0100455 if (time > context->lease_time)
Simon Kelley7f035f52012-12-22 21:27:08 +0000456 {
457 time = context->lease_time;
Simon Kelley1ecbaaa2013-07-25 14:19:27 +0100458 if (time < ((unsigned int)(3 * RA_INTERVAL)))
459 time = 3 * RA_INTERVAL;
Simon Kelley7f035f52012-12-22 21:27:08 +0000460 }
461
Simon Kelleyc8257542012-03-28 21:15:41 +0100462 if (context->flags & CONTEXT_DEPRECATE)
463 deprecate = 1;
Simon Kelley3bc0d932012-12-28 11:31:44 +0000464
465 if (context->flags & CONTEXT_CONSTRUCTED)
466 constructed = 1;
467
Simon Kelleyc8257542012-03-28 21:15:41 +0100468
Simon Kelley18f0fb02012-03-31 21:18:55 +0100469 /* collect dhcp-range tags */
470 if (context->netid.next == &context->netid && context->netid.net)
471 {
472 context->netid.next = param->tags;
473 param->tags = &context->netid;
474 }
475
Simon Kelley5ae34bf2012-06-04 21:14:03 +0100476 /* subsequent prefixes on the same interface
477 and subsequent instances of this prefix don't need timers.
478 Be careful not to find the same prefix twice with different
479 addresses. */
Simon Kelley1e02a852012-03-29 11:07:25 +0100480 if (!(context->flags & CONTEXT_RA_DONE))
481 {
Simon Kelley5ae34bf2012-06-04 21:14:03 +0100482 if (!param->first)
483 context->ra_time = 0;
Simon Kelley1e02a852012-03-29 11:07:25 +0100484 context->flags |= CONTEXT_RA_DONE;
485 do_prefix = 1;
486 }
Simon Kelley5ae34bf2012-06-04 21:14:03 +0100487
488 param->first = 0;
489 param->found_context = 1;
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000490 }
Simon Kelley1f776932012-12-16 19:46:08 +0000491
Simon Kelleyed8b68a2012-12-21 16:23:26 +0000492 /* configured time is ceiling */
Simon Kelley3bc0d932012-12-28 11:31:44 +0000493 if (!constructed || valid > time)
Simon Kelleyed8b68a2012-12-21 16:23:26 +0000494 valid = time;
495
Simon Kelley3bc0d932012-12-28 11:31:44 +0000496 if (flags & IFACE_DEPRECATED)
Simon Kelleyed8b68a2012-12-21 16:23:26 +0000497 preferred = 0;
Simon Kelley1e02a852012-03-29 11:07:25 +0100498
Simon Kelley3bc0d932012-12-28 11:31:44 +0000499 if (deprecate)
500 time = 0;
501
502 /* configured time is ceiling */
503 if (!constructed || preferred > time)
504 preferred = time;
505
Simon Kelley55b42f62012-12-21 16:53:15 +0000506 if (preferred > param->pref_time)
507 {
508 param->pref_time = preferred;
509 param->link_global = *local;
510 }
511
Simon Kelley1e02a852012-03-29 11:07:25 +0100512 if (do_prefix)
Simon Kelleyc8257542012-03-28 21:15:41 +0100513 {
Simon Kelley1e02a852012-03-29 11:07:25 +0100514 struct prefix_opt *opt;
515
516 if ((opt = expand(sizeof(struct prefix_opt))))
517 {
Simon Kelley5ef33272012-03-30 15:10:28 +0100518 /* zero net part of address */
519 setaddr6part(local, addr6part(local) & ~((prefix == 64) ? (u64)-1LL : (1LLU << (128 - prefix)) - 1LLU));
520
Simon Kelley1e02a852012-03-29 11:07:25 +0100521 opt->type = ICMP6_OPT_PREFIX;
522 opt->len = 4;
523 opt->prefix_len = prefix;
Simon Kelleyfd05f122012-08-12 17:48:50 +0100524 /* autonomous only if we're not doing dhcp, always set "on-link" */
525 opt->flags = do_slaac ? 0xC0 : 0x80;
Simon Kelleyed8b68a2012-12-21 16:23:26 +0000526 opt->valid_lifetime = htonl(valid);
527 opt->preferred_lifetime = htonl(preferred);
Simon Kelley5ef33272012-03-30 15:10:28 +0100528 opt->reserved = 0;
Simon Kelley1e02a852012-03-29 11:07:25 +0100529 opt->prefix = *local;
Simon Kelley1e02a852012-03-29 11:07:25 +0100530
Simon Kelley5ef33272012-03-30 15:10:28 +0100531 inet_ntop(AF_INET6, local, daemon->addrbuff, ADDRSTRLEN);
Simon Kelley1e02a852012-03-29 11:07:25 +0100532 my_syslog(MS_DHCP | LOG_INFO, "RTR-ADVERT(%s) %s", param->if_name, daemon->addrbuff);
533 }
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100534
Simon Kelleyc8257542012-03-28 21:15:41 +0100535 }
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000536 }
537 }
538 return 1;
539}
540
541static int add_lla(int index, unsigned int type, char *mac, size_t maclen, void *parm)
542{
543 (void)type;
544
545 if (index == *((int *)parm))
546 {
547 /* size is in units of 8 octets and includes type and length (2 bytes)
548 add 7 to round up */
549 int len = (maclen + 9) >> 3;
550 unsigned char *p = expand(len << 3);
551 memset(p, 0, len << 3);
552 *p++ = ICMP6_OPT_SOURCE_MAC;
553 *p++ = len;
554 memcpy(p, mac, maclen);
555
556 return 0;
557 }
558
559 return 1;
560}
561
562time_t periodic_ra(time_t now)
563{
564 struct search_param param;
565 struct dhcp_context *context;
566 time_t next_event;
567 char interface[IF_NAMESIZE+1];
568
569 param.now = now;
Simon Kelley29d28dd2012-12-03 14:05:59 +0000570 param.iface = 0;
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000571
572 while (1)
573 {
574 /* find overdue events, and time of first future event */
Simon Kelley1f776932012-12-16 19:46:08 +0000575 for (next_event = 0, context = daemon->dhcp6; context; context = context->next)
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000576 if (context->ra_time != 0)
577 {
Simon Kelley7dbe9812012-03-25 14:49:54 +0100578 if (difftime(context->ra_time, now) <= 0.0)
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000579 break; /* overdue */
580
Simon Kelley7dbe9812012-03-25 14:49:54 +0100581 if (next_event == 0 || difftime(next_event, context->ra_time) > 0.0)
582 next_event = context->ra_time;
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000583 }
584
585 /* none overdue */
586 if (!context)
587 break;
588
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100589 if ((context->flags & CONTEXT_OLD) && context->if_index != 0)
590 {
591 /* A context for an old address. We'll not find the interface by
592 looking for addresses, but we know it anyway, as long as we
593 sent at least one RA whilst the address was current. */
594 param.iface = context->if_index;
595 new_timeout(context, now);
596 }
597 else if (iface_enumerate(AF_INET6, &param, iface_search))
598 /* There's a context overdue, but we can't find an interface
599 associated with it, because it's for a subnet we dont
600 have an interface on. Probably we're doing DHCP on
601 a remote subnet via a relay. Zero the timer, since we won't
602 ever be able to send ra's and satistfy it. */
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000603 context->ra_time = 0;
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100604
605 if (param.iface != 0 &&
606 indextoname(daemon->icmp6fd, param.iface, interface) &&
607 iface_check(AF_LOCAL, NULL, interface, NULL))
Simon Kelley421594f2012-12-02 12:17:35 +0000608 {
609 struct iname *tmp;
610 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
Simon Kelley49333cb2013-03-15 20:30:51 +0000611 if (tmp->name && wildcard_match(tmp->name, interface))
Simon Kelley421594f2012-12-02 12:17:35 +0000612 break;
613 if (!tmp)
Simon Kelley1f776932012-12-16 19:46:08 +0000614 send_ra(now, param.iface, interface, NULL);
Simon Kelley421594f2012-12-02 12:17:35 +0000615 }
616 }
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000617 return next_event;
618}
Simon Kelley421594f2012-12-02 12:17:35 +0000619
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000620static int iface_search(struct in6_addr *local, int prefix,
Simon Kelleybad7b872012-12-20 22:00:39 +0000621 int scope, int if_index, int flags,
Simon Kelley1f776932012-12-16 19:46:08 +0000622 int preferred, int valid, void *vparam)
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000623{
624 struct search_param *param = vparam;
Simon Kelley741c2952012-02-25 13:09:18 +0000625 struct dhcp_context *context;
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000626
627 (void)scope;
Simon Kelley1f776932012-12-16 19:46:08 +0000628 (void)preferred;
629 (void)valid;
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000630
Simon Kelley1f776932012-12-16 19:46:08 +0000631 for (context = daemon->dhcp6; context; context = context->next)
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100632 if (!(context->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) &&
Simon Kelley6e3dba32012-12-16 21:52:45 +0000633 prefix == context->prefix &&
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000634 is_same_net6(local, &context->start6, prefix) &&
Simon Kelley6e3dba32012-12-16 21:52:45 +0000635 is_same_net6(local, &context->end6, prefix) &&
636 context->ra_time != 0 &&
637 difftime(context->ra_time, param->now) <= 0.0)
638 {
639 /* found an interface that's overdue for RA determine new
640 timeout value and arrange for RA to be sent unless interface is
641 still doing DAD.*/
642
Simon Kelleybad7b872012-12-20 22:00:39 +0000643 if (!(flags & IFACE_TENTATIVE))
Simon Kelley6e3dba32012-12-16 21:52:45 +0000644 param->iface = if_index;
645
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100646 new_timeout(context, param->now);
Simon Kelley6e3dba32012-12-16 21:52:45 +0000647
648 /* zero timers for other contexts on the same subnet, so they don't timeout
649 independently */
650 for (context = context->next; context; context = context->next)
651 if (prefix == context->prefix &&
652 is_same_net6(local, &context->start6, prefix) &&
653 is_same_net6(local, &context->end6, prefix))
654 context->ra_time = 0;
655
656 return 0; /* found, abort */
657 }
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000658
659 return 1; /* keep searching */
660}
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100661
662static void new_timeout(struct dhcp_context *context, time_t now)
663{
Simon Kelley8d030462013-07-29 15:41:26 +0100664 if (difftime(now, context->ra_short_period_start) < 60.0 || option_bool(OPT_FAST_RA))
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100665 /* range 5 - 20 */
666 context->ra_time = now + 5 + (rand16()/4400);
667 else
668 /* range 3/4 - 1 times RA_INTERVAL */
669 context->ra_time = now + (3 * RA_INTERVAL)/4 + ((RA_INTERVAL * (unsigned int)rand16()) >> 18);
670}
Simon Kelley801ca9a2012-03-06 19:30:17 +0000671
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000672#endif