blob: e31456c749ce66aeae6917e77f8503c93dd5fea7 [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 Kelleyc72daea2012-01-05 21:33:27 +000024 int ind;
25};
26
Simon Kelleyc72daea2012-01-05 21:33:27 +000027static int complete_context6(struct in6_addr *local, int prefix,
Simon Kelley52b92f42012-01-22 16:05:15 +000028 int scope, int if_index, int dad, void *vparam);
Simon Kelleyc72daea2012-01-05 21:33:27 +000029
Simon Kelleyc5ad4e72012-02-24 16:06:20 +000030static int make_duid1(int index, unsigned int type, char *mac, size_t maclen, void *parm);
Simon Kelley4cb1b322012-02-06 14:30:41 +000031
Simon Kelleyc72daea2012-01-05 21:33:27 +000032void dhcp6_init(void)
33{
34 int fd;
35 struct sockaddr_in6 saddr;
Simon Kelley0e88d532012-03-28 22:22:05 +010036#if defined(IPV6_TCLASS) && defined(IPTOS_CLASS_CS6)
Simon Kelleyc72daea2012-01-05 21:33:27 +000037 int class = IPTOS_CLASS_CS6;
Simon Kelley4cb1b322012-02-06 14:30:41 +000038#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +000039
40 if ((fd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP)) == -1 ||
Simon Kelley0e88d532012-03-28 22:22:05 +010041#if defined(IPV6_TCLASS) && defined(IPTOS_CLASS_CS6)
Simon Kelleyc72daea2012-01-05 21:33:27 +000042 setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &class, sizeof(class)) == -1 ||
Simon Kelley4cb1b322012-02-06 14:30:41 +000043#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +000044 !fix_fd(fd) ||
45 !set_ipv6pktinfo(fd))
46 die (_("cannot create DHCPv6 socket: %s"), NULL, EC_BADNET);
47
48 memset(&saddr, 0, sizeof(saddr));
49#ifdef HAVE_SOCKADDR_SA_LEN
Simon Kelley4cb1b322012-02-06 14:30:41 +000050 saddr.sin6_len = sizeof(struct sockaddr_in6);
Simon Kelleyc72daea2012-01-05 21:33:27 +000051#endif
52 saddr.sin6_family = AF_INET6;
53 saddr.sin6_addr = in6addr_any;
54 saddr.sin6_port = htons(DHCPV6_SERVER_PORT);
55
56 if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in6)))
57 die(_("failed to bind DHCPv6 server socket: %s"), NULL, EC_BADNET);
58
Simon Kelleyc72daea2012-01-05 21:33:27 +000059 daemon->dhcp6fd = fd;
Simon Kelleyc72daea2012-01-05 21:33:27 +000060}
61
Simon Kelleyc72daea2012-01-05 21:33:27 +000062void dhcp6_packet(time_t now)
63{
64 struct dhcp_context *context;
65 struct iface_param parm;
66 struct cmsghdr *cmptr;
67 struct msghdr msg;
68 int if_index = 0;
69 union {
70 struct cmsghdr align; /* this ensures alignment */
71 char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
72 } control_u;
Simon Kelleyc5ad4e72012-02-24 16:06:20 +000073 struct sockaddr_in6 from;
Simon Kelleyc72daea2012-01-05 21:33:27 +000074 struct all_addr dest;
75 ssize_t sz;
76 struct ifreq ifr;
77 struct iname *tmp;
Simon Kelley1d0f91c2012-03-12 11:56:22 +000078 unsigned short port;
Simon Kelleyc72daea2012-01-05 21:33:27 +000079
80 msg.msg_control = control_u.control6;
81 msg.msg_controllen = sizeof(control_u);
82 msg.msg_flags = 0;
83 msg.msg_name = &from;
84 msg.msg_namelen = sizeof(from);
85 msg.msg_iov = &daemon->dhcp_packet;
86 msg.msg_iovlen = 1;
87
Simon Kelley1d0f91c2012-03-12 11:56:22 +000088 if ((sz = recv_dhcp_packet(daemon->dhcp6fd, &msg)) == -1)
Simon Kelleyc72daea2012-01-05 21:33:27 +000089 return;
90
91 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
92 if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
93 {
94 union {
95 unsigned char *c;
96 struct in6_pktinfo *p;
97 } p;
98 p.c = CMSG_DATA(cmptr);
99
100 if_index = p.p->ipi6_ifindex;
101 dest.addr.addr6 = p.p->ipi6_addr;
102 }
103
104 if (!indextoname(daemon->dhcp6fd, if_index, ifr.ifr_name))
105 return;
Simon Kelley52b92f42012-01-22 16:05:15 +0000106
Simon Kelleyc72daea2012-01-05 21:33:27 +0000107 if (!iface_check(AF_INET6, (struct all_addr *)&dest, ifr.ifr_name))
108 return;
109
110 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
111 if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
112 return;
113
Simon Kelleyc72daea2012-01-05 21:33:27 +0000114 /* unlinked contexts are marked by context->current == context */
Simon Kelley52b92f42012-01-22 16:05:15 +0000115 for (context = daemon->dhcp6; context; context = context->next)
Simon Kelleye44ddca2012-02-18 17:08:50 +0000116 {
117 context->current = context;
118 memset(&context->local6, 0, IN6ADDRSZ);
119 }
120
Simon Kelleyc72daea2012-01-05 21:33:27 +0000121 parm.current = NULL;
122 parm.ind = if_index;
Simon Kelleye44ddca2012-02-18 17:08:50 +0000123 memset(&parm.fallback, 0, IN6ADDRSZ);
Simon Kelleyc72daea2012-01-05 21:33:27 +0000124
125 if (!iface_enumerate(AF_INET6, &parm, complete_context6))
126 return;
127
128 lease_prune(NULL, now); /* lose any expired leases */
129
Simon Kelley1d0f91c2012-03-12 11:56:22 +0000130 port = dhcp6_reply(parm.current, if_index, ifr.ifr_name, &parm.fallback,
131 sz, IN6_IS_ADDR_MULTICAST(&from.sin6_addr), now);
Simon Kelley62779782012-02-10 21:19:25 +0000132
Simon Kelleyc72daea2012-01-05 21:33:27 +0000133 lease_update_file(now);
Simon Kelley353ae4d2012-03-19 20:07:51 +0000134 lease_update_dns(0);
Simon Kelleyc72daea2012-01-05 21:33:27 +0000135
Simon Kelley1d0f91c2012-03-12 11:56:22 +0000136 /* The port in the source address of the original request should
137 be correct, but at least once client sends from the server port,
138 so we explicitly send to the client port to a client, and the
139 server port to a relay. */
140 if (port != 0)
141 {
142 from.sin6_port = htons(port);
143 while (sendto(daemon->dhcp6fd, daemon->outpacket.iov_base, save_counter(0),
144 0, (struct sockaddr *)&from, sizeof(from)) == -1 &&
Simon Kelley4cb1b322012-02-06 14:30:41 +0000145 retry_send());
Simon Kelley1d0f91c2012-03-12 11:56:22 +0000146 }
Simon Kelleyc72daea2012-01-05 21:33:27 +0000147}
148
149static int complete_context6(struct in6_addr *local, int prefix,
Simon Kelley52b92f42012-01-22 16:05:15 +0000150 int scope, int if_index, int dad, void *vparam)
Simon Kelleyc72daea2012-01-05 21:33:27 +0000151{
152 struct dhcp_context *context;
153 struct iface_param *param = vparam;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000154
Simon Kelley52b92f42012-01-22 16:05:15 +0000155 (void)scope; /* warning */
Simon Kelley4cb1b322012-02-06 14:30:41 +0000156 (void)dad;
Simon Kelleye44ddca2012-02-18 17:08:50 +0000157
158 if (if_index == param->ind &&
159 !IN6_IS_ADDR_LOOPBACK(local) &&
160 !IN6_IS_ADDR_LINKLOCAL(local) &&
161 !IN6_IS_ADDR_MULTICAST(local))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000162 {
Simon Kelleye44ddca2012-02-18 17:08:50 +0000163 /* Determine a globally address on the arrival interface, even
164 if we have no matching dhcp-context, because we're only
165 allocating on remote subnets via relays. This
166 is used as a default for the DNS server option. */
Simon Kelley270dc2e2012-02-19 20:53:20 +0000167 param->fallback = *local;
Simon Kelleye44ddca2012-02-18 17:08:50 +0000168
169 for (context = daemon->dhcp6; context; context = context->next)
170 {
171 if (prefix == context->prefix &&
172 is_same_net6(local, &context->start6, prefix) &&
173 is_same_net6(local, &context->end6, prefix))
174 {
175 /* link it onto the current chain if we've not seen it before */
176 if (context->current == context)
177 {
178 context->current = param->current;
179 param->current = context;
180 context->local6 = *local;
181 }
182 }
Simon Kelleyc72daea2012-01-05 21:33:27 +0000183 }
184 }
185 return 1;
186}
Simon Kelley52b92f42012-01-22 16:05:15 +0000187
188struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct in6_addr *net, int prefix, u64 addr)
189{
190 struct dhcp_config *config;
191
192 for (config = configs; config; config = config->next)
193 if ((config->flags & CONFIG_ADDR6) &&
194 is_same_net6(&config->addr6, net, prefix) &&
195 (prefix == 128 || addr6part(&config->addr6) == addr))
196 return config;
197
198 return NULL;
199}
200
201int address6_allocate(struct dhcp_context *context, unsigned char *clid, int clid_len,
Simon Kelley4cb1b322012-02-06 14:30:41 +0000202 int serial, struct dhcp_netid *netids, struct in6_addr *ans)
Simon Kelley52b92f42012-01-22 16:05:15 +0000203{
204 /* Find a free address: exclude anything in use and anything allocated to
205 a particular hwaddr/clientid/hostname in our configuration.
206 Try to return from contexts which match netids first.
207
208 Note that we assume the address prefix lengths are 64 or greater, so we can
209 get by with 64 bit arithmetic.
210*/
211
212 u64 start, addr;
213 struct dhcp_context *c, *d;
214 int i, pass;
215 u64 j;
216
217 /* hash hwaddr: use the SDBM hashing algorithm. This works
218 for MAC addresses, let's see how it manages with client-ids! */
219 for (j = 0, i = 0; i < clid_len; i++)
220 j += clid[i] + (j << 6) + (j << 16) - j;
221
222 for (pass = 0; pass <= 1; pass++)
223 for (c = context; c; c = c->current)
Simon Kelleyc8257542012-03-28 21:15:41 +0100224 if (c->flags & (CONTEXT_DEPRECATE | CONTEXT_STATIC | CONTEXT_RA_STATELESS))
Simon Kelley52b92f42012-01-22 16:05:15 +0000225 continue;
226 else if (!match_netid(c->filter, netids, pass))
227 continue;
228 else
Simon Kelley07933802012-02-14 20:55:25 +0000229 {
230 if (option_bool(OPT_CONSEC_ADDR))
231 /* seed is largest extant lease addr in this context */
232 start = lease_find_max_addr6(c) + serial;
233 else
234 start = addr6part(&c->start6) + ((j + c->addr_epoch + serial) % (1 + addr6part(&c->end6) - addr6part(&c->start6)));
Simon Kelley52b92f42012-01-22 16:05:15 +0000235
236 /* iterate until we find a free address. */
237 addr = start;
238
239 do {
240 /* eliminate addresses in use by the server. */
241 for (d = context; d; d = d->current)
Simon Kelleye44ddca2012-02-18 17:08:50 +0000242 if (addr == addr6part(&d->local6))
Simon Kelley52b92f42012-01-22 16:05:15 +0000243 break;
244
245 if (!d &&
246 !lease6_find_by_addr(&c->start6, c->prefix, addr) &&
247 !config_find_by_address6(daemon->dhcp_conf, &c->start6, c->prefix, addr))
248 {
249 *ans = c->start6;
250 setaddr6part (ans, addr);
251 return 1;
252 }
253
254 addr++;
255
256 if (addr == addr6part(&c->end6) + 1)
257 addr = addr6part(&c->start6);
258
259 } while (addr != start);
260 }
261
262 return 0;
263}
264
265struct dhcp_context *address6_available(struct dhcp_context *context,
266 struct in6_addr *taddr,
267 struct dhcp_netid *netids)
268{
269 u64 start, end, addr = addr6part(taddr);
270 struct dhcp_context *tmp;
271
272 for (tmp = context; tmp; tmp = tmp->current)
273 {
274 start = addr6part(&tmp->start6);
275 end = addr6part(&tmp->end6);
276
Simon Kelleyc8257542012-03-28 21:15:41 +0100277 if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_RA_STATELESS)) &&
278 is_same_net6(&tmp->start6, taddr, tmp->prefix) &&
279 is_same_net6(&tmp->end6, taddr, tmp->prefix) &&
Simon Kelley52b92f42012-01-22 16:05:15 +0000280 addr >= start &&
281 addr <= end &&
282 match_netid(tmp->filter, netids, 1))
283 return tmp;
284 }
285
286 return NULL;
287}
288
289struct dhcp_context *narrow_context6(struct dhcp_context *context,
290 struct in6_addr *taddr,
291 struct dhcp_netid *netids)
292{
293 /* We start of with a set of possible contexts, all on the current physical interface.
294 These are chained on ->current.
295 Here we have an address, and return the actual context correponding to that
296 address. Note that none may fit, if the address came a dhcp-host and is outside
297 any dhcp-range. In that case we return a static range if possible, or failing that,
298 any context on the correct subnet. (If there's more than one, this is a dodgy
299 configuration: maybe there should be a warning.) */
300
301 struct dhcp_context *tmp;
302
303 if (!(tmp = address6_available(context, taddr, netids)))
304 {
305 for (tmp = context; tmp; tmp = tmp->current)
306 if (match_netid(tmp->filter, netids, 1) &&
307 is_same_net6(taddr, &tmp->start6, tmp->prefix) &&
308 (tmp->flags & CONTEXT_STATIC))
309 break;
310
311 if (!tmp)
312 for (tmp = context; tmp; tmp = tmp->current)
313 if (match_netid(tmp->filter, netids, 1) &&
314 is_same_net6(taddr, &tmp->start6, tmp->prefix) &&
315 !(tmp->flags & CONTEXT_PROXY))
316 break;
317 }
318
319 /* Only one context allowed now */
320 if (tmp)
321 tmp->current = NULL;
322
323 return tmp;
324}
325
Simon Kelley4cb1b322012-02-06 14:30:41 +0000326static int is_addr_in_context6(struct dhcp_context *context, struct dhcp_config *config)
327{
328 if (!context) /* called via find_config() from lease_update_from_configs() */
329 return 1;
330 if (!(config->flags & CONFIG_ADDR6))
331 return 1;
332 for (; context; context = context->current)
333 if (is_same_net6(&config->addr6, &context->start6, context->prefix))
334 return 1;
335
336 return 0;
337}
338
339
340struct dhcp_config *find_config6(struct dhcp_config *configs,
341 struct dhcp_context *context,
342 unsigned char *duid, int duid_len,
343 char *hostname)
344{
Simon Kelley4cb1b322012-02-06 14:30:41 +0000345 struct dhcp_config *config;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000346
Simon Kelley60ac5af2012-02-15 10:41:03 +0000347 if (duid)
348 for (config = configs; config; config = config->next)
349 if (config->flags & CONFIG_CLID)
Simon Kelley4cb1b322012-02-06 14:30:41 +0000350 {
Simon Kelley60ac5af2012-02-15 10:41:03 +0000351 if (config->clid_len == duid_len &&
352 memcmp(config->clid, duid, duid_len) == 0 &&
Simon Kelley4cb1b322012-02-06 14:30:41 +0000353 is_addr_in_context6(context, config))
354 return config;
Simon Kelley60ac5af2012-02-15 10:41:03 +0000355 }
356
Simon Kelley4cb1b322012-02-06 14:30:41 +0000357 if (hostname && context)
358 for (config = configs; config; config = config->next)
359 if ((config->flags & CONFIG_NAME) &&
360 hostname_isequal(config->hostname, hostname) &&
361 is_addr_in_context6(context, config))
362 return config;
363
Simon Kelley4cb1b322012-02-06 14:30:41 +0000364 return NULL;
365}
366
367void make_duid(time_t now)
368{
Simon Kelley8b372702012-03-09 17:45:10 +0000369 if (daemon->duid_config)
370 {
371 unsigned char *p;
372
373 daemon->duid = p = safe_malloc(daemon->duid_config_len + 6);
374 daemon->duid_len = daemon->duid_config_len + 6;
375 PUTSHORT(2, p); /* DUID_EN */
376 PUTLONG(daemon->duid_enterprise, p);
377 memcpy(p, daemon->duid_config, daemon->duid_config_len);
378 }
379 else
380 {
381 /* rebase epoch to 1/1/2000 */
382 time_t newnow = now - 946684800;
383
384 iface_enumerate(AF_LOCAL, &newnow, make_duid1);
385
386 if(!daemon->duid)
387 die("Cannot create DHCPv6 server DUID: %s", NULL, EC_MISC);
388 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000389}
390
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000391static int make_duid1(int index, unsigned int type, char *mac, size_t maclen, void *parm)
Simon Kelley4cb1b322012-02-06 14:30:41 +0000392{
393 /* create DUID as specified in RFC3315. We use the MAC of the
Simon Kelley0f089832012-03-01 13:43:39 +0000394 first interface we find that isn't loopback or P-to-P and
395 has address-type < 256. Address types above 256 are things like
396 tunnels which don't have usable MAC addresses. */
Simon Kelley4cb1b322012-02-06 14:30:41 +0000397
398 unsigned char *p;
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000399 (void)index;
400
Simon Kelley0f089832012-03-01 13:43:39 +0000401 if (type >= 256)
402 return 1;
403
Simon Kelleye3e86342012-03-01 10:35:34 +0000404#ifdef HAVE_BROKEN_RTC
405 daemon->duid = p = safe_malloc(maclen + 4);
406 daemon->duid_len = maclen + 4;
407 PUTSHORT(3, p); /* DUID_LL */
408 PUTSHORT(type, p); /* address type */
409#else
Simon Kelley4cb1b322012-02-06 14:30:41 +0000410 daemon->duid = p = safe_malloc(maclen + 8);
411 daemon->duid_len = maclen + 8;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000412 PUTSHORT(1, p); /* DUID_LLT */
Simon Kelley4cb1b322012-02-06 14:30:41 +0000413 PUTSHORT(type, p); /* address type */
Simon Kelley4cb1b322012-02-06 14:30:41 +0000414 PUTLONG(*((time_t *)parm), p); /* time */
415#endif
416
417 memcpy(p, mac, maclen);
418
419 return 0;
420}
Simon Kelleyc72daea2012-01-05 21:33:27 +0000421#endif
422
423