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