udhcpd initial lease

Denys Vlasenko vda.linux at googlemail.com
Sat Nov 27 21:13:35 UTC 2010


On Friday 26 November 2010 21:48, Grahame Jordan wrote:
> Hi,
> 
> When using dhcpd on an embedded system after booting on some clients for 
> example Windows7 it takes a long time
> to negotiate an ip address. In the case of Win7 it is insistent on using 
> the previous IP address that it had at its previous
> connection to the device.
> 
> The problem is that on most embedded systems the leases file is empty 
> after every boot. Making the negotiation
> difficult as there is no previous record in the leases file to negotiate 
> with. The lease request needs to time out before
> a new lease can be requested resulting in long delays on getting an IP 
> Address.
> Additionally some embedded systems generate a random MAC address every 
> time they connect say over USB making the
> negotiation just as difficult as with an empty leases file.

I do not understand the scenario you are describing.
Trying to reconstruct it....

1. Embedded system with udhcpd server is rebooted, and it is not set up
   to retain lease file. So lease file is empty.

2. Win7 clients requests an IP and supplies it previous IP as
   "requested IP".

(can you show the exact DISCOVER/REQUEST packets they send?)

3. Embedded system's udhcpd rejects this IP
   [looking at the code] because it is not in lease file:

                /* Look for a static/dynamic lease */
                static_lease_nip = get_static_nip_by_mac(server_config.static_leases, &packet.chaddr);
                if (static_lease_nip) {
                        bb_info_msg("Found static lease: %x", static_lease_nip);
                        memcpy(&fake_lease.lease_mac, &packet.chaddr, 6);
                        fake_lease.lease_nip = static_lease_nip;
                        fake_lease.expires = 0;
                        lease = &fake_lease;
                } else {
                        lease = find_lease_by_mac(packet.chaddr);
                }
...
                case DHCPDISCOVER:
                        log1("Received DISCOVER");

                        send_offer(&packet, static_lease_nip, lease);
                        break;

                case DHCPREQUEST:
                        log1("Received REQUEST");
                        if (!requested_opt) {
                                requested_nip = packet.ciaddr;
                                if (requested_nip == 0) {
                                        log1("no requested IP and no ciaddr, ignoring");
                                        break;
                                }
                        }
                        if (lease && requested_nip == lease->lease_nip) {
                                /* client requested or configured IP matches the lease.
                                 * ACK it, and bump lease expiration time. */
                                send_ACK(&packet, lease->lease_nip);
                                break;
                        }
                        if (server_id_opt) {
                                /* client was talking specifically to us.
                                 * "No, we don't have this IP for you". */
                                send_NAK(&packet);
                        }
                        break;


The key here is what send_offer() does in response to DISCOVER packet:

        /* If it is a static lease, use its IP */
        packet.yiaddr = static_lease_nip;
        /* Else: */
        if (!static_lease_nip) {
                /* We have no static lease for client's chaddr */
                uint32_t req_nip;
                uint8_t *req_ip_opt;
                const char *p_host_name;

                if (lease) {
                        /* We have a dynamic lease for client's chaddr.
                         * Reuse its IP (even if lease is expired).
                         * Note that we ignore requested IP in this case.
                         */
                        packet.yiaddr = lease->lease_nip;
                }
                /* Or: if client has requested an IP */
                else if ((req_ip_opt = udhcp_get_option(oldpacket, DHCP_REQUESTED_IP)) != NULL
                 /* (read IP) */
                 && (move_from_unaligned32(req_nip, req_ip_opt), 1)
                 /* and the IP is in the lease range */
                 && ntohl(req_nip) >= server_config.start_ip
                 && ntohl(req_nip) <= server_config.end_ip
                 /* and */
                 && (  !(lease = find_lease_by_nip(req_nip)) /* is not already taken */
                    || is_expired_lease(lease) /* or is taken, but expired */
                    )
                ) {
                        packet.yiaddr = req_nip;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ HERE
                }
                else {
                        /* Otherwise, find a free IP */
                        packet.yiaddr = find_free_or_expired_nip(oldpacket->chaddr);
                }

                if (!packet.yiaddr) {
                        bb_error_msg("no free IP addresses. OFFER abandoned");
                        return;
                }
                /* Reserve the IP for a short time hoping to get DHCPREQUEST soon */
                p_host_name = (const char*) udhcp_get_option(oldpacket, DHCP_HOST_NAME);
                lease = add_lease(packet.chaddr, packet.yiaddr,
                                server_config.offer_time,
                                p_host_name,
                                p_host_name ? (unsigned char)p_host_name[OPT_LEN - OPT_DATA] : 0
                );
                if (!lease) {
                        bb_error_msg("no free IP addresses. OFFER abandoned");
                        return;
                }
        }
        ...
        send_packet(&packet, /*force_bcast:*/ 0);
}


The code labeled with HERE should pick the requested IP address from the client,
add it into lease table and sent it back in the response packet.

Why it does not happen? And *where exactly* it breaks?


> I have created a patch that allows a request to go through if the leases 
> file is empty. This results in very quick dhcp
> negotiating. To alleviate the random MAC address issue I empty the 
> leases file which results in quick dhcp negotiation also.

This would be a quick and dirty workaround, without understanding
why existing code doesn't work. I prefer to understand why current
code doesn't work, and fixing it.

-- 
vda


More information about the busybox mailing list