Changes to update Tomato RAF.
[tomato.git] / release / src / router / dnsmasq / src / dhcp6.c
blob6cd30b5e006b9343dd0600e9631f845543eed0b9
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 in6_addr fallback;
24 int ind, addr_match;
27 static int complete_context6(struct in6_addr *local, int prefix,
28 int scope, int if_index, int flags,
29 unsigned int preferred, unsigned int valid, void *vparam);
31 static int make_duid1(int index, unsigned int type, char *mac, size_t maclen, void *parm);
33 void dhcp6_init(void)
35 int fd;
36 struct sockaddr_in6 saddr;
37 #if defined(IPV6_TCLASS) && defined(IPTOS_CLASS_CS6)
38 int class = IPTOS_CLASS_CS6;
39 #endif
40 int oneopt = 1;
42 if ((fd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP)) == -1 ||
43 #if defined(IPV6_TCLASS) && defined(IPTOS_CLASS_CS6)
44 setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &class, sizeof(class)) == -1 ||
45 #endif
46 setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &oneopt, sizeof(oneopt)) == -1 ||
47 !fix_fd(fd) ||
48 !set_ipv6pktinfo(fd))
49 die (_("cannot create DHCPv6 socket: %s"), NULL, EC_BADNET);
51 /* 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|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. */
56 if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND))
58 int rc = -1, porterr = 0;
60 #ifdef SO_REUSEPORT
61 if ((rc = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &oneopt, sizeof(oneopt))) == -1 &&
62 errno != ENOPROTOOPT)
63 porterr = 1;
64 #endif
66 if (rc == -1 && !porterr)
67 rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt));
69 if (rc == -1)
70 die(_("failed to set SO_REUSE{ADDR|PORT} on DHCPv6 socket: %s"), NULL, EC_BADNET);
73 memset(&saddr, 0, sizeof(saddr));
74 #ifdef HAVE_SOCKADDR_SA_LEN
75 saddr.sin6_len = sizeof(struct sockaddr_in6);
76 #endif
77 saddr.sin6_family = AF_INET6;
78 saddr.sin6_addr = in6addr_any;
79 saddr.sin6_port = htons(DHCPV6_SERVER_PORT);
81 if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in6)))
82 die(_("failed to bind DHCPv6 server socket: %s"), NULL, EC_BADNET);
84 daemon->dhcp6fd = fd;
87 void dhcp6_packet(time_t now)
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;
98 struct sockaddr_in6 from;
99 ssize_t sz;
100 struct ifreq ifr;
101 struct iname *tmp;
102 unsigned short port;
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;
112 if ((sz = recv_dhcp_packet(daemon->dhcp6fd, &msg)) == -1)
113 return;
115 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
116 if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
118 union {
119 unsigned char *c;
120 struct in6_pktinfo *p;
121 } p;
122 p.c = CMSG_DATA(cmptr);
124 if_index = p.p->ipi6_ifindex;
127 if (!indextoname(daemon->dhcp6fd, if_index, ifr.ifr_name))
128 return;
130 for (tmp = daemon->if_except; tmp; tmp = tmp->next)
131 if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
132 return;
134 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
135 if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
136 return;
138 parm.current = NULL;
139 parm.ind = if_index;
140 parm.addr_match = 0;
141 memset(&parm.fallback, 0, IN6ADDRSZ);
143 for (context = daemon->dhcp6; context; context = context->next)
144 if (IN6_IS_ADDR_UNSPECIFIED(&context->start6) && context->prefix == 0)
146 /* wildcard context for DHCP-stateless only */
147 parm.current = context;
148 context->current = NULL;
150 else
152 /* unlinked contexts are marked by context->current == context */
153 context->current = context;
154 memset(&context->local6, 0, IN6ADDRSZ);
157 if (!iface_enumerate(AF_INET6, &parm, complete_context6))
158 return;
160 if (daemon->if_names || daemon->if_addrs)
163 for (tmp = daemon->if_names; tmp; tmp = tmp->next)
164 if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
165 break;
167 if (!tmp && !parm.addr_match)
168 return;
171 lease_prune(NULL, now); /* lose any expired leases */
173 port = dhcp6_reply(parm.current, if_index, ifr.ifr_name, &parm.fallback,
174 sz, IN6_IS_ADDR_MULTICAST(&from.sin6_addr), now);
176 lease_update_file(now);
177 lease_update_dns(0);
179 /* 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)
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 &&
188 retry_send());
192 static int complete_context6(struct in6_addr *local, int prefix,
193 int scope, int if_index, int flags, unsigned int preferred,
194 unsigned int valid, void *vparam)
196 struct dhcp_context *context;
197 struct iface_param *param = vparam;
198 struct iname *tmp;
200 (void)scope; /* warning */
202 if (if_index == param->ind &&
203 !IN6_IS_ADDR_LOOPBACK(local) &&
204 !IN6_IS_ADDR_LINKLOCAL(local) &&
205 !IN6_IS_ADDR_MULTICAST(local))
207 /* 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;
214 /* 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. */
218 param->fallback = *local;
220 for (context = daemon->dhcp6; context; context = context->next)
222 if ((context->flags & CONTEXT_DHCP) &&
223 !(context->flags & CONTEXT_TEMPLATE) &&
224 prefix == context->prefix &&
225 is_same_net6(local, &context->start6, prefix) &&
226 is_same_net6(local, &context->end6, prefix))
230 /* link it onto the current chain if we've not seen it before */
231 if (context->current == context)
233 struct dhcp_context *tmp, **up;
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;
241 if (context->flags & CONTEXT_DEPRECATE)
242 preferred = 0;
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;
248 else
249 up = &tmp->current;
251 context->current = *up;
252 *up = context;
253 context->local6 = *local;
254 context->preferred = preferred;
255 context->valid = valid;
260 return 1;
263 struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct in6_addr *net, int prefix, u64 addr)
265 struct dhcp_config *config;
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;
273 return NULL;
276 struct dhcp_context *address6_allocate(struct dhcp_context *context, unsigned char *clid, int clid_len,
277 int iaid, int serial, struct dhcp_netid *netids, int plain_range, struct in6_addr *ans)
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.
283 Note that we assume the address prefix lengths are 64 or greater, so we can
284 get by with 64 bit arithmetic.
287 u64 start, addr;
288 struct dhcp_context *c, *d;
289 int i, pass;
290 u64 j;
292 /* hash hwaddr: use the SDBM hashing algorithm. This works
293 for MAC addresses, let's see how it manages with client-ids! */
294 for (j = iaid, i = 0; i < clid_len; i++)
295 j += clid[i] + (j << 6) + (j << 16) - j;
297 for (pass = 0; pass <= plain_range ? 1 : 0; pass++)
298 for (c = context; c; c = c->current)
299 if (c->flags & (CONTEXT_DEPRECATE | CONTEXT_STATIC | CONTEXT_RA_STATELESS | CONTEXT_USED))
300 continue;
301 else if (!match_netid(c->filter, netids, pass))
302 continue;
303 else
305 if (option_bool(OPT_CONSEC_ADDR))
306 /* seed is largest extant lease addr in this context */
307 start = lease_find_max_addr6(c) + serial;
308 else
309 start = addr6part(&c->start6) + ((j + c->addr_epoch) % (1 + addr6part(&c->end6) - addr6part(&c->start6)));
311 /* iterate until we find a free address. */
312 addr = start;
314 do {
315 /* eliminate addresses in use by the server. */
316 for (d = context; d; d = d->current)
317 if (addr == addr6part(&d->local6))
318 break;
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))
324 *ans = c->start6;
325 setaddr6part (ans, addr);
326 return c;
329 addr++;
331 if (addr == addr6part(&c->end6) + 1)
332 addr = addr6part(&c->start6);
334 } while (addr != start);
337 return NULL;
340 /* can dynamically allocate addr */
341 struct dhcp_context *address6_available(struct dhcp_context *context,
342 struct in6_addr *taddr,
343 struct dhcp_netid *netids,
344 int plain_range)
346 u64 start, end, addr = addr6part(taddr);
347 struct dhcp_context *tmp;
349 for (tmp = context; tmp; tmp = tmp->current)
351 start = addr6part(&tmp->start6);
352 end = addr6part(&tmp->end6);
354 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) &&
357 addr >= start &&
358 addr <= end &&
359 match_netid(tmp->filter, netids, plain_range))
360 return tmp;
363 return NULL;
366 /* address OK if configured */
367 struct dhcp_context *address6_valid(struct dhcp_context *context,
368 struct in6_addr *taddr,
369 struct dhcp_netid *netids,
370 int plain_range)
372 struct dhcp_context *tmp;
374 for (tmp = context; tmp; tmp = tmp->current)
375 if (is_same_net6(&tmp->start6, taddr, tmp->prefix) &&
376 match_netid(tmp->filter, netids, plain_range))
377 return tmp;
379 return NULL;
382 int config_valid(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr)
384 if (!config || !(config->flags & CONFIG_ADDR6))
385 return 0;
387 if ((config->flags & CONFIG_WILDCARD) && context->prefix == 64)
389 *addr = context->start6;
390 setaddr6part(addr, addr6part(&config->addr6));
391 return 1;
394 if (is_same_net6(&context->start6, &config->addr6, context->prefix))
396 *addr = config->addr6;
397 return 1;
400 return 0;
403 static int is_config_in_context6(struct dhcp_context *context, struct dhcp_config *config)
405 if (!(config->flags & CONFIG_ADDR6) ||
406 (config->flags & CONFIG_WILDCARD))
408 return 1;
410 for (; context; context = context->current)
411 if (is_same_net6(&config->addr6, &context->start6, context->prefix))
412 return 1;
414 return 0;
418 struct dhcp_config *find_config6(struct dhcp_config *configs,
419 struct dhcp_context *context,
420 unsigned char *duid, int duid_len,
421 char *hostname)
423 struct dhcp_config *config;
425 if (duid)
426 for (config = configs; config; config = config->next)
427 if (config->flags & CONFIG_CLID)
429 if (config->clid_len == duid_len &&
430 memcmp(config->clid, duid, duid_len) == 0 &&
431 is_config_in_context6(context, config))
432 return config;
435 if (hostname && context)
436 for (config = configs; config; config = config->next)
437 if ((config->flags & CONFIG_NAME) &&
438 hostname_isequal(config->hostname, hostname) &&
439 is_config_in_context6(context, config))
440 return config;
442 return NULL;
445 void make_duid(time_t now)
447 if (daemon->duid_config)
449 unsigned char *p;
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);
457 else
459 /* rebase epoch to 1/1/2000 */
460 time_t newnow = now - 946684800;
462 iface_enumerate(AF_LOCAL, &newnow, make_duid1);
464 if(!daemon->duid)
465 die("Cannot create DHCPv6 server DUID: %s", NULL, EC_MISC);
469 static int make_duid1(int index, unsigned int type, char *mac, size_t maclen, void *parm)
471 /* create DUID as specified in RFC3315. We use the MAC of the
472 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. */
476 unsigned char *p;
477 (void)index;
479 if (type >= 256)
480 return 1;
482 #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
488 daemon->duid = p = safe_malloc(maclen + 8);
489 daemon->duid_len = maclen + 8;
490 PUTSHORT(1, p); /* DUID_LLT */
491 PUTSHORT(type, p); /* address type */
492 PUTLONG(*((time_t *)parm), p); /* time */
493 #endif
495 memcpy(p, mac, maclen);
497 return 0;
500 struct cparam {
501 time_t now;
502 int newone, newname;
505 static int construct_worker(struct in6_addr *local, int prefix,
506 int scope, int if_index, int flags,
507 int preferred, int valid, void *vparam)
509 char ifrn_name[IFNAMSIZ];
510 struct in6_addr start6, end6;
511 struct dhcp_context *template, *context;
513 (void)scope;
514 (void)flags;
515 (void)valid;
516 (void)preferred;
518 struct cparam *param = vparam;
520 if (IN6_IS_ADDR_LOOPBACK(local) ||
521 IN6_IS_ADDR_LINKLOCAL(local) ||
522 IN6_IS_ADDR_MULTICAST(local))
523 return 1;
525 if (!indextoname(daemon->doing_dhcp6 ? daemon->dhcp6fd : daemon->icmp6fd, if_index, ifrn_name))
526 return 0;
528 for (template = daemon->dhcp6; template; template = template->next)
529 if (!(template->flags & CONTEXT_TEMPLATE))
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))
536 template->if_index = if_index;
537 template->local6 = *local;
541 else if ((addr6part(local) == addr6part(&template->start6) ||
542 addr6part(local) == addr6part(&template->end6)) &&
543 wildcard_match(template->template_interface, ifrn_name))
545 start6 = *local;
546 setaddr6part(&start6, addr6part(&template->start6));
547 end6 = *local;
548 setaddr6part(&end6, addr6part(&template->end6));
550 for (context = daemon->dhcp6; context; context = context->next)
551 if ((context->flags & CONTEXT_CONSTRUCTED) &&
552 IN6_ARE_ADDR_EQUAL(&start6, &context->start6) &&
553 IN6_ARE_ADDR_EQUAL(&end6, &context->end6))
555 context->flags &= ~CONTEXT_GC;
556 break;
559 if (!context && (context = whine_malloc(sizeof (struct dhcp_context))))
561 *context = *template;
562 context->start6 = start6;
563 context->end6 = end6;
564 context->flags &= ~CONTEXT_TEMPLATE;
565 context->flags |= CONTEXT_CONSTRUCTED;
566 context->if_index = if_index;
567 context->local6 = *local;
569 context->next = daemon->dhcp6;
570 daemon->dhcp6 = context;
572 ra_start_unsolicted(param->now, context);
573 /* we created a new one, need to call
574 lease_update_file to get periodic functions called */
575 param->newone = 1;
577 /* Will need to add new putative SLAAC addresses to existing leases */
578 if (context->flags & CONTEXT_RA_NAME)
579 param->newname = 1;
581 log_context(AF_INET6, context);
585 return 1;
588 void dhcp_construct_contexts(time_t now)
590 struct dhcp_context *tmp, *context, **up;
591 struct cparam param;
592 param.newone = 0;
593 param.newname = 0;
594 param.now = now;
596 for (context = daemon->dhcp6; context; context = context->next)
598 context->if_index = 0;
599 if (context->flags & CONTEXT_CONSTRUCTED)
600 context->flags |= CONTEXT_GC;
603 iface_enumerate(AF_INET6, &param, construct_worker);
605 for (up = &daemon->dhcp6, context = daemon->dhcp6; context; context = tmp)
607 tmp = context->next;
609 if (context->flags & CONTEXT_GC)
611 *up = context->next;
612 param.newone = 1; /* include deletion */
613 if (context->flags & CONTEXT_RA_NAME)
614 param.newname = 1;
615 free(context);
617 else
618 up = &context->next;
621 if (param.newone)
623 if (daemon->dhcp || daemon->doing_dhcp6)
625 if (param.newname)
626 lease_update_slaac(now);
627 lease_update_file(now);
629 else
630 /* Not doing DHCP, so no lease system, manage alarms for ra only */
631 send_alarm(periodic_ra(now), now);
635 #endif