Пример #1
0
/*
 * Parse all available options out of the specified packet.
 */
void
parse_options(struct packet *packet)
{
	/* Initially, zero all option pointers. */
	memset(packet->options, 0, sizeof(packet->options));

	/* If we don't see the magic cookie, there's nothing to parse. */
	if (memcmp(packet->raw->options, DHCP_OPTIONS_COOKIE, 4)) {
		packet->options_valid = 0;
		return;
	}

	/*
	 * Go through the options field, up to the end of the packet or
	 * the End field.
	 */
	parse_option_buffer(packet, &packet->raw->options[4],
	    packet->packet_length - DHCP_FIXED_NON_UDP - 4);

	/*
	 * If we parsed a DHCP Option Overload option, parse more
	 * options out of the buffer(s) containing them.
	 */
	if (packet->options_valid &&
	    packet->options[DHO_DHCP_OPTION_OVERLOAD].data) {
		if (packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1)
			parse_option_buffer(packet,
			    (unsigned char *)packet->raw->file,
			    sizeof(packet->raw->file));
		if (packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2)
			parse_option_buffer(packet,
			    (unsigned char *)packet->raw->sname,
			    sizeof(packet->raw->sname));
	}
}
Пример #2
0
/*
 * Process a lease query.
 */
void
dhcpv6_leasequery(struct data_string *reply_ret, struct packet *packet) {
	static struct lq6_state lq;
	struct option_cache *oc;
	int allow_lq;

	/*
	 * Initialize the lease query state.
	 */
	lq.packet = NULL;
	memset(&lq.client_id, 0, sizeof(lq.client_id));
	memset(&lq.server_id, 0, sizeof(lq.server_id));
	memset(&lq.lq_query, 0, sizeof(lq.lq_query));
	lq.query_opts = NULL;
	lq.reply_opts = NULL;
	packet_reference(&lq.packet, packet, MDL);

	/*
	 * Validate our input.
	 */
	if (!valid_query_msg(&lq)) {
		goto exit;
	}

	/*
	 * Prepare our reply.
	 */
	if (!option_state_allocate(&lq.reply_opts, MDL)) {
		log_error("dhcpv6_leasequery: no memory for option state.");
		goto exit;
	}
	execute_statements_in_scope(NULL, lq.packet, NULL, NULL,
				    lq.packet->options, lq.reply_opts,
				    &global_scope, root_group, NULL, NULL);

	lq.buf.reply.msg_type = DHCPV6_LEASEQUERY_REPLY;

	memcpy(lq.buf.reply.transaction_id,
	       lq.packet->dhcpv6_transaction_id,
	       sizeof(lq.buf.reply.transaction_id));

	/* 
	 * Because LEASEQUERY has some privacy concerns, default to deny.
	 */
	allow_lq = 0;

	/*
	 * See if we are authorized to do LEASEQUERY.
	 */
	oc = lookup_option(&server_universe, lq.reply_opts, SV_LEASEQUERY);
	if (oc != NULL) {
		allow_lq = evaluate_boolean_option_cache(NULL,
							 lq.packet,
							 NULL, NULL,
							 lq.packet->options,
							 lq.reply_opts,
							 &global_scope,
							 oc, MDL);
	}

	if (!allow_lq) {
		log_info("dhcpv6_leasequery: not allowed, query ignored.");
		goto exit;
	}
	    
	/*
	 * Same than transmission of REPLY message in RFC 3315:
	 *  server-id
	 *  client-id
	 */

	oc = lookup_option(&dhcpv6_universe, lq.reply_opts, D6O_SERVERID);
	if (oc == NULL) {
		/* If not already in options, get from query then global. */
		if (lq.server_id.data == NULL)
			copy_server_duid(&lq.server_id, MDL);
		if (!save_option_buffer(&dhcpv6_universe,
					lq.reply_opts,
					NULL,
					(unsigned char *)lq.server_id.data,
					lq.server_id.len, 
					D6O_SERVERID,
					0)) {
			log_error("dhcpv6_leasequery: "
				  "error saving server identifier.");
			goto exit;
		}
	}

	if (!save_option_buffer(&dhcpv6_universe,
				lq.reply_opts,
				lq.client_id.buffer,
				(unsigned char *)lq.client_id.data,
				lq.client_id.len,
				D6O_CLIENTID,
				0)) {
		log_error("dhcpv6_leasequery: "
			  "error saving client identifier.");
		goto exit;
	}

	lq.cursor = 4;

	/*
	 * Decode the lq-query option.
	 */

	if (lq.lq_query.len <= LQ_QUERY_OFFSET) {
		if (!set_error(&lq, STATUS_MalformedQuery,
			       "OPTION_LQ_QUERY too short.")) {
			log_error("dhcpv6_leasequery: unable "
				  "to set MalformedQuery status code.");
			goto exit;
		}
		goto done;
	}

	lq.query_type = lq.lq_query.data [0];
	memcpy(&lq.link_addr, lq.lq_query.data + 1, sizeof(lq.link_addr));
	switch (lq.query_type) {
		case LQ6QT_BY_ADDRESS:
			break;
		case LQ6QT_BY_CLIENTID:
			if (!set_error(&lq, STATUS_UnknownQueryType,
				       "QUERY_BY_CLIENTID not supported.")) {
				log_error("dhcpv6_leasequery: unable to "
					  "set UnknownQueryType status code.");
				goto exit;
			}
			goto done;
		default:
			if (!set_error(&lq, STATUS_UnknownQueryType,
				       "Unknown query-type.")) {
				log_error("dhcpv6_leasequery: unable to "
					  "set UnknownQueryType status code.");
				goto exit;
			}
			goto done;
	}

	if (!option_state_allocate(&lq.query_opts, MDL)) {
		log_error("dhcpv6_leasequery: no memory for option state.");
		goto exit;
	}
	if (!parse_option_buffer(lq.query_opts,
				 lq.lq_query.data + LQ_QUERY_OFFSET,
				 lq.lq_query.len - LQ_QUERY_OFFSET,
				 &dhcpv6_universe)) {
		log_error("dhcpv6_leasequery: error parsing query-options.");
		if (!set_error(&lq, STATUS_MalformedQuery,
			       "Bad query-options.")) {
			log_error("dhcpv6_leasequery: unable "
				  "to set MalformedQuery status code.");
			goto exit;
		}
		goto done;
	}

	/* Do it. */
	if (!process_lq_by_address(&lq))
		goto exit;

      done:
	/* Store the options. */
	lq.cursor += store_options6((char *)lq.buf.data + lq.cursor,
				    sizeof(lq.buf) - lq.cursor,
				    lq.reply_opts,
				    lq.packet,
				    required_opts_lq,
				    NULL);

	/* Return our reply to the caller. */
	reply_ret->len = lq.cursor;
	reply_ret->buffer = NULL;
	if (!buffer_allocate(&reply_ret->buffer, lq.cursor, MDL)) {
		log_fatal("dhcpv6_leasequery: no memory to store Reply.");
	}
	memcpy(reply_ret->buffer->data, lq.buf.data, lq.cursor);
	reply_ret->data = reply_ret->buffer->data;

      exit:
	/* Cleanup. */
	if (lq.packet != NULL)
		packet_dereference(&lq.packet, MDL);
	if (lq.client_id.data != NULL)
		data_string_forget(&lq.client_id, MDL);
	if (lq.server_id.data != NULL)
		data_string_forget(&lq.server_id, MDL);
	if (lq.lq_query.data != NULL)
		data_string_forget(&lq.lq_query, MDL);
	if (lq.query_opts != NULL)
		option_state_dereference(&lq.query_opts, MDL);
	if (lq.reply_opts != NULL)
		option_state_dereference(&lq.reply_opts, MDL);
}
Пример #3
0
void
do_packet(unsigned int from_port, struct in_addr from,
    struct ether_addr *hfrom)
{
	struct dhcp_packet *packet = &client->packet;
	struct option_data options[256];
	struct reject_elem *ap;
	void (*handler)(struct in_addr, struct option_data *, char *);
	char *type, *info;
	int i, rslt, options_valid = 1;

