blob: 51788ed9b510e3001358f3c9865608f95c86bab0 [file] [log] [blame]
Simon Kelley2a8710a2020-01-05 16:40:06 +00001/* dnsmasq is Copyright (c) 2000-2020 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 +010030
Simon Kelleyc72daea2012-01-05 21:33:27 +000031static int complete_context6(struct in6_addr *local, int prefix,
Simon Kelleybad7b872012-12-20 22:00:39 +000032 int scope, int if_index, int flags,
Simon Kelley3bc0d932012-12-28 11:31:44 +000033 unsigned int preferred, unsigned int valid, void *vparam);
Simon Kelleyc5ad4e72012-02-24 16:06:20 +000034static int make_duid1(int index, unsigned int type, char *mac, size_t maclen, void *parm);
Simon Kelley4cb1b322012-02-06 14:30:41 +000035
Simon Kelleyc72daea2012-01-05 21:33:27 +000036void dhcp6_init(void)
37{
38 int fd;
39 struct sockaddr_in6 saddr;
Simon Kelley0e88d532012-03-28 22:22:05 +010040#if defined(IPV6_TCLASS) && defined(IPTOS_CLASS_CS6)
Simon Kelleyc72daea2012-01-05 21:33:27 +000041 int class = IPTOS_CLASS_CS6;
Simon Kelley4cb1b322012-02-06 14:30:41 +000042#endif
Simon Kelley20223102012-10-15 10:41:17 +010043 int oneopt = 1;
44
Simon Kelleyc72daea2012-01-05 21:33:27 +000045 if ((fd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP)) == -1 ||
Simon Kelley0e88d532012-03-28 22:22:05 +010046#if defined(IPV6_TCLASS) && defined(IPTOS_CLASS_CS6)
Simon Kelleyc72daea2012-01-05 21:33:27 +000047 setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &class, sizeof(class)) == -1 ||
Simon Kelley4cb1b322012-02-06 14:30:41 +000048#endif
Simon Kelley20223102012-10-15 10:41:17 +010049 setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &oneopt, sizeof(oneopt)) == -1 ||
Simon Kelleyc72daea2012-01-05 21:33:27 +000050 !fix_fd(fd) ||
51 !set_ipv6pktinfo(fd))
52 die (_("cannot create DHCPv6 socket: %s"), NULL, EC_BADNET);
53
Josh Soref730c6742017-02-06 16:14:04 +000054 /* When bind-interfaces is set, there might be more than one dnsmasq
Simon Kelley20223102012-10-15 10:41:17 +010055 instance binding port 547. That's OK if they serve different networks.
Josh Soref730c6742017-02-06 16:14:04 +000056 Need to set REUSEADDR|REUSEPORT to make this possible.
Simon Kelley56a11422013-04-02 17:02:58 +010057 Handle the case that REUSEPORT is defined, but the kernel doesn't
58 support it. This handles the introduction of REUSEPORT on Linux. */
Simon Kelley20223102012-10-15 10:41:17 +010059 if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND))
60 {
Simon Kelleyffbad342013-08-14 15:53:57 +010061 int rc = 0;
Simon Kelley56a11422013-04-02 17:02:58 +010062
Simon Kelley20223102012-10-15 10:41:17 +010063#ifdef SO_REUSEPORT
Simon Kelley56a11422013-04-02 17:02:58 +010064 if ((rc = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &oneopt, sizeof(oneopt))) == -1 &&
Simon Kelleyffbad342013-08-14 15:53:57 +010065 errno == ENOPROTOOPT)
66 rc = 0;
Simon Kelley20223102012-10-15 10:41:17 +010067#endif
Simon Kelley56a11422013-04-02 17:02:58 +010068
Simon Kelleyffbad342013-08-14 15:53:57 +010069 if (rc != -1)
Simon Kelley56a11422013-04-02 17:02:58 +010070 rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt));
71
Simon Kelley20223102012-10-15 10:41:17 +010072 if (rc == -1)
73 die(_("failed to set SO_REUSE{ADDR|PORT} on DHCPv6 socket: %s"), NULL, EC_BADNET);
74 }
75
Simon Kelleyc72daea2012-01-05 21:33:27 +000076 memset(&saddr, 0, sizeof(saddr));
77#ifdef HAVE_SOCKADDR_SA_LEN
Simon Kelley4cb1b322012-02-06 14:30:41 +000078 saddr.sin6_len = sizeof(struct sockaddr_in6);
Simon Kelleyc72daea2012-01-05 21:33:27 +000079#endif
80 saddr.sin6_family = AF_INET6;
81 saddr.sin6_addr = in6addr_any;
82 saddr.sin6_port = htons(DHCPV6_SERVER_PORT);
83
84 if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in6)))
85 die(_("failed to bind DHCPv6 server socket: %s"), NULL, EC_BADNET);
86
Simon Kelleyc72daea2012-01-05 21:33:27 +000087 daemon->dhcp6fd = fd;
Simon Kelleyc72daea2012-01-05 21:33:27 +000088}
89
Simon Kelleyc72daea2012-01-05 21:33:27 +000090void dhcp6_packet(time_t now)
91{
92 struct dhcp_context *context;
Simon Kelleyff7eea22013-09-04 18:01:38 +010093 struct dhcp_relay *relay;
Simon Kelleyc72daea2012-01-05 21:33:27 +000094 struct iface_param parm;
95 struct cmsghdr *cmptr;
96 struct msghdr msg;
97 int if_index = 0;
98 union {
99 struct cmsghdr align; /* this ensures alignment */
100 char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
101 } control_u;
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000102 struct sockaddr_in6 from;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000103 ssize_t sz;
104 struct ifreq ifr;
105 struct iname *tmp;
Simon Kelley1d0f91c2012-03-12 11:56:22 +0000106 unsigned short port;
Simon Kelley89500e32013-09-20 16:29:20 +0100107 struct in6_addr dst_addr;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000108
Simon Kelley8f51a292013-09-21 14:07:12 +0100109 memset(&dst_addr, 0, sizeof(dst_addr));
110
Simon Kelleyc72daea2012-01-05 21:33:27 +0000111 msg.msg_control = control_u.control6;
112 msg.msg_controllen = sizeof(control_u);
113 msg.msg_flags = 0;
114 msg.msg_name = &from;
115 msg.msg_namelen = sizeof(from);
116 msg.msg_iov = &daemon->dhcp_packet;
117 msg.msg_iovlen = 1;
118
Simon Kelley1d0f91c2012-03-12 11:56:22 +0000119 if ((sz = recv_dhcp_packet(daemon->dhcp6fd, &msg)) == -1)
Simon Kelleyc72daea2012-01-05 21:33:27 +0000120 return;
121
122 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
123 if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
124 {
125 union {
126 unsigned char *c;
127 struct in6_pktinfo *p;
128 } p;
129 p.c = CMSG_DATA(cmptr);
130
131 if_index = p.p->ipi6_ifindex;
Simon Kelley89500e32013-09-20 16:29:20 +0100132 dst_addr = p.p->ipi6_addr;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000133 }
134
135 if (!indextoname(daemon->dhcp6fd, if_index, ifr.ifr_name))
136 return;
Simon Kelleybe6cfb42012-10-16 20:38:31 +0100137
Simon Kelley18a6bdd2019-12-20 18:19:20 +0000138 if ((port = relay_reply6(&from, sz, ifr.ifr_name)) != 0)
139 {
140 from.sin6_port = htons(port);
141 while (retry_send(sendto(daemon->dhcp6fd, daemon->outpacket.iov_base,
142 save_counter(-1), 0, (struct sockaddr *)&from,
143 sizeof(from))));
144 }
145 else
Simon Kelleybe6cfb42012-10-16 20:38:31 +0100146 {
Neil Jerram0ddb8762015-06-10 22:11:06 +0100147 struct dhcp_bridge *bridge, *alias;
148
Simon Kelleyff7eea22013-09-04 18:01:38 +0100149 for (tmp = daemon->if_except; tmp; tmp = tmp->next)
Simon Kelley49333cb2013-03-15 20:30:51 +0000150 if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
Simon Kelleyff7eea22013-09-04 18:01:38 +0100151 return;
152
153 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
154 if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
155 return;
156
157 parm.current = NULL;
158 parm.relay = NULL;
159 memset(&parm.relay_local, 0, IN6ADDRSZ);
160 parm.ind = if_index;
161 parm.addr_match = 0;
162 memset(&parm.fallback, 0, IN6ADDRSZ);
Simon Kelleyc3a04082014-01-11 22:18:19 +0000163 memset(&parm.ll_addr, 0, IN6ADDRSZ);
164 memset(&parm.ula_addr, 0, IN6ADDRSZ);
Neil Jerram0ddb8762015-06-10 22:11:06 +0100165
166 /* If the interface on which the DHCPv6 request was received is
167 an alias of some other interface (as specified by the
Neil Jerram4918bd52015-06-10 22:23:20 +0100168 --bridge-interface option), change parm.ind so that we look
Neil Jerram0ddb8762015-06-10 22:11:06 +0100169 for DHCPv6 contexts associated with the aliased interface
170 instead of with the aliasing one. */
171 for (bridge = daemon->bridges; bridge; bridge = bridge->next)
172 {
173 for (alias = bridge->alias; alias; alias = alias->next)
174 if (wildcard_matchn(alias->iface, ifr.ifr_name, IF_NAMESIZE))
175 {
176 parm.ind = if_nametoindex(bridge->iface);
177 if (!parm.ind)
178 {
179 my_syslog(MS_DHCP | LOG_WARNING,
180 _("unknown interface %s in bridge-interface"),
181 bridge->iface);
182 return;
183 }
184 break;
185 }
186 if (alias)
187 break;
188 }
Simon Kelleyff7eea22013-09-04 18:01:38 +0100189
190 for (context = daemon->dhcp6; context; context = context->next)
191 if (IN6_IS_ADDR_UNSPECIFIED(&context->start6) && context->prefix == 0)
192 {
193 /* wildcard context for DHCP-stateless only */
194 parm.current = context;
195 context->current = NULL;
196 }
197 else
198 {
199 /* unlinked contexts are marked by context->current == context */
200 context->current = context;
201 memset(&context->local6, 0, IN6ADDRSZ);
202 }
Simon Kelleybe6cfb42012-10-16 20:38:31 +0100203
Simon Kelleyff7eea22013-09-04 18:01:38 +0100204 for (relay = daemon->relay6; relay; relay = relay->next)
205 relay->current = relay;
206
207 if (!iface_enumerate(AF_INET6, &parm, complete_context6))
Simon Kelleybe6cfb42012-10-16 20:38:31 +0100208 return;
Simon Kelley89500e32013-09-20 16:29:20 +0100209
Simon Kelleyff7eea22013-09-04 18:01:38 +0100210 if (daemon->if_names || daemon->if_addrs)
211 {
212
213 for (tmp = daemon->if_names; tmp; tmp = tmp->next)
214 if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
215 break;
216
217 if (!tmp && !parm.addr_match)
218 return;
219 }
220
221 if (parm.relay)
222 {
Simon Kelley89500e32013-09-20 16:29:20 +0100223 /* Ignore requests sent to the ALL_SERVERS multicast address for relay when
224 we're listening there for DHCPv6 server reasons. */
225 struct in6_addr all_servers;
226
227 inet_pton(AF_INET6, ALL_SERVERS, &all_servers);
228
229 if (!IN6_ARE_ADDR_EQUAL(&dst_addr, &all_servers))
Simon Kelley33702ab2015-12-28 23:17:15 +0000230 relay_upstream6(parm.relay, sz, &from.sin6_addr, from.sin6_scope_id, now);
Simon Kelleyff7eea22013-09-04 18:01:38 +0100231 return;
232 }
233
234 /* May have configured relay, but not DHCP server */
235 if (!daemon->doing_dhcp6)
236 return;
Neil Jerram0ddb8762015-06-10 22:11:06 +0100237
Simon Kelleyff7eea22013-09-04 18:01:38 +0100238 lease_prune(NULL, now); /* lose any expired leases */
239
240 port = dhcp6_reply(parm.current, if_index, ifr.ifr_name, &parm.fallback,
Simon Kelleyc3a04082014-01-11 22:18:19 +0000241 &parm.ll_addr, &parm.ula_addr, sz, &from.sin6_addr, now);
Simon Kelleyff7eea22013-09-04 18:01:38 +0100242
Simon Kelley18a6bdd2019-12-20 18:19:20 +0000243 /* The port in the source address of the original request should
244 be correct, but at least once client sends from the server port,
245 so we explicitly send to the client port to a client, and the
246 server port to a relay. */
247 if (port != 0)
248 {
249 from.sin6_port = htons(port);
250 while (retry_send(sendto(daemon->dhcp6fd, daemon->outpacket.iov_base,
251 save_counter(-1), 0, (struct sockaddr *)&from,
252 sizeof(from))));
253 }
254
255 /* These need to be called _after_ we send DHCPv6 packet, since lease_update_file()
256 may trigger sending an RA packet, which overwrites our buffer. */
Simon Kelleyff7eea22013-09-04 18:01:38 +0100257 lease_update_file(now);
258 lease_update_dns(0);
Simon Kelleybe6cfb42012-10-16 20:38:31 +0100259 }
Simon Kelleyc72daea2012-01-05 21:33:27 +0000260}
261
Simon Kelley33702ab2015-12-28 23:17:15 +0000262void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, unsigned int *maclenp, unsigned int *mactypep, time_t now)
Simon Kelley8939c952013-09-25 11:49:34 +0100263{
Josh Soref730c6742017-02-06 16:14:04 +0000264 /* Receiving a packet from a host does not populate the neighbour
Simon Kelley8939c952013-09-25 11:49:34 +0100265 cache, so we send a neighbour discovery request if we can't
266 find the sender. Repeat a few times in case of packet loss. */
267
268 struct neigh_packet neigh;
Simon Kelley11867dc2015-12-23 16:15:58 +0000269 union mysockaddr addr;
270 int i, maclen;
Simon Kelley8939c952013-09-25 11:49:34 +0100271
272 neigh.type = ND_NEIGHBOR_SOLICIT;
273 neigh.code = 0;
274 neigh.reserved = 0;
275 neigh.target = *client;
Tomas Hozza0705a7e2015-02-23 21:26:26 +0000276 /* RFC4443 section-2.3: checksum has to be zero to be calculated */
277 neigh.checksum = 0;
278
Simon Kelley8939c952013-09-25 11:49:34 +0100279 memset(&addr, 0, sizeof(addr));
280#ifdef HAVE_SOCKADDR_SA_LEN
Simon Kelley11867dc2015-12-23 16:15:58 +0000281 addr.in6.sin6_len = sizeof(struct sockaddr_in6);
Simon Kelley8939c952013-09-25 11:49:34 +0100282#endif
Simon Kelley11867dc2015-12-23 16:15:58 +0000283 addr.in6.sin6_family = AF_INET6;
284 addr.in6.sin6_port = htons(IPPROTO_ICMPV6);
285 addr.in6.sin6_addr = *client;
286 addr.in6.sin6_scope_id = iface;
Simon Kelley8939c952013-09-25 11:49:34 +0100287
288 for (i = 0; i < 5; i++)
289 {
290 struct timespec ts;
291
Simon Kelley33702ab2015-12-28 23:17:15 +0000292 if ((maclen = find_mac(&addr, mac, 0, now)) != 0)
Simon Kelley8939c952013-09-25 11:49:34 +0100293 break;
Simon Kelley11867dc2015-12-23 16:15:58 +0000294
295 sendto(daemon->icmp6fd, &neigh, sizeof(neigh), 0, &addr.sa, sizeof(addr));
Simon Kelley8939c952013-09-25 11:49:34 +0100296
297 ts.tv_sec = 0;
298 ts.tv_nsec = 100000000; /* 100ms */
299 nanosleep(&ts, NULL);
300 }
301
Simon Kelley11867dc2015-12-23 16:15:58 +0000302 *maclenp = maclen;
Simon Kelley8939c952013-09-25 11:49:34 +0100303 *mactypep = ARPHRD_ETHER;
304}
305
Simon Kelleyc72daea2012-01-05 21:33:27 +0000306static int complete_context6(struct in6_addr *local, int prefix,
Simon Kelley3bc0d932012-12-28 11:31:44 +0000307 int scope, int if_index, int flags, unsigned int preferred,
308 unsigned int valid, void *vparam)
Simon Kelleyc72daea2012-01-05 21:33:27 +0000309{
310 struct dhcp_context *context;
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000311 struct shared_network *share;
Simon Kelleyff7eea22013-09-04 18:01:38 +0100312 struct dhcp_relay *relay;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000313 struct iface_param *param = vparam;
Simon Kelleybe6cfb42012-10-16 20:38:31 +0100314 struct iname *tmp;
315
Simon Kelley52b92f42012-01-22 16:05:15 +0000316 (void)scope; /* warning */
Simon Kelley3bc0d932012-12-28 11:31:44 +0000317
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000318 if (if_index != param->ind)
319 return 1;
320
321 if (IN6_IS_ADDR_LINKLOCAL(local))
322 param->ll_addr = *local;
323 else if (IN6_IS_ADDR_ULA(local))
324 param->ula_addr = *local;
Simon Kelleyff7eea22013-09-04 18:01:38 +0100325
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000326 if (IN6_IS_ADDR_LOOPBACK(local) ||
327 IN6_IS_ADDR_LINKLOCAL(local) ||
328 IN6_IS_ADDR_MULTICAST(local))
329 return 1;
330
331 /* if we have --listen-address config, see if the
332 arrival interface has a matching address. */
333 for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
334 if (tmp->addr.sa.sa_family == AF_INET6 &&
335 IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, local))
336 param->addr_match = 1;
337
338 /* Determine a globally address on the arrival interface, even
339 if we have no matching dhcp-context, because we're only
340 allocating on remote subnets via relays. This
341 is used as a default for the DNS server option. */
342 param->fallback = *local;
343
344 for (context = daemon->dhcp6; context; context = context->next)
345 if ((context->flags & CONTEXT_DHCP) &&
346 !(context->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) &&
347 prefix <= context->prefix &&
348 context->current == context)
349 {
350 if (is_same_net6(local, &context->start6, context->prefix) &&
351 is_same_net6(local, &context->end6, context->prefix))
352 {
353 struct dhcp_context *tmp, **up;
354
355 /* use interface values only for constructed contexts */
356 if (!(context->flags & CONTEXT_CONSTRUCTED))
357 preferred = valid = 0xffffffff;
358 else if (flags & IFACE_DEPRECATED)
359 preferred = 0;
360
361 if (context->flags & CONTEXT_DEPRECATE)
362 preferred = 0;
363
364 /* order chain, longest preferred time first */
365 for (up = &param->current, tmp = param->current; tmp; tmp = tmp->current)
366 if (tmp->preferred <= preferred)
367 break;
368 else
369 up = &tmp->current;
370
371 context->current = *up;
372 *up = context;
373 context->local6 = *local;
374 context->preferred = preferred;
375 context->valid = valid;
376 }
377 else
378 {
379 for (share = daemon->shared_networks; share; share = share->next)
380 {
381 /* IPv4 shared_address - ignore */
382 if (share->shared_addr.s_addr != 0)
383 continue;
384
385 if (share->if_index != 0)
386 {
387 if (share->if_index != if_index)
388 continue;
389 }
390 else
391 {
392 if (!IN6_ARE_ADDR_EQUAL(&share->match_addr6, local))
393 continue;
394 }
395
396 if (is_same_net6(&share->shared_addr6, &context->start6, context->prefix) &&
397 is_same_net6(&share->shared_addr6, &context->end6, context->prefix))
398 {
399 context->current = param->current;
400 param->current = context;
401 context->local6 = *local;
402 context->preferred = context->flags & CONTEXT_DEPRECATE ? 0 :0xffffffff;
403 context->valid = 0xffffffff;
404 }
405 }
406 }
407 }
408
409 for (relay = daemon->relay6; relay; relay = relay->next)
410 if (IN6_ARE_ADDR_EQUAL(local, &relay->local.addr6) && relay->current == relay &&
411 (IN6_IS_ADDR_UNSPECIFIED(&param->relay_local) || IN6_ARE_ADDR_EQUAL(local, &param->relay_local)))
412 {
413 relay->current = param->relay;
414 param->relay = relay;
415 param->relay_local = *local;
416 }
417
418 return 1;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000419}
Simon Kelley52b92f42012-01-22 16:05:15 +0000420
421struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct in6_addr *net, int prefix, u64 addr)
422{
423 struct dhcp_config *config;
424
425 for (config = configs; config; config = config->next)
426 if ((config->flags & CONFIG_ADDR6) &&
427 is_same_net6(&config->addr6, net, prefix) &&
428 (prefix == 128 || addr6part(&config->addr6) == addr))
429 return config;
430
431 return NULL;
432}
433
Simon Kelley6586e832013-11-07 14:20:13 +0000434struct dhcp_context *address6_allocate(struct dhcp_context *context, unsigned char *clid, int clid_len, int temp_addr,
Dominik DL6ER456a3192019-10-20 18:51:52 +0200435 unsigned int iaid, int serial, struct dhcp_netid *netids, int plain_range, struct in6_addr *ans)
Simon Kelley52b92f42012-01-22 16:05:15 +0000436{
437 /* Find a free address: exclude anything in use and anything allocated to
438 a particular hwaddr/clientid/hostname in our configuration.
439 Try to return from contexts which match netids first.
440
441 Note that we assume the address prefix lengths are 64 or greater, so we can
442 get by with 64 bit arithmetic.
443*/
444
445 u64 start, addr;
446 struct dhcp_context *c, *d;
447 int i, pass;
448 u64 j;
449
450 /* hash hwaddr: use the SDBM hashing algorithm. This works
Simon Kelley6586e832013-11-07 14:20:13 +0000451 for MAC addresses, let's see how it manages with client-ids!
452 For temporary addresses, we generate a new random one each time. */
453 if (temp_addr)
454 j = rand64();
455 else
456 for (j = iaid, i = 0; i < clid_len; i++)
Simon Kelleyd6b749a2016-04-25 17:05:15 +0100457 j = clid[i] + (j << 6) + (j << 16) - j;
Simon Kelley52b92f42012-01-22 16:05:15 +0000458
Simon Kelleyc6309242013-03-07 20:59:28 +0000459 for (pass = 0; pass <= plain_range ? 1 : 0; pass++)
Simon Kelley52b92f42012-01-22 16:05:15 +0000460 for (c = context; c; c = c->current)
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000461 if (c->flags & (CONTEXT_DEPRECATE | CONTEXT_STATIC | CONTEXT_RA_STATELESS | CONTEXT_USED))
Simon Kelley52b92f42012-01-22 16:05:15 +0000462 continue;
463 else if (!match_netid(c->filter, netids, pass))
464 continue;
465 else
Simon Kelley07933802012-02-14 20:55:25 +0000466 {
Simon Kelley6586e832013-11-07 14:20:13 +0000467 if (!temp_addr && option_bool(OPT_CONSEC_ADDR))
Simon Kelleye7bfd552018-12-31 20:51:15 +0000468 {
469 /* seed is largest extant lease addr in this context,
470 skip addresses equal to the number of addresses rejected
471 by clients. This should avoid the same client being offered the same
472 address after it has rjected it. */
Simon Kelley9c0d4452019-01-09 17:57:56 +0000473 start = lease_find_max_addr6(c) + 1 + serial + c->addr_epoch;
Simon Kelleye7bfd552018-12-31 20:51:15 +0000474 if (c->addr_epoch)
475 c->addr_epoch--;
476 }
Simon Kelley07933802012-02-14 20:55:25 +0000477 else
Simon Kelleyfdc97e12016-02-13 17:47:17 +0000478 {
479 u64 range = 1 + addr6part(&c->end6) - addr6part(&c->start6);
480 u64 offset = j + c->addr_epoch;
481
482 /* don't divide by zero if range is whole 2^64 */
483 if (range != 0)
484 offset = offset % range;
485
486 start = addr6part(&c->start6) + offset;
487 }
Simon Kelley52b92f42012-01-22 16:05:15 +0000488
489 /* iterate until we find a free address. */
490 addr = start;
491
492 do {
493 /* eliminate addresses in use by the server. */
494 for (d = context; d; d = d->current)
Simon Kelleye44ddca2012-02-18 17:08:50 +0000495 if (addr == addr6part(&d->local6))
Simon Kelley52b92f42012-01-22 16:05:15 +0000496 break;
497
498 if (!d &&
499 !lease6_find_by_addr(&c->start6, c->prefix, addr) &&
500 !config_find_by_address6(daemon->dhcp_conf, &c->start6, c->prefix, addr))
501 {
502 *ans = c->start6;
503 setaddr6part (ans, addr);
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000504 return c;
Simon Kelley52b92f42012-01-22 16:05:15 +0000505 }
506
507 addr++;
508
509 if (addr == addr6part(&c->end6) + 1)
510 addr = addr6part(&c->start6);
511
512 } while (addr != start);
513 }
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000514
Simon Kelley5b37aa82013-04-02 16:32:25 +0100515 return NULL;
Simon Kelley52b92f42012-01-22 16:05:15 +0000516}
517
Simon Kelley37c9cce2013-01-09 19:51:04 +0000518/* can dynamically allocate addr */
Simon Kelley52b92f42012-01-22 16:05:15 +0000519struct dhcp_context *address6_available(struct dhcp_context *context,
520 struct in6_addr *taddr,
Simon Kelleyc6309242013-03-07 20:59:28 +0000521 struct dhcp_netid *netids,
522 int plain_range)
Simon Kelley52b92f42012-01-22 16:05:15 +0000523{
524 u64 start, end, addr = addr6part(taddr);
525 struct dhcp_context *tmp;
526
527 for (tmp = context; tmp; tmp = tmp->current)
528 {
529 start = addr6part(&tmp->start6);
530 end = addr6part(&tmp->end6);
531
Simon Kelleyc8257542012-03-28 21:15:41 +0100532 if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_RA_STATELESS)) &&
533 is_same_net6(&tmp->start6, taddr, tmp->prefix) &&
534 is_same_net6(&tmp->end6, taddr, tmp->prefix) &&
Simon Kelley52b92f42012-01-22 16:05:15 +0000535 addr >= start &&
536 addr <= end &&
Simon Kelleyc6309242013-03-07 20:59:28 +0000537 match_netid(tmp->filter, netids, plain_range))
Simon Kelley52b92f42012-01-22 16:05:15 +0000538 return tmp;
539 }
540
541 return NULL;
542}
543
Simon Kelley37c9cce2013-01-09 19:51:04 +0000544/* address OK if configured */
545struct dhcp_context *address6_valid(struct dhcp_context *context,
Simon Kelleyc6309242013-03-07 20:59:28 +0000546 struct in6_addr *taddr,
547 struct dhcp_netid *netids,
548 int plain_range)
Simon Kelley37c9cce2013-01-09 19:51:04 +0000549{
550 struct dhcp_context *tmp;
551
552 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelleybaeb3ad2013-01-10 11:47:38 +0000553 if (is_same_net6(&tmp->start6, taddr, tmp->prefix) &&
Simon Kelleyc6309242013-03-07 20:59:28 +0000554 match_netid(tmp->filter, netids, plain_range))
Simon Kelley37c9cce2013-01-09 19:51:04 +0000555 return tmp;
556
557 return NULL;
558}
559
Simon Kelleyde92b472013-03-15 18:25:10 +0000560int config_valid(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr)
Simon Kelley4cb1b322012-02-06 14:30:41 +0000561{
Simon Kelleyde92b472013-03-15 18:25:10 +0000562 if (!config || !(config->flags & CONFIG_ADDR6))
563 return 0;
564
565 if ((config->flags & CONFIG_WILDCARD) && context->prefix == 64)
Simon Kelley30393102013-01-17 16:34:16 +0000566 {
Simon Kelleyde92b472013-03-15 18:25:10 +0000567 *addr = context->start6;
568 setaddr6part(addr, addr6part(&config->addr6));
Simon Kelley30393102013-01-17 16:34:16 +0000569 return 1;
570 }
571
Simon Kelleyde92b472013-03-15 18:25:10 +0000572 if (is_same_net6(&context->start6, &config->addr6, context->prefix))
573 {
574 *addr = config->addr6;
575 return 1;
576 }
577
Simon Kelleyb2692212012-09-16 22:15:56 +0100578 return 0;
579}
580
Simon Kelley4cb1b322012-02-06 14:30:41 +0000581void make_duid(time_t now)
582{
Simon Kelley3511a922013-11-07 10:28:11 +0000583 (void)now;
584
Simon Kelley8b372702012-03-09 17:45:10 +0000585 if (daemon->duid_config)
586 {
587 unsigned char *p;
588
589 daemon->duid = p = safe_malloc(daemon->duid_config_len + 6);
590 daemon->duid_len = daemon->duid_config_len + 6;
591 PUTSHORT(2, p); /* DUID_EN */
592 PUTLONG(daemon->duid_enterprise, p);
593 memcpy(p, daemon->duid_config, daemon->duid_config_len);
594 }
595 else
596 {
Simon Kelley3511a922013-11-07 10:28:11 +0000597 time_t newnow = 0;
598
599 /* If we have no persistent lease database, or a non-stable RTC, use DUID_LL (newnow == 0) */
600#ifndef HAVE_BROKEN_RTC
Simon Kelley8b372702012-03-09 17:45:10 +0000601 /* rebase epoch to 1/1/2000 */
Simon Kelley3511a922013-11-07 10:28:11 +0000602 if (!option_bool(OPT_LEASE_RO) || daemon->lease_change_command)
603 newnow = now - 946684800;
604#endif
Simon Kelley8b372702012-03-09 17:45:10 +0000605
606 iface_enumerate(AF_LOCAL, &newnow, make_duid1);
607
608 if(!daemon->duid)
609 die("Cannot create DHCPv6 server DUID: %s", NULL, EC_MISC);
610 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000611}
612
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000613static int make_duid1(int index, unsigned int type, char *mac, size_t maclen, void *parm)
Simon Kelley4cb1b322012-02-06 14:30:41 +0000614{
615 /* create DUID as specified in RFC3315. We use the MAC of the
Simon Kelley0f089832012-03-01 13:43:39 +0000616 first interface we find that isn't loopback or P-to-P and
617 has address-type < 256. Address types above 256 are things like
618 tunnels which don't have usable MAC addresses. */
Simon Kelley4cb1b322012-02-06 14:30:41 +0000619
620 unsigned char *p;
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000621 (void)index;
Vladislav Grishenko408c3682013-09-24 16:18:49 +0100622 (void)parm;
Simon Kelley3511a922013-11-07 10:28:11 +0000623 time_t newnow = *((time_t *)parm);
624
Simon Kelley0f089832012-03-01 13:43:39 +0000625 if (type >= 256)
626 return 1;
627
Simon Kelley3511a922013-11-07 10:28:11 +0000628 if (newnow == 0)
629 {
630 daemon->duid = p = safe_malloc(maclen + 4);
631 daemon->duid_len = maclen + 4;
632 PUTSHORT(3, p); /* DUID_LL */
633 PUTSHORT(type, p); /* address type */
634 }
635 else
636 {
637 daemon->duid = p = safe_malloc(maclen + 8);
638 daemon->duid_len = maclen + 8;
639 PUTSHORT(1, p); /* DUID_LLT */
640 PUTSHORT(type, p); /* address type */
641 PUTLONG(*((time_t *)parm), p); /* time */
642 }
643
Simon Kelley4cb1b322012-02-06 14:30:41 +0000644 memcpy(p, mac, maclen);
645
646 return 0;
647}
Simon Kelley1f776932012-12-16 19:46:08 +0000648
649struct cparam {
650 time_t now;
Simon Kelley0c050242012-12-22 22:13:19 +0000651 int newone, newname;
Simon Kelley1f776932012-12-16 19:46:08 +0000652};
653
654static int construct_worker(struct in6_addr *local, int prefix,
Simon Kelleybad7b872012-12-20 22:00:39 +0000655 int scope, int if_index, int flags,
Simon Kelley1f776932012-12-16 19:46:08 +0000656 int preferred, int valid, void *vparam)
657{
658 char ifrn_name[IFNAMSIZ];
659 struct in6_addr start6, end6;
660 struct dhcp_context *template, *context;
Simon Kelley515ba972020-01-27 23:30:10 +0000661 struct iname *tmp;
662
Simon Kelley1f776932012-12-16 19:46:08 +0000663 (void)scope;
Simon Kelleybad7b872012-12-20 22:00:39 +0000664 (void)flags;
Simon Kelleyed8b68a2012-12-21 16:23:26 +0000665 (void)valid;
666 (void)preferred;
Simon Kelley1f776932012-12-16 19:46:08 +0000667
668 struct cparam *param = vparam;
669
670 if (IN6_IS_ADDR_LOOPBACK(local) ||
671 IN6_IS_ADDR_LINKLOCAL(local) ||
672 IN6_IS_ADDR_MULTICAST(local))
673 return 1;
674
Simon Kelley861c8912013-09-25 15:30:30 +0100675 if (!(flags & IFACE_PERMANENT))
676 return 1;
677
678 if (flags & IFACE_DEPRECATED)
679 return 1;
680
Simon Kelley515ba972020-01-27 23:30:10 +0000681 /* Ignore interfaces where we're not doing RA/DHCP6 */
682 if (!indextoname(daemon->icmp6fd, if_index, ifrn_name) ||
683 !iface_check(AF_LOCAL, NULL, ifrn_name, NULL))
684 return 1;
Simon Kelley1f776932012-12-16 19:46:08 +0000685
Simon Kelley515ba972020-01-27 23:30:10 +0000686 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
687 if (tmp->name && wildcard_match(tmp->name, ifrn_name))
688 return 1;
689
Simon Kelley1f776932012-12-16 19:46:08 +0000690 for (template = daemon->dhcp6; template; template = template->next)
Simon Kelleydb0f4882018-06-07 21:37:02 +0100691 if (!(template->flags & (CONTEXT_TEMPLATE | CONTEXT_CONSTRUCTED)))
Simon Kelley1f776932012-12-16 19:46:08 +0000692 {
693 /* non-template entries, just fill in interface and local addresses */
Vladislav Grishenko4c82efc2013-12-03 16:05:30 +0000694 if (prefix <= template->prefix &&
695 is_same_net6(local, &template->start6, template->prefix) &&
696 is_same_net6(local, &template->end6, template->prefix))
Simon Kelley1f776932012-12-16 19:46:08 +0000697 {
Maarten de Vries0a496f02018-05-11 23:20:58 +0100698 /* First time found, do fast RA. */
Simon Kelley515ba972020-01-27 23:30:10 +0000699 if (template->if_index == 0)
Maarten de Vries0a496f02018-05-11 23:20:58 +0100700 {
701 ra_start_unsolicited(param->now, template);
702 param->newone = 1;
703 }
704
Simon Kelley1f776932012-12-16 19:46:08 +0000705 template->if_index = if_index;
706 template->local6 = *local;
707 }
708
709 }
Simon Kelley486479e2013-10-14 17:18:03 +0100710 else if (wildcard_match(template->template_interface, ifrn_name) &&
Vladislav Grishenko4c82efc2013-12-03 16:05:30 +0000711 template->prefix >= prefix)
Simon Kelley1f776932012-12-16 19:46:08 +0000712 {
713 start6 = *local;
714 setaddr6part(&start6, addr6part(&template->start6));
715 end6 = *local;
716 setaddr6part(&end6, addr6part(&template->end6));
717
718 for (context = daemon->dhcp6; context; context = context->next)
Simon Kelleyc488b682018-06-02 13:06:00 +0100719 if (!(context->flags & CONTEXT_TEMPLATE) &&
Simon Kelley1f776932012-12-16 19:46:08 +0000720 IN6_ARE_ADDR_EQUAL(&start6, &context->start6) &&
721 IN6_ARE_ADDR_EQUAL(&end6, &context->end6))
722 {
Simon Kelleydb0f4882018-06-07 21:37:02 +0100723 /* If there's an absolute address context covering this address
724 then don't construct one as well. */
725 if (!(context->flags & CONTEXT_CONSTRUCTED))
726 break;
727
728 if (context->if_index == if_index)
Simon Kelleyf7a40ec2013-07-27 13:36:08 +0100729 {
Simon Kelleyc488b682018-06-02 13:06:00 +0100730 int cflags = context->flags;
731 context->flags &= ~(CONTEXT_GC | CONTEXT_OLD);
732 if (cflags & CONTEXT_OLD)
733 {
Simon Kelleydb0f4882018-06-07 21:37:02 +0100734 /* address went, now it's back, and on the same interface */
Simon Kelleyc488b682018-06-02 13:06:00 +0100735 log_context(AF_INET6, context);
736 /* fast RAs for a while */
737 ra_start_unsolicited(param->now, context);
738 param->newone = 1;
739 /* Add address to name again */
740 if (context->flags & CONTEXT_RA_NAME)
741 param->newname = 1;
Simon Kelleydb0f4882018-06-07 21:37:02 +0100742
Simon Kelleyc488b682018-06-02 13:06:00 +0100743 }
Simon Kelley05ff6592018-06-12 16:03:09 +0100744 break;
Simon Kelleyf7a40ec2013-07-27 13:36:08 +0100745 }
Simon Kelley1f776932012-12-16 19:46:08 +0000746 }
747
748 if (!context && (context = whine_malloc(sizeof (struct dhcp_context))))
749 {
750 *context = *template;
751 context->start6 = start6;
752 context->end6 = end6;
753 context->flags &= ~CONTEXT_TEMPLATE;
754 context->flags |= CONTEXT_CONSTRUCTED;
755 context->if_index = if_index;
756 context->local6 = *local;
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100757 context->saved_valid = 0;
Simon Kelley1f776932012-12-16 19:46:08 +0000758
759 context->next = daemon->dhcp6;
760 daemon->dhcp6 = context;
761
Josh Soref730c6742017-02-06 16:14:04 +0000762 ra_start_unsolicited(param->now, context);
Simon Kelley1f776932012-12-16 19:46:08 +0000763 /* we created a new one, need to call
764 lease_update_file to get periodic functions called */
765 param->newone = 1;
Simon Kelley0c050242012-12-22 22:13:19 +0000766
767 /* Will need to add new putative SLAAC addresses to existing leases */
768 if (context->flags & CONTEXT_RA_NAME)
769 param->newname = 1;
Simon Kelley1f776932012-12-16 19:46:08 +0000770
771 log_context(AF_INET6, context);
772 }
Simon Kelley1f776932012-12-16 19:46:08 +0000773 }
774
775 return 1;
776}
777
778void dhcp_construct_contexts(time_t now)
779{
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100780 struct dhcp_context *context, *tmp, **up;
Simon Kelley1f776932012-12-16 19:46:08 +0000781 struct cparam param;
782 param.newone = 0;
Simon Kelley0c050242012-12-22 22:13:19 +0000783 param.newname = 0;
Simon Kelley1f776932012-12-16 19:46:08 +0000784 param.now = now;
785
786 for (context = daemon->dhcp6; context; context = context->next)
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100787 if (context->flags & CONTEXT_CONSTRUCTED)
788 context->flags |= CONTEXT_GC;
789
Simon Kelley1f776932012-12-16 19:46:08 +0000790 iface_enumerate(AF_INET6, &param, construct_worker);
791
792 for (up = &daemon->dhcp6, context = daemon->dhcp6; context; context = tmp)
793 {
Simon Kelley1f776932012-12-16 19:46:08 +0000794
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100795 tmp = context->next;
796
797 if (context->flags & CONTEXT_GC && !(context->flags & CONTEXT_OLD))
Simon Kelley1f776932012-12-16 19:46:08 +0000798 {
Simon Kelley7ea3d3f2014-04-25 22:04:05 +0100799 if ((context->flags & CONTEXT_RA) || option_bool(OPT_RA))
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100800 {
801 /* previously constructed context has gone. advertise it's demise */
802 context->flags |= CONTEXT_OLD;
803 context->address_lost_time = now;
Simon Kelley9f48ffa2013-07-28 15:47:04 +0100804 /* Apply same ceiling of configured lease time as in radv.c */
805 if (context->saved_valid > context->lease_time)
806 context->saved_valid = context->lease_time;
807 /* maximum time is 2 hours, from RFC */
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100808 if (context->saved_valid > 7200) /* 2 hours */
809 context->saved_valid = 7200;
Josh Soref730c6742017-02-06 16:14:04 +0000810 ra_start_unsolicited(now, context);
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100811 param.newone = 1; /* include deletion */
812
813 if (context->flags & CONTEXT_RA_NAME)
814 param.newname = 1;
815
816 log_context(AF_INET6, context);
817
818 up = &context->next;
819 }
820 else
821 {
822 /* we were never doing RA for this, so free now */
823 *up = context->next;
824 free(context);
825 }
Simon Kelley1f776932012-12-16 19:46:08 +0000826 }
827 else
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100828 up = &context->next;
Simon Kelley1f776932012-12-16 19:46:08 +0000829 }
830
831 if (param.newone)
Simon Kelley7558ecd2012-12-16 21:45:16 +0000832 {
833 if (daemon->dhcp || daemon->doing_dhcp6)
Simon Kelley0c050242012-12-22 22:13:19 +0000834 {
835 if (param.newname)
836 lease_update_slaac(now);
837 lease_update_file(now);
838 }
Simon Kelley7558ecd2012-12-16 21:45:16 +0000839 else
840 /* Not doing DHCP, so no lease system, manage alarms for ra only */
841 send_alarm(periodic_ra(now), now);
842 }
Simon Kelley1f776932012-12-16 19:46:08 +0000843}
Simon Kelley1f776932012-12-16 19:46:08 +0000844
Simon Kelleyc72daea2012-01-05 21:33:27 +0000845#endif
846
847