This diff fixes dhcpinform to work without lease.
ok?
Fix dhcpinform to work without lease.
Diff from Yuuichi Someya.
Index: usr.sbin/dhcpd/dhcp.c
===================================================================
RCS file: /disk/cvs/openbsd/src/usr.sbin/dhcpd/dhcp.c,v
retrieving revision 1.36
diff -u -p -r1.36 dhcp.c
--- usr.sbin/dhcpd/dhcp.c 5 Apr 2013 19:31:36 -0000 1.36
+++ usr.sbin/dhcpd/dhcp.c 9 Jul 2014 14:00:05 -0000
@@ -45,6 +45,8 @@ int outstanding_pings;
static char dhcp_message[256];
+void ack_inform(struct packet *, struct subnet *, struct iaddr *);
+
void
dhcp(struct packet *packet)
{
@@ -500,7 +502,6 @@ dhcpdecline(struct packet *packet)
void
dhcpinform(struct packet *packet)
{
- struct lease *lease;
struct iaddr cip;
struct subnet *subnet;
@@ -528,28 +529,328 @@ dhcpinform(struct packet *packet)
return;
}
- lease = find_lease(packet, subnet->shared_network, 0);
- if (!lease) {
- note("DHCPINFORM packet from %s but no lease present",
- print_hw_addr(packet->raw->htype, packet->raw->hlen,
- packet->raw->chaddr));
- return;
- }
+ ack_inform(packet, subnet, &cip);
+}
- /* If this subnet won't boot unknown clients, ignore the
- request. */
- if (!lease->host &&
- !lease->subnet->group->boot_unknown_clients) {
- note("Ignoring unknown client %s",
- print_hw_addr(packet->raw->htype, packet->raw->hlen,
- packet->raw->chaddr));
- } else if (lease->host && !lease->host->group->allow_booting) {
- note("Declining to boot client %s",
- lease->host->name ? lease->host->name :
- print_hw_addr(packet->raw->htype, packet->raw->hlen,
- packet->raw->chaddr));
+void
+ack_inform(struct packet *packet, struct subnet *subnet, struct iaddr *cip)
+{
+ struct lease lt;
+ struct lease_state *state;
+ struct class *vendor_class, *user_class;
+ int ulafdr, i;
+
+ if (packet->options[DHO_DHCP_CLASS_IDENTIFIER].len) {
+ vendor_class = find_class(0,
+ packet->options[DHO_DHCP_CLASS_IDENTIFIER].data,
+ packet->options[DHO_DHCP_CLASS_IDENTIFIER].len);
} else
- ack_lease(packet, lease, DHCPACK, 0);
+ vendor_class = NULL;
+
+ if (packet->options[DHO_DHCP_USER_CLASS_ID].len) {
+ user_class = find_class(1,
+ packet->options[DHO_DHCP_USER_CLASS_ID].data,
+ packet->options[DHO_DHCP_USER_CLASS_ID].len);
+ } else
+ user_class = NULL;
+
+ /* Allocate a lease state structure... */
+ state = new_lease_state("ack_inform");
+ if (!state)
+ error("unable to allocate lease state!");
+ memset(state, 0, sizeof *state);
+ state->got_requested_address = packet->got_requested_address;
+ state->shared_network = packet->interface->shared_network;
+
+ /* Remember if we got a server identifier option. */
+ if (packet->options[DHO_DHCP_SERVER_IDENTIFIER].len)
+ state->got_server_identifier = 1;
+
+ if (user_class && user_class->group->filename)
+ strlcpy(state->filename, user_class->group->filename,
+ sizeof state->filename);
+ else if (vendor_class && vendor_class->group->filename)
+ strlcpy(state->filename, vendor_class->group->filename,
+ sizeof state->filename);
+ else if (packet->raw->file[0])
+ strlcpy(state->filename, packet->raw->file,
+ sizeof state->filename);
+ else if (subnet->group->filename)
+ strlcpy(state->filename, subnet->group->filename,
+ sizeof state->filename);
+ else
+ strlcpy(state->filename, "", sizeof state->filename);
+
+ /* Choose a server name as above. */
+ if (user_class && user_class->group->server_name)
+ state->server_name = user_class->group->server_name;
+ else if (vendor_class && vendor_class->group->server_name)
+ state->server_name = vendor_class->group->server_name;
+ else if (subnet->group->server_name)
+ state->server_name = subnet->group->server_name;
+ else state->server_name = NULL;
+
+ memset(<, 0, sizeof lt);
+ lt.ip_addr = *cip;
+
+ /* Record the uid, if given... */
+ i = DHO_DHCP_CLIENT_IDENTIFIER;
+ if (packet->options[i].len) {
+ if (packet->options[i].len <= sizeof lt.uid_buf) {
+ memcpy(lt.uid_buf, packet->options[i].data,
+ packet->options[i].len);
+ lt.uid = lt.uid_buf;
+ lt.uid_max = sizeof lt.uid_buf;
+ lt.uid_len = packet->options[i].len;
+ } else {
+ lt.uid_max = lt.uid_len = packet->options[i].len;
+ lt.uid = (unsigned char *)malloc(lt.uid_max);
+ if (!lt.uid)
+ error("can't allocate memory for large uid.");
+ memcpy(lt.uid, packet->options[i].data, lt.uid_len);
+ }
+ }
+
+ lt.subnet = subnet;
+ lt.shared_network = subnet->shared_network;
+ lt.hardware_addr.hlen = packet->raw->hlen;
+ lt.hardware_addr.htype = packet->raw->htype;
+ memcpy(lt.hardware_addr.haddr, packet->raw->chaddr,
+ sizeof packet->raw->chaddr);
+
+ /* Remember the interface on which the packet arrived. */
+ state->ip = packet->interface;
+
+ /* Set a flag if this client is a lame Microsoft client that NUL
+ terminates string options and expects us to do likewise. */
+ if (packet->options[DHO_HOST_NAME].data &&
+ packet->options[DHO_HOST_NAME].data[
+ packet->options[DHO_HOST_NAME].len - 1] == '\0')
+ lt.flags |= MS_NULL_TERMINATION;
+ else
+ lt.flags &= ~MS_NULL_TERMINATION;
+
+ /* Remember the giaddr, xid, secs, flags and hops. */
+ state->giaddr = packet->raw->giaddr;
+ state->ciaddr = packet->raw->ciaddr;
+ state->xid = packet->raw->xid;
+ state->secs = packet->raw->secs;
+ state->bootp_flags = packet->raw->flags;
+ state->hops = packet->raw->hops;
+ state->offer = DHCPACK;
+ memcpy(&state->haddr, packet->haddr, sizeof state->haddr);
+
+ /* Figure out what options to send to the client: */
+
+ /* Start out with the subnet options... */
+ memcpy(state->options, subnet->group->options, sizeof state->options);
+
+ /* Vendor and user classes are only supported for DHCP clients. */
+ /* If we have a vendor class, install those options,
+ superseding any subnet options. */
+ if (vendor_class) {
+ for (i = 0; i < 256; i++)
+ if (vendor_class->group->options[i])
+ state->options[i] =
+ vendor_class->group->options[i];
+ }
+
+ /* If we have a user class, install those options,
+ superseding any subnet and vendor class options. */
+ if (user_class) {
+ for (i = 0; i < 256; i++)
+ if (user_class->group->options[i])
+ state->options[i] =
+ user_class->group->options[i];
+ }
+
+ /* If we have a host_decl structure, install the associated
+ options, superseding anything that's in the way. */
+ if (lt.host) {
+ for (i = 0; i < 256; i++)
+ if (lt.host->group->options[i])
+ state->options[i] =
+ lt.host->group->options[i];
+ }
+
+ /* Get the Maximum Message Size option from the packet, if one
+ was sent. */
+ i = DHO_DHCP_MAX_MESSAGE_SIZE;
+ if (packet->options[i].data &&
+ packet->options[i].len == sizeof(u_int16_t))
+ state->max_message_size = getUShort(packet->options[i].data);
+ /* Otherwise, if a maximum message size was specified, use that. */
+ else if (state->options[i] && state->options[i]->value)
+ state->max_message_size = getUShort(state->options[i]->value);
+
+ /* Save the parameter request list if there is one. */
+ i = DHO_DHCP_PARAMETER_REQUEST_LIST;
+ if (packet->options[i].data) {
+ state->prl = calloc(1, packet->options[i].len);
+ if (!state->prl)
+ warning("no memory for parameter request list");
+ else {
+ memcpy(state->prl, packet->options[i].data,
+ packet->options[i].len);
+ state->prl_len = packet->options[i].len;
+ }
+ }
+
+ /* If we didn't get a hostname from an option somewhere, see if
+ we can get one from the lease. */
+ i = DHO_HOST_NAME;
+ if (!state->options[i] && lt.hostname) {
+ state->options[i] = new_tree_cache("hostname");
+ state->options[i]->flags = TC_TEMPORARY;
+ state->options[i]->value = (unsigned char *)lt.hostname;
+ state->options[i]->len = strlen(lt.hostname);
+ state->options[i]->buf_size = state->options[i]->len;
+ state->options[i]->timeout = -1;
+ state->options[i]->tree = NULL;
+ }
+
+ /*
+ * Now, if appropriate, put in DHCP-specific options that
+ * override those.
+ */
+ i = DHO_DHCP_MESSAGE_TYPE;
+ state->options[i] = new_tree_cache("message-type");
+ state->options[i]->flags = TC_TEMPORARY;
+ state->options[i]->value = &state->offer;
+ state->options[i]->len = sizeof state->offer;
+ state->options[i]->buf_size = sizeof state->offer;
+ state->options[i]->timeout = -1;
+ state->options[i]->tree = NULL;
+
+ i = DHO_DHCP_SERVER_IDENTIFIER;
+ if (!state->options[i]) {
+ use_primary:
+ state->options[i] = new_tree_cache("server-id");
+ state->options[i]->value =
+ (unsigned char *)&state->ip->primary_address;
+ state->options[i]->len =
+ sizeof state->ip->primary_address;
+ state->options[i]->buf_size =
+ state->options[i]->len;
+ state->options[i]->timeout = -1;
+ state->options[i]->tree = NULL;
+ state->from.len = sizeof state->ip->primary_address;
+ memcpy(state->from.iabuf, &state->ip->primary_address,
+ state->from.len);
+ } else {
+ /* Find the value of the server identifier... */
+ if (!tree_evaluate(state->options[i]))
+ goto use_primary;
+ if (!state->options[i]->value ||
+ (state->options[i]->len > sizeof state->from.iabuf))
+ goto use_primary;
+
+ state->from.len = state->options[i]->len;
+ memcpy(state->from.iabuf, state->options[i]->value,
+ state->from.len);
+ }
+
+ /* If we used the vendor class the client specified, we
+ have to return it. */
+ if (vendor_class) {
+ i = DHO_DHCP_CLASS_IDENTIFIER;
+ state->options[i] =
+ new_tree_cache("class-identifier");
+ state->options[i]->flags = TC_TEMPORARY;
+ state->options[i]->value =
+ (unsigned char *)vendor_class->name;
+ state->options[i]->len =
+ strlen(vendor_class->name);
+ state->options[i]->buf_size =
+ state->options[i]->len;
+ state->options[i]->timeout = -1;
+ state->options[i]->tree = NULL;
+ }
+
+ /* If we used the user class the client specified, we
+ have to return it. */
+ if (user_class) {
+ i = DHO_DHCP_USER_CLASS_ID;
+ state->options[i] = new_tree_cache("user-class");
+ state->options[i]->flags = TC_TEMPORARY;
+ state->options[i]->value =
+ (unsigned char *)user_class->name;
+ state->options[i]->len =
+ strlen(user_class->name);
+ state->options[i]->buf_size =
+ state->options[i]->len;
+ state->options[i]->timeout = -1;
+ state->options[i]->tree = NULL;
+ }
+
+ /* Use the subnet mask from the subnet declaration if no other
+ mask has been provided. */
+ i = DHO_SUBNET_MASK;
+ if (!state->options[i]) {
+ state->options[i] = new_tree_cache("subnet-mask");
+ state->options[i]->flags = TC_TEMPORARY;
+ state->options[i]->value =
+ subnet->netmask.iabuf;
+ state->options[i]->len = subnet->netmask.len;
+ state->options[i]->buf_size =
+ subnet->netmask.len;
+ state->options[i]->timeout = -1;
+ state->options[i]->tree = NULL;
+ }
+
+ /* If so directed, use the leased IP address as the router address.
+ This supposedly makes Win95 machines ARP for all IP addresses,
+ so if the local router does proxy arp, you win. */
+ ulafdr = 0;
+ if (user_class) {
+ if (user_class->group->use_lease_addr_for_default_route)
+ ulafdr = 1;
+ } else if (vendor_class) {
+ if (vendor_class->group->use_lease_addr_for_default_route)
+ ulafdr = 1;
+ } else if (subnet->group->use_lease_addr_for_default_route)
+ ulafdr = 1;
+ else
+ ulafdr = 0;
+ i = DHO_ROUTERS;
+ if (ulafdr && !state->options[i]) {
+ state->options[i] = new_tree_cache("routers");
+ state->options[i]->flags = TC_TEMPORARY;
+ state->options[i]->value = cip->iabuf;
+ state->options[i]->len = 4;
+ state->options[i]->buf_size = 4;
+ state->options[i]->timeout = -1;
+ state->options[i]->tree = NULL;
+ }
+
+ /* Echo back the relay agent information, if present */
+ i = DHO_RELAY_AGENT_INFORMATION;
+ if (state->giaddr.s_addr && !state->options[i] &&
+ packet->options[i].data && packet->options[i].len) {
+ state->options[i] = new_tree_cache("relay-agent-information");
+ state->options[i]->flags = TC_TEMPORARY;
+ state->options[i]->value = packet->options[i].data;
+ state->options[i]->len = packet->options[i].len;
+ state->options[i]->buf_size = packet->options[i].len;
+ state->options[i]->timeout = -1;
+ state->options[i]->tree = NULL;
+ }
+
+ /* RFC 2131: MUST NOT send client identifier option in OFFER/ACK! */
+ i = DHO_DHCP_CLIENT_IDENTIFIER;
+ memset(&state->options[i], 0, sizeof(state->options[i]));
+
+ /* Remove any time options, per section 3.4 RFC 2131 */
+ i = DHO_DHCP_LEASE_TIME;
+ memset(&state->options[i], 0, sizeof(state->options[i]));
+ i = DHO_DHCP_RENEWAL_TIME;
+ memset(&state->options[i], 0, sizeof(state->options[i]));
+ i = DHO_DHCP_REBINDING_TIME;
+ memset(&state->options[i], 0, sizeof(state->options[i]));
+
+ lt.state = state;
+ lt.timestamp = cur_time;
+ dhcp_reply(<);
}
void