blob: c2909938c3a36948737761d8eec5da3317b23d70 [file] [log] [blame]
Simon Kelley59546082012-01-06 20:02:04 +00001/* dnsmasq is Copyright (c) 2000-2012 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
21struct iface_param {
22 struct dhcp_context *current;
Simon Kelleye44ddca2012-02-18 17:08:50 +000023 struct in6_addr fallback;
Simon Kelleybe6cfb42012-10-16 20:38:31 +010024 int ind, addr_match;
Simon Kelleyc72daea2012-01-05 21:33:27 +000025};
26
Simon Kelleyc72daea2012-01-05 21:33:27 +000027static int complete_context6(struct in6_addr *local, int prefix,
Simon Kelleybad7b872012-12-20 22:00:39 +000028 int scope, int if_index, int flags,
Simon Kelley3bc0d932012-12-28 11:31:44 +000029 unsigned int preferred, unsigned int valid, void *vparam);
Simon Kelleyc72daea2012-01-05 21:33:27 +000030
Simon Kelleyc5ad4e72012-02-24 16:06:20 +000031static int make_duid1(int index, unsigned int type, char *mac, size_t maclen, void *parm);
Simon Kelley4cb1b322012-02-06 14:30:41 +000032
Simon Kelleyc72daea2012-01-05 21:33:27 +000033void dhcp6_init(void)
34{
35 int fd;
36 struct sockaddr_in6 saddr;
Simon Kelley0e88d532012-03-28 22:22:05 +010037#if defined(IPV6_TCLASS) && defined(IPTOS_CLASS_CS6)
Simon Kelleyc72daea2012-01-05 21:33:27 +000038 int class = IPTOS_CLASS_CS6;
Simon Kelley4cb1b322012-02-06 14:30:41 +000039#endif
Simon Kelley20223102012-10-15 10:41:17 +010040 int oneopt = 1;
41
Simon Kelleyc72daea2012-01-05 21:33:27 +000042 if ((fd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP)) == -1 ||
Simon Kelley0e88d532012-03-28 22:22:05 +010043#if defined(IPV6_TCLASS) && defined(IPTOS_CLASS_CS6)
Simon Kelleyc72daea2012-01-05 21:33:27 +000044 setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &class, sizeof(class)) == -1 ||
Simon Kelley4cb1b322012-02-06 14:30:41 +000045#endif
Simon Kelley20223102012-10-15 10:41:17 +010046 setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &oneopt, sizeof(oneopt)) == -1 ||
Simon Kelleyc72daea2012-01-05 21:33:27 +000047 !fix_fd(fd) ||
48 !set_ipv6pktinfo(fd))
49 die (_("cannot create DHCPv6 socket: %s"), NULL, EC_BADNET);
50
Simon Kelley20223102012-10-15 10:41:17 +010051 /* When bind-interfaces is set, there might be more than one dnmsasq
52 instance binding port 547. That's OK if they serve different networks.
53 Need to set REUSEADDR to make this posible, or REUSEPORT on *BSD. */
54 if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND))
55 {
56#ifdef SO_REUSEPORT
57 int rc = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &oneopt, sizeof(oneopt));
58#else
59 int rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt));
60#endif
61 if (rc == -1)
62 die(_("failed to set SO_REUSE{ADDR|PORT} on DHCPv6 socket: %s"), NULL, EC_BADNET);
63 }
64
Simon Kelleyc72daea2012-01-05 21:33:27 +000065 memset(&saddr, 0, sizeof(saddr));
66#ifdef HAVE_SOCKADDR_SA_LEN
Simon Kelley4cb1b322012-02-06 14:30:41 +000067 saddr.sin6_len = sizeof(struct sockaddr_in6);
Simon Kelleyc72daea2012-01-05 21:33:27 +000068#endif
69 saddr.sin6_family = AF_INET6;
70 saddr.sin6_addr = in6addr_any;
71 saddr.sin6_port = htons(DHCPV6_SERVER_PORT);
72
73 if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in6)))
74 die(_("failed to bind DHCPv6 server socket: %s"), NULL, EC_BADNET);
75
Simon Kelleyc72daea2012-01-05 21:33:27 +000076 daemon->dhcp6fd = fd;
Simon Kelleyc72daea2012-01-05 21:33:27 +000077}
78
Simon Kelleyc72daea2012-01-05 21:33:27 +000079void dhcp6_packet(time_t now)
80{
81 struct dhcp_context *context;
82 struct iface_param parm;
83 struct cmsghdr *cmptr;
84 struct msghdr msg;
85 int if_index = 0;
86 union {
87 struct cmsghdr align; /* this ensures alignment */
88 char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
89 } control_u;
Simon Kelleyc5ad4e72012-02-24 16:06:20 +000090 struct sockaddr_in6 from;
Simon Kelleyc72daea2012-01-05 21:33:27 +000091 ssize_t sz;
92 struct ifreq ifr;
93 struct iname *tmp;
Simon Kelley1d0f91c2012-03-12 11:56:22 +000094 unsigned short port;
Simon Kelleyc72daea2012-01-05 21:33:27 +000095
96 msg.msg_control = control_u.control6;
97 msg.msg_controllen = sizeof(control_u);
98 msg.msg_flags = 0;
99 msg.msg_name = &from;
100 msg.msg_namelen = sizeof(from);
101 msg.msg_iov = &daemon->dhcp_packet;
102 msg.msg_iovlen = 1;
103
Simon Kelley1d0f91c2012-03-12 11:56:22 +0000104 if ((sz = recv_dhcp_packet(daemon->dhcp6fd, &msg)) == -1)
Simon Kelleyc72daea2012-01-05 21:33:27 +0000105 return;
106
107 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
108 if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
109 {
110 union {
111 unsigned char *c;
112 struct in6_pktinfo *p;
113 } p;
114 p.c = CMSG_DATA(cmptr);
115
116 if_index = p.p->ipi6_ifindex;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000117 }
118
119 if (!indextoname(daemon->dhcp6fd, if_index, ifr.ifr_name))
120 return;
Simon Kelley52b92f42012-01-22 16:05:15 +0000121
Simon Kelleybe6cfb42012-10-16 20:38:31 +0100122 for (tmp = daemon->if_except; tmp; tmp = tmp->next)
123 if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
124 return;
125
Simon Kelleyc72daea2012-01-05 21:33:27 +0000126 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
127 if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
128 return;
129
Simon Kelleyc72daea2012-01-05 21:33:27 +0000130 parm.current = NULL;
131 parm.ind = if_index;
Simon Kelleybe6cfb42012-10-16 20:38:31 +0100132 parm.addr_match = 0;
Simon Kelleye44ddca2012-02-18 17:08:50 +0000133 memset(&parm.fallback, 0, IN6ADDRSZ);
Simon Kelley819ff4d2012-10-21 18:25:12 +0100134
135 for (context = daemon->dhcp6; context; context = context->next)
136 if (IN6_IS_ADDR_UNSPECIFIED(&context->start6) && context->prefix == 0)
137 {
138 /* wildcard context for DHCP-stateless only */
139 parm.current = context;
140 context->current = NULL;
141 }
142 else
143 {
144 /* unlinked contexts are marked by context->current == context */
145 context->current = context;
146 memset(&context->local6, 0, IN6ADDRSZ);
147 }
Simon Kelleyc72daea2012-01-05 21:33:27 +0000148
149 if (!iface_enumerate(AF_INET6, &parm, complete_context6))
150 return;
151
Simon Kelleybe6cfb42012-10-16 20:38:31 +0100152 if (daemon->if_names || daemon->if_addrs)
153 {
154
155 for (tmp = daemon->if_names; tmp; tmp = tmp->next)
156 if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
157 break;
158
159 if (!tmp && !parm.addr_match)
160 return;
161 }
162
Simon Kelleyc72daea2012-01-05 21:33:27 +0000163 lease_prune(NULL, now); /* lose any expired leases */
164
Simon Kelley1d0f91c2012-03-12 11:56:22 +0000165 port = dhcp6_reply(parm.current, if_index, ifr.ifr_name, &parm.fallback,
166 sz, IN6_IS_ADDR_MULTICAST(&from.sin6_addr), now);
Simon Kelley62779782012-02-10 21:19:25 +0000167
Simon Kelleyc72daea2012-01-05 21:33:27 +0000168 lease_update_file(now);
Simon Kelley353ae4d2012-03-19 20:07:51 +0000169 lease_update_dns(0);
Simon Kelleyc72daea2012-01-05 21:33:27 +0000170
Simon Kelley1d0f91c2012-03-12 11:56:22 +0000171 /* The port in the source address of the original request should
172 be correct, but at least once client sends from the server port,
173 so we explicitly send to the client port to a client, and the
174 server port to a relay. */
175 if (port != 0)
176 {
177 from.sin6_port = htons(port);
178 while (sendto(daemon->dhcp6fd, daemon->outpacket.iov_base, save_counter(0),
179 0, (struct sockaddr *)&from, sizeof(from)) == -1 &&
Simon Kelley4cb1b322012-02-06 14:30:41 +0000180 retry_send());
Simon Kelley1d0f91c2012-03-12 11:56:22 +0000181 }
Simon Kelleyc72daea2012-01-05 21:33:27 +0000182}
183
184static int complete_context6(struct in6_addr *local, int prefix,
Simon Kelley3bc0d932012-12-28 11:31:44 +0000185 int scope, int if_index, int flags, unsigned int preferred,
186 unsigned int valid, void *vparam)
Simon Kelleyc72daea2012-01-05 21:33:27 +0000187{
188 struct dhcp_context *context;
189 struct iface_param *param = vparam;
Simon Kelleybe6cfb42012-10-16 20:38:31 +0100190 struct iname *tmp;
191
Simon Kelley52b92f42012-01-22 16:05:15 +0000192 (void)scope; /* warning */
Simon Kelley3bc0d932012-12-28 11:31:44 +0000193
Simon Kelleye44ddca2012-02-18 17:08:50 +0000194 if (if_index == param->ind &&
195 !IN6_IS_ADDR_LOOPBACK(local) &&
196 !IN6_IS_ADDR_LINKLOCAL(local) &&
197 !IN6_IS_ADDR_MULTICAST(local))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000198 {
Simon Kelleybe6cfb42012-10-16 20:38:31 +0100199 /* if we have --listen-address config, see if the
200 arrival interface has a matching address. */
201 for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
202 if (tmp->addr.sa.sa_family == AF_INET6 &&
203 IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, local))
204 param->addr_match = 1;
205
Simon Kelleye44ddca2012-02-18 17:08:50 +0000206 /* Determine a globally address on the arrival interface, even
207 if we have no matching dhcp-context, because we're only
208 allocating on remote subnets via relays. This
209 is used as a default for the DNS server option. */
Simon Kelley270dc2e2012-02-19 20:53:20 +0000210 param->fallback = *local;
Simon Kelleye44ddca2012-02-18 17:08:50 +0000211
212 for (context = daemon->dhcp6; context; context = context->next)
213 {
Simon Kelleybaeb3ad2013-01-10 11:47:38 +0000214 if ((context->flags & CONTEXT_DHCP) &&
215 !(context->flags & CONTEXT_TEMPLATE) &&
Simon Kelley6e3dba32012-12-16 21:52:45 +0000216 prefix == context->prefix &&
Simon Kelleye44ddca2012-02-18 17:08:50 +0000217 is_same_net6(local, &context->start6, prefix) &&
218 is_same_net6(local, &context->end6, prefix))
219 {
Simon Kelley3bc0d932012-12-28 11:31:44 +0000220
221
Simon Kelleye44ddca2012-02-18 17:08:50 +0000222 /* link it onto the current chain if we've not seen it before */
223 if (context->current == context)
224 {
Simon Kelley3bc0d932012-12-28 11:31:44 +0000225 struct dhcp_context *tmp, **up;
226
227 /* use interface values only for contructed contexts */
228 if (!(context->flags & CONTEXT_CONSTRUCTED))
229 preferred = valid = 0xffffffff;
230 else if (flags & IFACE_DEPRECATED)
231 preferred = 0;
232
233 if (context->flags & CONTEXT_DEPRECATE)
234 preferred = 0;
235
236 /* order chain, longest preferred time first */
237 for (up = &param->current, tmp = param->current; tmp; tmp = tmp->current)
238 if (tmp->preferred <= preferred)
239 break;
Simon Kelleyb456b9f2013-01-02 17:59:28 +0000240 else
241 up = &tmp->current;
Simon Kelley3bc0d932012-12-28 11:31:44 +0000242
243 context->current = *up;
244 *up = context;
Simon Kelleye44ddca2012-02-18 17:08:50 +0000245 context->local6 = *local;
Simon Kelley3bc0d932012-12-28 11:31:44 +0000246 context->preferred = preferred;
247 context->valid = valid;
Simon Kelleye44ddca2012-02-18 17:08:50 +0000248 }
249 }
Simon Kelleyc72daea2012-01-05 21:33:27 +0000250 }
251 }
252 return 1;
253}
Simon Kelley52b92f42012-01-22 16:05:15 +0000254
255struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct in6_addr *net, int prefix, u64 addr)
256{
257 struct dhcp_config *config;
258
259 for (config = configs; config; config = config->next)
260 if ((config->flags & CONFIG_ADDR6) &&
261 is_same_net6(&config->addr6, net, prefix) &&
262 (prefix == 128 || addr6part(&config->addr6) == addr))
263 return config;
264
265 return NULL;
266}
267
268int address6_allocate(struct dhcp_context *context, unsigned char *clid, int clid_len,
Simon Kelley4cb1b322012-02-06 14:30:41 +0000269 int serial, struct dhcp_netid *netids, struct in6_addr *ans)
Simon Kelley52b92f42012-01-22 16:05:15 +0000270{
271 /* Find a free address: exclude anything in use and anything allocated to
272 a particular hwaddr/clientid/hostname in our configuration.
273 Try to return from contexts which match netids first.
274
275 Note that we assume the address prefix lengths are 64 or greater, so we can
276 get by with 64 bit arithmetic.
277*/
278
279 u64 start, addr;
280 struct dhcp_context *c, *d;
281 int i, pass;
282 u64 j;
283
284 /* hash hwaddr: use the SDBM hashing algorithm. This works
285 for MAC addresses, let's see how it manages with client-ids! */
286 for (j = 0, i = 0; i < clid_len; i++)
287 j += clid[i] + (j << 6) + (j << 16) - j;
288
289 for (pass = 0; pass <= 1; pass++)
290 for (c = context; c; c = c->current)
Simon Kelleyc8257542012-03-28 21:15:41 +0100291 if (c->flags & (CONTEXT_DEPRECATE | CONTEXT_STATIC | CONTEXT_RA_STATELESS))
Simon Kelley52b92f42012-01-22 16:05:15 +0000292 continue;
293 else if (!match_netid(c->filter, netids, pass))
294 continue;
295 else
Simon Kelley07933802012-02-14 20:55:25 +0000296 {
297 if (option_bool(OPT_CONSEC_ADDR))
298 /* seed is largest extant lease addr in this context */
299 start = lease_find_max_addr6(c) + serial;
300 else
301 start = addr6part(&c->start6) + ((j + c->addr_epoch + serial) % (1 + addr6part(&c->end6) - addr6part(&c->start6)));
Simon Kelley52b92f42012-01-22 16:05:15 +0000302
303 /* iterate until we find a free address. */
304 addr = start;
305
306 do {
307 /* eliminate addresses in use by the server. */
308 for (d = context; d; d = d->current)
Simon Kelleye44ddca2012-02-18 17:08:50 +0000309 if (addr == addr6part(&d->local6))
Simon Kelley52b92f42012-01-22 16:05:15 +0000310 break;
311
312 if (!d &&
313 !lease6_find_by_addr(&c->start6, c->prefix, addr) &&
314 !config_find_by_address6(daemon->dhcp_conf, &c->start6, c->prefix, addr))
315 {
316 *ans = c->start6;
317 setaddr6part (ans, addr);
318 return 1;
319 }
320
321 addr++;
322
323 if (addr == addr6part(&c->end6) + 1)
324 addr = addr6part(&c->start6);
325
326 } while (addr != start);
327 }
328
329 return 0;
330}
331
Simon Kelley37c9cce2013-01-09 19:51:04 +0000332/* can dynamically allocate addr */
Simon Kelley52b92f42012-01-22 16:05:15 +0000333struct dhcp_context *address6_available(struct dhcp_context *context,
334 struct in6_addr *taddr,
335 struct dhcp_netid *netids)
336{
337 u64 start, end, addr = addr6part(taddr);
338 struct dhcp_context *tmp;
339
340 for (tmp = context; tmp; tmp = tmp->current)
341 {
342 start = addr6part(&tmp->start6);
343 end = addr6part(&tmp->end6);
344
Simon Kelleyc8257542012-03-28 21:15:41 +0100345 if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_RA_STATELESS)) &&
346 is_same_net6(&tmp->start6, taddr, tmp->prefix) &&
347 is_same_net6(&tmp->end6, taddr, tmp->prefix) &&
Simon Kelley52b92f42012-01-22 16:05:15 +0000348 addr >= start &&
349 addr <= end &&
350 match_netid(tmp->filter, netids, 1))
351 return tmp;
352 }
353
354 return NULL;
355}
356
Simon Kelley37c9cce2013-01-09 19:51:04 +0000357/* address OK if configured */
358struct dhcp_context *address6_valid(struct dhcp_context *context,
359 struct in6_addr *taddr,
360 struct dhcp_netid *netids)
361{
362 struct dhcp_context *tmp;
363
364 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelleybaeb3ad2013-01-10 11:47:38 +0000365 if (is_same_net6(&tmp->start6, taddr, tmp->prefix) &&
Simon Kelley37c9cce2013-01-09 19:51:04 +0000366 match_netid(tmp->filter, netids, 1))
367 return tmp;
368
369 return NULL;
370}
371
Simon Kelley52b92f42012-01-22 16:05:15 +0000372struct dhcp_context *narrow_context6(struct dhcp_context *context,
373 struct in6_addr *taddr,
374 struct dhcp_netid *netids)
375{
376 /* We start of with a set of possible contexts, all on the current physical interface.
377 These are chained on ->current.
378 Here we have an address, and return the actual context correponding to that
379 address. Note that none may fit, if the address came a dhcp-host and is outside
380 any dhcp-range. In that case we return a static range if possible, or failing that,
381 any context on the correct subnet. (If there's more than one, this is a dodgy
382 configuration: maybe there should be a warning.) */
383
384 struct dhcp_context *tmp;
385
Simon Kelley37c9cce2013-01-09 19:51:04 +0000386 if (!(tmp = address6_available(context, taddr, netids)) &&
387 !(tmp = address6_valid(context, taddr, netids)))
388 for (tmp = context; tmp; tmp = tmp->current)
389 if (match_netid(tmp->filter, netids, 1) &&
390 is_same_net6(taddr, &tmp->start6, tmp->prefix))
391 break;
Simon Kelley52b92f42012-01-22 16:05:15 +0000392
393 /* Only one context allowed now */
394 if (tmp)
395 tmp->current = NULL;
396
397 return tmp;
398}
399
Simon Kelleyb2692212012-09-16 22:15:56 +0100400static int is_config_in_context6(struct dhcp_context *context, struct dhcp_config *config)
Simon Kelley4cb1b322012-02-06 14:30:41 +0000401{
402 if (!context) /* called via find_config() from lease_update_from_configs() */
403 return 1;
Simon Kelleyb2692212012-09-16 22:15:56 +0100404 if (!(config->flags & CONFIG_ADDR6) || is_addr_in_context6(context, &config->addr6))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000405 return 1;
Simon Kelleyb2692212012-09-16 22:15:56 +0100406
407 return 0;
408}
409
410
411int is_addr_in_context6(struct dhcp_context *context, struct in6_addr *addr)
412{
Simon Kelley4cb1b322012-02-06 14:30:41 +0000413 for (; context; context = context->current)
Simon Kelleyb2692212012-09-16 22:15:56 +0100414 if (is_same_net6(addr, &context->start6, context->prefix))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000415 return 1;
416
417 return 0;
418}
419
420
421struct dhcp_config *find_config6(struct dhcp_config *configs,
422 struct dhcp_context *context,
423 unsigned char *duid, int duid_len,
424 char *hostname)
425{
Simon Kelley4cb1b322012-02-06 14:30:41 +0000426 struct dhcp_config *config;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000427
Simon Kelley60ac5af2012-02-15 10:41:03 +0000428 if (duid)
429 for (config = configs; config; config = config->next)
430 if (config->flags & CONFIG_CLID)
Simon Kelley4cb1b322012-02-06 14:30:41 +0000431 {
Simon Kelley60ac5af2012-02-15 10:41:03 +0000432 if (config->clid_len == duid_len &&
433 memcmp(config->clid, duid, duid_len) == 0 &&
Simon Kelleyb2692212012-09-16 22:15:56 +0100434 is_config_in_context6(context, config))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000435 return config;
Simon Kelley60ac5af2012-02-15 10:41:03 +0000436 }
437
Simon Kelley4cb1b322012-02-06 14:30:41 +0000438 if (hostname && context)
439 for (config = configs; config; config = config->next)
440 if ((config->flags & CONFIG_NAME) &&
441 hostname_isequal(config->hostname, hostname) &&
Simon Kelleyb2692212012-09-16 22:15:56 +0100442 is_config_in_context6(context, config))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000443 return config;
444
Simon Kelley4cb1b322012-02-06 14:30:41 +0000445 return NULL;
446}
447
448void make_duid(time_t now)
449{
Simon Kelley8b372702012-03-09 17:45:10 +0000450 if (daemon->duid_config)
451 {
452 unsigned char *p;
453
454 daemon->duid = p = safe_malloc(daemon->duid_config_len + 6);
455 daemon->duid_len = daemon->duid_config_len + 6;
456 PUTSHORT(2, p); /* DUID_EN */
457 PUTLONG(daemon->duid_enterprise, p);
458 memcpy(p, daemon->duid_config, daemon->duid_config_len);
459 }
460 else
461 {
462 /* rebase epoch to 1/1/2000 */
463 time_t newnow = now - 946684800;
464
465 iface_enumerate(AF_LOCAL, &newnow, make_duid1);
466
467 if(!daemon->duid)
468 die("Cannot create DHCPv6 server DUID: %s", NULL, EC_MISC);
469 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000470}
471
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000472static int make_duid1(int index, unsigned int type, char *mac, size_t maclen, void *parm)
Simon Kelley4cb1b322012-02-06 14:30:41 +0000473{
474 /* create DUID as specified in RFC3315. We use the MAC of the
Simon Kelley0f089832012-03-01 13:43:39 +0000475 first interface we find that isn't loopback or P-to-P and
476 has address-type < 256. Address types above 256 are things like
477 tunnels which don't have usable MAC addresses. */
Simon Kelley4cb1b322012-02-06 14:30:41 +0000478
479 unsigned char *p;
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000480 (void)index;
481
Simon Kelley0f089832012-03-01 13:43:39 +0000482 if (type >= 256)
483 return 1;
484
Simon Kelleye3e86342012-03-01 10:35:34 +0000485#ifdef HAVE_BROKEN_RTC
486 daemon->duid = p = safe_malloc(maclen + 4);
487 daemon->duid_len = maclen + 4;
488 PUTSHORT(3, p); /* DUID_LL */
489 PUTSHORT(type, p); /* address type */
490#else
Simon Kelley4cb1b322012-02-06 14:30:41 +0000491 daemon->duid = p = safe_malloc(maclen + 8);
492 daemon->duid_len = maclen + 8;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000493 PUTSHORT(1, p); /* DUID_LLT */
Simon Kelley4cb1b322012-02-06 14:30:41 +0000494 PUTSHORT(type, p); /* address type */
Simon Kelley4cb1b322012-02-06 14:30:41 +0000495 PUTLONG(*((time_t *)parm), p); /* time */
496#endif
497
498 memcpy(p, mac, maclen);
499
500 return 0;
501}
Simon Kelley1f776932012-12-16 19:46:08 +0000502
503struct cparam {
504 time_t now;
Simon Kelley0c050242012-12-22 22:13:19 +0000505 int newone, newname;
Simon Kelley1f776932012-12-16 19:46:08 +0000506};
507
508static int construct_worker(struct in6_addr *local, int prefix,
Simon Kelleybad7b872012-12-20 22:00:39 +0000509 int scope, int if_index, int flags,
Simon Kelley1f776932012-12-16 19:46:08 +0000510 int preferred, int valid, void *vparam)
511{
512 char ifrn_name[IFNAMSIZ];
513 struct in6_addr start6, end6;
514 struct dhcp_context *template, *context;
515
516 (void)scope;
Simon Kelleybad7b872012-12-20 22:00:39 +0000517 (void)flags;
Simon Kelleyed8b68a2012-12-21 16:23:26 +0000518 (void)valid;
519 (void)preferred;
Simon Kelley1f776932012-12-16 19:46:08 +0000520
521 struct cparam *param = vparam;
522
523 if (IN6_IS_ADDR_LOOPBACK(local) ||
524 IN6_IS_ADDR_LINKLOCAL(local) ||
525 IN6_IS_ADDR_MULTICAST(local))
526 return 1;
527
528 if (!indextoname(daemon->doing_dhcp6 ? daemon->dhcp6fd : daemon->icmp6fd, if_index, ifrn_name))
529 return 0;
530
531 for (template = daemon->dhcp6; template; template = template->next)
532 if (!(template->flags & CONTEXT_TEMPLATE))
533 {
534 /* non-template entries, just fill in interface and local addresses */
535 if (prefix == template->prefix &&
536 is_same_net6(local, &template->start6, prefix) &&
537 is_same_net6(local, &template->end6, prefix))
538 {
539 template->if_index = if_index;
540 template->local6 = *local;
541 }
542
543 }
Simon Kelley771287b2012-12-30 17:38:09 +0000544 else if (addr6part(local) == addr6part(&template->start6) &&
545 strncmp(template->template_interface, ifrn_name, strlen(template->template_interface)) == 0 &&
546 (strlen(template->template_interface) == strlen(ifrn_name) || (template->flags & CONTEXT_WILDCARD)))
Simon Kelley1f776932012-12-16 19:46:08 +0000547 {
548 start6 = *local;
549 setaddr6part(&start6, addr6part(&template->start6));
550 end6 = *local;
551 setaddr6part(&end6, addr6part(&template->end6));
552
553 for (context = daemon->dhcp6; context; context = context->next)
554 if ((context->flags & CONTEXT_CONSTRUCTED) &&
555 IN6_ARE_ADDR_EQUAL(&start6, &context->start6) &&
556 IN6_ARE_ADDR_EQUAL(&end6, &context->end6))
557 {
558 context->flags &= ~CONTEXT_GC;
559 break;
560 }
561
562 if (!context && (context = whine_malloc(sizeof (struct dhcp_context))))
563 {
564 *context = *template;
565 context->start6 = start6;
566 context->end6 = end6;
567 context->flags &= ~CONTEXT_TEMPLATE;
568 context->flags |= CONTEXT_CONSTRUCTED;
569 context->if_index = if_index;
570 context->local6 = *local;
Simon Kelley1f776932012-12-16 19:46:08 +0000571
572 context->next = daemon->dhcp6;
573 daemon->dhcp6 = context;
574
Simon Kelley1b75c1e2012-12-18 19:55:25 +0000575 ra_start_unsolicted(param->now, context);
Simon Kelley1f776932012-12-16 19:46:08 +0000576 /* we created a new one, need to call
577 lease_update_file to get periodic functions called */
578 param->newone = 1;
Simon Kelley0c050242012-12-22 22:13:19 +0000579
580 /* Will need to add new putative SLAAC addresses to existing leases */
581 if (context->flags & CONTEXT_RA_NAME)
582 param->newname = 1;
Simon Kelley1f776932012-12-16 19:46:08 +0000583
584 log_context(AF_INET6, context);
585 }
Simon Kelley1f776932012-12-16 19:46:08 +0000586 }
587
588 return 1;
589}
590
591void dhcp_construct_contexts(time_t now)
592{
593 struct dhcp_context *tmp, *context, **up;
594 struct cparam param;
595 param.newone = 0;
Simon Kelley0c050242012-12-22 22:13:19 +0000596 param.newname = 0;
Simon Kelley1f776932012-12-16 19:46:08 +0000597 param.now = now;
598
599 for (context = daemon->dhcp6; context; context = context->next)
Simon Kelleyd7346a12012-12-22 22:45:54 +0000600 {
601 context->if_index = 0;
602 if (context->flags & CONTEXT_CONSTRUCTED)
603 context->flags |= CONTEXT_GC;
604 }
Simon Kelley8445f5d2012-12-17 21:54:08 +0000605
Simon Kelley1f776932012-12-16 19:46:08 +0000606 iface_enumerate(AF_INET6, &param, construct_worker);
607
608 for (up = &daemon->dhcp6, context = daemon->dhcp6; context; context = tmp)
609 {
610 tmp = context->next;
611
612 if (context->flags & CONTEXT_GC)
613 {
Simon Kelley1f776932012-12-16 19:46:08 +0000614 *up = context->next;
615 free(context);
Simon Kelley0c050242012-12-22 22:13:19 +0000616 param.newone = 1; /* include deletion */
617 if (context->flags & CONTEXT_RA_NAME)
618 param.newname = 1;
Simon Kelley1f776932012-12-16 19:46:08 +0000619 }
620 else
621 up = &context->next;
622 }
623
624 if (param.newone)
Simon Kelley7558ecd2012-12-16 21:45:16 +0000625 {
626 if (daemon->dhcp || daemon->doing_dhcp6)
Simon Kelley0c050242012-12-22 22:13:19 +0000627 {
628 if (param.newname)
629 lease_update_slaac(now);
630 lease_update_file(now);
631 }
Simon Kelley7558ecd2012-12-16 21:45:16 +0000632 else
633 /* Not doing DHCP, so no lease system, manage alarms for ra only */
634 send_alarm(periodic_ra(now), now);
635 }
Simon Kelley1f776932012-12-16 19:46:08 +0000636}
Simon Kelley1f776932012-12-16 19:46:08 +0000637
Simon Kelleyc72daea2012-01-05 21:33:27 +0000638#endif
639
640