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 == 0 /* empty entry */
  21                 || g_leases[i].expires < oldest_time
  22                ) {
  23                        oldest_time = g_leases[i].expires;
  24                        oldest_lease = &g_leases[i];
  25                }
  26        }
  27        return oldest_lease;
  28}
  29
  30/* Clear out all leases with matching nonzero chaddr OR yiaddr.
  31 * If chaddr == NULL, this is a conflict lease.
  32 */
  33static void clear_leases(const uint8_t *chaddr, uint32_t yiaddr)
  34{
  35        unsigned i;
  36
  37        for (i = 0; i < server_config.max_leases; i++) {
  38                if ((chaddr && memcmp(g_leases[i].lease_mac, chaddr, 6) == 0)
  39                 || (yiaddr && g_leases[i].lease_nip == yiaddr)
  40                ) {
  41                        memset(&g_leases[i], 0, sizeof(g_leases[i]));
  42                }
  43        }
  44}
  45
  46/* Add a lease into the table, clearing out any old ones.
  47 * If chaddr == NULL, this is a conflict lease.
  48 */
  49struct dyn_lease* FAST_FUNC add_lease(
  50                const uint8_t *chaddr, uint32_t yiaddr,
  51                leasetime_t leasetime,
  52                const char *hostname, int hostname_len)
  53{
  54        struct dyn_lease *oldest;
  55
  56        /* clean out any old ones */
  57        clear_leases(chaddr, yiaddr);
  58
  59        oldest = oldest_expired_lease();
  60
  61        if (oldest) {
  62                memset(oldest, 0, sizeof(*oldest));
  63                if (hostname) {
  64                        char *p;
  65
  66                        hostname_len++; /* include NUL */
  67                        if (hostname_len > sizeof(oldest->hostname))
  68                                hostname_len = sizeof(oldest->hostname);
  69                        p = safe_strncpy(oldest->hostname, hostname, hostname_len);
  70                        /*
  71                         * Sanitization (s/bad_char/./g).
  72                         * The intent is not to allow only "DNS-valid" hostnames,
  73                         * but merely make dumpleases output safe for shells to use.
  74                         * We accept "0-9A-Za-z._-", all other chars turn to dots.
  75                         */
  76                        while (*p) {
  77                                if (!isalnum(*p) && *p != '-' && *p != '_')
  78                                        *p = '.';
  79                                p++;
  80                        }
  81                }
  82                if (chaddr)
  83                        memcpy(oldest->lease_mac, chaddr, 6);
  84                oldest->lease_nip = yiaddr;
  85                oldest->expires = time(NULL) + leasetime;
  86        }
  87
  88        return oldest;
  89}
  90
  91/* True if a lease has expired */
  92int FAST_FUNC is_expired_lease(struct dyn_lease *lease)
  93{
  94        return (lease->expires < (leasetime_t) time(NULL));
  95}
  96
  97/* Find the first lease that matches MAC, NULL if no match */
  98struct dyn_lease* FAST_FUNC find_lease_by_mac(const uint8_t *mac)
  99{
 100        unsigned i;
 101
 102        for (i = 0; i < server_config.max_leases; i++)
 103                if (memcmp(g_leases[i].lease_mac, mac, 6) == 0)
 104                        return &g_leases[i];
 105
 106        return NULL;
 107}
 108
 109/* Find the first lease that matches IP, NULL is no match */
 110struct dyn_lease* FAST_FUNC find_lease_by_nip(uint32_t nip)
 111{
 112        unsigned i;
 113
 114        for (i = 0; i < server_config.max_leases; i++)
 115                if (g_leases[i].lease_nip == nip)
 116                        return &g_leases[i];
 117
 118        return NULL;
 119}
 120
 121/* Check if the IP is taken; if it is, add it to the lease table */
 122static int nobody_responds_to_arp(uint32_t nip, const uint8_t *safe_mac, unsigned arpping_ms)
 123{
 124        struct in_addr temp;
 125        int r;
 126
 127        r = arpping(nip, safe_mac,
 128                        server_config.server_nip,
 129                        server_config.server_mac,
 130                        server_config.interface,
 131                        arpping_ms);
 132        if (r)
 133                return r;
 134
 135        temp.s_addr = nip;
 136        bb_error_msg("%s belongs to someone, reserving it for %u seconds",
 137                inet_ntoa(temp), (unsigned)server_config.conflict_time);
 138        add_lease(NULL, nip, server_config.conflict_time, NULL, 0);
 139        return 0;
 140}
 141
 142/* Find a new usable (we think) address */
 143uint32_t FAST_FUNC find_free_or_expired_nip(const uint8_t *safe_mac, unsigned arpping_ms)
 144{
 145        uint32_t addr;
 146        struct dyn_lease *oldest_lease = NULL;
 147
 148#if ENABLE_FEATURE_UDHCPD_BASE_IP_ON_MAC
 149        uint32_t stop;
 150        unsigned i, hash;
 151
 152        /* hash hwaddr: use the SDBM hashing algorithm.  Seems to give good
 153         * dispersal even with similarly-valued "strings".
 154         */
 155        hash = 0;
 156        for (i = 0; i < 6; i++)
 157                hash += safe_mac[i] + (hash << 6) + (hash << 16) - hash;
 158
 159        /* pick a seed based on hwaddr then iterate until we find a free address. */
 160        addr = server_config.start_ip
 161                + (hash % (1 + server_config.end_ip - server_config.start_ip));
 162        stop = addr;
 163#else
 164        addr = server_config.start_ip;
 165#define stop (server_config.end_ip + 1)
 166#endif
 167        do {
 168                uint32_t nip;
 169                struct dyn_lease *lease;
 170
 171                /* ie, 192.168.55.0 */
 172                if ((addr & 0xff) == 0)
 173                        goto next_addr;
 174                /* ie, 192.168.55.255 */
 175                if ((addr & 0xff) == 0xff)
 176                        goto next_addr;
 177                nip = htonl(addr);
 178                /* skip our own address */
 179                if (nip == server_config.server_nip)
 180                        goto next_addr;
 181                /* is this a static lease addr? */
 182                if (is_nip_reserved(server_config.static_leases, nip))
 183                        goto next_addr;
 184
 185                lease = find_lease_by_nip(nip);
 186                if (!lease) {
 187//TODO: DHCP servers do not always sit on the same subnet as clients: should *ping*, not arp-ping!
 188                        if (nobody_responds_to_arp(nip, safe_mac, arpping_ms))
 189                                return nip;
 190                } else {
 191                        if (!oldest_lease || lease->expires < oldest_lease->expires)
 192                                oldest_lease = lease;
 193                }
 194
 195 next_addr:
 196                addr++;
 197#if ENABLE_FEATURE_UDHCPD_BASE_IP_ON_MAC
 198                if (addr > server_config.end_ip)
 199                        addr = server_config.start_ip;
 200#endif
 201        } while (addr != stop);
 202
 203        if (oldest_lease
 204         && is_expired_lease(oldest_lease)
 205         && nobody_responds_to_arp(oldest_lease->lease_nip, safe_mac, arpping_ms)
 206        ) {
 207                return oldest_lease->lease_nip;
 208        }
 209
 210        return 0;
 211}
 212