]> git.ipfire.org Git - thirdparty/dhcp.git/blob - server/dhcpleasequery.c
-n [master]
[thirdparty/dhcp.git] / server / dhcpleasequery.c
1 /*
2 * Copyright (C) 2011-2013 by Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 2006-2007,2009 by Internet Systems Consortium, Inc. ("ISC")
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 * PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include "dhcpd.h"
19
20 /*
21 * TODO: RFC4388 specifies that the server SHOULD return the same
22 * options it would for a DHCREQUEST message, if no Parameter
23 * Request List option (option 55) is passed. We do not do that.
24 *
25 * TODO: RFC4388 specifies the creation of a "non-sensitive options"
26 * configuration list, and that these SHOULD be returned. We
27 * have no such list.
28 *
29 * TODO: RFC4388 says the server SHOULD use RFC3118, "Authentication
30 * for DHCP Messages".
31 *
32 * TODO: RFC4388 specifies that you SHOULD insure that you cannot be
33 * DoS'ed by DHCPLEASEQUERY message.
34 */
35
36 /*
37 * If you query by hardware address or by client ID, then you may have
38 * more than one IP address for your query argument. We need to do two
39 * things:
40 *
41 * 1. Find the most recent lease.
42 * 2. Find all additional IP addresses for the query argument.
43 *
44 * We do this by looking through all of the leases associated with a
45 * given hardware address or client ID. We use the cltt (client last
46 * transaction time) of the lease, which only has a resolution of one
47 * second, so we might not actually give the very latest IP.
48 */
49
50 static struct lease*
51 next_hw(const struct lease *lease) {
52 /* INSIST(lease != NULL); */
53 return lease->n_hw;
54 }
55
56 static struct lease*
57 next_uid(const struct lease *lease) {
58 /* INSIST(lease != NULL); */
59 return lease->n_uid;
60 }
61
62 void
63 get_newest_lease(struct lease **retval,
64 struct lease *lease,
65 struct lease *(*next)(const struct lease *)) {
66
67 struct lease *p;
68 struct lease *newest;
69
70 /* INSIST(newest != NULL); */
71 /* INSIST(next != NULL); */
72
73 *retval = NULL;
74
75 if (lease == NULL) {
76 return;
77 }
78
79 newest = lease;
80 for (p=next(lease); p != NULL; p=next(p)) {
81 if (newest->binding_state == FTS_ACTIVE) {
82 if ((p->binding_state == FTS_ACTIVE) &&
83 (p->cltt > newest->cltt)) {
84 newest = p;
85 }
86 } else {
87 if (p->ends > newest->ends) {
88 newest = p;
89 }
90 }
91 }
92
93 lease_reference(retval, newest, MDL);
94 }
95
96 static int
97 get_associated_ips(const struct lease *lease,
98 struct lease *(*next)(const struct lease *),
99 const struct lease *newest,
100 u_int32_t *associated_ips,
101 unsigned int associated_ips_size) {
102
103 const struct lease *p;
104 int cnt;
105
106 /* INSIST(next != NULL); */
107 /* INSIST(associated_ips != NULL); */
108
109 if (lease == NULL) {
110 return 0;
111 }
112
113 cnt = 0;
114 for (p=lease; p != NULL; p=next(p)) {
115 if ((p->binding_state == FTS_ACTIVE) && (p != newest)) {
116 if (cnt < associated_ips_size) {
117 memcpy(&associated_ips[cnt],
118 p->ip_addr.iabuf,
119 sizeof(associated_ips[cnt]));
120 }
121 cnt++;
122 }
123 }
124 return cnt;
125 }
126
127
128 void
129 dhcpleasequery(struct packet *packet, int ms_nulltp) {
130 char msgbuf[256];
131 char dbg_info[128];
132 struct iaddr cip;
133 struct iaddr gip;
134 struct data_string uid;
135 struct hardware h;
136 struct lease *tmp_lease;
137 struct lease *lease;
138 int want_associated_ip;
139 int assoc_ip_cnt;
140 u_int32_t assoc_ips[40]; /* XXXSK: arbitrary maximum number of IPs */
141 const int nassoc_ips = sizeof(assoc_ips) / sizeof(assoc_ips[0]);
142
143 unsigned char dhcpMsgType;
144 const char *dhcp_msg_type_name;
145 struct subnet *subnet;
146 struct group *relay_group;
147 struct option_state *options;
148 struct option_cache *oc;
149 int allow_leasequery;
150 int ignorep;
151 u_int32_t lease_duration;
152 u_int32_t time_renewal;
153 u_int32_t time_rebinding;
154 u_int32_t time_expiry;
155 u_int32_t client_last_transaction_time;
156 struct sockaddr_in to;
157 struct in_addr siaddr;
158 struct data_string prl;
159 struct data_string *prl_ptr;
160
161 int i;
162 struct interface_info *interface;
163
164 /* INSIST(packet != NULL); */
165
166 /*
167 * Prepare log information.
168 */
169 snprintf(msgbuf, sizeof(msgbuf),
170 "DHCPLEASEQUERY from %s", inet_ntoa(packet->raw->giaddr));
171
172 /*
173 * We can't reply if there is no giaddr field.
174 */
175 if (!packet->raw->giaddr.s_addr) {
176 log_info("%s: missing giaddr, ciaddr is %s, no reply sent",
177 msgbuf, inet_ntoa(packet->raw->ciaddr));
178 return;
179 }
180
181 /*
182 * Initially we use the 'giaddr' subnet options scope to determine if
183 * the giaddr-identified relay agent is permitted to perform a
184 * leasequery. The subnet is not required, and may be omitted, in
185 * which case we are essentially interrogating the root options class
186 * to find a globally permit.
187 */
188 gip.len = sizeof(packet->raw->giaddr);
189 memcpy(gip.iabuf, &packet->raw->giaddr, sizeof(packet->raw->giaddr));
190
191 subnet = NULL;
192 find_subnet(&subnet, gip, MDL);
193 if (subnet != NULL)
194 relay_group = subnet->group;
195 else
196 relay_group = root_group;
197
198 subnet_dereference(&subnet, MDL);
199
200 options = NULL;
201 if (!option_state_allocate(&options, MDL)) {
202 log_error("No memory for option state.");
203 log_info("%s: out of memory, no reply sent", msgbuf);
204 return;
205 }
206
207 execute_statements_in_scope(NULL, packet, NULL, NULL, packet->options,
208 options, &global_scope, relay_group,
209 NULL, NULL);
210
211 for (i=packet->class_count-1; i>=0; i--) {
212 execute_statements_in_scope(NULL, packet, NULL, NULL,
213 packet->options, options,
214 &global_scope,
215 packet->classes[i]->group,
216 relay_group, NULL);
217 }
218
219 /*
220 * Because LEASEQUERY has some privacy concerns, default to deny.
221 */
222 allow_leasequery = 0;
223
224 /*
225 * See if we are authorized to do LEASEQUERY.
226 */
227 oc = lookup_option(&server_universe, options, SV_LEASEQUERY);
228 if (oc != NULL) {
229 allow_leasequery = evaluate_boolean_option_cache(&ignorep,
230 packet, NULL, NULL, packet->options,
231 options, &global_scope, oc, MDL);
232 }
233
234 if (!allow_leasequery) {
235 log_info("%s: LEASEQUERY not allowed, query ignored", msgbuf);
236 option_state_dereference(&options, MDL);
237 return;
238 }
239
240
241 /*
242 * Copy out the client IP address.
243 */
244 cip.len = sizeof(packet->raw->ciaddr);
245 memcpy(cip.iabuf, &packet->raw->ciaddr, sizeof(packet->raw->ciaddr));
246
247 /*
248 * If the client IP address is valid (not all zero), then we
249 * are looking for information about that IP address.
250 */
251 assoc_ip_cnt = 0;
252 lease = tmp_lease = NULL;
253 if (memcmp(cip.iabuf, "\0\0\0", 4)) {
254
255 want_associated_ip = 0;
256
257 snprintf(dbg_info, sizeof(dbg_info), "IP %s", piaddr(cip));
258 find_lease_by_ip_addr(&lease, cip, MDL);
259
260
261 } else {
262
263 want_associated_ip = 1;
264
265 /*
266 * If the client IP address is all zero, then we will
267 * either look up by the client identifier (if we have
268 * one), or by the MAC address.
269 */
270
271 memset(&uid, 0, sizeof(uid));
272 if (get_option(&uid,
273 &dhcp_universe,
274 packet,
275 NULL,
276 NULL,
277 packet->options,
278 NULL,
279 packet->options,
280 &global_scope,
281 DHO_DHCP_CLIENT_IDENTIFIER,
282 MDL)) {
283
284 snprintf(dbg_info,
285 sizeof(dbg_info),
286 "client-id %s",
287 print_hex_1(uid.len, uid.data, 60));
288
289 find_lease_by_uid(&tmp_lease, uid.data, uid.len, MDL);
290 data_string_forget(&uid, MDL);
291 get_newest_lease(&lease, tmp_lease, next_uid);
292 assoc_ip_cnt = get_associated_ips(tmp_lease,
293 next_uid,
294 lease,
295 assoc_ips,
296 nassoc_ips);
297
298 } else {
299
300 if (packet->raw->hlen+1 > sizeof(h.hbuf)) {
301 log_info("%s: hardware length too long, "
302 "no reply sent", msgbuf);
303 option_state_dereference(&options, MDL);
304 return;
305 }
306
307 h.hlen = packet->raw->hlen + 1;
308 h.hbuf[0] = packet->raw->htype;
309 memcpy(&h.hbuf[1],
310 packet->raw->chaddr,
311 packet->raw->hlen);
312
313 snprintf(dbg_info,
314 sizeof(dbg_info),
315 "MAC address %s",
316 print_hw_addr(h.hbuf[0],
317 h.hlen - 1,
318 &h.hbuf[1]));
319
320 find_lease_by_hw_addr(&tmp_lease, h.hbuf, h.hlen, MDL);
321 get_newest_lease(&lease, tmp_lease, next_hw);
322 assoc_ip_cnt = get_associated_ips(tmp_lease,
323 next_hw,
324 lease,
325 assoc_ips,
326 nassoc_ips);
327
328 }
329
330 lease_dereference(&tmp_lease, MDL);
331
332 if (lease != NULL) {
333 memcpy(&packet->raw->ciaddr,
334 lease->ip_addr.iabuf,
335 sizeof(packet->raw->ciaddr));
336 }
337
338 /*
339 * Log if we have too many IP addresses associated
340 * with this client.
341 */
342 if (want_associated_ip && (assoc_ip_cnt > nassoc_ips)) {
343 log_info("%d IP addresses associated with %s, "
344 "only %d sent in reply.",
345 assoc_ip_cnt, dbg_info, nassoc_ips);
346 }
347 }
348
349 /*
350 * We now know the query target too, so can report this in
351 * our log message.
352 */
353 snprintf(msgbuf, sizeof(msgbuf),
354 "DHCPLEASEQUERY from %s for %s",
355 inet_ntoa(packet->raw->giaddr), dbg_info);
356
357 /*
358 * Figure our our return type.
359 */
360 if (lease == NULL) {
361 dhcpMsgType = DHCPLEASEUNKNOWN;
362 dhcp_msg_type_name = "DHCPLEASEUNKNOWN";
363 } else {
364 if (lease->binding_state == FTS_ACTIVE) {
365 dhcpMsgType = DHCPLEASEACTIVE;
366 dhcp_msg_type_name = "DHCPLEASEACTIVE";
367 } else {
368 dhcpMsgType = DHCPLEASEUNASSIGNED;
369 dhcp_msg_type_name = "DHCPLEASEUNASSIGNED";
370 }
371 }
372
373 /*
374 * Set options that only make sense if we have an active lease.
375 */
376
377 if (dhcpMsgType == DHCPLEASEACTIVE)
378 {
379 /*
380 * RFC 4388 uses the PRL to request options for the agent to
381 * receive that are "about" the client. It is confusing
382 * because in some cases it wants to know what was sent to
383 * the client (lease times, adjusted), and in others it wants
384 * to know information the client sent. You're supposed to
385 * know this on a case-by-case basis.
386 *
387 * "Name servers", "domain name", and the like from the relay
388 * agent's scope seems less than useful. Our options are to
389 * restart the option cache from the lease's best point of view
390 * (execute statements from the lease pool's group), or to
391 * simply restart the option cache from empty.
392 *
393 * I think restarting the option cache from empty best
394 * approaches RFC 4388's intent; specific options are included.
395 */
396 option_state_dereference(&options, MDL);
397
398 if (!option_state_allocate(&options, MDL)) {
399 log_error("%s: out of memory, no reply sent", msgbuf);
400 lease_dereference(&lease, MDL);
401 return;
402 }
403
404 /*
405 * Set the hardware address fields.
406 */
407
408 packet->raw->hlen = lease->hardware_addr.hlen - 1;
409 packet->raw->htype = lease->hardware_addr.hbuf[0];
410 memcpy(packet->raw->chaddr,
411 &lease->hardware_addr.hbuf[1],
412 sizeof(packet->raw->chaddr));
413
414 /*
415 * Set client identifier option.
416 */
417 if (lease->uid_len > 0) {
418 if (!add_option(options,
419 DHO_DHCP_CLIENT_IDENTIFIER,
420 lease->uid,
421 lease->uid_len)) {
422 option_state_dereference(&options, MDL);
423 lease_dereference(&lease, MDL);
424 log_info("%s: out of memory, no reply sent",
425 msgbuf);
426 return;
427 }
428 }
429
430
431 /*
432 * Calculate T1 and T2, the times when the client
433 * tries to extend its lease on its networking
434 * address.
435 * These seem to be hard-coded in ISC DHCP, to 0.5 and
436 * 0.875 of the lease time.
437 */
438
439 lease_duration = lease->ends - lease->starts;
440 time_renewal = lease->starts +
441 (lease_duration / 2);
442 time_rebinding = lease->starts +
443 (lease_duration / 2) +
444 (lease_duration / 4) +
445 (lease_duration / 8);
446
447 if (time_renewal > cur_time) {
448 time_renewal = htonl(time_renewal - cur_time);
449
450 if (!add_option(options,
451 DHO_DHCP_RENEWAL_TIME,
452 &time_renewal,
453 sizeof(time_renewal))) {
454 option_state_dereference(&options, MDL);
455 lease_dereference(&lease, MDL);
456 log_info("%s: out of memory, no reply sent",
457 msgbuf);
458 return;
459 }
460 }
461
462 if (time_rebinding > cur_time) {
463 time_rebinding = htonl(time_rebinding - cur_time);
464
465 if (!add_option(options,
466 DHO_DHCP_REBINDING_TIME,
467 &time_rebinding,
468 sizeof(time_rebinding))) {
469 option_state_dereference(&options, MDL);
470 lease_dereference(&lease, MDL);
471 log_info("%s: out of memory, no reply sent",
472 msgbuf);
473 return;
474 }
475 }
476
477 if (lease->ends > cur_time) {
478 time_expiry = htonl(lease->ends - cur_time);
479
480 if (!add_option(options,
481 DHO_DHCP_LEASE_TIME,
482 &time_expiry,
483 sizeof(time_expiry))) {
484 option_state_dereference(&options, MDL);
485 lease_dereference(&lease, MDL);
486 log_info("%s: out of memory, no reply sent",
487 msgbuf);
488 return;
489 }
490 }
491
492 /* Supply the Vendor-Class-Identifier. */
493 if (lease->scope != NULL) {
494 struct data_string vendor_class;
495
496 memset(&vendor_class, 0, sizeof(vendor_class));
497
498 if (find_bound_string(&vendor_class, lease->scope,
499 "vendor-class-identifier")) {
500 if (!add_option(options,
501 DHO_VENDOR_CLASS_IDENTIFIER,
502 (void *)vendor_class.data,
503 vendor_class.len)) {
504 option_state_dereference(&options,
505 MDL);
506 lease_dereference(&lease, MDL);
507 log_error("%s: error adding vendor "
508 "class identifier, no reply "
509 "sent", msgbuf);
510 data_string_forget(&vendor_class, MDL);
511 return;
512 }
513 data_string_forget(&vendor_class, MDL);
514 }
515 }
516
517 /*
518 * Set the relay agent info.
519 *
520 * Note that because agent info is appended without regard
521 * to the PRL in cons_options(), this will be sent as the
522 * last option in the packet whether it is listed on PRL or
523 * not.
524 */
525
526 if (lease->agent_options != NULL) {
527 int idx = agent_universe.index;
528 struct option_chain_head **tmp1 =
529 (struct option_chain_head **)
530 &(options->universes[idx]);
531 struct option_chain_head *tmp2 =
532 (struct option_chain_head *)
533 lease->agent_options;
534
535 option_chain_head_reference(tmp1, tmp2, MDL);
536 }
537
538 /*
539 * Set the client last transaction time.
540 * We check to make sure we have a timestamp. For
541 * lease files that were saved before running a
542 * timestamp-aware version of the server, this may
543 * not be set.
544 */
545
546 if (lease->cltt != MIN_TIME) {
547 if (cur_time > lease->cltt) {
548 client_last_transaction_time =
549 htonl(cur_time - lease->cltt);
550 } else {
551 client_last_transaction_time = htonl(0);
552 }
553 if (!add_option(options,
554 DHO_CLIENT_LAST_TRANSACTION_TIME,
555 &client_last_transaction_time,
556 sizeof(client_last_transaction_time))) {
557 option_state_dereference(&options, MDL);
558 lease_dereference(&lease, MDL);
559 log_info("%s: out of memory, no reply sent",
560 msgbuf);
561 return;
562 }
563 }
564
565 /*
566 * Set associated IPs, if requested and there are some.
567 */
568 if (want_associated_ip && (assoc_ip_cnt > 0)) {
569 if (!add_option(options,
570 DHO_ASSOCIATED_IP,
571 assoc_ips,
572 assoc_ip_cnt * sizeof(assoc_ips[0]))) {
573 option_state_dereference(&options, MDL);
574 lease_dereference(&lease, MDL);
575 log_info("%s: out of memory, no reply sent",
576 msgbuf);
577 return;
578 }
579 }
580 }
581
582 /*
583 * Set the message type.
584 */
585
586 packet->raw->op = BOOTREPLY;
587
588 /*
589 * Set DHCP message type.
590 */
591 if (!add_option(options,
592 DHO_DHCP_MESSAGE_TYPE,
593 &dhcpMsgType,
594 sizeof(dhcpMsgType))) {
595 option_state_dereference(&options, MDL);
596 lease_dereference(&lease, MDL);
597 log_info("%s: error adding option, no reply sent", msgbuf);
598 return;
599 }
600
601 /*
602 * Log the message we've received.
603 */
604 log_info("%s", msgbuf);
605
606 /*
607 * Figure out which address to use to send from.
608 */
609 get_server_source_address(&siaddr, options, options, packet);
610
611 /*
612 * Set up the option buffer.
613 */
614
615 memset(&prl, 0, sizeof(prl));
616 oc = lookup_option(&dhcp_universe, options,
617 DHO_DHCP_PARAMETER_REQUEST_LIST);
618 if (oc != NULL) {
619 evaluate_option_cache(&prl,
620 packet,
621 NULL,
622 NULL,
623 packet->options,
624 options,
625 &global_scope,
626 oc,
627 MDL);
628 }
629 if (prl.len > 0) {
630 prl_ptr = &prl;
631 } else {
632 prl_ptr = NULL;
633 }
634
635 packet->packet_length = cons_options(packet,
636 packet->raw,
637 lease,
638 NULL,
639 0,
640 packet->options,
641 options,
642 &global_scope,
643 0,
644 0,
645 0,
646 prl_ptr,
647 NULL);
648
649 data_string_forget(&prl, MDL); /* SK: safe, even if empty */
650 option_state_dereference(&options, MDL);
651 lease_dereference(&lease, MDL);
652
653 to.sin_family = AF_INET;
654 #ifdef HAVE_SA_LEN
655 to.sin_len = sizeof(to);
656 #endif
657 memset(to.sin_zero, 0, sizeof(to.sin_zero));
658
659 /*
660 * Leasequery packets are be sent to the gateway address.
661 */
662 to.sin_addr = packet->raw->giaddr;
663 if (packet->raw->giaddr.s_addr != htonl(INADDR_LOOPBACK)) {
664 to.sin_port = local_port;
665 } else {
666 to.sin_port = remote_port; /* XXXSK: For debugging. */
667 }
668
669 /*
670 * The fallback_interface lets us send with a real IP
671 * address. The packet interface sends from all-zeros.
672 */
673 if (fallback_interface != NULL) {
674 interface = fallback_interface;
675 } else {
676 interface = packet->interface;
677 }
678
679 /*
680 * Report what we're sending.
681 */
682 log_info("%s to %s for %s (%d associated IPs)",
683 dhcp_msg_type_name,
684 inet_ntoa(to.sin_addr), dbg_info, assoc_ip_cnt);
685
686 send_packet(interface,
687 NULL,
688 packet->raw,
689 packet->packet_length,
690 siaddr,
691 &to,
692 NULL);
693 }
694
695 #ifdef DHCPv6
696
697 /*
698 * TODO: RFC5007 query-by-clientid.
699 *
700 * TODO: RFC5007 look at the pools according to the link-address.
701 *
702 * TODO: get fixed leases too.
703 *
704 * TODO: RFC5007 ORO in query-options.
705 *
706 * TODO: RFC5007 lq-relay-data.
707 *
708 * TODO: RFC5007 lq-client-link.
709 *
710 * Note: the code is still nearly compliant and usable for the target
711 * case with these missing features!
712 */
713
714 /*
715 * The structure to handle a leasequery.
716 */
717 struct lq6_state {
718 struct packet *packet;
719 struct data_string client_id;
720 struct data_string server_id;
721 struct data_string lq_query;
722 uint8_t query_type;
723 struct in6_addr link_addr;
724 struct option_state *query_opts;
725
726 struct option_state *reply_opts;
727 unsigned cursor;
728 union reply_buffer {
729 unsigned char data[65536];
730 struct dhcpv6_packet reply;
731 } buf;
732 };
733
734 /*
735 * Options that we want to send.
736 */
737 static const int required_opts_lq[] = {
738 D6O_CLIENTID,
739 D6O_SERVERID,
740 D6O_STATUS_CODE,
741 D6O_CLIENT_DATA,
742 D6O_LQ_RELAY_DATA,
743 D6O_LQ_CLIENT_LINK,
744 0
745 };
746 static const int required_opt_CLIENT_DATA[] = {
747 D6O_CLIENTID,
748 D6O_IAADDR,
749 D6O_IAPREFIX,
750 D6O_CLT_TIME,
751 0
752 };
753
754 /*
755 * Get the lq-query option from the packet.
756 */
757 static isc_result_t
758 get_lq_query(struct lq6_state *lq)
759 {
760 struct data_string *lq_query = &lq->lq_query;
761 struct packet *packet = lq->packet;
762 struct option_cache *oc;
763
764 /*
765 * Verify our lq_query structure is empty.
766 */
767 if ((lq_query->data != NULL) || (lq_query->len != 0)) {
768 return DHCP_R_INVALIDARG;
769 }
770
771 oc = lookup_option(&dhcpv6_universe, packet->options, D6O_LQ_QUERY);
772 if (oc == NULL) {
773 return ISC_R_NOTFOUND;
774 }
775
776 if (!evaluate_option_cache(lq_query, packet, NULL, NULL,
777 packet->options, NULL,
778 &global_scope, oc, MDL)) {
779 return ISC_R_FAILURE;
780 }
781
782 return ISC_R_SUCCESS;
783 }
784
785 /*
786 * Message validation, RFC 5007 section 4.2.1:
787 * dhcpv6.c:valid_client_msg() - unicast + lq-query option.
788 */
789 static int
790 valid_query_msg(struct lq6_state *lq) {
791 struct packet *packet = lq->packet;
792 int ret_val = 0;
793 struct option_cache *oc;
794
795 /* INSIST((lq != NULL) || (packet != NULL)); */
796
797 switch (get_client_id(packet, &lq->client_id)) {
798 case ISC_R_SUCCESS:
799 break;
800 case ISC_R_NOTFOUND:
801 log_debug("Discarding %s from %s; "
802 "client identifier missing",
803 dhcpv6_type_names[packet->dhcpv6_msg_type],
804 piaddr(packet->client_addr));
805 goto exit;
806 default:
807 log_error("Error processing %s from %s; "
808 "unable to evaluate Client Identifier",
809 dhcpv6_type_names[packet->dhcpv6_msg_type],
810 piaddr(packet->client_addr));
811 goto exit;
812 }
813
814 oc = lookup_option(&dhcpv6_universe, packet->options, D6O_SERVERID);
815 if (oc != NULL) {
816 if (evaluate_option_cache(&lq->server_id, packet, NULL, NULL,
817 packet->options, NULL,
818 &global_scope, oc, MDL)) {
819 log_debug("Discarding %s from %s; "
820 "server identifier found "
821 "(CLIENTID %s, SERVERID %s)",
822 dhcpv6_type_names[packet->dhcpv6_msg_type],
823 piaddr(packet->client_addr),
824 print_hex_1(lq->client_id.len,
825 lq->client_id.data, 60),
826 print_hex_2(lq->server_id.len,
827 lq->server_id.data, 60));
828 } else {
829 log_debug("Discarding %s from %s; "
830 "server identifier found "
831 "(CLIENTID %s)",
832 dhcpv6_type_names[packet->dhcpv6_msg_type],
833 print_hex_1(lq->client_id.len,
834 lq->client_id.data, 60),
835 piaddr(packet->client_addr));
836 }
837 goto exit;
838 }
839
840 switch (get_lq_query(lq)) {
841 case ISC_R_SUCCESS:
842 break;
843 case ISC_R_NOTFOUND:
844 log_debug("Discarding %s from %s; lq-query missing",
845 dhcpv6_type_names[packet->dhcpv6_msg_type],
846 piaddr(packet->client_addr));
847 goto exit;
848 default:
849 log_error("Error processing %s from %s; "
850 "unable to evaluate LQ-Query",
851 dhcpv6_type_names[packet->dhcpv6_msg_type],
852 piaddr(packet->client_addr));
853 goto exit;
854 }
855
856 /* looks good */
857 ret_val = 1;
858
859 exit:
860 if (!ret_val) {
861 if (lq->client_id.len > 0) {
862 data_string_forget(&lq->client_id, MDL);
863 }
864 if (lq->server_id.len > 0) {
865 data_string_forget(&lq->server_id, MDL);
866 }
867 if (lq->lq_query.len > 0) {
868 data_string_forget(&lq->lq_query, MDL);
869 }
870 }
871 return ret_val;
872 }
873
874 /*
875 * Set an error in a status-code option (from set_status_code).
876 */
877 static int
878 set_error(struct lq6_state *lq, u_int16_t code, const char *message) {
879 struct data_string d;
880 int ret_val;
881
882 memset(&d, 0, sizeof(d));
883 d.len = sizeof(code) + strlen(message);
884 if (!buffer_allocate(&d.buffer, d.len, MDL)) {
885 log_fatal("set_error: no memory for status code.");
886 }
887 d.data = d.buffer->data;
888 putUShort(d.buffer->data, code);
889 memcpy(d.buffer->data + sizeof(code), message, d.len - sizeof(code));
890 if (!save_option_buffer(&dhcpv6_universe, lq->reply_opts,
891 d.buffer, (unsigned char *)d.data, d.len,
892 D6O_STATUS_CODE, 0)) {
893 log_error("set_error: error saving status code.");
894 ret_val = 0;
895 } else {
896 ret_val = 1;
897 }
898 data_string_forget(&d, MDL);
899 return ret_val;
900 }
901
902 /*
903 * Process a by-address lease query.
904 */
905 static int
906 process_lq_by_address(struct lq6_state *lq) {
907 struct packet *packet = lq->packet;
908 struct option_cache *oc;
909 struct ipv6_pool *pool = NULL;
910 struct data_string data;
911 struct in6_addr addr;
912 struct iasubopt *iaaddr = NULL;
913 struct option_state *opt_state = NULL;
914 u_int32_t lifetime;
915 unsigned opt_cursor;
916 int ret_val = 0;
917
918 /*
919 * Get the IAADDR.
920 */
921 oc = lookup_option(&dhcpv6_universe, lq->query_opts, D6O_IAADDR);
922 if (oc == NULL) {
923 if (!set_error(lq, STATUS_MalformedQuery,
924 "No OPTION_IAADDR.")) {
925 log_error("process_lq_by_address: unable "
926 "to set MalformedQuery status code.");
927 return 0;
928 }
929 return 1;
930 }
931 memset(&data, 0, sizeof(data));
932 if (!evaluate_option_cache(&data, packet,
933 NULL, NULL,
934 lq->query_opts, NULL,
935 &global_scope, oc, MDL) ||
936 (data.len < IAADDR_OFFSET)) {
937 log_error("process_lq_by_address: error evaluating IAADDR.");
938 goto exit;
939 }
940 memcpy(&addr, data.data, sizeof(addr));
941 data_string_forget(&data, MDL);
942
943 /*
944 * Find the lease.
945 * Note the RFC 5007 says to use the link-address to find the link
946 * or the ia-aadr when it is :: but in any case the ia-addr has
947 * to be on the link, so we ignore the link-address here.
948 */
949 if (find_ipv6_pool(&pool, D6O_IA_NA, &addr) != ISC_R_SUCCESS) {
950 if (!set_error(lq, STATUS_NotConfigured,
951 "Address not in a pool.")) {
952 log_error("process_lq_by_address: unable "
953 "to set NotConfigured status code.");
954 goto exit;
955 }
956 ret_val = 1;
957 goto exit;
958 }
959 if (iasubopt_hash_lookup(&iaaddr, pool->leases, &addr,
960 sizeof(addr), MDL) == 0) {
961 ret_val = 1;
962 goto exit;
963 }
964 if ((iaaddr == NULL) || (iaaddr->state != FTS_ACTIVE) ||
965 (iaaddr->ia == NULL) || (iaaddr->ia->iaid_duid.len <= 4)) {
966 ret_val = 1;
967 goto exit;
968 }
969
970 /*
971 * Build the client-data option (with client-id, ia-addr and clt-time).
972 */
973 if (!option_state_allocate(&opt_state, MDL)) {
974 log_error("process_lq_by_address: "
975 "no memory for option state.");
976 goto exit;
977 }
978
979 data_string_copy(&data, &iaaddr->ia->iaid_duid, MDL);
980 data.data += 4;
981 data.len -= 4;
982 if (!save_option_buffer(&dhcpv6_universe, opt_state,
983 NULL, (unsigned char *)data.data, data.len,
984 D6O_CLIENTID, 0)) {
985 log_error("process_lq_by_address: error saving client ID.");
986 goto exit;
987 }
988 data_string_forget(&data, MDL);
989
990 data.len = IAADDR_OFFSET;
991 if (!buffer_allocate(&data.buffer, data.len, MDL)) {
992 log_error("process_lq_by_address: no memory for ia-addr.");
993 goto exit;
994 }
995 data.data = data.buffer->data;
996 memcpy(data.buffer->data, &iaaddr->addr, 16);
997 lifetime = iaaddr->prefer;
998 putULong(data.buffer->data + 16, lifetime);
999 lifetime = iaaddr->valid;
1000 putULong(data.buffer->data + 20, lifetime);
1001 if (!save_option_buffer(&dhcpv6_universe, opt_state,
1002 NULL, (unsigned char *)data.data, data.len,
1003 D6O_IAADDR, 0)) {
1004 log_error("process_lq_by_address: error saving ia-addr.");
1005 goto exit;
1006 }
1007 data_string_forget(&data, MDL);
1008
1009 lifetime = htonl(iaaddr->ia->cltt);
1010 if (!save_option_buffer(&dhcpv6_universe, opt_state,
1011 NULL, (unsigned char *)&lifetime, 4,
1012 D6O_CLT_TIME, 0)) {
1013 log_error("process_lq_by_address: error saving clt time.");
1014 goto exit;
1015 }
1016
1017 /*
1018 * Store the client-data option.
1019 */
1020 opt_cursor = lq->cursor;
1021 putUShort(lq->buf.data + lq->cursor, (unsigned)D6O_CLIENT_DATA);
1022 lq->cursor += 2;
1023 /* Skip option length. */
1024 lq->cursor += 2;
1025
1026 lq->cursor += store_options6((char *)lq->buf.data + lq->cursor,
1027 sizeof(lq->buf) - lq->cursor,
1028 opt_state, lq->packet,
1029 required_opt_CLIENT_DATA, NULL);
1030 /* Reset the length. */
1031 putUShort(lq->buf.data + opt_cursor + 2,
1032 lq->cursor - (opt_cursor + 4));
1033
1034 /* Done. */
1035 ret_val = 1;
1036
1037 exit:
1038 if (data.data != NULL)
1039 data_string_forget(&data, MDL);
1040 if (pool != NULL)
1041 ipv6_pool_dereference(&pool, MDL);
1042 if (iaaddr != NULL)
1043 iasubopt_dereference(&iaaddr, MDL);
1044 if (opt_state != NULL)
1045 option_state_dereference(&opt_state, MDL);
1046 return ret_val;
1047 }
1048
1049
1050 /*
1051 * Process a lease query.
1052 */
1053 void
1054 dhcpv6_leasequery(struct data_string *reply_ret, struct packet *packet) {
1055 static struct lq6_state lq;
1056 struct option_cache *oc;
1057 int allow_lq;
1058
1059 /*
1060 * Initialize the lease query state.
1061 */
1062 lq.packet = NULL;
1063 memset(&lq.client_id, 0, sizeof(lq.client_id));
1064 memset(&lq.server_id, 0, sizeof(lq.server_id));
1065 memset(&lq.lq_query, 0, sizeof(lq.lq_query));
1066 lq.query_opts = NULL;
1067 lq.reply_opts = NULL;
1068 packet_reference(&lq.packet, packet, MDL);
1069
1070 /*
1071 * Validate our input.
1072 */
1073 if (!valid_query_msg(&lq)) {
1074 goto exit;
1075 }
1076
1077 /*
1078 * Prepare our reply.
1079 */
1080 if (!option_state_allocate(&lq.reply_opts, MDL)) {
1081 log_error("dhcpv6_leasequery: no memory for option state.");
1082 goto exit;
1083 }
1084 execute_statements_in_scope(NULL, lq.packet, NULL, NULL,
1085 lq.packet->options, lq.reply_opts,
1086 &global_scope, root_group, NULL, NULL);
1087
1088 lq.buf.reply.msg_type = DHCPV6_LEASEQUERY_REPLY;
1089
1090 memcpy(lq.buf.reply.transaction_id,
1091 lq.packet->dhcpv6_transaction_id,
1092 sizeof(lq.buf.reply.transaction_id));
1093
1094 /*
1095 * Because LEASEQUERY has some privacy concerns, default to deny.
1096 */
1097 allow_lq = 0;
1098
1099 /*
1100 * See if we are authorized to do LEASEQUERY.
1101 */
1102 oc = lookup_option(&server_universe, lq.reply_opts, SV_LEASEQUERY);
1103 if (oc != NULL) {
1104 allow_lq = evaluate_boolean_option_cache(NULL,
1105 lq.packet,
1106 NULL, NULL,
1107 lq.packet->options,
1108 lq.reply_opts,
1109 &global_scope,
1110 oc, MDL);
1111 }
1112
1113 if (!allow_lq) {
1114 log_info("dhcpv6_leasequery: not allowed, query ignored.");
1115 goto exit;
1116 }
1117
1118 /*
1119 * Same than transmission of REPLY message in RFC 3315:
1120 * server-id
1121 * client-id
1122 */
1123
1124 oc = lookup_option(&dhcpv6_universe, lq.reply_opts, D6O_SERVERID);
1125 if (oc == NULL) {
1126 /* If not already in options, get from query then global. */
1127 if (lq.server_id.data == NULL)
1128 copy_server_duid(&lq.server_id, MDL);
1129 if (!save_option_buffer(&dhcpv6_universe,
1130 lq.reply_opts,
1131 NULL,
1132 (unsigned char *)lq.server_id.data,
1133 lq.server_id.len,
1134 D6O_SERVERID,
1135 0)) {
1136 log_error("dhcpv6_leasequery: "
1137 "error saving server identifier.");
1138 goto exit;
1139 }
1140 }
1141
1142 if (!save_option_buffer(&dhcpv6_universe,
1143 lq.reply_opts,
1144 lq.client_id.buffer,
1145 (unsigned char *)lq.client_id.data,
1146 lq.client_id.len,
1147 D6O_CLIENTID,
1148 0)) {
1149 log_error("dhcpv6_leasequery: "
1150 "error saving client identifier.");
1151 goto exit;
1152 }
1153
1154 lq.cursor = 4;
1155
1156 /*
1157 * Decode the lq-query option.
1158 */
1159
1160 if (lq.lq_query.len <= LQ_QUERY_OFFSET) {
1161 if (!set_error(&lq, STATUS_MalformedQuery,
1162 "OPTION_LQ_QUERY too short.")) {
1163 log_error("dhcpv6_leasequery: unable "
1164 "to set MalformedQuery status code.");
1165 goto exit;
1166 }
1167 goto done;
1168 }
1169
1170 lq.query_type = lq.lq_query.data [0];
1171 memcpy(&lq.link_addr, lq.lq_query.data + 1, sizeof(lq.link_addr));
1172 switch (lq.query_type) {
1173 case LQ6QT_BY_ADDRESS:
1174 break;
1175 case LQ6QT_BY_CLIENTID:
1176 if (!set_error(&lq, STATUS_UnknownQueryType,
1177 "QUERY_BY_CLIENTID not supported.")) {
1178 log_error("dhcpv6_leasequery: unable to "
1179 "set UnknownQueryType status code.");
1180 goto exit;
1181 }
1182 goto done;
1183 default:
1184 if (!set_error(&lq, STATUS_UnknownQueryType,
1185 "Unknown query-type.")) {
1186 log_error("dhcpv6_leasequery: unable to "
1187 "set UnknownQueryType status code.");
1188 goto exit;
1189 }
1190 goto done;
1191 }
1192
1193 if (!option_state_allocate(&lq.query_opts, MDL)) {
1194 log_error("dhcpv6_leasequery: no memory for option state.");
1195 goto exit;
1196 }
1197 if (!parse_option_buffer(lq.query_opts,
1198 lq.lq_query.data + LQ_QUERY_OFFSET,
1199 lq.lq_query.len - LQ_QUERY_OFFSET,
1200 &dhcpv6_universe)) {
1201 log_error("dhcpv6_leasequery: error parsing query-options.");
1202 if (!set_error(&lq, STATUS_MalformedQuery,
1203 "Bad query-options.")) {
1204 log_error("dhcpv6_leasequery: unable "
1205 "to set MalformedQuery status code.");
1206 goto exit;
1207 }
1208 goto done;
1209 }
1210
1211 /* Do it. */
1212 if (!process_lq_by_address(&lq))
1213 goto exit;
1214
1215 done:
1216 /* Store the options. */
1217 lq.cursor += store_options6((char *)lq.buf.data + lq.cursor,
1218 sizeof(lq.buf) - lq.cursor,
1219 lq.reply_opts,
1220 lq.packet,
1221 required_opts_lq,
1222 NULL);
1223
1224 /* Return our reply to the caller. */
1225 reply_ret->len = lq.cursor;
1226 reply_ret->buffer = NULL;
1227 if (!buffer_allocate(&reply_ret->buffer, lq.cursor, MDL)) {
1228 log_fatal("dhcpv6_leasequery: no memory to store Reply.");
1229 }
1230 memcpy(reply_ret->buffer->data, lq.buf.data, lq.cursor);
1231 reply_ret->data = reply_ret->buffer->data;
1232
1233 exit:
1234 /* Cleanup. */
1235 if (lq.packet != NULL)
1236 packet_dereference(&lq.packet, MDL);
1237 if (lq.client_id.data != NULL)
1238 data_string_forget(&lq.client_id, MDL);
1239 if (lq.server_id.data != NULL)
1240 data_string_forget(&lq.server_id, MDL);
1241 if (lq.lq_query.data != NULL)
1242 data_string_forget(&lq.lq_query, MDL);
1243 if (lq.query_opts != NULL)
1244 option_state_dereference(&lq.query_opts, MDL);
1245 if (lq.reply_opts != NULL)
1246 option_state_dereference(&lq.reply_opts, MDL);
1247 }
1248
1249 #endif /* DHCPv6 */