blob: 3bb855f78bf655dc0cc14125a53b9a6fefb1e457 [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
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 Kelley56a11422013-04-02 17:02:58 +010051 /* When bind-interfaces is set, there might be more than one dnmsasq
Simon Kelley20223102012-10-15 10:41:17 +010052 instance binding port 547. That's OK if they serve different networks.
Simon Kelley56a11422013-04-02 17:02:58 +010053 Need to set REUSEADDR|REUSEPORT to make this posible.
54 Handle the case that REUSEPORT is defined, but the kernel doesn't
55 support it. This handles the introduction of REUSEPORT on Linux. */
Simon Kelley20223102012-10-15 10:41:17 +010056 if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND))
57 {
Simon Kelleyffbad342013-08-14 15:53:57 +010058 int rc = 0;
Simon Kelley56a11422013-04-02 17:02:58 +010059
Simon Kelley20223102012-10-15 10:41:17 +010060#ifdef SO_REUSEPORT
Simon Kelley56a11422013-04-02 17:02:58 +010061 if ((rc = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &oneopt, sizeof(oneopt))) == -1 &&
Simon Kelleyffbad342013-08-14 15:53:57 +010062 errno == ENOPROTOOPT)
63 rc = 0;
Simon Kelley20223102012-10-15 10:41:17 +010064#endif
Simon Kelley56a11422013-04-02 17:02:58 +010065
Simon Kelleyffbad342013-08-14 15:53:57 +010066 if (rc != -1)
Simon Kelley56a11422013-04-02 17:02:58 +010067 rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt));
68
Simon Kelley20223102012-10-15 10:41:17 +010069 if (rc == -1)
70 die(_("failed to set SO_REUSE{ADDR|PORT} on DHCPv6 socket: %s"), NULL, EC_BADNET);
71 }
72
Simon Kelleyc72daea2012-01-05 21:33:27 +000073 memset(&saddr, 0, sizeof(saddr));
74#ifdef HAVE_SOCKADDR_SA_LEN
Simon Kelley4cb1b322012-02-06 14:30:41 +000075 saddr.sin6_len = sizeof(struct sockaddr_in6);
Simon Kelleyc72daea2012-01-05 21:33:27 +000076#endif
77 saddr.sin6_family = AF_INET6;
78 saddr.sin6_addr = in6addr_any;
79 saddr.sin6_port = htons(DHCPV6_SERVER_PORT);
80
81 if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in6)))
82 die(_("failed to bind DHCPv6 server socket: %s"), NULL, EC_BADNET);
83
Simon Kelleyc72daea2012-01-05 21:33:27 +000084 daemon->dhcp6fd = fd;
Simon Kelleyc72daea2012-01-05 21:33:27 +000085}
86
Simon Kelleyc72daea2012-01-05 21:33:27 +000087void dhcp6_packet(time_t now)
88{
89 struct dhcp_context *context;
90 struct iface_param parm;
91 struct cmsghdr *cmptr;
92 struct msghdr msg;
93 int if_index = 0;
94 union {
95 struct cmsghdr align; /* this ensures alignment */
96 char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
97 } control_u;
Simon Kelleyc5ad4e72012-02-24 16:06:20 +000098 struct sockaddr_in6 from;
Simon Kelleyc72daea2012-01-05 21:33:27 +000099 ssize_t sz;
100 struct ifreq ifr;
101 struct iname *tmp;
Simon Kelley1d0f91c2012-03-12 11:56:22 +0000102 unsigned short port;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000103
104 msg.msg_control = control_u.control6;
105 msg.msg_controllen = sizeof(control_u);
106 msg.msg_flags = 0;
107 msg.msg_name = &from;
108 msg.msg_namelen = sizeof(from);
109 msg.msg_iov = &daemon->dhcp_packet;
110 msg.msg_iovlen = 1;
111
Simon Kelley1d0f91c2012-03-12 11:56:22 +0000112 if ((sz = recv_dhcp_packet(daemon->dhcp6fd, &msg)) == -1)
Simon Kelleyc72daea2012-01-05 21:33:27 +0000113 return;
114
115 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
116 if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
117 {
118 union {
119 unsigned char *c;
120 struct in6_pktinfo *p;
121 } p;
122 p.c = CMSG_DATA(cmptr);
123
124 if_index = p.p->ipi6_ifindex;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000125 }
126
127 if (!indextoname(daemon->dhcp6fd, if_index, ifr.ifr_name))
128 return;
Simon Kelley52b92f42012-01-22 16:05:15 +0000129
Simon Kelleybe6cfb42012-10-16 20:38:31 +0100130 for (tmp = daemon->if_except; tmp; tmp = tmp->next)
Simon Kelley49333cb2013-03-15 20:30:51 +0000131 if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
Simon Kelleybe6cfb42012-10-16 20:38:31 +0100132 return;
133
Simon Kelleyc72daea2012-01-05 21:33:27 +0000134 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
Simon Kelley49333cb2013-03-15 20:30:51 +0000135 if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000136 return;
137
Simon Kelleyc72daea2012-01-05 21:33:27 +0000138 parm.current = NULL;
139 parm.ind = if_index;
Simon Kelleybe6cfb42012-10-16 20:38:31 +0100140 parm.addr_match = 0;
Simon Kelleye44ddca2012-02-18 17:08:50 +0000141 memset(&parm.fallback, 0, IN6ADDRSZ);
Simon Kelley819ff4d2012-10-21 18:25:12 +0100142
143 for (context = daemon->dhcp6; context; context = context->next)
144 if (IN6_IS_ADDR_UNSPECIFIED(&context->start6) && context->prefix == 0)
145 {
146 /* wildcard context for DHCP-stateless only */
147 parm.current = context;
148 context->current = NULL;
149 }
150 else
151 {
152 /* unlinked contexts are marked by context->current == context */
153 context->current = context;
154 memset(&context->local6, 0, IN6ADDRSZ);
155 }
Simon Kelleyc72daea2012-01-05 21:33:27 +0000156
157 if (!iface_enumerate(AF_INET6, &parm, complete_context6))
158 return;
159
Simon Kelleybe6cfb42012-10-16 20:38:31 +0100160 if (daemon->if_names || daemon->if_addrs)
161 {
162
163 for (tmp = daemon->if_names; tmp; tmp = tmp->next)
Simon Kelley49333cb2013-03-15 20:30:51 +0000164 if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
Simon Kelleybe6cfb42012-10-16 20:38:31 +0100165 break;
166
167 if (!tmp && !parm.addr_match)
168 return;
169 }
170
Simon Kelleyc72daea2012-01-05 21:33:27 +0000171 lease_prune(NULL, now); /* lose any expired leases */
172
Simon Kelley1d0f91c2012-03-12 11:56:22 +0000173 port = dhcp6_reply(parm.current, if_index, ifr.ifr_name, &parm.fallback,
174 sz, IN6_IS_ADDR_MULTICAST(&from.sin6_addr), now);
Simon Kelley62779782012-02-10 21:19:25 +0000175
Simon Kelleyc72daea2012-01-05 21:33:27 +0000176 lease_update_file(now);
Simon Kelley353ae4d2012-03-19 20:07:51 +0000177 lease_update_dns(0);
Simon Kelleyc72daea2012-01-05 21:33:27 +0000178
Simon Kelley1d0f91c2012-03-12 11:56:22 +0000179 /* The port in the source address of the original request should
180 be correct, but at least once client sends from the server port,
181 so we explicitly send to the client port to a client, and the
182 server port to a relay. */
183 if (port != 0)
184 {
185 from.sin6_port = htons(port);
186 while (sendto(daemon->dhcp6fd, daemon->outpacket.iov_base, save_counter(0),
187 0, (struct sockaddr *)&from, sizeof(from)) == -1 &&
Simon Kelley4cb1b322012-02-06 14:30:41 +0000188 retry_send());
Simon Kelley1d0f91c2012-03-12 11:56:22 +0000189 }
Simon Kelleyc72daea2012-01-05 21:33:27 +0000190}
191
192static int complete_context6(struct in6_addr *local, int prefix,
Simon Kelley3bc0d932012-12-28 11:31:44 +0000193 int scope, int if_index, int flags, unsigned int preferred,
194 unsigned int valid, void *vparam)
Simon Kelleyc72daea2012-01-05 21:33:27 +0000195{
196 struct dhcp_context *context;
197 struct iface_param *param = vparam;
Simon Kelleybe6cfb42012-10-16 20:38:31 +0100198 struct iname *tmp;
199
Simon Kelley52b92f42012-01-22 16:05:15 +0000200 (void)scope; /* warning */
Simon Kelley3bc0d932012-12-28 11:31:44 +0000201
Simon Kelleye44ddca2012-02-18 17:08:50 +0000202 if (if_index == param->ind &&
203 !IN6_IS_ADDR_LOOPBACK(local) &&
204 !IN6_IS_ADDR_LINKLOCAL(local) &&
205 !IN6_IS_ADDR_MULTICAST(local))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000206 {
Simon Kelleybe6cfb42012-10-16 20:38:31 +0100207 /* if we have --listen-address config, see if the
208 arrival interface has a matching address. */
209 for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
210 if (tmp->addr.sa.sa_family == AF_INET6 &&
211 IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, local))
212 param->addr_match = 1;
213
Simon Kelleye44ddca2012-02-18 17:08:50 +0000214 /* Determine a globally address on the arrival interface, even
215 if we have no matching dhcp-context, because we're only
216 allocating on remote subnets via relays. This
217 is used as a default for the DNS server option. */
Simon Kelley270dc2e2012-02-19 20:53:20 +0000218 param->fallback = *local;
Simon Kelleye44ddca2012-02-18 17:08:50 +0000219
220 for (context = daemon->dhcp6; context; context = context->next)
221 {
Simon Kelleybaeb3ad2013-01-10 11:47:38 +0000222 if ((context->flags & CONTEXT_DHCP) &&
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100223 !(context->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) &&
Simon Kelley6e3dba32012-12-16 21:52:45 +0000224 prefix == context->prefix &&
Simon Kelleye44ddca2012-02-18 17:08:50 +0000225 is_same_net6(local, &context->start6, prefix) &&
226 is_same_net6(local, &context->end6, prefix))
227 {
Simon Kelley3bc0d932012-12-28 11:31:44 +0000228
229
Simon Kelleye44ddca2012-02-18 17:08:50 +0000230 /* link it onto the current chain if we've not seen it before */
231 if (context->current == context)
232 {
Simon Kelley3bc0d932012-12-28 11:31:44 +0000233 struct dhcp_context *tmp, **up;
234
235 /* use interface values only for contructed contexts */
236 if (!(context->flags & CONTEXT_CONSTRUCTED))
237 preferred = valid = 0xffffffff;
238 else if (flags & IFACE_DEPRECATED)
239 preferred = 0;
240
241 if (context->flags & CONTEXT_DEPRECATE)
242 preferred = 0;
243
244 /* order chain, longest preferred time first */
245 for (up = &param->current, tmp = param->current; tmp; tmp = tmp->current)
246 if (tmp->preferred <= preferred)
247 break;
Simon Kelleyb456b9f2013-01-02 17:59:28 +0000248 else
249 up = &tmp->current;
Simon Kelley3bc0d932012-12-28 11:31:44 +0000250
251 context->current = *up;
252 *up = context;
Simon Kelleye44ddca2012-02-18 17:08:50 +0000253 context->local6 = *local;
Simon Kelley3bc0d932012-12-28 11:31:44 +0000254 context->preferred = preferred;
255 context->valid = valid;
Simon Kelleye44ddca2012-02-18 17:08:50 +0000256 }
257 }
Simon Kelleyc72daea2012-01-05 21:33:27 +0000258 }
259 }
260 return 1;
261}
Simon Kelley52b92f42012-01-22 16:05:15 +0000262
263struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct in6_addr *net, int prefix, u64 addr)
264{
265 struct dhcp_config *config;
266
267 for (config = configs; config; config = config->next)
268 if ((config->flags & CONFIG_ADDR6) &&
269 is_same_net6(&config->addr6, net, prefix) &&
270 (prefix == 128 || addr6part(&config->addr6) == addr))
271 return config;
272
273 return NULL;
274}
275
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000276struct dhcp_context *address6_allocate(struct dhcp_context *context, unsigned char *clid, int clid_len,
Simon Kelleyc6309242013-03-07 20:59:28 +0000277 int iaid, int serial, struct dhcp_netid *netids, int plain_range, struct in6_addr *ans)
Simon Kelley52b92f42012-01-22 16:05:15 +0000278{
279 /* Find a free address: exclude anything in use and anything allocated to
280 a particular hwaddr/clientid/hostname in our configuration.
281 Try to return from contexts which match netids first.
282
283 Note that we assume the address prefix lengths are 64 or greater, so we can
284 get by with 64 bit arithmetic.
285*/
286
287 u64 start, addr;
288 struct dhcp_context *c, *d;
289 int i, pass;
290 u64 j;
291
292 /* hash hwaddr: use the SDBM hashing algorithm. This works
293 for MAC addresses, let's see how it manages with client-ids! */
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000294 for (j = iaid, i = 0; i < clid_len; i++)
Simon Kelley52b92f42012-01-22 16:05:15 +0000295 j += clid[i] + (j << 6) + (j << 16) - j;
296
Simon Kelleyc6309242013-03-07 20:59:28 +0000297 for (pass = 0; pass <= plain_range ? 1 : 0; pass++)
Simon Kelley52b92f42012-01-22 16:05:15 +0000298 for (c = context; c; c = c->current)
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000299 if (c->flags & (CONTEXT_DEPRECATE | CONTEXT_STATIC | CONTEXT_RA_STATELESS | CONTEXT_USED))
Simon Kelley52b92f42012-01-22 16:05:15 +0000300 continue;
301 else if (!match_netid(c->filter, netids, pass))
302 continue;
303 else
Simon Kelley07933802012-02-14 20:55:25 +0000304 {
305 if (option_bool(OPT_CONSEC_ADDR))
306 /* seed is largest extant lease addr in this context */
Simon Kelleyc6309242013-03-07 20:59:28 +0000307 start = lease_find_max_addr6(c) + serial;
Simon Kelley07933802012-02-14 20:55:25 +0000308 else
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000309 start = addr6part(&c->start6) + ((j + c->addr_epoch) % (1 + addr6part(&c->end6) - addr6part(&c->start6)));
Simon Kelley52b92f42012-01-22 16:05:15 +0000310
311 /* iterate until we find a free address. */
312 addr = start;
313
314 do {
315 /* eliminate addresses in use by the server. */
316 for (d = context; d; d = d->current)
Simon Kelleye44ddca2012-02-18 17:08:50 +0000317 if (addr == addr6part(&d->local6))
Simon Kelley52b92f42012-01-22 16:05:15 +0000318 break;
319
320 if (!d &&
321 !lease6_find_by_addr(&c->start6, c->prefix, addr) &&
322 !config_find_by_address6(daemon->dhcp_conf, &c->start6, c->prefix, addr))
323 {
324 *ans = c->start6;
325 setaddr6part (ans, addr);
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000326 return c;
Simon Kelley52b92f42012-01-22 16:05:15 +0000327 }
328
329 addr++;
330
331 if (addr == addr6part(&c->end6) + 1)
332 addr = addr6part(&c->start6);
333
334 } while (addr != start);
335 }
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000336
Simon Kelley5b37aa82013-04-02 16:32:25 +0100337 return NULL;
Simon Kelley52b92f42012-01-22 16:05:15 +0000338}
339
Simon Kelley37c9cce2013-01-09 19:51:04 +0000340/* can dynamically allocate addr */
Simon Kelley52b92f42012-01-22 16:05:15 +0000341struct dhcp_context *address6_available(struct dhcp_context *context,
342 struct in6_addr *taddr,
Simon Kelleyc6309242013-03-07 20:59:28 +0000343 struct dhcp_netid *netids,
344 int plain_range)
Simon Kelley52b92f42012-01-22 16:05:15 +0000345{
346 u64 start, end, addr = addr6part(taddr);
347 struct dhcp_context *tmp;
348
349 for (tmp = context; tmp; tmp = tmp->current)
350 {
351 start = addr6part(&tmp->start6);
352 end = addr6part(&tmp->end6);
353
Simon Kelleyc8257542012-03-28 21:15:41 +0100354 if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_RA_STATELESS)) &&
355 is_same_net6(&tmp->start6, taddr, tmp->prefix) &&
356 is_same_net6(&tmp->end6, taddr, tmp->prefix) &&
Simon Kelley52b92f42012-01-22 16:05:15 +0000357 addr >= start &&
358 addr <= end &&
Simon Kelleyc6309242013-03-07 20:59:28 +0000359 match_netid(tmp->filter, netids, plain_range))
Simon Kelley52b92f42012-01-22 16:05:15 +0000360 return tmp;
361 }
362
363 return NULL;
364}
365
Simon Kelley37c9cce2013-01-09 19:51:04 +0000366/* address OK if configured */
367struct dhcp_context *address6_valid(struct dhcp_context *context,
Simon Kelleyc6309242013-03-07 20:59:28 +0000368 struct in6_addr *taddr,
369 struct dhcp_netid *netids,
370 int plain_range)
Simon Kelley37c9cce2013-01-09 19:51:04 +0000371{
372 struct dhcp_context *tmp;
373
374 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelleybaeb3ad2013-01-10 11:47:38 +0000375 if (is_same_net6(&tmp->start6, taddr, tmp->prefix) &&
Simon Kelleyc6309242013-03-07 20:59:28 +0000376 match_netid(tmp->filter, netids, plain_range))
Simon Kelley37c9cce2013-01-09 19:51:04 +0000377 return tmp;
378
379 return NULL;
380}
381
Simon Kelleyde92b472013-03-15 18:25:10 +0000382int config_valid(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr)
Simon Kelley4cb1b322012-02-06 14:30:41 +0000383{
Simon Kelleyde92b472013-03-15 18:25:10 +0000384 if (!config || !(config->flags & CONFIG_ADDR6))
385 return 0;
386
387 if ((config->flags & CONFIG_WILDCARD) && context->prefix == 64)
Simon Kelley30393102013-01-17 16:34:16 +0000388 {
Simon Kelleyde92b472013-03-15 18:25:10 +0000389 *addr = context->start6;
390 setaddr6part(addr, addr6part(&config->addr6));
Simon Kelley30393102013-01-17 16:34:16 +0000391 return 1;
392 }
393
Simon Kelleyde92b472013-03-15 18:25:10 +0000394 if (is_same_net6(&context->start6, &config->addr6, context->prefix))
395 {
396 *addr = config->addr6;
397 return 1;
398 }
399
Simon Kelleyb2692212012-09-16 22:15:56 +0100400 return 0;
401}
402
Simon Kelleyde92b472013-03-15 18:25:10 +0000403static int is_config_in_context6(struct dhcp_context *context, struct dhcp_config *config)
Simon Kelleyb2692212012-09-16 22:15:56 +0100404{
Simon Kelleyde92b472013-03-15 18:25:10 +0000405 if (!(config->flags & CONFIG_ADDR6) ||
406 (config->flags & CONFIG_WILDCARD))
407
408 return 1;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000409
Simon Kelleyde92b472013-03-15 18:25:10 +0000410 for (; context; context = context->current)
411 if (is_same_net6(&config->addr6, &context->start6, context->prefix))
412 return 1;
413
Simon Kelley4cb1b322012-02-06 14:30:41 +0000414 return 0;
415}
416
417
418struct dhcp_config *find_config6(struct dhcp_config *configs,
419 struct dhcp_context *context,
420 unsigned char *duid, int duid_len,
421 char *hostname)
422{
Simon Kelley4cb1b322012-02-06 14:30:41 +0000423 struct dhcp_config *config;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000424
Simon Kelley60ac5af2012-02-15 10:41:03 +0000425 if (duid)
426 for (config = configs; config; config = config->next)
427 if (config->flags & CONFIG_CLID)
Simon Kelley4cb1b322012-02-06 14:30:41 +0000428 {
Simon Kelley60ac5af2012-02-15 10:41:03 +0000429 if (config->clid_len == duid_len &&
430 memcmp(config->clid, duid, duid_len) == 0 &&
Simon Kelleyb2692212012-09-16 22:15:56 +0100431 is_config_in_context6(context, config))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000432 return config;
Simon Kelley60ac5af2012-02-15 10:41:03 +0000433 }
434
Simon Kelley4cb1b322012-02-06 14:30:41 +0000435 if (hostname && context)
436 for (config = configs; config; config = config->next)
437 if ((config->flags & CONFIG_NAME) &&
438 hostname_isequal(config->hostname, hostname) &&
Simon Kelleyb2692212012-09-16 22:15:56 +0100439 is_config_in_context6(context, config))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000440 return config;
441
Simon Kelley4cb1b322012-02-06 14:30:41 +0000442 return NULL;
443}
444
445void make_duid(time_t now)
446{
Simon Kelley8b372702012-03-09 17:45:10 +0000447 if (daemon->duid_config)
448 {
449 unsigned char *p;
450
451 daemon->duid = p = safe_malloc(daemon->duid_config_len + 6);
452 daemon->duid_len = daemon->duid_config_len + 6;
453 PUTSHORT(2, p); /* DUID_EN */
454 PUTLONG(daemon->duid_enterprise, p);
455 memcpy(p, daemon->duid_config, daemon->duid_config_len);
456 }
457 else
458 {
459 /* rebase epoch to 1/1/2000 */
460 time_t newnow = now - 946684800;
461
462 iface_enumerate(AF_LOCAL, &newnow, make_duid1);
463
464 if(!daemon->duid)
465 die("Cannot create DHCPv6 server DUID: %s", NULL, EC_MISC);
466 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000467}
468
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000469static int make_duid1(int index, unsigned int type, char *mac, size_t maclen, void *parm)
Simon Kelley4cb1b322012-02-06 14:30:41 +0000470{
471 /* create DUID as specified in RFC3315. We use the MAC of the
Simon Kelley0f089832012-03-01 13:43:39 +0000472 first interface we find that isn't loopback or P-to-P and
473 has address-type < 256. Address types above 256 are things like
474 tunnels which don't have usable MAC addresses. */
Simon Kelley4cb1b322012-02-06 14:30:41 +0000475
476 unsigned char *p;
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000477 (void)index;
478
Simon Kelley0f089832012-03-01 13:43:39 +0000479 if (type >= 256)
480 return 1;
481
Simon Kelleye3e86342012-03-01 10:35:34 +0000482#ifdef HAVE_BROKEN_RTC
483 daemon->duid = p = safe_malloc(maclen + 4);
484 daemon->duid_len = maclen + 4;
485 PUTSHORT(3, p); /* DUID_LL */
486 PUTSHORT(type, p); /* address type */
487#else
Simon Kelley4cb1b322012-02-06 14:30:41 +0000488 daemon->duid = p = safe_malloc(maclen + 8);
489 daemon->duid_len = maclen + 8;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000490 PUTSHORT(1, p); /* DUID_LLT */
Simon Kelley4cb1b322012-02-06 14:30:41 +0000491 PUTSHORT(type, p); /* address type */
Simon Kelley4cb1b322012-02-06 14:30:41 +0000492 PUTLONG(*((time_t *)parm), p); /* time */
493#endif
494
495 memcpy(p, mac, maclen);
496
497 return 0;
498}
Simon Kelley1f776932012-12-16 19:46:08 +0000499
500struct cparam {
501 time_t now;
Simon Kelley0c050242012-12-22 22:13:19 +0000502 int newone, newname;
Simon Kelley1f776932012-12-16 19:46:08 +0000503};
504
505static int construct_worker(struct in6_addr *local, int prefix,
Simon Kelleybad7b872012-12-20 22:00:39 +0000506 int scope, int if_index, int flags,
Simon Kelley1f776932012-12-16 19:46:08 +0000507 int preferred, int valid, void *vparam)
508{
509 char ifrn_name[IFNAMSIZ];
510 struct in6_addr start6, end6;
511 struct dhcp_context *template, *context;
512
513 (void)scope;
Simon Kelleybad7b872012-12-20 22:00:39 +0000514 (void)flags;
Simon Kelleyed8b68a2012-12-21 16:23:26 +0000515 (void)valid;
516 (void)preferred;
Simon Kelley1f776932012-12-16 19:46:08 +0000517
518 struct cparam *param = vparam;
519
520 if (IN6_IS_ADDR_LOOPBACK(local) ||
521 IN6_IS_ADDR_LINKLOCAL(local) ||
522 IN6_IS_ADDR_MULTICAST(local))
523 return 1;
524
525 if (!indextoname(daemon->doing_dhcp6 ? daemon->dhcp6fd : daemon->icmp6fd, if_index, ifrn_name))
526 return 0;
527
528 for (template = daemon->dhcp6; template; template = template->next)
529 if (!(template->flags & CONTEXT_TEMPLATE))
530 {
531 /* non-template entries, just fill in interface and local addresses */
532 if (prefix == template->prefix &&
533 is_same_net6(local, &template->start6, prefix) &&
534 is_same_net6(local, &template->end6, prefix))
535 {
536 template->if_index = if_index;
537 template->local6 = *local;
538 }
539
540 }
Simon Kelley429805d2013-05-31 13:47:26 +0100541 else if ((addr6part(local) == addr6part(&template->start6) ||
Vladislav Grishenkoe4cdbbf2013-08-19 16:20:31 +0100542 addr6part(local) == addr6part(&template->end6) ||
543 (IN6_IS_ADDR_UNSPECIFIED(&template->start6) &&
544 IFACE_PERMANENT == (flags & (IFACE_PERMANENT | IFACE_DEPRECATED)))) &&
Simon Kelley429805d2013-05-31 13:47:26 +0100545 wildcard_match(template->template_interface, ifrn_name))
Simon Kelley1f776932012-12-16 19:46:08 +0000546 {
547 start6 = *local;
548 setaddr6part(&start6, addr6part(&template->start6));
549 end6 = *local;
550 setaddr6part(&end6, addr6part(&template->end6));
551
552 for (context = daemon->dhcp6; context; context = context->next)
553 if ((context->flags & CONTEXT_CONSTRUCTED) &&
554 IN6_ARE_ADDR_EQUAL(&start6, &context->start6) &&
555 IN6_ARE_ADDR_EQUAL(&end6, &context->end6))
556 {
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100557 int flags = context->flags;
558 context->flags &= ~(CONTEXT_GC | CONTEXT_OLD);
559 if (flags & CONTEXT_OLD)
Simon Kelleyf7a40ec2013-07-27 13:36:08 +0100560 {
561 /* address went, now it's back */
562 log_context(AF_INET6, context);
563 /* fast RAs for a while */
564 ra_start_unsolicted(param->now, context);
565 /* Add address to name again */
566 if (context->flags & CONTEXT_RA_NAME)
567 param->newname = 1;
568 }
Simon Kelley1f776932012-12-16 19:46:08 +0000569 break;
570 }
571
572 if (!context && (context = whine_malloc(sizeof (struct dhcp_context))))
573 {
574 *context = *template;
575 context->start6 = start6;
576 context->end6 = end6;
577 context->flags &= ~CONTEXT_TEMPLATE;
578 context->flags |= CONTEXT_CONSTRUCTED;
579 context->if_index = if_index;
580 context->local6 = *local;
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100581 context->saved_valid = 0;
Simon Kelley1f776932012-12-16 19:46:08 +0000582
583 context->next = daemon->dhcp6;
584 daemon->dhcp6 = context;
585
Simon Kelley1b75c1e2012-12-18 19:55:25 +0000586 ra_start_unsolicted(param->now, context);
Simon Kelley1f776932012-12-16 19:46:08 +0000587 /* we created a new one, need to call
588 lease_update_file to get periodic functions called */
589 param->newone = 1;
Simon Kelley0c050242012-12-22 22:13:19 +0000590
591 /* Will need to add new putative SLAAC addresses to existing leases */
592 if (context->flags & CONTEXT_RA_NAME)
593 param->newname = 1;
Simon Kelley1f776932012-12-16 19:46:08 +0000594
595 log_context(AF_INET6, context);
596 }
Simon Kelley1f776932012-12-16 19:46:08 +0000597 }
598
599 return 1;
600}
601
602void dhcp_construct_contexts(time_t now)
603{
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100604 struct dhcp_context *context, *tmp, **up;
Simon Kelley1f776932012-12-16 19:46:08 +0000605 struct cparam param;
606 param.newone = 0;
Simon Kelley0c050242012-12-22 22:13:19 +0000607 param.newname = 0;
Simon Kelley1f776932012-12-16 19:46:08 +0000608 param.now = now;
609
610 for (context = daemon->dhcp6; context; context = context->next)
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100611 if (context->flags & CONTEXT_CONSTRUCTED)
612 context->flags |= CONTEXT_GC;
613
Simon Kelley1f776932012-12-16 19:46:08 +0000614 iface_enumerate(AF_INET6, &param, construct_worker);
615
616 for (up = &daemon->dhcp6, context = daemon->dhcp6; context; context = tmp)
617 {
Simon Kelley1f776932012-12-16 19:46:08 +0000618
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100619 tmp = context->next;
620
621 if (context->flags & CONTEXT_GC && !(context->flags & CONTEXT_OLD))
Simon Kelley1f776932012-12-16 19:46:08 +0000622 {
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100623
624 if ((context->flags & (CONTEXT_RA_ONLY | CONTEXT_RA_NAME | CONTEXT_RA_STATELESS)) ||
625 option_bool(OPT_RA))
626 {
627 /* previously constructed context has gone. advertise it's demise */
628 context->flags |= CONTEXT_OLD;
629 context->address_lost_time = now;
Simon Kelley9f48ffa2013-07-28 15:47:04 +0100630 /* Apply same ceiling of configured lease time as in radv.c */
631 if (context->saved_valid > context->lease_time)
632 context->saved_valid = context->lease_time;
633 /* maximum time is 2 hours, from RFC */
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100634 if (context->saved_valid > 7200) /* 2 hours */
635 context->saved_valid = 7200;
636 ra_start_unsolicted(now, context);
637 param.newone = 1; /* include deletion */
638
639 if (context->flags & CONTEXT_RA_NAME)
640 param.newname = 1;
641
642 log_context(AF_INET6, context);
643
644 up = &context->next;
645 }
646 else
647 {
648 /* we were never doing RA for this, so free now */
649 *up = context->next;
650 free(context);
651 }
Simon Kelley1f776932012-12-16 19:46:08 +0000652 }
653 else
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100654 up = &context->next;
Simon Kelley1f776932012-12-16 19:46:08 +0000655 }
656
657 if (param.newone)
Simon Kelley7558ecd2012-12-16 21:45:16 +0000658 {
659 if (daemon->dhcp || daemon->doing_dhcp6)
Simon Kelley0c050242012-12-22 22:13:19 +0000660 {
661 if (param.newname)
662 lease_update_slaac(now);
663 lease_update_file(now);
664 }
Simon Kelley7558ecd2012-12-16 21:45:16 +0000665 else
666 /* Not doing DHCP, so no lease system, manage alarms for ra only */
667 send_alarm(periodic_ra(now), now);
668 }
Simon Kelley1f776932012-12-16 19:46:08 +0000669}
Simon Kelley1f776932012-12-16 19:46:08 +0000670
Simon Kelleyc72daea2012-01-05 21:33:27 +0000671#endif
672
673