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