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