	if (packet->hlen != ETHER_ADDR_LEN) {
#ifdef DEBUG
		debug("Discarding packet with hlen != %s (%u)",
		    ifi->name, packet->hlen);
#endif
		return;
	} else if (memcmp(&ifi->hw_address, packet->chaddr,
	    sizeof(ifi->hw_address))) {
#ifdef DEBUG
		debug("Discarding packet with chaddr != %s (%s)", ifi->name,
		    ether_ntoa((struct ether_addr *)packet->chaddr));
#endif
		return;
	}

	if (client->xid != client->packet.xid) {
#ifdef DEBUG
		debug("Discarding packet with XID != %u (%u)", client->xid,
		    client->packet.xid);
#endif
		return;
	}

	TAILQ_FOREACH(ap, &config->reject_list, next)
		if (from.s_addr == ap->addr.s_addr) {
#ifdef DEBUG
			debug("Discarding packet from address on reject list "
			    "(%s)", inet_ntoa(from));
#endif
			return;
		}

	memset(options, 0, sizeof(options));

	if (memcmp(&packet->options, DHCP_OPTIONS_COOKIE, 4) == 0) {
		/* Parse the BOOTP/DHCP options field. */
		options_valid = parse_option_buffer(options,
		    &packet->options[4], sizeof(packet->options) - 4);

		/* Only DHCP packets have overload areas for options. */
		if (options_valid &&
		    options[DHO_DHCP_MESSAGE_TYPE].data &&
		    options[DHO_DHCP_OPTION_OVERLOAD].data) {
			if (options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1)
				options_valid = parse_option_buffer(options,
				    (unsigned char *)packet->file,
				    sizeof(packet->file));
			if (options_valid &&
			    options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2)
				options_valid = parse_option_buffer(options,
				    (unsigned char *)packet->sname,
				    sizeof(packet->sname));
		}
	}

