blob: 5e151d6d772beb14d62b245da1cb45499d8b16dd [file] [log] [blame]
Simon Kelley61744352013-01-31 14:34:40 +00001/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
Simon Kelleyc72daea2012-01-05 21:33:27 +00002
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; version 2 dated June, 1991, or
6 (at your option) version 3 dated 29 June, 2007.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program. If not, see <http://www.gnu.org/licenses/>.
15*/
16
17#include "dnsmasq.h"
18
19#ifdef HAVE_DHCP6
20
Simon Kelley89500e32013-09-20 16:29:20 +010021#include <netinet/icmp6.h>
22
Simon Kelleyc72daea2012-01-05 21:33:27 +000023struct iface_param {
24 struct dhcp_context *current;
Simon Kelleyff7eea22013-09-04 18:01:38 +010025 struct dhcp_relay *relay;
26 struct in6_addr fallback, relay_local;
Simon Kelleybe6cfb42012-10-16 20:38:31 +010027 int ind, addr_match;
Simon Kelleyc72daea2012-01-05 21:33:27 +000028};
29
Simon Kelley89500e32013-09-20 16:29:20 +010030struct mac_param {
31 struct in6_addr *target;
32 unsigned char mac[DHCP_CHADDR_MAX];
33 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;
Simon Kelley89500e32013-09-20 16:29:20 +0100102 struct mac_param mac_param;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000103 struct cmsghdr *cmptr;
104 struct msghdr msg;
105 int if_index = 0;
106 union {
107 struct cmsghdr align; /* this ensures alignment */
108 char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
109 } control_u;
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000110 struct sockaddr_in6 from;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000111 ssize_t sz;
112 struct ifreq ifr;
113 struct iname *tmp;
Simon Kelley1d0f91c2012-03-12 11:56:22 +0000114 unsigned short port;
Simon Kelley89500e32013-09-20 16:29:20 +0100115 struct in6_addr dst_addr;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000116
Simon Kelley8f51a292013-09-21 14:07:12 +0100117 memset(&dst_addr, 0, sizeof(dst_addr));
118
Simon Kelleyc72daea2012-01-05 21:33:27 +0000119 msg.msg_control = control_u.control6;
120 msg.msg_controllen = sizeof(control_u);
121 msg.msg_flags = 0;
122 msg.msg_name = &from;
123 msg.msg_namelen = sizeof(from);
124 msg.msg_iov = &daemon->dhcp_packet;
125 msg.msg_iovlen = 1;
126
Simon Kelley1d0f91c2012-03-12 11:56:22 +0000127 if ((sz = recv_dhcp_packet(daemon->dhcp6fd, &msg)) == -1)
Simon Kelleyc72daea2012-01-05 21:33:27 +0000128 return;
129
130 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
131 if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
132 {
133 union {
134 unsigned char *c;
135 struct in6_pktinfo *p;
136 } p;
137 p.c = CMSG_DATA(cmptr);
138
139 if_index = p.p->ipi6_ifindex;
Simon Kelley89500e32013-09-20 16:29:20 +0100140 dst_addr = p.p->ipi6_addr;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000141 }
142
143 if (!indextoname(daemon->dhcp6fd, if_index, ifr.ifr_name))
144 return;
Simon Kelleybe6cfb42012-10-16 20:38:31 +0100145
Simon Kelleyff7eea22013-09-04 18:01:38 +0100146 if ((port = relay_reply6(&from, sz, ifr.ifr_name)) == 0)
Simon Kelleybe6cfb42012-10-16 20:38:31 +0100147 {
Simon Kelley89500e32013-09-20 16:29:20 +0100148 int i;
149
Simon Kelleyff7eea22013-09-04 18:01:38 +0100150 for (tmp = daemon->if_except; tmp; tmp = tmp->next)
Simon Kelley49333cb2013-03-15 20:30:51 +0000151 if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
Simon Kelleyff7eea22013-09-04 18:01:38 +0100152 return;
153
154 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
155 if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
156 return;
157
158 parm.current = NULL;
159 parm.relay = NULL;
160 memset(&parm.relay_local, 0, IN6ADDRSZ);
161 parm.ind = if_index;
162 parm.addr_match = 0;
163 memset(&parm.fallback, 0, IN6ADDRSZ);
164
165 for (context = daemon->dhcp6; context; context = context->next)
166 if (IN6_IS_ADDR_UNSPECIFIED(&context->start6) && context->prefix == 0)
167 {
168 /* wildcard context for DHCP-stateless only */
169 parm.current = context;
170 context->current = NULL;
171 }
172 else
173 {
174 /* unlinked contexts are marked by context->current == context */
175 context->current = context;
176 memset(&context->local6, 0, IN6ADDRSZ);
177 }
Simon Kelleybe6cfb42012-10-16 20:38:31 +0100178
Simon Kelleyff7eea22013-09-04 18:01:38 +0100179 for (relay = daemon->relay6; relay; relay = relay->next)
180 relay->current = relay;
181
182 if (!iface_enumerate(AF_INET6, &parm, complete_context6))
Simon Kelleybe6cfb42012-10-16 20:38:31 +0100183 return;
Simon Kelley89500e32013-09-20 16:29:20 +0100184
185 /* Recieving a packet from a host does not populate the neighbour
Simon Kelleyd81b42d2013-09-23 12:26:34 +0100186 cache, so we send a neighbour discovery request if we can't
Simon Kelley89500e32013-09-20 16:29:20 +0100187 find the sender. Repeat a few times in case of packet loss. */
Simon Kelleyff7eea22013-09-04 18:01:38 +0100188
Simon Kelley89500e32013-09-20 16:29:20 +0100189 for (i = 0; i < 5; i++)
190 {
191 struct timespec ts;
Simon Kelleyd81b42d2013-09-23 12:26:34 +0100192 struct neigh_packet *neigh;
Simon Kelley89500e32013-09-20 16:29:20 +0100193 struct sockaddr_in6 addr;
194
195 mac_param.target = &from.sin6_addr;
196 mac_param.maclen = 0;
197
198 iface_enumerate(AF_UNSPEC, &mac_param, find_mac);
199
200 if (mac_param.maclen != 0)
201 break;
202
203 save_counter(0);
Simon Kelleyd81b42d2013-09-23 12:26:34 +0100204 neigh = expand(sizeof(struct neigh_packet));
205 neigh->type = ND_NEIGHBOR_SOLICIT;
206 neigh->code = 0;
207 neigh->reserved = 0;
208 neigh->target = from.sin6_addr;
209
Simon Kelley89500e32013-09-20 16:29:20 +0100210 memset(&addr, 0, sizeof(addr));
211#ifdef HAVE_SOCKADDR_SA_LEN
212 addr.sin6_len = sizeof(struct sockaddr_in6);
213#endif
214 addr.sin6_family = AF_INET6;
215 addr.sin6_port = htons(IPPROTO_ICMPV6);
216 addr.sin6_addr = from.sin6_addr;
Simon Kelleyd81b42d2013-09-23 12:26:34 +0100217 addr.sin6_scope_id = from.sin6_scope_id;
Simon Kelley89500e32013-09-20 16:29:20 +0100218
219 sendto(daemon->icmp6fd, daemon->outpacket.iov_base, save_counter(0), 0,
220 (struct sockaddr *)&addr, sizeof(addr));
221
222 ts.tv_sec = 0;
223 ts.tv_nsec = 100000000; /* 100ms */
224 nanosleep(&ts, NULL);
225 }
226
Simon Kelleyff7eea22013-09-04 18:01:38 +0100227 if (daemon->if_names || daemon->if_addrs)
228 {
229
230 for (tmp = daemon->if_names; tmp; tmp = tmp->next)
231 if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
232 break;
233
234 if (!tmp && !parm.addr_match)
235 return;
236 }
237
238 if (parm.relay)
239 {
Simon Kelley89500e32013-09-20 16:29:20 +0100240 /* Ignore requests sent to the ALL_SERVERS multicast address for relay when
241 we're listening there for DHCPv6 server reasons. */
242 struct in6_addr all_servers;
243
244 inet_pton(AF_INET6, ALL_SERVERS, &all_servers);
245
246 if (!IN6_ARE_ADDR_EQUAL(&dst_addr, &all_servers))
247 relay_upstream6(parm.relay, sz, &from.sin6_addr, from.sin6_scope_id,
248 mac_param.maclen == 0 ? NULL : &mac_param.mac[0], mac_param.maclen, ARPHRD_ETHER);
Simon Kelleyff7eea22013-09-04 18:01:38 +0100249 return;
250 }
251
252 /* May have configured relay, but not DHCP server */
253 if (!daemon->doing_dhcp6)
254 return;
255
256 lease_prune(NULL, now); /* lose any expired leases */
257
258 port = dhcp6_reply(parm.current, if_index, ifr.ifr_name, &parm.fallback,
Simon Kelley89500e32013-09-20 16:29:20 +0100259 sz, IN6_IS_ADDR_MULTICAST(&from.sin6_addr), now,
260 mac_param.maclen == 0 ? NULL : &mac_param.mac[0], mac_param.maclen, ARPHRD_ETHER);
Simon Kelleyff7eea22013-09-04 18:01:38 +0100261
262 lease_update_file(now);
263 lease_update_dns(0);
Simon Kelleybe6cfb42012-10-16 20:38:31 +0100264 }
Simon Kelleyff7eea22013-09-04 18:01:38 +0100265
Simon Kelley1d0f91c2012-03-12 11:56:22 +0000266 /* The port in the source address of the original request should
267 be correct, but at least once client sends from the server port,
268 so we explicitly send to the client port to a client, and the
269 server port to a relay. */
270 if (port != 0)
271 {
272 from.sin6_port = htons(port);
273 while (sendto(daemon->dhcp6fd, daemon->outpacket.iov_base, save_counter(0),
274 0, (struct sockaddr *)&from, sizeof(from)) == -1 &&
Simon Kelley4cb1b322012-02-06 14:30:41 +0000275 retry_send());
Simon Kelley1d0f91c2012-03-12 11:56:22 +0000276 }
Simon Kelleyc72daea2012-01-05 21:33:27 +0000277}
278
Simon Kelley89500e32013-09-20 16:29:20 +0100279static int find_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv)
280{
281 struct mac_param *parm = parmv;
282
283 if (family == AF_INET6 && IN6_ARE_ADDR_EQUAL(parm->target, addrp))
284 {
285 if (maclen <= DHCP_CHADDR_MAX)
286 {
287 parm->maclen = maclen;
288 memcpy(parm->mac, mac, maclen);
289 }
290
291 return 0; /* found, abort */
292 }
293
294 return 1;
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 Kelleyff7eea22013-09-04 18:01:38 +0100310 if (!IN6_IS_ADDR_LOOPBACK(local) &&
311 !IN6_IS_ADDR_LINKLOCAL(local) &&
312 !IN6_IS_ADDR_MULTICAST(local))
Simon Kelleye44ddca2012-02-18 17:08:50 +0000313 {
Simon Kelleyff7eea22013-09-04 18:01:38 +0100314 /* if we have --listen-address config, see if the
315 arrival interface has a matching address. */
316 for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
317 if (tmp->addr.sa.sa_family == AF_INET6 &&
318 IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, local))
319 param->addr_match = 1;
320
321 /* Determine a globally address on the arrival interface, even
322 if we have no matching dhcp-context, because we're only
323 allocating on remote subnets via relays. This
324 is used as a default for the DNS server option. */
325 param->fallback = *local;
326
327 for (context = daemon->dhcp6; context; context = context->next)
Simon Kelleye44ddca2012-02-18 17:08:50 +0000328 {
Simon Kelleyff7eea22013-09-04 18:01:38 +0100329 if ((context->flags & CONTEXT_DHCP) &&
330 !(context->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) &&
331 prefix == context->prefix &&
332 is_same_net6(local, &context->start6, prefix) &&
333 is_same_net6(local, &context->end6, prefix))
Simon Kelleye44ddca2012-02-18 17:08:50 +0000334 {
Simon Kelley3bc0d932012-12-28 11:31:44 +0000335
Simon Kelley3bc0d932012-12-28 11:31:44 +0000336
Simon Kelleyff7eea22013-09-04 18:01:38 +0100337 /* link it onto the current chain if we've not seen it before */
338 if (context->current == context)
339 {
340 struct dhcp_context *tmp, **up;
341
342 /* use interface values only for contructed contexts */
343 if (!(context->flags & CONTEXT_CONSTRUCTED))
344 preferred = valid = 0xffffffff;
345 else if (flags & IFACE_DEPRECATED)
346 preferred = 0;
347
348 if (context->flags & CONTEXT_DEPRECATE)
349 preferred = 0;
350
351 /* order chain, longest preferred time first */
352 for (up = &param->current, tmp = param->current; tmp; tmp = tmp->current)
353 if (tmp->preferred <= preferred)
354 break;
355 else
356 up = &tmp->current;
357
358 context->current = *up;
359 *up = context;
360 context->local6 = *local;
361 context->preferred = preferred;
362 context->valid = valid;
363 }
Simon Kelleye44ddca2012-02-18 17:08:50 +0000364 }
365 }
Simon Kelleyc72daea2012-01-05 21:33:27 +0000366 }
Simon Kelleyff7eea22013-09-04 18:01:38 +0100367
368 for (relay = daemon->relay6; relay; relay = relay->next)
369 if (IN6_ARE_ADDR_EQUAL(local, &relay->local.addr.addr6) && relay->current == relay &&
370 (IN6_IS_ADDR_UNSPECIFIED(&param->relay_local) || IN6_ARE_ADDR_EQUAL(local, &param->relay_local)))
371 {
372 relay->current = param->relay;
373 param->relay = relay;
374 param->relay_local = *local;
375 }
376
Simon Kelleyc72daea2012-01-05 21:33:27 +0000377 }
Simon Kelleyff7eea22013-09-04 18:01:38 +0100378
379 return 1;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000380}
Simon Kelley52b92f42012-01-22 16:05:15 +0000381
382struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct in6_addr *net, int prefix, u64 addr)
383{
384 struct dhcp_config *config;
385
386 for (config = configs; config; config = config->next)
387 if ((config->flags & CONFIG_ADDR6) &&
388 is_same_net6(&config->addr6, net, prefix) &&
389 (prefix == 128 || addr6part(&config->addr6) == addr))
390 return config;
391
392 return NULL;
393}
394
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000395struct dhcp_context *address6_allocate(struct dhcp_context *context, unsigned char *clid, int clid_len,
Simon Kelleyc6309242013-03-07 20:59:28 +0000396 int iaid, int serial, struct dhcp_netid *netids, int plain_range, struct in6_addr *ans)
Simon Kelley52b92f42012-01-22 16:05:15 +0000397{
398 /* Find a free address: exclude anything in use and anything allocated to
399 a particular hwaddr/clientid/hostname in our configuration.
400 Try to return from contexts which match netids first.
401
402 Note that we assume the address prefix lengths are 64 or greater, so we can
403 get by with 64 bit arithmetic.
404*/
405
406 u64 start, addr;
407 struct dhcp_context *c, *d;
408 int i, pass;
409 u64 j;
410
411 /* hash hwaddr: use the SDBM hashing algorithm. This works
412 for MAC addresses, let's see how it manages with client-ids! */
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000413 for (j = iaid, i = 0; i < clid_len; i++)
Simon Kelley52b92f42012-01-22 16:05:15 +0000414 j += clid[i] + (j << 6) + (j << 16) - j;
415
Simon Kelleyc6309242013-03-07 20:59:28 +0000416 for (pass = 0; pass <= plain_range ? 1 : 0; pass++)
Simon Kelley52b92f42012-01-22 16:05:15 +0000417 for (c = context; c; c = c->current)
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000418 if (c->flags & (CONTEXT_DEPRECATE | CONTEXT_STATIC | CONTEXT_RA_STATELESS | CONTEXT_USED))
Simon Kelley52b92f42012-01-22 16:05:15 +0000419 continue;
420 else if (!match_netid(c->filter, netids, pass))
421 continue;
422 else
Simon Kelley07933802012-02-14 20:55:25 +0000423 {
424 if (option_bool(OPT_CONSEC_ADDR))
425 /* seed is largest extant lease addr in this context */
Simon Kelleyc6309242013-03-07 20:59:28 +0000426 start = lease_find_max_addr6(c) + serial;
Simon Kelley07933802012-02-14 20:55:25 +0000427 else
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000428 start = addr6part(&c->start6) + ((j + c->addr_epoch) % (1 + addr6part(&c->end6) - addr6part(&c->start6)));
Simon Kelley52b92f42012-01-22 16:05:15 +0000429
430 /* iterate until we find a free address. */
431 addr = start;
432
433 do {
434 /* eliminate addresses in use by the server. */
435 for (d = context; d; d = d->current)
Simon Kelleye44ddca2012-02-18 17:08:50 +0000436 if (addr == addr6part(&d->local6))
Simon Kelley52b92f42012-01-22 16:05:15 +0000437 break;
438
439 if (!d &&
440 !lease6_find_by_addr(&c->start6, c->prefix, addr) &&
441 !config_find_by_address6(daemon->dhcp_conf, &c->start6, c->prefix, addr))
442 {
443 *ans = c->start6;
444 setaddr6part (ans, addr);
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000445 return c;
Simon Kelley52b92f42012-01-22 16:05:15 +0000446 }
447
448 addr++;
449
450 if (addr == addr6part(&c->end6) + 1)
451 addr = addr6part(&c->start6);
452
453 } while (addr != start);
454 }
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000455
Simon Kelley5b37aa82013-04-02 16:32:25 +0100456 return NULL;
Simon Kelley52b92f42012-01-22 16:05:15 +0000457}
458
Simon Kelley37c9cce2013-01-09 19:51:04 +0000459/* can dynamically allocate addr */
Simon Kelley52b92f42012-01-22 16:05:15 +0000460struct dhcp_context *address6_available(struct dhcp_context *context,
461 struct in6_addr *taddr,
Simon Kelleyc6309242013-03-07 20:59:28 +0000462 struct dhcp_netid *netids,
463 int plain_range)
Simon Kelley52b92f42012-01-22 16:05:15 +0000464{
465 u64 start, end, addr = addr6part(taddr);
466 struct dhcp_context *tmp;
467
468 for (tmp = context; tmp; tmp = tmp->current)
469 {
470 start = addr6part(&tmp->start6);
471 end = addr6part(&tmp->end6);
472
Simon Kelleyc8257542012-03-28 21:15:41 +0100473 if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_RA_STATELESS)) &&
474 is_same_net6(&tmp->start6, taddr, tmp->prefix) &&
475 is_same_net6(&tmp->end6, taddr, tmp->prefix) &&
Simon Kelley52b92f42012-01-22 16:05:15 +0000476 addr >= start &&
477 addr <= end &&
Simon Kelleyc6309242013-03-07 20:59:28 +0000478 match_netid(tmp->filter, netids, plain_range))
Simon Kelley52b92f42012-01-22 16:05:15 +0000479 return tmp;
480 }
481
482 return NULL;
483}
484
Simon Kelley37c9cce2013-01-09 19:51:04 +0000485/* address OK if configured */
486struct dhcp_context *address6_valid(struct dhcp_context *context,
Simon Kelleyc6309242013-03-07 20:59:28 +0000487 struct in6_addr *taddr,
488 struct dhcp_netid *netids,
489 int plain_range)
Simon Kelley37c9cce2013-01-09 19:51:04 +0000490{
491 struct dhcp_context *tmp;
492
493 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelleybaeb3ad2013-01-10 11:47:38 +0000494 if (is_same_net6(&tmp->start6, taddr, tmp->prefix) &&
Simon Kelleyc6309242013-03-07 20:59:28 +0000495 match_netid(tmp->filter, netids, plain_range))
Simon Kelley37c9cce2013-01-09 19:51:04 +0000496 return tmp;
497
498 return NULL;
499}
500
Simon Kelleyde92b472013-03-15 18:25:10 +0000501int config_valid(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr)
Simon Kelley4cb1b322012-02-06 14:30:41 +0000502{
Simon Kelleyde92b472013-03-15 18:25:10 +0000503 if (!config || !(config->flags & CONFIG_ADDR6))
504 return 0;
505
506 if ((config->flags & CONFIG_WILDCARD) && context->prefix == 64)
Simon Kelley30393102013-01-17 16:34:16 +0000507 {
Simon Kelleyde92b472013-03-15 18:25:10 +0000508 *addr = context->start6;
509 setaddr6part(addr, addr6part(&config->addr6));
Simon Kelley30393102013-01-17 16:34:16 +0000510 return 1;
511 }
512
Simon Kelleyde92b472013-03-15 18:25:10 +0000513 if (is_same_net6(&context->start6, &config->addr6, context->prefix))
514 {
515 *addr = config->addr6;
516 return 1;
517 }
518
Simon Kelleyb2692212012-09-16 22:15:56 +0100519 return 0;
520}
521
Simon Kelley4cb1b322012-02-06 14:30:41 +0000522void make_duid(time_t now)
523{
Simon Kelley8b372702012-03-09 17:45:10 +0000524 if (daemon->duid_config)
525 {
526 unsigned char *p;
527
528 daemon->duid = p = safe_malloc(daemon->duid_config_len + 6);
529 daemon->duid_len = daemon->duid_config_len + 6;
530 PUTSHORT(2, p); /* DUID_EN */
531 PUTLONG(daemon->duid_enterprise, p);
532 memcpy(p, daemon->duid_config, daemon->duid_config_len);
533 }
534 else
535 {
536 /* rebase epoch to 1/1/2000 */
537 time_t newnow = now - 946684800;
538
539 iface_enumerate(AF_LOCAL, &newnow, make_duid1);
540
541 if(!daemon->duid)
542 die("Cannot create DHCPv6 server DUID: %s", NULL, EC_MISC);
543 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000544}
545
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000546static int make_duid1(int index, unsigned int type, char *mac, size_t maclen, void *parm)
Simon Kelley4cb1b322012-02-06 14:30:41 +0000547{
548 /* create DUID as specified in RFC3315. We use the MAC of the
Simon Kelley0f089832012-03-01 13:43:39 +0000549 first interface we find that isn't loopback or P-to-P and
550 has address-type < 256. Address types above 256 are things like
551 tunnels which don't have usable MAC addresses. */
Simon Kelley4cb1b322012-02-06 14:30:41 +0000552
553 unsigned char *p;
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000554 (void)index;
555
Simon Kelley0f089832012-03-01 13:43:39 +0000556 if (type >= 256)
557 return 1;
558
Simon Kelleye3e86342012-03-01 10:35:34 +0000559#ifdef HAVE_BROKEN_RTC
560 daemon->duid = p = safe_malloc(maclen + 4);
561 daemon->duid_len = maclen + 4;
562 PUTSHORT(3, p); /* DUID_LL */
563 PUTSHORT(type, p); /* address type */
564#else
Simon Kelley4cb1b322012-02-06 14:30:41 +0000565 daemon->duid = p = safe_malloc(maclen + 8);
566 daemon->duid_len = maclen + 8;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000567 PUTSHORT(1, p); /* DUID_LLT */
Simon Kelley4cb1b322012-02-06 14:30:41 +0000568 PUTSHORT(type, p); /* address type */
Simon Kelley4cb1b322012-02-06 14:30:41 +0000569 PUTLONG(*((time_t *)parm), p); /* time */
570#endif
571
572 memcpy(p, mac, maclen);
573
574 return 0;
575}
Simon Kelley1f776932012-12-16 19:46:08 +0000576
577struct cparam {
578 time_t now;
Simon Kelley0c050242012-12-22 22:13:19 +0000579 int newone, newname;
Simon Kelley1f776932012-12-16 19:46:08 +0000580};
581
582static int construct_worker(struct in6_addr *local, int prefix,
Simon Kelleybad7b872012-12-20 22:00:39 +0000583 int scope, int if_index, int flags,
Simon Kelley1f776932012-12-16 19:46:08 +0000584 int preferred, int valid, void *vparam)
585{
586 char ifrn_name[IFNAMSIZ];
587 struct in6_addr start6, end6;
588 struct dhcp_context *template, *context;
589
590 (void)scope;
Simon Kelleybad7b872012-12-20 22:00:39 +0000591 (void)flags;
Simon Kelleyed8b68a2012-12-21 16:23:26 +0000592 (void)valid;
593 (void)preferred;
Simon Kelley1f776932012-12-16 19:46:08 +0000594
595 struct cparam *param = vparam;
596
597 if (IN6_IS_ADDR_LOOPBACK(local) ||
598 IN6_IS_ADDR_LINKLOCAL(local) ||
599 IN6_IS_ADDR_MULTICAST(local))
600 return 1;
601
602 if (!indextoname(daemon->doing_dhcp6 ? daemon->dhcp6fd : daemon->icmp6fd, if_index, ifrn_name))
603 return 0;
604
605 for (template = daemon->dhcp6; template; template = template->next)
606 if (!(template->flags & CONTEXT_TEMPLATE))
607 {
608 /* non-template entries, just fill in interface and local addresses */
609 if (prefix == template->prefix &&
610 is_same_net6(local, &template->start6, prefix) &&
611 is_same_net6(local, &template->end6, prefix))
612 {
613 template->if_index = if_index;
614 template->local6 = *local;
615 }
616
617 }
Simon Kelley429805d2013-05-31 13:47:26 +0100618 else if ((addr6part(local) == addr6part(&template->start6) ||
Vladislav Grishenkoe4cdbbf2013-08-19 16:20:31 +0100619 addr6part(local) == addr6part(&template->end6) ||
620 (IN6_IS_ADDR_UNSPECIFIED(&template->start6) &&
621 IFACE_PERMANENT == (flags & (IFACE_PERMANENT | IFACE_DEPRECATED)))) &&
Simon Kelley429805d2013-05-31 13:47:26 +0100622 wildcard_match(template->template_interface, ifrn_name))
Simon Kelley1f776932012-12-16 19:46:08 +0000623 {
624 start6 = *local;
625 setaddr6part(&start6, addr6part(&template->start6));
626 end6 = *local;
627 setaddr6part(&end6, addr6part(&template->end6));
628
629 for (context = daemon->dhcp6; context; context = context->next)
630 if ((context->flags & CONTEXT_CONSTRUCTED) &&
631 IN6_ARE_ADDR_EQUAL(&start6, &context->start6) &&
632 IN6_ARE_ADDR_EQUAL(&end6, &context->end6))
633 {
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100634 int flags = context->flags;
635 context->flags &= ~(CONTEXT_GC | CONTEXT_OLD);
636 if (flags & CONTEXT_OLD)
Simon Kelleyf7a40ec2013-07-27 13:36:08 +0100637 {
638 /* address went, now it's back */
639 log_context(AF_INET6, context);
640 /* fast RAs for a while */
641 ra_start_unsolicted(param->now, context);
642 /* Add address to name again */
643 if (context->flags & CONTEXT_RA_NAME)
644 param->newname = 1;
645 }
Simon Kelley1f776932012-12-16 19:46:08 +0000646 break;
647 }
648
649 if (!context && (context = whine_malloc(sizeof (struct dhcp_context))))
650 {
651 *context = *template;
652 context->start6 = start6;
653 context->end6 = end6;
654 context->flags &= ~CONTEXT_TEMPLATE;
655 context->flags |= CONTEXT_CONSTRUCTED;
656 context->if_index = if_index;
657 context->local6 = *local;
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100658 context->saved_valid = 0;
Simon Kelley1f776932012-12-16 19:46:08 +0000659
660 context->next = daemon->dhcp6;
661 daemon->dhcp6 = context;
662
Simon Kelley1b75c1e2012-12-18 19:55:25 +0000663 ra_start_unsolicted(param->now, context);
Simon Kelley1f776932012-12-16 19:46:08 +0000664 /* we created a new one, need to call
665 lease_update_file to get periodic functions called */
666 param->newone = 1;
Simon Kelley0c050242012-12-22 22:13:19 +0000667
668 /* Will need to add new putative SLAAC addresses to existing leases */
669 if (context->flags & CONTEXT_RA_NAME)
670 param->newname = 1;
Simon Kelley1f776932012-12-16 19:46:08 +0000671
672 log_context(AF_INET6, context);
673 }
Simon Kelley1f776932012-12-16 19:46:08 +0000674 }
675
676 return 1;
677}
678
679void dhcp_construct_contexts(time_t now)
680{
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100681 struct dhcp_context *context, *tmp, **up;
Simon Kelley1f776932012-12-16 19:46:08 +0000682 struct cparam param;
683 param.newone = 0;
Simon Kelley0c050242012-12-22 22:13:19 +0000684 param.newname = 0;
Simon Kelley1f776932012-12-16 19:46:08 +0000685 param.now = now;
686
687 for (context = daemon->dhcp6; context; context = context->next)
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100688 if (context->flags & CONTEXT_CONSTRUCTED)
689 context->flags |= CONTEXT_GC;
690
Simon Kelley1f776932012-12-16 19:46:08 +0000691 iface_enumerate(AF_INET6, &param, construct_worker);
692
693 for (up = &daemon->dhcp6, context = daemon->dhcp6; context; context = tmp)
694 {
Simon Kelley1f776932012-12-16 19:46:08 +0000695
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100696 tmp = context->next;
697
698 if (context->flags & CONTEXT_GC && !(context->flags & CONTEXT_OLD))
Simon Kelley1f776932012-12-16 19:46:08 +0000699 {
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100700
701 if ((context->flags & (CONTEXT_RA_ONLY | CONTEXT_RA_NAME | CONTEXT_RA_STATELESS)) ||
702 option_bool(OPT_RA))
703 {
704 /* previously constructed context has gone. advertise it's demise */
705 context->flags |= CONTEXT_OLD;
706 context->address_lost_time = now;
Simon Kelley9f48ffa2013-07-28 15:47:04 +0100707 /* Apply same ceiling of configured lease time as in radv.c */
708 if (context->saved_valid > context->lease_time)
709 context->saved_valid = context->lease_time;
710 /* maximum time is 2 hours, from RFC */
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100711 if (context->saved_valid > 7200) /* 2 hours */
712 context->saved_valid = 7200;
713 ra_start_unsolicted(now, context);
714 param.newone = 1; /* include deletion */
715
716 if (context->flags & CONTEXT_RA_NAME)
717 param.newname = 1;
718
719 log_context(AF_INET6, context);
720
721 up = &context->next;
722 }
723 else
724 {
725 /* we were never doing RA for this, so free now */
726 *up = context->next;
727 free(context);
728 }
Simon Kelley1f776932012-12-16 19:46:08 +0000729 }
730 else
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100731 up = &context->next;
Simon Kelley1f776932012-12-16 19:46:08 +0000732 }
733
734 if (param.newone)
Simon Kelley7558ecd2012-12-16 21:45:16 +0000735 {
736 if (daemon->dhcp || daemon->doing_dhcp6)
Simon Kelley0c050242012-12-22 22:13:19 +0000737 {
738 if (param.newname)
739 lease_update_slaac(now);
740 lease_update_file(now);
741 }
Simon Kelley7558ecd2012-12-16 21:45:16 +0000742 else
743 /* Not doing DHCP, so no lease system, manage alarms for ra only */
744 send_alarm(periodic_ra(now), now);
745 }
Simon Kelley1f776932012-12-16 19:46:08 +0000746}
Simon Kelley1f776932012-12-16 19:46:08 +0000747
Simon Kelleyc72daea2012-01-05 21:33:27 +0000748#endif
749
750