blob: 7e6e3df0bcff59fa69d4ac979e3a3ee81622ddb3 [file] [log] [blame]
Simon Kelleyd1ced3a2018-01-01 22:18:03 +00001/* dnsmasq is Copyright (c) 2000-2018 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 Kelleyff7eea22013-09-04 18:01:38 +0100138 if ((port = relay_reply6(&from, sz, ifr.ifr_name)) == 0)
Simon Kelleybe6cfb42012-10-16 20:38:31 +0100139 {
Neil Jerram0ddb8762015-06-10 22:11:06 +0100140 struct dhcp_bridge *bridge, *alias;
141
Simon Kelleyff7eea22013-09-04 18:01:38 +0100142 for (tmp = daemon->if_except; tmp; tmp = tmp->next)
Simon Kelley49333cb2013-03-15 20:30:51 +0000143 if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
Simon Kelleyff7eea22013-09-04 18:01:38 +0100144 return;
145
146 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
147 if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
148 return;
149
150 parm.current = NULL;
151 parm.relay = NULL;
152 memset(&parm.relay_local, 0, IN6ADDRSZ);
153 parm.ind = if_index;
154 parm.addr_match = 0;
155 memset(&parm.fallback, 0, IN6ADDRSZ);
Simon Kelleyc3a04082014-01-11 22:18:19 +0000156 memset(&parm.ll_addr, 0, IN6ADDRSZ);
157 memset(&parm.ula_addr, 0, IN6ADDRSZ);
Neil Jerram0ddb8762015-06-10 22:11:06 +0100158
159 /* If the interface on which the DHCPv6 request was received is
160 an alias of some other interface (as specified by the
Neil Jerram4918bd52015-06-10 22:23:20 +0100161 --bridge-interface option), change parm.ind so that we look
Neil Jerram0ddb8762015-06-10 22:11:06 +0100162 for DHCPv6 contexts associated with the aliased interface
163 instead of with the aliasing one. */
164 for (bridge = daemon->bridges; bridge; bridge = bridge->next)
165 {
166 for (alias = bridge->alias; alias; alias = alias->next)
167 if (wildcard_matchn(alias->iface, ifr.ifr_name, IF_NAMESIZE))
168 {
169 parm.ind = if_nametoindex(bridge->iface);
170 if (!parm.ind)
171 {
172 my_syslog(MS_DHCP | LOG_WARNING,
173 _("unknown interface %s in bridge-interface"),
174 bridge->iface);
175 return;
176 }
177 break;
178 }
179 if (alias)
180 break;
181 }
Simon Kelleyff7eea22013-09-04 18:01:38 +0100182
183 for (context = daemon->dhcp6; context; context = context->next)
184 if (IN6_IS_ADDR_UNSPECIFIED(&context->start6) && context->prefix == 0)
185 {
186 /* wildcard context for DHCP-stateless only */
187 parm.current = context;
188 context->current = NULL;
189 }
190 else
191 {
192 /* unlinked contexts are marked by context->current == context */
193 context->current = context;
194 memset(&context->local6, 0, IN6ADDRSZ);
195 }
Simon Kelleybe6cfb42012-10-16 20:38:31 +0100196
Simon Kelleyff7eea22013-09-04 18:01:38 +0100197 for (relay = daemon->relay6; relay; relay = relay->next)
198 relay->current = relay;
199
200 if (!iface_enumerate(AF_INET6, &parm, complete_context6))
Simon Kelleybe6cfb42012-10-16 20:38:31 +0100201 return;
Simon Kelley89500e32013-09-20 16:29:20 +0100202
Simon Kelleyff7eea22013-09-04 18:01:38 +0100203 if (daemon->if_names || daemon->if_addrs)
204 {
205
206 for (tmp = daemon->if_names; tmp; tmp = tmp->next)
207 if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
208 break;
209
210 if (!tmp && !parm.addr_match)
211 return;
212 }
213
214 if (parm.relay)
215 {
Simon Kelley89500e32013-09-20 16:29:20 +0100216 /* Ignore requests sent to the ALL_SERVERS multicast address for relay when
217 we're listening there for DHCPv6 server reasons. */
218 struct in6_addr all_servers;
219
220 inet_pton(AF_INET6, ALL_SERVERS, &all_servers);
221
222 if (!IN6_ARE_ADDR_EQUAL(&dst_addr, &all_servers))
Simon Kelley33702ab2015-12-28 23:17:15 +0000223 relay_upstream6(parm.relay, sz, &from.sin6_addr, from.sin6_scope_id, now);
Simon Kelleyff7eea22013-09-04 18:01:38 +0100224 return;
225 }
226
227 /* May have configured relay, but not DHCP server */
228 if (!daemon->doing_dhcp6)
229 return;
Neil Jerram0ddb8762015-06-10 22:11:06 +0100230
Simon Kelleyff7eea22013-09-04 18:01:38 +0100231 lease_prune(NULL, now); /* lose any expired leases */
232
233 port = dhcp6_reply(parm.current, if_index, ifr.ifr_name, &parm.fallback,
Simon Kelleyc3a04082014-01-11 22:18:19 +0000234 &parm.ll_addr, &parm.ula_addr, sz, &from.sin6_addr, now);
Simon Kelleyff7eea22013-09-04 18:01:38 +0100235
236 lease_update_file(now);
237 lease_update_dns(0);
Simon Kelleybe6cfb42012-10-16 20:38:31 +0100238 }
Simon Kelleyff7eea22013-09-04 18:01:38 +0100239
Simon Kelley1d0f91c2012-03-12 11:56:22 +0000240 /* The port in the source address of the original request should
241 be correct, but at least once client sends from the server port,
242 so we explicitly send to the client port to a client, and the
243 server port to a relay. */
244 if (port != 0)
245 {
246 from.sin6_port = htons(port);
Simon Kelleyff841eb2015-03-11 21:36:30 +0000247 while (retry_send(sendto(daemon->dhcp6fd, daemon->outpacket.iov_base,
248 save_counter(0), 0, (struct sockaddr *)&from,
249 sizeof(from))));
Simon Kelley1d0f91c2012-03-12 11:56:22 +0000250 }
Simon Kelleyc72daea2012-01-05 21:33:27 +0000251}
252
Simon Kelley33702ab2015-12-28 23:17:15 +0000253void 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 +0100254{
Josh Soref730c6742017-02-06 16:14:04 +0000255 /* Receiving a packet from a host does not populate the neighbour
Simon Kelley8939c952013-09-25 11:49:34 +0100256 cache, so we send a neighbour discovery request if we can't
257 find the sender. Repeat a few times in case of packet loss. */
258
259 struct neigh_packet neigh;
Simon Kelley11867dc2015-12-23 16:15:58 +0000260 union mysockaddr addr;
261 int i, maclen;
Simon Kelley8939c952013-09-25 11:49:34 +0100262
263 neigh.type = ND_NEIGHBOR_SOLICIT;
264 neigh.code = 0;
265 neigh.reserved = 0;
266 neigh.target = *client;
Tomas Hozza0705a7e2015-02-23 21:26:26 +0000267 /* RFC4443 section-2.3: checksum has to be zero to be calculated */
268 neigh.checksum = 0;
269
Simon Kelley8939c952013-09-25 11:49:34 +0100270 memset(&addr, 0, sizeof(addr));
271#ifdef HAVE_SOCKADDR_SA_LEN
Simon Kelley11867dc2015-12-23 16:15:58 +0000272 addr.in6.sin6_len = sizeof(struct sockaddr_in6);
Simon Kelley8939c952013-09-25 11:49:34 +0100273#endif
Simon Kelley11867dc2015-12-23 16:15:58 +0000274 addr.in6.sin6_family = AF_INET6;
275 addr.in6.sin6_port = htons(IPPROTO_ICMPV6);
276 addr.in6.sin6_addr = *client;
277 addr.in6.sin6_scope_id = iface;
Simon Kelley8939c952013-09-25 11:49:34 +0100278
279 for (i = 0; i < 5; i++)
280 {
281 struct timespec ts;
282
Simon Kelley33702ab2015-12-28 23:17:15 +0000283 if ((maclen = find_mac(&addr, mac, 0, now)) != 0)
Simon Kelley8939c952013-09-25 11:49:34 +0100284 break;
Simon Kelley11867dc2015-12-23 16:15:58 +0000285
286 sendto(daemon->icmp6fd, &neigh, sizeof(neigh), 0, &addr.sa, sizeof(addr));
Simon Kelley8939c952013-09-25 11:49:34 +0100287
288 ts.tv_sec = 0;
289 ts.tv_nsec = 100000000; /* 100ms */
290 nanosleep(&ts, NULL);
291 }
292
Simon Kelley11867dc2015-12-23 16:15:58 +0000293 *maclenp = maclen;
Simon Kelley8939c952013-09-25 11:49:34 +0100294 *mactypep = ARPHRD_ETHER;
295}
296
Simon Kelleyc72daea2012-01-05 21:33:27 +0000297static int complete_context6(struct in6_addr *local, int prefix,
Simon Kelley3bc0d932012-12-28 11:31:44 +0000298 int scope, int if_index, int flags, unsigned int preferred,
299 unsigned int valid, void *vparam)
Simon Kelleyc72daea2012-01-05 21:33:27 +0000300{
301 struct dhcp_context *context;
Simon Kelleyff7eea22013-09-04 18:01:38 +0100302 struct dhcp_relay *relay;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000303 struct iface_param *param = vparam;
Simon Kelleybe6cfb42012-10-16 20:38:31 +0100304 struct iname *tmp;
305
Simon Kelley52b92f42012-01-22 16:05:15 +0000306 (void)scope; /* warning */
Simon Kelley3bc0d932012-12-28 11:31:44 +0000307
Simon Kelleyff7eea22013-09-04 18:01:38 +0100308 if (if_index == param->ind)
Simon Kelleyc72daea2012-01-05 21:33:27 +0000309 {
Simon Kelleyc3a04082014-01-11 22:18:19 +0000310 if (IN6_IS_ADDR_LINKLOCAL(local))
311 param->ll_addr = *local;
312 else if (IN6_IS_ADDR_ULA(local))
313 param->ula_addr = *local;
314
Simon Kelleyff7eea22013-09-04 18:01:38 +0100315 if (!IN6_IS_ADDR_LOOPBACK(local) &&
316 !IN6_IS_ADDR_LINKLOCAL(local) &&
317 !IN6_IS_ADDR_MULTICAST(local))
Simon Kelleye44ddca2012-02-18 17:08:50 +0000318 {
Simon Kelleyff7eea22013-09-04 18:01:38 +0100319 /* if we have --listen-address config, see if the
320 arrival interface has a matching address. */
321 for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
322 if (tmp->addr.sa.sa_family == AF_INET6 &&
323 IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, local))
324 param->addr_match = 1;
325
326 /* Determine a globally address on the arrival interface, even
327 if we have no matching dhcp-context, because we're only
328 allocating on remote subnets via relays. This
329 is used as a default for the DNS server option. */
330 param->fallback = *local;
331
332 for (context = daemon->dhcp6; context; context = context->next)
Simon Kelleye44ddca2012-02-18 17:08:50 +0000333 {
Simon Kelleyff7eea22013-09-04 18:01:38 +0100334 if ((context->flags & CONTEXT_DHCP) &&
335 !(context->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) &&
Vladislav Grishenko4c82efc2013-12-03 16:05:30 +0000336 prefix <= context->prefix &&
337 is_same_net6(local, &context->start6, context->prefix) &&
338 is_same_net6(local, &context->end6, context->prefix))
Simon Kelleye44ddca2012-02-18 17:08:50 +0000339 {
Simon Kelley3bc0d932012-12-28 11:31:44 +0000340
Simon Kelley3bc0d932012-12-28 11:31:44 +0000341
Simon Kelleyff7eea22013-09-04 18:01:38 +0100342 /* link it onto the current chain if we've not seen it before */
343 if (context->current == context)
344 {
345 struct dhcp_context *tmp, **up;
346
Josh Soref730c6742017-02-06 16:14:04 +0000347 /* use interface values only for constructed contexts */
Simon Kelleyff7eea22013-09-04 18:01:38 +0100348 if (!(context->flags & CONTEXT_CONSTRUCTED))
349 preferred = valid = 0xffffffff;
350 else if (flags & IFACE_DEPRECATED)
351 preferred = 0;
352
353 if (context->flags & CONTEXT_DEPRECATE)
354 preferred = 0;
355
356 /* order chain, longest preferred time first */
357 for (up = &param->current, tmp = param->current; tmp; tmp = tmp->current)
358 if (tmp->preferred <= preferred)
359 break;
360 else
361 up = &tmp->current;
362
363 context->current = *up;
364 *up = context;
365 context->local6 = *local;
366 context->preferred = preferred;
367 context->valid = valid;
368 }
Simon Kelleye44ddca2012-02-18 17:08:50 +0000369 }
370 }
Simon Kelleyc72daea2012-01-05 21:33:27 +0000371 }
Simon Kelleyff7eea22013-09-04 18:01:38 +0100372
373 for (relay = daemon->relay6; relay; relay = relay->next)
Simon Kelleycc921df2019-01-02 22:48:59 +0000374 if (IN6_ARE_ADDR_EQUAL(local, &relay->local.addr6) && relay->current == relay &&
Simon Kelleyff7eea22013-09-04 18:01:38 +0100375 (IN6_IS_ADDR_UNSPECIFIED(&param->relay_local) || IN6_ARE_ADDR_EQUAL(local, &param->relay_local)))
376 {
377 relay->current = param->relay;
378 param->relay = relay;
379 param->relay_local = *local;
380 }
381
Simon Kelleyc72daea2012-01-05 21:33:27 +0000382 }
Simon Kelleyff7eea22013-09-04 18:01:38 +0100383
384 return 1;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000385}
Simon Kelley52b92f42012-01-22 16:05:15 +0000386
387struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct in6_addr *net, int prefix, u64 addr)
388{
389 struct dhcp_config *config;
390
391 for (config = configs; config; config = config->next)
392 if ((config->flags & CONFIG_ADDR6) &&
393 is_same_net6(&config->addr6, net, prefix) &&
394 (prefix == 128 || addr6part(&config->addr6) == addr))
395 return config;
396
397 return NULL;
398}
399
Simon Kelley6586e832013-11-07 14:20:13 +0000400struct dhcp_context *address6_allocate(struct dhcp_context *context, unsigned char *clid, int clid_len, int temp_addr,
Simon Kelleyc6309242013-03-07 20:59:28 +0000401 int iaid, int serial, struct dhcp_netid *netids, int plain_range, struct in6_addr *ans)
Simon Kelley52b92f42012-01-22 16:05:15 +0000402{
403 /* Find a free address: exclude anything in use and anything allocated to
404 a particular hwaddr/clientid/hostname in our configuration.
405 Try to return from contexts which match netids first.
406
407 Note that we assume the address prefix lengths are 64 or greater, so we can
408 get by with 64 bit arithmetic.
409*/
410
411 u64 start, addr;
412 struct dhcp_context *c, *d;
413 int i, pass;
414 u64 j;
415
416 /* hash hwaddr: use the SDBM hashing algorithm. This works
Simon Kelley6586e832013-11-07 14:20:13 +0000417 for MAC addresses, let's see how it manages with client-ids!
418 For temporary addresses, we generate a new random one each time. */
419 if (temp_addr)
420 j = rand64();
421 else
422 for (j = iaid, i = 0; i < clid_len; i++)
Simon Kelleyd6b749a2016-04-25 17:05:15 +0100423 j = clid[i] + (j << 6) + (j << 16) - j;
Simon Kelley52b92f42012-01-22 16:05:15 +0000424
Simon Kelleyc6309242013-03-07 20:59:28 +0000425 for (pass = 0; pass <= plain_range ? 1 : 0; pass++)
Simon Kelley52b92f42012-01-22 16:05:15 +0000426 for (c = context; c; c = c->current)
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000427 if (c->flags & (CONTEXT_DEPRECATE | CONTEXT_STATIC | CONTEXT_RA_STATELESS | CONTEXT_USED))
Simon Kelley52b92f42012-01-22 16:05:15 +0000428 continue;
429 else if (!match_netid(c->filter, netids, pass))
430 continue;
431 else
Simon Kelley07933802012-02-14 20:55:25 +0000432 {
Simon Kelley6586e832013-11-07 14:20:13 +0000433 if (!temp_addr && option_bool(OPT_CONSEC_ADDR))
Simon Kelleye7bfd552018-12-31 20:51:15 +0000434 {
435 /* seed is largest extant lease addr in this context,
436 skip addresses equal to the number of addresses rejected
437 by clients. This should avoid the same client being offered the same
438 address after it has rjected it. */
Simon Kelley9c0d4452019-01-09 17:57:56 +0000439 start = lease_find_max_addr6(c) + 1 + serial + c->addr_epoch;
Simon Kelleye7bfd552018-12-31 20:51:15 +0000440 if (c->addr_epoch)
441 c->addr_epoch--;
442 }
Simon Kelley07933802012-02-14 20:55:25 +0000443 else
Simon Kelleyfdc97e12016-02-13 17:47:17 +0000444 {
445 u64 range = 1 + addr6part(&c->end6) - addr6part(&c->start6);
446 u64 offset = j + c->addr_epoch;
447
448 /* don't divide by zero if range is whole 2^64 */
449 if (range != 0)
450 offset = offset % range;
451
452 start = addr6part(&c->start6) + offset;
453 }
Simon Kelley52b92f42012-01-22 16:05:15 +0000454
455 /* iterate until we find a free address. */
456 addr = start;
457
458 do {
459 /* eliminate addresses in use by the server. */
460 for (d = context; d; d = d->current)
Simon Kelleye44ddca2012-02-18 17:08:50 +0000461 if (addr == addr6part(&d->local6))
Simon Kelley52b92f42012-01-22 16:05:15 +0000462 break;
463
464 if (!d &&
465 !lease6_find_by_addr(&c->start6, c->prefix, addr) &&
466 !config_find_by_address6(daemon->dhcp_conf, &c->start6, c->prefix, addr))
467 {
468 *ans = c->start6;
469 setaddr6part (ans, addr);
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000470 return c;
Simon Kelley52b92f42012-01-22 16:05:15 +0000471 }
472
473 addr++;
474
475 if (addr == addr6part(&c->end6) + 1)
476 addr = addr6part(&c->start6);
477
478 } while (addr != start);
479 }
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000480
Simon Kelley5b37aa82013-04-02 16:32:25 +0100481 return NULL;
Simon Kelley52b92f42012-01-22 16:05:15 +0000482}
483
Simon Kelley37c9cce2013-01-09 19:51:04 +0000484/* can dynamically allocate addr */
Simon Kelley52b92f42012-01-22 16:05:15 +0000485struct dhcp_context *address6_available(struct dhcp_context *context,
486 struct in6_addr *taddr,
Simon Kelleyc6309242013-03-07 20:59:28 +0000487 struct dhcp_netid *netids,
488 int plain_range)
Simon Kelley52b92f42012-01-22 16:05:15 +0000489{
490 u64 start, end, addr = addr6part(taddr);
491 struct dhcp_context *tmp;
492
493 for (tmp = context; tmp; tmp = tmp->current)
494 {
495 start = addr6part(&tmp->start6);
496 end = addr6part(&tmp->end6);
497
Simon Kelleyc8257542012-03-28 21:15:41 +0100498 if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_RA_STATELESS)) &&
499 is_same_net6(&tmp->start6, taddr, tmp->prefix) &&
500 is_same_net6(&tmp->end6, taddr, tmp->prefix) &&
Simon Kelley52b92f42012-01-22 16:05:15 +0000501 addr >= start &&
502 addr <= end &&
Simon Kelleyc6309242013-03-07 20:59:28 +0000503 match_netid(tmp->filter, netids, plain_range))
Simon Kelley52b92f42012-01-22 16:05:15 +0000504 return tmp;
505 }
506
507 return NULL;
508}
509
Simon Kelley37c9cce2013-01-09 19:51:04 +0000510/* address OK if configured */
511struct dhcp_context *address6_valid(struct dhcp_context *context,
Simon Kelleyc6309242013-03-07 20:59:28 +0000512 struct in6_addr *taddr,
513 struct dhcp_netid *netids,
514 int plain_range)
Simon Kelley37c9cce2013-01-09 19:51:04 +0000515{
516 struct dhcp_context *tmp;
517
518 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelleybaeb3ad2013-01-10 11:47:38 +0000519 if (is_same_net6(&tmp->start6, taddr, tmp->prefix) &&
Simon Kelleyc6309242013-03-07 20:59:28 +0000520 match_netid(tmp->filter, netids, plain_range))
Simon Kelley37c9cce2013-01-09 19:51:04 +0000521 return tmp;
522
523 return NULL;
524}
525
Simon Kelleyde92b472013-03-15 18:25:10 +0000526int config_valid(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr)
Simon Kelley4cb1b322012-02-06 14:30:41 +0000527{
Simon Kelleyde92b472013-03-15 18:25:10 +0000528 if (!config || !(config->flags & CONFIG_ADDR6))
529 return 0;
530
531 if ((config->flags & CONFIG_WILDCARD) && context->prefix == 64)
Simon Kelley30393102013-01-17 16:34:16 +0000532 {
Simon Kelleyde92b472013-03-15 18:25:10 +0000533 *addr = context->start6;
534 setaddr6part(addr, addr6part(&config->addr6));
Simon Kelley30393102013-01-17 16:34:16 +0000535 return 1;
536 }
537
Simon Kelleyde92b472013-03-15 18:25:10 +0000538 if (is_same_net6(&context->start6, &config->addr6, context->prefix))
539 {
540 *addr = config->addr6;
541 return 1;
542 }
543
Simon Kelleyb2692212012-09-16 22:15:56 +0100544 return 0;
545}
546
Simon Kelley4cb1b322012-02-06 14:30:41 +0000547void make_duid(time_t now)
548{
Simon Kelley3511a922013-11-07 10:28:11 +0000549 (void)now;
550
Simon Kelley8b372702012-03-09 17:45:10 +0000551 if (daemon->duid_config)
552 {
553 unsigned char *p;
554
555 daemon->duid = p = safe_malloc(daemon->duid_config_len + 6);
556 daemon->duid_len = daemon->duid_config_len + 6;
557 PUTSHORT(2, p); /* DUID_EN */
558 PUTLONG(daemon->duid_enterprise, p);
559 memcpy(p, daemon->duid_config, daemon->duid_config_len);
560 }
561 else
562 {
Simon Kelley3511a922013-11-07 10:28:11 +0000563 time_t newnow = 0;
564
565 /* If we have no persistent lease database, or a non-stable RTC, use DUID_LL (newnow == 0) */
566#ifndef HAVE_BROKEN_RTC
Simon Kelley8b372702012-03-09 17:45:10 +0000567 /* rebase epoch to 1/1/2000 */
Simon Kelley3511a922013-11-07 10:28:11 +0000568 if (!option_bool(OPT_LEASE_RO) || daemon->lease_change_command)
569 newnow = now - 946684800;
570#endif
Simon Kelley8b372702012-03-09 17:45:10 +0000571
572 iface_enumerate(AF_LOCAL, &newnow, make_duid1);
573
574 if(!daemon->duid)
575 die("Cannot create DHCPv6 server DUID: %s", NULL, EC_MISC);
576 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000577}
578
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000579static int make_duid1(int index, unsigned int type, char *mac, size_t maclen, void *parm)
Simon Kelley4cb1b322012-02-06 14:30:41 +0000580{
581 /* create DUID as specified in RFC3315. We use the MAC of the
Simon Kelley0f089832012-03-01 13:43:39 +0000582 first interface we find that isn't loopback or P-to-P and
583 has address-type < 256. Address types above 256 are things like
584 tunnels which don't have usable MAC addresses. */
Simon Kelley4cb1b322012-02-06 14:30:41 +0000585
586 unsigned char *p;
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000587 (void)index;
Vladislav Grishenko408c3682013-09-24 16:18:49 +0100588 (void)parm;
Simon Kelley3511a922013-11-07 10:28:11 +0000589 time_t newnow = *((time_t *)parm);
590
Simon Kelley0f089832012-03-01 13:43:39 +0000591 if (type >= 256)
592 return 1;
593
Simon Kelley3511a922013-11-07 10:28:11 +0000594 if (newnow == 0)
595 {
596 daemon->duid = p = safe_malloc(maclen + 4);
597 daemon->duid_len = maclen + 4;
598 PUTSHORT(3, p); /* DUID_LL */
599 PUTSHORT(type, p); /* address type */
600 }
601 else
602 {
603 daemon->duid = p = safe_malloc(maclen + 8);
604 daemon->duid_len = maclen + 8;
605 PUTSHORT(1, p); /* DUID_LLT */
606 PUTSHORT(type, p); /* address type */
607 PUTLONG(*((time_t *)parm), p); /* time */
608 }
609
Simon Kelley4cb1b322012-02-06 14:30:41 +0000610 memcpy(p, mac, maclen);
611
612 return 0;
613}
Simon Kelley1f776932012-12-16 19:46:08 +0000614
615struct cparam {
616 time_t now;
Simon Kelley0c050242012-12-22 22:13:19 +0000617 int newone, newname;
Simon Kelley1f776932012-12-16 19:46:08 +0000618};
619
620static int construct_worker(struct in6_addr *local, int prefix,
Simon Kelleybad7b872012-12-20 22:00:39 +0000621 int scope, int if_index, int flags,
Simon Kelley1f776932012-12-16 19:46:08 +0000622 int preferred, int valid, void *vparam)
623{
624 char ifrn_name[IFNAMSIZ];
625 struct in6_addr start6, end6;
626 struct dhcp_context *template, *context;
627
628 (void)scope;
Simon Kelleybad7b872012-12-20 22:00:39 +0000629 (void)flags;
Simon Kelleyed8b68a2012-12-21 16:23:26 +0000630 (void)valid;
631 (void)preferred;
Simon Kelley1f776932012-12-16 19:46:08 +0000632
633 struct cparam *param = vparam;
634
635 if (IN6_IS_ADDR_LOOPBACK(local) ||
636 IN6_IS_ADDR_LINKLOCAL(local) ||
637 IN6_IS_ADDR_MULTICAST(local))
638 return 1;
639
Simon Kelley861c8912013-09-25 15:30:30 +0100640 if (!(flags & IFACE_PERMANENT))
641 return 1;
642
643 if (flags & IFACE_DEPRECATED)
644 return 1;
645
Simon Kelleya8105592013-09-25 15:36:00 +0100646 if (!indextoname(daemon->icmp6fd, if_index, ifrn_name))
Simon Kelley1f776932012-12-16 19:46:08 +0000647 return 0;
648
649 for (template = daemon->dhcp6; template; template = template->next)
Simon Kelleydb0f4882018-06-07 21:37:02 +0100650 if (!(template->flags & (CONTEXT_TEMPLATE | CONTEXT_CONSTRUCTED)))
Simon Kelley1f776932012-12-16 19:46:08 +0000651 {
652 /* non-template entries, just fill in interface and local addresses */
Vladislav Grishenko4c82efc2013-12-03 16:05:30 +0000653 if (prefix <= template->prefix &&
654 is_same_net6(local, &template->start6, template->prefix) &&
655 is_same_net6(local, &template->end6, template->prefix))
Simon Kelley1f776932012-12-16 19:46:08 +0000656 {
Maarten de Vries0a496f02018-05-11 23:20:58 +0100657 /* First time found, do fast RA. */
658 if (template->if_index != if_index || !IN6_ARE_ADDR_EQUAL(&template->local6, local))
659 {
660 ra_start_unsolicited(param->now, template);
661 param->newone = 1;
662 }
663
Simon Kelley1f776932012-12-16 19:46:08 +0000664 template->if_index = if_index;
665 template->local6 = *local;
666 }
667
668 }
Simon Kelley486479e2013-10-14 17:18:03 +0100669 else if (wildcard_match(template->template_interface, ifrn_name) &&
Vladislav Grishenko4c82efc2013-12-03 16:05:30 +0000670 template->prefix >= prefix)
Simon Kelley1f776932012-12-16 19:46:08 +0000671 {
672 start6 = *local;
673 setaddr6part(&start6, addr6part(&template->start6));
674 end6 = *local;
675 setaddr6part(&end6, addr6part(&template->end6));
676
677 for (context = daemon->dhcp6; context; context = context->next)
Simon Kelleyc488b682018-06-02 13:06:00 +0100678 if (!(context->flags & CONTEXT_TEMPLATE) &&
Simon Kelley1f776932012-12-16 19:46:08 +0000679 IN6_ARE_ADDR_EQUAL(&start6, &context->start6) &&
680 IN6_ARE_ADDR_EQUAL(&end6, &context->end6))
681 {
Simon Kelleydb0f4882018-06-07 21:37:02 +0100682 /* If there's an absolute address context covering this address
683 then don't construct one as well. */
684 if (!(context->flags & CONTEXT_CONSTRUCTED))
685 break;
686
687 if (context->if_index == if_index)
Simon Kelleyf7a40ec2013-07-27 13:36:08 +0100688 {
Simon Kelleyc488b682018-06-02 13:06:00 +0100689 int cflags = context->flags;
690 context->flags &= ~(CONTEXT_GC | CONTEXT_OLD);
691 if (cflags & CONTEXT_OLD)
692 {
Simon Kelleydb0f4882018-06-07 21:37:02 +0100693 /* address went, now it's back, and on the same interface */
Simon Kelleyc488b682018-06-02 13:06:00 +0100694 log_context(AF_INET6, context);
695 /* fast RAs for a while */
696 ra_start_unsolicited(param->now, context);
697 param->newone = 1;
698 /* Add address to name again */
699 if (context->flags & CONTEXT_RA_NAME)
700 param->newname = 1;
Simon Kelleydb0f4882018-06-07 21:37:02 +0100701
Simon Kelleyc488b682018-06-02 13:06:00 +0100702 }
Simon Kelley05ff6592018-06-12 16:03:09 +0100703 break;
Simon Kelleyf7a40ec2013-07-27 13:36:08 +0100704 }
Simon Kelley1f776932012-12-16 19:46:08 +0000705 }
706
707 if (!context && (context = whine_malloc(sizeof (struct dhcp_context))))
708 {
709 *context = *template;
710 context->start6 = start6;
711 context->end6 = end6;
712 context->flags &= ~CONTEXT_TEMPLATE;
713 context->flags |= CONTEXT_CONSTRUCTED;
714 context->if_index = if_index;
715 context->local6 = *local;
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100716 context->saved_valid = 0;
Simon Kelley1f776932012-12-16 19:46:08 +0000717
718 context->next = daemon->dhcp6;
719 daemon->dhcp6 = context;
720
Josh Soref730c6742017-02-06 16:14:04 +0000721 ra_start_unsolicited(param->now, context);
Simon Kelley1f776932012-12-16 19:46:08 +0000722 /* we created a new one, need to call
723 lease_update_file to get periodic functions called */
724 param->newone = 1;
Simon Kelley0c050242012-12-22 22:13:19 +0000725
726 /* Will need to add new putative SLAAC addresses to existing leases */
727 if (context->flags & CONTEXT_RA_NAME)
728 param->newname = 1;
Simon Kelley1f776932012-12-16 19:46:08 +0000729
730 log_context(AF_INET6, context);
731 }
Simon Kelley1f776932012-12-16 19:46:08 +0000732 }
733
734 return 1;
735}
736
737void dhcp_construct_contexts(time_t now)
738{
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100739 struct dhcp_context *context, *tmp, **up;
Simon Kelley1f776932012-12-16 19:46:08 +0000740 struct cparam param;
741 param.newone = 0;
Simon Kelley0c050242012-12-22 22:13:19 +0000742 param.newname = 0;
Simon Kelley1f776932012-12-16 19:46:08 +0000743 param.now = now;
744
745 for (context = daemon->dhcp6; context; context = context->next)
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100746 if (context->flags & CONTEXT_CONSTRUCTED)
747 context->flags |= CONTEXT_GC;
748
Simon Kelley1f776932012-12-16 19:46:08 +0000749 iface_enumerate(AF_INET6, &param, construct_worker);
750
751 for (up = &daemon->dhcp6, context = daemon->dhcp6; context; context = tmp)
752 {
Simon Kelley1f776932012-12-16 19:46:08 +0000753
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100754 tmp = context->next;
755
756 if (context->flags & CONTEXT_GC && !(context->flags & CONTEXT_OLD))
Simon Kelley1f776932012-12-16 19:46:08 +0000757 {
Simon Kelley7ea3d3f2014-04-25 22:04:05 +0100758 if ((context->flags & CONTEXT_RA) || option_bool(OPT_RA))
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100759 {
760 /* previously constructed context has gone. advertise it's demise */
761 context->flags |= CONTEXT_OLD;
762 context->address_lost_time = now;
Simon Kelley9f48ffa2013-07-28 15:47:04 +0100763 /* Apply same ceiling of configured lease time as in radv.c */
764 if (context->saved_valid > context->lease_time)
765 context->saved_valid = context->lease_time;
766 /* maximum time is 2 hours, from RFC */
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100767 if (context->saved_valid > 7200) /* 2 hours */
768 context->saved_valid = 7200;
Josh Soref730c6742017-02-06 16:14:04 +0000769 ra_start_unsolicited(now, context);
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100770 param.newone = 1; /* include deletion */
771
772 if (context->flags & CONTEXT_RA_NAME)
773 param.newname = 1;
774
775 log_context(AF_INET6, context);
776
777 up = &context->next;
778 }
779 else
780 {
781 /* we were never doing RA for this, so free now */
782 *up = context->next;
783 free(context);
784 }
Simon Kelley1f776932012-12-16 19:46:08 +0000785 }
786 else
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100787 up = &context->next;
Simon Kelley1f776932012-12-16 19:46:08 +0000788 }
789
790 if (param.newone)
Simon Kelley7558ecd2012-12-16 21:45:16 +0000791 {
792 if (daemon->dhcp || daemon->doing_dhcp6)
Simon Kelley0c050242012-12-22 22:13:19 +0000793 {
794 if (param.newname)
795 lease_update_slaac(now);
796 lease_update_file(now);
797 }
Simon Kelley7558ecd2012-12-16 21:45:16 +0000798 else
799 /* Not doing DHCP, so no lease system, manage alarms for ra only */
800 send_alarm(periodic_ra(now), now);
801 }
Simon Kelley1f776932012-12-16 19:46:08 +0000802}
Simon Kelley1f776932012-12-16 19:46:08 +0000803
Simon Kelleyc72daea2012-01-05 21:33:27 +0000804#endif
805
806