busybox/networking/udhcp/leases.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * Russ Dill <Russ.Dill@asu.edu> July 2001
   4 *
   5 * Licensed under GPLv2, see file LICENSE in this source tree.
   6 */
   7#include "common.h"
   8#include "dhcpd.h"
   9
  10/* Find the oldest expired lease, NULL if there are no expired leases */
  11static struct dyn_lease *oldest_expired_lease(void)
  12{
  13        struct dyn_lease *oldest_lease = NULL;
  14        leasetime_t oldest_time = time(NULL);
  15        unsigned i;
  16
  17        /* Unexpired leases have g_leases[i].expires >= current time
  18         * and therefore can't ever match */
  19        for (i = 0; i < server_config.max_leases; i++) {
  20                if (g_leases[i].expires < oldest_time) {
  21                        oldest_time = g_leases[i].expires;
  22                        oldest_lease = &g_leases[i];
  23                }
  24        }
  25        return oldest_lease;
  26}
  27
  28/* Clear out all leases with matching nonzero chaddr OR yiaddr.
  29 * If chaddr == NULL, this is a conflict lease.
  30 */
  31static void clear_leases(const uint8_t *chaddr, uint32_t yiaddr)
  32{
  33        unsigned i;
  34
  35        for (i = 0; i < server_config.max_leases; i++) {
  36                if ((chaddr && memcmp(g_leases[i].lease_mac, chaddr, 6) == 0)
  37                 || (yiaddr && g_leases[i].lease_nip == yiaddr)
  38                ) {
  39                        memset(&g_leases[i], 0, sizeof(g_leases[i]));
  40                }
  41        }
  42}
  43
  44/* Add a lease into the table, clearing out any old ones.
  45 * If chaddr == NULL, this is a conflict lease.
  46 */
  47struct dyn_lease* FAST_FUNC add_lease(
  48                const uint8_t *chaddr, uint32_t yiaddr,
  49                leasetime_t leasetime,
  50                const char *hostname, int hostname_len)
  51{
  52        struct dyn_lease *oldest;
  53
  54        /* clean out any old ones */
  55        clear_leases(chaddr, yiaddr);
  56
  57        oldest = oldest_expired_lease();
  58
  59        if (oldest) {
  60                memset(oldest, 0, sizeof(*oldest));
  61                if (hostname) {
  62                        char *p;
  63
  64                        hostname_len++; /* include NUL */
  65                        if (hostname_len > sizeof(oldest->hostname))
  66                                hostname_len = sizeof(oldest->hostname);
  67                        p = safe_strncpy(oldest->hostname, hostname, hostname_len);
  68                        /* sanitization (s/non-ASCII/^/g) */
  69                        while (*p) {
  70                                if (*p < ' ' || *p > 126)
  71                                        *p = '^';
  72                                p++;
  73                        }
  74                }
  75                if (chaddr)
  76                        memcpy(oldest->lease_mac, chaddr, 6);
  77                oldest->lease_nip = yiaddr;
  78                oldest->expires = time(NULL) + leasetime;
  79        }
  80
  81        return oldest;
  82}
  83
  84/* True if a lease has expired */
  85int FAST_FUNC is_expired_lease(struct dyn_lease *lease)
  86{
  87        return (lease->expires < (leasetime_t) time(NULL));
  88}
  89
  90/* Find the first lease that matches MAC, NULL if no match */
  91struct dyn_lease* FAST_FUNC find_lease_by_mac(const uint8_t *mac)
  92{
  93        unsigned i;
  94
  95        for (i = 0; i < server_config.max_leases; i++)
  96                if (memcmp(g_leases[i].lease_mac, mac, 6) == 0)
  97                        return &g_leases[i];
  98
  99        return NULL;
 100}
 101
 102/* Find the first lease that matches IP, NULL is no match */
 103struct dyn_lease* FAST_FUNC find_lease_by_nip(uint32_t nip)
 104{
 105        unsigned i;
 106
 107        for (i = 0; i < server_config.max_leases; i++)
 108                if (g_leases[i].lease_nip == nip)
 109                        return &g_leases[i];
 110
 111        return NULL;
 112}
 113
 114/* Check if the IP is taken; if it is, add it to the lease table */
 115static int nobody_responds_to_arp(uint32_t nip, const uint8_t *safe_mac, unsigned arpping_ms)
 116{
 117        struct in_addr temp;
 118        int r;
 119
 120        r = arpping(nip, safe_mac,
 121                        server_config.server_nip,
 122                        server_config.server_mac,
 123                        server_config.interface,
 124                        arpping_ms);
 125        if (r)
 126                return r;
 127
 128        temp.s_addr = nip;
 129        bb_info_msg("%s belongs to someone, reserving it for %u seconds",
 130                inet_ntoa(temp), (unsigned)server_config.conflict_time);
 131        add_lease(NULL, nip, server_config.conflict_time, NULL, 0);
 132        return 0;
 133}
 134
 135/* Find a new usable (we think) address */
 136uint32_t FAST_FUNC find_free_or_expired_nip(const uint8_t *safe_mac, unsigned arpping_ms)
 137{
 138        uint32_t addr;
 139        struct dyn_lease *oldest_lease = NULL;
 140
 141#if ENABLE_FEATURE_UDHCPD_BASE_IP_ON_MAC
 142        uint32_t stop;
 143        unsigned i, hash;
 144
 145        /* hash hwaddr: use the SDBM hashing algorithm.  Seems to give good
 146         * dispersal even with similarly-valued "strings".
 147         */
 148        hash = 0;
 149        for (i = 0; i < 6; i++)
 150                hash += safe_mac[i] + (hash << 6) + (hash << 16) - hash;
 151
 152        /* pick a seed based on hwaddr then iterate until we find a free address. */
 153        addr = server_config.start_ip
 154                + (hash % (1 + server_config.end_ip - server_config.start_ip));
 155        stop = addr;
 156#else
 157        addr = server_config.start_ip;
 158#define stop (server_config.end_ip + 1)
 159#endif
 160        do {
 161                uint32_t nip;
 162                struct dyn_lease *lease;
 163
 164                /* ie, 192.168.55.0 */
 165                if ((addr & 0xff) == 0)
 166                        goto next_addr;
 167                /* ie, 192.168.55.255 */
 168                if ((addr & 0xff) == 0xff)
 169                        goto next_addr;
 170                nip = htonl(addr);
 171                /* skip our own address */
 172                if (nip == server_config.server_nip)
 173                        goto next_addr;
 174                /* is this a static lease addr? */
 175                if (is_nip_reserved(server_config.static_leases, nip))
 176                        goto next_addr;
 177
 178                lease = find_lease_by_nip(nip);
 179                if (!lease) {
 180//TODO: DHCP servers do not always sit on the same subnet as clients: should *ping*, not arp-ping!
 181                        if (nobody_responds_to_arp(nip, safe_mac, arpping_ms))
 182                                return nip;
 183                } else {
 184                        if (!oldest_lease || lease->expires < oldest_lease->expires)
 185                                oldest_lease = lease;
 186                }
 187
 188 next_addr:
 189                addr++;
 190#if ENABLE_FEATURE_UDHCPD_BASE_IP_ON_MAC
 191                if (addr > server_config.end_ip)
 192                        addr = server_config.start_ip;
 193#endif
 194        } while (addr != stop);
 195
 196        if (oldest_lease
 197         && is_expired_lease(oldest_lease)
 198         && nobody_responds_to_arp(oldest_lease->lease_nip, safe_mac, arpping_ms)
 199        ) {
 200                return oldest_lease->lease_nip;
 201        }
 202
 203        return 0;
 204}
 205