File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / dhcp / server / dhcpleasequery.c
Revision 1.1: download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 22:30:18 2012 UTC (12 years, 4 months ago) by misho
CVS tags: MAIN, HEAD
Initial revision

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>