2 * Copyright (C) 2006-2017 by Internet Systems Consortium, Inc. ("ISC")
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/.
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.
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.
24 * TODO: RFC4388 specifies the creation of a "non-sensitive options"
25 * configuration list, and that these SHOULD be returned. We
28 * TODO: RFC4388 says the server SHOULD use RFC3118, "Authentication
31 * TODO: RFC4388 specifies that you SHOULD insure that you cannot be
32 * DoS'ed by DHCPLEASEQUERY message.
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
40 * 1. Find the most recent lease.
41 * 2. Find all additional IP addresses for the query argument.
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.
50 next_hw(const struct lease
*lease
) {
51 /* INSIST(lease != NULL); */
56 next_uid(const struct lease
*lease
) {
57 /* INSIST(lease != NULL); */
62 get_newest_lease(struct lease
**retval
,
64 struct lease
*(*next
)(const struct lease
*)) {
69 /* INSIST(newest != NULL); */
70 /* INSIST(next != NULL); */
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
)) {
86 if (p
->ends
> newest
->ends
) {
92 lease_reference(retval
, newest
, MDL
);
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
) {
102 const struct lease
*p
;
105 /* INSIST(next != NULL); */
106 /* INSIST(associated_ips != NULL); */
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
],
118 sizeof(associated_ips
[cnt
]));
128 dhcpleasequery(struct packet
*packet
, int ms_nulltp
) {
133 struct data_string uid
;
135 struct lease
*tmp_lease
;
137 int want_associated_ip
;
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]);
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
;
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
;
161 struct interface_info
*interface
;
163 /* INSIST(packet != NULL); */
166 * Prepare log information.
168 snprintf(msgbuf
, sizeof(msgbuf
),
169 "DHCPLEASEQUERY from %s", inet_ntoa(packet
->raw
->giaddr
));
172 * We can't reply if there is no giaddr field.
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).
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
));
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.
192 gip
.len
= sizeof(packet
->raw
->giaddr
);
193 memcpy(gip
.iabuf
, &packet
->raw
->giaddr
, sizeof(packet
->raw
->giaddr
));
196 find_subnet(&subnet
, gip
, MDL
);
198 relay_group
= subnet
->group
;
200 relay_group
= root_group
;
202 subnet_dereference(&subnet
, MDL
);
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
);
211 execute_statements_in_scope(NULL
, packet
, NULL
, NULL
, packet
->options
,
212 options
, &global_scope
, relay_group
,
215 for (i
=packet
->class_count
-1; i
>=0; i
--) {
216 execute_statements_in_scope(NULL
, packet
, NULL
, NULL
,
217 packet
->options
, options
,
219 packet
->classes
[i
]->group
,
224 * Because LEASEQUERY has some privacy concerns, default to deny.
226 allow_leasequery
= 0;
229 * See if we are authorized to do LEASEQUERY.
231 oc
= lookup_option(&server_universe
, options
, SV_LEASEQUERY
);
233 allow_leasequery
= evaluate_boolean_option_cache(&ignorep
,
234 packet
, NULL
, NULL
, packet
->options
,
235 options
, &global_scope
, oc
, MDL
);
238 if (!allow_leasequery
) {
239 log_info("%s: LEASEQUERY not allowed, query ignored", msgbuf
);
240 option_state_dereference(&options
, MDL
);
246 * Copy out the client IP address.
248 cip
.len
= sizeof(packet
->raw
->ciaddr
);
249 memcpy(cip
.iabuf
, &packet
->raw
->ciaddr
, sizeof(packet
->raw
->ciaddr
));
252 * If the client IP address is valid (not all zero), then we
253 * are looking for information about that IP address.
256 lease
= tmp_lease
= NULL
;
257 if (memcmp(cip
.iabuf
, "\0\0\0", 4)) {
259 want_associated_ip
= 0;
261 snprintf(dbg_info
, sizeof(dbg_info
), "IP %s", piaddr(cip
));
262 find_lease_by_ip_addr(&lease
, cip
, MDL
);
267 want_associated_ip
= 1;
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.
275 memset(&uid
, 0, sizeof(uid
));
285 DHO_DHCP_CLIENT_IDENTIFIER
,
291 print_hex_1(uid
.len
, uid
.data
, 60));
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
,
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
);
311 h
.hlen
= packet
->raw
->hlen
+ 1;
312 h
.hbuf
[0] = packet
->raw
->htype
;
320 print_hw_addr(h
.hbuf
[0],
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
,
334 lease_dereference(&tmp_lease
, MDL
);
337 memcpy(&packet
->raw
->ciaddr
,
338 lease
->ip_addr
.iabuf
,
339 sizeof(packet
->raw
->ciaddr
));
343 * Log if we have too many IP addresses associated
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
);
354 * We now know the query target too, so can report this in
357 snprintf(msgbuf
, sizeof(msgbuf
),
358 "DHCPLEASEQUERY from %s for %s",
359 inet_ntoa(packet
->raw
->giaddr
), dbg_info
);
362 * Figure our our return type.
365 dhcpMsgType
= DHCPLEASEUNKNOWN
;
366 dhcp_msg_type_name
= "DHCPLEASEUNKNOWN";
368 if (lease
->binding_state
== FTS_ACTIVE
) {
369 dhcpMsgType
= DHCPLEASEACTIVE
;
370 dhcp_msg_type_name
= "DHCPLEASEACTIVE";
372 dhcpMsgType
= DHCPLEASEUNASSIGNED
;
373 dhcp_msg_type_name
= "DHCPLEASEUNASSIGNED";
378 * Set options that only make sense if we have an active lease.
381 if (dhcpMsgType
== DHCPLEASEACTIVE
)
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.
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.
397 * I think restarting the option cache from empty best
398 * approaches RFC 4388's intent; specific options are included.
400 option_state_dereference(&options
, MDL
);
402 if (!option_state_allocate(&options
, MDL
)) {
403 log_error("%s: out of memory, no reply sent", msgbuf
);
404 lease_dereference(&lease
, MDL
);
409 * Set the hardware address fields.
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
));
419 * Set client identifier option.
421 if (lease
->uid_len
> 0) {
422 if (!add_option(options
,
423 DHO_DHCP_CLIENT_IDENTIFIER
,
426 option_state_dereference(&options
, MDL
);
427 lease_dereference(&lease
, MDL
);
428 log_info("%s: out of memory, no reply sent",
436 * Calculate T1 and T2, the times when the client
437 * tries to extend its lease on its networking
439 * These seem to be hard-coded in ISC DHCP, to 0.5 and
440 * 0.875 of the lease time.
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);
451 if (time_renewal
> cur_time
) {
452 time_renewal
= htonl(time_renewal
- cur_time
);
454 if (!add_option(options
,
455 DHO_DHCP_RENEWAL_TIME
,
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",
466 if (time_rebinding
> cur_time
) {
467 time_rebinding
= htonl(time_rebinding
- cur_time
);
469 if (!add_option(options
,
470 DHO_DHCP_REBINDING_TIME
,
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",
481 if (lease
->ends
> cur_time
) {
482 time_expiry
= htonl(lease
->ends
- cur_time
);
484 if (!add_option(options
,
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",
496 /* Supply the Vendor-Class-Identifier. */
497 if (lease
->scope
!= NULL
) {
498 struct data_string vendor_class
;
500 memset(&vendor_class
, 0, sizeof(vendor_class
));
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
,
508 option_state_dereference(&options
,
510 lease_dereference(&lease
, MDL
);
511 log_error("%s: error adding vendor "
512 "class identifier, no reply "
514 data_string_forget(&vendor_class
, MDL
);
517 data_string_forget(&vendor_class
, MDL
);
522 * Set the relay agent info.
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
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
;
539 option_chain_head_reference(tmp1
, tmp2
, MDL
);
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
550 if (lease
->cltt
!= MIN_TIME
) {
551 if (cur_time
> lease
->cltt
) {
552 client_last_transaction_time
=
553 htonl(cur_time
- lease
->cltt
);
555 client_last_transaction_time
= htonl(0);
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",
570 * Set associated IPs, if requested and there are some.
572 if (want_associated_ip
&& (assoc_ip_cnt
> 0)) {
573 if (!add_option(options
,
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",
587 * Set the message type.
590 packet
->raw
->op
= BOOTREPLY
;
593 * Set DHCP message type.
595 if (!add_option(options
,
596 DHO_DHCP_MESSAGE_TYPE
,
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
);
606 * Log the message we've received.
608 log_info("%s", msgbuf
);
611 * Figure out which address to use to send from.
613 get_server_source_address(&siaddr
, options
, options
, packet
);
616 * Set up the option buffer.
619 memset(&prl
, 0, sizeof(prl
));
620 oc
= lookup_option(&dhcp_universe
, options
,
621 DHO_DHCP_PARAMETER_REQUEST_LIST
);
623 evaluate_option_cache(&prl
,
639 packet
->packet_length
= cons_options(packet
,
653 data_string_forget(&prl
, MDL
); /* SK: safe, even if empty */
654 option_state_dereference(&options
, MDL
);
655 lease_dereference(&lease
, MDL
);
657 to
.sin_family
= AF_INET
;
659 to
.sin_len
= sizeof(to
);
661 memset(to
.sin_zero
, 0, sizeof(to
.sin_zero
));
664 * Leasequery packets are be sent to the gateway address.
666 to
.sin_addr
= packet
->raw
->giaddr
;
667 if (packet
->raw
->giaddr
.s_addr
!= htonl(INADDR_LOOPBACK
)) {
668 to
.sin_port
= local_port
;
670 to
.sin_port
= remote_port
; /* XXXSK: For debugging. */
674 * The fallback_interface lets us send with a real IP
675 * address. The packet interface sends from all-zeros.
677 if (fallback_interface
!= NULL
) {
678 interface
= fallback_interface
;
680 interface
= packet
->interface
;
684 * Report what we're sending.
686 log_info("%s to %s for %s (%d associated IPs)",
688 inet_ntoa(to
.sin_addr
), dbg_info
, assoc_ip_cnt
);
690 send_packet(interface
,
693 packet
->packet_length
,
702 * TODO: RFC5007 query-by-clientid.
704 * TODO: RFC5007 look at the pools according to the link-address.
706 * TODO: get fixed leases too.
708 * TODO: RFC5007 ORO in query-options.
710 * TODO: RFC5007 lq-relay-data.
712 * TODO: RFC5007 lq-client-link.
714 * Note: the code is still nearly compliant and usable for the target
715 * case with these missing features!
719 * The structure to handle a leasequery.
722 struct packet
*packet
;
723 struct data_string client_id
;
724 struct data_string server_id
;
725 struct data_string lq_query
;
727 struct in6_addr link_addr
;
728 struct option_state
*query_opts
;
730 struct option_state
*reply_opts
;
733 unsigned char data
[65536];
734 struct dhcpv6_packet reply
;
739 * Options that we want to send.
741 static const int required_opts_lq
[] = {
750 static const int required_opt_CLIENT_DATA
[] = {
759 * Get the lq-query option from the packet.
762 get_lq_query(struct lq6_state
*lq
)
764 struct data_string
*lq_query
= &lq
->lq_query
;
765 struct packet
*packet
= lq
->packet
;
766 struct option_cache
*oc
;
769 * Verify our lq_query structure is empty.
771 if ((lq_query
->data
!= NULL
) || (lq_query
->len
!= 0)) {
772 return DHCP_R_INVALIDARG
;
775 oc
= lookup_option(&dhcpv6_universe
, packet
->options
, D6O_LQ_QUERY
);
777 return ISC_R_NOTFOUND
;
780 if (!evaluate_option_cache(lq_query
, packet
, NULL
, NULL
,
781 packet
->options
, NULL
,
782 &global_scope
, oc
, MDL
)) {
783 return ISC_R_FAILURE
;
786 return ISC_R_SUCCESS
;
790 * Message validation, RFC 5007 section 4.2.1:
791 * dhcpv6.c:valid_client_msg() - unicast + lq-query option.
794 valid_query_msg(struct lq6_state
*lq
) {
795 struct packet
*packet
= lq
->packet
;
797 struct option_cache
*oc
;
799 /* INSIST((lq != NULL) || (packet != NULL)); */
801 switch (get_client_id(packet
, &lq
->client_id
)) {
805 log_debug("Discarding %s from %s; "
806 "client identifier missing",
807 dhcpv6_type_names
[packet
->dhcpv6_msg_type
],
808 piaddr(packet
->client_addr
));
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
));
818 oc
= lookup_option(&dhcpv6_universe
, packet
->options
, D6O_SERVERID
);
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));
833 log_debug("Discarding %s from %s; "
834 "server identifier found "
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
));
844 switch (get_lq_query(lq
)) {
848 log_debug("Discarding %s from %s; lq-query missing",
849 dhcpv6_type_names
[packet
->dhcpv6_msg_type
],
850 piaddr(packet
->client_addr
));
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
));
865 if (lq
->client_id
.len
> 0) {
866 data_string_forget(&lq
->client_id
, MDL
);
868 if (lq
->server_id
.len
> 0) {
869 data_string_forget(&lq
->server_id
, MDL
);
871 if (lq
->lq_query
.len
> 0) {
872 data_string_forget(&lq
->lq_query
, MDL
);
879 * Set an error in a status-code option (from set_status_code).
882 set_error(struct lq6_state
*lq
, u_int16_t code
, const char *message
) {
883 struct data_string d
;
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.");
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.");
902 data_string_forget(&d
, MDL
);
907 * Process a by-address lease query.
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
;
925 oc
= lookup_option(&dhcpv6_universe
, lq
->query_opts
, D6O_IAADDR
);
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.");
935 memset(&data
, 0, sizeof(data
));
936 if (!evaluate_option_cache(&data
, packet
,
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.");
944 memcpy(&addr
, data
.data
, sizeof(addr
));
945 data_string_forget(&data
, MDL
);
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.
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.");
963 if (iasubopt_hash_lookup(&iaaddr
, pool
->leases
, &addr
,
964 sizeof(addr
), MDL
) == 0) {
968 if ((iaaddr
== NULL
) || (iaaddr
->state
!= FTS_ACTIVE
) ||
969 (iaaddr
->ia
== NULL
) || (iaaddr
->ia
->iaid_duid
.len
<= 4)) {
975 * Build the client-data option (with client-id, ia-addr and clt-time).
977 if (!option_state_allocate(&opt_state
, MDL
)) {
978 log_error("process_lq_by_address: "
979 "no memory for option state.");
983 data_string_copy(&data
, &iaaddr
->ia
->iaid_duid
, MDL
);
986 if (!save_option_buffer(&dhcpv6_universe
, opt_state
,
987 NULL
, (unsigned char *)data
.data
, data
.len
,
989 log_error("process_lq_by_address: error saving client ID.");
992 data_string_forget(&data
, MDL
);
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.");
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
,
1008 log_error("process_lq_by_address: error saving ia-addr.");
1011 data_string_forget(&data
, MDL
);
1013 lifetime
= htonl(iaaddr
->ia
->cltt
);
1014 if (!save_option_buffer(&dhcpv6_universe
, opt_state
,
1015 NULL
, (unsigned char *)&lifetime
, 4,
1017 log_error("process_lq_by_address: error saving clt time.");
1022 * Store the client-data option.
1024 opt_cursor
= lq
->cursor
;
1025 putUShort(lq
->buf
.data
+ lq
->cursor
, (unsigned)D6O_CLIENT_DATA
);
1027 /* Skip option length. */
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));
1042 if (data
.data
!= NULL
)
1043 data_string_forget(&data
, MDL
);
1045 ipv6_pool_dereference(&pool
, MDL
);
1047 iasubopt_dereference(&iaaddr
, MDL
);
1048 if (opt_state
!= NULL
)
1049 option_state_dereference(&opt_state
, MDL
);
1055 * Process a lease query.
1058 dhcpv6_leasequery(struct data_string
*reply_ret
, struct packet
*packet
) {
1059 static struct lq6_state lq
;
1060 struct option_cache
*oc
;
1064 * Initialize the lease query state.
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
);
1075 * Validate our input.
1077 if (!valid_query_msg(&lq
)) {
1082 * Prepare our reply.
1084 if (!option_state_allocate(&lq
.reply_opts
, MDL
)) {
1085 log_error("dhcpv6_leasequery: no memory for option state.");
1088 execute_statements_in_scope(NULL
, lq
.packet
, NULL
, NULL
,
1089 lq
.packet
->options
, lq
.reply_opts
,
1090 &global_scope
, root_group
, NULL
, NULL
);
1092 lq
.buf
.reply
.msg_type
= DHCPV6_LEASEQUERY_REPLY
;
1094 memcpy(lq
.buf
.reply
.transaction_id
,
1095 lq
.packet
->dhcpv6_transaction_id
,
1096 sizeof(lq
.buf
.reply
.transaction_id
));
1099 * Because LEASEQUERY has some privacy concerns, default to deny.
1104 * See if we are authorized to do LEASEQUERY.
1106 oc
= lookup_option(&server_universe
, lq
.reply_opts
, SV_LEASEQUERY
);
1108 allow_lq
= evaluate_boolean_option_cache(NULL
,
1118 log_info("dhcpv6_leasequery: not allowed, query ignored.");
1123 * Same than transmission of REPLY message in RFC 3315:
1128 oc
= lookup_option(&dhcpv6_universe
, lq
.reply_opts
, D6O_SERVERID
);
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
,
1136 (unsigned char *)lq
.server_id
.data
,
1140 log_error("dhcpv6_leasequery: "
1141 "error saving server identifier.");
1146 if (!save_option_buffer(&dhcpv6_universe
,
1148 lq
.client_id
.buffer
,
1149 (unsigned char *)lq
.client_id
.data
,
1153 log_error("dhcpv6_leasequery: "
1154 "error saving client identifier.");
1161 * Decode the lq-query option.
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.");
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
:
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.");
1188 if (!set_error(&lq
, STATUS_UnknownQueryType
,
1189 "Unknown query-type.")) {
1190 log_error("dhcpv6_leasequery: unable to "
1191 "set UnknownQueryType status code.");
1197 if (!option_state_allocate(&lq
.query_opts
, MDL
)) {
1198 log_error("dhcpv6_leasequery: no memory for option state.");
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.");
1216 if (!process_lq_by_address(&lq
))
1220 /* Store the options. */
1221 lq
.cursor
+= store_options6((char *)lq
.buf
.data
+ lq
.cursor
,
1222 sizeof(lq
.buf
) - lq
.cursor
,
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.");
1234 memcpy(reply_ret
->buffer
->data
, lq
.buf
.data
, lq
.cursor
);
1235 reply_ret
->data
= reply_ret
->buffer
->data
;
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
);