dnsmasq: Update to v2.67test14.
[tomato.git] / release / src / router / dnsmasq / src / dhcp6.c
blob35bb74829b552372f372170660342fdb39752c8d
1 /* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
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.
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.
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/>.
17 #include "dnsmasq.h"
19 #ifdef HAVE_DHCP6
21 struct iface_param {
22 struct dhcp_context *current;
23 struct dhcp_relay *relay;
24 struct in6_addr fallback, relay_local;
25 int ind, addr_match;
28 static int complete_context6(struct in6_addr *local, int prefix,
29 int scope, int if_index, int flags,
30 unsigned int preferred, unsigned int valid, void *vparam);
32 static int make_duid1(int index, unsigned int type, char *mac, size_t maclen, void *parm);
34 void dhcp6_init(void)
36 int fd;
37 struct sockaddr_in6 saddr;
38 #if defined(IPV6_TCLASS) && defined(IPTOS_CLASS_CS6)
39 int class = IPTOS_CLASS_CS6;
40 #endif
41 int oneopt = 1;
43 if ((fd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP)) == -1 ||
44 #if defined(IPV6_TCLASS) && defined(IPTOS_CLASS_CS6)
45 setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &class, sizeof(class)) == -1 ||
46 #endif
47 setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &oneopt, sizeof(oneopt)) == -1 ||
48 !fix_fd(fd) ||
49 !set_ipv6pktinfo(fd))
50 die (_("cannot create DHCPv6 socket: %s"), NULL, EC_BADNET);
52 /* When bind-interfaces is set, there might be more than one dnmsasq
53 instance binding port 547. That's OK if they serve different networks.
54 Need to set REUSEADDR|REUSEPORT to make this posible.
55 Handle the case that REUSEPORT is defined, but the kernel doesn't
56 support it. This handles the introduction of REUSEPORT on Linux. */
57 if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND))
59 int rc = 0;
61 #ifdef SO_REUSEPORT
62 if ((rc = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &oneopt, sizeof(oneopt))) == -1 &&
63 errno == ENOPROTOOPT)
64 rc = 0;
65 #endif
67 if (rc != -1)
68 rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt));
70 if (rc == -1)
71 die(_("failed to set SO_REUSE{ADDR|PORT} on DHCPv6 socket: %s"), NULL, EC_BADNET);
74 memset(&saddr, 0, sizeof(saddr));
75 #ifdef HAVE_SOCKADDR_SA_LEN
76 saddr.sin6_len = sizeof(struct sockaddr_in6);
77 #endif
78 saddr.sin6_family = AF_INET6;
79 saddr.sin6_addr = in6addr_any;
80 saddr.sin6_port = htons(DHCPV6_SERVER_PORT);
82 if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in6)))
83 die(_("failed to bind DHCPv6 server socket: %s"), NULL, EC_BADNET);
85 daemon->dhcp6fd = fd;
88 void dhcp6_packet(time_t now)
90 struct dhcp_context *context;
91 struct dhcp_relay *relay;
92 struct iface_param parm;
93 struct cmsghdr *cmptr;
94 struct msghdr msg;
95 int if_index = 0;
96 union {
97 struct cmsghdr align; /* this ensures alignment */
98 char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
99 } control_u;
100 struct sockaddr_in6 from;
101 ssize_t sz;
102 struct ifreq ifr;
103 struct iname *tmp;
104 unsigned short port;
106 msg.msg_control = control_u.control6;
107 msg.msg_controllen = sizeof(control_u);
108 msg.msg_flags = 0;
109 msg.msg_name = &from;
110 msg.msg_namelen = sizeof(from);
111 msg.msg_iov = &daemon->dhcp_packet;
112 msg.msg_iovlen = 1;
114 if ((sz = recv_dhcp_packet(daemon->dhcp6fd, &msg)) == -1)
115 return;
117 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
118 if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
120 union {
121 unsigned char *c;
122 struct in6_pktinfo *p;
123 } p;
124 p.c = CMSG_DATA(cmptr);
126 if_index = p.p->ipi6_ifindex;
129 if (!indextoname(daemon->dhcp6fd, if_index, ifr.ifr_name))
130 return;
132 if ((port = relay_reply6(&from, sz, ifr.ifr_name)) == 0)
135 for (tmp = daemon->if_except; tmp; tmp = tmp->next)
136 if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
137 return;
139 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
140 if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
141 return;
143 parm.current = NULL;
144 parm.relay = NULL;
145 memset(&parm.relay_local, 0, IN6ADDRSZ);
146 parm.ind = if_index;
147 parm.addr_match = 0;
148 memset(&parm.fallback, 0, IN6ADDRSZ);
150 for (context = daemon->dhcp6; context; context = context->next)
151 if (IN6_IS_ADDR_UNSPECIFIED(&context->start6) && context->prefix == 0)
153 /* wildcard context for DHCP-stateless only */
154 parm.current = context;
155 context->current = NULL;
157 else
159 /* unlinked contexts are marked by context->current == context */
160 context->current = context;
161 memset(&context->local6, 0, IN6ADDRSZ);
164 for (relay = daemon->relay6; relay; relay = relay->next)
165 relay->current = relay;
167 if (!iface_enumerate(AF_INET6, &parm, complete_context6))
168 return;
170 if (daemon->if_names || daemon->if_addrs)
173 for (tmp = daemon->if_names; tmp; tmp = tmp->next)
174 if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
175 break;
177 if (!tmp && !parm.addr_match)
178 return;
181 if (parm.relay)
183 relay_upstream6(parm.relay, sz, &from.sin6_addr, from.sin6_scope_id);
184 return;
187 /* May have configured relay, but not DHCP server */
188 if (!daemon->doing_dhcp6)
189 return;
191 lease_prune(NULL, now); /* lose any expired leases */
193 port = dhcp6_reply(parm.current, if_index, ifr.ifr_name, &parm.fallback,
194 sz, IN6_IS_ADDR_MULTICAST(&from.sin6_addr), now);
196 lease_update_file(now);
197 lease_update_dns(0);
200 /* The port in the source address of the original request should
201 be correct, but at least once client sends from the server port,
202 so we explicitly send to the client port to a client, and the
203 server port to a relay. */
204 if (port != 0)
206 from.sin6_port = htons(port);
207 while (sendto(daemon->dhcp6fd, daemon->outpacket.iov_base, save_counter(0),
208 0, (struct sockaddr *)&from, sizeof(from)) == -1 &&
209 retry_send());
213 static int complete_context6(struct in6_addr *local, int prefix,
214 int scope, int if_index, int flags, unsigned int preferred,
215 unsigned int valid, void *vparam)
217 struct dhcp_context *context;
218 struct dhcp_relay *relay;
219 struct iface_param *param = vparam;
220 struct iname *tmp;
222 (void)scope; /* warning */
224 if (if_index == param->ind)
226 if (!IN6_IS_ADDR_LOOPBACK(local) &&
227 !IN6_IS_ADDR_LINKLOCAL(local) &&
228 !IN6_IS_ADDR_MULTICAST(local))
230 /* if we have --listen-address config, see if the
231 arrival interface has a matching address. */
232 for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
233 if (tmp->addr.sa.sa_family == AF_INET6 &&
234 IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, local))
235 param->addr_match = 1;
237 /* Determine a globally address on the arrival interface, even
238 if we have no matching dhcp-context, because we're only
239 allocating on remote subnets via relays. This
240 is used as a default for the DNS server option. */
241 param->fallback = *local;
243 for (context = daemon->dhcp6; context; context = context->next)
245 if ((context->flags & CONTEXT_DHCP) &&
246 !(context->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) &&
247 prefix == context->prefix &&
248 is_same_net6(local, &context->start6, prefix) &&
249 is_same_net6(local, &context->end6, prefix))
253 /* link it onto the current chain if we've not seen it before */
254 if (context->current == context)
256 struct dhcp_context *tmp, **up;
258 /* use interface values only for contructed contexts */
259 if (!(context->flags & CONTEXT_CONSTRUCTED))
260 preferred = valid = 0xffffffff;
261 else if (flags & IFACE_DEPRECATED)
262 preferred = 0;
264 if (context->flags & CONTEXT_DEPRECATE)
265 preferred = 0;
267 /* order chain, longest preferred time first */
268 for (up = &param->current, tmp = param->current; tmp; tmp = tmp->current)
269 if (tmp->preferred <= preferred)
270 break;
271 else
272 up = &tmp->current;
274 context->current = *up;
275 *up = context;
276 context->local6 = *local;
277 context->preferred = preferred;
278 context->valid = valid;
284 for (relay = daemon->relay6; relay; relay = relay->next)
285 if (IN6_ARE_ADDR_EQUAL(local, &relay->local.addr.addr6) && relay->current == relay &&
286 (IN6_IS_ADDR_UNSPECIFIED(&param->relay_local) || IN6_ARE_ADDR_EQUAL(local, &param->relay_local)))
288 relay->current = param->relay;
289 param->relay = relay;
290 param->relay_local = *local;
295 return 1;
298 struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct in6_addr *net, int prefix, u64 addr)
300 struct dhcp_config *config;
302 for (config = configs; config; config = config->next)
303 if ((config->flags & CONFIG_ADDR6) &&
304 is_same_net6(&config->addr6, net, prefix) &&
305 (prefix == 128 || addr6part(&config->addr6) == addr))
306 return config;
308 return NULL;
311 struct dhcp_context *address6_allocate(struct dhcp_context *context, unsigned char *clid, int clid_len,
312 int iaid, int serial, struct dhcp_netid *netids, int plain_range, struct in6_addr *ans)
314 /* Find a free address: exclude anything in use and anything allocated to
315 a particular hwaddr/clientid/hostname in our configuration.
316 Try to return from contexts which match netids first.
318 Note that we assume the address prefix lengths are 64 or greater, so we can
319 get by with 64 bit arithmetic.
322 u64 start, addr;
323 struct dhcp_context *c, *d;
324 int i, pass;
325 u64 j;
327 /* hash hwaddr: use the SDBM hashing algorithm. This works
328 for MAC addresses, let's see how it manages with client-ids! */
329 for (j = iaid, i = 0; i < clid_len; i++)
330 j += clid[i] + (j << 6) + (j << 16) - j;
332 for (pass = 0; pass <= plain_range ? 1 : 0; pass++)
333 for (c = context; c; c = c->current)
334 if (c->flags & (CONTEXT_DEPRECATE | CONTEXT_STATIC | CONTEXT_RA_STATELESS | CONTEXT_USED))
335 continue;
336 else if (!match_netid(c->filter, netids, pass))
337 continue;
338 else
340 if (option_bool(OPT_CONSEC_ADDR))
341 /* seed is largest extant lease addr in this context */
342 start = lease_find_max_addr6(c) + serial;
343 else
344 start = addr6part(&c->start6) + ((j + c->addr_epoch) % (1 + addr6part(&c->end6) - addr6part(&c->start6)));
346 /* iterate until we find a free address. */
347 addr = start;
349 do {
350 /* eliminate addresses in use by the server. */
351 for (d = context; d; d = d->current)
352 if (addr == addr6part(&d->local6))
353 break;
355 if (!d &&
356 !lease6_find_by_addr(&c->start6, c->prefix, addr) &&
357 !config_find_by_address6(daemon->dhcp_conf, &c->start6, c->prefix, addr))
359 *ans = c->start6;
360 setaddr6part (ans, addr);
361 return c;
364 addr++;
366 if (addr == addr6part(&c->end6) + 1)
367 addr = addr6part(&c->start6);
369 } while (addr != start);
372 return NULL;
375 /* can dynamically allocate addr */
376 struct dhcp_context *address6_available(struct dhcp_context *context,
377 struct in6_addr *taddr,
378 struct dhcp_netid *netids,
379 int plain_range)
381 u64 start, end, addr = addr6part(taddr);
382 struct dhcp_context *tmp;
384 for (tmp = context; tmp; tmp = tmp->current)
386 start = addr6part(&tmp->start6);
387 end = addr6part(&tmp->end6);
389 if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_RA_STATELESS)) &&
390 is_same_net6(&tmp->start6, taddr, tmp->prefix) &&
391 is_same_net6(&tmp->end6, taddr, tmp->prefix) &&
392 addr >= start &&
393 addr <= end &&
394 match_netid(tmp->filter, netids, plain_range))
395 return tmp;
398 return NULL;
401 /* address OK if configured */
402 struct dhcp_context *address6_valid(struct dhcp_context *context,
403 struct in6_addr *taddr,
404 struct dhcp_netid *netids,
405 int plain_range)
407 struct dhcp_context *tmp;
409 for (tmp = context; tmp; tmp = tmp->current)
410 if (is_same_net6(&tmp->start6, taddr, tmp->prefix) &&
411 match_netid(tmp->filter, netids, plain_range))
412 return tmp;
414 return NULL;
417 int config_valid(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr)
419 if (!config || !(config->flags & CONFIG_ADDR6))
420 return 0;
422 if ((config->flags & CONFIG_WILDCARD) && context->prefix == 64)
424 *addr = context->start6;
425 setaddr6part(addr, addr6part(&config->addr6));
426 return 1;
429 if (is_same_net6(&context->start6, &config->addr6, context->prefix))
431 *addr = config->addr6;
432 return 1;
435 return 0;
438 static int is_config_in_context6(struct dhcp_context *context, struct dhcp_config *config)
440 if (!(config->flags & CONFIG_ADDR6) ||
441 (config->flags & CONFIG_WILDCARD))
443 return 1;
445 for (; context; context = context->current)
446 if (is_same_net6(&config->addr6, &context->start6, context->prefix))
447 return 1;
449 return 0;
453 struct dhcp_config *find_config6(struct dhcp_config *configs,
454 struct dhcp_context *context,
455 unsigned char *duid, int duid_len,
456 char *hostname)
458 struct dhcp_config *config;
460 if (duid)
461 for (config = configs; config; config = config->next)
462 if (config->flags & CONFIG_CLID)
464 if (config->clid_len == duid_len &&
465 memcmp(config->clid, duid, duid_len) == 0 &&
466 is_config_in_context6(context, config))
467 return config;
470 if (hostname && context)
471 for (config = configs; config; config = config->next)
472 if ((config->flags & CONFIG_NAME) &&
473 hostname_isequal(config->hostname, hostname) &&
474 is_config_in_context6(context, config))
475 return config;
477 return NULL;
480 void make_duid(time_t now)
482 if (daemon->duid_config)
484 unsigned char *p;
486 daemon->duid = p = safe_malloc(daemon->duid_config_len + 6);
487 daemon->duid_len = daemon->duid_config_len + 6;
488 PUTSHORT(2, p); /* DUID_EN */
489 PUTLONG(daemon->duid_enterprise, p);
490 memcpy(p, daemon->duid_config, daemon->duid_config_len);
492 else
494 /* rebase epoch to 1/1/2000 */
495 time_t newnow = now - 946684800;
497 iface_enumerate(AF_LOCAL, &newnow, make_duid1);
499 if(!daemon->duid)
500 die("Cannot create DHCPv6 server DUID: %s", NULL, EC_MISC);
504 static int make_duid1(int index, unsigned int type, char *mac, size_t maclen, void *parm)
506 /* create DUID as specified in RFC3315. We use the MAC of the
507 first interface we find that isn't loopback or P-to-P and
508 has address-type < 256. Address types above 256 are things like
509 tunnels which don't have usable MAC addresses. */
511 unsigned char *p;
512 (void)index;
514 if (type >= 256)
515 return 1;
517 #ifdef HAVE_BROKEN_RTC
518 daemon->duid = p = safe_malloc(maclen + 4);
519 daemon->duid_len = maclen + 4;
520 PUTSHORT(3, p); /* DUID_LL */
521 PUTSHORT(type, p); /* address type */
522 #else
523 daemon->duid = p = safe_malloc(maclen + 8);
524 daemon->duid_len = maclen + 8;
525 PUTSHORT(1, p); /* DUID_LLT */
526 PUTSHORT(type, p); /* address type */
527 PUTLONG(*((time_t *)parm), p); /* time */
528 #endif
530 memcpy(p, mac, maclen);
532 return 0;
535 struct cparam {
536 time_t now;
537 int newone, newname;
540 static int construct_worker(struct in6_addr *local, int prefix,
541 int scope, int if_index, int flags,
542 int preferred, int valid, void *vparam)
544 char ifrn_name[IFNAMSIZ];
545 struct in6_addr start6, end6;
546 struct dhcp_context *template, *context;
548 (void)scope;
549 (void)flags;
550 (void)valid;
551 (void)preferred;
553 struct cparam *param = vparam;
555 if (IN6_IS_ADDR_LOOPBACK(local) ||
556 IN6_IS_ADDR_LINKLOCAL(local) ||
557 IN6_IS_ADDR_MULTICAST(local))
558 return 1;
560 if (!indextoname(daemon->doing_dhcp6 ? daemon->dhcp6fd : daemon->icmp6fd, if_index, ifrn_name))
561 return 0;
563 for (template = daemon->dhcp6; template; template = template->next)
564 if (!(template->flags & CONTEXT_TEMPLATE))
566 /* non-template entries, just fill in interface and local addresses */
567 if (prefix == template->prefix &&
568 is_same_net6(local, &template->start6, prefix) &&
569 is_same_net6(local, &template->end6, prefix))
571 template->if_index = if_index;
572 template->local6 = *local;
576 else if ((addr6part(local) == addr6part(&template->start6) ||
577 addr6part(local) == addr6part(&template->end6) ||
578 (IN6_IS_ADDR_UNSPECIFIED(&template->start6) &&
579 IFACE_PERMANENT == (flags & (IFACE_PERMANENT | IFACE_DEPRECATED)))) &&
580 wildcard_match(template->template_interface, ifrn_name))
582 start6 = *local;
583 setaddr6part(&start6, addr6part(&template->start6));
584 end6 = *local;
585 setaddr6part(&end6, addr6part(&template->end6));
587 for (context = daemon->dhcp6; context; context = context->next)
588 if ((context->flags & CONTEXT_CONSTRUCTED) &&
589 IN6_ARE_ADDR_EQUAL(&start6, &context->start6) &&
590 IN6_ARE_ADDR_EQUAL(&end6, &context->end6))
592 int flags = context->flags;
593 context->flags &= ~(CONTEXT_GC | CONTEXT_OLD);
594 if (flags & CONTEXT_OLD)
596 /* address went, now it's back */
597 log_context(AF_INET6, context);
598 /* fast RAs for a while */
599 ra_start_unsolicted(param->now, context);
600 /* Add address to name again */
601 if (context->flags & CONTEXT_RA_NAME)
602 param->newname = 1;
604 break;
607 if (!context && (context = whine_malloc(sizeof (struct dhcp_context))))
609 *context = *template;
610 context->start6 = start6;
611 context->end6 = end6;
612 context->flags &= ~CONTEXT_TEMPLATE;
613 context->flags |= CONTEXT_CONSTRUCTED;
614 context->if_index = if_index;
615 context->local6 = *local;
616 context->saved_valid = 0;
618 context->next = daemon->dhcp6;
619 daemon->dhcp6 = context;
621 ra_start_unsolicted(param->now, context);
622 /* we created a new one, need to call
623 lease_update_file to get periodic functions called */
624 param->newone = 1;
626 /* Will need to add new putative SLAAC addresses to existing leases */
627 if (context->flags & CONTEXT_RA_NAME)
628 param->newname = 1;
630 log_context(AF_INET6, context);
634 return 1;
637 void dhcp_construct_contexts(time_t now)
639 struct dhcp_context *context, *tmp, **up;
640 struct cparam param;
641 param.newone = 0;
642 param.newname = 0;
643 param.now = now;
645 for (context = daemon->dhcp6; context; context = context->next)
646 if (context->flags & CONTEXT_CONSTRUCTED)
647 context->flags |= CONTEXT_GC;
649 iface_enumerate(AF_INET6, &param, construct_worker);
651 for (up = &daemon->dhcp6, context = daemon->dhcp6; context; context = tmp)
654 tmp = context->next;
656 if (context->flags & CONTEXT_GC && !(context->flags & CONTEXT_OLD))
659 if ((context->flags & (CONTEXT_RA_ONLY | CONTEXT_RA_NAME | CONTEXT_RA_STATELESS)) ||
660 option_bool(OPT_RA))
662 /* previously constructed context has gone. advertise it's demise */
663 context->flags |= CONTEXT_OLD;
664 context->address_lost_time = now;
665 /* Apply same ceiling of configured lease time as in radv.c */
666 if (context->saved_valid > context->lease_time)
667 context->saved_valid = context->lease_time;
668 /* maximum time is 2 hours, from RFC */
669 if (context->saved_valid > 7200) /* 2 hours */
670 context->saved_valid = 7200;
671 ra_start_unsolicted(now, context);
672 param.newone = 1; /* include deletion */
674 if (context->flags & CONTEXT_RA_NAME)
675 param.newname = 1;
677 log_context(AF_INET6, context);
679 up = &context->next;
681 else
683 /* we were never doing RA for this, so free now */
684 *up = context->next;
685 free(context);
688 else
689 up = &context->next;
692 if (param.newone)
694 if (daemon->dhcp || daemon->doing_dhcp6)
696 if (param.newname)
697 lease_update_slaac(now);
698 lease_update_file(now);
700 else
701 /* Not doing DHCP, so no lease system, manage alarms for ra only */
702 send_alarm(periodic_ra(now), now);
706 #endif