]> git.ipfire.org Git - people/ms/dnsmasq.git/blame - src/dhcp6.c
lease script should work with IPv6 now.
[people/ms/dnsmasq.git] / src / dhcp6.c
CommitLineData
59546085 1/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
c72daea8
SK
2
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;
23 int ind;
24};
25
4cb1b320
SK
26struct listen_param {
27 int fd_or_iface;
28 struct listen_param *next;
29};
30
c72daea8 31static int join_multicast(struct in6_addr *local, int prefix,
52b92f4d 32 int scope, int if_index, int dad, void *vparam);
c72daea8
SK
33
34static int complete_context6(struct in6_addr *local, int prefix,
52b92f4d 35 int scope, int if_index, int dad, void *vparam);
c72daea8 36
4cb1b320
SK
37static int make_duid1(unsigned int type, unsigned int flags, char *mac,
38 size_t maclen, void *parm);
39
c72daea8
SK
40void dhcp6_init(void)
41{
42 int fd;
43 struct sockaddr_in6 saddr;
4cb1b320
SK
44 struct listen_param *listenp, listen;
45#if defined(IP_TOS) && defined(IPTOS_CLASS_CS6)
c72daea8 46 int class = IPTOS_CLASS_CS6;
4cb1b320 47#endif
c72daea8
SK
48
49 if ((fd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP)) == -1 ||
4cb1b320 50#if defined(IP_TOS) && defined(IPTOS_CLASS_CS6)
c72daea8 51 setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &class, sizeof(class)) == -1 ||
4cb1b320 52#endif
c72daea8
SK
53 !fix_fd(fd) ||
54 !set_ipv6pktinfo(fd))
55 die (_("cannot create DHCPv6 socket: %s"), NULL, EC_BADNET);
56
57 memset(&saddr, 0, sizeof(saddr));
58#ifdef HAVE_SOCKADDR_SA_LEN
4cb1b320 59 saddr.sin6_len = sizeof(struct sockaddr_in6);
c72daea8
SK
60#endif
61 saddr.sin6_family = AF_INET6;
62 saddr.sin6_addr = in6addr_any;
63 saddr.sin6_port = htons(DHCPV6_SERVER_PORT);
64
65 if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in6)))
66 die(_("failed to bind DHCPv6 server socket: %s"), NULL, EC_BADNET);
67
68 /* join multicast groups on each interface we're interested in */
4cb1b320
SK
69 listen.fd_or_iface = fd;
70 listen.next = NULL;
71 if (!iface_enumerate(AF_INET6, &listen, join_multicast))
c72daea8 72 die(_("failed to join DHCPv6 multicast group: %s"), NULL, EC_BADNET);
4cb1b320
SK
73 for (listenp = listen.next; listenp; )
74 {
75 struct listen_param *tmp = listenp->next;
76 free(listenp);
77 listenp = tmp;
78 }
c72daea8
SK
79
80 daemon->dhcp6fd = fd;
c72daea8
SK
81}
82
83static int join_multicast(struct in6_addr *local, int prefix,
52b92f4d 84 int scope, int if_index, int dad, void *vparam)
c72daea8
SK
85{
86 char ifrn_name[IFNAMSIZ];
87 struct ipv6_mreq mreq;
4cb1b320
SK
88 struct listen_param *listenp, *param = vparam;
89 int fd = param->fd_or_iface;
c72daea8
SK
90 struct dhcp_context *context;
91 struct iname *tmp;
92
93 (void)prefix;
4cb1b320
SK
94 (void)scope;
95 (void)dad;
96
97 /* record which interfaces we join on, so
98 that we do it at most one per interface, even when they
99 have multiple addresses */
100 for (listenp = param->next; listenp; listenp = listenp->next)
101 if (if_index == listenp->fd_or_iface)
102 return 1;
52b92f4d 103
c72daea8
SK
104 if (!indextoname(fd, if_index, ifrn_name))
105 return 0;
4cb1b320 106
c72daea8
SK
107 /* Are we doing DHCP on this interface? */
108 if (!iface_check(AF_INET6, (struct all_addr *)local, ifrn_name))
109 return 1;
110
111 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
112 if (tmp->name && (strcmp(tmp->name, ifrn_name) == 0))
113 return 1;
114
115 /* weird libvirt-inspired access control */
52b92f4d 116 for (context = daemon->dhcp6; context; context = context->next)
c72daea8
SK
117 if (!context->interface || strcmp(context->interface, ifrn_name) == 0)
118 break;
119
120 if (!context)
121 return 1;
122
123 mreq.ipv6mr_interface = if_index;
52b92f4d 124 inet_pton(AF_INET6, ALL_RELAY_AGENTS_AND_SERVERS, &mreq.ipv6mr_multiaddr);
c72daea8 125
52b92f4d 126 if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
c72daea8
SK
127 return 0;
128
52b92f4d 129 inet_pton(AF_INET6, ALL_SERVERS, &mreq.ipv6mr_multiaddr);
c72daea8 130
52b92f4d 131 if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
c72daea8
SK
132 return 0;
133
4cb1b320
SK
134 listenp = whine_malloc(sizeof(struct listen_param));
135 listenp->fd_or_iface = if_index;
136 listenp->next = param->next;
137 param->next = listenp;
138
c72daea8 139 return 1;
c72daea8
SK
140}
141
142
143
144
145void dhcp6_packet(time_t now)
146{
147 struct dhcp_context *context;
148 struct iface_param parm;
149 struct cmsghdr *cmptr;
150 struct msghdr msg;
151 int if_index = 0;
152 union {
153 struct cmsghdr align; /* this ensures alignment */
154 char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
155 } control_u;
156 union mysockaddr from;
157 struct all_addr dest;
158 ssize_t sz;
159 struct ifreq ifr;
160 struct iname *tmp;
161
162 msg.msg_control = control_u.control6;
163 msg.msg_controllen = sizeof(control_u);
164 msg.msg_flags = 0;
165 msg.msg_name = &from;
166 msg.msg_namelen = sizeof(from);
167 msg.msg_iov = &daemon->dhcp_packet;
168 msg.msg_iovlen = 1;
169
52b92f4d 170 if ((sz = recv_dhcp_packet(daemon->dhcp6fd, &msg)) == -1 || sz <= 4)
c72daea8
SK
171 return;
172
173 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
174 if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
175 {
176 union {
177 unsigned char *c;
178 struct in6_pktinfo *p;
179 } p;
180 p.c = CMSG_DATA(cmptr);
181
182 if_index = p.p->ipi6_ifindex;
183 dest.addr.addr6 = p.p->ipi6_addr;
184 }
185
186 if (!indextoname(daemon->dhcp6fd, if_index, ifr.ifr_name))
187 return;
52b92f4d 188
c72daea8
SK
189 if (!iface_check(AF_INET6, (struct all_addr *)&dest, ifr.ifr_name))
190 return;
191
192 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
193 if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
194 return;
195
196 /* weird libvirt-inspired access control */
52b92f4d 197 for (context = daemon->dhcp6; context; context = context->next)
c72daea8
SK
198 if (!context->interface || strcmp(context->interface, ifr.ifr_name) == 0)
199 break;
200
201 if (!context)
202 return;
4cb1b320 203
c72daea8 204 /* unlinked contexts are marked by context->current == context */
52b92f4d 205 for (context = daemon->dhcp6; context; context = context->next)
c72daea8
SK
206 context->current = context;
207
208 parm.current = NULL;
209 parm.ind = if_index;
210
211 if (!iface_enumerate(AF_INET6, &parm, complete_context6))
212 return;
213
214 lease_prune(NULL, now); /* lose any expired leases */
215
216 msg.msg_iov = &daemon->dhcp_packet;
ceae00dd 217 sz = dhcp6_reply(parm.current, if_index, ifr.ifr_name, sz, IN6_IS_ADDR_MULTICAST(&from), now);
c72daea8
SK
218 /* ifr.ifr_name, if_index, (size_t)sz,
219 now, unicast_dest, &is_inform, pxe_fd, iface_addr); */
220 lease_update_file(now);
221 lease_update_dns();
222
223 if (sz != 0)
4cb1b320
SK
224 while (sendto(daemon->dhcp6fd, daemon->outpacket.iov_base, sz, 0, (struct sockaddr *)&from, sizeof(from)) &&
225 retry_send());
c72daea8
SK
226}
227
228static int complete_context6(struct in6_addr *local, int prefix,
52b92f4d 229 int scope, int if_index, int dad, void *vparam)
c72daea8
SK
230{
231 struct dhcp_context *context;
232 struct iface_param *param = vparam;
4cb1b320 233
52b92f4d 234 (void)scope; /* warning */
4cb1b320 235 (void)dad;
52b92f4d 236
c72daea8
SK
237 for (context = daemon->dhcp6; context; context = context->next)
238 {
52b92f4d 239 if (prefix == context->prefix &&
4cb1b320
SK
240 !IN6_IS_ADDR_LOOPBACK(local) &&
241 !IN6_IS_ADDR_LINKLOCAL(local) &&
242 !IN6_IS_ADDR_MULTICAST(local) &&
c72daea8
SK
243 is_same_net6(local, &context->start6, prefix) &&
244 is_same_net6(local, &context->end6, prefix))
245 {
246 /* link it onto the current chain if we've not seen it before */
247 if (if_index == param->ind && context->current == context)
248 {
249 context->current = param->current;
250 param->current = context;
4cb1b320 251 context->local6 = *local;
c72daea8
SK
252 }
253 }
254 }
255 return 1;
256}
52b92f4d
SK
257
258struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct in6_addr *net, int prefix, u64 addr)
259{
260 struct dhcp_config *config;
261
262 for (config = configs; config; config = config->next)
263 if ((config->flags & CONFIG_ADDR6) &&
264 is_same_net6(&config->addr6, net, prefix) &&
265 (prefix == 128 || addr6part(&config->addr6) == addr))
266 return config;
267
268 return NULL;
269}
270
271int address6_allocate(struct dhcp_context *context, unsigned char *clid, int clid_len,
4cb1b320 272 int serial, struct dhcp_netid *netids, struct in6_addr *ans)
52b92f4d
SK
273{
274 /* Find a free address: exclude anything in use and anything allocated to
275 a particular hwaddr/clientid/hostname in our configuration.
276 Try to return from contexts which match netids first.
277
278 Note that we assume the address prefix lengths are 64 or greater, so we can
279 get by with 64 bit arithmetic.
280*/
281
282 u64 start, addr;
283 struct dhcp_context *c, *d;
284 int i, pass;
285 u64 j;
286
287 /* hash hwaddr: use the SDBM hashing algorithm. This works
288 for MAC addresses, let's see how it manages with client-ids! */
289 for (j = 0, i = 0; i < clid_len; i++)
290 j += clid[i] + (j << 6) + (j << 16) - j;
291
292 for (pass = 0; pass <= 1; pass++)
293 for (c = context; c; c = c->current)
294 if (c->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
295 continue;
296 else if (!match_netid(c->filter, netids, pass))
297 continue;
298 else
299 {
4cb1b320 300 start = addr6part(&c->start6) + ((j + c->addr_epoch + serial) % (1 + addr6part(&c->end6) - addr6part(&c->start6)));
52b92f4d
SK
301
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)
308 if (addr == addr6part(&d->router6))
309 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
343 if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY)) &&
344 is_same_net6(&context->start6, taddr, context->prefix) &&
345 is_same_net6(&context->end6, taddr, context->prefix) &&
346 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
4cb1b320
SK
392static int is_addr_in_context6(struct dhcp_context *context, struct dhcp_config *config)
393{
394 if (!context) /* called via find_config() from lease_update_from_configs() */
395 return 1;
396 if (!(config->flags & CONFIG_ADDR6))
397 return 1;
398 for (; context; context = context->current)
399 if (is_same_net6(&config->addr6, &context->start6, context->prefix))
400 return 1;
401
402 return 0;
403}
404
405
406struct dhcp_config *find_config6(struct dhcp_config *configs,
407 struct dhcp_context *context,
408 unsigned char *duid, int duid_len,
409 char *hostname)
410{
411 int count, new;
412 struct dhcp_config *config;
413 struct hwaddr_config *conf_addr;
414 unsigned char *hwaddr = NULL;
415 int duid_type, hw_len = 0, hw_type = 0;
416
417 if (duid)
418 {
419 for (config = configs; config; config = config->next)
420 if (config->flags & CONFIG_CLID)
421 {
422 if (config->clid_len == duid_len &&
423 memcmp(config->clid, duid, duid_len) == 0 &&
424 is_addr_in_context6(context, config))
425 return config;
426 }
427
428 /* DHCPv6 doesn't deal in MAC addresses per-se, but some DUIDs do include
429 MAC addresses, so we try and parse them out here. Not that there is only one
430 DUID per host and it's created using any one of the MACs, so this is no
431 good no good for multihomed hosts. */
432 hwaddr = duid;
433 GETSHORT(duid_type, hwaddr);
434 if (duid_type == 1 || duid_type == 3)
435 {
436 GETSHORT(hw_type, hwaddr);
437 if (duid_type == 1)
438 hwaddr += 4; /* skip time */
439 hw_len = duid_len - 8;
440 }
441
442 if (hwaddr)
443 for (config = configs; config; config = config->next)
444 if (config_has_mac(config, hwaddr, hw_len, hw_type) &&
445 is_addr_in_context6(context, config))
446 return config;
447 }
448
449 if (hostname && context)
450 for (config = configs; config; config = config->next)
451 if ((config->flags & CONFIG_NAME) &&
452 hostname_isequal(config->hostname, hostname) &&
453 is_addr_in_context6(context, config))
454 return config;
455
456 /* use match with fewest wildcard octets */
457 if (hwaddr)
458 {
459 struct dhcp_config *candidate;
460
461 for (candidate = NULL, count = 0, config = configs; config; config = config->next)
462 if (is_addr_in_context6(context, config))
463 for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next)
464 if (conf_addr->wildcard_mask != 0 &&
465 conf_addr->hwaddr_len == hw_len &&
466 (conf_addr->hwaddr_type == hw_type || conf_addr->hwaddr_type == 0) &&
467 (new = memcmp_masked(conf_addr->hwaddr, hwaddr, hw_len, conf_addr->wildcard_mask)) > count)
468 {
469 count = new;
470 candidate = config;
471 }
472
473 return candidate;
474 }
475
476 return NULL;
477}
478
479void make_duid(time_t now)
480{
481 /* rebase epoch to 1/1/2000 */
482 time_t newnow = now - 946684800;
483 iface_enumerate(AF_LOCAL, &newnow, make_duid1);
484
485 if (!daemon->duid)
486 die("Cannot create DHCPv6 server DUID", NULL, EC_MISC);
487}
488
489static int make_duid1(unsigned int type, unsigned int flags, char *mac,
490 size_t maclen, void *parm)
491{
492 /* create DUID as specified in RFC3315. We use the MAC of the
493 first interface we find that isn't loopback or P-to-P */
494
495 unsigned char *p;
496
497 if (flags & (IFF_LOOPBACK | IFF_POINTOPOINT))
498 return 1;
499
500 daemon->duid = p = safe_malloc(maclen + 8);
501 daemon->duid_len = maclen + 8;
502
503#ifdef HAVE_BROKEN_RTC
504 PUTSHORT(3, p); /* DUID_LL */
505#else
506 PUTSHORT(1, p); /* DUID_LLT */
507#endif
508
509 PUTSHORT(type, p); /* address type */
510
511#ifndef HAVE_BROKEN_RTC
512 PUTLONG(*((time_t *)parm), p); /* time */
513#endif
514
515 memcpy(p, mac, maclen);
516
517 return 0;
518}
c72daea8
SK
519#endif
520
521