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