blob: bc48fdddd3de6eaca7f63d4f9e00ba0781ed01a5 [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;
Simon Kelleyc3a04082014-01-11 22:18:19 +000026 struct in6_addr fallback, relay_local, ll_addr, ula_addr;
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);
Simon Kelleyc3a04082014-01-11 22:18:19 +0000161 memset(&parm.ll_addr, 0, IN6ADDRSZ);
162 memset(&parm.ula_addr, 0, IN6ADDRSZ);
Simon Kelleyff7eea22013-09-04 18:01:38 +0100163
164 for (context = daemon->dhcp6; context; context = context->next)
165 if (IN6_IS_ADDR_UNSPECIFIED(&context->start6) && context->prefix == 0)
166 {
167 /* wildcard context for DHCP-stateless only */
168 parm.current = context;
169 context->current = NULL;
170 }
171 else
172 {
173 /* unlinked contexts are marked by context->current == context */
174 context->current = context;
175 memset(&context->local6, 0, IN6ADDRSZ);
176 }
Simon Kelleybe6cfb42012-10-16 20:38:31 +0100177
Simon Kelleyff7eea22013-09-04 18:01:38 +0100178 for (relay = daemon->relay6; relay; relay = relay->next)
179 relay->current = relay;
180
181 if (!iface_enumerate(AF_INET6, &parm, complete_context6))
Simon Kelleybe6cfb42012-10-16 20:38:31 +0100182 return;
Simon Kelley89500e32013-09-20 16:29:20 +0100183
Simon Kelleyff7eea22013-09-04 18:01:38 +0100184 if (daemon->if_names || daemon->if_addrs)
185 {
186
187 for (tmp = daemon->if_names; tmp; tmp = tmp->next)
188 if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
189 break;
190
191 if (!tmp && !parm.addr_match)
192 return;
193 }
194
195 if (parm.relay)
196 {
Simon Kelley89500e32013-09-20 16:29:20 +0100197 /* Ignore requests sent to the ALL_SERVERS multicast address for relay when
198 we're listening there for DHCPv6 server reasons. */
199 struct in6_addr all_servers;
200
201 inet_pton(AF_INET6, ALL_SERVERS, &all_servers);
202
203 if (!IN6_ARE_ADDR_EQUAL(&dst_addr, &all_servers))
Simon Kelley8939c952013-09-25 11:49:34 +0100204 relay_upstream6(parm.relay, sz, &from.sin6_addr, from.sin6_scope_id);
Simon Kelleyff7eea22013-09-04 18:01:38 +0100205 return;
206 }
207
208 /* May have configured relay, but not DHCP server */
209 if (!daemon->doing_dhcp6)
210 return;
211
212 lease_prune(NULL, now); /* lose any expired leases */
213
214 port = dhcp6_reply(parm.current, if_index, ifr.ifr_name, &parm.fallback,
Simon Kelleyc3a04082014-01-11 22:18:19 +0000215 &parm.ll_addr, &parm.ula_addr, sz, &from.sin6_addr, now);
Simon Kelleyff7eea22013-09-04 18:01:38 +0100216
217 lease_update_file(now);
218 lease_update_dns(0);
Simon Kelleybe6cfb42012-10-16 20:38:31 +0100219 }
Simon Kelleyff7eea22013-09-04 18:01:38 +0100220
Simon Kelley1d0f91c2012-03-12 11:56:22 +0000221 /* The port in the source address of the original request should
222 be correct, but at least once client sends from the server port,
223 so we explicitly send to the client port to a client, and the
224 server port to a relay. */
225 if (port != 0)
226 {
227 from.sin6_port = htons(port);
228 while (sendto(daemon->dhcp6fd, daemon->outpacket.iov_base, save_counter(0),
229 0, (struct sockaddr *)&from, sizeof(from)) == -1 &&
Simon Kelley4cb1b322012-02-06 14:30:41 +0000230 retry_send());
Simon Kelley1d0f91c2012-03-12 11:56:22 +0000231 }
Simon Kelleyc72daea2012-01-05 21:33:27 +0000232}
233
Simon Kelley8939c952013-09-25 11:49:34 +0100234void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, unsigned int *maclenp, unsigned int *mactypep)
235{
236 /* Recieving a packet from a host does not populate the neighbour
237 cache, so we send a neighbour discovery request if we can't
238 find the sender. Repeat a few times in case of packet loss. */
239
240 struct neigh_packet neigh;
241 struct sockaddr_in6 addr;
242 struct mac_param mac_param;
243 int i;
244
245 neigh.type = ND_NEIGHBOR_SOLICIT;
246 neigh.code = 0;
247 neigh.reserved = 0;
248 neigh.target = *client;
249
250 memset(&addr, 0, sizeof(addr));
251#ifdef HAVE_SOCKADDR_SA_LEN
252 addr.sin6_len = sizeof(struct sockaddr_in6);
253#endif
254 addr.sin6_family = AF_INET6;
255 addr.sin6_port = htons(IPPROTO_ICMPV6);
256 addr.sin6_addr = *client;
257 addr.sin6_scope_id = iface;
258
259 mac_param.target = client;
260 mac_param.maclen = 0;
261 mac_param.mac = mac;
262
263 for (i = 0; i < 5; i++)
264 {
265 struct timespec ts;
266
267 iface_enumerate(AF_UNSPEC, &mac_param, find_mac);
268
269 if (mac_param.maclen != 0)
270 break;
271
272 sendto(daemon->icmp6fd, &neigh, sizeof(neigh), 0, (struct sockaddr *)&addr, sizeof(addr));
273
274 ts.tv_sec = 0;
275 ts.tv_nsec = 100000000; /* 100ms */
276 nanosleep(&ts, NULL);
277 }
278
279 *maclenp = mac_param.maclen;
280 *mactypep = ARPHRD_ETHER;
281}
282
Simon Kelley89500e32013-09-20 16:29:20 +0100283static int find_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv)
284{
285 struct mac_param *parm = parmv;
286
Simon Kelley1b551902013-09-23 15:03:05 +0100287 if (family == AF_INET6 && IN6_ARE_ADDR_EQUAL(parm->target, (struct in6_addr *)addrp))
Simon Kelley89500e32013-09-20 16:29:20 +0100288 {
289 if (maclen <= DHCP_CHADDR_MAX)
290 {
291 parm->maclen = maclen;
292 memcpy(parm->mac, mac, maclen);
293 }
294
295 return 0; /* found, abort */
296 }
297
298 return 1;
299}
300
Simon Kelleyc72daea2012-01-05 21:33:27 +0000301static int complete_context6(struct in6_addr *local, int prefix,
Simon Kelley3bc0d932012-12-28 11:31:44 +0000302 int scope, int if_index, int flags, unsigned int preferred,
303 unsigned int valid, void *vparam)
Simon Kelleyc72daea2012-01-05 21:33:27 +0000304{
305 struct dhcp_context *context;
Simon Kelleyff7eea22013-09-04 18:01:38 +0100306 struct dhcp_relay *relay;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000307 struct iface_param *param = vparam;
Simon Kelleybe6cfb42012-10-16 20:38:31 +0100308 struct iname *tmp;
309
Simon Kelley52b92f42012-01-22 16:05:15 +0000310 (void)scope; /* warning */
Simon Kelley3bc0d932012-12-28 11:31:44 +0000311
Simon Kelleyff7eea22013-09-04 18:01:38 +0100312 if (if_index == param->ind)
Simon Kelleyc72daea2012-01-05 21:33:27 +0000313 {
Simon Kelleyc3a04082014-01-11 22:18:19 +0000314 if (IN6_IS_ADDR_LINKLOCAL(local))
315 param->ll_addr = *local;
316 else if (IN6_IS_ADDR_ULA(local))
317 param->ula_addr = *local;
318
Simon Kelleyff7eea22013-09-04 18:01:38 +0100319 if (!IN6_IS_ADDR_LOOPBACK(local) &&
320 !IN6_IS_ADDR_LINKLOCAL(local) &&
321 !IN6_IS_ADDR_MULTICAST(local))
Simon Kelleye44ddca2012-02-18 17:08:50 +0000322 {
Simon Kelleyff7eea22013-09-04 18:01:38 +0100323 /* if we have --listen-address config, see if the
324 arrival interface has a matching address. */
325 for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
326 if (tmp->addr.sa.sa_family == AF_INET6 &&
327 IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, local))
328 param->addr_match = 1;
329
330 /* Determine a globally address on the arrival interface, even
331 if we have no matching dhcp-context, because we're only
332 allocating on remote subnets via relays. This
333 is used as a default for the DNS server option. */
334 param->fallback = *local;
335
336 for (context = daemon->dhcp6; context; context = context->next)
Simon Kelleye44ddca2012-02-18 17:08:50 +0000337 {
Simon Kelleyff7eea22013-09-04 18:01:38 +0100338 if ((context->flags & CONTEXT_DHCP) &&
339 !(context->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) &&
Vladislav Grishenko4c82efc2013-12-03 16:05:30 +0000340 prefix <= context->prefix &&
341 is_same_net6(local, &context->start6, context->prefix) &&
342 is_same_net6(local, &context->end6, context->prefix))
Simon Kelleye44ddca2012-02-18 17:08:50 +0000343 {
Simon Kelley3bc0d932012-12-28 11:31:44 +0000344
Simon Kelley3bc0d932012-12-28 11:31:44 +0000345
Simon Kelleyff7eea22013-09-04 18:01:38 +0100346 /* link it onto the current chain if we've not seen it before */
347 if (context->current == context)
348 {
349 struct dhcp_context *tmp, **up;
350
351 /* use interface values only for contructed contexts */
352 if (!(context->flags & CONTEXT_CONSTRUCTED))
353 preferred = valid = 0xffffffff;
354 else if (flags & IFACE_DEPRECATED)
355 preferred = 0;
356
357 if (context->flags & CONTEXT_DEPRECATE)
358 preferred = 0;
359
360 /* order chain, longest preferred time first */
361 for (up = &param->current, tmp = param->current; tmp; tmp = tmp->current)
362 if (tmp->preferred <= preferred)
363 break;
364 else
365 up = &tmp->current;
366
367 context->current = *up;
368 *up = context;
369 context->local6 = *local;
370 context->preferred = preferred;
371 context->valid = valid;
372 }
Simon Kelleye44ddca2012-02-18 17:08:50 +0000373 }
374 }
Simon Kelleyc72daea2012-01-05 21:33:27 +0000375 }
Simon Kelleyff7eea22013-09-04 18:01:38 +0100376
377 for (relay = daemon->relay6; relay; relay = relay->next)
378 if (IN6_ARE_ADDR_EQUAL(local, &relay->local.addr.addr6) && relay->current == relay &&
379 (IN6_IS_ADDR_UNSPECIFIED(&param->relay_local) || IN6_ARE_ADDR_EQUAL(local, &param->relay_local)))
380 {
381 relay->current = param->relay;
382 param->relay = relay;
383 param->relay_local = *local;
384 }
385
Simon Kelleyc72daea2012-01-05 21:33:27 +0000386 }
Simon Kelleyff7eea22013-09-04 18:01:38 +0100387
388 return 1;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000389}
Simon Kelley52b92f42012-01-22 16:05:15 +0000390
391struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct in6_addr *net, int prefix, u64 addr)
392{
393 struct dhcp_config *config;
394
395 for (config = configs; config; config = config->next)
396 if ((config->flags & CONFIG_ADDR6) &&
397 is_same_net6(&config->addr6, net, prefix) &&
398 (prefix == 128 || addr6part(&config->addr6) == addr))
399 return config;
400
401 return NULL;
402}
403
Simon Kelley6586e832013-11-07 14:20:13 +0000404struct dhcp_context *address6_allocate(struct dhcp_context *context, unsigned char *clid, int clid_len, int temp_addr,
Simon Kelleyc6309242013-03-07 20:59:28 +0000405 int iaid, int serial, struct dhcp_netid *netids, int plain_range, struct in6_addr *ans)
Simon Kelley52b92f42012-01-22 16:05:15 +0000406{
407 /* Find a free address: exclude anything in use and anything allocated to
408 a particular hwaddr/clientid/hostname in our configuration.
409 Try to return from contexts which match netids first.
410
411 Note that we assume the address prefix lengths are 64 or greater, so we can
412 get by with 64 bit arithmetic.
413*/
414
415 u64 start, addr;
416 struct dhcp_context *c, *d;
417 int i, pass;
418 u64 j;
419
420 /* hash hwaddr: use the SDBM hashing algorithm. This works
Simon Kelley6586e832013-11-07 14:20:13 +0000421 for MAC addresses, let's see how it manages with client-ids!
422 For temporary addresses, we generate a new random one each time. */
423 if (temp_addr)
424 j = rand64();
425 else
426 for (j = iaid, i = 0; i < clid_len; i++)
427 j += clid[i] + (j << 6) + (j << 16) - j;
Simon Kelley52b92f42012-01-22 16:05:15 +0000428
Simon Kelleyc6309242013-03-07 20:59:28 +0000429 for (pass = 0; pass <= plain_range ? 1 : 0; pass++)
Simon Kelley52b92f42012-01-22 16:05:15 +0000430 for (c = context; c; c = c->current)
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000431 if (c->flags & (CONTEXT_DEPRECATE | CONTEXT_STATIC | CONTEXT_RA_STATELESS | CONTEXT_USED))
Simon Kelley52b92f42012-01-22 16:05:15 +0000432 continue;
433 else if (!match_netid(c->filter, netids, pass))
434 continue;
435 else
Simon Kelley07933802012-02-14 20:55:25 +0000436 {
Simon Kelley6586e832013-11-07 14:20:13 +0000437 if (!temp_addr && option_bool(OPT_CONSEC_ADDR))
Simon Kelley07933802012-02-14 20:55:25 +0000438 /* seed is largest extant lease addr in this context */
Simon Kelleyc6309242013-03-07 20:59:28 +0000439 start = lease_find_max_addr6(c) + serial;
Simon Kelley07933802012-02-14 20:55:25 +0000440 else
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000441 start = addr6part(&c->start6) + ((j + c->addr_epoch) % (1 + addr6part(&c->end6) - addr6part(&c->start6)));
Simon Kelley52b92f42012-01-22 16:05:15 +0000442
443 /* iterate until we find a free address. */
444 addr = start;
445
446 do {
447 /* eliminate addresses in use by the server. */
448 for (d = context; d; d = d->current)
Simon Kelleye44ddca2012-02-18 17:08:50 +0000449 if (addr == addr6part(&d->local6))
Simon Kelley52b92f42012-01-22 16:05:15 +0000450 break;
451
452 if (!d &&
453 !lease6_find_by_addr(&c->start6, c->prefix, addr) &&
454 !config_find_by_address6(daemon->dhcp_conf, &c->start6, c->prefix, addr))
455 {
456 *ans = c->start6;
457 setaddr6part (ans, addr);
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000458 return c;
Simon Kelley52b92f42012-01-22 16:05:15 +0000459 }
460
461 addr++;
462
463 if (addr == addr6part(&c->end6) + 1)
464 addr = addr6part(&c->start6);
465
466 } while (addr != start);
467 }
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000468
Simon Kelley5b37aa82013-04-02 16:32:25 +0100469 return NULL;
Simon Kelley52b92f42012-01-22 16:05:15 +0000470}
471
Simon Kelley37c9cce2013-01-09 19:51:04 +0000472/* can dynamically allocate addr */
Simon Kelley52b92f42012-01-22 16:05:15 +0000473struct dhcp_context *address6_available(struct dhcp_context *context,
474 struct in6_addr *taddr,
Simon Kelleyc6309242013-03-07 20:59:28 +0000475 struct dhcp_netid *netids,
476 int plain_range)
Simon Kelley52b92f42012-01-22 16:05:15 +0000477{
478 u64 start, end, addr = addr6part(taddr);
479 struct dhcp_context *tmp;
480
481 for (tmp = context; tmp; tmp = tmp->current)
482 {
483 start = addr6part(&tmp->start6);
484 end = addr6part(&tmp->end6);
485
Simon Kelleyc8257542012-03-28 21:15:41 +0100486 if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_RA_STATELESS)) &&
487 is_same_net6(&tmp->start6, taddr, tmp->prefix) &&
488 is_same_net6(&tmp->end6, taddr, tmp->prefix) &&
Simon Kelley52b92f42012-01-22 16:05:15 +0000489 addr >= start &&
490 addr <= end &&
Simon Kelleyc6309242013-03-07 20:59:28 +0000491 match_netid(tmp->filter, netids, plain_range))
Simon Kelley52b92f42012-01-22 16:05:15 +0000492 return tmp;
493 }
494
495 return NULL;
496}
497
Simon Kelley37c9cce2013-01-09 19:51:04 +0000498/* address OK if configured */
499struct dhcp_context *address6_valid(struct dhcp_context *context,
Simon Kelleyc6309242013-03-07 20:59:28 +0000500 struct in6_addr *taddr,
501 struct dhcp_netid *netids,
502 int plain_range)
Simon Kelley37c9cce2013-01-09 19:51:04 +0000503{
504 struct dhcp_context *tmp;
505
506 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelleybaeb3ad2013-01-10 11:47:38 +0000507 if (is_same_net6(&tmp->start6, taddr, tmp->prefix) &&
Simon Kelleyc6309242013-03-07 20:59:28 +0000508 match_netid(tmp->filter, netids, plain_range))
Simon Kelley37c9cce2013-01-09 19:51:04 +0000509 return tmp;
510
511 return NULL;
512}
513
Simon Kelleyde92b472013-03-15 18:25:10 +0000514int config_valid(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr)
Simon Kelley4cb1b322012-02-06 14:30:41 +0000515{
Simon Kelleyde92b472013-03-15 18:25:10 +0000516 if (!config || !(config->flags & CONFIG_ADDR6))
517 return 0;
518
519 if ((config->flags & CONFIG_WILDCARD) && context->prefix == 64)
Simon Kelley30393102013-01-17 16:34:16 +0000520 {
Simon Kelleyde92b472013-03-15 18:25:10 +0000521 *addr = context->start6;
522 setaddr6part(addr, addr6part(&config->addr6));
Simon Kelley30393102013-01-17 16:34:16 +0000523 return 1;
524 }
525
Simon Kelleyde92b472013-03-15 18:25:10 +0000526 if (is_same_net6(&context->start6, &config->addr6, context->prefix))
527 {
528 *addr = config->addr6;
529 return 1;
530 }
531
Simon Kelleyb2692212012-09-16 22:15:56 +0100532 return 0;
533}
534
Simon Kelley4cb1b322012-02-06 14:30:41 +0000535void make_duid(time_t now)
536{
Simon Kelley3511a922013-11-07 10:28:11 +0000537 (void)now;
538
Simon Kelley8b372702012-03-09 17:45:10 +0000539 if (daemon->duid_config)
540 {
541 unsigned char *p;
542
543 daemon->duid = p = safe_malloc(daemon->duid_config_len + 6);
544 daemon->duid_len = daemon->duid_config_len + 6;
545 PUTSHORT(2, p); /* DUID_EN */
546 PUTLONG(daemon->duid_enterprise, p);
547 memcpy(p, daemon->duid_config, daemon->duid_config_len);
548 }
549 else
550 {
Simon Kelley3511a922013-11-07 10:28:11 +0000551 time_t newnow = 0;
552
553 /* If we have no persistent lease database, or a non-stable RTC, use DUID_LL (newnow == 0) */
554#ifndef HAVE_BROKEN_RTC
Simon Kelley8b372702012-03-09 17:45:10 +0000555 /* rebase epoch to 1/1/2000 */
Simon Kelley3511a922013-11-07 10:28:11 +0000556 if (!option_bool(OPT_LEASE_RO) || daemon->lease_change_command)
557 newnow = now - 946684800;
558#endif
Simon Kelley8b372702012-03-09 17:45:10 +0000559
560 iface_enumerate(AF_LOCAL, &newnow, make_duid1);
561
562 if(!daemon->duid)
563 die("Cannot create DHCPv6 server DUID: %s", NULL, EC_MISC);
564 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000565}
566
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000567static int make_duid1(int index, unsigned int type, char *mac, size_t maclen, void *parm)
Simon Kelley4cb1b322012-02-06 14:30:41 +0000568{
569 /* create DUID as specified in RFC3315. We use the MAC of the
Simon Kelley0f089832012-03-01 13:43:39 +0000570 first interface we find that isn't loopback or P-to-P and
571 has address-type < 256. Address types above 256 are things like
572 tunnels which don't have usable MAC addresses. */
Simon Kelley4cb1b322012-02-06 14:30:41 +0000573
574 unsigned char *p;
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000575 (void)index;
Vladislav Grishenko408c3682013-09-24 16:18:49 +0100576 (void)parm;
Simon Kelley3511a922013-11-07 10:28:11 +0000577 time_t newnow = *((time_t *)parm);
578
Simon Kelley0f089832012-03-01 13:43:39 +0000579 if (type >= 256)
580 return 1;
581
Simon Kelley3511a922013-11-07 10:28:11 +0000582 if (newnow == 0)
583 {
584 daemon->duid = p = safe_malloc(maclen + 4);
585 daemon->duid_len = maclen + 4;
586 PUTSHORT(3, p); /* DUID_LL */
587 PUTSHORT(type, p); /* address type */
588 }
589 else
590 {
591 daemon->duid = p = safe_malloc(maclen + 8);
592 daemon->duid_len = maclen + 8;
593 PUTSHORT(1, p); /* DUID_LLT */
594 PUTSHORT(type, p); /* address type */
595 PUTLONG(*((time_t *)parm), p); /* time */
596 }
597
Simon Kelley4cb1b322012-02-06 14:30:41 +0000598 memcpy(p, mac, maclen);
599
600 return 0;
601}
Simon Kelley1f776932012-12-16 19:46:08 +0000602
603struct cparam {
604 time_t now;
Simon Kelley0c050242012-12-22 22:13:19 +0000605 int newone, newname;
Simon Kelley1f776932012-12-16 19:46:08 +0000606};
607
608static int construct_worker(struct in6_addr *local, int prefix,
Simon Kelleybad7b872012-12-20 22:00:39 +0000609 int scope, int if_index, int flags,
Simon Kelley1f776932012-12-16 19:46:08 +0000610 int preferred, int valid, void *vparam)
611{
612 char ifrn_name[IFNAMSIZ];
613 struct in6_addr start6, end6;
614 struct dhcp_context *template, *context;
615
616 (void)scope;
Simon Kelleybad7b872012-12-20 22:00:39 +0000617 (void)flags;
Simon Kelleyed8b68a2012-12-21 16:23:26 +0000618 (void)valid;
619 (void)preferred;
Simon Kelley1f776932012-12-16 19:46:08 +0000620
621 struct cparam *param = vparam;
622
623 if (IN6_IS_ADDR_LOOPBACK(local) ||
624 IN6_IS_ADDR_LINKLOCAL(local) ||
625 IN6_IS_ADDR_MULTICAST(local))
626 return 1;
627
Simon Kelley861c8912013-09-25 15:30:30 +0100628 if (!(flags & IFACE_PERMANENT))
629 return 1;
630
631 if (flags & IFACE_DEPRECATED)
632 return 1;
633
Simon Kelleya8105592013-09-25 15:36:00 +0100634 if (!indextoname(daemon->icmp6fd, if_index, ifrn_name))
Simon Kelley1f776932012-12-16 19:46:08 +0000635 return 0;
636
637 for (template = daemon->dhcp6; template; template = template->next)
638 if (!(template->flags & CONTEXT_TEMPLATE))
639 {
640 /* non-template entries, just fill in interface and local addresses */
Vladislav Grishenko4c82efc2013-12-03 16:05:30 +0000641 if (prefix <= template->prefix &&
642 is_same_net6(local, &template->start6, template->prefix) &&
643 is_same_net6(local, &template->end6, template->prefix))
Simon Kelley1f776932012-12-16 19:46:08 +0000644 {
645 template->if_index = if_index;
646 template->local6 = *local;
647 }
648
649 }
Simon Kelley486479e2013-10-14 17:18:03 +0100650 else if (wildcard_match(template->template_interface, ifrn_name) &&
Vladislav Grishenko4c82efc2013-12-03 16:05:30 +0000651 template->prefix >= prefix)
Simon Kelley1f776932012-12-16 19:46:08 +0000652 {
653 start6 = *local;
654 setaddr6part(&start6, addr6part(&template->start6));
655 end6 = *local;
656 setaddr6part(&end6, addr6part(&template->end6));
657
658 for (context = daemon->dhcp6; context; context = context->next)
659 if ((context->flags & CONTEXT_CONSTRUCTED) &&
660 IN6_ARE_ADDR_EQUAL(&start6, &context->start6) &&
661 IN6_ARE_ADDR_EQUAL(&end6, &context->end6))
662 {
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100663 int flags = context->flags;
664 context->flags &= ~(CONTEXT_GC | CONTEXT_OLD);
665 if (flags & CONTEXT_OLD)
Simon Kelleyf7a40ec2013-07-27 13:36:08 +0100666 {
667 /* address went, now it's back */
668 log_context(AF_INET6, context);
669 /* fast RAs for a while */
670 ra_start_unsolicted(param->now, context);
Simon Kelley0d6eb132013-11-26 13:30:12 +0000671 param->newone = 1;
Simon Kelleyf7a40ec2013-07-27 13:36:08 +0100672 /* Add address to name again */
673 if (context->flags & CONTEXT_RA_NAME)
674 param->newname = 1;
675 }
Simon Kelley1f776932012-12-16 19:46:08 +0000676 break;
677 }
678
679 if (!context && (context = whine_malloc(sizeof (struct dhcp_context))))
680 {
681 *context = *template;
682 context->start6 = start6;
683 context->end6 = end6;
684 context->flags &= ~CONTEXT_TEMPLATE;
685 context->flags |= CONTEXT_CONSTRUCTED;
686 context->if_index = if_index;
687 context->local6 = *local;
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100688 context->saved_valid = 0;
Simon Kelley1f776932012-12-16 19:46:08 +0000689
690 context->next = daemon->dhcp6;
691 daemon->dhcp6 = context;
692
Simon Kelley1b75c1e2012-12-18 19:55:25 +0000693 ra_start_unsolicted(param->now, context);
Simon Kelley1f776932012-12-16 19:46:08 +0000694 /* we created a new one, need to call
695 lease_update_file to get periodic functions called */
696 param->newone = 1;
Simon Kelley0c050242012-12-22 22:13:19 +0000697
698 /* Will need to add new putative SLAAC addresses to existing leases */
699 if (context->flags & CONTEXT_RA_NAME)
700 param->newname = 1;
Simon Kelley1f776932012-12-16 19:46:08 +0000701
702 log_context(AF_INET6, context);
703 }
Simon Kelley1f776932012-12-16 19:46:08 +0000704 }
705
706 return 1;
707}
708
709void dhcp_construct_contexts(time_t now)
710{
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100711 struct dhcp_context *context, *tmp, **up;
Simon Kelley1f776932012-12-16 19:46:08 +0000712 struct cparam param;
713 param.newone = 0;
Simon Kelley0c050242012-12-22 22:13:19 +0000714 param.newname = 0;
Simon Kelley1f776932012-12-16 19:46:08 +0000715 param.now = now;
716
717 for (context = daemon->dhcp6; context; context = context->next)
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100718 if (context->flags & CONTEXT_CONSTRUCTED)
719 context->flags |= CONTEXT_GC;
720
Simon Kelley1f776932012-12-16 19:46:08 +0000721 iface_enumerate(AF_INET6, &param, construct_worker);
722
723 for (up = &daemon->dhcp6, context = daemon->dhcp6; context; context = tmp)
724 {
Simon Kelley1f776932012-12-16 19:46:08 +0000725
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100726 tmp = context->next;
727
728 if (context->flags & CONTEXT_GC && !(context->flags & CONTEXT_OLD))
Simon Kelley1f776932012-12-16 19:46:08 +0000729 {
Simon Kelley7ea3d3f2014-04-25 22:04:05 +0100730 if ((context->flags & CONTEXT_RA) || option_bool(OPT_RA))
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100731 {
732 /* previously constructed context has gone. advertise it's demise */
733 context->flags |= CONTEXT_OLD;
734 context->address_lost_time = now;
Simon Kelley9f48ffa2013-07-28 15:47:04 +0100735 /* Apply same ceiling of configured lease time as in radv.c */
736 if (context->saved_valid > context->lease_time)
737 context->saved_valid = context->lease_time;
738 /* maximum time is 2 hours, from RFC */
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100739 if (context->saved_valid > 7200) /* 2 hours */
740 context->saved_valid = 7200;
741 ra_start_unsolicted(now, context);
742 param.newone = 1; /* include deletion */
743
744 if (context->flags & CONTEXT_RA_NAME)
745 param.newname = 1;
746
747 log_context(AF_INET6, context);
748
749 up = &context->next;
750 }
751 else
752 {
753 /* we were never doing RA for this, so free now */
754 *up = context->next;
755 free(context);
756 }
Simon Kelley1f776932012-12-16 19:46:08 +0000757 }
758 else
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100759 up = &context->next;
Simon Kelley1f776932012-12-16 19:46:08 +0000760 }
761
762 if (param.newone)
Simon Kelley7558ecd2012-12-16 21:45:16 +0000763 {
764 if (daemon->dhcp || daemon->doing_dhcp6)
Simon Kelley0c050242012-12-22 22:13:19 +0000765 {
766 if (param.newname)
767 lease_update_slaac(now);
768 lease_update_file(now);
769 }
Simon Kelley7558ecd2012-12-16 21:45:16 +0000770 else
771 /* Not doing DHCP, so no lease system, manage alarms for ra only */
772 send_alarm(periodic_ra(now), now);
773 }
Simon Kelley1f776932012-12-16 19:46:08 +0000774}
Simon Kelley1f776932012-12-16 19:46:08 +0000775
Simon Kelleyc72daea2012-01-05 21:33:27 +0000776#endif
777
778