• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/router/busybox/networking/udhcp/
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			/*
69			 * Sanitization (s/bad_char/./g).
70			 * The intent is not to allow only "DNS-valid" hostnames,
71			 * but merely make dumpleases output safe for shells to use.
72			 * We accept "0-9A-Za-z._-", all other chars turn to dots.
73			 */
74			while (*p) {
75				if (!isalnum(*p) && *p != '-' && *p != '_')
76					*p = '.';
77				p++;
78			}
79		}
80		if (chaddr)
81			memcpy(oldest->lease_mac, chaddr, 6);
82		oldest->lease_nip = yiaddr;
83		oldest->expires = time(NULL) + leasetime;
84	}
85
86	return oldest;
87}
88
89/* True if a lease has expired */
90int FAST_FUNC is_expired_lease(struct dyn_lease *lease)
91{
92	return (lease->expires < (leasetime_t) time(NULL));
93}
94
95/* Find the first lease that matches MAC, NULL if no match */
96struct dyn_lease* FAST_FUNC find_lease_by_mac(const uint8_t *mac)
97{
98	unsigned i;
99
100	for (i = 0; i < server_config.max_leases; i++)
101		if (memcmp(g_leases[i].lease_mac, mac, 6) == 0)
102			return &g_leases[i];
103
104	return NULL;
105}
106
107/* Find the first lease that matches IP, NULL is no match */
108struct dyn_lease* FAST_FUNC find_lease_by_nip(uint32_t nip)
109{
110	unsigned i;
111
112	for (i = 0; i < server_config.max_leases; i++)
113		if (g_leases[i].lease_nip == nip)
114			return &g_leases[i];
115
116	return NULL;
117}
118
119/* Check if the IP is taken; if it is, add it to the lease table */
120static int nobody_responds_to_arp(uint32_t nip, const uint8_t *safe_mac, unsigned arpping_ms)
121{
122	struct in_addr temp;
123	int r;
124
125	r = arpping(nip, safe_mac,
126			server_config.server_nip,
127			server_config.server_mac,
128			server_config.interface,
129			arpping_ms);
130	if (r)
131		return r;
132
133	temp.s_addr = nip;
134	bb_info_msg("%s belongs to someone, reserving it for %u seconds",
135		inet_ntoa(temp), (unsigned)server_config.conflict_time);
136	add_lease(NULL, nip, server_config.conflict_time, NULL, 0);
137	return 0;
138}
139
140/* Find a new usable (we think) address */
141uint32_t FAST_FUNC find_free_or_expired_nip(const uint8_t *safe_mac, unsigned arpping_ms)
142{
143	uint32_t addr;
144	struct dyn_lease *oldest_lease = NULL;
145
146#if ENABLE_FEATURE_UDHCPD_BASE_IP_ON_MAC
147	uint32_t stop;
148	unsigned i, hash;
149
150	/* hash hwaddr: use the SDBM hashing algorithm.  Seems to give good
151	 * dispersal even with similarly-valued "strings".
152	 */
153	hash = 0;
154	for (i = 0; i < 6; i++)
155		hash += safe_mac[i] + (hash << 6) + (hash << 16) - hash;
156
157	/* pick a seed based on hwaddr then iterate until we find a free address. */
158	addr = server_config.start_ip
159		+ (hash % (1 + server_config.end_ip - server_config.start_ip));
160	stop = addr;
161#else
162	addr = server_config.start_ip;
163#define stop (server_config.end_ip + 1)
164#endif
165	do {
166		uint32_t nip;
167		struct dyn_lease *lease;
168
169		/* ie, 192.168.55.0 */
170		if ((addr & 0xff) == 0)
171			goto next_addr;
172		/* ie, 192.168.55.255 */
173		if ((addr & 0xff) == 0xff)
174			goto next_addr;
175		nip = htonl(addr);
176		/* skip our own address */
177		if (nip == server_config.server_nip)
178			goto next_addr;
179		/* is this a static lease addr? */
180		if (is_nip_reserved(server_config.static_leases, nip))
181			goto next_addr;
182
183		lease = find_lease_by_nip(nip);
184		if (!lease) {
185//TODO: DHCP servers do not always sit on the same subnet as clients: should *ping*, not arp-ping!
186			if (nobody_responds_to_arp(nip, safe_mac, arpping_ms))
187				return nip;
188		} else {
189			if (!oldest_lease || lease->expires < oldest_lease->expires)
190				oldest_lease = lease;
191		}
192
193 next_addr:
194		addr++;
195#if ENABLE_FEATURE_UDHCPD_BASE_IP_ON_MAC
196		if (addr > server_config.end_ip)
197			addr = server_config.start_ip;
198#endif
199	} while (addr != stop);
200
201	if (oldest_lease
202	 && is_expired_lease(oldest_lease)
203	 && nobody_responds_to_arp(oldest_lease->lease_nip, safe_mac, arpping_ms)
204	) {
205		return oldest_lease->lease_nip;
206	}
207
208	return 0;
209}
210