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 #if defined(RELAY_PORT)
156 u_int16_t relay_port
= 0;
158 struct sockaddr_in to
;
159 struct in_addr siaddr
;
160 struct data_string prl
;
161 struct data_string
*prl_ptr
;
164 struct interface_info
*interface
;
166 /* INSIST(packet != NULL); */
169 * Prepare log information.
171 snprintf(msgbuf
, sizeof(msgbuf
),
172 "DHCPLEASEQUERY from %s", inet_ntoa(packet
->raw
->giaddr
));
175 * We can't reply if there is no giaddr field.
178 * Note: this makes DHCPv4-over-DHCPv6 always fail but it should not
179 * really be a problem because it is not a specified use case
180 * (or even one that makes sense).
182 if (!packet
->raw
->giaddr
.s_addr
) {
183 log_info("%s: missing giaddr, ciaddr is %s, no reply sent",
184 msgbuf
, inet_ntoa(packet
->raw
->ciaddr
));
189 * Initially we use the 'giaddr' subnet options scope to determine if
190 * the giaddr-identified relay agent is permitted to perform a
191 * leasequery. The subnet is not required, and may be omitted, in
192 * which case we are essentially interrogating the root options class
193 * to find a globally permit.
195 gip
.len
= sizeof(packet
->raw
->giaddr
);
196 memcpy(gip
.iabuf
, &packet
->raw
->giaddr
, sizeof(packet
->raw
->giaddr
));
199 find_subnet(&subnet
, gip
, MDL
);
201 relay_group
= subnet
->group
;
203 relay_group
= root_group
;
205 subnet_dereference(&subnet
, MDL
);
208 if (!option_state_allocate(&options
, MDL
)) {
209 log_error("No memory for option state.");
210 log_info("%s: out of memory, no reply sent", msgbuf
);
214 execute_statements_in_scope(NULL
, packet
, NULL
, NULL
, packet
->options
,
215 options
, &global_scope
, relay_group
,
218 for (i
=packet
->class_count
-1; i
>=0; i
--) {
219 execute_statements_in_scope(NULL
, packet
, NULL
, NULL
,
220 packet
->options
, options
,
222 packet
->classes
[i
]->group
,
227 * Because LEASEQUERY has some privacy concerns, default to deny.
229 allow_leasequery
= 0;
232 * See if we are authorized to do LEASEQUERY.
234 oc
= lookup_option(&server_universe
, options
, SV_LEASEQUERY
);
236 allow_leasequery
= evaluate_boolean_option_cache(&ignorep
,
237 packet
, NULL
, NULL
, packet
->options
,
238 options
, &global_scope
, oc
, MDL
);
241 if (!allow_leasequery
) {
242 log_info("%s: LEASEQUERY not allowed, query ignored", msgbuf
);
243 option_state_dereference(&options
, MDL
);
249 * Copy out the client IP address.
251 cip
.len
= sizeof(packet
->raw
->ciaddr
);
252 memcpy(cip
.iabuf
, &packet
->raw
->ciaddr
, sizeof(packet
->raw
->ciaddr
));
255 * If the client IP address is valid (not all zero), then we
256 * are looking for information about that IP address.
259 lease
= tmp_lease
= NULL
;
260 if (memcmp(cip
.iabuf
, "\0\0\0", 4)) {
262 want_associated_ip
= 0;
264 snprintf(dbg_info
, sizeof(dbg_info
), "IP %s", piaddr(cip
));
265 find_lease_by_ip_addr(&lease
, cip
, MDL
);
270 want_associated_ip
= 1;
273 * If the client IP address is all zero, then we will
274 * either look up by the client identifier (if we have
275 * one), or by the MAC address.
278 memset(&uid
, 0, sizeof(uid
));
288 DHO_DHCP_CLIENT_IDENTIFIER
,
294 print_hex_1(uid
.len
, uid
.data
, 60));
296 find_lease_by_uid(&tmp_lease
, uid
.data
, uid
.len
, MDL
);
297 data_string_forget(&uid
, MDL
);
298 get_newest_lease(&lease
, tmp_lease
, next_uid
);
299 assoc_ip_cnt
= get_associated_ips(tmp_lease
,
307 if (packet
->raw
->hlen
+1 > sizeof(h
.hbuf
)) {
308 log_info("%s: hardware length too long, "
309 "no reply sent", msgbuf
);
310 option_state_dereference(&options
, MDL
);
314 h
.hlen
= packet
->raw
->hlen
+ 1;
315 h
.hbuf
[0] = packet
->raw
->htype
;
323 print_hw_addr(h
.hbuf
[0],
327 find_lease_by_hw_addr(&tmp_lease
, h
.hbuf
, h
.hlen
, MDL
);
328 get_newest_lease(&lease
, tmp_lease
, next_hw
);
329 assoc_ip_cnt
= get_associated_ips(tmp_lease
,
337 lease_dereference(&tmp_lease
, MDL
);
340 memcpy(&packet
->raw
->ciaddr
,
341 lease
->ip_addr
.iabuf
,
342 sizeof(packet
->raw
->ciaddr
));
346 * Log if we have too many IP addresses associated
349 if (want_associated_ip
&& (assoc_ip_cnt
> nassoc_ips
)) {
350 log_info("%d IP addresses associated with %s, "
351 "only %d sent in reply.",
352 assoc_ip_cnt
, dbg_info
, nassoc_ips
);
357 * We now know the query target too, so can report this in
360 snprintf(msgbuf
, sizeof(msgbuf
),
361 "DHCPLEASEQUERY from %s for %s",
362 inet_ntoa(packet
->raw
->giaddr
), dbg_info
);
365 * Figure our our return type.
368 dhcpMsgType
= DHCPLEASEUNKNOWN
;
369 dhcp_msg_type_name
= "DHCPLEASEUNKNOWN";
371 if (lease
->binding_state
== FTS_ACTIVE
) {
372 dhcpMsgType
= DHCPLEASEACTIVE
;
373 dhcp_msg_type_name
= "DHCPLEASEACTIVE";
375 dhcpMsgType
= DHCPLEASEUNASSIGNED
;
376 dhcp_msg_type_name
= "DHCPLEASEUNASSIGNED";
381 * Set options that only make sense if we have an active lease.
384 if (dhcpMsgType
== DHCPLEASEACTIVE
)
387 * RFC 4388 uses the PRL to request options for the agent to
388 * receive that are "about" the client. It is confusing
389 * because in some cases it wants to know what was sent to
390 * the client (lease times, adjusted), and in others it wants
391 * to know information the client sent. You're supposed to
392 * know this on a case-by-case basis.
394 * "Name servers", "domain name", and the like from the relay
395 * agent's scope seems less than useful. Our options are to
396 * restart the option cache from the lease's best point of view
397 * (execute statements from the lease pool's group), or to
398 * simply restart the option cache from empty.
400 * I think restarting the option cache from empty best
401 * approaches RFC 4388's intent; specific options are included.
403 option_state_dereference(&options
, MDL
);
405 if (!option_state_allocate(&options
, MDL
)) {
406 log_error("%s: out of memory, no reply sent", msgbuf
);
407 lease_dereference(&lease
, MDL
);
412 * Set the hardware address fields.
415 packet
->raw
->hlen
= lease
->hardware_addr
.hlen
- 1;
416 packet
->raw
->htype
= lease
->hardware_addr
.hbuf
[0];
417 memcpy(packet
->raw
->chaddr
,
418 &lease
->hardware_addr
.hbuf
[1],
419 sizeof(packet
->raw
->chaddr
));
422 * Set client identifier option.
424 if (lease
->uid_len
> 0) {
425 if (!add_option(options
,
426 DHO_DHCP_CLIENT_IDENTIFIER
,
429 option_state_dereference(&options
, MDL
);
430 lease_dereference(&lease
, MDL
);
431 log_info("%s: out of memory, no reply sent",
439 * Calculate T1 and T2, the times when the client
440 * tries to extend its lease on its networking
442 * These seem to be hard-coded in ISC DHCP, to 0.5 and
443 * 0.875 of the lease time.
446 lease_duration
= lease
->ends
- lease
->starts
;
447 time_renewal
= lease
->starts
+
448 (lease_duration
/ 2);
449 time_rebinding
= lease
->starts
+
450 (lease_duration
/ 2) +
451 (lease_duration
/ 4) +
452 (lease_duration
/ 8);
454 if (time_renewal
> cur_time
) {
455 time_renewal
= htonl(time_renewal
- cur_time
);
457 if (!add_option(options
,
458 DHO_DHCP_RENEWAL_TIME
,
460 sizeof(time_renewal
))) {
461 option_state_dereference(&options
, MDL
);
462 lease_dereference(&lease
, MDL
);
463 log_info("%s: out of memory, no reply sent",
469 if (time_rebinding
> cur_time
) {
470 time_rebinding
= htonl(time_rebinding
- cur_time
);
472 if (!add_option(options
,
473 DHO_DHCP_REBINDING_TIME
,
475 sizeof(time_rebinding
))) {
476 option_state_dereference(&options
, MDL
);
477 lease_dereference(&lease
, MDL
);
478 log_info("%s: out of memory, no reply sent",
484 if (lease
->ends
> cur_time
) {
485 time_expiry
= htonl(lease
->ends
- cur_time
);
487 if (!add_option(options
,
490 sizeof(time_expiry
))) {
491 option_state_dereference(&options
, MDL
);
492 lease_dereference(&lease
, MDL
);
493 log_info("%s: out of memory, no reply sent",
499 /* Supply the Vendor-Class-Identifier. */
500 if (lease
->scope
!= NULL
) {
501 struct data_string vendor_class
;
503 memset(&vendor_class
, 0, sizeof(vendor_class
));
505 if (find_bound_string(&vendor_class
, lease
->scope
,
506 "vendor-class-identifier")) {
507 if (!add_option(options
,
508 DHO_VENDOR_CLASS_IDENTIFIER
,
509 (void *)vendor_class
.data
,
511 option_state_dereference(&options
,
513 lease_dereference(&lease
, MDL
);
514 log_error("%s: error adding vendor "
515 "class identifier, no reply "
517 data_string_forget(&vendor_class
, MDL
);
520 data_string_forget(&vendor_class
, MDL
);
525 * Set the relay agent info.
527 * Note that because agent info is appended without regard
528 * to the PRL in cons_options(), this will be sent as the
529 * last option in the packet whether it is listed on PRL or
533 if (lease
->agent_options
!= NULL
) {
534 int idx
= agent_universe
.index
;
535 struct option_chain_head
**tmp1
=
536 (struct option_chain_head
**)
537 &(options
->universes
[idx
]);
538 struct option_chain_head
*tmp2
=
539 (struct option_chain_head
*)
540 lease
->agent_options
;
542 option_chain_head_reference(tmp1
, tmp2
, MDL
);
546 * Set the client last transaction time.
547 * We check to make sure we have a timestamp. For
548 * lease files that were saved before running a
549 * timestamp-aware version of the server, this may
553 if (lease
->cltt
!= MIN_TIME
) {
554 if (cur_time
> lease
->cltt
) {
555 client_last_transaction_time
=
556 htonl(cur_time
- lease
->cltt
);
558 client_last_transaction_time
= htonl(0);
560 if (!add_option(options
,
561 DHO_CLIENT_LAST_TRANSACTION_TIME
,
562 &client_last_transaction_time
,
563 sizeof(client_last_transaction_time
))) {
564 option_state_dereference(&options
, MDL
);
565 lease_dereference(&lease
, MDL
);
566 log_info("%s: out of memory, no reply sent",
573 * Set associated IPs, if requested and there are some.
575 if (want_associated_ip
&& (assoc_ip_cnt
> 0)) {
576 if (!add_option(options
,
579 assoc_ip_cnt
* sizeof(assoc_ips
[0]))) {
580 option_state_dereference(&options
, MDL
);
581 lease_dereference(&lease
, MDL
);
582 log_info("%s: out of memory, no reply sent",
590 * Set the message type.
593 packet
->raw
->op
= BOOTREPLY
;
596 * Set DHCP message type.
598 if (!add_option(options
,
599 DHO_DHCP_MESSAGE_TYPE
,
601 sizeof(dhcpMsgType
))) {
602 option_state_dereference(&options
, MDL
);
603 lease_dereference(&lease
, MDL
);
604 log_info("%s: error adding option, no reply sent", msgbuf
);
609 * Log the message we've received.
611 log_info("%s", msgbuf
);
614 * Figure out which address to use to send from.
616 get_server_source_address(&siaddr
, options
, options
, packet
);
619 * Set up the option buffer.
622 memset(&prl
, 0, sizeof(prl
));
623 oc
= lookup_option(&dhcp_universe
, options
,
624 DHO_DHCP_PARAMETER_REQUEST_LIST
);
626 evaluate_option_cache(&prl
,
642 packet
->packet_length
= cons_options(packet
,
656 data_string_forget(&prl
, MDL
); /* SK: safe, even if empty */
657 option_state_dereference(&options
, MDL
);
658 lease_dereference(&lease
, MDL
);
660 to
.sin_family
= AF_INET
;
662 to
.sin_len
= sizeof(to
);
664 memset(to
.sin_zero
, 0, sizeof(to
.sin_zero
));
666 #if defined(RELAY_PORT)
667 relay_port
= dhcp_check_relayport(packet
);
671 * Leasequery packets are be sent to the gateway address.
673 to
.sin_addr
= packet
->raw
->giaddr
;
674 if (packet
->raw
->giaddr
.s_addr
!= htonl(INADDR_LOOPBACK
)) {
675 #if defined(RELAY_PORT)
676 to
.sin_port
= relay_port
? relay_port
: local_port
;
678 to
.sin_port
= local_port
;
681 to
.sin_port
= remote_port
; /* XXXSK: For debugging. */
685 * The fallback_interface lets us send with a real IP
686 * address. The packet interface sends from all-zeros.
688 if (fallback_interface
!= NULL
) {
689 interface
= fallback_interface
;
691 interface
= packet
->interface
;
695 * Report what we're sending.
697 log_info("%s to %s for %s (%d associated IPs)",
699 inet_ntoa(to
.sin_addr
), dbg_info
, assoc_ip_cnt
);
701 send_packet(interface
,
704 packet
->packet_length
,
713 * TODO: RFC5007 query-by-clientid.
715 * TODO: RFC5007 look at the pools according to the link-address.
717 * TODO: get fixed leases too.
719 * TODO: RFC5007 ORO in query-options.
721 * TODO: RFC5007 lq-relay-data.
723 * TODO: RFC5007 lq-client-link.
725 * Note: the code is still nearly compliant and usable for the target
726 * case with these missing features!
730 * The structure to handle a leasequery.
733 struct packet
*packet
;
734 struct data_string client_id
;
735 struct data_string server_id
;
736 struct data_string lq_query
;
738 struct in6_addr link_addr
;
739 struct option_state
*query_opts
;
741 struct option_state
*reply_opts
;
744 unsigned char data
[65536];
745 struct dhcpv6_packet reply
;
750 * Options that we want to send.
752 static const int required_opts_lq
[] = {
761 static const int required_opt_CLIENT_DATA
[] = {
770 * Get the lq-query option from the packet.
773 get_lq_query(struct lq6_state
*lq
)
775 struct data_string
*lq_query
= &lq
->lq_query
;
776 struct packet
*packet
= lq
->packet
;
777 struct option_cache
*oc
;
780 * Verify our lq_query structure is empty.
782 if ((lq_query
->data
!= NULL
) || (lq_query
->len
!= 0)) {
783 return DHCP_R_INVALIDARG
;
786 oc
= lookup_option(&dhcpv6_universe
, packet
->options
, D6O_LQ_QUERY
);
788 return ISC_R_NOTFOUND
;
791 if (!evaluate_option_cache(lq_query
, packet
, NULL
, NULL
,
792 packet
->options
, NULL
,
793 &global_scope
, oc
, MDL
)) {
794 return ISC_R_FAILURE
;
797 return ISC_R_SUCCESS
;
801 * Message validation, RFC 5007 section 4.2.1:
802 * dhcpv6.c:valid_client_msg() - unicast + lq-query option.
805 valid_query_msg(struct lq6_state
*lq
) {
806 struct packet
*packet
= lq
->packet
;
808 struct option_cache
*oc
;
810 /* INSIST((lq != NULL) || (packet != NULL)); */
812 switch (get_client_id(packet
, &lq
->client_id
)) {
816 log_debug("Discarding %s from %s; "
817 "client identifier missing",
818 dhcpv6_type_names
[packet
->dhcpv6_msg_type
],
819 piaddr(packet
->client_addr
));
822 log_error("Error processing %s from %s; "
823 "unable to evaluate Client Identifier",
824 dhcpv6_type_names
[packet
->dhcpv6_msg_type
],
825 piaddr(packet
->client_addr
));
829 oc
= lookup_option(&dhcpv6_universe
, packet
->options
, D6O_SERVERID
);
831 if (evaluate_option_cache(&lq
->server_id
, packet
, NULL
, NULL
,
832 packet
->options
, NULL
,
833 &global_scope
, oc
, MDL
)) {
834 log_debug("Discarding %s from %s; "
835 "server identifier found "
836 "(CLIENTID %s, SERVERID %s)",
837 dhcpv6_type_names
[packet
->dhcpv6_msg_type
],
838 piaddr(packet
->client_addr
),
839 print_hex_1(lq
->client_id
.len
,
840 lq
->client_id
.data
, 60),
841 print_hex_2(lq
->server_id
.len
,
842 lq
->server_id
.data
, 60));
844 log_debug("Discarding %s from %s; "
845 "server identifier found "
847 dhcpv6_type_names
[packet
->dhcpv6_msg_type
],
848 print_hex_1(lq
->client_id
.len
,
849 lq
->client_id
.data
, 60),
850 piaddr(packet
->client_addr
));
855 switch (get_lq_query(lq
)) {
859 log_debug("Discarding %s from %s; lq-query missing",
860 dhcpv6_type_names
[packet
->dhcpv6_msg_type
],
861 piaddr(packet
->client_addr
));
864 log_error("Error processing %s from %s; "
865 "unable to evaluate LQ-Query",
866 dhcpv6_type_names
[packet
->dhcpv6_msg_type
],
867 piaddr(packet
->client_addr
));
876 data_string_forget(&lq
->client_id
, MDL
);
877 data_string_forget(&lq
->server_id
, MDL
);
878 data_string_forget(&lq
->lq_query
, MDL
);
884 * Set an error in a status-code option (from set_status_code).
887 set_error(struct lq6_state
*lq
, u_int16_t code
, const char *message
) {
888 struct data_string d
;
891 memset(&d
, 0, sizeof(d
));
892 d
.len
= sizeof(code
) + strlen(message
);
893 if (!buffer_allocate(&d
.buffer
, d
.len
, MDL
)) {
894 log_fatal("set_error: no memory for status code.");
896 d
.data
= d
.buffer
->data
;
897 putUShort(d
.buffer
->data
, code
);
898 memcpy(d
.buffer
->data
+ sizeof(code
), message
, d
.len
- sizeof(code
));
899 if (!save_option_buffer(&dhcpv6_universe
, lq
->reply_opts
,
900 d
.buffer
, (unsigned char *)d
.data
, d
.len
,
901 D6O_STATUS_CODE
, 0)) {
902 log_error("set_error: error saving status code.");
907 data_string_forget(&d
, MDL
);
912 * Process a by-address lease query.
915 process_lq_by_address(struct lq6_state
*lq
) {
916 struct packet
*packet
= lq
->packet
;
917 struct option_cache
*oc
;
918 struct ipv6_pool
*pool
= NULL
;
919 struct data_string data
;
920 struct in6_addr addr
;
921 struct iasubopt
*iaaddr
= NULL
;
922 struct option_state
*opt_state
= NULL
;
930 oc
= lookup_option(&dhcpv6_universe
, lq
->query_opts
, D6O_IAADDR
);
932 if (!set_error(lq
, STATUS_MalformedQuery
,
933 "No OPTION_IAADDR.")) {
934 log_error("process_lq_by_address: unable "
935 "to set MalformedQuery status code.");
940 memset(&data
, 0, sizeof(data
));
941 if (!evaluate_option_cache(&data
, packet
,
943 lq
->query_opts
, NULL
,
944 &global_scope
, oc
, MDL
) ||
945 (data
.len
< IAADDR_OFFSET
)) {
946 log_error("process_lq_by_address: error evaluating IAADDR.");
949 memcpy(&addr
, data
.data
, sizeof(addr
));
950 data_string_forget(&data
, MDL
);
954 * Note the RFC 5007 says to use the link-address to find the link
955 * or the ia-aadr when it is :: but in any case the ia-addr has
956 * to be on the link, so we ignore the link-address here.
958 if (find_ipv6_pool(&pool
, D6O_IA_NA
, &addr
) != ISC_R_SUCCESS
) {
959 if (!set_error(lq
, STATUS_NotConfigured
,
960 "Address not in a pool.")) {
961 log_error("process_lq_by_address: unable "
962 "to set NotConfigured status code.");
968 if (iasubopt_hash_lookup(&iaaddr
, pool
->leases
, &addr
,
969 sizeof(addr
), MDL
) == 0) {
973 if ((iaaddr
== NULL
) || (iaaddr
->state
!= FTS_ACTIVE
) ||
974 (iaaddr
->ia
== NULL
) || (iaaddr
->ia
->iaid_duid
.len
<= 4)) {
980 * Build the client-data option (with client-id, ia-addr and clt-time).
982 if (!option_state_allocate(&opt_state
, MDL
)) {
983 log_error("process_lq_by_address: "
984 "no memory for option state.");
988 data_string_copy(&data
, &iaaddr
->ia
->iaid_duid
, MDL
);
991 if (!save_option_buffer(&dhcpv6_universe
, opt_state
,
992 NULL
, (unsigned char *)data
.data
, data
.len
,
994 log_error("process_lq_by_address: error saving client ID.");
997 data_string_forget(&data
, MDL
);
999 data
.len
= IAADDR_OFFSET
;
1000 if (!buffer_allocate(&data
.buffer
, data
.len
, MDL
)) {
1001 log_error("process_lq_by_address: no memory for ia-addr.");
1004 data
.data
= data
.buffer
->data
;
1005 memcpy(data
.buffer
->data
, &iaaddr
->addr
, 16);
1006 lifetime
= iaaddr
->prefer
;
1007 putULong(data
.buffer
->data
+ 16, lifetime
);
1008 lifetime
= iaaddr
->valid
;
1009 putULong(data
.buffer
->data
+ 20, lifetime
);
1010 if (!save_option_buffer(&dhcpv6_universe
, opt_state
,
1011 NULL
, (unsigned char *)data
.data
, data
.len
,
1013 log_error("process_lq_by_address: error saving ia-addr.");
1016 data_string_forget(&data
, MDL
);
1018 lifetime
= htonl(iaaddr
->ia
->cltt
);
1019 if (!save_option_buffer(&dhcpv6_universe
, opt_state
,
1020 NULL
, (unsigned char *)&lifetime
, 4,
1022 log_error("process_lq_by_address: error saving clt time.");
1027 * Store the client-data option.
1029 opt_cursor
= lq
->cursor
;
1030 putUShort(lq
->buf
.data
+ lq
->cursor
, (unsigned)D6O_CLIENT_DATA
);
1032 /* Skip option length. */
1035 lq
->cursor
+= store_options6((char *)lq
->buf
.data
+ lq
->cursor
,
1036 sizeof(lq
->buf
) - lq
->cursor
,
1037 opt_state
, lq
->packet
,
1038 required_opt_CLIENT_DATA
, NULL
);
1039 /* Reset the length. */
1040 putUShort(lq
->buf
.data
+ opt_cursor
+ 2,
1041 lq
->cursor
- (opt_cursor
+ 4));
1047 if (data
.data
!= NULL
)
1048 data_string_forget(&data
, MDL
);
1050 ipv6_pool_dereference(&pool
, MDL
);
1052 iasubopt_dereference(&iaaddr
, MDL
);
1053 if (opt_state
!= NULL
)
1054 option_state_dereference(&opt_state
, MDL
);
1060 * Process a lease query.
1063 dhcpv6_leasequery(struct data_string
*reply_ret
, struct packet
*packet
) {
1064 static struct lq6_state lq
;
1065 struct option_cache
*oc
;
1069 * Initialize the lease query state.
1072 memset(&lq
.client_id
, 0, sizeof(lq
.client_id
));
1073 memset(&lq
.server_id
, 0, sizeof(lq
.server_id
));
1074 memset(&lq
.lq_query
, 0, sizeof(lq
.lq_query
));
1075 lq
.query_opts
= NULL
;
1076 lq
.reply_opts
= NULL
;
1077 packet_reference(&lq
.packet
, packet
, MDL
);
1080 * Validate our input.
1082 if (!valid_query_msg(&lq
)) {
1087 * Prepare our reply.
1089 if (!option_state_allocate(&lq
.reply_opts
, MDL
)) {
1090 log_error("dhcpv6_leasequery: no memory for option state.");
1093 execute_statements_in_scope(NULL
, lq
.packet
, NULL
, NULL
,
1094 lq
.packet
->options
, lq
.reply_opts
,
1095 &global_scope
, root_group
, NULL
, NULL
);
1097 lq
.buf
.reply
.msg_type
= DHCPV6_LEASEQUERY_REPLY
;
1099 memcpy(lq
.buf
.reply
.transaction_id
,
1100 lq
.packet
->dhcpv6_transaction_id
,
1101 sizeof(lq
.buf
.reply
.transaction_id
));
1104 * Because LEASEQUERY has some privacy concerns, default to deny.
1109 * See if we are authorized to do LEASEQUERY.
1111 oc
= lookup_option(&server_universe
, lq
.reply_opts
, SV_LEASEQUERY
);
1113 allow_lq
= evaluate_boolean_option_cache(NULL
,
1123 log_info("dhcpv6_leasequery: not allowed, query ignored.");
1128 * Same than transmission of REPLY message in RFC 3315:
1133 oc
= lookup_option(&dhcpv6_universe
, lq
.reply_opts
, D6O_SERVERID
);
1135 /* If not already in options, get from query then global. */
1136 if (lq
.server_id
.data
== NULL
)
1137 copy_server_duid(&lq
.server_id
, MDL
);
1138 if (!save_option_buffer(&dhcpv6_universe
,
1141 (unsigned char *)lq
.server_id
.data
,
1145 log_error("dhcpv6_leasequery: "
1146 "error saving server identifier.");
1151 if (!save_option_buffer(&dhcpv6_universe
,
1153 lq
.client_id
.buffer
,
1154 (unsigned char *)lq
.client_id
.data
,
1158 log_error("dhcpv6_leasequery: "
1159 "error saving client identifier.");
1166 * Decode the lq-query option.
1169 if (lq
.lq_query
.len
<= LQ_QUERY_OFFSET
) {
1170 if (!set_error(&lq
, STATUS_MalformedQuery
,
1171 "OPTION_LQ_QUERY too short.")) {
1172 log_error("dhcpv6_leasequery: unable "
1173 "to set MalformedQuery status code.");
1179 lq
.query_type
= lq
.lq_query
.data
[0];
1180 memcpy(&lq
.link_addr
, lq
.lq_query
.data
+ 1, sizeof(lq
.link_addr
));
1181 switch (lq
.query_type
) {
1182 case LQ6QT_BY_ADDRESS
:
1184 case LQ6QT_BY_CLIENTID
:
1185 if (!set_error(&lq
, STATUS_UnknownQueryType
,
1186 "QUERY_BY_CLIENTID not supported.")) {
1187 log_error("dhcpv6_leasequery: unable to "
1188 "set UnknownQueryType status code.");
1193 if (!set_error(&lq
, STATUS_UnknownQueryType
,
1194 "Unknown query-type.")) {
1195 log_error("dhcpv6_leasequery: unable to "
1196 "set UnknownQueryType status code.");
1202 if (!option_state_allocate(&lq
.query_opts
, MDL
)) {
1203 log_error("dhcpv6_leasequery: no memory for option state.");
1206 if (!parse_option_buffer(lq
.query_opts
,
1207 lq
.lq_query
.data
+ LQ_QUERY_OFFSET
,
1208 lq
.lq_query
.len
- LQ_QUERY_OFFSET
,
1209 &dhcpv6_universe
)) {
1210 log_error("dhcpv6_leasequery: error parsing query-options.");
1211 if (!set_error(&lq
, STATUS_MalformedQuery
,
1212 "Bad query-options.")) {
1213 log_error("dhcpv6_leasequery: unable "
1214 "to set MalformedQuery status code.");
1221 if (!process_lq_by_address(&lq
))
1225 /* Store the options. */
1226 lq
.cursor
+= store_options6((char *)lq
.buf
.data
+ lq
.cursor
,
1227 sizeof(lq
.buf
) - lq
.cursor
,
1233 /* Return our reply to the caller. */
1234 reply_ret
->len
= lq
.cursor
;
1235 reply_ret
->buffer
= NULL
;
1236 if (!buffer_allocate(&reply_ret
->buffer
, lq
.cursor
, MDL
)) {
1237 log_fatal("dhcpv6_leasequery: no memory to store Reply.");
1239 memcpy(reply_ret
->buffer
->data
, lq
.buf
.data
, lq
.cursor
);
1240 reply_ret
->data
= reply_ret
->buffer
->data
;
1244 if (lq
.packet
!= NULL
)
1245 packet_dereference(&lq
.packet
, MDL
);
1246 if (lq
.client_id
.data
!= NULL
)
1247 data_string_forget(&lq
.client_id
, MDL
);
1248 if (lq
.server_id
.data
!= NULL
)
1249 data_string_forget(&lq
.server_id
, MDL
);
1250 if (lq
.lq_query
.data
!= NULL
)
1251 data_string_forget(&lq
.lq_query
, MDL
);
1252 if (lq
.query_opts
!= NULL
)
1253 option_state_dereference(&lq
.query_opts
, MDL
);
1254 if (lq
.reply_opts
!= NULL
)
1255 option_state_dereference(&lq
.reply_opts
, MDL
);