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