dhcpd.c 7.0 KB

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