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(&lt, 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(&lt);
 }
 
 void

Reply via email to