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