blob: 560fdc1b5db89222c1a5d1daa9774f5636a35c49 [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
18#include "dnsmasq.h"
19
20#ifdef HAVE_DHCP6
21
Simon Kelleyceae00d2012-02-09 21:28:14 +000022static int dhcp6_maybe_relay(struct in6_addr *link_address, struct dhcp_netid **relay_tagsp, struct dhcp_context *context,
Simon Kelleye44ddca2012-02-18 17:08:50 +000023 int interface, char *iface_name, struct in6_addr *fallback, void *inbuff, size_t sz, int is_unicast, time_t now);
Simon Kelley07933802012-02-14 20:55:25 +000024static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dhcp_netid *tags, struct dhcp_context *context,
Simon Kelleye44ddca2012-02-18 17:08:50 +000025 int interface, char *iface_name, struct in6_addr *fallback, void *inbuff, size_t sz, int is_unicast, time_t now);
Simon Kelley6c8f21e2012-03-12 15:06:55 +000026static void log6_opts(int nest, unsigned int xid, void *start_opts, void *end_opts);
Simon Kelley4cb1b322012-02-06 14:30:41 +000027static void log6_packet(char *type, unsigned char *clid, int clid_len, struct in6_addr *addr, int xid, char *iface, char *string);
Simon Kelleyc72daea2012-01-05 21:33:27 +000028
Simon Kelley4cb1b322012-02-06 14:30:41 +000029static void *opt6_find (void *opts, void *end, unsigned int search, unsigned int minsize);
30static void *opt6_next(void *opts, void *end);
31static unsigned int opt6_uint(unsigned char *opt, int offset, int size);
Simon Kelley62779782012-02-10 21:19:25 +000032
Simon Kelley4cb1b322012-02-06 14:30:41 +000033#define opt6_len(opt) ((int)(opt6_uint(opt, -2, 2)))
34#define opt6_type(opt) (opt6_uint(opt, -4, 2))
35#define opt6_ptr(opt, i) ((void *)&(((unsigned char *)(opt))[4+(i)]))
36
37
Simon Kelley1d0f91c2012-03-12 11:56:22 +000038unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name,
39 struct in6_addr *fallback, size_t sz, int is_unicast, time_t now)
Simon Kelleyc72daea2012-01-05 21:33:27 +000040{
Simon Kelley4cb1b322012-02-06 14:30:41 +000041 struct dhcp_netid *relay_tags = NULL;
42 struct dhcp_vendor *vendor;
Simon Kelley1d0f91c2012-03-12 11:56:22 +000043 int msg_type;
44
45 if (sz <= 4)
46 return 0;
47
48 msg_type = *((unsigned char *)daemon->dhcp_packet.iov_base);
49
Simon Kelley4cb1b322012-02-06 14:30:41 +000050 /* Mark these so we only match each at most once, to avoid tangled linked lists */
51 for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
52 vendor->netid.next = &vendor->netid;
Simon Kelleyc72daea2012-01-05 21:33:27 +000053
Simon Kelleyc5ad4e72012-02-24 16:06:20 +000054 save_counter(0);
Simon Kelleyc72daea2012-01-05 21:33:27 +000055
Simon Kelleye44ddca2012-02-18 17:08:50 +000056 if (dhcp6_maybe_relay(NULL, &relay_tags, context, interface, iface_name, fallback, daemon->dhcp_packet.iov_base, sz, is_unicast, now))
Simon Kelley1d0f91c2012-03-12 11:56:22 +000057 return msg_type == DHCP6RELAYFORW ? DHCPV6_SERVER_PORT : DHCPV6_CLIENT_PORT;
Simon Kelleyc72daea2012-01-05 21:33:27 +000058
59 return 0;
60}
61
Simon Kelley4cb1b322012-02-06 14:30:41 +000062/* This cost me blood to write, it will probably cost you blood to understand - srk. */
Simon Kelleyceae00d2012-02-09 21:28:14 +000063static int dhcp6_maybe_relay(struct in6_addr *link_address, struct dhcp_netid **relay_tagsp, struct dhcp_context *context,
Simon Kelleye44ddca2012-02-18 17:08:50 +000064 int interface, char *iface_name, struct in6_addr *fallback, void *inbuff, size_t sz, int is_unicast, time_t now)
Simon Kelley4cb1b322012-02-06 14:30:41 +000065{
66 void *end = inbuff + sz;
67 void *opts = inbuff + 34;
68 int msg_type = *((unsigned char *)inbuff);
69 unsigned char *outmsgtypep;
70 void *opt;
71 struct dhcp_vendor *vendor;
72
73 /* if not an encaplsulated relayed message, just do the stuff */
74 if (msg_type != DHCP6RELAYFORW)
75 {
76 /* if link_address != NULL if points to the link address field of the
77 innermost nested RELAYFORW message, which is where we find the
78 address of the network on which we can allocate an address.
79 Recalculate the available contexts using that information. */
80
81 if (link_address)
82 {
83 struct dhcp_context *c;
84 context = NULL;
85
86 for (c = daemon->dhcp6; c; c = c->next)
87 if (!IN6_IS_ADDR_LOOPBACK(link_address) &&
88 !IN6_IS_ADDR_LINKLOCAL(link_address) &&
89 !IN6_IS_ADDR_MULTICAST(link_address) &&
90 is_same_net6(link_address, &c->start6, c->prefix) &&
91 is_same_net6(link_address, &c->end6, c->prefix))
92 {
93 c->current = context;
94 context = c;
95 }
96
97 if (!context)
98 {
99 inet_ntop(AF_INET6, link_address, daemon->addrbuff, ADDRSTRLEN);
100 my_syslog(MS_DHCP | LOG_WARNING,
101 _("no address range available for DHCPv6 request from relay at %s"),
102 daemon->addrbuff);
103 return 0;
104 }
105 }
106
107 if (!context)
108 {
109 my_syslog(MS_DHCP | LOG_WARNING,
110 _("no address range available for DHCPv6 request via %s"), iface_name);
111 return 0;
112 }
113
Simon Kelleye44ddca2012-02-18 17:08:50 +0000114 return dhcp6_no_relay(msg_type, link_address, *relay_tagsp, context, interface, iface_name, fallback, inbuff, sz, is_unicast, now);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000115 }
116
117 /* must have at least msg_type+hopcount+link_address+peer_address+minimal size option
118 which is 1 + 1 + 16 + 16 + 2 + 2 = 38 */
119 if (sz < 38)
120 return 0;
121
122 /* copy header stuff into reply message and set type to reply */
123 outmsgtypep = put_opt6(inbuff, 34);
124 *outmsgtypep = DHCP6RELAYREPL;
125
126 /* look for relay options and set tags if found. */
127 for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
128 {
129 int mopt;
130
131 if (vendor->match_type == MATCH_SUBSCRIBER)
132 mopt = OPTION6_SUBSCRIBER_ID;
133 else if (vendor->match_type == MATCH_REMOTE)
134 mopt = OPTION6_REMOTE_ID;
135 else
136 continue;
137
138 if ((opt = opt6_find(opts, end, mopt, 1)) &&
139 vendor->len == opt6_len(opt) &&
140 memcmp(vendor->data, opt6_ptr(opt, 0), vendor->len) == 0 &&
141 vendor->netid.next != &vendor->netid)
142 {
143 vendor->netid.next = *relay_tagsp;
144 *relay_tagsp = &vendor->netid;
145 break;
146 }
147 }
148
149 for (opt = opts; opt; opt = opt6_next(opt, end))
150 {
151 int o = new_opt6(opt6_type(opt));
152 if (opt6_type(opt) == OPTION6_RELAY_MSG)
153 {
154 struct in6_addr link_address;
155 /* the packet data is unaligned, copy to aligned storage */
156 memcpy(&link_address, inbuff + 2, IN6ADDRSZ);
157 /* Not, zero is_unicast since that is now known to refer to the
158 relayed packet, not the original sent by the client */
Simon Kelleye44ddca2012-02-18 17:08:50 +0000159 if (!dhcp6_maybe_relay(&link_address, relay_tagsp, context, interface, iface_name, fallback, opt6_ptr(opt, 0), opt6_len(opt), 0, now))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000160 return 0;
161 }
162 else
163 put_opt6(opt6_ptr(opt, 0), opt6_len(opt));
164 end_opt6(o);
165 }
166
167 return 1;
168}
169
Simon Kelley07933802012-02-14 20:55:25 +0000170static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dhcp_netid *tags, struct dhcp_context *context,
Simon Kelleye44ddca2012-02-18 17:08:50 +0000171 int interface, char *iface_name, struct in6_addr *fallback, void *inbuff, size_t sz, int is_unicast, time_t now)
Simon Kelley4cb1b322012-02-06 14:30:41 +0000172{
173 void *packet_options = inbuff + 4;
174 void *end = inbuff + sz;
175 void *opt, *oro;
176 int i, o, o1;
177 unsigned char *clid = NULL;
178 int clid_len = 0, start_opts;
179 struct dhcp_netid *tagif, *context_tags = NULL;
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000180 char *client_hostname= NULL, *hostname = NULL;
181 char *domain = NULL, *send_domain = NULL;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000182 struct dhcp_config *config = NULL;
Simon Kelley0d28af82012-09-20 21:24:06 +0100183 struct dhcp_netid known_id, iface_id, v6_id;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000184 int done_dns = 0, hostname_auth = 0, do_encap = 0;
185 unsigned char *outmsgtypep;
186 struct dhcp_opt *opt_cfg;
187 struct dhcp_vendor *vendor;
188 struct dhcp_context *context_tmp;
Simon Kelley3634c542012-02-08 14:22:37 +0000189 unsigned int xid, ignore = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000190 unsigned int fqdn_flags = 0x01; /* default to send if we recieve no FQDN option */
191
Simon Kelleyceae00d2012-02-09 21:28:14 +0000192 /* set tag with name == interface */
193 iface_id.net = iface_name;
194 iface_id.next = tags;
195 tags = &iface_id;
196
Simon Kelley23780dd2012-10-23 17:04:37 +0100197 /* set tag "dhcpv6" */
198 v6_id.net = "dhcpv6";
Simon Kelley0d28af82012-09-20 21:24:06 +0100199 v6_id.next = tags;
200 tags = &v6_id;
201
Simon Kelley4cb1b322012-02-06 14:30:41 +0000202 /* copy over transaction-id, and save pointer to message type */
203 outmsgtypep = put_opt6(inbuff, 4);
204 start_opts = save_counter(-1);
205 xid = outmsgtypep[3] | outmsgtypep[2] << 8 | outmsgtypep[1] << 16;
206
207 /* We're going to be linking tags from all context we use.
208 mark them as unused so we don't link one twice and break the list */
209 for (context_tmp = context; context_tmp; context_tmp = context_tmp->current)
210 {
211 context->netid.next = &context->netid;
212
213 if (option_bool(OPT_LOG_OPTS))
214 {
215 inet_ntop(AF_INET6, &context_tmp->start6, daemon->dhcp_buff, ADDRSTRLEN);
216 inet_ntop(AF_INET6, &context_tmp->end6, daemon->dhcp_buff2, ADDRSTRLEN);
217 if (context_tmp->flags & (CONTEXT_STATIC))
218 my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCPv6 subnet: %s/%d"),
219 xid, daemon->dhcp_buff, context_tmp->prefix);
220 else
221 my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP range: %s -- %s"),
222 xid, daemon->dhcp_buff, daemon->dhcp_buff2);
223 }
224 }
225
226 if ((opt = opt6_find(packet_options, end, OPTION6_CLIENT_ID, 1)))
227 {
228 clid = opt6_ptr(opt, 0);
229 clid_len = opt6_len(opt);
230 o = new_opt6(OPTION6_CLIENT_ID);
231 put_opt6(clid, clid_len);
232 end_opt6(o);
233 }
234 else if (msg_type != DHCP6IREQ)
235 return 0;
236
237 /* server-id must match except for SOLICIT and CONFIRM messages */
238 if (msg_type != DHCP6SOLICIT && msg_type != DHCP6CONFIRM && msg_type != DHCP6IREQ &&
239 (!(opt = opt6_find(packet_options, end, OPTION6_SERVER_ID, 1)) ||
240 opt6_len(opt) != daemon->duid_len ||
241 memcmp(opt6_ptr(opt, 0), daemon->duid, daemon->duid_len) != 0))
242 return 0;
243
244 o = new_opt6(OPTION6_SERVER_ID);
245 put_opt6(daemon->duid, daemon->duid_len);
246 end_opt6(o);
247
248 if (is_unicast &&
249 (msg_type == DHCP6REQUEST || msg_type == DHCP6RENEW || msg_type == DHCP6RELEASE || msg_type == DHCP6DECLINE))
250
251 {
252 o1 = new_opt6(OPTION6_STATUS_CODE);
253 put_opt6_short(DHCP6USEMULTI);
254 put_opt6_string("Use multicast");
255 end_opt6(o1);
256 return 1;
257 }
258
259 /* match vendor and user class options */
260 for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
261 {
262 int mopt;
263
264 if (vendor->match_type == MATCH_VENDOR)
265 mopt = OPTION6_VENDOR_CLASS;
266 else if (vendor->match_type == MATCH_USER)
267 mopt = OPTION6_USER_CLASS;
268 else
269 continue;
270
271 if ((opt = opt6_find(packet_options, end, mopt, 2)))
272 {
273 void *enc_opt, *enc_end = opt6_ptr(opt, opt6_len(opt));
Simon Kelleya5c72ab2012-02-10 13:42:47 +0000274 int offset = 0;
275
276 if (mopt == OPTION6_VENDOR_CLASS)
277 {
278 if (opt6_len(opt) < 4)
279 continue;
280
281 if (vendor->enterprise != opt6_uint(opt, 0, 4))
282 continue;
283
284 offset = 4;
285 }
286
287 for (enc_opt = opt6_ptr(opt, offset); enc_opt; enc_opt = opt6_next(enc_opt, enc_end))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000288 for (i = 0; i <= (opt6_len(enc_opt) - vendor->len); i++)
289 if (memcmp(vendor->data, opt6_ptr(enc_opt, i), vendor->len) == 0)
290 {
291 vendor->netid.next = tags;
292 tags = &vendor->netid;
293 break;
294 }
295 }
296 }
Simon Kelley3634c542012-02-08 14:22:37 +0000297
Simon Kelley1567fea2012-03-12 22:15:35 +0000298 if (option_bool(OPT_LOG_OPTS) && (opt = opt6_find(packet_options, end, OPTION6_VENDOR_CLASS, 4)))
299 my_syslog(MS_DHCP | LOG_INFO, _("%u vendor class: %u"), xid, opt6_uint(opt, 0, 4));
300
Simon Kelley3634c542012-02-08 14:22:37 +0000301 /* dhcp-match. If we have hex-and-wildcards, look for a left-anchored match.
302 Otherwise assume the option is an array, and look for a matching element.
303 If no data given, existance of the option is enough. This code handles
304 V-I opts too. */
305 for (opt_cfg = daemon->dhcp_match6; opt_cfg; opt_cfg = opt_cfg->next)
306 {
Simon Kelleyceae00d2012-02-09 21:28:14 +0000307 int match = 0;
308
Simon Kelley3634c542012-02-08 14:22:37 +0000309 if (opt_cfg->flags & DHOPT_RFC3925)
310 {
311 for (opt = opt6_find(packet_options, end, OPTION6_VENDOR_OPTS, 4);
312 opt;
313 opt = opt6_find(opt6_next(opt, end), end, OPTION6_VENDOR_OPTS, 4))
314 {
315 void *vopt;
316 void *vend = opt6_ptr(opt, opt6_len(opt));
317
318 for (vopt = opt6_find(opt6_ptr(opt, 4), vend, opt_cfg->opt, 0);
319 vopt;
320 vopt = opt6_find(opt6_next(vopt, vend), vend, opt_cfg->opt, 0))
Simon Kelleyceae00d2012-02-09 21:28:14 +0000321 if ((match = match_bytes(opt_cfg, opt6_ptr(vopt, 0), opt6_len(vopt))))
Simon Kelley3634c542012-02-08 14:22:37 +0000322 break;
323 }
324 if (match)
325 break;
326 }
327 else
328 {
329 if (!(opt = opt6_find(packet_options, end, opt_cfg->opt, 1)))
330 continue;
331
332 match = match_bytes(opt_cfg, opt6_ptr(opt, 0), opt6_len(opt));
333 }
334
335 if (match)
336 {
337 opt_cfg->netid->next = tags;
338 tags = opt_cfg->netid;
339 }
340 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000341
342 if ((opt = opt6_find(packet_options, end, OPTION6_FQDN, 1)))
343 {
344 /* RFC4704 refers */
345 int len = opt6_len(opt) - 1;
346
347 fqdn_flags = opt6_uint(opt, 0, 1);
348
349 /* Always force update, since the client has no way to do it itself. */
350 if (!option_bool(OPT_FQDN_UPDATE) && !(fqdn_flags & 0x01))
351 fqdn_flags |= 0x03;
352
353 fqdn_flags &= ~0x04;
354
355 if (len != 0 && len < 255)
356 {
Simon Kelleyfbbc1452012-03-30 20:48:20 +0100357 unsigned char *pp, *op = opt6_ptr(opt, 1);
358 char *pq = daemon->dhcp_buff;
359
360 pp = op;
361 while (*op != 0 && ((op + (*op)) - pp) < len)
362 {
363 memcpy(pq, op+1, *op);
364 pq += *op;
365 op += (*op)+1;
366 *(pq++) = '.';
367 }
368
369 if (pq != daemon->dhcp_buff)
370 pq--;
371 *pq = 0;
372
373 if (legal_hostname(daemon->dhcp_buff))
374 {
375 client_hostname = daemon->dhcp_buff;
376 if (option_bool(OPT_LOG_OPTS))
377 my_syslog(MS_DHCP | LOG_INFO, _("%u client provides name: %s"), xid, client_hostname);
378 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000379 }
380 }
381
382 if (clid)
383 {
384 config = find_config6(daemon->dhcp_conf, context, clid, clid_len, NULL);
385
386 if (have_config(config, CONFIG_NAME))
387 {
388 hostname = config->hostname;
389 domain = config->domain;
390 hostname_auth = 1;
391 }
392 else if (client_hostname)
393 {
394 domain = strip_hostname(client_hostname);
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000395
Simon Kelley4cb1b322012-02-06 14:30:41 +0000396 if (strlen(client_hostname) != 0)
397 {
398 hostname = client_hostname;
399 if (!config)
400 {
401 /* Search again now we have a hostname.
Simon Kelley00e9ad52012-02-16 21:53:11 +0000402 Only accept configs without CLID here, (it won't match)
Simon Kelley4cb1b322012-02-06 14:30:41 +0000403 to avoid impersonation by name. */
404 struct dhcp_config *new = find_config6(daemon->dhcp_conf, context, NULL, 0, hostname);
405 if (new && !have_config(new, CONFIG_CLID) && !new->hwaddr)
406 config = new;
407 }
408 }
409 }
410 }
411
412 if (config)
413 {
414 struct dhcp_netid_list *list;
415
416 for (list = config->netid; list; list = list->next)
417 {
418 list->list->next = tags;
419 tags = list->list;
420 }
421
422 /* set "known" tag for known hosts */
423 known_id.net = "known";
424 known_id.next = tags;
425 tags = &known_id;
Simon Kelley3634c542012-02-08 14:22:37 +0000426
427 if (have_config(config, CONFIG_DISABLE))
428 ignore = 1;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000429 }
Simon Kelley00e9ad52012-02-16 21:53:11 +0000430
431 tagif = run_tag_if(tags);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000432
Simon Kelley3634c542012-02-08 14:22:37 +0000433 /* if all the netids in the ignore list are present, ignore this client */
434 if (daemon->dhcp_ignore)
435 {
436 struct dhcp_netid_list *id_list;
437
Simon Kelley3634c542012-02-08 14:22:37 +0000438 for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
439 if (match_netid(id_list->list, tagif, 0))
440 ignore = 1;
441 }
Simon Kelley00e9ad52012-02-16 21:53:11 +0000442
443 /* if all the netids in the ignore_name list are present, ignore client-supplied name */
444 if (!hostname_auth)
445 {
446 struct dhcp_netid_list *id_list;
447
448 for (id_list = daemon->dhcp_ignore_names; id_list; id_list = id_list->next)
449 if ((!id_list->list) || match_netid(id_list->list, tagif, 0))
450 break;
451 if (id_list)
452 hostname = NULL;
453 }
454
Simon Kelley4cb1b322012-02-06 14:30:41 +0000455
456 switch (msg_type)
457 {
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000458 default:
459 return 0;
460
Simon Kelley4cb1b322012-02-06 14:30:41 +0000461 case DHCP6SOLICIT:
462 case DHCP6REQUEST:
463 {
464 void *rapid_commit = opt6_find(packet_options, end, OPTION6_RAPID_COMMIT, 0);
465 int make_lease = (msg_type == DHCP6REQUEST || rapid_commit);
466 int serial = 0, used_config = 0;
467
468 if (rapid_commit)
469 {
470 o = new_opt6(OPTION6_RAPID_COMMIT);
471 end_opt6(o);
472 }
473
474 /* set reply message type */
475 *outmsgtypep = make_lease ? DHCP6REPLY : DHCP6ADVERTISE;
476
477 log6_packet(msg_type == DHCP6SOLICIT ? "DHCPSOLICIT" : "DHCPREQUEST",
Simon Kelley3634c542012-02-08 14:22:37 +0000478 clid, clid_len, NULL, xid, iface_name, ignore ? "ignored" : NULL);
479
480 if (ignore)
481 return 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000482
483 for (opt = packet_options; opt; opt = opt6_next(opt, end))
484 {
485 int iaid, ia_type = opt6_type(opt);
486 void *ia_option, *ia_end;
487 unsigned int min_time = 0xffffffff;
Simon Kelley98d76a02012-02-10 22:16:45 +0000488 int t1cntr = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000489 int address_assigned = 0;
490
491 if (ia_type != OPTION6_IA_NA && ia_type != OPTION6_IA_TA)
492 continue;
493
494 if (ia_type == OPTION6_IA_NA && opt6_len(opt) < 12)
495 continue;
496
497 if (ia_type == OPTION6_IA_TA && opt6_len(opt) < 4)
498 continue;
499
500 iaid = opt6_uint(opt, 0, 4);
501 ia_end = opt6_ptr(opt, opt6_len(opt));
502 ia_option = opt6_find(opt6_ptr(opt, ia_type == OPTION6_IA_NA ? 12 : 4), ia_end, OPTION6_IAADDR, 24);
503
504 /* reset "USED" flags on leases */
Simon Kelley8b460612012-09-08 21:47:28 +0100505 lease6_filter(ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, iaid, context);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000506
507 o = new_opt6(ia_type);
508 put_opt6_long(iaid);
509 if (ia_type == OPTION6_IA_NA)
510 {
511 /* save pointer */
512 t1cntr = save_counter(-1);
513 /* so we can fill these in later */
514 put_opt6_long(0);
515 put_opt6_long(0);
516 }
517
518 while (1)
519 {
520 struct in6_addr alloced_addr, *addrp = NULL;
Simon Kelleyc8257542012-03-28 21:15:41 +0100521 u32 requested_time = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000522 struct dhcp_lease *lease = NULL;
523
524 if (ia_option)
525 {
526 struct in6_addr *req_addr = opt6_ptr(ia_option, 0);
Simon Kelleyc8257542012-03-28 21:15:41 +0100527 requested_time = opt6_uint(ia_option, 16, 4);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000528
Simon Kelley00e9ad52012-02-16 21:53:11 +0000529 if (!address6_available(context, req_addr, tags) &&
530 (!have_config(config, CONFIG_ADDR6) || memcmp(&config->addr6, req_addr, IN6ADDRSZ) != 0))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000531 {
532 if (msg_type == DHCP6REQUEST)
533 {
534 /* host has a lease, but it's not on the correct link */
535 o1 = new_opt6(OPTION6_STATUS_CODE);
536 put_opt6_short(DHCP6NOTONLINK);
537 put_opt6_string("Not on link");
538 end_opt6(o1);
539 }
540 }
541 else if ((lease = lease6_find(NULL, 0, ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA,
542 iaid, req_addr)) &&
543 (clid_len != lease->clid_len ||
544 memcmp(clid, lease->clid, clid_len) != 0))
545 {
546 /* Address leased to another DUID */
547 o1 = new_opt6(OPTION6_STATUS_CODE);
548 put_opt6_short(DHCP6UNSPEC);
549 put_opt6_string("Address in use");
550 end_opt6(o1);
551 }
552 else
553 addrp = req_addr;
554 }
555 else
556 {
557 /* must have an address to CONFIRM */
558 if (msg_type == DHCP6REQUEST && ia_type == OPTION6_IA_NA)
559 return 0;
560
561 /* Don't used configured addresses for temporary leases. */
562 if (have_config(config, CONFIG_ADDR6) && !used_config && ia_type == OPTION6_IA_NA)
563 {
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000564 struct dhcp_lease *ltmp = lease6_find_by_addr(&config->addr6, 128, 0);
565
Simon Kelley4cb1b322012-02-06 14:30:41 +0000566 used_config = 1;
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000567 inet_ntop(AF_INET6, &config->addr6, daemon->addrbuff, ADDRSTRLEN);
568
569 if (ltmp && ltmp->clid &&
570 (ltmp->clid_len != clid_len || memcmp(ltmp->clid, clid, clid_len) != 0))
571 my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is leased to %s"),
572 daemon->addrbuff, print_mac(daemon->namebuff, ltmp->clid, ltmp->clid_len));
573 else if (have_config(config, CONFIG_DECLINED) &&
574 difftime(now, config->decline_time) < (float)DECLINE_BACKOFF)
575 my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it was previously declined"),
576 daemon->addrbuff);
577 else
Simon Kelley8b460612012-09-08 21:47:28 +0100578 {
579 addrp = &config->addr6;
580 /* may have existing lease for this address */
581 lease = lease6_find(clid, clid_len,
582 ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, iaid, addrp);
583 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000584 }
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000585
Simon Kelley4cb1b322012-02-06 14:30:41 +0000586 /* existing lease */
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000587 if (!addrp &&
588 (lease = lease6_find(clid, clid_len,
589 ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, iaid, NULL)) &&
Simon Kelleyceae00d2012-02-09 21:28:14 +0000590 !config_find_by_address6(daemon->dhcp_conf, (struct in6_addr *)&lease->hwaddr, 128, 0))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000591 addrp = (struct in6_addr *)&lease->hwaddr;
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000592
593 if (!addrp && address6_allocate(context, clid, clid_len, serial++, tags, &alloced_addr))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000594 addrp = &alloced_addr;
595 }
596
597 if (addrp)
598 {
599 unsigned int lease_time;
600 struct dhcp_context *this_context;
601 struct dhcp_config *valid_config = config;
602
603 /* don't use a config to set lease time if it specifies an address which isn't this. */
604 if (have_config(config, CONFIG_ADDR6) && memcmp(&config->addr6, addrp, IN6ADDRSZ) != 0)
605 valid_config = NULL;
606
607 address_assigned = 1;
608
609 /* shouldn't ever fail */
Simon Kelley00e9ad52012-02-16 21:53:11 +0000610 if ((this_context = narrow_context6(context, addrp, tagif)))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000611 {
612 /* get tags from context if we've not used it before */
Simon Kelley00e9ad52012-02-16 21:53:11 +0000613 if (this_context->netid.next == &this_context->netid && this_context->netid.net)
Simon Kelley4cb1b322012-02-06 14:30:41 +0000614 {
615 this_context->netid.next = context_tags;
616 context_tags = &this_context->netid;
Simon Kelley00e9ad52012-02-16 21:53:11 +0000617 if (!hostname_auth)
618 {
619 struct dhcp_netid_list *id_list;
620
621 for (id_list = daemon->dhcp_ignore_names; id_list; id_list = id_list->next)
622 if ((!id_list->list) || match_netid(id_list->list, &this_context->netid, 0))
623 break;
624 if (id_list)
625 hostname = NULL;
626 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000627 }
Simon Kelley3bc0d932012-12-28 11:31:44 +0000628
629 if (have_config(valid_config, CONFIG_TIME))
630 lease_time = valid_config->lease_time;
631 else
632 lease_time = this_context->lease_time;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000633
Simon Kelley3bc0d932012-12-28 11:31:44 +0000634 if (this_context->valid < lease_time)
635 lease_time = this_context->valid;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000636
Simon Kelley3bc0d932012-12-28 11:31:44 +0000637 if (ia_option)
Simon Kelley4cb1b322012-02-06 14:30:41 +0000638 {
Simon Kelleyc8257542012-03-28 21:15:41 +0100639 if (requested_time < 120u )
640 requested_time = 120u; /* sanity */
641 if (lease_time == 0xffffffff || (requested_time != 0xffffffff && requested_time < lease_time))
642 lease_time = requested_time;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000643 }
644
645 if (lease_time < min_time)
646 min_time = lease_time;
647
648 /* May fail to create lease */
649 if (!lease && make_lease)
650 lease = lease6_allocate(addrp, ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA);
651
652 if (lease)
653 {
654 lease_set_expires(lease, lease_time, now);
Simon Kelleya9ab7322012-04-28 11:29:37 +0100655 lease_set_hwaddr(lease, NULL, clid, 0, iaid, clid_len, now, 0);
Simon Kelley353ae4d2012-03-19 20:07:51 +0000656 lease_set_interface(lease, interface, now);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000657 if (hostname && ia_type == OPTION6_IA_NA)
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000658 {
659 char *addr_domain = get_domain6(addrp);
660 if (!send_domain)
661 send_domain = addr_domain;
662 lease_set_hostname(lease, hostname, hostname_auth, addr_domain, domain);
663 }
Simon Kelleyceae00d2012-02-09 21:28:14 +0000664
665#ifdef HAVE_SCRIPT
666 if (daemon->lease_change_command)
667 {
668 void *class_opt;
669 lease->flags |= LEASE_CHANGED;
670 free(lease->extradata);
671 lease->extradata = NULL;
672 lease->extradata_size = lease->extradata_len = 0;
673 lease->hwaddr_len = 0; /* surrogate for no of vendor classes */
674
Simon Kelleya5c72ab2012-02-10 13:42:47 +0000675 if ((class_opt = opt6_find(packet_options, end, OPTION6_VENDOR_CLASS, 4)))
Simon Kelleyceae00d2012-02-09 21:28:14 +0000676 {
677 void *enc_opt, *enc_end = opt6_ptr(class_opt, opt6_len(class_opt));
Simon Kelleya5c72ab2012-02-10 13:42:47 +0000678 lease->hwaddr_len++;
679 /* send enterprise number first */
680 sprintf(daemon->dhcp_buff2, "%u", opt6_uint(class_opt, 0, 4));
681 lease_add_extradata(lease, (unsigned char *)daemon->dhcp_buff2, strlen(daemon->dhcp_buff2), 0);
682
683 if (opt6_len(class_opt) >= 6)
684 for (enc_opt = opt6_ptr(class_opt, 4); enc_opt; enc_opt = opt6_next(enc_opt, enc_end))
685 {
686 lease->hwaddr_len++;
687 lease_add_extradata(lease, opt6_ptr(enc_opt, 0), opt6_len(enc_opt), 0);
688 }
Simon Kelleyceae00d2012-02-09 21:28:14 +0000689 }
690
691 lease_add_extradata(lease, (unsigned char *)client_hostname,
692 client_hostname ? strlen(client_hostname) : 0, 0);
693
694 /* space-concat tag set */
Simon Kelley00e9ad52012-02-16 21:53:11 +0000695 if (!tagif && !context_tags)
Simon Kelleyceae00d2012-02-09 21:28:14 +0000696 lease_add_extradata(lease, NULL, 0, 0);
697 else
698 {
Simon Kelley87b8ecb2012-02-18 21:20:43 +0000699 struct dhcp_netid *n, *l, *tmp = tags;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000700
Simon Kelley00e9ad52012-02-16 21:53:11 +0000701 /* link temporarily */
702 for (n = context_tags; n && n->next; n = n->next);
Simon Kelleye44ddca2012-02-18 17:08:50 +0000703 if ((l = n))
Simon Kelley87b8ecb2012-02-18 21:20:43 +0000704 {
705 l->next = tags;
706 tmp = context_tags;
707 }
Simon Kelley00e9ad52012-02-16 21:53:11 +0000708
Simon Kelley87b8ecb2012-02-18 21:20:43 +0000709 for (n = run_tag_if(tmp); n; n = n->next)
Simon Kelleyceae00d2012-02-09 21:28:14 +0000710 {
711 struct dhcp_netid *n1;
712 /* kill dupes */
713 for (n1 = n->next; n1; n1 = n1->next)
714 if (strcmp(n->net, n1->net) == 0)
715 break;
716 if (!n1)
717 lease_add_extradata(lease, (unsigned char *)n->net, strlen(n->net), n->next ? ' ' : 0);
718 }
Simon Kelley00e9ad52012-02-16 21:53:11 +0000719
720 /* unlink again */
721 if (l)
722 l->next = NULL;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000723 }
Simon Kelley07933802012-02-14 20:55:25 +0000724
725 if (link_address)
726 inet_ntop(AF_INET6, link_address, daemon->addrbuff, ADDRSTRLEN);
727
728 lease_add_extradata(lease, (unsigned char *)daemon->addrbuff, link_address ? strlen(daemon->addrbuff) : 0, 0);
Simon Kelleyceae00d2012-02-09 21:28:14 +0000729
730 if ((class_opt = opt6_find(packet_options, end, OPTION6_USER_CLASS, 2)))
731 {
732 void *enc_opt, *enc_end = opt6_ptr(class_opt, opt6_len(class_opt));
733 for (enc_opt = opt6_ptr(class_opt, 0); enc_opt; enc_opt = opt6_next(enc_opt, enc_end))
734 lease_add_extradata(lease, opt6_ptr(enc_opt, 0), opt6_len(enc_opt), 0);
735 }
736 }
737#endif
738
Simon Kelley4cb1b322012-02-06 14:30:41 +0000739 }
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000740 else if (!send_domain)
741 send_domain = get_domain6(addrp);
742
Simon Kelley4cb1b322012-02-06 14:30:41 +0000743
744 if (lease || !make_lease)
745 {
746 o1 = new_opt6(OPTION6_IAADDR);
747 put_opt6(addrp, sizeof(*addrp));
Simon Kelleyc8257542012-03-28 21:15:41 +0100748 /* preferred lifetime */
Simon Kelley3bc0d932012-12-28 11:31:44 +0000749 put_opt6_long(this_context && (this_context->preferred < lease_time) ?
750 this_context->preferred : lease_time);
Simon Kelleyc8257542012-03-28 21:15:41 +0100751 put_opt6_long(lease_time); /* valid lifetime */
Simon Kelley4cb1b322012-02-06 14:30:41 +0000752 end_opt6(o1);
753
754 log6_packet( make_lease ? "DHCPREPLY" : "DHCPADVERTISE",
755 clid, clid_len, addrp, xid, iface_name, hostname);
756 }
757
758 }
759 }
760
761
762 if (!ia_option ||
763 !(ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24)))
764 {
765 if (address_assigned)
766 {
Simon Kelleydd88c172012-03-10 20:46:57 +0000767 o1 = new_opt6(OPTION6_STATUS_CODE);
768 put_opt6_short(DHCP6SUCCESS);
769 put_opt6_string("Oh hai from dnsmasq");
770 end_opt6(o1);
771
Simon Kelley4cb1b322012-02-06 14:30:41 +0000772 if (ia_type == OPTION6_IA_NA)
773 {
774 /* go back an fill in fields in IA_NA option */
775 unsigned int t1 = min_time == 0xffffffff ? 0xffffffff : min_time/2;
776 unsigned int t2 = min_time == 0xffffffff ? 0xffffffff : (min_time/8) * 7;
777 int sav = save_counter(t1cntr);
778 put_opt6_long(t1);
779 put_opt6_long(t2);
780 save_counter(sav);
781 }
782 }
783 else
784 {
Simon Kelleydd88c172012-03-10 20:46:57 +0000785 /* no address, return error */
Simon Kelley4cb1b322012-02-06 14:30:41 +0000786 o1 = new_opt6(OPTION6_STATUS_CODE);
787 put_opt6_short(DHCP6NOADDRS);
788 put_opt6_string("No addresses available");
789 end_opt6(o1);
790 }
791
792 end_opt6(o);
Simon Kelley2a82db42012-03-10 21:40:10 +0000793
Simon Kelley1d0f91c2012-03-12 11:56:22 +0000794 if (address_assigned)
795 {
796 /* If --dhcp-authoritative is set, we can tell client not to wait for
797 other possible servers */
798 o = new_opt6(OPTION6_PREFERENCE);
799 put_opt6_char(option_bool(OPT_AUTHORITATIVE) ? 255 : 0);
800 end_opt6(o);
801 }
802
Simon Kelley4cb1b322012-02-06 14:30:41 +0000803 break;
804 }
805 }
806 }
807
808 break;
809 }
810
811 case DHCP6RENEW:
812 {
813 /* set reply message type */
814 *outmsgtypep = DHCP6REPLY;
815
816 log6_packet("DHCPRENEW", clid, clid_len, NULL, xid, iface_name, NULL);
817
818 for (opt = packet_options; opt; opt = opt6_next(opt, end))
819 {
820 int ia_type = opt6_type(opt);
821 void *ia_option, *ia_end;
822 unsigned int min_time = 0xffffffff;
823 int t1cntr = 0, iacntr;
824 unsigned int iaid;
825
826 if (ia_type != OPTION6_IA_NA && ia_type != OPTION6_IA_TA)
827 continue;
828
829 if (ia_type == OPTION6_IA_NA && opt6_len(opt) < 12)
830 continue;
831
832 if (ia_type == OPTION6_IA_TA && opt6_len(opt) < 4)
833 continue;
834
835 iaid = opt6_uint(opt, 0, 4);
836
837 o = new_opt6(ia_type);
838 put_opt6_long(iaid);
839 if (ia_type == OPTION6_IA_NA)
840 {
841 /* save pointer */
842 t1cntr = save_counter(-1);
843 /* so we can fill these in later */
844 put_opt6_long(0);
845 put_opt6_long(0);
846 }
847
848 iacntr = save_counter(-1);
849
850 /* reset "USED" flags on leases */
Simon Kelley8b460612012-09-08 21:47:28 +0100851 lease6_filter(ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, iaid, context);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000852
853 ia_option = opt6_ptr(opt, ia_type == OPTION6_IA_NA ? 12 : 4);
854 ia_end = opt6_ptr(opt, opt6_len(opt));
855
856 for (ia_option = opt6_find(ia_option, ia_end, OPTION6_IAADDR, 24);
857 ia_option;
858 ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
859 {
860 struct dhcp_lease *lease = NULL;
861 struct in6_addr *req_addr = opt6_ptr(ia_option, 0);
Simon Kelleyc8257542012-03-28 21:15:41 +0100862 u32 requested_time = opt6_uint(ia_option, 16, 4);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000863 unsigned int lease_time;
864 struct dhcp_context *this_context;
865 struct dhcp_config *valid_config = config;
866
867 /* don't use a config to set lease time if it specifies an address which isn't this. */
868 if (have_config(config, CONFIG_ADDR6) && memcmp(&config->addr6, req_addr, IN6ADDRSZ) != 0)
869 valid_config = NULL;
870
871 if (!(lease = lease6_find(clid, clid_len,
872 ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA,
873 iaid, req_addr)))
874 {
875 /* If the server cannot find a client entry for the IA the server
876 returns the IA containing no addresses with a Status Code option set
877 to NoBinding in the Reply message. */
878 save_counter(iacntr);
879 t1cntr = 0;
880
881 log6_packet("DHCPREPLY", clid, clid_len, req_addr, xid, iface_name, "lease not found");
882
883 o1 = new_opt6(OPTION6_STATUS_CODE);
884 put_opt6_short(DHCP6NOBINDING);
885 put_opt6_string("No binding found");
886 end_opt6(o1);
887 break;
888 }
889
Simon Kelley00e9ad52012-02-16 21:53:11 +0000890
Simon Kelleyceae00d2012-02-09 21:28:14 +0000891 if (!address6_available(context, req_addr, tagif) ||
892 !(this_context = narrow_context6(context, req_addr, tagif)))
Simon Kelleyc8257542012-03-28 21:15:41 +0100893 {
894 lease_time = 0;
895 this_context = NULL;
896 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000897 else
898 {
899 /* get tags from context if we've not used it before */
Simon Kelley00e9ad52012-02-16 21:53:11 +0000900 if (this_context->netid.next == &this_context->netid && this_context->netid.net)
Simon Kelley4cb1b322012-02-06 14:30:41 +0000901 {
902 this_context->netid.next = context_tags;
903 context_tags = &this_context->netid;
Simon Kelley00e9ad52012-02-16 21:53:11 +0000904 if (!hostname_auth)
905 {
906 struct dhcp_netid_list *id_list;
907
908 for (id_list = daemon->dhcp_ignore_names; id_list; id_list = id_list->next)
909 if ((!id_list->list) || match_netid(id_list->list, &this_context->netid, 0))
910 break;
911 if (id_list)
912 hostname = NULL;
913 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000914 }
915
916 lease_time = have_config(valid_config, CONFIG_TIME) ? valid_config->lease_time : this_context->lease_time;
917
Simon Kelleyc8257542012-03-28 21:15:41 +0100918 if (requested_time < 120u )
919 requested_time = 120u; /* sanity */
920 if (lease_time == 0xffffffff || (requested_time != 0xffffffff && requested_time < lease_time))
921 lease_time = requested_time;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000922
923 lease_set_expires(lease, lease_time, now);
924 if (ia_type == OPTION6_IA_NA && hostname)
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000925 {
926 char *addr_domain = get_domain6(req_addr);
927 if (!send_domain)
928 send_domain = addr_domain;
929 lease_set_hostname(lease, hostname, hostname_auth, addr_domain, domain);
930 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000931
932 if (lease_time < min_time)
933 min_time = lease_time;
934 }
935
936 log6_packet("DHCPREPLY", clid, clid_len, req_addr, xid, iface_name, hostname);
937
938 o1 = new_opt6(OPTION6_IAADDR);
939 put_opt6(req_addr, sizeof(*req_addr));
Simon Kelleyc8257542012-03-28 21:15:41 +0100940 /* preferred lifetime */
941 put_opt6_long(this_context && (this_context->flags & CONTEXT_DEPRECATE) ? 0 : lease_time);
942 put_opt6_long(lease_time); /* valid lifetime */
Simon Kelley4cb1b322012-02-06 14:30:41 +0000943 end_opt6(o1);
944 }
945
946 if (t1cntr != 0)
947 {
948 /* go back an fill in fields in IA_NA option */
Simon Kelley4cb1b322012-02-06 14:30:41 +0000949 int sav = save_counter(t1cntr);
Simon Kelleydaf061c2012-03-12 21:57:18 +0000950 unsigned int t1, t2, fuzz = rand16();
951
952 while (fuzz > (min_time/16))
953 fuzz = fuzz/2;
954 t1 = min_time == 0xffffffff ? 0xffffffff : min_time/2 - fuzz;
955 t2 = min_time == 0xffffffff ? 0xffffffff : ((min_time/8)*7) - fuzz;
956
Simon Kelley4cb1b322012-02-06 14:30:41 +0000957 put_opt6_long(t1);
958 put_opt6_long(t2);
959 save_counter(sav);
960 }
961
962 end_opt6(o);
963 }
964 break;
965
966 }
967
968 case DHCP6CONFIRM:
969 {
970 /* set reply message type */
971 *outmsgtypep = DHCP6REPLY;
972
973 log6_packet("DHCPCONFIRM", clid, clid_len, NULL, xid, iface_name, NULL);
974
975 for (opt = packet_options; opt; opt = opt6_next(opt, end))
976 {
977 int ia_type = opt6_type(opt);
978 void *ia_option, *ia_end;
979
980 if (ia_type != OPTION6_IA_NA && ia_type != OPTION6_IA_TA)
981 continue;
982
983 if (ia_type == OPTION6_IA_NA && opt6_len(opt) < 12)
984 continue;
985
986 if (ia_type == OPTION6_IA_TA && opt6_len(opt) < 4)
987 continue;
988
989 ia_option = opt6_ptr(opt, ia_type == OPTION6_IA_NA ? 12 : 4);
990 ia_end = opt6_ptr(opt, opt6_len(opt));
991
992 for (ia_option = opt6_find(ia_option, ia_end, OPTION6_IAADDR, 24);
993 ia_option;
994 ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
995 {
996 struct in6_addr *req_addr = opt6_ptr(ia_option, 0);
997
Simon Kelleyceae00d2012-02-09 21:28:14 +0000998 if (!address6_available(context, req_addr, run_tag_if(tags)))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000999 {
1000 o1 = new_opt6(OPTION6_STATUS_CODE);
1001 put_opt6_short(DHCP6NOTONLINK);
1002 put_opt6_string("Confirm failed");
1003 end_opt6(o1);
1004 return 1;
1005 }
1006
1007 log6_packet("DHCPREPLY", clid, clid_len, req_addr, xid, iface_name, hostname);
1008 }
1009 }
1010
1011 o1 = new_opt6(OPTION6_STATUS_CODE);
1012 put_opt6_short(DHCP6SUCCESS );
1013 put_opt6_string("All addresses still on link");
1014 end_opt6(o1);
1015 return 1;
1016 }
1017
1018 case DHCP6IREQ:
1019 {
Simon Kelleyd1e9a582012-10-23 17:00:57 +01001020 /* We can't discriminate contexts based on address, as we don't know it.
1021 If there is only one possible context, we can use its tags */
1022 if (context && !context->current)
1023 {
1024 context->netid.next = NULL;
1025 context_tags = &context->netid;
1026 }
Simon Kelley42698cb2012-09-20 21:19:35 +01001027 log6_packet("DHCPINFORMATION-REQUEST", clid, clid_len, NULL, xid, iface_name, ignore ? "ignored" : hostname);
Simon Kelley3634c542012-02-08 14:22:37 +00001028 if (ignore)
1029 return 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001030 *outmsgtypep = DHCP6REPLY;
1031 break;
1032 }
1033
1034
1035 case DHCP6RELEASE:
1036 {
1037 /* set reply message type */
1038 *outmsgtypep = DHCP6REPLY;
1039
1040 log6_packet("DHCPRELEASE", clid, clid_len, NULL, xid, iface_name, NULL);
1041
1042 for (opt = packet_options; opt; opt = opt6_next(opt, end))
1043 {
1044 int iaid, ia_type = opt6_type(opt);
1045 void *ia_option, *ia_end;
1046 int made_ia = 0;
1047
1048 if (ia_type != OPTION6_IA_NA && ia_type != OPTION6_IA_TA)
1049 continue;
1050
1051 if (ia_type == OPTION6_IA_NA && opt6_len(opt) < 12)
1052 continue;
1053
1054 if (ia_type == OPTION6_IA_TA && opt6_len(opt) < 4)
1055 continue;
1056
1057 iaid = opt6_uint(opt, 0, 4);
1058 ia_end = opt6_ptr(opt, opt6_len(opt));
1059 ia_option = opt6_ptr(opt, ia_type == OPTION6_IA_NA ? 12 : 4);
1060
1061 /* reset "USED" flags on leases */
Simon Kelley8b460612012-09-08 21:47:28 +01001062 lease6_filter(ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, iaid, context);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001063
1064 for (ia_option = opt6_find(ia_option, ia_end, OPTION6_IAADDR, 24);
1065 ia_option;
1066 ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
1067 {
1068 struct dhcp_lease *lease;
1069
1070 if ((lease = lease6_find(clid, clid_len, ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA,
1071 iaid, opt6_ptr(ia_option, 0))))
1072 lease_prune(lease, now);
1073 else
1074 {
1075 if (!made_ia)
1076 {
1077 o = new_opt6(ia_type);
1078 put_opt6_long(iaid);
1079 if (ia_type == OPTION6_IA_NA)
1080 {
1081 put_opt6_long(0);
1082 put_opt6_long(0);
1083 }
1084 made_ia = 1;
1085 }
1086
1087 o1 = new_opt6(OPTION6_IAADDR);
1088 put_opt6(opt6_ptr(ia_option, 0), IN6ADDRSZ);
1089 put_opt6_long(0);
1090 put_opt6_long(0);
1091 end_opt6(o1);
1092 }
1093 }
1094
1095 if (made_ia)
1096 {
1097 o1 = new_opt6(OPTION6_STATUS_CODE);
1098 put_opt6_short(DHCP6NOBINDING);
1099 put_opt6_string("No binding found");
1100 end_opt6(o1);
1101
1102 end_opt6(o);
1103 }
1104 }
1105
1106 o1 = new_opt6(OPTION6_STATUS_CODE);
1107 put_opt6_short(DHCP6SUCCESS);
1108 put_opt6_string("Release received");
1109 end_opt6(o1);
1110
1111 return 1;
1112 }
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001113
1114 case DHCP6DECLINE:
1115 {
1116 /* set reply message type */
1117 *outmsgtypep = DHCP6REPLY;
1118
1119 log6_packet("DHCPDECLINE", clid, clid_len, NULL, xid, iface_name, NULL);
1120
1121 for (opt = packet_options; opt; opt = opt6_next(opt, end))
1122 {
1123 int iaid, ia_type = opt6_type(opt);
1124 void *ia_option, *ia_end;
1125 int made_ia = 0;
1126
1127 if (ia_type != OPTION6_IA_NA && ia_type != OPTION6_IA_TA)
1128 continue;
1129
1130 if (ia_type == OPTION6_IA_NA && opt6_len(opt) < 12)
1131 continue;
1132
1133 if (ia_type == OPTION6_IA_TA && opt6_len(opt) < 4)
1134 continue;
1135
1136 iaid = opt6_uint(opt, 0, 4);
1137 ia_end = opt6_ptr(opt, opt6_len(opt));
1138 ia_option = opt6_ptr(opt, ia_type == OPTION6_IA_NA ? 12 : 4);
1139
1140 /* reset "USED" flags on leases */
Simon Kelley8b460612012-09-08 21:47:28 +01001141 lease6_filter(ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, iaid, context);
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001142
1143 for (ia_option = opt6_find(ia_option, ia_end, OPTION6_IAADDR, 24);
1144 ia_option;
1145 ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
1146 {
1147 struct dhcp_lease *lease;
1148 struct in6_addr *addrp = opt6_ptr(ia_option, 0);
1149
1150 if (have_config(config, CONFIG_ADDR6) &&
1151 memcmp(&config->addr6, addrp, IN6ADDRSZ) == 0)
1152 {
Simon Kelleyceae00d2012-02-09 21:28:14 +00001153 prettyprint_time(daemon->dhcp_buff3, DECLINE_BACKOFF);
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001154 inet_ntop(AF_INET6, addrp, daemon->addrbuff, ADDRSTRLEN);
1155 my_syslog(MS_DHCP | LOG_WARNING, _("disabling DHCP static address %s for %s"),
Simon Kelleyceae00d2012-02-09 21:28:14 +00001156 daemon->addrbuff, daemon->dhcp_buff3);
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001157 config->flags |= CONFIG_DECLINED;
1158 config->decline_time = now;
1159 }
1160 else
1161 /* make sure this host gets a different address next time. */
1162 for (; context; context = context->current)
1163 context->addr_epoch++;
1164
1165 if ((lease = lease6_find(clid, clid_len, ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA,
1166 iaid, opt6_ptr(ia_option, 0))))
1167 lease_prune(lease, now);
1168 else
1169 {
1170 if (!made_ia)
1171 {
1172 o = new_opt6(ia_type);
1173 put_opt6_long(iaid);
1174 if (ia_type == OPTION6_IA_NA)
1175 {
1176 put_opt6_long(0);
1177 put_opt6_long(0);
1178 }
1179 made_ia = 1;
1180 }
1181
1182 o1 = new_opt6(OPTION6_IAADDR);
1183 put_opt6(opt6_ptr(ia_option, 0), IN6ADDRSZ);
1184 put_opt6_long(0);
1185 put_opt6_long(0);
1186 end_opt6(o1);
1187 }
1188 }
1189
1190 if (made_ia)
1191 {
1192 o1 = new_opt6(OPTION6_STATUS_CODE);
1193 put_opt6_short(DHCP6NOBINDING);
1194 put_opt6_string("No binding found");
1195 end_opt6(o1);
1196
1197 end_opt6(o);
1198 }
1199
1200 }
1201 return 1;
1202 }
1203
Simon Kelley4cb1b322012-02-06 14:30:41 +00001204 }
1205
1206
1207 /* filter options based on tags, those we want get DHOPT_TAGOK bit set */
1208 tagif = option_filter(tags, context_tags, daemon->dhcp_opts6);
1209
1210 oro = opt6_find(packet_options, end, OPTION6_ORO, 0);
1211
1212 for (opt_cfg = daemon->dhcp_opts6; opt_cfg; opt_cfg = opt_cfg->next)
1213 {
1214 /* netids match and not encapsulated? */
1215 if (!(opt_cfg->flags & DHOPT_TAGOK))
1216 continue;
1217
1218 if (!(opt_cfg->flags & DHOPT_FORCE) && oro)
1219 {
1220 for (i = 0; i < opt6_len(oro) - 1; i += 2)
1221 if (opt6_uint(oro, i, 2) == (unsigned)opt_cfg->opt)
1222 break;
1223
1224 /* option not requested */
1225 if (i >= opt6_len(oro) - 1)
1226 continue;
1227 }
1228
1229 if (opt_cfg->opt == OPTION6_DNS_SERVER)
1230 {
1231 done_dns = 1;
1232 if (opt_cfg->len == 0)
1233 continue;
1234 }
1235
1236 o = new_opt6(opt_cfg->opt);
Simon Kelley4b86b652012-02-29 11:45:37 +00001237 if (opt_cfg->flags & DHOPT_ADDR6)
1238 {
1239 int j;
1240 struct in6_addr *a = (struct in6_addr *)opt_cfg->val;
1241 for (j = 0; j < opt_cfg->len; j+=IN6ADDRSZ, a++)
1242 {
1243 /* zero means "self" (but not in vendorclass options.) */
1244 if (IN6_IS_ADDR_UNSPECIFIED(a))
1245 {
1246 if (IN6_IS_ADDR_UNSPECIFIED(&context->local6))
1247 put_opt6(fallback, IN6ADDRSZ);
1248 else
1249 put_opt6(&context->local6, IN6ADDRSZ);
1250 }
1251 else
1252 put_opt6(a, IN6ADDRSZ);
1253 }
1254 }
1255 else if (opt_cfg->val)
Simon Kelley4cb1b322012-02-06 14:30:41 +00001256 put_opt6(opt_cfg->val, opt_cfg->len);
1257 end_opt6(o);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001258 }
1259
Simon Kelleye44ddca2012-02-18 17:08:50 +00001260 if (!done_dns &&
1261 (!IN6_IS_ADDR_UNSPECIFIED(&context->local6) ||
1262 !IN6_IS_ADDR_UNSPECIFIED(fallback)))
Simon Kelley4cb1b322012-02-06 14:30:41 +00001263 {
1264 o = new_opt6(OPTION6_DNS_SERVER);
Simon Kelleye44ddca2012-02-18 17:08:50 +00001265 if (IN6_IS_ADDR_UNSPECIFIED(&context->local6))
1266 put_opt6(fallback, IN6ADDRSZ);
1267 else
1268 put_opt6(&context->local6, IN6ADDRSZ);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001269 end_opt6(o);
1270 }
1271
1272 /* handle vendor-identifying vendor-encapsulated options,
1273 dhcp-option = vi-encap:13,17,....... */
1274 for (opt_cfg = daemon->dhcp_opts6; opt_cfg; opt_cfg = opt_cfg->next)
1275 opt_cfg->flags &= ~DHOPT_ENCAP_DONE;
Simon Kelley4b86b652012-02-29 11:45:37 +00001276
Simon Kelley4cb1b322012-02-06 14:30:41 +00001277 if (oro)
1278 for (i = 0; i < opt6_len(oro) - 1; i += 2)
1279 if (opt6_uint(oro, i, 2) == OPTION6_VENDOR_OPTS)
1280 do_encap = 1;
1281
1282 for (opt_cfg = daemon->dhcp_opts6; opt_cfg; opt_cfg = opt_cfg->next)
1283 {
1284 if (opt_cfg->flags & DHOPT_RFC3925)
1285 {
1286 int found = 0;
1287 struct dhcp_opt *oc;
1288
1289 if (opt_cfg->flags & DHOPT_ENCAP_DONE)
1290 continue;
1291
1292 for (oc = daemon->dhcp_opts6; oc; oc = oc->next)
1293 {
1294 oc->flags &= ~DHOPT_ENCAP_MATCH;
1295
1296 if (!(oc->flags & DHOPT_RFC3925) || opt_cfg->u.encap != oc->u.encap)
1297 continue;
1298
1299 oc->flags |= DHOPT_ENCAP_DONE;
1300 if (match_netid(oc->netid, tagif, 1))
1301 {
1302 /* option requested/forced? */
1303 if (!oro || do_encap || (oc->flags & DHOPT_FORCE))
1304 {
1305 oc->flags |= DHOPT_ENCAP_MATCH;
1306 found = 1;
1307 }
1308 }
1309 }
1310
1311 if (found)
1312 {
1313 o = new_opt6(OPTION6_VENDOR_OPTS);
1314 put_opt6_long(opt_cfg->u.encap);
1315
1316 for (oc = daemon->dhcp_opts6; oc; oc = oc->next)
1317 if (oc->flags & DHOPT_ENCAP_MATCH)
1318 {
1319 o1 = new_opt6(oc->opt);
1320 put_opt6(oc->val, oc->len);
1321 end_opt6(o1);
1322 }
1323 end_opt6(o);
1324 }
1325 }
1326 }
Simon Kelley07933802012-02-14 20:55:25 +00001327
1328
Simon Kelley4cb1b322012-02-06 14:30:41 +00001329 if (hostname)
1330 {
1331 unsigned char *p;
1332 size_t len = strlen(hostname);
1333
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001334 if (send_domain)
1335 len += strlen(send_domain) + 1;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001336
1337 o = new_opt6(OPTION6_FQDN);
Simon Kelleyc5ad4e72012-02-24 16:06:20 +00001338 if ((p = expand(len + 3)))
1339 {
1340 *(p++) = fqdn_flags;
1341 p = do_rfc1035_name(p, hostname);
1342 if (send_domain)
1343 p = do_rfc1035_name(p, send_domain);
1344 *p = 0;
1345 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001346 end_opt6(o);
1347 }
1348
1349
1350 /* logging */
1351 if (option_bool(OPT_LOG_OPTS) && oro)
1352 {
1353 char *q = daemon->namebuff;
1354 for (i = 0; i < opt6_len(oro) - 1; i += 2)
1355 {
1356 char *s = option_string(AF_INET6, opt6_uint(oro, i, 2), NULL, 0, NULL, 0);
1357 q += snprintf(q, MAXDNAME - (q - daemon->namebuff),
1358 "%d%s%s%s",
1359 opt6_uint(oro, i, 2),
1360 strlen(s) != 0 ? ":" : "",
1361 s,
1362 (i > opt6_len(oro) - 3) ? "" : ", ");
1363 if ( i > opt6_len(oro) - 3 || (q - daemon->namebuff) > 40)
1364 {
1365 q = daemon->namebuff;
1366 my_syslog(MS_DHCP | LOG_INFO, _("%u requested options: %s"), xid, daemon->namebuff);
1367 }
1368 }
1369 }
1370
1371 log_tags(tagif, xid);
1372
1373 if (option_bool(OPT_LOG_OPTS))
Simon Kelley6c8f21e2012-03-12 15:06:55 +00001374 log6_opts(0, xid, daemon->outpacket.iov_base + start_opts, daemon->outpacket.iov_base + save_counter(-1));
1375
1376 return 1;
1377}
1378
1379static void log6_opts(int nest, unsigned int xid, void *start_opts, void *end_opts)
1380{
1381 void *opt;
1382 char *desc = nest ? "nest" : "sent";
Simon Kelley5cfea3d2012-03-12 17:28:27 +00001383
1384 if (start_opts == end_opts)
1385 return;
1386
1387 for (opt = start_opts; opt; opt = opt6_next(opt, end_opts))
1388 {
1389 int type = opt6_type(opt);
1390 void *ia_options = NULL;
1391 char *optname;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001392
Simon Kelley5cfea3d2012-03-12 17:28:27 +00001393 if (type == OPTION6_IA_NA)
1394 {
1395 sprintf(daemon->namebuff, "IAID=%u T1=%u T2=%u",
1396 opt6_uint(opt, 0, 4), opt6_uint(opt, 4, 4), opt6_uint(opt, 8, 4));
1397 optname = "ia-na";
1398 ia_options = opt6_ptr(opt, 12);
1399 }
1400 else if (type == OPTION6_IA_TA)
1401 {
1402 sprintf(daemon->namebuff, "IAID=%u", opt6_uint(opt, 0, 4));
1403 optname = "ia-ta";
1404 ia_options = opt6_ptr(opt, 4);
1405 }
1406 else if (type == OPTION6_IAADDR)
1407 {
1408 inet_ntop(AF_INET6, opt6_ptr(opt, 0), daemon->addrbuff, ADDRSTRLEN);
1409 sprintf(daemon->namebuff, "%s PL=%u VL=%u",
1410 daemon->addrbuff, opt6_uint(opt, 16, 4), opt6_uint(opt, 20, 4));
1411 optname = "iaaddr";
1412 ia_options = opt6_ptr(opt, 24);
1413 }
1414 else if (type == OPTION6_STATUS_CODE)
1415 {
1416 int len = sprintf(daemon->namebuff, "%u ", opt6_uint(opt, 0, 2));
1417 memcpy(daemon->namebuff + len, opt6_ptr(opt, 2), opt6_len(opt)-2);
1418 daemon->namebuff[len + opt6_len(opt) - 2] = 0;
1419 optname = "status";
1420 }
1421 else
1422 {
1423 /* account for flag byte on FQDN */
1424 int offset = type == OPTION6_FQDN ? 1 : 0;
1425 optname = option_string(AF_INET6, type, opt6_ptr(opt, offset), opt6_len(opt) - offset, daemon->namebuff, MAXDNAME);
1426 }
1427
1428 my_syslog(MS_DHCP | LOG_INFO, "%u %s size:%3d option:%3d %s %s",
1429 xid, desc, opt6_len(opt), type, optname, daemon->namebuff);
1430
1431 if (ia_options)
1432 log6_opts(1, xid, ia_options, opt6_ptr(opt, opt6_len(opt)));
1433 }
Simon Kelley6c8f21e2012-03-12 15:06:55 +00001434}
1435
Simon Kelley4cb1b322012-02-06 14:30:41 +00001436static void log6_packet(char *type, unsigned char *clid, int clid_len, struct in6_addr *addr, int xid, char *iface, char *string)
1437{
1438 /* avoid buffer overflow */
1439 if (clid_len > 100)
1440 clid_len = 100;
1441
1442 print_mac(daemon->namebuff, clid, clid_len);
1443
1444 if (addr)
1445 {
1446 inet_ntop(AF_INET6, addr, daemon->dhcp_buff2, 255);
1447 strcat(daemon->dhcp_buff2, " ");
1448 }
1449 else
1450 daemon->dhcp_buff2[0] = 0;
1451
1452 if(option_bool(OPT_LOG_OPTS))
Simon Kelley58dc02e2012-02-27 11:49:37 +00001453 my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s %s",
Simon Kelley4cb1b322012-02-06 14:30:41 +00001454 xid,
1455 type,
1456 iface,
Simon Kelley4cb1b322012-02-06 14:30:41 +00001457 daemon->dhcp_buff2,
Simon Kelley58dc02e2012-02-27 11:49:37 +00001458 daemon->namebuff,
Simon Kelley4cb1b322012-02-06 14:30:41 +00001459 string ? string : "");
1460 else
Simon Kelley58dc02e2012-02-27 11:49:37 +00001461 my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s %s",
Simon Kelley4cb1b322012-02-06 14:30:41 +00001462 type,
1463 iface,
Simon Kelley4cb1b322012-02-06 14:30:41 +00001464 daemon->dhcp_buff2,
Simon Kelley58dc02e2012-02-27 11:49:37 +00001465 daemon->namebuff,
Simon Kelley4cb1b322012-02-06 14:30:41 +00001466 string ? string : "");
1467}
1468
1469static void *opt6_find (void *opts, void *end, unsigned int search, unsigned int minsize)
Simon Kelleyc72daea2012-01-05 21:33:27 +00001470{
1471 u16 opt, opt_len;
1472 void *start;
1473
1474 if (!opts)
1475 return NULL;
1476
1477 while (1)
1478 {
1479 if (end - opts < 4)
1480 return NULL;
1481
1482 start = opts;
1483 GETSHORT(opt, opts);
1484 GETSHORT(opt_len, opts);
1485
1486 if (opt_len > (end - opts))
1487 return NULL;
1488
1489 if (opt == search && (opt_len >= minsize))
1490 return start;
1491
1492 opts += opt_len;
1493 }
1494}
1495
Simon Kelley4cb1b322012-02-06 14:30:41 +00001496static void *opt6_next(void *opts, void *end)
Simon Kelleyc72daea2012-01-05 21:33:27 +00001497{
1498 u16 opt_len;
1499
1500 if (end - opts < 4)
1501 return NULL;
1502
1503 opts += 2;
1504 GETSHORT(opt_len, opts);
1505
1506 if (opt_len >= (end - opts))
1507 return NULL;
1508
1509 return opts + opt_len;
1510}
Simon Kelleyc72daea2012-01-05 21:33:27 +00001511
1512static unsigned int opt6_uint(unsigned char *opt, int offset, int size)
1513{
1514 /* this worries about unaligned data and byte order */
1515 unsigned int ret = 0;
1516 int i;
1517 unsigned char *p = opt6_ptr(opt, offset);
1518
1519 for (i = 0; i < size; i++)
1520 ret = (ret << 8) | *p++;
1521
1522 return ret;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001523}
Simon Kelleyc72daea2012-01-05 21:33:27 +00001524
Simon Kelleyc72daea2012-01-05 21:33:27 +00001525#endif