2 * Copyright (C) 2006-2007 by Internet Systems Consortium, Inc. ("ISC")
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.
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 store the
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.
27 * TODO: RFC4388 specifies the creation of a "non-sensitive options"
28 * configuration list, and that these SHOULD be returned. We
31 * TODO: RFC4388 says the server SHOULD use RFC3118, "Authentication
34 * TODO: RFC4388 specifies that you SHOULD insure that you cannot be
35 * DoS'ed by DHCPLEASEQUERY message.
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
43 * 1. Find the most recent lease.
44 * 2. Find all additional IP addresses for the query argument.
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.
53 next_hw(const struct lease
*lease
) {
54 /* INSIST(lease != NULL); */
59 next_uid(const struct lease
*lease
) {
60 /* INSIST(lease != NULL); */
65 get_newest_lease(struct lease
**retval
,
67 struct lease
*(*next
)(const struct lease
*)) {
72 /* INSIST(newest != NULL); */
73 /* INSIST(next != NULL); */
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
)) {
89 if (p
->ends
> newest
->ends
) {
95 lease_reference(retval
, newest
, MDL
);
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
) {
105 const struct lease
*p
;
108 /* INSIST(next != NULL); */
109 /* INSIST(associated_ips != NULL); */
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
],
121 sizeof(associated_ips
[cnt
]));
131 dhcpleasequery(struct packet
*packet
, int ms_nulltp
) {
136 struct data_string uid
;
138 struct lease
*tmp_lease
;
140 int want_associated_ip
;
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]);
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
;
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
;
163 struct interface_info
*interface
;
165 /* INSIST(packet != NULL); */
168 * Prepare log information.
170 snprintf(msgbuf
, sizeof(msgbuf
),
171 "DHCPLEASEQUERY from %s", inet_ntoa(packet
->raw
->giaddr
));
174 * We can't reply if there is no giaddr field.
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
));
183 * Set up our options, scope, and, um... stuff.
184 * This is basically copied from dhcpinform() in dhcp.c.
186 gip
.len
= sizeof(packet
->raw
->giaddr
);
187 memcpy(gip
.iabuf
, &packet
->raw
->giaddr
, sizeof(packet
->raw
->giaddr
));
190 find_subnet(&subnet
, gip
, MDL
);
191 if (subnet
== NULL
) {
192 log_info("%s: unknown subnet for address %s",
193 msgbuf
, piaddr(gip
));
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
);
205 execute_statements_in_scope(NULL
,
214 for (i
=packet
->class_count
-1; i
>=0; i
--) {
215 execute_statements_in_scope(NULL
,
222 packet
->classes
[i
]->group
,
226 subnet_dereference(&subnet
, MDL
);
229 * Because LEASEQUERY has some privacy concerns, default to deny.
231 allow_leasequery
= 0;
234 * See if we are authorized to do LEASEQUERY.
236 oc
= lookup_option(&server_universe
, options
, SV_LEASEQUERY
);
238 allow_leasequery
= evaluate_boolean_option_cache(&ignorep
,
239 packet
, NULL
, NULL
, packet
->options
,
240 options
, &global_scope
, oc
, MDL
);
243 if (!allow_leasequery
) {
244 log_info("%s: LEASEQUERY not allowed, query ignored", msgbuf
);
245 option_state_dereference(&options
, MDL
);
251 * Copy out the client IP address.
253 cip
.len
= sizeof(packet
->raw
->ciaddr
);
254 memcpy(cip
.iabuf
, &packet
->raw
->ciaddr
, sizeof(packet
->raw
->ciaddr
));
257 * If the client IP address is valid (not all zero), then we
258 * are looking for information about that IP address.
261 lease
= tmp_lease
= NULL
;
262 if (memcmp(cip
.iabuf
, "\0\0\0", 4)) {
264 want_associated_ip
= 0;
266 snprintf(dbg_info
, sizeof(dbg_info
), "IP %s", piaddr(cip
));
267 find_lease_by_ip_addr(&lease
, cip
, MDL
);
272 want_associated_ip
= 1;
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.
280 memset(&uid
, 0, sizeof(uid
));
290 DHO_DHCP_CLIENT_IDENTIFIER
,
296 print_hex_1(uid
.len
, uid
.data
, 60));
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
,
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
);
316 h
.hlen
= packet
->raw
->hlen
+ 1;
317 h
.hbuf
[0] = packet
->raw
->htype
;
325 print_hw_addr(h
.hbuf
[0],
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
,
339 lease_dereference(&tmp_lease
, MDL
);
342 memcpy(&packet
->raw
->ciaddr
,
343 lease
->ip_addr
.iabuf
,
344 sizeof(packet
->raw
->ciaddr
));
348 * Log if we have too many IP addresses associated
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
);
359 * We now know the query target too, so can report this in
362 snprintf(msgbuf
, sizeof(msgbuf
),
363 "DHCPLEASEQUERY from %s for %s",
364 inet_ntoa(packet
->raw
->giaddr
), dbg_info
);
367 * Figure our our return type.
370 dhcpMsgType
= DHCPLEASEUNKNOWN
;
371 dhcp_msg_type_name
= "DHCPLEASEUNKNOWN";
373 if (lease
->binding_state
== FTS_ACTIVE
) {
374 dhcpMsgType
= DHCPLEASEACTIVE
;
375 dhcp_msg_type_name
= "DHCPLEASEACTIVE";
377 dhcpMsgType
= DHCPLEASEUNASSIGNED
;
378 dhcp_msg_type_name
= "DHCPLEASEUNASSIGNED";
383 * Set options that only make sense if we have an active lease.
386 if (dhcpMsgType
== DHCPLEASEACTIVE
)
390 * Set the hardware address fields.
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
));
400 * Set client identifier option.
402 if (lease
->uid_len
> 0) {
403 if (!add_option(options
,
404 DHO_DHCP_CLIENT_IDENTIFIER
,
407 option_state_dereference(&options
, MDL
);
408 lease_dereference(&lease
, MDL
);
409 log_info("%s: out of memory, no reply sent",
417 * Calculate T1 and T2, the times when the client
418 * tries to extend its lease on its networking
420 * These seem to be hard-coded in ISC DHCP, to 0.5 and
421 * 0.875 of the lease time.
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);
432 if (time_renewal
> cur_time
) {
433 time_renewal
= htonl(time_renewal
- cur_time
);
434 if (!add_option(options
,
435 DHO_DHCP_RENEWAL_TIME
,
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",
446 if (time_rebinding
> cur_time
) {
447 time_rebinding
= htonl(time_rebinding
- cur_time
);
448 if (!add_option(options
,
449 DHO_DHCP_REBINDING_TIME
,
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",
460 if (lease
->ends
> cur_time
) {
461 time_expiry
= htonl(lease
->ends
- cur_time
);
462 if (!add_option(options
,
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",
476 * Set the relay agent info.
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
;
488 option_chain_head_reference(tmp1
, tmp2
, MDL
);
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
499 if (lease
->cltt
!= MIN_TIME
) {
500 if (cur_time
> lease
->cltt
) {
501 client_last_transaction_time
=
502 htonl(cur_time
- lease
->cltt
);
504 client_last_transaction_time
= htonl(0);
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",
519 * Set associated IPs, if requested and there are some.
521 if (want_associated_ip
&& (assoc_ip_cnt
> 0)) {
522 if (!add_option(options
,
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",
536 * Set the message type.
539 packet
->raw
->op
= BOOTREPLY
;
542 * Set DHCP message type.
544 if (!add_option(options
,
545 DHO_DHCP_MESSAGE_TYPE
,
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
);
555 * Log the message we've received.
557 log_info("%s", msgbuf
);
560 * Figure out which address to use to send from.
562 get_server_source_address(&siaddr
, options
, packet
);
565 * Set up the option buffer.
568 memset(&prl
, 0, sizeof(prl
));
569 oc
= lookup_option(&dhcp_universe
, options
,
570 DHO_DHCP_PARAMETER_REQUEST_LIST
);
572 evaluate_option_cache(&prl
,
588 packet
->packet_length
= cons_options(packet
,
602 data_string_forget(&prl
, MDL
); /* SK: safe, even if empty */
603 option_state_dereference(&options
, MDL
);
604 lease_dereference(&lease
, MDL
);
606 to
.sin_family
= AF_INET
;
608 to
.sin_len
= sizeof(to
);
610 memset(to
.sin_zero
, 0, sizeof(to
.sin_zero
));
613 * Leasequery packets are be sent to the gateway address.
615 to
.sin_addr
= packet
->raw
->giaddr
;
616 if (packet
->raw
->giaddr
.s_addr
!= htonl(INADDR_LOOPBACK
)) {
617 to
.sin_port
= local_port
;
619 to
.sin_port
= remote_port
; /* XXXSK: For debugging. */
623 * The fallback_interface lets us send with a real IP
624 * address. The packet interface sends from all-zeros.
626 if (fallback_interface
!= NULL
) {
627 interface
= fallback_interface
;
629 interface
= packet
->interface
;
633 * Report what we're sending.
635 log_info("%s to %s for %s (%d associated IPs)",
637 inet_ntoa(to
.sin_addr
), dbg_info
, assoc_ip_cnt
);
639 send_packet(interface
,
642 packet
->packet_length
,
651 * TODO: RFC5007 query-by-clientid.
653 * TODO: RFC5007 look at the pools according to the link-address.
655 * TODO: get fixed leases too.
657 * TODO: RFC5007 ORO in query-options.
659 * TODO: RFC5007 not default preferred and valid time.
661 * TODO: RFC5007 not zero Client Last Transaction Time (clt-time).
663 * TODO: RFC5007 lq-relay-data.
665 * TODO: RFC5007 lq-client-link.
667 * Note: the code is still nearly compliant and usable for the target
668 * case with these missing features!
672 * The structure to handle a leasequery.
675 struct packet
*packet
;
676 struct data_string client_id
;
677 struct data_string server_id
;
678 struct data_string lq_query
;
680 struct in6_addr link_addr
;
681 struct option_state
*query_opts
;
683 struct option_state
*reply_opts
;
686 unsigned char data
[65536];
687 struct dhcpv6_packet reply
;
692 * Options that we want to send.
694 static const int required_opts_lq
[] = {
703 static const int required_opt_CLIENT_DATA
[] = {
712 * Get the lq-query option from the packet.
715 get_lq_query(struct lq6_state
*lq
)
717 struct data_string
*lq_query
= &lq
->lq_query
;
718 struct packet
*packet
= lq
->packet
;
719 struct option_cache
*oc
;
722 * Verify our lq_query structure is empty.
724 if ((lq_query
->data
!= NULL
) || (lq_query
->len
!= 0)) {
725 return ISC_R_INVALIDARG
;
728 oc
= lookup_option(&dhcpv6_universe
, packet
->options
, D6O_LQ_QUERY
);
730 return ISC_R_NOTFOUND
;
733 if (!evaluate_option_cache(lq_query
, packet
, NULL
, NULL
,
734 packet
->options
, NULL
,
735 &global_scope
, oc
, MDL
)) {
736 return ISC_R_FAILURE
;
739 return ISC_R_SUCCESS
;
743 * Message validation, RFC 5007 section 4.2.1:
744 * dhcpv6.c:valid_client_msg() - unicast + lq-query option.
747 valid_query_msg(struct lq6_state
*lq
) {
748 struct packet
*packet
= lq
->packet
;
750 struct option_cache
*oc
;
752 /* INSIST((lq != NULL) || (packet != NULL)); */
754 switch (get_client_id(packet
, &lq
->client_id
)) {
758 log_debug("Discarding %s from %s; "
759 "client identifier missing",
760 dhcpv6_type_names
[packet
->dhcpv6_msg_type
],
761 piaddr(packet
->client_addr
));
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
));
771 oc
= lookup_option(&dhcpv6_universe
, packet
->options
, D6O_SERVERID
);
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));
786 log_debug("Discarding %s from %s; "
787 "server identifier found "
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
));
797 switch (get_lq_query(lq
)) {
801 log_debug("Discarding %s from %s; lq-query missing",
802 dhcpv6_type_names
[packet
->dhcpv6_msg_type
],
803 piaddr(packet
->client_addr
));
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
));
818 if (lq
->client_id
.len
> 0) {
819 data_string_forget(&lq
->client_id
, MDL
);
821 if (lq
->server_id
.len
> 0) {
822 data_string_forget(&lq
->server_id
, MDL
);
824 if (lq
->lq_query
.len
> 0) {
825 data_string_forget(&lq
->lq_query
, MDL
);
832 * Set an error in a status-code option (from set_status_code).
835 set_error(struct lq6_state
*lq
, u_int16_t code
, const char *message
) {
836 struct data_string d
;
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.");
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.");
855 data_string_forget(&d
, MDL
);
860 * Process a by-address lease query.
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
;
878 oc
= lookup_option(&dhcpv6_universe
, lq
->query_opts
, D6O_IAADDR
);
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.");
888 memset(&data
, 0, sizeof(data
));
889 if (!evaluate_option_cache(&data
, packet
,
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.");
897 memcpy(&addr
, data
.data
, sizeof(addr
));
898 data_string_forget(&data
, MDL
);
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.
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.");
916 if (iaaddr_hash_lookup(&iaaddr
, pool
->addrs
, &addr
,
917 sizeof(addr
), MDL
) == 0) {
921 if ((iaaddr
== NULL
) || (iaaddr
->state
!= FTS_ACTIVE
) ||
922 (iaaddr
->ia_na
== NULL
) ||
923 (iaaddr
->ia_na
->iaid_duid
.len
<= 4)) {
929 * Build the client-data option (with client-id, ia-addr and clt-time).
931 if (!option_state_allocate(&opt_state
, MDL
)) {
932 log_error("process_lq_by_address: "
933 "no memory for option state.");
937 data_string_copy(&data
, &iaaddr
->ia_na
->iaid_duid
, MDL
);
940 if (!save_option_buffer(&dhcpv6_universe
, opt_state
,
941 NULL
, (unsigned char *)data
.data
, data
.len
,
943 log_error("process_lq_by_address: error saving client ID.");
946 data_string_forget(&data
, MDL
);
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.");
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
,
963 log_error("process_lq_by_address: error saving ia-addr.");
966 data_string_forget(&data
, MDL
);
969 if (!save_option_buffer(&dhcpv6_universe
, opt_state
,
970 NULL
, (unsigned char *)&lifetime
, 4,
972 log_error("process_lq_by_address: error saving clt time.");
977 * Store the client-data option.
979 opt_cursor
= lq
->cursor
;
980 putUShort(lq
->buf
.data
+ lq
->cursor
, (unsigned)D6O_CLIENT_DATA
);
982 /* Skip option length. */
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));
997 if (data
.data
!= NULL
)
998 data_string_forget(&data
, MDL
);
1000 ipv6_pool_dereference(&pool
, MDL
);
1002 iaaddr_dereference(&iaaddr
, MDL
);
1003 if (opt_state
!= NULL
)
1004 option_state_dereference(&opt_state
, MDL
);
1010 * Process a lease query.
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
;
1020 * Initialize the lease query state.
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
);
1031 * Validate our input.
1033 if (!valid_query_msg(&lq
)) {
1038 * Prepare our reply.
1040 if (!option_state_allocate(&lq
.reply_opts
, MDL
)) {
1041 log_error("dhcpv6_leasequery: no memory for option state.");
1044 execute_statements_in_scope(NULL
, lq
.packet
, NULL
, NULL
,
1045 lq
.packet
->options
, lq
.reply_opts
,
1046 &global_scope
, root_group
, NULL
);
1048 lq
.buf
.reply
.msg_type
= DHCPV6_LEASEQUERY_REPLY
;
1050 memcpy(lq
.buf
.reply
.transaction_id
,
1051 lq
.packet
->dhcpv6_transaction_id
,
1052 sizeof(lq
.buf
.reply
.transaction_id
));
1055 * Because LEASEQUERY has some privacy concerns, default to deny.
1060 * See if we are authorized to do LEASEQUERY.
1062 oc
= lookup_option(&server_universe
, lq
.reply_opts
, SV_LEASEQUERY
);
1064 allow_lq
= evaluate_boolean_option_cache(NULL
,
1074 log_info("dhcpv6_leasequery: not allowed, query ignored.");
1079 * Same than transmission of REPLY message in RFC 3315:
1084 oc
= lookup_option(&dhcpv6_universe
, lq
.reply_opts
, D6O_SERVERID
);
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
,
1092 (unsigned char *)lq
.server_id
.data
,
1096 log_error("dhcpv6_leasequery: "
1097 "error saving server identifier.");
1102 if (!save_option_buffer(&dhcpv6_universe
,
1104 lq
.client_id
.buffer
,
1105 (unsigned char *)lq
.client_id
.data
,
1109 log_error("dhcpv6_leasequery: "
1110 "error saving client identifier.");
1117 * Decode the lq-query option.
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.");
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
:
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.");
1144 if (!set_error(&lq
, STATUS_UnknownQueryType
,
1145 "Unknown query-type.")) {
1146 log_error("dhcpv6_leasequery: unable to "
1147 "set UnknownQueryType status code.");
1153 if (!option_state_allocate(&lq
.query_opts
, MDL
)) {
1154 log_error("dhcpv6_leasequery: no memory for option state.");
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.");
1172 if (!process_lq_by_address(&lq
))
1176 /* Store the options. */
1177 lq
.cursor
+= store_options6((char *)lq
.buf
.data
+ lq
.cursor
,
1178 sizeof(lq
.buf
) - lq
.cursor
,
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.");
1190 memcpy(reply_ret
->buffer
->data
, lq
.buf
.data
, lq
.cursor
);
1191 reply_ret
->data
= reply_ret
->buffer
->data
;
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
);