blob: d9cd69058ad62d567ca57a4b772b3eba9bd7b5e4 [file] [log] [blame]
Simon Kelleyc47e3ba2014-01-08 17:07:54 +00001/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
Simon Kelleyc72daea2012-01-05 21:33:27 +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#include "dnsmasq.h"
18
19#ifdef HAVE_DHCP6
20
Simon Kelley89500e32013-09-20 16:29:20 +010021#include <netinet/icmp6.h>
22
Simon Kelleyc72daea2012-01-05 21:33:27 +000023struct iface_param {
24 struct dhcp_context *current;
Simon Kelleyff7eea22013-09-04 18:01:38 +010025 struct dhcp_relay *relay;
26 struct in6_addr fallback, relay_local;
Simon Kelleybe6cfb42012-10-16 20:38:31 +010027 int ind, addr_match;
Simon Kelleyc72daea2012-01-05 21:33:27 +000028};
29
Simon Kelley89500e32013-09-20 16:29:20 +010030struct mac_param {
31 struct in6_addr *target;
Simon Kelley8939c952013-09-25 11:49:34 +010032 unsigned char *mac;
Simon Kelley89500e32013-09-20 16:29:20 +010033 unsigned int maclen;
34};
35
36
Simon Kelleyc72daea2012-01-05 21:33:27 +000037static int complete_context6(struct in6_addr *local, int prefix,
Simon Kelleybad7b872012-12-20 22:00:39 +000038 int scope, int if_index, int flags,
Simon Kelley3bc0d932012-12-28 11:31:44 +000039 unsigned int preferred, unsigned int valid, void *vparam);
Simon Kelley89500e32013-09-20 16:29:20 +010040static int find_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv);
Simon Kelleyc5ad4e72012-02-24 16:06:20 +000041static int make_duid1(int index, unsigned int type, char *mac, size_t maclen, void *parm);
Simon Kelley4cb1b322012-02-06 14:30:41 +000042
Simon Kelleyc72daea2012-01-05 21:33:27 +000043void dhcp6_init(void)
44{
45 int fd;
46 struct sockaddr_in6 saddr;
Simon Kelley0e88d532012-03-28 22:22:05 +010047#if defined(IPV6_TCLASS) && defined(IPTOS_CLASS_CS6)
Simon Kelleyc72daea2012-01-05 21:33:27 +000048 int class = IPTOS_CLASS_CS6;
Simon Kelley4cb1b322012-02-06 14:30:41 +000049#endif
Simon Kelley20223102012-10-15 10:41:17 +010050 int oneopt = 1;
51
Simon Kelleyc72daea2012-01-05 21:33:27 +000052 if ((fd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP)) == -1 ||
Simon Kelley0e88d532012-03-28 22:22:05 +010053#if defined(IPV6_TCLASS) && defined(IPTOS_CLASS_CS6)
Simon Kelleyc72daea2012-01-05 21:33:27 +000054 setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &class, sizeof(class)) == -1 ||
Simon Kelley4cb1b322012-02-06 14:30:41 +000055#endif
Simon Kelley20223102012-10-15 10:41:17 +010056 setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &oneopt, sizeof(oneopt)) == -1 ||
Simon Kelleyc72daea2012-01-05 21:33:27 +000057 !fix_fd(fd) ||
58 !set_ipv6pktinfo(fd))
59 die (_("cannot create DHCPv6 socket: %s"), NULL, EC_BADNET);
60
Simon Kelley56a11422013-04-02 17:02:58 +010061 /* When bind-interfaces is set, there might be more than one dnmsasq
Simon Kelley20223102012-10-15 10:41:17 +010062 instance binding port 547. That's OK if they serve different networks.
Simon Kelley56a11422013-04-02 17:02:58 +010063 Need to set REUSEADDR|REUSEPORT to make this posible.
64 Handle the case that REUSEPORT is defined, but the kernel doesn't
65 support it. This handles the introduction of REUSEPORT on Linux. */
Simon Kelley20223102012-10-15 10:41:17 +010066 if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND))
67 {
Simon Kelleyffbad342013-08-14 15:53:57 +010068 int rc = 0;
Simon Kelley56a11422013-04-02 17:02:58 +010069
Simon Kelley20223102012-10-15 10:41:17 +010070#ifdef SO_REUSEPORT
Simon Kelley56a11422013-04-02 17:02:58 +010071 if ((rc = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &oneopt, sizeof(oneopt))) == -1 &&
Simon Kelleyffbad342013-08-14 15:53:57 +010072 errno == ENOPROTOOPT)
73 rc = 0;
Simon Kelley20223102012-10-15 10:41:17 +010074#endif
Simon Kelley56a11422013-04-02 17:02:58 +010075
Simon Kelleyffbad342013-08-14 15:53:57 +010076 if (rc != -1)
Simon Kelley56a11422013-04-02 17:02:58 +010077 rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt));
78
Simon Kelley20223102012-10-15 10:41:17 +010079 if (rc == -1)
80 die(_("failed to set SO_REUSE{ADDR|PORT} on DHCPv6 socket: %s"), NULL, EC_BADNET);
81 }
82
Simon Kelleyc72daea2012-01-05 21:33:27 +000083 memset(&saddr, 0, sizeof(saddr));
84#ifdef HAVE_SOCKADDR_SA_LEN
Simon Kelley4cb1b322012-02-06 14:30:41 +000085 saddr.sin6_len = sizeof(struct sockaddr_in6);
Simon Kelleyc72daea2012-01-05 21:33:27 +000086#endif
87 saddr.sin6_family = AF_INET6;
88 saddr.sin6_addr = in6addr_any;
89 saddr.sin6_port = htons(DHCPV6_SERVER_PORT);
90
91 if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in6)))
92 die(_("failed to bind DHCPv6 server socket: %s"), NULL, EC_BADNET);
93
Simon Kelleyc72daea2012-01-05 21:33:27 +000094 daemon->dhcp6fd = fd;
Simon Kelleyc72daea2012-01-05 21:33:27 +000095}
96
Simon Kelleyc72daea2012-01-05 21:33:27 +000097void dhcp6_packet(time_t now)
98{
99 struct dhcp_context *context;
Simon Kelleyff7eea22013-09-04 18:01:38 +0100100 struct dhcp_relay *relay;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000101 struct iface_param parm;
102 struct cmsghdr *cmptr;
103 struct msghdr msg;
104 int if_index = 0;
105 union {
106 struct cmsghdr align; /* this ensures alignment */
107 char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
108 } control_u;
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000109 struct sockaddr_in6 from;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000110 ssize_t sz;
111 struct ifreq ifr;
112 struct iname *tmp;
Simon Kelley1d0f91c2012-03-12 11:56:22 +0000113 unsigned short port;
Simon Kelley89500e32013-09-20 16:29:20 +0100114 struct in6_addr dst_addr;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000115
Simon Kelley8f51a292013-09-21 14:07:12 +0100116 memset(&dst_addr, 0, sizeof(dst_addr));
117
Simon Kelleyc72daea2012-01-05 21:33:27 +0000118 msg.msg_control = control_u.control6;
119 msg.msg_controllen = sizeof(control_u);
120 msg.msg_flags = 0;
121 msg.msg_name = &from;
122 msg.msg_namelen = sizeof(from);
123 msg.msg_iov = &daemon->dhcp_packet;
124 msg.msg_iovlen = 1;
125
Simon Kelley1d0f91c2012-03-12 11:56:22 +0000126 if ((sz = recv_dhcp_packet(daemon->dhcp6fd, &msg)) == -1)
Simon Kelleyc72daea2012-01-05 21:33:27 +0000127 return;
128
129 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
130 if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
131 {
132 union {
133 unsigned char *c;
134 struct in6_pktinfo *p;
135 } p;
136 p.c = CMSG_DATA(cmptr);
137
138 if_index = p.p->ipi6_ifindex;
Simon Kelley89500e32013-09-20 16:29:20 +0100139 dst_addr = p.p->ipi6_addr;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000140 }
141
142 if (!indextoname(daemon->dhcp6fd, if_index, ifr.ifr_name))
143 return;
Simon Kelleybe6cfb42012-10-16 20:38:31 +0100144
Simon Kelleyff7eea22013-09-04 18:01:38 +0100145 if ((port = relay_reply6(&from, sz, ifr.ifr_name)) == 0)
Simon Kelleybe6cfb42012-10-16 20:38:31 +0100146 {
Simon Kelleyff7eea22013-09-04 18:01:38 +0100147 for (tmp = daemon->if_except; tmp; tmp = tmp->next)
Simon Kelley49333cb2013-03-15 20:30:51 +0000148 if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
Simon Kelleyff7eea22013-09-04 18:01:38 +0100149 return;
150
151 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
152 if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
153 return;
154
155 parm.current = NULL;
156 parm.relay = NULL;
157 memset(&parm.relay_local, 0, IN6ADDRSZ);
158 parm.ind = if_index;
159 parm.addr_match = 0;
160 memset(&parm.fallback, 0, IN6ADDRSZ);
161
162 for (context = daemon->dhcp6; context; context = context->next)
163 if (IN6_IS_ADDR_UNSPECIFIED(&context->start6) && context->prefix == 0)
164 {
165 /* wildcard context for DHCP-stateless only */
166 parm.current = context;
167 context->current = NULL;
168 }
169 else
170 {
171 /* unlinked contexts are marked by context->current == context */
172 context->current = context;
173 memset(&context->local6, 0, IN6ADDRSZ);
174 }
Simon Kelleybe6cfb42012-10-16 20:38:31 +0100175
Simon Kelleyff7eea22013-09-04 18:01:38 +0100176 for (relay = daemon->relay6; relay; relay = relay->next)
177 relay->current = relay;
178
179 if (!iface_enumerate(AF_INET6, &parm, complete_context6))
Simon Kelleybe6cfb42012-10-16 20:38:31 +0100180 return;
Simon Kelley89500e32013-09-20 16:29:20 +0100181
Simon Kelleyff7eea22013-09-04 18:01:38 +0100182 if (daemon->if_names || daemon->if_addrs)
183 {
184
185 for (tmp = daemon->if_names; tmp; tmp = tmp->next)
186 if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
187 break;
188
189 if (!tmp && !parm.addr_match)
190 return;
191 }
192
193 if (parm.relay)
194 {
Simon Kelley89500e32013-09-20 16:29:20 +0100195 /* Ignore requests sent to the ALL_SERVERS multicast address for relay when
196 we're listening there for DHCPv6 server reasons. */
197 struct in6_addr all_servers;
198
199 inet_pton(AF_INET6, ALL_SERVERS, &all_servers);
200
201 if (!IN6_ARE_ADDR_EQUAL(&dst_addr, &all_servers))
Simon Kelley8939c952013-09-25 11:49:34 +0100202 relay_upstream6(parm.relay, sz, &from.sin6_addr, from.sin6_scope_id);
Simon Kelleyff7eea22013-09-04 18:01:38 +0100203 return;
204 }
205
206 /* May have configured relay, but not DHCP server */
207 if (!daemon->doing_dhcp6)
208 return;
209
210 lease_prune(NULL, now); /* lose any expired leases */
211
212 port = dhcp6_reply(parm.current, if_index, ifr.ifr_name, &parm.fallback,
Simon Kelley8939c952013-09-25 11:49:34 +0100213 sz, &from.sin6_addr, now);
Simon Kelleyff7eea22013-09-04 18:01:38 +0100214
215 lease_update_file(now);
216 lease_update_dns(0);
Simon Kelleybe6cfb42012-10-16 20:38:31 +0100217 }
Simon Kelleyff7eea22013-09-04 18:01:38 +0100218
Simon Kelley1d0f91c2012-03-12 11:56:22 +0000219 /* The port in the source address of the original request should
220 be correct, but at least once client sends from the server port,
221 so we explicitly send to the client port to a client, and the
222 server port to a relay. */
223 if (port != 0)
224 {
225 from.sin6_port = htons(port);
226 while (sendto(daemon->dhcp6fd, daemon->outpacket.iov_base, save_counter(0),
227 0, (struct sockaddr *)&from, sizeof(from)) == -1 &&
Simon Kelley4cb1b322012-02-06 14:30:41 +0000228 retry_send());
Simon Kelley1d0f91c2012-03-12 11:56:22 +0000229 }
Simon Kelleyc72daea2012-01-05 21:33:27 +0000230}
231
Simon Kelley8939c952013-09-25 11:49:34 +0100232void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, unsigned int *maclenp, unsigned int *mactypep)
233{
234 /* Recieving a packet from a host does not populate the neighbour
235 cache, so we send a neighbour discovery request if we can't
236 find the sender. Repeat a few times in case of packet loss. */
237
238 struct neigh_packet neigh;
239 struct sockaddr_in6 addr;
240 struct mac_param mac_param;
241 int i;
242
243 neigh.type = ND_NEIGHBOR_SOLICIT;
244 neigh.code = 0;
245 neigh.reserved = 0;
246 neigh.target = *client;
247
248 memset(&addr, 0, sizeof(addr));
249#ifdef HAVE_SOCKADDR_SA_LEN
250 addr.sin6_len = sizeof(struct sockaddr_in6);
251#endif
252 addr.sin6_family = AF_INET6;
253 addr.sin6_port = htons(IPPROTO_ICMPV6);
254 addr.sin6_addr = *client;
255 addr.sin6_scope_id = iface;
256
257 mac_param.target = client;
258 mac_param.maclen = 0;
259 mac_param.mac = mac;
260
261 for (i = 0; i < 5; i++)
262 {
263 struct timespec ts;
264
265 iface_enumerate(AF_UNSPEC, &mac_param, find_mac);
266
267 if (mac_param.maclen != 0)
268 break;
269
270 sendto(daemon->icmp6fd, &neigh, sizeof(neigh), 0, (struct sockaddr *)&addr, sizeof(addr));
271
272 ts.tv_sec = 0;
273 ts.tv_nsec = 100000000; /* 100ms */
274 nanosleep(&ts, NULL);
275 }
276
277 *maclenp = mac_param.maclen;
278 *mactypep = ARPHRD_ETHER;
279}
280
Simon Kelley89500e32013-09-20 16:29:20 +0100281static int find_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv)
282{
283 struct mac_param *parm = parmv;
284
Simon Kelley1b551902013-09-23 15:03:05 +0100285 if (family == AF_INET6 && IN6_ARE_ADDR_EQUAL(parm->target, (struct in6_addr *)addrp))
Simon Kelley89500e32013-09-20 16:29:20 +0100286 {
287 if (maclen <= DHCP_CHADDR_MAX)
288 {
289 parm->maclen = maclen;
290 memcpy(parm->mac, mac, maclen);
291 }
292
293 return 0; /* found, abort */
294 }
295
296 return 1;
297}
298
Simon Kelleyc72daea2012-01-05 21:33:27 +0000299static int complete_context6(struct in6_addr *local, int prefix,
Simon Kelley3bc0d932012-12-28 11:31:44 +0000300 int scope, int if_index, int flags, unsigned int preferred,
301 unsigned int valid, void *vparam)
Simon Kelleyc72daea2012-01-05 21:33:27 +0000302{
303 struct dhcp_context *context;
Simon Kelleyff7eea22013-09-04 18:01:38 +0100304 struct dhcp_relay *relay;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000305 struct iface_param *param = vparam;
Simon Kelleybe6cfb42012-10-16 20:38:31 +0100306 struct iname *tmp;
307
Simon Kelley52b92f42012-01-22 16:05:15 +0000308 (void)scope; /* warning */
Simon Kelley3bc0d932012-12-28 11:31:44 +0000309
Simon Kelleyff7eea22013-09-04 18:01:38 +0100310 if (if_index == param->ind)
Simon Kelleyc72daea2012-01-05 21:33:27 +0000311 {
Simon Kelleyff7eea22013-09-04 18:01:38 +0100312 if (!IN6_IS_ADDR_LOOPBACK(local) &&
313 !IN6_IS_ADDR_LINKLOCAL(local) &&
314 !IN6_IS_ADDR_MULTICAST(local))
Simon Kelleye44ddca2012-02-18 17:08:50 +0000315 {
Simon Kelleyff7eea22013-09-04 18:01:38 +0100316 /* if we have --listen-address config, see if the
317 arrival interface has a matching address. */
318 for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
319 if (tmp->addr.sa.sa_family == AF_INET6 &&
320 IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, local))
321 param->addr_match = 1;
322
323 /* Determine a globally address on the arrival interface, even
324 if we have no matching dhcp-context, because we're only
325 allocating on remote subnets via relays. This
326 is used as a default for the DNS server option. */
327 param->fallback = *local;
328
329 for (context = daemon->dhcp6; context; context = context->next)
Simon Kelleye44ddca2012-02-18 17:08:50 +0000330 {
Simon Kelleyff7eea22013-09-04 18:01:38 +0100331 if ((context->flags & CONTEXT_DHCP) &&
332 !(context->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) &&
Vladislav Grishenko4c82efc2013-12-03 16:05:30 +0000333 prefix <= context->prefix &&
334 is_same_net6(local, &context->start6, context->prefix) &&
335 is_same_net6(local, &context->end6, context->prefix))
Simon Kelleye44ddca2012-02-18 17:08:50 +0000336 {
Simon Kelley3bc0d932012-12-28 11:31:44 +0000337
Simon Kelley3bc0d932012-12-28 11:31:44 +0000338
Simon Kelleyff7eea22013-09-04 18:01:38 +0100339 /* link it onto the current chain if we've not seen it before */
340 if (context->current == context)
341 {
342 struct dhcp_context *tmp, **up;
343
344 /* use interface values only for contructed contexts */
345 if (!(context->flags & CONTEXT_CONSTRUCTED))
346 preferred = valid = 0xffffffff;
347 else if (flags & IFACE_DEPRECATED)
348 preferred = 0;
349
350 if (context->flags & CONTEXT_DEPRECATE)
351 preferred = 0;
352
353 /* order chain, longest preferred time first */
354 for (up = &param->current, tmp = param->current; tmp; tmp = tmp->current)
355 if (tmp->preferred <= preferred)
356 break;
357 else
358 up = &tmp->current;
359
360 context->current = *up;
361 *up = context;
362 context->local6 = *local;
363 context->preferred = preferred;
364 context->valid = valid;
365 }
Simon Kelleye44ddca2012-02-18 17:08:50 +0000366 }
367 }
Simon Kelleyc72daea2012-01-05 21:33:27 +0000368 }
Simon Kelleyff7eea22013-09-04 18:01:38 +0100369
370 for (relay = daemon->relay6; relay; relay = relay->next)
371 if (IN6_ARE_ADDR_EQUAL(local, &relay->local.addr.addr6) && relay->current == relay &&
372 (IN6_IS_ADDR_UNSPECIFIED(&param->relay_local) || IN6_ARE_ADDR_EQUAL(local, &param->relay_local)))
373 {
374 relay->current = param->relay;
375 param->relay = relay;
376 param->relay_local = *local;
377 }
378
Simon Kelleyc72daea2012-01-05 21:33:27 +0000379 }
Simon Kelleyff7eea22013-09-04 18:01:38 +0100380
381 return 1;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000382}
Simon Kelley52b92f42012-01-22 16:05:15 +0000383
384struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct in6_addr *net, int prefix, u64 addr)
385{
386 struct dhcp_config *config;
387
388 for (config = configs; config; config = config->next)
389 if ((config->flags & CONFIG_ADDR6) &&
390 is_same_net6(&config->addr6, net, prefix) &&
391 (prefix == 128 || addr6part(&config->addr6) == addr))
392 return config;
393
394 return NULL;
395}
396
Simon Kelley6586e832013-11-07 14:20:13 +0000397struct dhcp_context *address6_allocate(struct dhcp_context *context, unsigned char *clid, int clid_len, int temp_addr,
Simon Kelleyc6309242013-03-07 20:59:28 +0000398 int iaid, int serial, struct dhcp_netid *netids, int plain_range, struct in6_addr *ans)
Simon Kelley52b92f42012-01-22 16:05:15 +0000399{
400 /* Find a free address: exclude anything in use and anything allocated to
401 a particular hwaddr/clientid/hostname in our configuration.
402 Try to return from contexts which match netids first.
403
404 Note that we assume the address prefix lengths are 64 or greater, so we can
405 get by with 64 bit arithmetic.
406*/
407
408 u64 start, addr;
409 struct dhcp_context *c, *d;
410 int i, pass;
411 u64 j;
412
413 /* hash hwaddr: use the SDBM hashing algorithm. This works
Simon Kelley6586e832013-11-07 14:20:13 +0000414 for MAC addresses, let's see how it manages with client-ids!
415 For temporary addresses, we generate a new random one each time. */
416 if (temp_addr)
417 j = rand64();
418 else
419 for (j = iaid, i = 0; i < clid_len; i++)
420 j += clid[i] + (j << 6) + (j << 16) - j;
Simon Kelley52b92f42012-01-22 16:05:15 +0000421
Simon Kelleyc6309242013-03-07 20:59:28 +0000422 for (pass = 0; pass <= plain_range ? 1 : 0; pass++)
Simon Kelley52b92f42012-01-22 16:05:15 +0000423 for (c = context; c; c = c->current)
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000424 if (c->flags & (CONTEXT_DEPRECATE | CONTEXT_STATIC | CONTEXT_RA_STATELESS | CONTEXT_USED))
Simon Kelley52b92f42012-01-22 16:05:15 +0000425 continue;
426 else if (!match_netid(c->filter, netids, pass))
427 continue;
428 else
Simon Kelley07933802012-02-14 20:55:25 +0000429 {
Simon Kelley6586e832013-11-07 14:20:13 +0000430 if (!temp_addr && option_bool(OPT_CONSEC_ADDR))
Simon Kelley07933802012-02-14 20:55:25 +0000431 /* seed is largest extant lease addr in this context */
Simon Kelleyc6309242013-03-07 20:59:28 +0000432 start = lease_find_max_addr6(c) + serial;
Simon Kelley07933802012-02-14 20:55:25 +0000433 else
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000434 start = addr6part(&c->start6) + ((j + c->addr_epoch) % (1 + addr6part(&c->end6) - addr6part(&c->start6)));
Simon Kelley52b92f42012-01-22 16:05:15 +0000435
436 /* iterate until we find a free address. */
437 addr = start;
438
439 do {
440 /* eliminate addresses in use by the server. */
441 for (d = context; d; d = d->current)
Simon Kelleye44ddca2012-02-18 17:08:50 +0000442 if (addr == addr6part(&d->local6))
Simon Kelley52b92f42012-01-22 16:05:15 +0000443 break;
444
445 if (!d &&
446 !lease6_find_by_addr(&c->start6, c->prefix, addr) &&
447 !config_find_by_address6(daemon->dhcp_conf, &c->start6, c->prefix, addr))
448 {
449 *ans = c->start6;
450 setaddr6part (ans, addr);
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000451 return c;
Simon Kelley52b92f42012-01-22 16:05:15 +0000452 }
453
454 addr++;
455
456 if (addr == addr6part(&c->end6) + 1)
457 addr = addr6part(&c->start6);
458
459 } while (addr != start);
460 }
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000461
Simon Kelley5b37aa82013-04-02 16:32:25 +0100462 return NULL;
Simon Kelley52b92f42012-01-22 16:05:15 +0000463}
464
Simon Kelley37c9cce2013-01-09 19:51:04 +0000465/* can dynamically allocate addr */
Simon Kelley52b92f42012-01-22 16:05:15 +0000466struct dhcp_context *address6_available(struct dhcp_context *context,
467 struct in6_addr *taddr,
Simon Kelleyc6309242013-03-07 20:59:28 +0000468 struct dhcp_netid *netids,
469 int plain_range)
Simon Kelley52b92f42012-01-22 16:05:15 +0000470{
471 u64 start, end, addr = addr6part(taddr);
472 struct dhcp_context *tmp;
473
474 for (tmp = context; tmp; tmp = tmp->current)
475 {
476 start = addr6part(&tmp->start6);
477 end = addr6part(&tmp->end6);
478
Simon Kelleyc8257542012-03-28 21:15:41 +0100479 if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_RA_STATELESS)) &&
480 is_same_net6(&tmp->start6, taddr, tmp->prefix) &&
481 is_same_net6(&tmp->end6, taddr, tmp->prefix) &&
Simon Kelley52b92f42012-01-22 16:05:15 +0000482 addr >= start &&
483 addr <= end &&
Simon Kelleyc6309242013-03-07 20:59:28 +0000484 match_netid(tmp->filter, netids, plain_range))
Simon Kelley52b92f42012-01-22 16:05:15 +0000485 return tmp;
486 }
487
488 return NULL;
489}
490
Simon Kelley37c9cce2013-01-09 19:51:04 +0000491/* address OK if configured */
492struct dhcp_context *address6_valid(struct dhcp_context *context,
Simon Kelleyc6309242013-03-07 20:59:28 +0000493 struct in6_addr *taddr,
494 struct dhcp_netid *netids,
495 int plain_range)
Simon Kelley37c9cce2013-01-09 19:51:04 +0000496{
497 struct dhcp_context *tmp;
498
499 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelleybaeb3ad2013-01-10 11:47:38 +0000500 if (is_same_net6(&tmp->start6, taddr, tmp->prefix) &&
Simon Kelleyc6309242013-03-07 20:59:28 +0000501 match_netid(tmp->filter, netids, plain_range))
Simon Kelley37c9cce2013-01-09 19:51:04 +0000502 return tmp;
503
504 return NULL;
505}
506
Simon Kelleyde92b472013-03-15 18:25:10 +0000507int config_valid(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr)
Simon Kelley4cb1b322012-02-06 14:30:41 +0000508{
Simon Kelleyde92b472013-03-15 18:25:10 +0000509 if (!config || !(config->flags & CONFIG_ADDR6))
510 return 0;
511
512 if ((config->flags & CONFIG_WILDCARD) && context->prefix == 64)
Simon Kelley30393102013-01-17 16:34:16 +0000513 {
Simon Kelleyde92b472013-03-15 18:25:10 +0000514 *addr = context->start6;
515 setaddr6part(addr, addr6part(&config->addr6));
Simon Kelley30393102013-01-17 16:34:16 +0000516 return 1;
517 }
518
Simon Kelleyde92b472013-03-15 18:25:10 +0000519 if (is_same_net6(&context->start6, &config->addr6, context->prefix))
520 {
521 *addr = config->addr6;
522 return 1;
523 }
524
Simon Kelleyb2692212012-09-16 22:15:56 +0100525 return 0;
526}
527
Simon Kelley4cb1b322012-02-06 14:30:41 +0000528void make_duid(time_t now)
529{
Simon Kelley3511a922013-11-07 10:28:11 +0000530 (void)now;
531
Simon Kelley8b372702012-03-09 17:45:10 +0000532 if (daemon->duid_config)
533 {
534 unsigned char *p;
535
536 daemon->duid = p = safe_malloc(daemon->duid_config_len + 6);
537 daemon->duid_len = daemon->duid_config_len + 6;
538 PUTSHORT(2, p); /* DUID_EN */
539 PUTLONG(daemon->duid_enterprise, p);
540 memcpy(p, daemon->duid_config, daemon->duid_config_len);
541 }
542 else
543 {
Simon Kelley3511a922013-11-07 10:28:11 +0000544 time_t newnow = 0;
545
546 /* If we have no persistent lease database, or a non-stable RTC, use DUID_LL (newnow == 0) */
547#ifndef HAVE_BROKEN_RTC
Simon Kelley8b372702012-03-09 17:45:10 +0000548 /* rebase epoch to 1/1/2000 */
Simon Kelley3511a922013-11-07 10:28:11 +0000549 if (!option_bool(OPT_LEASE_RO) || daemon->lease_change_command)
550 newnow = now - 946684800;
551#endif
Simon Kelley8b372702012-03-09 17:45:10 +0000552
553 iface_enumerate(AF_LOCAL, &newnow, make_duid1);
554
555 if(!daemon->duid)
556 die("Cannot create DHCPv6 server DUID: %s", NULL, EC_MISC);
557 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000558}
559
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000560static int make_duid1(int index, unsigned int type, char *mac, size_t maclen, void *parm)
Simon Kelley4cb1b322012-02-06 14:30:41 +0000561{
562 /* create DUID as specified in RFC3315. We use the MAC of the
Simon Kelley0f089832012-03-01 13:43:39 +0000563 first interface we find that isn't loopback or P-to-P and
564 has address-type < 256. Address types above 256 are things like
565 tunnels which don't have usable MAC addresses. */
Simon Kelley4cb1b322012-02-06 14:30:41 +0000566
567 unsigned char *p;
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000568 (void)index;
Vladislav Grishenko408c3682013-09-24 16:18:49 +0100569 (void)parm;
Simon Kelley3511a922013-11-07 10:28:11 +0000570 time_t newnow = *((time_t *)parm);
571
Simon Kelley0f089832012-03-01 13:43:39 +0000572 if (type >= 256)
573 return 1;
574
Simon Kelley3511a922013-11-07 10:28:11 +0000575 if (newnow == 0)
576 {
577 daemon->duid = p = safe_malloc(maclen + 4);
578 daemon->duid_len = maclen + 4;
579 PUTSHORT(3, p); /* DUID_LL */
580 PUTSHORT(type, p); /* address type */
581 }
582 else
583 {
584 daemon->duid = p = safe_malloc(maclen + 8);
585 daemon->duid_len = maclen + 8;
586 PUTSHORT(1, p); /* DUID_LLT */
587 PUTSHORT(type, p); /* address type */
588 PUTLONG(*((time_t *)parm), p); /* time */
589 }
590
Simon Kelley4cb1b322012-02-06 14:30:41 +0000591 memcpy(p, mac, maclen);
592
593 return 0;
594}
Simon Kelley1f776932012-12-16 19:46:08 +0000595
596struct cparam {
597 time_t now;
Simon Kelley0c050242012-12-22 22:13:19 +0000598 int newone, newname;
Simon Kelley1f776932012-12-16 19:46:08 +0000599};
600
601static int construct_worker(struct in6_addr *local, int prefix,
Simon Kelleybad7b872012-12-20 22:00:39 +0000602 int scope, int if_index, int flags,
Simon Kelley1f776932012-12-16 19:46:08 +0000603 int preferred, int valid, void *vparam)
604{
605 char ifrn_name[IFNAMSIZ];
606 struct in6_addr start6, end6;
607 struct dhcp_context *template, *context;
608
609 (void)scope;
Simon Kelleybad7b872012-12-20 22:00:39 +0000610 (void)flags;
Simon Kelleyed8b68a2012-12-21 16:23:26 +0000611 (void)valid;
612 (void)preferred;
Simon Kelley1f776932012-12-16 19:46:08 +0000613
614 struct cparam *param = vparam;
615
616 if (IN6_IS_ADDR_LOOPBACK(local) ||
617 IN6_IS_ADDR_LINKLOCAL(local) ||
618 IN6_IS_ADDR_MULTICAST(local))
619 return 1;
620
Simon Kelley861c8912013-09-25 15:30:30 +0100621 if (!(flags & IFACE_PERMANENT))
622 return 1;
623
624 if (flags & IFACE_DEPRECATED)
625 return 1;
626
Simon Kelleya8105592013-09-25 15:36:00 +0100627 if (!indextoname(daemon->icmp6fd, if_index, ifrn_name))
Simon Kelley1f776932012-12-16 19:46:08 +0000628 return 0;
629
630 for (template = daemon->dhcp6; template; template = template->next)
631 if (!(template->flags & CONTEXT_TEMPLATE))
632 {
633 /* non-template entries, just fill in interface and local addresses */
Vladislav Grishenko4c82efc2013-12-03 16:05:30 +0000634 if (prefix <= template->prefix &&
635 is_same_net6(local, &template->start6, template->prefix) &&
636 is_same_net6(local, &template->end6, template->prefix))
Simon Kelley1f776932012-12-16 19:46:08 +0000637 {
638 template->if_index = if_index;
639 template->local6 = *local;
640 }
641
642 }
Simon Kelley486479e2013-10-14 17:18:03 +0100643 else if (wildcard_match(template->template_interface, ifrn_name) &&
Vladislav Grishenko4c82efc2013-12-03 16:05:30 +0000644 template->prefix >= prefix)
Simon Kelley1f776932012-12-16 19:46:08 +0000645 {
646 start6 = *local;
647 setaddr6part(&start6, addr6part(&template->start6));
648 end6 = *local;
649 setaddr6part(&end6, addr6part(&template->end6));
650
651 for (context = daemon->dhcp6; context; context = context->next)
652 if ((context->flags & CONTEXT_CONSTRUCTED) &&
653 IN6_ARE_ADDR_EQUAL(&start6, &context->start6) &&
654 IN6_ARE_ADDR_EQUAL(&end6, &context->end6))
655 {
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100656 int flags = context->flags;
657 context->flags &= ~(CONTEXT_GC | CONTEXT_OLD);
658 if (flags & CONTEXT_OLD)
Simon Kelleyf7a40ec2013-07-27 13:36:08 +0100659 {
660 /* address went, now it's back */
661 log_context(AF_INET6, context);
662 /* fast RAs for a while */
663 ra_start_unsolicted(param->now, context);
Simon Kelley0d6eb132013-11-26 13:30:12 +0000664 param->newone = 1;
Simon Kelleyf7a40ec2013-07-27 13:36:08 +0100665 /* Add address to name again */
666 if (context->flags & CONTEXT_RA_NAME)
667 param->newname = 1;
668 }
Simon Kelley1f776932012-12-16 19:46:08 +0000669 break;
670 }
671
672 if (!context && (context = whine_malloc(sizeof (struct dhcp_context))))
673 {
674 *context = *template;
675 context->start6 = start6;
676 context->end6 = end6;
677 context->flags &= ~CONTEXT_TEMPLATE;
678 context->flags |= CONTEXT_CONSTRUCTED;
679 context->if_index = if_index;
680 context->local6 = *local;
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100681 context->saved_valid = 0;
Simon Kelley1f776932012-12-16 19:46:08 +0000682
683 context->next = daemon->dhcp6;
684 daemon->dhcp6 = context;
685
Simon Kelley1b75c1e2012-12-18 19:55:25 +0000686 ra_start_unsolicted(param->now, context);
Simon Kelley1f776932012-12-16 19:46:08 +0000687 /* we created a new one, need to call
688 lease_update_file to get periodic functions called */
689 param->newone = 1;
Simon Kelley0c050242012-12-22 22:13:19 +0000690
691 /* Will need to add new putative SLAAC addresses to existing leases */
692 if (context->flags & CONTEXT_RA_NAME)
693 param->newname = 1;
Simon Kelley1f776932012-12-16 19:46:08 +0000694
695 log_context(AF_INET6, context);
696 }
Simon Kelley1f776932012-12-16 19:46:08 +0000697 }
698
699 return 1;
700}
701
702void dhcp_construct_contexts(time_t now)
703{
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100704 struct dhcp_context *context, *tmp, **up;
Simon Kelley1f776932012-12-16 19:46:08 +0000705 struct cparam param;
706 param.newone = 0;
Simon Kelley0c050242012-12-22 22:13:19 +0000707 param.newname = 0;
Simon Kelley1f776932012-12-16 19:46:08 +0000708 param.now = now;
709
710 for (context = daemon->dhcp6; context; context = context->next)
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100711 if (context->flags & CONTEXT_CONSTRUCTED)
712 context->flags |= CONTEXT_GC;
713
Simon Kelley1f776932012-12-16 19:46:08 +0000714 iface_enumerate(AF_INET6, &param, construct_worker);
715
716 for (up = &daemon->dhcp6, context = daemon->dhcp6; context; context = tmp)
717 {
Simon Kelley1f776932012-12-16 19:46:08 +0000718
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100719 tmp = context->next;
720
721 if (context->flags & CONTEXT_GC && !(context->flags & CONTEXT_OLD))
Simon Kelley1f776932012-12-16 19:46:08 +0000722 {
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100723 if ((context->flags & (CONTEXT_RA_ONLY | CONTEXT_RA_NAME | CONTEXT_RA_STATELESS)) ||
724 option_bool(OPT_RA))
725 {
726 /* previously constructed context has gone. advertise it's demise */
727 context->flags |= CONTEXT_OLD;
728 context->address_lost_time = now;
Simon Kelley9f48ffa2013-07-28 15:47:04 +0100729 /* Apply same ceiling of configured lease time as in radv.c */
730 if (context->saved_valid > context->lease_time)
731 context->saved_valid = context->lease_time;
732 /* maximum time is 2 hours, from RFC */
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100733 if (context->saved_valid > 7200) /* 2 hours */
734 context->saved_valid = 7200;
735 ra_start_unsolicted(now, context);
736 param.newone = 1; /* include deletion */
737
738 if (context->flags & CONTEXT_RA_NAME)
739 param.newname = 1;
740
741 log_context(AF_INET6, context);
742
743 up = &context->next;
744 }
745 else
746 {
747 /* we were never doing RA for this, so free now */
748 *up = context->next;
749 free(context);
750 }
Simon Kelley1f776932012-12-16 19:46:08 +0000751 }
752 else
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100753 up = &context->next;
Simon Kelley1f776932012-12-16 19:46:08 +0000754 }
755
756 if (param.newone)
Simon Kelley7558ecd2012-12-16 21:45:16 +0000757 {
758 if (daemon->dhcp || daemon->doing_dhcp6)
Simon Kelley0c050242012-12-22 22:13:19 +0000759 {
760 if (param.newname)
761 lease_update_slaac(now);
762 lease_update_file(now);
763 }
Simon Kelley7558ecd2012-12-16 21:45:16 +0000764 else
765 /* Not doing DHCP, so no lease system, manage alarms for ra only */
766 send_alarm(periodic_ra(now), now);
767 }
Simon Kelley1f776932012-12-16 19:46:08 +0000768}
Simon Kelley1f776932012-12-16 19:46:08 +0000769
Simon Kelleyc72daea2012-01-05 21:33:27 +0000770#endif
771
772