leases.c 5.0 KB

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