busybox/networking/udhcp/leases.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * leases.c -- tools to manage DHCP leases
   4 * Russ Dill <Russ.Dill@asu.edu> July 2001
   5 *
   6 * Licensed under GPLv2, see file LICENSE in this tarball for details.
   7 */
   8
   9#include "common.h"
  10#include "dhcpd.h"
  11
  12
  13/* Find the oldest expired lease, NULL if there are no expired leases */
  14static struct dhcpOfferedAddr *oldest_expired_lease(void)
  15{
  16        struct dhcpOfferedAddr *oldest_lease = NULL;
  17        leasetime_t oldest_time = time(NULL);
  18        unsigned i;
  19
  20        /* Unexpired leases have leases[i].expires >= current time
  21         * and therefore can't ever match */
  22        for (i = 0; i < server_config.max_leases; i++) {
  23                if (leases[i].expires < oldest_time) {
  24                        oldest_time = leases[i].expires;
  25                        oldest_lease = &(leases[i]);
  26                }
  27        }
  28        return oldest_lease;
  29}
  30
  31
  32/* Clear every lease out that chaddr OR yiaddr matches and is nonzero */
  33static void clear_lease(const uint8_t *chaddr, uint32_t yiaddr)
  34{
  35        unsigned i, j;
  36
  37        for (j = 0; j < 16 && !chaddr[j]; j++)
  38                continue;
  39
  40        for (i = 0; i < server_config.max_leases; i++) {
  41                if ((j != 16 && memcmp(leases[i].chaddr, chaddr, 16) == 0)
  42                 || (yiaddr && leases[i].yiaddr == yiaddr)
  43                ) {
  44                        memset(&(leases[i]), 0, sizeof(leases[i]));
  45                }
  46        }
  47}
  48
  49
  50/* Add a lease into the table, clearing out any old ones */
  51struct dhcpOfferedAddr* FAST_FUNC add_lease(
  52                const uint8_t *chaddr, uint32_t yiaddr,
  53                leasetime_t leasetime, uint8_t *hostname)
  54{
  55        struct dhcpOfferedAddr *oldest;
  56        uint8_t hostname_length;
  57
  58        /* clean out any old ones */
  59        clear_lease(chaddr, yiaddr);
  60
  61        oldest = oldest_expired_lease();
  62
  63        if (oldest) {
  64                oldest->hostname[0] = '\0';
  65                if (hostname) {
  66                        /* option size byte, + 1 for NUL */
  67                        hostname_length = hostname[-1] + 1;
  68                        if (hostname_length > sizeof(oldest->hostname))
  69                                hostname_length = sizeof(oldest->hostname);
  70                        hostname = (uint8_t*) safe_strncpy((char*)oldest->hostname, (char*)hostname, hostname_length);
  71                        /* sanitization (s/non-ASCII/^/g) */
  72                        while (*hostname) {
  73                                if (*hostname < ' ' || *hostname > 126)
  74                                        *hostname = '^';
  75                                hostname++;
  76                        }
  77                }
  78                memcpy(oldest->chaddr, chaddr, 16);
  79                oldest->yiaddr = yiaddr;
  80                oldest->expires = time(NULL) + leasetime;
  81        }
  82
  83        return oldest;
  84}
  85
  86
  87/* True if a lease has expired */
  88int FAST_FUNC lease_expired(struct dhcpOfferedAddr *lease)
  89{
  90        return (lease->expires < (leasetime_t) time(NULL));
  91}
  92
  93
  94/* Find the first lease that matches chaddr, NULL if no match */
  95struct dhcpOfferedAddr* FAST_FUNC find_lease_by_chaddr(const uint8_t *chaddr)
  96{
  97        unsigned i;
  98
  99        for (i = 0; i < server_config.max_leases; i++)
 100                if (!memcmp(leases[i].chaddr, chaddr, 16))
 101                        return &(leases[i]);
 102
 103        return NULL;
 104}
 105
 106
 107/* Find the first lease that matches yiaddr, NULL is no match */
 108struct dhcpOfferedAddr* FAST_FUNC find_lease_by_yiaddr(uint32_t yiaddr)
 109{
 110        unsigned i;
 111
 112        for (i = 0; i < server_config.max_leases; i++)
 113                if (leases[i].yiaddr == yiaddr)
 114                        return &(leases[i]);
 115
 116        return NULL;
 117}
 118
 119
 120/* check is an IP is taken, if it is, add it to the lease table */
 121static int nobody_responds_to_arp(uint32_t addr)
 122{
 123        /* 16 zero bytes */
 124        static const uint8_t blank_chaddr[16] = { 0 };
 125        /* = { 0 } helps gcc to put it in rodata, not bss */
 126
 127        struct in_addr temp;
 128        int r;
 129
 130        r = arpping(addr, server_config.server, server_config.arp, server_config.interface);
 131        if (r)
 132                return r;
 133
 134        temp.s_addr = addr;
 135        bb_info_msg("%s belongs to someone, reserving it for %u seconds",
 136                inet_ntoa(temp), (unsigned)server_config.conflict_time);
 137        add_lease(blank_chaddr, addr, server_config.conflict_time, NULL);
 138        return 0;
 139}
 140
 141
 142/* Find a new usable (we think) address. */
 143uint32_t FAST_FUNC find_free_or_expired_address(void)
 144{
 145        uint32_t addr;
 146        struct dhcpOfferedAddr *oldest_lease = NULL;
 147
 148        addr = server_config.start_ip; /* addr is in host order here */
 149        for (; addr <= server_config.end_ip; addr++) {
 150                uint32_t net_addr;
 151                struct dhcpOfferedAddr *lease;
 152
 153                /* ie, 192.168.55.0 */
 154                if ((addr & 0xff) == 0)
 155                        continue;
 156                /* ie, 192.168.55.255 */
 157                if ((addr & 0xff) == 0xff)
 158                        continue;
 159                net_addr = htonl(addr);
 160                /* addr has a static lease? */
 161                if (reservedIp(server_config.static_leases, net_addr))
 162                        continue;
 163
 164                lease = find_lease_by_yiaddr(net_addr);
 165                if (!lease) {
 166                        if (nobody_responds_to_arp(net_addr))
 167                                return net_addr;
 168                } else {
 169                        if (!oldest_lease || lease->expires < oldest_lease->expires)
 170                                oldest_lease = lease;
 171                }
 172        }
 173
 174        if (oldest_lease && lease_expired(oldest_lease)
 175         && nobody_responds_to_arp(oldest_lease->yiaddr)
 176        ) {
 177                return oldest_lease->yiaddr;
 178        }
 179
 180        return 0;
 181}
 182