blob: dd880583ed0d3e0784bc2cde465aaded3386047f [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 Kelley6e3dba32012-12-16 21:52:45 +0000214 if (!(context->flags & CONTEXT_TEMPLATE) &&
215 prefix == context->prefix &&
Simon Kelleye44ddca2012-02-18 17:08:50 +0000216 is_same_net6(local, &context->start6, prefix) &&
217 is_same_net6(local, &context->end6, prefix))
218 {
Simon Kelley3bc0d932012-12-28 11:31:44 +0000219
220
Simon Kelleye44ddca2012-02-18 17:08:50 +0000221 /* link it onto the current chain if we've not seen it before */
222 if (context->current == context)
223 {
Simon Kelley3bc0d932012-12-28 11:31:44 +0000224 struct dhcp_context *tmp, **up;
225
226 /* use interface values only for contructed contexts */
227 if (!(context->flags & CONTEXT_CONSTRUCTED))
228 preferred = valid = 0xffffffff;
229 else if (flags & IFACE_DEPRECATED)
230 preferred = 0;
231
232 if (context->flags & CONTEXT_DEPRECATE)
233 preferred = 0;
234
235 /* order chain, longest preferred time first */
236 for (up = &param->current, tmp = param->current; tmp; tmp = tmp->current)
237 if (tmp->preferred <= preferred)
238 break;
Simon Kelleyb456b9f2013-01-02 17:59:28 +0000239 else
240 up = &tmp->current;
Simon Kelley3bc0d932012-12-28 11:31:44 +0000241
242 context->current = *up;
243 *up = context;
Simon Kelleye44ddca2012-02-18 17:08:50 +0000244 context->local6 = *local;
Simon Kelley3bc0d932012-12-28 11:31:44 +0000245 context->preferred = preferred;
246 context->valid = valid;
Simon Kelleye44ddca2012-02-18 17:08:50 +0000247 }
248 }
Simon Kelleyc72daea2012-01-05 21:33:27 +0000249 }
250 }
251 return 1;
252}
Simon Kelley52b92f42012-01-22 16:05:15 +0000253
254struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct in6_addr *net, int prefix, u64 addr)
255{
256 struct dhcp_config *config;
257
258 for (config = configs; config; config = config->next)
259 if ((config->flags & CONFIG_ADDR6) &&
260 is_same_net6(&config->addr6, net, prefix) &&
261 (prefix == 128 || addr6part(&config->addr6) == addr))
262 return config;
263
264 return NULL;
265}
266
267int address6_allocate(struct dhcp_context *context, unsigned char *clid, int clid_len,
Simon Kelley4cb1b322012-02-06 14:30:41 +0000268 int serial, struct dhcp_netid *netids, struct in6_addr *ans)
Simon Kelley52b92f42012-01-22 16:05:15 +0000269{
270 /* Find a free address: exclude anything in use and anything allocated to
271 a particular hwaddr/clientid/hostname in our configuration.
272 Try to return from contexts which match netids first.
273
274 Note that we assume the address prefix lengths are 64 or greater, so we can
275 get by with 64 bit arithmetic.
276*/
277
278 u64 start, addr;
279 struct dhcp_context *c, *d;
280 int i, pass;
281 u64 j;
282
283 /* hash hwaddr: use the SDBM hashing algorithm. This works
284 for MAC addresses, let's see how it manages with client-ids! */
285 for (j = 0, i = 0; i < clid_len; i++)
286 j += clid[i] + (j << 6) + (j << 16) - j;
287
288 for (pass = 0; pass <= 1; pass++)
289 for (c = context; c; c = c->current)
Simon Kelleyc8257542012-03-28 21:15:41 +0100290 if (c->flags & (CONTEXT_DEPRECATE | CONTEXT_STATIC | CONTEXT_RA_STATELESS))
Simon Kelley52b92f42012-01-22 16:05:15 +0000291 continue;
292 else if (!match_netid(c->filter, netids, pass))
293 continue;
294 else
Simon Kelley07933802012-02-14 20:55:25 +0000295 {
296 if (option_bool(OPT_CONSEC_ADDR))
297 /* seed is largest extant lease addr in this context */
298 start = lease_find_max_addr6(c) + serial;
299 else
300 start = addr6part(&c->start6) + ((j + c->addr_epoch + serial) % (1 + addr6part(&c->end6) - addr6part(&c->start6)));
Simon Kelley52b92f42012-01-22 16:05:15 +0000301
302 /* iterate until we find a free address. */
303 addr = start;
304
305 do {
306 /* eliminate addresses in use by the server. */
307 for (d = context; d; d = d->current)
Simon Kelleye44ddca2012-02-18 17:08:50 +0000308 if (addr == addr6part(&d->local6))
Simon Kelley52b92f42012-01-22 16:05:15 +0000309 break;
310
311 if (!d &&
312 !lease6_find_by_addr(&c->start6, c->prefix, addr) &&
313 !config_find_by_address6(daemon->dhcp_conf, &c->start6, c->prefix, addr))
314 {
315 *ans = c->start6;
316 setaddr6part (ans, addr);
317 return 1;
318 }
319
320 addr++;
321
322 if (addr == addr6part(&c->end6) + 1)
323 addr = addr6part(&c->start6);
324
325 } while (addr != start);
326 }
327
328 return 0;
329}
330
331struct dhcp_context *address6_available(struct dhcp_context *context,
332 struct in6_addr *taddr,
333 struct dhcp_netid *netids)
334{
335 u64 start, end, addr = addr6part(taddr);
336 struct dhcp_context *tmp;
337
338 for (tmp = context; tmp; tmp = tmp->current)
339 {
340 start = addr6part(&tmp->start6);
341 end = addr6part(&tmp->end6);
342
Simon Kelleyc8257542012-03-28 21:15:41 +0100343 if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_RA_STATELESS)) &&
344 is_same_net6(&tmp->start6, taddr, tmp->prefix) &&
345 is_same_net6(&tmp->end6, taddr, tmp->prefix) &&
Simon Kelley52b92f42012-01-22 16:05:15 +0000346 addr >= start &&
347 addr <= end &&
348 match_netid(tmp->filter, netids, 1))
349 return tmp;
350 }
351
352 return NULL;
353}
354
355struct dhcp_context *narrow_context6(struct dhcp_context *context,
356 struct in6_addr *taddr,
357 struct dhcp_netid *netids)
358{
359 /* We start of with a set of possible contexts, all on the current physical interface.
360 These are chained on ->current.
361 Here we have an address, and return the actual context correponding to that
362 address. Note that none may fit, if the address came a dhcp-host and is outside
363 any dhcp-range. In that case we return a static range if possible, or failing that,
364 any context on the correct subnet. (If there's more than one, this is a dodgy
365 configuration: maybe there should be a warning.) */
366
367 struct dhcp_context *tmp;
368
369 if (!(tmp = address6_available(context, taddr, netids)))
370 {
371 for (tmp = context; tmp; tmp = tmp->current)
372 if (match_netid(tmp->filter, netids, 1) &&
373 is_same_net6(taddr, &tmp->start6, tmp->prefix) &&
374 (tmp->flags & CONTEXT_STATIC))
375 break;
376
377 if (!tmp)
378 for (tmp = context; tmp; tmp = tmp->current)
379 if (match_netid(tmp->filter, netids, 1) &&
380 is_same_net6(taddr, &tmp->start6, tmp->prefix) &&
381 !(tmp->flags & CONTEXT_PROXY))
382 break;
383 }
384
385 /* Only one context allowed now */
386 if (tmp)
387 tmp->current = NULL;
388
389 return tmp;
390}
391
Simon Kelleyb2692212012-09-16 22:15:56 +0100392static int is_config_in_context6(struct dhcp_context *context, struct dhcp_config *config)
Simon Kelley4cb1b322012-02-06 14:30:41 +0000393{
394 if (!context) /* called via find_config() from lease_update_from_configs() */
395 return 1;
Simon Kelleyb2692212012-09-16 22:15:56 +0100396 if (!(config->flags & CONFIG_ADDR6) || is_addr_in_context6(context, &config->addr6))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000397 return 1;
Simon Kelleyb2692212012-09-16 22:15:56 +0100398
399 return 0;
400}
401
402
403int is_addr_in_context6(struct dhcp_context *context, struct in6_addr *addr)
404{
Simon Kelley4cb1b322012-02-06 14:30:41 +0000405 for (; context; context = context->current)
Simon Kelleyb2692212012-09-16 22:15:56 +0100406 if (is_same_net6(addr, &context->start6, context->prefix))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000407 return 1;
408
409 return 0;
410}
411
412
413struct dhcp_config *find_config6(struct dhcp_config *configs,
414 struct dhcp_context *context,
415 unsigned char *duid, int duid_len,
416 char *hostname)
417{
Simon Kelley4cb1b322012-02-06 14:30:41 +0000418 struct dhcp_config *config;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000419
Simon Kelley60ac5af2012-02-15 10:41:03 +0000420 if (duid)
421 for (config = configs; config; config = config->next)
422 if (config->flags & CONFIG_CLID)
Simon Kelley4cb1b322012-02-06 14:30:41 +0000423 {
Simon Kelley60ac5af2012-02-15 10:41:03 +0000424 if (config->clid_len == duid_len &&
425 memcmp(config->clid, duid, duid_len) == 0 &&
Simon Kelleyb2692212012-09-16 22:15:56 +0100426 is_config_in_context6(context, config))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000427 return config;
Simon Kelley60ac5af2012-02-15 10:41:03 +0000428 }
429
Simon Kelley4cb1b322012-02-06 14:30:41 +0000430 if (hostname && context)
431 for (config = configs; config; config = config->next)
432 if ((config->flags & CONFIG_NAME) &&
433 hostname_isequal(config->hostname, hostname) &&
Simon Kelleyb2692212012-09-16 22:15:56 +0100434 is_config_in_context6(context, config))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000435 return config;
436
Simon Kelley4cb1b322012-02-06 14:30:41 +0000437 return NULL;
438}
439
440void make_duid(time_t now)
441{
Simon Kelley8b372702012-03-09 17:45:10 +0000442 if (daemon->duid_config)
443 {
444 unsigned char *p;
445
446 daemon->duid = p = safe_malloc(daemon->duid_config_len + 6);
447 daemon->duid_len = daemon->duid_config_len + 6;
448 PUTSHORT(2, p); /* DUID_EN */
449 PUTLONG(daemon->duid_enterprise, p);
450 memcpy(p, daemon->duid_config, daemon->duid_config_len);
451 }
452 else
453 {
454 /* rebase epoch to 1/1/2000 */
455 time_t newnow = now - 946684800;
456
457 iface_enumerate(AF_LOCAL, &newnow, make_duid1);
458
459 if(!daemon->duid)
460 die("Cannot create DHCPv6 server DUID: %s", NULL, EC_MISC);
461 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000462}
463
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000464static int make_duid1(int index, unsigned int type, char *mac, size_t maclen, void *parm)
Simon Kelley4cb1b322012-02-06 14:30:41 +0000465{
466 /* create DUID as specified in RFC3315. We use the MAC of the
Simon Kelley0f089832012-03-01 13:43:39 +0000467 first interface we find that isn't loopback or P-to-P and
468 has address-type < 256. Address types above 256 are things like
469 tunnels which don't have usable MAC addresses. */
Simon Kelley4cb1b322012-02-06 14:30:41 +0000470
471 unsigned char *p;
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000472 (void)index;
473
Simon Kelley0f089832012-03-01 13:43:39 +0000474 if (type >= 256)
475 return 1;
476
Simon Kelleye3e86342012-03-01 10:35:34 +0000477#ifdef HAVE_BROKEN_RTC
478 daemon->duid = p = safe_malloc(maclen + 4);
479 daemon->duid_len = maclen + 4;
480 PUTSHORT(3, p); /* DUID_LL */
481 PUTSHORT(type, p); /* address type */
482#else
Simon Kelley4cb1b322012-02-06 14:30:41 +0000483 daemon->duid = p = safe_malloc(maclen + 8);
484 daemon->duid_len = maclen + 8;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000485 PUTSHORT(1, p); /* DUID_LLT */
Simon Kelley4cb1b322012-02-06 14:30:41 +0000486 PUTSHORT(type, p); /* address type */
Simon Kelley4cb1b322012-02-06 14:30:41 +0000487 PUTLONG(*((time_t *)parm), p); /* time */
488#endif
489
490 memcpy(p, mac, maclen);
491
492 return 0;
493}
Simon Kelley1f776932012-12-16 19:46:08 +0000494
495struct cparam {
496 time_t now;
Simon Kelley0c050242012-12-22 22:13:19 +0000497 int newone, newname;
Simon Kelley1f776932012-12-16 19:46:08 +0000498};
499
500static int construct_worker(struct in6_addr *local, int prefix,
Simon Kelleybad7b872012-12-20 22:00:39 +0000501 int scope, int if_index, int flags,
Simon Kelley1f776932012-12-16 19:46:08 +0000502 int preferred, int valid, void *vparam)
503{
504 char ifrn_name[IFNAMSIZ];
505 struct in6_addr start6, end6;
506 struct dhcp_context *template, *context;
507
508 (void)scope;
Simon Kelleybad7b872012-12-20 22:00:39 +0000509 (void)flags;
Simon Kelleyed8b68a2012-12-21 16:23:26 +0000510 (void)valid;
511 (void)preferred;
Simon Kelley1f776932012-12-16 19:46:08 +0000512
513 struct cparam *param = vparam;
514
515 if (IN6_IS_ADDR_LOOPBACK(local) ||
516 IN6_IS_ADDR_LINKLOCAL(local) ||
517 IN6_IS_ADDR_MULTICAST(local))
518 return 1;
519
520 if (!indextoname(daemon->doing_dhcp6 ? daemon->dhcp6fd : daemon->icmp6fd, if_index, ifrn_name))
521 return 0;
522
523 for (template = daemon->dhcp6; template; template = template->next)
524 if (!(template->flags & CONTEXT_TEMPLATE))
525 {
526 /* non-template entries, just fill in interface and local addresses */
527 if (prefix == template->prefix &&
528 is_same_net6(local, &template->start6, prefix) &&
529 is_same_net6(local, &template->end6, prefix))
530 {
531 template->if_index = if_index;
532 template->local6 = *local;
533 }
534
535 }
Simon Kelley771287b2012-12-30 17:38:09 +0000536 else if (addr6part(local) == addr6part(&template->start6) &&
537 strncmp(template->template_interface, ifrn_name, strlen(template->template_interface)) == 0 &&
538 (strlen(template->template_interface) == strlen(ifrn_name) || (template->flags & CONTEXT_WILDCARD)))
Simon Kelley1f776932012-12-16 19:46:08 +0000539 {
540 start6 = *local;
541 setaddr6part(&start6, addr6part(&template->start6));
542 end6 = *local;
543 setaddr6part(&end6, addr6part(&template->end6));
544
545 for (context = daemon->dhcp6; context; context = context->next)
546 if ((context->flags & CONTEXT_CONSTRUCTED) &&
547 IN6_ARE_ADDR_EQUAL(&start6, &context->start6) &&
548 IN6_ARE_ADDR_EQUAL(&end6, &context->end6))
549 {
550 context->flags &= ~CONTEXT_GC;
551 break;
552 }
553
554 if (!context && (context = whine_malloc(sizeof (struct dhcp_context))))
555 {
556 *context = *template;
557 context->start6 = start6;
558 context->end6 = end6;
559 context->flags &= ~CONTEXT_TEMPLATE;
560 context->flags |= CONTEXT_CONSTRUCTED;
561 context->if_index = if_index;
562 context->local6 = *local;
Simon Kelley1f776932012-12-16 19:46:08 +0000563
564 context->next = daemon->dhcp6;
565 daemon->dhcp6 = context;
566
Simon Kelley1b75c1e2012-12-18 19:55:25 +0000567 ra_start_unsolicted(param->now, context);
Simon Kelley1f776932012-12-16 19:46:08 +0000568 /* we created a new one, need to call
569 lease_update_file to get periodic functions called */
570 param->newone = 1;
Simon Kelley0c050242012-12-22 22:13:19 +0000571
572 /* Will need to add new putative SLAAC addresses to existing leases */
573 if (context->flags & CONTEXT_RA_NAME)
574 param->newname = 1;
Simon Kelley1f776932012-12-16 19:46:08 +0000575
576 log_context(AF_INET6, context);
577 }
Simon Kelley1f776932012-12-16 19:46:08 +0000578 }
579
580 return 1;
581}
582
583void dhcp_construct_contexts(time_t now)
584{
585 struct dhcp_context *tmp, *context, **up;
586 struct cparam param;
587 param.newone = 0;
Simon Kelley0c050242012-12-22 22:13:19 +0000588 param.newname = 0;
Simon Kelley1f776932012-12-16 19:46:08 +0000589 param.now = now;
590
591 for (context = daemon->dhcp6; context; context = context->next)
Simon Kelleyd7346a12012-12-22 22:45:54 +0000592 {
593 context->if_index = 0;
594 if (context->flags & CONTEXT_CONSTRUCTED)
595 context->flags |= CONTEXT_GC;
596 }
Simon Kelley8445f5d2012-12-17 21:54:08 +0000597
Simon Kelley1f776932012-12-16 19:46:08 +0000598 iface_enumerate(AF_INET6, &param, construct_worker);
599
600 for (up = &daemon->dhcp6, context = daemon->dhcp6; context; context = tmp)
601 {
602 tmp = context->next;
603
604 if (context->flags & CONTEXT_GC)
605 {
Simon Kelley1f776932012-12-16 19:46:08 +0000606 *up = context->next;
607 free(context);
Simon Kelley0c050242012-12-22 22:13:19 +0000608 param.newone = 1; /* include deletion */
609 if (context->flags & CONTEXT_RA_NAME)
610 param.newname = 1;
Simon Kelley1f776932012-12-16 19:46:08 +0000611 }
612 else
613 up = &context->next;
614 }
615
616 if (param.newone)
Simon Kelley7558ecd2012-12-16 21:45:16 +0000617 {
618 if (daemon->dhcp || daemon->doing_dhcp6)
Simon Kelley0c050242012-12-22 22:13:19 +0000619 {
620 if (param.newname)
621 lease_update_slaac(now);
622 lease_update_file(now);
623 }
Simon Kelley7558ecd2012-12-16 21:45:16 +0000624 else
625 /* Not doing DHCP, so no lease system, manage alarms for ra only */
626 send_alarm(periodic_ra(now), now);
627 }
Simon Kelley1f776932012-12-16 19:46:08 +0000628}
Simon Kelley1f776932012-12-16 19:46:08 +0000629
Simon Kelleyc72daea2012-01-05 21:33:27 +0000630#endif
631
632