blob: 4eb6d2f9da9ae83f56b1ed93594545d0fa28fd5f [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 Kelley4cb1b322012-02-06 14:30:41 +000026static 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 +000027
Simon Kelley4cb1b322012-02-06 14:30:41 +000028static void *opt6_find (void *opts, void *end, unsigned int search, unsigned int minsize);
29static void *opt6_next(void *opts, void *end);
30static unsigned int opt6_uint(unsigned char *opt, int offset, int size);
Simon Kelley62779782012-02-10 21:19:25 +000031
Simon Kelley4cb1b322012-02-06 14:30:41 +000032#define opt6_len(opt) ((int)(opt6_uint(opt, -2, 2)))
33#define opt6_type(opt) (opt6_uint(opt, -4, 2))
34#define opt6_ptr(opt, i) ((void *)&(((unsigned char *)(opt))[4+(i)]))
35
36
Simon Kelleye44ddca2012-02-18 17:08:50 +000037size_t dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name,
38 struct in6_addr *fallback, size_t sz, int is_unicast, time_t now)
Simon Kelleyc72daea2012-01-05 21:33:27 +000039{
Simon Kelley4cb1b322012-02-06 14:30:41 +000040 struct dhcp_netid *relay_tags = NULL;
41 struct dhcp_vendor *vendor;
Simon Kelleyc72daea2012-01-05 21:33:27 +000042
Simon Kelley4cb1b322012-02-06 14:30:41 +000043 /* Mark these so we only match each at most once, to avoid tangled linked lists */
44 for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
45 vendor->netid.next = &vendor->netid;
Simon Kelleyc72daea2012-01-05 21:33:27 +000046
Simon Kelleyc5ad4e72012-02-24 16:06:20 +000047 save_counter(0);
Simon Kelleyc72daea2012-01-05 21:33:27 +000048
Simon Kelleye44ddca2012-02-18 17:08:50 +000049 if (dhcp6_maybe_relay(NULL, &relay_tags, context, interface, iface_name, fallback, daemon->dhcp_packet.iov_base, sz, is_unicast, now))
Simon Kelleyc5ad4e72012-02-24 16:06:20 +000050 return save_counter(0);
Simon Kelleyc72daea2012-01-05 21:33:27 +000051
52 return 0;
53}
54
Simon Kelley4cb1b322012-02-06 14:30:41 +000055/* This cost me blood to write, it will probably cost you blood to understand - srk. */
Simon Kelleyceae00d2012-02-09 21:28:14 +000056static 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 +000057 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 +000058{
59 void *end = inbuff + sz;
60 void *opts = inbuff + 34;
61 int msg_type = *((unsigned char *)inbuff);
62 unsigned char *outmsgtypep;
63 void *opt;
64 struct dhcp_vendor *vendor;
65
66 /* if not an encaplsulated relayed message, just do the stuff */
67 if (msg_type != DHCP6RELAYFORW)
68 {
69 /* if link_address != NULL if points to the link address field of the
70 innermost nested RELAYFORW message, which is where we find the
71 address of the network on which we can allocate an address.
72 Recalculate the available contexts using that information. */
73
74 if (link_address)
75 {
76 struct dhcp_context *c;
77 context = NULL;
78
79 for (c = daemon->dhcp6; c; c = c->next)
80 if (!IN6_IS_ADDR_LOOPBACK(link_address) &&
81 !IN6_IS_ADDR_LINKLOCAL(link_address) &&
82 !IN6_IS_ADDR_MULTICAST(link_address) &&
83 is_same_net6(link_address, &c->start6, c->prefix) &&
84 is_same_net6(link_address, &c->end6, c->prefix))
85 {
86 c->current = context;
87 context = c;
88 }
89
90 if (!context)
91 {
92 inet_ntop(AF_INET6, link_address, daemon->addrbuff, ADDRSTRLEN);
93 my_syslog(MS_DHCP | LOG_WARNING,
94 _("no address range available for DHCPv6 request from relay at %s"),
95 daemon->addrbuff);
96 return 0;
97 }
98 }
99
100 if (!context)
101 {
102 my_syslog(MS_DHCP | LOG_WARNING,
103 _("no address range available for DHCPv6 request via %s"), iface_name);
104 return 0;
105 }
106
Simon Kelleye44ddca2012-02-18 17:08:50 +0000107 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 +0000108 }
109
110 /* must have at least msg_type+hopcount+link_address+peer_address+minimal size option
111 which is 1 + 1 + 16 + 16 + 2 + 2 = 38 */
112 if (sz < 38)
113 return 0;
114
115 /* copy header stuff into reply message and set type to reply */
116 outmsgtypep = put_opt6(inbuff, 34);
117 *outmsgtypep = DHCP6RELAYREPL;
118
119 /* look for relay options and set tags if found. */
120 for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
121 {
122 int mopt;
123
124 if (vendor->match_type == MATCH_SUBSCRIBER)
125 mopt = OPTION6_SUBSCRIBER_ID;
126 else if (vendor->match_type == MATCH_REMOTE)
127 mopt = OPTION6_REMOTE_ID;
128 else
129 continue;
130
131 if ((opt = opt6_find(opts, end, mopt, 1)) &&
132 vendor->len == opt6_len(opt) &&
133 memcmp(vendor->data, opt6_ptr(opt, 0), vendor->len) == 0 &&
134 vendor->netid.next != &vendor->netid)
135 {
136 vendor->netid.next = *relay_tagsp;
137 *relay_tagsp = &vendor->netid;
138 break;
139 }
140 }
141
142 for (opt = opts; opt; opt = opt6_next(opt, end))
143 {
144 int o = new_opt6(opt6_type(opt));
145 if (opt6_type(opt) == OPTION6_RELAY_MSG)
146 {
147 struct in6_addr link_address;
148 /* the packet data is unaligned, copy to aligned storage */
149 memcpy(&link_address, inbuff + 2, IN6ADDRSZ);
150 /* Not, zero is_unicast since that is now known to refer to the
151 relayed packet, not the original sent by the client */
Simon Kelleye44ddca2012-02-18 17:08:50 +0000152 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 +0000153 return 0;
154 }
155 else
156 put_opt6(opt6_ptr(opt, 0), opt6_len(opt));
157 end_opt6(o);
158 }
159
160 return 1;
161}
162
Simon Kelley07933802012-02-14 20:55:25 +0000163static 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 +0000164 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 +0000165{
166 void *packet_options = inbuff + 4;
167 void *end = inbuff + sz;
168 void *opt, *oro;
169 int i, o, o1;
170 unsigned char *clid = NULL;
171 int clid_len = 0, start_opts;
172 struct dhcp_netid *tagif, *context_tags = NULL;
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000173 char *client_hostname= NULL, *hostname = NULL;
174 char *domain = NULL, *send_domain = NULL;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000175 struct dhcp_config *config = NULL;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000176 struct dhcp_netid known_id, iface_id;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000177 int done_dns = 0, hostname_auth = 0, do_encap = 0;
178 unsigned char *outmsgtypep;
179 struct dhcp_opt *opt_cfg;
180 struct dhcp_vendor *vendor;
181 struct dhcp_context *context_tmp;
Simon Kelley3634c542012-02-08 14:22:37 +0000182 unsigned int xid, ignore = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000183 unsigned int fqdn_flags = 0x01; /* default to send if we recieve no FQDN option */
184
Simon Kelleyceae00d2012-02-09 21:28:14 +0000185 /* set tag with name == interface */
186 iface_id.net = iface_name;
187 iface_id.next = tags;
188 tags = &iface_id;
189
Simon Kelley4cb1b322012-02-06 14:30:41 +0000190 /* copy over transaction-id, and save pointer to message type */
191 outmsgtypep = put_opt6(inbuff, 4);
192 start_opts = save_counter(-1);
193 xid = outmsgtypep[3] | outmsgtypep[2] << 8 | outmsgtypep[1] << 16;
194
195 /* We're going to be linking tags from all context we use.
196 mark them as unused so we don't link one twice and break the list */
197 for (context_tmp = context; context_tmp; context_tmp = context_tmp->current)
198 {
199 context->netid.next = &context->netid;
200
201 if (option_bool(OPT_LOG_OPTS))
202 {
203 inet_ntop(AF_INET6, &context_tmp->start6, daemon->dhcp_buff, ADDRSTRLEN);
204 inet_ntop(AF_INET6, &context_tmp->end6, daemon->dhcp_buff2, ADDRSTRLEN);
205 if (context_tmp->flags & (CONTEXT_STATIC))
206 my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCPv6 subnet: %s/%d"),
207 xid, daemon->dhcp_buff, context_tmp->prefix);
208 else
209 my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP range: %s -- %s"),
210 xid, daemon->dhcp_buff, daemon->dhcp_buff2);
211 }
212 }
213
214 if ((opt = opt6_find(packet_options, end, OPTION6_CLIENT_ID, 1)))
215 {
216 clid = opt6_ptr(opt, 0);
217 clid_len = opt6_len(opt);
218 o = new_opt6(OPTION6_CLIENT_ID);
219 put_opt6(clid, clid_len);
220 end_opt6(o);
221 }
222 else if (msg_type != DHCP6IREQ)
223 return 0;
224
225 /* server-id must match except for SOLICIT and CONFIRM messages */
226 if (msg_type != DHCP6SOLICIT && msg_type != DHCP6CONFIRM && msg_type != DHCP6IREQ &&
227 (!(opt = opt6_find(packet_options, end, OPTION6_SERVER_ID, 1)) ||
228 opt6_len(opt) != daemon->duid_len ||
229 memcmp(opt6_ptr(opt, 0), daemon->duid, daemon->duid_len) != 0))
230 return 0;
231
232 o = new_opt6(OPTION6_SERVER_ID);
233 put_opt6(daemon->duid, daemon->duid_len);
234 end_opt6(o);
235
236 if (is_unicast &&
237 (msg_type == DHCP6REQUEST || msg_type == DHCP6RENEW || msg_type == DHCP6RELEASE || msg_type == DHCP6DECLINE))
238
239 {
240 o1 = new_opt6(OPTION6_STATUS_CODE);
241 put_opt6_short(DHCP6USEMULTI);
242 put_opt6_string("Use multicast");
243 end_opt6(o1);
244 return 1;
245 }
246
247 /* match vendor and user class options */
248 for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
249 {
250 int mopt;
251
252 if (vendor->match_type == MATCH_VENDOR)
253 mopt = OPTION6_VENDOR_CLASS;
254 else if (vendor->match_type == MATCH_USER)
255 mopt = OPTION6_USER_CLASS;
256 else
257 continue;
258
259 if ((opt = opt6_find(packet_options, end, mopt, 2)))
260 {
261 void *enc_opt, *enc_end = opt6_ptr(opt, opt6_len(opt));
Simon Kelleya5c72ab2012-02-10 13:42:47 +0000262 int offset = 0;
263
264 if (mopt == OPTION6_VENDOR_CLASS)
265 {
266 if (opt6_len(opt) < 4)
267 continue;
268
269 if (vendor->enterprise != opt6_uint(opt, 0, 4))
270 continue;
271
272 offset = 4;
273 }
274
275 for (enc_opt = opt6_ptr(opt, offset); enc_opt; enc_opt = opt6_next(enc_opt, enc_end))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000276 for (i = 0; i <= (opt6_len(enc_opt) - vendor->len); i++)
277 if (memcmp(vendor->data, opt6_ptr(enc_opt, i), vendor->len) == 0)
278 {
279 vendor->netid.next = tags;
280 tags = &vendor->netid;
281 break;
282 }
283 }
284 }
Simon Kelley3634c542012-02-08 14:22:37 +0000285
286 /* dhcp-match. If we have hex-and-wildcards, look for a left-anchored match.
287 Otherwise assume the option is an array, and look for a matching element.
288 If no data given, existance of the option is enough. This code handles
289 V-I opts too. */
290 for (opt_cfg = daemon->dhcp_match6; opt_cfg; opt_cfg = opt_cfg->next)
291 {
Simon Kelleyceae00d2012-02-09 21:28:14 +0000292 int match = 0;
293
Simon Kelley3634c542012-02-08 14:22:37 +0000294 if (opt_cfg->flags & DHOPT_RFC3925)
295 {
296 for (opt = opt6_find(packet_options, end, OPTION6_VENDOR_OPTS, 4);
297 opt;
298 opt = opt6_find(opt6_next(opt, end), end, OPTION6_VENDOR_OPTS, 4))
299 {
300 void *vopt;
301 void *vend = opt6_ptr(opt, opt6_len(opt));
302
303 for (vopt = opt6_find(opt6_ptr(opt, 4), vend, opt_cfg->opt, 0);
304 vopt;
305 vopt = opt6_find(opt6_next(vopt, vend), vend, opt_cfg->opt, 0))
Simon Kelleyceae00d2012-02-09 21:28:14 +0000306 if ((match = match_bytes(opt_cfg, opt6_ptr(vopt, 0), opt6_len(vopt))))
Simon Kelley3634c542012-02-08 14:22:37 +0000307 break;
308 }
309 if (match)
310 break;
311 }
312 else
313 {
314 if (!(opt = opt6_find(packet_options, end, opt_cfg->opt, 1)))
315 continue;
316
317 match = match_bytes(opt_cfg, opt6_ptr(opt, 0), opt6_len(opt));
318 }
319
320 if (match)
321 {
322 opt_cfg->netid->next = tags;
323 tags = opt_cfg->netid;
324 }
325 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000326
327 if ((opt = opt6_find(packet_options, end, OPTION6_FQDN, 1)))
328 {
329 /* RFC4704 refers */
330 int len = opt6_len(opt) - 1;
331
332 fqdn_flags = opt6_uint(opt, 0, 1);
333
334 /* Always force update, since the client has no way to do it itself. */
335 if (!option_bool(OPT_FQDN_UPDATE) && !(fqdn_flags & 0x01))
336 fqdn_flags |= 0x03;
337
338 fqdn_flags &= ~0x04;
339
340 if (len != 0 && len < 255)
341 {
342 unsigned char *pp, *op = opt6_ptr(opt, 1);
343 char *pq = daemon->dhcp_buff;
344
345 pp = op;
346 while (*op != 0 && ((op + (*op) + 1) - pp) < len)
347 {
348 memcpy(pq, op+1, *op);
349 pq += *op;
350 op += (*op)+1;
351 *(pq++) = '.';
352 }
353
354 if (pq != daemon->dhcp_buff)
355 pq--;
356 *pq = 0;
357
358 if (legal_hostname(daemon->dhcp_buff))
359 {
360 client_hostname = daemon->dhcp_buff;
361 if (option_bool(OPT_LOG_OPTS))
362 my_syslog(MS_DHCP | LOG_INFO, _("%u client provides name: %s"), xid, client_hostname);
363 }
364 }
365 }
366
367 if (clid)
368 {
369 config = find_config6(daemon->dhcp_conf, context, clid, clid_len, NULL);
370
371 if (have_config(config, CONFIG_NAME))
372 {
373 hostname = config->hostname;
374 domain = config->domain;
375 hostname_auth = 1;
376 }
377 else if (client_hostname)
378 {
379 domain = strip_hostname(client_hostname);
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000380
Simon Kelley4cb1b322012-02-06 14:30:41 +0000381 if (strlen(client_hostname) != 0)
382 {
383 hostname = client_hostname;
384 if (!config)
385 {
386 /* Search again now we have a hostname.
Simon Kelley00e9ad52012-02-16 21:53:11 +0000387 Only accept configs without CLID here, (it won't match)
Simon Kelley4cb1b322012-02-06 14:30:41 +0000388 to avoid impersonation by name. */
389 struct dhcp_config *new = find_config6(daemon->dhcp_conf, context, NULL, 0, hostname);
390 if (new && !have_config(new, CONFIG_CLID) && !new->hwaddr)
391 config = new;
392 }
393 }
394 }
395 }
396
397 if (config)
398 {
399 struct dhcp_netid_list *list;
400
401 for (list = config->netid; list; list = list->next)
402 {
403 list->list->next = tags;
404 tags = list->list;
405 }
406
407 /* set "known" tag for known hosts */
408 known_id.net = "known";
409 known_id.next = tags;
410 tags = &known_id;
Simon Kelley3634c542012-02-08 14:22:37 +0000411
412 if (have_config(config, CONFIG_DISABLE))
413 ignore = 1;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000414 }
Simon Kelley00e9ad52012-02-16 21:53:11 +0000415
416 tagif = run_tag_if(tags);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000417
Simon Kelley3634c542012-02-08 14:22:37 +0000418 /* if all the netids in the ignore list are present, ignore this client */
419 if (daemon->dhcp_ignore)
420 {
421 struct dhcp_netid_list *id_list;
422
Simon Kelley3634c542012-02-08 14:22:37 +0000423 for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
424 if (match_netid(id_list->list, tagif, 0))
425 ignore = 1;
426 }
Simon Kelley00e9ad52012-02-16 21:53:11 +0000427
428 /* if all the netids in the ignore_name list are present, ignore client-supplied name */
429 if (!hostname_auth)
430 {
431 struct dhcp_netid_list *id_list;
432
433 for (id_list = daemon->dhcp_ignore_names; id_list; id_list = id_list->next)
434 if ((!id_list->list) || match_netid(id_list->list, tagif, 0))
435 break;
436 if (id_list)
437 hostname = NULL;
438 }
439
Simon Kelley4cb1b322012-02-06 14:30:41 +0000440
441 switch (msg_type)
442 {
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000443 default:
444 return 0;
445
Simon Kelley4cb1b322012-02-06 14:30:41 +0000446 case DHCP6SOLICIT:
447 case DHCP6REQUEST:
448 {
449 void *rapid_commit = opt6_find(packet_options, end, OPTION6_RAPID_COMMIT, 0);
450 int make_lease = (msg_type == DHCP6REQUEST || rapid_commit);
451 int serial = 0, used_config = 0;
452
453 if (rapid_commit)
454 {
455 o = new_opt6(OPTION6_RAPID_COMMIT);
456 end_opt6(o);
457 }
458
459 /* set reply message type */
460 *outmsgtypep = make_lease ? DHCP6REPLY : DHCP6ADVERTISE;
461
462 log6_packet(msg_type == DHCP6SOLICIT ? "DHCPSOLICIT" : "DHCPREQUEST",
Simon Kelley3634c542012-02-08 14:22:37 +0000463 clid, clid_len, NULL, xid, iface_name, ignore ? "ignored" : NULL);
464
465 if (ignore)
466 return 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000467
468 for (opt = packet_options; opt; opt = opt6_next(opt, end))
469 {
470 int iaid, ia_type = opt6_type(opt);
471 void *ia_option, *ia_end;
472 unsigned int min_time = 0xffffffff;
Simon Kelley98d76a02012-02-10 22:16:45 +0000473 int t1cntr = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000474 int address_assigned = 0;
475
476 if (ia_type != OPTION6_IA_NA && ia_type != OPTION6_IA_TA)
477 continue;
478
479 if (ia_type == OPTION6_IA_NA && opt6_len(opt) < 12)
480 continue;
481
482 if (ia_type == OPTION6_IA_TA && opt6_len(opt) < 4)
483 continue;
484
485 iaid = opt6_uint(opt, 0, 4);
486 ia_end = opt6_ptr(opt, opt6_len(opt));
487 ia_option = opt6_find(opt6_ptr(opt, ia_type == OPTION6_IA_NA ? 12 : 4), ia_end, OPTION6_IAADDR, 24);
488
489 /* reset "USED" flags on leases */
490 lease6_find(NULL, 0, ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, iaid, NULL);
491
492 o = new_opt6(ia_type);
493 put_opt6_long(iaid);
494 if (ia_type == OPTION6_IA_NA)
495 {
496 /* save pointer */
497 t1cntr = save_counter(-1);
498 /* so we can fill these in later */
499 put_opt6_long(0);
500 put_opt6_long(0);
501 }
502
503 while (1)
504 {
505 struct in6_addr alloced_addr, *addrp = NULL;
506 u32 preferred_time = 0;
507 struct dhcp_lease *lease = NULL;
508
509 if (ia_option)
510 {
511 struct in6_addr *req_addr = opt6_ptr(ia_option, 0);
512 preferred_time = opt6_uint(ia_option, 16, 4);
513
Simon Kelley00e9ad52012-02-16 21:53:11 +0000514 if (!address6_available(context, req_addr, tags) &&
515 (!have_config(config, CONFIG_ADDR6) || memcmp(&config->addr6, req_addr, IN6ADDRSZ) != 0))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000516 {
517 if (msg_type == DHCP6REQUEST)
518 {
519 /* host has a lease, but it's not on the correct link */
520 o1 = new_opt6(OPTION6_STATUS_CODE);
521 put_opt6_short(DHCP6NOTONLINK);
522 put_opt6_string("Not on link");
523 end_opt6(o1);
524 }
525 }
526 else if ((lease = lease6_find(NULL, 0, ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA,
527 iaid, req_addr)) &&
528 (clid_len != lease->clid_len ||
529 memcmp(clid, lease->clid, clid_len) != 0))
530 {
531 /* Address leased to another DUID */
532 o1 = new_opt6(OPTION6_STATUS_CODE);
533 put_opt6_short(DHCP6UNSPEC);
534 put_opt6_string("Address in use");
535 end_opt6(o1);
536 }
537 else
538 addrp = req_addr;
539 }
540 else
541 {
542 /* must have an address to CONFIRM */
543 if (msg_type == DHCP6REQUEST && ia_type == OPTION6_IA_NA)
544 return 0;
545
546 /* Don't used configured addresses for temporary leases. */
547 if (have_config(config, CONFIG_ADDR6) && !used_config && ia_type == OPTION6_IA_NA)
548 {
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000549 struct dhcp_lease *ltmp = lease6_find_by_addr(&config->addr6, 128, 0);
550
Simon Kelley4cb1b322012-02-06 14:30:41 +0000551 used_config = 1;
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000552 inet_ntop(AF_INET6, &config->addr6, daemon->addrbuff, ADDRSTRLEN);
553
554 if (ltmp && ltmp->clid &&
555 (ltmp->clid_len != clid_len || memcmp(ltmp->clid, clid, clid_len) != 0))
556 my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is leased to %s"),
557 daemon->addrbuff, print_mac(daemon->namebuff, ltmp->clid, ltmp->clid_len));
558 else if (have_config(config, CONFIG_DECLINED) &&
559 difftime(now, config->decline_time) < (float)DECLINE_BACKOFF)
560 my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it was previously declined"),
561 daemon->addrbuff);
562 else
563 addrp = &config->addr6;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000564 }
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000565
Simon Kelley4cb1b322012-02-06 14:30:41 +0000566 /* existing lease */
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000567 if (!addrp &&
568 (lease = lease6_find(clid, clid_len,
569 ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, iaid, NULL)) &&
570 address6_available(context, (struct in6_addr *)&lease->hwaddr, tags) &&
Simon Kelleyceae00d2012-02-09 21:28:14 +0000571 !config_find_by_address6(daemon->dhcp_conf, (struct in6_addr *)&lease->hwaddr, 128, 0))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000572 addrp = (struct in6_addr *)&lease->hwaddr;
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000573
574 if (!addrp && address6_allocate(context, clid, clid_len, serial++, tags, &alloced_addr))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000575 addrp = &alloced_addr;
576 }
577
578 if (addrp)
579 {
580 unsigned int lease_time;
581 struct dhcp_context *this_context;
582 struct dhcp_config *valid_config = config;
583
584 /* don't use a config to set lease time if it specifies an address which isn't this. */
585 if (have_config(config, CONFIG_ADDR6) && memcmp(&config->addr6, addrp, IN6ADDRSZ) != 0)
586 valid_config = NULL;
587
588 address_assigned = 1;
589
590 /* shouldn't ever fail */
Simon Kelley00e9ad52012-02-16 21:53:11 +0000591 if ((this_context = narrow_context6(context, addrp, tagif)))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000592 {
593 /* get tags from context if we've not used it before */
Simon Kelley00e9ad52012-02-16 21:53:11 +0000594 if (this_context->netid.next == &this_context->netid && this_context->netid.net)
Simon Kelley4cb1b322012-02-06 14:30:41 +0000595 {
596 this_context->netid.next = context_tags;
597 context_tags = &this_context->netid;
Simon Kelley00e9ad52012-02-16 21:53:11 +0000598 if (!hostname_auth)
599 {
600 struct dhcp_netid_list *id_list;
601
602 for (id_list = daemon->dhcp_ignore_names; id_list; id_list = id_list->next)
603 if ((!id_list->list) || match_netid(id_list->list, &this_context->netid, 0))
604 break;
605 if (id_list)
606 hostname = NULL;
607 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000608 }
609
610 lease_time = have_config(valid_config, CONFIG_TIME) ? valid_config->lease_time : this_context->lease_time;
611
612 if (ia_option)
613 {
614 if (preferred_time < 120u )
615 preferred_time = 120u; /* sanity */
616 if (lease_time == 0xffffffff || (preferred_time != 0xffffffff && preferred_time < lease_time))
617 lease_time = preferred_time;
618 }
619
620 if (lease_time < min_time)
621 min_time = lease_time;
622
623 /* May fail to create lease */
624 if (!lease && make_lease)
625 lease = lease6_allocate(addrp, ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA);
626
627 if (lease)
628 {
629 lease_set_expires(lease, lease_time, now);
630 lease_set_hwaddr(lease, NULL, clid, 0, iaid, clid_len);
Simon Kelleyceae00d2012-02-09 21:28:14 +0000631 lease_set_interface(lease, interface);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000632 if (hostname && ia_type == OPTION6_IA_NA)
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000633 {
634 char *addr_domain = get_domain6(addrp);
635 if (!send_domain)
636 send_domain = addr_domain;
637 lease_set_hostname(lease, hostname, hostname_auth, addr_domain, domain);
638 }
Simon Kelleyceae00d2012-02-09 21:28:14 +0000639
640#ifdef HAVE_SCRIPT
641 if (daemon->lease_change_command)
642 {
643 void *class_opt;
644 lease->flags |= LEASE_CHANGED;
645 free(lease->extradata);
646 lease->extradata = NULL;
647 lease->extradata_size = lease->extradata_len = 0;
648 lease->hwaddr_len = 0; /* surrogate for no of vendor classes */
649
Simon Kelleya5c72ab2012-02-10 13:42:47 +0000650 if ((class_opt = opt6_find(packet_options, end, OPTION6_VENDOR_CLASS, 4)))
Simon Kelleyceae00d2012-02-09 21:28:14 +0000651 {
652 void *enc_opt, *enc_end = opt6_ptr(class_opt, opt6_len(class_opt));
Simon Kelleya5c72ab2012-02-10 13:42:47 +0000653 lease->hwaddr_len++;
654 /* send enterprise number first */
655 sprintf(daemon->dhcp_buff2, "%u", opt6_uint(class_opt, 0, 4));
656 lease_add_extradata(lease, (unsigned char *)daemon->dhcp_buff2, strlen(daemon->dhcp_buff2), 0);
657
658 if (opt6_len(class_opt) >= 6)
659 for (enc_opt = opt6_ptr(class_opt, 4); enc_opt; enc_opt = opt6_next(enc_opt, enc_end))
660 {
661 lease->hwaddr_len++;
662 lease_add_extradata(lease, opt6_ptr(enc_opt, 0), opt6_len(enc_opt), 0);
663 }
Simon Kelleyceae00d2012-02-09 21:28:14 +0000664 }
665
666 lease_add_extradata(lease, (unsigned char *)client_hostname,
667 client_hostname ? strlen(client_hostname) : 0, 0);
668
669 /* space-concat tag set */
Simon Kelley00e9ad52012-02-16 21:53:11 +0000670 if (!tagif && !context_tags)
Simon Kelleyceae00d2012-02-09 21:28:14 +0000671 lease_add_extradata(lease, NULL, 0, 0);
672 else
673 {
Simon Kelley87b8ecb2012-02-18 21:20:43 +0000674 struct dhcp_netid *n, *l, *tmp = tags;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000675
Simon Kelley00e9ad52012-02-16 21:53:11 +0000676 /* link temporarily */
677 for (n = context_tags; n && n->next; n = n->next);
Simon Kelleye44ddca2012-02-18 17:08:50 +0000678 if ((l = n))
Simon Kelley87b8ecb2012-02-18 21:20:43 +0000679 {
680 l->next = tags;
681 tmp = context_tags;
682 }
Simon Kelley00e9ad52012-02-16 21:53:11 +0000683
Simon Kelley87b8ecb2012-02-18 21:20:43 +0000684 for (n = run_tag_if(tmp); n; n = n->next)
Simon Kelleyceae00d2012-02-09 21:28:14 +0000685 {
686 struct dhcp_netid *n1;
687 /* kill dupes */
688 for (n1 = n->next; n1; n1 = n1->next)
689 if (strcmp(n->net, n1->net) == 0)
690 break;
691 if (!n1)
692 lease_add_extradata(lease, (unsigned char *)n->net, strlen(n->net), n->next ? ' ' : 0);
693 }
Simon Kelley00e9ad52012-02-16 21:53:11 +0000694
695 /* unlink again */
696 if (l)
697 l->next = NULL;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000698 }
Simon Kelley07933802012-02-14 20:55:25 +0000699
700 if (link_address)
701 inet_ntop(AF_INET6, link_address, daemon->addrbuff, ADDRSTRLEN);
702
703 lease_add_extradata(lease, (unsigned char *)daemon->addrbuff, link_address ? strlen(daemon->addrbuff) : 0, 0);
Simon Kelleyceae00d2012-02-09 21:28:14 +0000704
705 if ((class_opt = opt6_find(packet_options, end, OPTION6_USER_CLASS, 2)))
706 {
707 void *enc_opt, *enc_end = opt6_ptr(class_opt, opt6_len(class_opt));
708 for (enc_opt = opt6_ptr(class_opt, 0); enc_opt; enc_opt = opt6_next(enc_opt, enc_end))
709 lease_add_extradata(lease, opt6_ptr(enc_opt, 0), opt6_len(enc_opt), 0);
710 }
711 }
712#endif
713
Simon Kelley4cb1b322012-02-06 14:30:41 +0000714 }
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000715 else if (!send_domain)
716 send_domain = get_domain6(addrp);
717
Simon Kelley4cb1b322012-02-06 14:30:41 +0000718
719 if (lease || !make_lease)
720 {
721 o1 = new_opt6(OPTION6_IAADDR);
722 put_opt6(addrp, sizeof(*addrp));
723 put_opt6_long(lease_time);
724 put_opt6_long(lease_time);
725 end_opt6(o1);
726
727 log6_packet( make_lease ? "DHCPREPLY" : "DHCPADVERTISE",
728 clid, clid_len, addrp, xid, iface_name, hostname);
729 }
730
731 }
732 }
733
734
735 if (!ia_option ||
736 !(ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24)))
737 {
738 if (address_assigned)
739 {
740 if (ia_type == OPTION6_IA_NA)
741 {
742 /* go back an fill in fields in IA_NA option */
743 unsigned int t1 = min_time == 0xffffffff ? 0xffffffff : min_time/2;
744 unsigned int t2 = min_time == 0xffffffff ? 0xffffffff : (min_time/8) * 7;
745 int sav = save_counter(t1cntr);
746 put_opt6_long(t1);
747 put_opt6_long(t2);
748 save_counter(sav);
749 }
750 }
751 else
752 {
753 /* no address, return erro */
754 o1 = new_opt6(OPTION6_STATUS_CODE);
755 put_opt6_short(DHCP6NOADDRS);
756 put_opt6_string("No addresses available");
757 end_opt6(o1);
758 }
759
760 end_opt6(o);
761
762 break;
763 }
764 }
765 }
766
767 break;
768 }
769
770 case DHCP6RENEW:
771 {
772 /* set reply message type */
773 *outmsgtypep = DHCP6REPLY;
774
775 log6_packet("DHCPRENEW", clid, clid_len, NULL, xid, iface_name, NULL);
776
777 for (opt = packet_options; opt; opt = opt6_next(opt, end))
778 {
779 int ia_type = opt6_type(opt);
780 void *ia_option, *ia_end;
781 unsigned int min_time = 0xffffffff;
782 int t1cntr = 0, iacntr;
783 unsigned int iaid;
784
785 if (ia_type != OPTION6_IA_NA && ia_type != OPTION6_IA_TA)
786 continue;
787
788 if (ia_type == OPTION6_IA_NA && opt6_len(opt) < 12)
789 continue;
790
791 if (ia_type == OPTION6_IA_TA && opt6_len(opt) < 4)
792 continue;
793
794 iaid = opt6_uint(opt, 0, 4);
795
796 o = new_opt6(ia_type);
797 put_opt6_long(iaid);
798 if (ia_type == OPTION6_IA_NA)
799 {
800 /* save pointer */
801 t1cntr = save_counter(-1);
802 /* so we can fill these in later */
803 put_opt6_long(0);
804 put_opt6_long(0);
805 }
806
807 iacntr = save_counter(-1);
808
809 /* reset "USED" flags on leases */
810 lease6_find(NULL, 0, ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, iaid, NULL);
811
812 ia_option = opt6_ptr(opt, ia_type == OPTION6_IA_NA ? 12 : 4);
813 ia_end = opt6_ptr(opt, opt6_len(opt));
814
815 for (ia_option = opt6_find(ia_option, ia_end, OPTION6_IAADDR, 24);
816 ia_option;
817 ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
818 {
819 struct dhcp_lease *lease = NULL;
820 struct in6_addr *req_addr = opt6_ptr(ia_option, 0);
821 u32 preferred_time = opt6_uint(ia_option, 16, 4);
822 unsigned int lease_time;
823 struct dhcp_context *this_context;
824 struct dhcp_config *valid_config = config;
825
826 /* don't use a config to set lease time if it specifies an address which isn't this. */
827 if (have_config(config, CONFIG_ADDR6) && memcmp(&config->addr6, req_addr, IN6ADDRSZ) != 0)
828 valid_config = NULL;
829
830 if (!(lease = lease6_find(clid, clid_len,
831 ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA,
832 iaid, req_addr)))
833 {
834 /* If the server cannot find a client entry for the IA the server
835 returns the IA containing no addresses with a Status Code option set
836 to NoBinding in the Reply message. */
837 save_counter(iacntr);
838 t1cntr = 0;
839
840 log6_packet("DHCPREPLY", clid, clid_len, req_addr, xid, iface_name, "lease not found");
841
842 o1 = new_opt6(OPTION6_STATUS_CODE);
843 put_opt6_short(DHCP6NOBINDING);
844 put_opt6_string("No binding found");
845 end_opt6(o1);
846 break;
847 }
848
Simon Kelley00e9ad52012-02-16 21:53:11 +0000849
Simon Kelleyceae00d2012-02-09 21:28:14 +0000850 if (!address6_available(context, req_addr, tagif) ||
851 !(this_context = narrow_context6(context, req_addr, tagif)))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000852 lease_time = 0;
853 else
854 {
855 /* get tags from context if we've not used it before */
Simon Kelley00e9ad52012-02-16 21:53:11 +0000856 if (this_context->netid.next == &this_context->netid && this_context->netid.net)
Simon Kelley4cb1b322012-02-06 14:30:41 +0000857 {
858 this_context->netid.next = context_tags;
859 context_tags = &this_context->netid;
Simon Kelley00e9ad52012-02-16 21:53:11 +0000860 if (!hostname_auth)
861 {
862 struct dhcp_netid_list *id_list;
863
864 for (id_list = daemon->dhcp_ignore_names; id_list; id_list = id_list->next)
865 if ((!id_list->list) || match_netid(id_list->list, &this_context->netid, 0))
866 break;
867 if (id_list)
868 hostname = NULL;
869 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000870 }
871
872 lease_time = have_config(valid_config, CONFIG_TIME) ? valid_config->lease_time : this_context->lease_time;
873
874 if (preferred_time < 120u )
875 preferred_time = 120u; /* sanity */
876 if (lease_time == 0xffffffff || (preferred_time != 0xffffffff && preferred_time < lease_time))
877 lease_time = preferred_time;
878
879 lease_set_expires(lease, lease_time, now);
880 if (ia_type == OPTION6_IA_NA && hostname)
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000881 {
882 char *addr_domain = get_domain6(req_addr);
883 if (!send_domain)
884 send_domain = addr_domain;
885 lease_set_hostname(lease, hostname, hostname_auth, addr_domain, domain);
886 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000887
888 if (lease_time < min_time)
889 min_time = lease_time;
890 }
891
892 log6_packet("DHCPREPLY", clid, clid_len, req_addr, xid, iface_name, hostname);
893
894 o1 = new_opt6(OPTION6_IAADDR);
895 put_opt6(req_addr, sizeof(*req_addr));
896 put_opt6_long(lease_time);
897 put_opt6_long(lease_time);
898 end_opt6(o1);
899 }
900
901 if (t1cntr != 0)
902 {
903 /* go back an fill in fields in IA_NA option */
904 unsigned int t1 = min_time == 0xffffffff ? 0xffffffff : min_time/2;
905 unsigned int t2 = min_time == 0xffffffff ? 0xffffffff : (min_time/8) * 7;
906 int sav = save_counter(t1cntr);
907 put_opt6_long(t1);
908 put_opt6_long(t2);
909 save_counter(sav);
910 }
911
912 end_opt6(o);
913 }
914 break;
915
916 }
917
918 case DHCP6CONFIRM:
919 {
920 /* set reply message type */
921 *outmsgtypep = DHCP6REPLY;
922
923 log6_packet("DHCPCONFIRM", clid, clid_len, NULL, xid, iface_name, NULL);
924
925 for (opt = packet_options; opt; opt = opt6_next(opt, end))
926 {
927 int ia_type = opt6_type(opt);
928 void *ia_option, *ia_end;
929
930 if (ia_type != OPTION6_IA_NA && ia_type != OPTION6_IA_TA)
931 continue;
932
933 if (ia_type == OPTION6_IA_NA && opt6_len(opt) < 12)
934 continue;
935
936 if (ia_type == OPTION6_IA_TA && opt6_len(opt) < 4)
937 continue;
938
939 ia_option = opt6_ptr(opt, ia_type == OPTION6_IA_NA ? 12 : 4);
940 ia_end = opt6_ptr(opt, opt6_len(opt));
941
942 for (ia_option = opt6_find(ia_option, ia_end, OPTION6_IAADDR, 24);
943 ia_option;
944 ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
945 {
946 struct in6_addr *req_addr = opt6_ptr(ia_option, 0);
947
Simon Kelleyceae00d2012-02-09 21:28:14 +0000948 if (!address6_available(context, req_addr, run_tag_if(tags)))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000949 {
950 o1 = new_opt6(OPTION6_STATUS_CODE);
951 put_opt6_short(DHCP6NOTONLINK);
952 put_opt6_string("Confirm failed");
953 end_opt6(o1);
954 return 1;
955 }
956
957 log6_packet("DHCPREPLY", clid, clid_len, req_addr, xid, iface_name, hostname);
958 }
959 }
960
961 o1 = new_opt6(OPTION6_STATUS_CODE);
962 put_opt6_short(DHCP6SUCCESS );
963 put_opt6_string("All addresses still on link");
964 end_opt6(o1);
965 return 1;
966 }
967
968 case DHCP6IREQ:
969 {
Simon Kelley3634c542012-02-08 14:22:37 +0000970 if (ignore)
971 return 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000972 *outmsgtypep = DHCP6REPLY;
Simon Kelley62779782012-02-10 21:19:25 +0000973 log6_packet("DHCPINFORMATION-REQUEST", clid, clid_len, NULL, xid, iface_name, hostname);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000974 break;
975 }
976
977
978 case DHCP6RELEASE:
979 {
980 /* set reply message type */
981 *outmsgtypep = DHCP6REPLY;
982
983 log6_packet("DHCPRELEASE", clid, clid_len, NULL, xid, iface_name, NULL);
984
985 for (opt = packet_options; opt; opt = opt6_next(opt, end))
986 {
987 int iaid, ia_type = opt6_type(opt);
988 void *ia_option, *ia_end;
989 int made_ia = 0;
990
991 if (ia_type != OPTION6_IA_NA && ia_type != OPTION6_IA_TA)
992 continue;
993
994 if (ia_type == OPTION6_IA_NA && opt6_len(opt) < 12)
995 continue;
996
997 if (ia_type == OPTION6_IA_TA && opt6_len(opt) < 4)
998 continue;
999
1000 iaid = opt6_uint(opt, 0, 4);
1001 ia_end = opt6_ptr(opt, opt6_len(opt));
1002 ia_option = opt6_ptr(opt, ia_type == OPTION6_IA_NA ? 12 : 4);
1003
1004 /* reset "USED" flags on leases */
1005 lease6_find(NULL, 0, ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, iaid, NULL);
1006
1007 for (ia_option = opt6_find(ia_option, ia_end, OPTION6_IAADDR, 24);
1008 ia_option;
1009 ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
1010 {
1011 struct dhcp_lease *lease;
1012
1013 if ((lease = lease6_find(clid, clid_len, ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA,
1014 iaid, opt6_ptr(ia_option, 0))))
1015 lease_prune(lease, now);
1016 else
1017 {
1018 if (!made_ia)
1019 {
1020 o = new_opt6(ia_type);
1021 put_opt6_long(iaid);
1022 if (ia_type == OPTION6_IA_NA)
1023 {
1024 put_opt6_long(0);
1025 put_opt6_long(0);
1026 }
1027 made_ia = 1;
1028 }
1029
1030 o1 = new_opt6(OPTION6_IAADDR);
1031 put_opt6(opt6_ptr(ia_option, 0), IN6ADDRSZ);
1032 put_opt6_long(0);
1033 put_opt6_long(0);
1034 end_opt6(o1);
1035 }
1036 }
1037
1038 if (made_ia)
1039 {
1040 o1 = new_opt6(OPTION6_STATUS_CODE);
1041 put_opt6_short(DHCP6NOBINDING);
1042 put_opt6_string("No binding found");
1043 end_opt6(o1);
1044
1045 end_opt6(o);
1046 }
1047 }
1048
1049 o1 = new_opt6(OPTION6_STATUS_CODE);
1050 put_opt6_short(DHCP6SUCCESS);
1051 put_opt6_string("Release received");
1052 end_opt6(o1);
1053
1054 return 1;
1055 }
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001056
1057 case DHCP6DECLINE:
1058 {
1059 /* set reply message type */
1060 *outmsgtypep = DHCP6REPLY;
1061
1062 log6_packet("DHCPDECLINE", clid, clid_len, NULL, xid, iface_name, NULL);
1063
1064 for (opt = packet_options; opt; opt = opt6_next(opt, end))
1065 {
1066 int iaid, ia_type = opt6_type(opt);
1067 void *ia_option, *ia_end;
1068 int made_ia = 0;
1069
1070 if (ia_type != OPTION6_IA_NA && ia_type != OPTION6_IA_TA)
1071 continue;
1072
1073 if (ia_type == OPTION6_IA_NA && opt6_len(opt) < 12)
1074 continue;
1075
1076 if (ia_type == OPTION6_IA_TA && opt6_len(opt) < 4)
1077 continue;
1078
1079 iaid = opt6_uint(opt, 0, 4);
1080 ia_end = opt6_ptr(opt, opt6_len(opt));
1081 ia_option = opt6_ptr(opt, ia_type == OPTION6_IA_NA ? 12 : 4);
1082
1083 /* reset "USED" flags on leases */
1084 lease6_find(NULL, 0, ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, iaid, NULL);
1085
1086 for (ia_option = opt6_find(ia_option, ia_end, OPTION6_IAADDR, 24);
1087 ia_option;
1088 ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
1089 {
1090 struct dhcp_lease *lease;
1091 struct in6_addr *addrp = opt6_ptr(ia_option, 0);
1092
1093 if (have_config(config, CONFIG_ADDR6) &&
1094 memcmp(&config->addr6, addrp, IN6ADDRSZ) == 0)
1095 {
Simon Kelleyceae00d2012-02-09 21:28:14 +00001096 prettyprint_time(daemon->dhcp_buff3, DECLINE_BACKOFF);
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001097 inet_ntop(AF_INET6, addrp, daemon->addrbuff, ADDRSTRLEN);
1098 my_syslog(MS_DHCP | LOG_WARNING, _("disabling DHCP static address %s for %s"),
Simon Kelleyceae00d2012-02-09 21:28:14 +00001099 daemon->addrbuff, daemon->dhcp_buff3);
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001100 config->flags |= CONFIG_DECLINED;
1101 config->decline_time = now;
1102 }
1103 else
1104 /* make sure this host gets a different address next time. */
1105 for (; context; context = context->current)
1106 context->addr_epoch++;
1107
1108 if ((lease = lease6_find(clid, clid_len, ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA,
1109 iaid, opt6_ptr(ia_option, 0))))
1110 lease_prune(lease, now);
1111 else
1112 {
1113 if (!made_ia)
1114 {
1115 o = new_opt6(ia_type);
1116 put_opt6_long(iaid);
1117 if (ia_type == OPTION6_IA_NA)
1118 {
1119 put_opt6_long(0);
1120 put_opt6_long(0);
1121 }
1122 made_ia = 1;
1123 }
1124
1125 o1 = new_opt6(OPTION6_IAADDR);
1126 put_opt6(opt6_ptr(ia_option, 0), IN6ADDRSZ);
1127 put_opt6_long(0);
1128 put_opt6_long(0);
1129 end_opt6(o1);
1130 }
1131 }
1132
1133 if (made_ia)
1134 {
1135 o1 = new_opt6(OPTION6_STATUS_CODE);
1136 put_opt6_short(DHCP6NOBINDING);
1137 put_opt6_string("No binding found");
1138 end_opt6(o1);
1139
1140 end_opt6(o);
1141 }
1142
1143 }
1144 return 1;
1145 }
1146
Simon Kelley4cb1b322012-02-06 14:30:41 +00001147 }
1148
1149
1150 /* filter options based on tags, those we want get DHOPT_TAGOK bit set */
1151 tagif = option_filter(tags, context_tags, daemon->dhcp_opts6);
1152
1153 oro = opt6_find(packet_options, end, OPTION6_ORO, 0);
1154
1155 for (opt_cfg = daemon->dhcp_opts6; opt_cfg; opt_cfg = opt_cfg->next)
1156 {
1157 /* netids match and not encapsulated? */
1158 if (!(opt_cfg->flags & DHOPT_TAGOK))
1159 continue;
1160
1161 if (!(opt_cfg->flags & DHOPT_FORCE) && oro)
1162 {
1163 for (i = 0; i < opt6_len(oro) - 1; i += 2)
1164 if (opt6_uint(oro, i, 2) == (unsigned)opt_cfg->opt)
1165 break;
1166
1167 /* option not requested */
1168 if (i >= opt6_len(oro) - 1)
1169 continue;
1170 }
1171
1172 if (opt_cfg->opt == OPTION6_DNS_SERVER)
1173 {
1174 done_dns = 1;
1175 if (opt_cfg->len == 0)
1176 continue;
1177 }
1178
1179 o = new_opt6(opt_cfg->opt);
1180 /* Maye be empty */
1181 if (opt_cfg->val)
1182 put_opt6(opt_cfg->val, opt_cfg->len);
1183 end_opt6(o);
1184
1185 }
1186
Simon Kelleye44ddca2012-02-18 17:08:50 +00001187 if (!done_dns &&
1188 (!IN6_IS_ADDR_UNSPECIFIED(&context->local6) ||
1189 !IN6_IS_ADDR_UNSPECIFIED(fallback)))
Simon Kelley4cb1b322012-02-06 14:30:41 +00001190 {
1191 o = new_opt6(OPTION6_DNS_SERVER);
Simon Kelleye44ddca2012-02-18 17:08:50 +00001192 if (IN6_IS_ADDR_UNSPECIFIED(&context->local6))
1193 put_opt6(fallback, IN6ADDRSZ);
1194 else
1195 put_opt6(&context->local6, IN6ADDRSZ);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001196 end_opt6(o);
1197 }
1198
1199 /* handle vendor-identifying vendor-encapsulated options,
1200 dhcp-option = vi-encap:13,17,....... */
1201 for (opt_cfg = daemon->dhcp_opts6; opt_cfg; opt_cfg = opt_cfg->next)
1202 opt_cfg->flags &= ~DHOPT_ENCAP_DONE;
1203
1204
1205 if (oro)
1206 for (i = 0; i < opt6_len(oro) - 1; i += 2)
1207 if (opt6_uint(oro, i, 2) == OPTION6_VENDOR_OPTS)
1208 do_encap = 1;
1209
1210 for (opt_cfg = daemon->dhcp_opts6; opt_cfg; opt_cfg = opt_cfg->next)
1211 {
1212 if (opt_cfg->flags & DHOPT_RFC3925)
1213 {
1214 int found = 0;
1215 struct dhcp_opt *oc;
1216
1217 if (opt_cfg->flags & DHOPT_ENCAP_DONE)
1218 continue;
1219
1220 for (oc = daemon->dhcp_opts6; oc; oc = oc->next)
1221 {
1222 oc->flags &= ~DHOPT_ENCAP_MATCH;
1223
1224 if (!(oc->flags & DHOPT_RFC3925) || opt_cfg->u.encap != oc->u.encap)
1225 continue;
1226
1227 oc->flags |= DHOPT_ENCAP_DONE;
1228 if (match_netid(oc->netid, tagif, 1))
1229 {
1230 /* option requested/forced? */
1231 if (!oro || do_encap || (oc->flags & DHOPT_FORCE))
1232 {
1233 oc->flags |= DHOPT_ENCAP_MATCH;
1234 found = 1;
1235 }
1236 }
1237 }
1238
1239 if (found)
1240 {
1241 o = new_opt6(OPTION6_VENDOR_OPTS);
1242 put_opt6_long(opt_cfg->u.encap);
1243
1244 for (oc = daemon->dhcp_opts6; oc; oc = oc->next)
1245 if (oc->flags & DHOPT_ENCAP_MATCH)
1246 {
1247 o1 = new_opt6(oc->opt);
1248 put_opt6(oc->val, oc->len);
1249 end_opt6(o1);
1250 }
1251 end_opt6(o);
1252 }
1253 }
1254 }
Simon Kelley07933802012-02-14 20:55:25 +00001255
1256
Simon Kelley4cb1b322012-02-06 14:30:41 +00001257 if (hostname)
1258 {
1259 unsigned char *p;
1260 size_t len = strlen(hostname);
1261
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001262 if (send_domain)
1263 len += strlen(send_domain) + 1;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001264
1265 o = new_opt6(OPTION6_FQDN);
Simon Kelleyc5ad4e72012-02-24 16:06:20 +00001266 if ((p = expand(len + 3)))
1267 {
1268 *(p++) = fqdn_flags;
1269 p = do_rfc1035_name(p, hostname);
1270 if (send_domain)
1271 p = do_rfc1035_name(p, send_domain);
1272 *p = 0;
1273 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001274 end_opt6(o);
1275 }
1276
1277
1278 /* logging */
1279 if (option_bool(OPT_LOG_OPTS) && oro)
1280 {
1281 char *q = daemon->namebuff;
1282 for (i = 0; i < opt6_len(oro) - 1; i += 2)
1283 {
1284 char *s = option_string(AF_INET6, opt6_uint(oro, i, 2), NULL, 0, NULL, 0);
1285 q += snprintf(q, MAXDNAME - (q - daemon->namebuff),
1286 "%d%s%s%s",
1287 opt6_uint(oro, i, 2),
1288 strlen(s) != 0 ? ":" : "",
1289 s,
1290 (i > opt6_len(oro) - 3) ? "" : ", ");
1291 if ( i > opt6_len(oro) - 3 || (q - daemon->namebuff) > 40)
1292 {
1293 q = daemon->namebuff;
1294 my_syslog(MS_DHCP | LOG_INFO, _("%u requested options: %s"), xid, daemon->namebuff);
1295 }
1296 }
1297 }
1298
1299 log_tags(tagif, xid);
1300
1301 if (option_bool(OPT_LOG_OPTS))
1302 {
1303 int end_opts = save_counter(-1);
1304
1305 /* must have created at least one option */
1306 if (start_opts != end_opts)
1307 for (opt = daemon->outpacket.iov_base + start_opts; opt; opt = opt6_next(opt, daemon->outpacket.iov_base + end_opts))
1308 {
1309 int offset = 0;
1310 char *optname;
1311
1312 /* account for flag byte on FQDN */
1313 if (opt6_type(opt) == OPTION6_FQDN)
1314 offset = 1;
1315
1316 optname = option_string(AF_INET6, opt6_type(opt), opt6_ptr(opt, offset), opt6_len(opt) - offset, daemon->namebuff, MAXDNAME);
1317
1318 my_syslog(MS_DHCP | LOG_INFO, "%u sent size:%3d option:%3d %s %s",
1319 xid, opt6_len(opt), opt6_type(opt), optname, daemon->namebuff);
1320 }
1321 }
1322
1323 return 1;
1324
1325}
1326
1327static void log6_packet(char *type, unsigned char *clid, int clid_len, struct in6_addr *addr, int xid, char *iface, char *string)
1328{
1329 /* avoid buffer overflow */
1330 if (clid_len > 100)
1331 clid_len = 100;
1332
1333 print_mac(daemon->namebuff, clid, clid_len);
1334
1335 if (addr)
1336 {
1337 inet_ntop(AF_INET6, addr, daemon->dhcp_buff2, 255);
1338 strcat(daemon->dhcp_buff2, " ");
1339 }
1340 else
1341 daemon->dhcp_buff2[0] = 0;
1342
1343 if(option_bool(OPT_LOG_OPTS))
1344 my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s %s%s",
1345 xid,
1346 type,
1347 iface,
1348 daemon->namebuff,
1349 daemon->dhcp_buff2,
1350 string ? string : "");
1351 else
1352 my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s %s%s",
1353 type,
1354 iface,
1355 daemon->namebuff,
1356 daemon->dhcp_buff2,
1357 string ? string : "");
1358}
1359
1360static void *opt6_find (void *opts, void *end, unsigned int search, unsigned int minsize)
Simon Kelleyc72daea2012-01-05 21:33:27 +00001361{
1362 u16 opt, opt_len;
1363 void *start;
1364
1365 if (!opts)
1366 return NULL;
1367
1368 while (1)
1369 {
1370 if (end - opts < 4)
1371 return NULL;
1372
1373 start = opts;
1374 GETSHORT(opt, opts);
1375 GETSHORT(opt_len, opts);
1376
1377 if (opt_len > (end - opts))
1378 return NULL;
1379
1380 if (opt == search && (opt_len >= minsize))
1381 return start;
1382
1383 opts += opt_len;
1384 }
1385}
1386
Simon Kelley4cb1b322012-02-06 14:30:41 +00001387static void *opt6_next(void *opts, void *end)
Simon Kelleyc72daea2012-01-05 21:33:27 +00001388{
1389 u16 opt_len;
1390
1391 if (end - opts < 4)
1392 return NULL;
1393
1394 opts += 2;
1395 GETSHORT(opt_len, opts);
1396
1397 if (opt_len >= (end - opts))
1398 return NULL;
1399
1400 return opts + opt_len;
1401}
Simon Kelleyc72daea2012-01-05 21:33:27 +00001402
1403static unsigned int opt6_uint(unsigned char *opt, int offset, int size)
1404{
1405 /* this worries about unaligned data and byte order */
1406 unsigned int ret = 0;
1407 int i;
1408 unsigned char *p = opt6_ptr(opt, offset);
1409
1410 for (i = 0; i < size; i++)
1411 ret = (ret << 8) | *p++;
1412
1413 return ret;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001414}
Simon Kelleyc72daea2012-01-05 21:33:27 +00001415
Simon Kelleyc72daea2012-01-05 21:33:27 +00001416#endif