2 * Copyright (C) 2011-2013 by Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 2006-2007,2009 by Internet Systems Consortium, Inc. ("ISC")
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 * PERFORMANCE OF THIS SOFTWARE.
21 * TODO: RFC4388 specifies that the server SHOULD return the same
22 * options it would for a DHCREQUEST message, if no Parameter
23 * Request List option (option 55) is passed. We do not do that.
25 * TODO: RFC4388 specifies the creation of a "non-sensitive options"
26 * configuration list, and that these SHOULD be returned. We
29 * TODO: RFC4388 says the server SHOULD use RFC3118, "Authentication
32 * TODO: RFC4388 specifies that you SHOULD insure that you cannot be
33 * DoS'ed by DHCPLEASEQUERY message.
37 * If you query by hardware address or by client ID, then you may have
38 * more than one IP address for your query argument. We need to do two
41 * 1. Find the most recent lease.
42 * 2. Find all additional IP addresses for the query argument.
44 * We do this by looking through all of the leases associated with a
45 * given hardware address or client ID. We use the cltt (client last
46 * transaction time) of the lease, which only has a resolution of one
47 * second, so we might not actually give the very latest IP.
51 next_hw(const struct lease
*lease
) {
52 /* INSIST(lease != NULL); */
57 next_uid(const struct lease
*lease
) {
58 /* INSIST(lease != NULL); */
63 get_newest_lease(struct lease
**retval
,
65 struct lease
*(*next
)(const struct lease
*)) {
70 /* INSIST(newest != NULL); */
71 /* INSIST(next != NULL); */
80 for (p
=next(lease
); p
!= NULL
; p
=next(p
)) {
81 if (newest
->binding_state
== FTS_ACTIVE
) {
82 if ((p
->binding_state
== FTS_ACTIVE
) &&
83 (p
->cltt
> newest
->cltt
)) {
87 if (p
->ends
> newest
->ends
) {
93 lease_reference(retval
, newest
, MDL
);
97 get_associated_ips(const struct lease
*lease
,
98 struct lease
*(*next
)(const struct lease
*),
99 const struct lease
*newest
,
100 u_int32_t
*associated_ips
,
101 unsigned int associated_ips_size
) {
103 const struct lease
*p
;
106 /* INSIST(next != NULL); */
107 /* INSIST(associated_ips != NULL); */
114 for (p
=lease
; p
!= NULL
; p
=next(p
)) {
115 if ((p
->binding_state
== FTS_ACTIVE
) && (p
!= newest
)) {
116 if (cnt
< associated_ips_size
) {
117 memcpy(&associated_ips
[cnt
],
119 sizeof(associated_ips
[cnt
]));
129 dhcpleasequery(struct packet
*packet
, int ms_nulltp
) {
134 struct data_string uid
;
136 struct lease
*tmp_lease
;
138 int want_associated_ip
;
140 u_int32_t assoc_ips
[40]; /* XXXSK: arbitrary maximum number of IPs */
141 const int nassoc_ips
= sizeof(assoc_ips
) / sizeof(assoc_ips
[0]);
143 unsigned char dhcpMsgType
;
144 const char *dhcp_msg_type_name
;
145 struct subnet
*subnet
;
146 struct group
*relay_group
;
147 struct option_state
*options
;
148 struct option_cache
*oc
;
149 int allow_leasequery
;
151 u_int32_t lease_duration
;
152 u_int32_t time_renewal
;
153 u_int32_t time_rebinding
;
154 u_int32_t time_expiry
;
155 u_int32_t client_last_transaction_time
;
156 struct sockaddr_in to
;
157 struct in_addr siaddr
;
158 struct data_string prl
;
159 struct data_string
*prl_ptr
;
162 struct interface_info
*interface
;
164 /* INSIST(packet != NULL); */
167 * Prepare log information.
169 snprintf(msgbuf
, sizeof(msgbuf
),
170 "DHCPLEASEQUERY from %s", inet_ntoa(packet
->raw
->giaddr
));
173 * We can't reply if there is no giaddr field.
175 if (!packet
->raw
->giaddr
.s_addr
) {
176 log_info("%s: missing giaddr, ciaddr is %s, no reply sent",
177 msgbuf
, inet_ntoa(packet
->raw
->ciaddr
));
182 * Initially we use the 'giaddr' subnet options scope to determine if
183 * the giaddr-identified relay agent is permitted to perform a
184 * leasequery. The subnet is not required, and may be omitted, in
185 * which case we are essentially interrogating the root options class
186 * to find a globally permit.
188 gip
.len
= sizeof(packet
->raw
->giaddr
);
189 memcpy(gip
.iabuf
, &packet
->raw
->giaddr
, sizeof(packet
->raw
->giaddr
));
192 find_subnet(&subnet
, gip
, MDL
);
194 relay_group
= subnet
->group
;
196 relay_group
= root_group
;
198 subnet_dereference(&subnet
, MDL
);
201 if (!option_state_allocate(&options
, MDL
)) {
202 log_error("No memory for option state.");
203 log_info("%s: out of memory, no reply sent", msgbuf
);
207 execute_statements_in_scope(NULL
, packet
, NULL
, NULL
, packet
->options
,
208 options
, &global_scope
, relay_group
,
211 for (i
=packet
->class_count
-1; i
>=0; i
--) {
212 execute_statements_in_scope(NULL
, packet
, NULL
, NULL
,
213 packet
->options
, options
,
215 packet
->classes
[i
]->group
,
220 * Because LEASEQUERY has some privacy concerns, default to deny.
222 allow_leasequery
= 0;
225 * See if we are authorized to do LEASEQUERY.
227 oc
= lookup_option(&server_universe
, options
, SV_LEASEQUERY
);
229 allow_leasequery
= evaluate_boolean_option_cache(&ignorep
,
230 packet
, NULL
, NULL
, packet
->options
,
231 options
, &global_scope
, oc
, MDL
);
234 if (!allow_leasequery
) {
235 log_info("%s: LEASEQUERY not allowed, query ignored", msgbuf
);
236 option_state_dereference(&options
, MDL
);
242 * Copy out the client IP address.
244 cip
.len
= sizeof(packet
->raw
->ciaddr
);
245 memcpy(cip
.iabuf
, &packet
->raw
->ciaddr
, sizeof(packet
->raw
->ciaddr
));
248 * If the client IP address is valid (not all zero), then we
249 * are looking for information about that IP address.
252 lease
= tmp_lease
= NULL
;
253 if (memcmp(cip
.iabuf
, "\0\0\0", 4)) {
255 want_associated_ip
= 0;
257 snprintf(dbg_info
, sizeof(dbg_info
), "IP %s", piaddr(cip
));
258 find_lease_by_ip_addr(&lease
, cip
, MDL
);
263 want_associated_ip
= 1;
266 * If the client IP address is all zero, then we will
267 * either look up by the client identifier (if we have
268 * one), or by the MAC address.
271 memset(&uid
, 0, sizeof(uid
));
281 DHO_DHCP_CLIENT_IDENTIFIER
,
287 print_hex_1(uid
.len
, uid
.data
, 60));
289 find_lease_by_uid(&tmp_lease
, uid
.data
, uid
.len
, MDL
);
290 data_string_forget(&uid
, MDL
);
291 get_newest_lease(&lease
, tmp_lease
, next_uid
);
292 assoc_ip_cnt
= get_associated_ips(tmp_lease
,
300 if (packet
->raw
->hlen
+1 > sizeof(h
.hbuf
)) {
301 log_info("%s: hardware length too long, "
302 "no reply sent", msgbuf
);
303 option_state_dereference(&options
, MDL
);
307 h
.hlen
= packet
->raw
->hlen
+ 1;
308 h
.hbuf
[0] = packet
->raw
->htype
;
316 print_hw_addr(h
.hbuf
[0],
320 find_lease_by_hw_addr(&tmp_lease
, h
.hbuf
, h
.hlen
, MDL
);
321 get_newest_lease(&lease
, tmp_lease
, next_hw
);
322 assoc_ip_cnt
= get_associated_ips(tmp_lease
,
330 lease_dereference(&tmp_lease
, MDL
);
333 memcpy(&packet
->raw
->ciaddr
,
334 lease
->ip_addr
.iabuf
,
335 sizeof(packet
->raw
->ciaddr
));
339 * Log if we have too many IP addresses associated
342 if (want_associated_ip
&& (assoc_ip_cnt
> nassoc_ips
)) {
343 log_info("%d IP addresses associated with %s, "
344 "only %d sent in reply.",
345 assoc_ip_cnt
, dbg_info
, nassoc_ips
);
350 * We now know the query target too, so can report this in
353 snprintf(msgbuf
, sizeof(msgbuf
),
354 "DHCPLEASEQUERY from %s for %s",
355 inet_ntoa(packet
->raw
->giaddr
), dbg_info
);
358 * Figure our our return type.
361 dhcpMsgType
= DHCPLEASEUNKNOWN
;
362 dhcp_msg_type_name
= "DHCPLEASEUNKNOWN";
364 if (lease
->binding_state
== FTS_ACTIVE
) {
365 dhcpMsgType
= DHCPLEASEACTIVE
;
366 dhcp_msg_type_name
= "DHCPLEASEACTIVE";
368 dhcpMsgType
= DHCPLEASEUNASSIGNED
;
369 dhcp_msg_type_name
= "DHCPLEASEUNASSIGNED";
374 * Set options that only make sense if we have an active lease.
377 if (dhcpMsgType
== DHCPLEASEACTIVE
)
380 * RFC 4388 uses the PRL to request options for the agent to
381 * receive that are "about" the client. It is confusing
382 * because in some cases it wants to know what was sent to
383 * the client (lease times, adjusted), and in others it wants
384 * to know information the client sent. You're supposed to
385 * know this on a case-by-case basis.
387 * "Name servers", "domain name", and the like from the relay
388 * agent's scope seems less than useful. Our options are to
389 * restart the option cache from the lease's best point of view
390 * (execute statements from the lease pool's group), or to
391 * simply restart the option cache from empty.
393 * I think restarting the option cache from empty best
394 * approaches RFC 4388's intent; specific options are included.
396 option_state_dereference(&options
, MDL
);
398 if (!option_state_allocate(&options
, MDL
)) {
399 log_error("%s: out of memory, no reply sent", msgbuf
);
400 lease_dereference(&lease
, MDL
);
405 * Set the hardware address fields.
408 packet
->raw
->hlen
= lease
->hardware_addr
.hlen
- 1;
409 packet
->raw
->htype
= lease
->hardware_addr
.hbuf
[0];
410 memcpy(packet
->raw
->chaddr
,
411 &lease
->hardware_addr
.hbuf
[1],
412 sizeof(packet
->raw
->chaddr
));
415 * Set client identifier option.
417 if (lease
->uid_len
> 0) {
418 if (!add_option(options
,
419 DHO_DHCP_CLIENT_IDENTIFIER
,
422 option_state_dereference(&options
, MDL
);
423 lease_dereference(&lease
, MDL
);
424 log_info("%s: out of memory, no reply sent",
432 * Calculate T1 and T2, the times when the client
433 * tries to extend its lease on its networking
435 * These seem to be hard-coded in ISC DHCP, to 0.5 and
436 * 0.875 of the lease time.
439 lease_duration
= lease
->ends
- lease
->starts
;
440 time_renewal
= lease
->starts
+
441 (lease_duration
/ 2);
442 time_rebinding
= lease
->starts
+
443 (lease_duration
/ 2) +
444 (lease_duration
/ 4) +
445 (lease_duration
/ 8);
447 if (time_renewal
> cur_time
) {
448 time_renewal
= htonl(time_renewal
- cur_time
);
450 if (!add_option(options
,
451 DHO_DHCP_RENEWAL_TIME
,
453 sizeof(time_renewal
))) {
454 option_state_dereference(&options
, MDL
);
455 lease_dereference(&lease
, MDL
);
456 log_info("%s: out of memory, no reply sent",
462 if (time_rebinding
> cur_time
) {
463 time_rebinding
= htonl(time_rebinding
- cur_time
);
465 if (!add_option(options
,
466 DHO_DHCP_REBINDING_TIME
,
468 sizeof(time_rebinding
))) {
469 option_state_dereference(&options
, MDL
);
470 lease_dereference(&lease
, MDL
);
471 log_info("%s: out of memory, no reply sent",
477 if (lease
->ends
> cur_time
) {
478 time_expiry
= htonl(lease
->ends
- cur_time
);
480 if (!add_option(options
,
483 sizeof(time_expiry
))) {
484 option_state_dereference(&options
, MDL
);
485 lease_dereference(&lease
, MDL
);
486 log_info("%s: out of memory, no reply sent",
492 /* Supply the Vendor-Class-Identifier. */
493 if (lease
->scope
!= NULL
) {
494 struct data_string vendor_class
;
496 memset(&vendor_class
, 0, sizeof(vendor_class
));
498 if (find_bound_string(&vendor_class
, lease
->scope
,
499 "vendor-class-identifier")) {
500 if (!add_option(options
,
501 DHO_VENDOR_CLASS_IDENTIFIER
,
502 (void *)vendor_class
.data
,
504 option_state_dereference(&options
,
506 lease_dereference(&lease
, MDL
);
507 log_error("%s: error adding vendor "
508 "class identifier, no reply "
510 data_string_forget(&vendor_class
, MDL
);
513 data_string_forget(&vendor_class
, MDL
);
518 * Set the relay agent info.
520 * Note that because agent info is appended without regard
521 * to the PRL in cons_options(), this will be sent as the
522 * last option in the packet whether it is listed on PRL or
526 if (lease
->agent_options
!= NULL
) {
527 int idx
= agent_universe
.index
;
528 struct option_chain_head
**tmp1
=
529 (struct option_chain_head
**)
530 &(options
->universes
[idx
]);
531 struct option_chain_head
*tmp2
=
532 (struct option_chain_head
*)
533 lease
->agent_options
;
535 option_chain_head_reference(tmp1
, tmp2
, MDL
);
539 * Set the client last transaction time.
540 * We check to make sure we have a timestamp. For
541 * lease files that were saved before running a
542 * timestamp-aware version of the server, this may
546 if (lease
->cltt
!= MIN_TIME
) {
547 if (cur_time
> lease
->cltt
) {
548 client_last_transaction_time
=
549 htonl(cur_time
- lease
->cltt
);
551 client_last_transaction_time
= htonl(0);
553 if (!add_option(options
,
554 DHO_CLIENT_LAST_TRANSACTION_TIME
,
555 &client_last_transaction_time
,
556 sizeof(client_last_transaction_time
))) {
557 option_state_dereference(&options
, MDL
);
558 lease_dereference(&lease
, MDL
);
559 log_info("%s: out of memory, no reply sent",
566 * Set associated IPs, if requested and there are some.
568 if (want_associated_ip
&& (assoc_ip_cnt
> 0)) {
569 if (!add_option(options
,
572 assoc_ip_cnt
* sizeof(assoc_ips
[0]))) {
573 option_state_dereference(&options
, MDL
);
574 lease_dereference(&lease
, MDL
);
575 log_info("%s: out of memory, no reply sent",
583 * Set the message type.
586 packet
->raw
->op
= BOOTREPLY
;
589 * Set DHCP message type.
591 if (!add_option(options
,
592 DHO_DHCP_MESSAGE_TYPE
,
594 sizeof(dhcpMsgType
))) {
595 option_state_dereference(&options
, MDL
);
596 lease_dereference(&lease
, MDL
);
597 log_info("%s: error adding option, no reply sent", msgbuf
);
602 * Log the message we've received.
604 log_info("%s", msgbuf
);
607 * Figure out which address to use to send from.
609 get_server_source_address(&siaddr
, options
, options
, packet
);
612 * Set up the option buffer.
615 memset(&prl
, 0, sizeof(prl
));
616 oc
= lookup_option(&dhcp_universe
, options
,
617 DHO_DHCP_PARAMETER_REQUEST_LIST
);
619 evaluate_option_cache(&prl
,
635 packet
->packet_length
= cons_options(packet
,
649 data_string_forget(&prl
, MDL
); /* SK: safe, even if empty */
650 option_state_dereference(&options
, MDL
);
651 lease_dereference(&lease
, MDL
);
653 to
.sin_family
= AF_INET
;
655 to
.sin_len
= sizeof(to
);
657 memset(to
.sin_zero
, 0, sizeof(to
.sin_zero
));
660 * Leasequery packets are be sent to the gateway address.
662 to
.sin_addr
= packet
->raw
->giaddr
;
663 if (packet
->raw
->giaddr
.s_addr
!= htonl(INADDR_LOOPBACK
)) {
664 to
.sin_port
= local_port
;
666 to
.sin_port
= remote_port
; /* XXXSK: For debugging. */
670 * The fallback_interface lets us send with a real IP
671 * address. The packet interface sends from all-zeros.
673 if (fallback_interface
!= NULL
) {
674 interface
= fallback_interface
;
676 interface
= packet
->interface
;
680 * Report what we're sending.
682 log_info("%s to %s for %s (%d associated IPs)",
684 inet_ntoa(to
.sin_addr
), dbg_info
, assoc_ip_cnt
);
686 send_packet(interface
,
689 packet
->packet_length
,
698 * TODO: RFC5007 query-by-clientid.
700 * TODO: RFC5007 look at the pools according to the link-address.
702 * TODO: get fixed leases too.
704 * TODO: RFC5007 ORO in query-options.
706 * TODO: RFC5007 lq-relay-data.
708 * TODO: RFC5007 lq-client-link.
710 * Note: the code is still nearly compliant and usable for the target
711 * case with these missing features!
715 * The structure to handle a leasequery.
718 struct packet
*packet
;
719 struct data_string client_id
;
720 struct data_string server_id
;
721 struct data_string lq_query
;
723 struct in6_addr link_addr
;
724 struct option_state
*query_opts
;
726 struct option_state
*reply_opts
;
729 unsigned char data
[65536];
730 struct dhcpv6_packet reply
;
735 * Options that we want to send.
737 static const int required_opts_lq
[] = {
746 static const int required_opt_CLIENT_DATA
[] = {
755 * Get the lq-query option from the packet.
758 get_lq_query(struct lq6_state
*lq
)
760 struct data_string
*lq_query
= &lq
->lq_query
;
761 struct packet
*packet
= lq
->packet
;
762 struct option_cache
*oc
;
765 * Verify our lq_query structure is empty.
767 if ((lq_query
->data
!= NULL
) || (lq_query
->len
!= 0)) {
768 return DHCP_R_INVALIDARG
;
771 oc
= lookup_option(&dhcpv6_universe
, packet
->options
, D6O_LQ_QUERY
);
773 return ISC_R_NOTFOUND
;
776 if (!evaluate_option_cache(lq_query
, packet
, NULL
, NULL
,
777 packet
->options
, NULL
,
778 &global_scope
, oc
, MDL
)) {
779 return ISC_R_FAILURE
;
782 return ISC_R_SUCCESS
;
786 * Message validation, RFC 5007 section 4.2.1:
787 * dhcpv6.c:valid_client_msg() - unicast + lq-query option.
790 valid_query_msg(struct lq6_state
*lq
) {
791 struct packet
*packet
= lq
->packet
;
793 struct option_cache
*oc
;
795 /* INSIST((lq != NULL) || (packet != NULL)); */
797 switch (get_client_id(packet
, &lq
->client_id
)) {
801 log_debug("Discarding %s from %s; "
802 "client identifier missing",
803 dhcpv6_type_names
[packet
->dhcpv6_msg_type
],
804 piaddr(packet
->client_addr
));
807 log_error("Error processing %s from %s; "
808 "unable to evaluate Client Identifier",
809 dhcpv6_type_names
[packet
->dhcpv6_msg_type
],
810 piaddr(packet
->client_addr
));
814 oc
= lookup_option(&dhcpv6_universe
, packet
->options
, D6O_SERVERID
);
816 if (evaluate_option_cache(&lq
->server_id
, packet
, NULL
, NULL
,
817 packet
->options
, NULL
,
818 &global_scope
, oc
, MDL
)) {
819 log_debug("Discarding %s from %s; "
820 "server identifier found "
821 "(CLIENTID %s, SERVERID %s)",
822 dhcpv6_type_names
[packet
->dhcpv6_msg_type
],
823 piaddr(packet
->client_addr
),
824 print_hex_1(lq
->client_id
.len
,
825 lq
->client_id
.data
, 60),
826 print_hex_2(lq
->server_id
.len
,
827 lq
->server_id
.data
, 60));
829 log_debug("Discarding %s from %s; "
830 "server identifier found "
832 dhcpv6_type_names
[packet
->dhcpv6_msg_type
],
833 print_hex_1(lq
->client_id
.len
,
834 lq
->client_id
.data
, 60),
835 piaddr(packet
->client_addr
));
840 switch (get_lq_query(lq
)) {
844 log_debug("Discarding %s from %s; lq-query missing",
845 dhcpv6_type_names
[packet
->dhcpv6_msg_type
],
846 piaddr(packet
->client_addr
));
849 log_error("Error processing %s from %s; "
850 "unable to evaluate LQ-Query",
851 dhcpv6_type_names
[packet
->dhcpv6_msg_type
],
852 piaddr(packet
->client_addr
));
861 if (lq
->client_id
.len
> 0) {
862 data_string_forget(&lq
->client_id
, MDL
);
864 if (lq
->server_id
.len
> 0) {
865 data_string_forget(&lq
->server_id
, MDL
);
867 if (lq
->lq_query
.len
> 0) {
868 data_string_forget(&lq
->lq_query
, MDL
);
875 * Set an error in a status-code option (from set_status_code).
878 set_error(struct lq6_state
*lq
, u_int16_t code
, const char *message
) {
879 struct data_string d
;
882 memset(&d
, 0, sizeof(d
));
883 d
.len
= sizeof(code
) + strlen(message
);
884 if (!buffer_allocate(&d
.buffer
, d
.len
, MDL
)) {
885 log_fatal("set_error: no memory for status code.");
887 d
.data
= d
.buffer
->data
;
888 putUShort(d
.buffer
->data
, code
);
889 memcpy(d
.buffer
->data
+ sizeof(code
), message
, d
.len
- sizeof(code
));
890 if (!save_option_buffer(&dhcpv6_universe
, lq
->reply_opts
,
891 d
.buffer
, (unsigned char *)d
.data
, d
.len
,
892 D6O_STATUS_CODE
, 0)) {
893 log_error("set_error: error saving status code.");
898 data_string_forget(&d
, MDL
);
903 * Process a by-address lease query.
906 process_lq_by_address(struct lq6_state
*lq
) {
907 struct packet
*packet
= lq
->packet
;
908 struct option_cache
*oc
;
909 struct ipv6_pool
*pool
= NULL
;
910 struct data_string data
;
911 struct in6_addr addr
;
912 struct iasubopt
*iaaddr
= NULL
;
913 struct option_state
*opt_state
= NULL
;
921 oc
= lookup_option(&dhcpv6_universe
, lq
->query_opts
, D6O_IAADDR
);
923 if (!set_error(lq
, STATUS_MalformedQuery
,
924 "No OPTION_IAADDR.")) {
925 log_error("process_lq_by_address: unable "
926 "to set MalformedQuery status code.");
931 memset(&data
, 0, sizeof(data
));
932 if (!evaluate_option_cache(&data
, packet
,
934 lq
->query_opts
, NULL
,
935 &global_scope
, oc
, MDL
) ||
936 (data
.len
< IAADDR_OFFSET
)) {
937 log_error("process_lq_by_address: error evaluating IAADDR.");
940 memcpy(&addr
, data
.data
, sizeof(addr
));
941 data_string_forget(&data
, MDL
);
945 * Note the RFC 5007 says to use the link-address to find the link
946 * or the ia-aadr when it is :: but in any case the ia-addr has
947 * to be on the link, so we ignore the link-address here.
949 if (find_ipv6_pool(&pool
, D6O_IA_NA
, &addr
) != ISC_R_SUCCESS
) {
950 if (!set_error(lq
, STATUS_NotConfigured
,
951 "Address not in a pool.")) {
952 log_error("process_lq_by_address: unable "
953 "to set NotConfigured status code.");
959 if (iasubopt_hash_lookup(&iaaddr
, pool
->leases
, &addr
,
960 sizeof(addr
), MDL
) == 0) {
964 if ((iaaddr
== NULL
) || (iaaddr
->state
!= FTS_ACTIVE
) ||
965 (iaaddr
->ia
== NULL
) || (iaaddr
->ia
->iaid_duid
.len
<= 4)) {
971 * Build the client-data option (with client-id, ia-addr and clt-time).
973 if (!option_state_allocate(&opt_state
, MDL
)) {
974 log_error("process_lq_by_address: "
975 "no memory for option state.");
979 data_string_copy(&data
, &iaaddr
->ia
->iaid_duid
, MDL
);
982 if (!save_option_buffer(&dhcpv6_universe
, opt_state
,
983 NULL
, (unsigned char *)data
.data
, data
.len
,
985 log_error("process_lq_by_address: error saving client ID.");
988 data_string_forget(&data
, MDL
);
990 data
.len
= IAADDR_OFFSET
;
991 if (!buffer_allocate(&data
.buffer
, data
.len
, MDL
)) {
992 log_error("process_lq_by_address: no memory for ia-addr.");
995 data
.data
= data
.buffer
->data
;
996 memcpy(data
.buffer
->data
, &iaaddr
->addr
, 16);
997 lifetime
= iaaddr
->prefer
;
998 putULong(data
.buffer
->data
+ 16, lifetime
);
999 lifetime
= iaaddr
->valid
;
1000 putULong(data
.buffer
->data
+ 20, lifetime
);
1001 if (!save_option_buffer(&dhcpv6_universe
, opt_state
,
1002 NULL
, (unsigned char *)data
.data
, data
.len
,
1004 log_error("process_lq_by_address: error saving ia-addr.");
1007 data_string_forget(&data
, MDL
);
1009 lifetime
= htonl(iaaddr
->ia
->cltt
);
1010 if (!save_option_buffer(&dhcpv6_universe
, opt_state
,
1011 NULL
, (unsigned char *)&lifetime
, 4,
1013 log_error("process_lq_by_address: error saving clt time.");
1018 * Store the client-data option.
1020 opt_cursor
= lq
->cursor
;
1021 putUShort(lq
->buf
.data
+ lq
->cursor
, (unsigned)D6O_CLIENT_DATA
);
1023 /* Skip option length. */
1026 lq
->cursor
+= store_options6((char *)lq
->buf
.data
+ lq
->cursor
,
1027 sizeof(lq
->buf
) - lq
->cursor
,
1028 opt_state
, lq
->packet
,
1029 required_opt_CLIENT_DATA
, NULL
);
1030 /* Reset the length. */
1031 putUShort(lq
->buf
.data
+ opt_cursor
+ 2,
1032 lq
->cursor
- (opt_cursor
+ 4));
1038 if (data
.data
!= NULL
)
1039 data_string_forget(&data
, MDL
);
1041 ipv6_pool_dereference(&pool
, MDL
);
1043 iasubopt_dereference(&iaaddr
, MDL
);
1044 if (opt_state
!= NULL
)
1045 option_state_dereference(&opt_state
, MDL
);
1051 * Process a lease query.
1054 dhcpv6_leasequery(struct data_string
*reply_ret
, struct packet
*packet
) {
1055 static struct lq6_state lq
;
1056 struct option_cache
*oc
;
1060 * Initialize the lease query state.
1063 memset(&lq
.client_id
, 0, sizeof(lq
.client_id
));
1064 memset(&lq
.server_id
, 0, sizeof(lq
.server_id
));
1065 memset(&lq
.lq_query
, 0, sizeof(lq
.lq_query
));
1066 lq
.query_opts
= NULL
;
1067 lq
.reply_opts
= NULL
;
1068 packet_reference(&lq
.packet
, packet
, MDL
);
1071 * Validate our input.
1073 if (!valid_query_msg(&lq
)) {
1078 * Prepare our reply.
1080 if (!option_state_allocate(&lq
.reply_opts
, MDL
)) {
1081 log_error("dhcpv6_leasequery: no memory for option state.");
1084 execute_statements_in_scope(NULL
, lq
.packet
, NULL
, NULL
,
1085 lq
.packet
->options
, lq
.reply_opts
,
1086 &global_scope
, root_group
, NULL
, NULL
);
1088 lq
.buf
.reply
.msg_type
= DHCPV6_LEASEQUERY_REPLY
;
1090 memcpy(lq
.buf
.reply
.transaction_id
,
1091 lq
.packet
->dhcpv6_transaction_id
,
1092 sizeof(lq
.buf
.reply
.transaction_id
));
1095 * Because LEASEQUERY has some privacy concerns, default to deny.
1100 * See if we are authorized to do LEASEQUERY.
1102 oc
= lookup_option(&server_universe
, lq
.reply_opts
, SV_LEASEQUERY
);
1104 allow_lq
= evaluate_boolean_option_cache(NULL
,
1114 log_info("dhcpv6_leasequery: not allowed, query ignored.");
1119 * Same than transmission of REPLY message in RFC 3315:
1124 oc
= lookup_option(&dhcpv6_universe
, lq
.reply_opts
, D6O_SERVERID
);
1126 /* If not already in options, get from query then global. */
1127 if (lq
.server_id
.data
== NULL
)
1128 copy_server_duid(&lq
.server_id
, MDL
);
1129 if (!save_option_buffer(&dhcpv6_universe
,
1132 (unsigned char *)lq
.server_id
.data
,
1136 log_error("dhcpv6_leasequery: "
1137 "error saving server identifier.");
1142 if (!save_option_buffer(&dhcpv6_universe
,
1144 lq
.client_id
.buffer
,
1145 (unsigned char *)lq
.client_id
.data
,
1149 log_error("dhcpv6_leasequery: "
1150 "error saving client identifier.");
1157 * Decode the lq-query option.
1160 if (lq
.lq_query
.len
<= LQ_QUERY_OFFSET
) {
1161 if (!set_error(&lq
, STATUS_MalformedQuery
,
1162 "OPTION_LQ_QUERY too short.")) {
1163 log_error("dhcpv6_leasequery: unable "
1164 "to set MalformedQuery status code.");
1170 lq
.query_type
= lq
.lq_query
.data
[0];
1171 memcpy(&lq
.link_addr
, lq
.lq_query
.data
+ 1, sizeof(lq
.link_addr
));
1172 switch (lq
.query_type
) {
1173 case LQ6QT_BY_ADDRESS
:
1175 case LQ6QT_BY_CLIENTID
:
1176 if (!set_error(&lq
, STATUS_UnknownQueryType
,
1177 "QUERY_BY_CLIENTID not supported.")) {
1178 log_error("dhcpv6_leasequery: unable to "
1179 "set UnknownQueryType status code.");
1184 if (!set_error(&lq
, STATUS_UnknownQueryType
,
1185 "Unknown query-type.")) {
1186 log_error("dhcpv6_leasequery: unable to "
1187 "set UnknownQueryType status code.");
1193 if (!option_state_allocate(&lq
.query_opts
, MDL
)) {
1194 log_error("dhcpv6_leasequery: no memory for option state.");
1197 if (!parse_option_buffer(lq
.query_opts
,
1198 lq
.lq_query
.data
+ LQ_QUERY_OFFSET
,
1199 lq
.lq_query
.len
- LQ_QUERY_OFFSET
,
1200 &dhcpv6_universe
)) {
1201 log_error("dhcpv6_leasequery: error parsing query-options.");
1202 if (!set_error(&lq
, STATUS_MalformedQuery
,
1203 "Bad query-options.")) {
1204 log_error("dhcpv6_leasequery: unable "
1205 "to set MalformedQuery status code.");
1212 if (!process_lq_by_address(&lq
))
1216 /* Store the options. */
1217 lq
.cursor
+= store_options6((char *)lq
.buf
.data
+ lq
.cursor
,
1218 sizeof(lq
.buf
) - lq
.cursor
,
1224 /* Return our reply to the caller. */
1225 reply_ret
->len
= lq
.cursor
;
1226 reply_ret
->buffer
= NULL
;
1227 if (!buffer_allocate(&reply_ret
->buffer
, lq
.cursor
, MDL
)) {
1228 log_fatal("dhcpv6_leasequery: no memory to store Reply.");
1230 memcpy(reply_ret
->buffer
->data
, lq
.buf
.data
, lq
.cursor
);
1231 reply_ret
->data
= reply_ret
->buffer
->data
;
1235 if (lq
.packet
!= NULL
)
1236 packet_dereference(&lq
.packet
, MDL
);
1237 if (lq
.client_id
.data
!= NULL
)
1238 data_string_forget(&lq
.client_id
, MDL
);
1239 if (lq
.server_id
.data
!= NULL
)
1240 data_string_forget(&lq
.server_id
, MDL
);
1241 if (lq
.lq_query
.data
!= NULL
)
1242 data_string_forget(&lq
.lq_query
, MDL
);
1243 if (lq
.query_opts
!= NULL
)
1244 option_state_dereference(&lq
.query_opts
, MDL
);
1245 if (lq
.reply_opts
!= NULL
)
1246 option_state_dereference(&lq
.reply_opts
, MDL
);