	type = "<unknown>";
	handler = NULL;

	if (options[DHO_DHCP_MESSAGE_TYPE].data) {
		/* Always try a DHCP packet, even if a bad option was seen. */
		switch (options[DHO_DHCP_MESSAGE_TYPE].data[0]) {
		case DHCPOFFER:
			handler = dhcpoffer;
			type = "DHCPOFFER";
			break;
		case DHCPNAK:
			handler = dhcpnak;
			type = "DHCPNACK";
			break;
		case DHCPACK:
			handler = dhcpack;
			type = "DHCPACK";
			break;
		default:
#ifdef DEBUG
			debug("Discarding DHCP packet of unknown type (%d)",
				options[DHO_DHCP_MESSAGE_TYPE].data[0]);
#endif
			break;
		}
	} else if (options_valid && packet->op == BOOTREPLY) {
		handler = dhcpoffer;
		type = "BOOTREPLY";
	} else {
#ifdef DEBUG
		debug("Discarding packet which is neither DHCP nor BOOTP");
#endif
	}

	rslt = asprintf(&info, "%s from %s (%s)", type, inet_ntoa(from),
	    ether_ntoa(hfrom));
	if (rslt == -1)
		error("no memory for info string");

	if (handler)
		(*handler)(from, options, info);

	free(info);

	for (i = 0; i < 256; i++)
		if (options[i].len && options[i].data)
			free(options[i].data);
}
Пример #4
0
void
do_packet(int len, unsigned int from_port, struct iaddr from,
    struct hardware *hfrom)
{
	struct dhcp_packet *packet = &client->packet;
	struct option_data options[256];
	struct iaddrlist *ap;
	void (*handler)(struct iaddr, struct option_data *);
	char *type;
	int i, options_valid = 1;

	if (packet->hlen > sizeof(packet->chaddr)) {
		note("Discarding packet with invalid hlen.");
		return;
	}

	/*
	 * Silently drop the packet if the client hardware address in the
	 * packet is not the hardware address of the interface being managed.
	 */
	if ((ifi->hw_address.hlen != packet->hlen) ||
	    (memcmp(ifi->hw_address.haddr, packet->chaddr, packet->hlen)))
		return;

	memset(options, 0, sizeof(options));

	if (memcmp(&packet->options, DHCP_OPTIONS_COOKIE, 4) == 0) {
		/* Parse the BOOTP/DHCP options field. */
		options_valid = parse_option_buffer(options,
		    &packet->options[4], sizeof(packet->options) - 4);

		/* Only DHCP packets have overload areas for options. */
		if (options_valid &&
		    options[DHO_DHCP_MESSAGE_TYPE].data &&
		    options[DHO_DHCP_OPTION_OVERLOAD].data) {
			if (options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1)
				options_valid = parse_option_buffer(options,
				    (unsigned char *)packet->file,
				    sizeof(packet->file));
			if (options_valid &&
			    options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2)
				options_valid = parse_option_buffer(options,
				    (unsigned char *)packet->sname,
				    sizeof(packet->sname));
		}
	}

	type = "";
	handler = NULL;

	if (options[DHO_DHCP_MESSAGE_TYPE].data) {
		/* Always try a DHCP packet, even if a bad option was seen. */
		switch (options[DHO_DHCP_MESSAGE_TYPE].data[0]) {
		case DHCPOFFER:
			handler = dhcpoffer;
			type = "DHCPOFFER";
			break;
		case DHCPNAK:
			handler = dhcpnak;
			type = "DHCPNACK";
			break;
		case DHCPACK:
			handler = dhcpack;
			type = "DHCPACK";
			break;
		default:
			break;
		}
	} else if (options_valid && packet->op == BOOTREPLY) {
		handler = dhcpoffer;
		type = "BOOTREPLY";
	}

	for (ap = config->reject_list; ap && handler; ap = ap->next)
		if (addr_eq(from, ap->addr)) {
			note("%s from %s rejected.", type, piaddr(from));
			handler = NULL;
		}

	if (handler)
		(*handler)(from, options);

	for (i = 0; i < 256; i++)
		if (options[i].len && options[i].data)
			free(options[i].data);
}