3
0

dhcpd.c 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. /* vi: set sw=4 ts=4: */
  2. /* dhcpd.c
  3. *
  4. * udhcp Server
  5. * Copyright (C) 1999 Matthew Ramsay <matthewr@moreton.com.au>
  6. * Chris Trew <ctrew@moreton.com.au>
  7. *
  8. * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
  9. *
  10. * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
  11. */
  12. #include <syslog.h>
  13. #include "common.h"
  14. #include "dhcpd.h"
  15. #include "options.h"
  16. /* globals */
  17. struct dhcpOfferedAddr *leases;
  18. /* struct server_config_t server_config is in bb_common_bufsiz1 */
  19. int udhcpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  20. int udhcpd_main(int argc, char **argv)
  21. {
  22. fd_set rfds;
  23. struct timeval tv;
  24. int server_socket = -1, bytes, retval, max_sock;
  25. struct dhcpMessage packet;
  26. uint8_t *state, *server_id, *requested;
  27. uint32_t server_id_align, requested_align, static_lease_ip;
  28. unsigned timeout_end;
  29. unsigned num_ips;
  30. unsigned opt;
  31. struct option_set *option;
  32. struct dhcpOfferedAddr *lease, static_lease;
  33. opt = getopt32(argv, "fS");
  34. argv += optind;
  35. if (!(opt & 1)) { /* no -f */
  36. bb_daemonize_or_rexec(0, argv);
  37. logmode &= ~LOGMODE_STDIO;
  38. }
  39. if (opt & 2) { /* -S */
  40. openlog(applet_name, LOG_PID, LOG_LOCAL0);
  41. logmode |= LOGMODE_SYSLOG;
  42. }
  43. /* Would rather not do read_config before daemonization -
  44. * otherwise NOMMU machines will parse config twice */
  45. read_config(argv[0] ? argv[0] : DHCPD_CONF_FILE);
  46. /* Make sure fd 0,1,2 are open */
  47. bb_sanitize_stdio();
  48. /* Equivalent of doing a fflush after every \n */
  49. setlinebuf(stdout);
  50. /* Create pidfile */
  51. write_pidfile(server_config.pidfile);
  52. /* if (!..) bb_perror_msg("cannot create pidfile %s", pidfile); */
  53. bb_info_msg("%s (v"BB_VER") started", applet_name);
  54. option = find_option(server_config.options, DHCP_LEASE_TIME);
  55. server_config.lease = LEASE_TIME;
  56. if (option) {
  57. memcpy(&server_config.lease, option->data + 2, 4);
  58. server_config.lease = ntohl(server_config.lease);
  59. }
  60. /* Sanity check */
  61. num_ips = server_config.end_ip - server_config.start_ip + 1;
  62. if (server_config.max_leases > num_ips) {
  63. bb_error_msg("max_leases=%u is too big, setting to %u",
  64. (unsigned)server_config.max_leases, num_ips);
  65. server_config.max_leases = num_ips;
  66. }
  67. leases = xzalloc(server_config.max_leases * sizeof(*leases));
  68. read_leases(server_config.lease_file);
  69. if (read_interface(server_config.interface, &server_config.ifindex,
  70. &server_config.server, server_config.arp)) {
  71. retval = 1;
  72. goto ret;
  73. }
  74. /* Setup the signal pipe */
  75. udhcp_sp_setup();
  76. timeout_end = monotonic_sec() + server_config.auto_time;
  77. while (1) { /* loop until universe collapses */
  78. if (server_socket < 0) {
  79. server_socket = listen_socket(/*INADDR_ANY,*/ SERVER_PORT,
  80. server_config.interface);
  81. }
  82. max_sock = udhcp_sp_fd_set(&rfds, server_socket);
  83. if (server_config.auto_time) {
  84. tv.tv_sec = timeout_end - monotonic_sec();
  85. tv.tv_usec = 0;
  86. }
  87. retval = 0;
  88. if (!server_config.auto_time || tv.tv_sec > 0) {
  89. retval = select(max_sock + 1, &rfds, NULL, NULL,
  90. server_config.auto_time ? &tv : NULL);
  91. }
  92. if (retval == 0) {
  93. write_leases();
  94. timeout_end = monotonic_sec() + server_config.auto_time;
  95. continue;
  96. }
  97. if (retval < 0 && errno != EINTR) {
  98. DEBUG("error on select");
  99. continue;
  100. }
  101. switch (udhcp_sp_read(&rfds)) {
  102. case SIGUSR1:
  103. bb_info_msg("Received a SIGUSR1");
  104. write_leases();
  105. /* why not just reset the timeout, eh */
  106. timeout_end = monotonic_sec() + server_config.auto_time;
  107. continue;
  108. case SIGTERM:
  109. bb_info_msg("Received a SIGTERM");
  110. goto ret0;
  111. case 0: break; /* no signal */
  112. default: continue; /* signal or error (probably EINTR) */
  113. }
  114. bytes = udhcp_get_packet(&packet, server_socket); /* this waits for a packet - idle */
  115. if (bytes < 0) {
  116. if (bytes == -1 && errno != EINTR) {
  117. DEBUG("error on read, %s, reopening socket", strerror(errno));
  118. close(server_socket);
  119. server_socket = -1;
  120. }
  121. continue;
  122. }
  123. state = get_option(&packet, DHCP_MESSAGE_TYPE);
  124. if (state == NULL) {
  125. bb_error_msg("cannot get option from packet, ignoring");
  126. continue;
  127. }
  128. /* Look for a static lease */
  129. static_lease_ip = getIpByMac(server_config.static_leases, &packet.chaddr);
  130. if (static_lease_ip) {
  131. bb_info_msg("Found static lease: %x", static_lease_ip);
  132. memcpy(&static_lease.chaddr, &packet.chaddr, 16);
  133. static_lease.yiaddr = static_lease_ip;
  134. static_lease.expires = 0;
  135. lease = &static_lease;
  136. } else {
  137. lease = find_lease_by_chaddr(packet.chaddr);
  138. }
  139. switch (state[0]) {
  140. case DHCPDISCOVER:
  141. DEBUG("Received DISCOVER");
  142. if (sendOffer(&packet) < 0) {
  143. bb_error_msg("send OFFER failed");
  144. }
  145. break;
  146. case DHCPREQUEST:
  147. DEBUG("received REQUEST");
  148. requested = get_option(&packet, DHCP_REQUESTED_IP);
  149. server_id = get_option(&packet, DHCP_SERVER_ID);
  150. if (requested) memcpy(&requested_align, requested, 4);
  151. if (server_id) memcpy(&server_id_align, server_id, 4);
  152. if (lease) {
  153. if (server_id) {
  154. /* SELECTING State */
  155. DEBUG("server_id = %08x", ntohl(server_id_align));
  156. if (server_id_align == server_config.server && requested
  157. && requested_align == lease->yiaddr
  158. ) {
  159. sendACK(&packet, lease->yiaddr);
  160. }
  161. } else if (requested) {
  162. /* INIT-REBOOT State */
  163. if (lease->yiaddr == requested_align)
  164. sendACK(&packet, lease->yiaddr);
  165. else
  166. sendNAK(&packet);
  167. } else if (lease->yiaddr == packet.ciaddr) {
  168. /* RENEWING or REBINDING State */
  169. sendACK(&packet, lease->yiaddr);
  170. } else {
  171. /* don't know what to do!!!! */
  172. sendNAK(&packet);
  173. }
  174. /* what to do if we have no record of the client */
  175. } else if (server_id) {
  176. /* SELECTING State */
  177. } else if (requested) {
  178. /* INIT-REBOOT State */
  179. lease = find_lease_by_yiaddr(requested_align);
  180. if (lease) {
  181. if (lease_expired(lease)) {
  182. /* probably best if we drop this lease */
  183. memset(lease->chaddr, 0, 16);
  184. /* make some contention for this address */
  185. } else
  186. sendNAK(&packet);
  187. } else {
  188. uint32_t r = ntohl(requested_align);
  189. if (r < server_config.start_ip
  190. || r > server_config.end_ip
  191. ) {
  192. sendNAK(&packet);
  193. }
  194. /* else remain silent */
  195. }
  196. } else {
  197. /* RENEWING or REBINDING State */
  198. }
  199. break;
  200. case DHCPDECLINE:
  201. DEBUG("Received DECLINE");
  202. if (lease) {
  203. memset(lease->chaddr, 0, 16);
  204. lease->expires = time(0) + server_config.decline_time;
  205. }
  206. break;
  207. case DHCPRELEASE:
  208. DEBUG("Received RELEASE");
  209. if (lease)
  210. lease->expires = time(0);
  211. break;
  212. case DHCPINFORM:
  213. DEBUG("Received INFORM");
  214. send_inform(&packet);
  215. break;
  216. default:
  217. bb_info_msg("Unsupported DHCP message (%02x) - ignoring", state[0]);
  218. }
  219. }
  220. ret0:
  221. retval = 0;
  222. ret:
  223. /*if (server_config.pidfile) - server_config.pidfile is never NULL */
  224. remove_pidfile(server_config.pidfile);
  225. return retval;
  226. }