xref: /netbsd-src/external/mpl/dhcp/dist/server/dhcpleasequery.c (revision 56e2dc54c361727ddc9248251f043840f68d71b4)
1 /*	$NetBSD: dhcpleasequery.c,v 1.3 2020/08/03 21:10:57 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.3 2020/08/03 21:10:57 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*
next_hw(const struct lease * lease)56 next_hw(const struct lease *lease) {
57 	/* INSIST(lease != NULL); */
58 	return lease->n_hw;
59 }
60 
61 static struct lease*
next_uid(const struct lease * lease)62 next_uid(const struct lease *lease) {
63 	/* INSIST(lease != NULL); */
64 	return lease->n_uid;
65 }
66 
67 static void
get_newest_lease(struct lease ** retval,struct lease * lease,struct lease * (* next)(const struct lease *))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
get_associated_ips(const struct lease * lease,struct lease * (* next)(const struct lease *),const struct lease * newest,u_int32_t * associated_ips,unsigned int associated_ips_size)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
dhcpleasequery(struct packet * packet,int ms_nulltp)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
get_lq_query(struct lq6_state * lq)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
valid_query_msg(struct lq6_state * lq)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 		data_string_forget(&lq->client_id, MDL);
883 		data_string_forget(&lq->server_id, MDL);
884 		data_string_forget(&lq->lq_query, MDL);
885 	}
886 	return ret_val;
887 }
888 
889 /*
890  * Set an error in a status-code option (from set_status_code).
891  */
892 static int
set_error(struct lq6_state * lq,u_int16_t code,const char * message)893 set_error(struct lq6_state *lq, u_int16_t code, const char *message) {
894 	struct data_string d;
895 	int ret_val;
896 
897 	memset(&d, 0, sizeof(d));
898 	d.len = sizeof(code) + strlen(message);
899 	if (!buffer_allocate(&d.buffer, d.len, MDL)) {
900 		log_fatal("set_error: no memory for status code.");
901 	}
902 	d.data = d.buffer->data;
903 	putUShort(d.buffer->data, code);
904 	memcpy(d.buffer->data + sizeof(code), message, d.len - sizeof(code));
905 	if (!save_option_buffer(&dhcpv6_universe, lq->reply_opts,
906 				d.buffer, (unsigned char *)d.data, d.len,
907 				D6O_STATUS_CODE, 0)) {
908 		log_error("set_error: error saving status code.");
909 		ret_val = 0;
910 	} else {
911 		ret_val = 1;
912 	}
913 	data_string_forget(&d, MDL);
914 	return ret_val;
915 }
916 
917 /*
918  * Process a by-address lease query.
919  */
920 static int
process_lq_by_address(struct lq6_state * lq)921 process_lq_by_address(struct lq6_state *lq) {
922 	struct packet *packet = lq->packet;
923 	struct option_cache *oc;
924 	struct ipv6_pool *pool = NULL;
925 	struct data_string data;
926 	struct in6_addr addr;
927 	struct iasubopt *iaaddr = NULL;
928 	struct option_state *opt_state = NULL;
929 	u_int32_t lifetime;
930 	unsigned opt_cursor;
931 	int ret_val = 0;
932 
933 	/*
934 	 * Get the IAADDR.
935 	 */
936 	oc = lookup_option(&dhcpv6_universe, lq->query_opts, D6O_IAADDR);
937 	if (oc == NULL) {
938 		if (!set_error(lq, STATUS_MalformedQuery,
939 			       "No OPTION_IAADDR.")) {
940 			log_error("process_lq_by_address: unable "
941 				  "to set MalformedQuery status code.");
942 			return 0;
943 		}
944 		return 1;
945 	}
946 	memset(&data, 0, sizeof(data));
947 	if (!evaluate_option_cache(&data, packet,
948 				   NULL, NULL,
949 				   lq->query_opts, NULL,
950 				   &global_scope, oc, MDL) ||
951 	    (data.len < IAADDR_OFFSET)) {
952 		log_error("process_lq_by_address: error evaluating IAADDR.");
953 		goto exit;
954 	}
955 	memcpy(&addr, data.data, sizeof(addr));
956 	data_string_forget(&data, MDL);
957 
958 	/*
959 	 * Find the lease.
960 	 * Note the RFC 5007 says to use the link-address to find the link
961 	 * or the ia-aadr when it is :: but in any case the ia-addr has
962 	 * to be on the link, so we ignore the link-address here.
963 	 */
964 	if (find_ipv6_pool(&pool, D6O_IA_NA, &addr) != ISC_R_SUCCESS) {
965 		if (!set_error(lq, STATUS_NotConfigured,
966 			       "Address not in a pool.")) {
967 			log_error("process_lq_by_address: unable "
968 				  "to set NotConfigured status code.");
969 			goto exit;
970 		}
971 		ret_val = 1;
972 		goto exit;
973 	}
974 	if (iasubopt_hash_lookup(&iaaddr, pool->leases, &addr,
975 				 sizeof(addr), MDL) == 0) {
976 		ret_val = 1;
977 		goto exit;
978 	}
979 	if ((iaaddr == NULL) || (iaaddr->state != FTS_ACTIVE) ||
980 	    (iaaddr->ia == NULL) || (iaaddr->ia->iaid_duid.len <= 4)) {
981 		ret_val = 1;
982 		goto exit;
983 	}
984 
985 	/*
986 	 * Build the client-data option (with client-id, ia-addr and clt-time).
987 	 */
988 	if (!option_state_allocate(&opt_state, MDL)) {
989 		log_error("process_lq_by_address: "
990 			  "no memory for option state.");
991 		goto exit;
992 	}
993 
994 	data_string_copy(&data, &iaaddr->ia->iaid_duid, MDL);
995 	data.data += 4;
996 	data.len -= 4;
997 	if (!save_option_buffer(&dhcpv6_universe, opt_state,
998 				NULL, (unsigned char *)data.data, data.len,
999 				D6O_CLIENTID, 0)) {
1000 		log_error("process_lq_by_address: error saving client ID.");
1001 		goto exit;
1002 	}
1003 	data_string_forget(&data, MDL);
1004 
1005 	data.len = IAADDR_OFFSET;
1006 	if (!buffer_allocate(&data.buffer, data.len, MDL)) {
1007 		log_error("process_lq_by_address: no memory for ia-addr.");
1008 		goto exit;
1009 	}
1010 	data.data = data.buffer->data;
1011 	memcpy(data.buffer->data, &iaaddr->addr, 16);
1012 	lifetime = iaaddr->prefer;
1013 	putULong(data.buffer->data + 16, lifetime);
1014 	lifetime = iaaddr->valid;
1015 	putULong(data.buffer->data + 20, lifetime);
1016 	if (!save_option_buffer(&dhcpv6_universe, opt_state,
1017 				NULL, (unsigned char *)data.data, data.len,
1018 				D6O_IAADDR, 0)) {
1019 		log_error("process_lq_by_address: error saving ia-addr.");
1020 		goto exit;
1021 	}
1022 	data_string_forget(&data, MDL);
1023 
1024 	lifetime = htonl(iaaddr->ia->cltt);
1025 	if (!save_option_buffer(&dhcpv6_universe, opt_state,
1026 				NULL, (unsigned char *)&lifetime, 4,
1027 				D6O_CLT_TIME, 0)) {
1028 		log_error("process_lq_by_address: error saving clt time.");
1029 		goto exit;
1030 	}
1031 
1032 	/*
1033 	 * Store the client-data option.
1034 	 */
1035 	opt_cursor = lq->cursor;
1036 	putUShort(lq->buf.data + lq->cursor, (unsigned)D6O_CLIENT_DATA);
1037 	lq->cursor += 2;
1038 	/* Skip option length. */
1039 	lq->cursor += 2;
1040 
1041 	lq->cursor += store_options6((char *)lq->buf.data + lq->cursor,
1042 				     sizeof(lq->buf) - lq->cursor,
1043 				     opt_state, lq->packet,
1044 				     required_opt_CLIENT_DATA, NULL);
1045 	/* Reset the length. */
1046 	putUShort(lq->buf.data + opt_cursor + 2,
1047 		  lq->cursor - (opt_cursor + 4));
1048 
1049 	/* Done. */
1050 	ret_val = 1;
1051 
1052      exit:
1053 	if (data.data != NULL)
1054 		data_string_forget(&data, MDL);
1055 	if (pool != NULL)
1056 		ipv6_pool_dereference(&pool, MDL);
1057 	if (iaaddr != NULL)
1058 		iasubopt_dereference(&iaaddr, MDL);
1059 	if (opt_state != NULL)
1060 		option_state_dereference(&opt_state, MDL);
1061 	return ret_val;
1062 }
1063 
1064 
1065 /*
1066  * Process a lease query.
1067  */
1068 void
dhcpv6_leasequery(struct data_string * reply_ret,struct packet * packet)1069 dhcpv6_leasequery(struct data_string *reply_ret, struct packet *packet) {
1070 	static struct lq6_state lq;
1071 	struct option_cache *oc;
1072 	int allow_lq;
1073 
1074 	/*
1075 	 * Initialize the lease query state.
1076 	 */
1077 	lq.packet = NULL;
1078 	memset(&lq.client_id, 0, sizeof(lq.client_id));
1079 	memset(&lq.server_id, 0, sizeof(lq.server_id));
1080 	memset(&lq.lq_query, 0, sizeof(lq.lq_query));
1081 	lq.query_opts = NULL;
1082 	lq.reply_opts = NULL;
1083 	packet_reference(&lq.packet, packet, MDL);
1084 
1085 	/*
1086 	 * Validate our input.
1087 	 */
1088 	if (!valid_query_msg(&lq)) {
1089 		goto exit;
1090 	}
1091 
1092 	/*
1093 	 * Prepare our reply.
1094 	 */
1095 	if (!option_state_allocate(&lq.reply_opts, MDL)) {
1096 		log_error("dhcpv6_leasequery: no memory for option state.");
1097 		goto exit;
1098 	}
1099 	execute_statements_in_scope(NULL, lq.packet, NULL, NULL,
1100 				    lq.packet->options, lq.reply_opts,
1101 				    &global_scope, root_group, NULL, NULL);
1102 
1103 	lq.buf.reply.msg_type = DHCPV6_LEASEQUERY_REPLY;
1104 
1105 	memcpy(lq.buf.reply.transaction_id,
1106 	       lq.packet->dhcpv6_transaction_id,
1107 	       sizeof(lq.buf.reply.transaction_id));
1108 
1109 	/*
1110 	 * Because LEASEQUERY has some privacy concerns, default to deny.
1111 	 */
1112 	allow_lq = 0;
1113 
1114 	/*
1115 	 * See if we are authorized to do LEASEQUERY.
1116 	 */
1117 	oc = lookup_option(&server_universe, lq.reply_opts, SV_LEASEQUERY);
1118 	if (oc != NULL) {
1119 		allow_lq = evaluate_boolean_option_cache(NULL,
1120 							 lq.packet,
1121 							 NULL, NULL,
1122 							 lq.packet->options,
1123 							 lq.reply_opts,
1124 							 &global_scope,
1125 							 oc, MDL);
1126 	}
1127 
1128 	if (!allow_lq) {
1129 		log_info("dhcpv6_leasequery: not allowed, query ignored.");
1130 		goto exit;
1131 	}
1132 
1133 	/*
1134 	 * Same than transmission of REPLY message in RFC 3315:
1135 	 *  server-id
1136 	 *  client-id
1137 	 */
1138 
1139 	oc = lookup_option(&dhcpv6_universe, lq.reply_opts, D6O_SERVERID);
1140 	if (oc == NULL) {
1141 		/* If not already in options, get from query then global. */
1142 		if (lq.server_id.data == NULL)
1143 			copy_server_duid(&lq.server_id, MDL);
1144 		if (!save_option_buffer(&dhcpv6_universe,
1145 					lq.reply_opts,
1146 					NULL,
1147 					(unsigned char *)lq.server_id.data,
1148 					lq.server_id.len,
1149 					D6O_SERVERID,
1150 					0)) {
1151 			log_error("dhcpv6_leasequery: "
1152 				  "error saving server identifier.");
1153 			goto exit;
1154 		}
1155 	}
1156 
1157 	if (!save_option_buffer(&dhcpv6_universe,
1158 				lq.reply_opts,
1159 				lq.client_id.buffer,
1160 				(unsigned char *)lq.client_id.data,
1161 				lq.client_id.len,
1162 				D6O_CLIENTID,
1163 				0)) {
1164 		log_error("dhcpv6_leasequery: "
1165 			  "error saving client identifier.");
1166 		goto exit;
1167 	}
1168 
1169 	lq.cursor = 4;
1170 
1171 	/*
1172 	 * Decode the lq-query option.
1173 	 */
1174 
1175 	if (lq.lq_query.len <= LQ_QUERY_OFFSET) {
1176 		if (!set_error(&lq, STATUS_MalformedQuery,
1177 			       "OPTION_LQ_QUERY too short.")) {
1178 			log_error("dhcpv6_leasequery: unable "
1179 				  "to set MalformedQuery status code.");
1180 			goto exit;
1181 		}
1182 		goto done;
1183 	}
1184 
1185 	lq.query_type = lq.lq_query.data [0];
1186 	memcpy(&lq.link_addr, lq.lq_query.data + 1, sizeof(lq.link_addr));
1187 	switch (lq.query_type) {
1188 		case LQ6QT_BY_ADDRESS:
1189 			break;
1190 		case LQ6QT_BY_CLIENTID:
1191 			if (!set_error(&lq, STATUS_UnknownQueryType,
1192 				       "QUERY_BY_CLIENTID not supported.")) {
1193 				log_error("dhcpv6_leasequery: unable to "
1194 					  "set UnknownQueryType status code.");
1195 				goto exit;
1196 			}
1197 			goto done;
1198 		default:
1199 			if (!set_error(&lq, STATUS_UnknownQueryType,
1200 				       "Unknown query-type.")) {
1201 				log_error("dhcpv6_leasequery: unable to "
1202 					  "set UnknownQueryType status code.");
1203 				goto exit;
1204 			}
1205 			goto done;
1206 	}
1207 
1208 	if (!option_state_allocate(&lq.query_opts, MDL)) {
1209 		log_error("dhcpv6_leasequery: no memory for option state.");
1210 		goto exit;
1211 	}
1212 	if (!parse_option_buffer(lq.query_opts,
1213 				 lq.lq_query.data + LQ_QUERY_OFFSET,
1214 				 lq.lq_query.len - LQ_QUERY_OFFSET,
1215 				 &dhcpv6_universe)) {
1216 		log_error("dhcpv6_leasequery: error parsing query-options.");
1217 		if (!set_error(&lq, STATUS_MalformedQuery,
1218 			       "Bad query-options.")) {
1219 			log_error("dhcpv6_leasequery: unable "
1220 				  "to set MalformedQuery status code.");
1221 			goto exit;
1222 		}
1223 		goto done;
1224 	}
1225 
1226 	/* Do it. */
1227 	if (!process_lq_by_address(&lq))
1228 		goto exit;
1229 
1230       done:
1231 	/* Store the options. */
1232 	lq.cursor += store_options6((char *)lq.buf.data + lq.cursor,
1233 				    sizeof(lq.buf) - lq.cursor,
1234 				    lq.reply_opts,
1235 				    lq.packet,
1236 				    required_opts_lq,
1237 				    NULL);
1238 
1239 	/* Return our reply to the caller. */
1240 	reply_ret->len = lq.cursor;
1241 	reply_ret->buffer = NULL;
1242 	if (!buffer_allocate(&reply_ret->buffer, lq.cursor, MDL)) {
1243 		log_fatal("dhcpv6_leasequery: no memory to store Reply.");
1244 	}
1245 	memcpy(reply_ret->buffer->data, lq.buf.data, lq.cursor);
1246 	reply_ret->data = reply_ret->buffer->data;
1247 
1248       exit:
1249 	/* Cleanup. */
1250 	if (lq.packet != NULL)
1251 		packet_dereference(&lq.packet, MDL);
1252 	if (lq.client_id.data != NULL)
1253 		data_string_forget(&lq.client_id, MDL);
1254 	if (lq.server_id.data != NULL)
1255 		data_string_forget(&lq.server_id, MDL);
1256 	if (lq.lq_query.data != NULL)
1257 		data_string_forget(&lq.lq_query, MDL);
1258 	if (lq.query_opts != NULL)
1259 		option_state_dereference(&lq.query_opts, MDL);
1260 	if (lq.reply_opts != NULL)
1261 		option_state_dereference(&lq.reply_opts, MDL);
1262 }
1263 
1264 #endif /* DHCPv6 */
1265