4 * Copyright (C) 1999 Matthew Ramsay <matthewr@moreton.com.au>
5 * Chris Trew <ctrew@moreton.com.au>
7 * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include <arpa/inet.h>
31 #include <netinet/in.h>
33 #include <sys/types.h>
34 #include <sys/socket.h>
38 #include <sys/ioctl.h>
50 #include "serverpacket.h"
55 struct dhcpOfferedAddr *leases;
56 struct server_config_t server_config;
57 static int signal_pipe[2];
59 /* Exit and cleanup */
60 static void exit_server(int retval)
62 pidfile_delete(server_config.pidfile);
69 static void signal_handler(int sig)
71 if (send(signal_pipe[1], &sig, sizeof(sig), MSG_DONTWAIT) < 0) {
72 LOG(LOG_ERR, "Could not send signal: %s",
78 #ifdef COMBINED_BINARY
79 int udhcpd_main(int argc, char *argv[])
81 int main(int argc, char *argv[])
86 int server_socket = -1;
88 struct dhcpMessage packet;
90 unsigned char *server_id, *requested;
91 u_int32_t server_id_align, requested_align;
92 unsigned long timeout_end;
93 struct option_set *option;
94 struct dhcpOfferedAddr *lease;
98 unsigned long num_ips;
101 LOG(LOG_INFO, "udhcp server (v%s) started", VERSION);
103 memset(&server_config, 0, sizeof(struct server_config_t));
106 read_config(DHCPD_CONF_FILE);
107 else read_config(argv[1]);
109 pid_fd = pidfile_acquire(server_config.pidfile);
110 pidfile_write_release(pid_fd);
112 if ((option = find_option(server_config.options, DHCP_LEASE_TIME))) {
113 memcpy(&server_config.lease, option->data + 2, 4);
114 server_config.lease = ntohl(server_config.lease);
116 else server_config.lease = LEASE_TIME;
119 num_ips = ntohl(server_config.end) - ntohl(server_config.start);
120 if (server_config.max_leases > num_ips) {
121 LOG(LOG_ERR, "max_leases value (%lu) not sane, setting to %lu instead",
122 server_config.max_leases, num_ips);
123 server_config.max_leases = num_ips;
126 leases = xmalloc(sizeof(struct dhcpOfferedAddr) * server_config.max_leases);
127 memset(leases, 0, sizeof(struct dhcpOfferedAddr) * server_config.max_leases);
128 read_leases(server_config.lease_file);
130 if (read_interface(server_config.interface, &server_config.ifindex,
131 &server_config.server, server_config.arp) < 0)
135 pid_fd = pidfile_acquire(server_config.pidfile); /* hold lock during fork. */
136 if (daemon(0, 0) == -1) {
140 pidfile_write_release(pid_fd);
144 socketpair(AF_UNIX, SOCK_STREAM, 0, signal_pipe);
145 signal(SIGUSR1, signal_handler);
146 signal(SIGTERM, signal_handler);
148 timeout_end = time(0) + server_config.auto_time;
149 while(1) { /* loop until universe collapses */
151 if (server_socket < 0)
152 if ((server_socket = listen_socket(INADDR_ANY, SERVER_PORT, server_config.interface)) < 0) {
153 LOG(LOG_ERR, "FATAL: couldn't create server socket, %s", strerror(errno));
158 FD_SET(server_socket, &rfds);
159 FD_SET(signal_pipe[0], &rfds);
160 if (server_config.auto_time) {
161 tv.tv_sec = timeout_end - time(0);
164 if (!server_config.auto_time || tv.tv_sec > 0) {
165 max_sock = server_socket > signal_pipe[0] ? server_socket : signal_pipe[0];
166 retval = select(max_sock + 1, &rfds, NULL, NULL,
167 server_config.auto_time ? &tv : NULL);
168 } else retval = 0; /* If we already timed out, fall through */
172 timeout_end = time(0) + server_config.auto_time;
174 } else if (retval < 0 && errno != EINTR) {
175 DEBUG(LOG_INFO, "error on select");
179 if (FD_ISSET(signal_pipe[0], &rfds)) {
180 if (read(signal_pipe[0], &sig, sizeof(sig)) < 0)
181 continue; /* probably just EINTR */
184 LOG(LOG_INFO, "Received a SIGUSR1");
186 /* why not just reset the timeout, eh */
187 timeout_end = time(0) + server_config.auto_time;
190 LOG(LOG_INFO, "Received a SIGTERM");
195 if ((bytes = get_packet(&packet, server_socket)) < 0) { /* this waits for a packet - idle */
196 if (bytes == -1 && errno != EINTR) {
197 DEBUG(LOG_INFO, "error on read, %s, reopening socket", strerror(errno));
198 close(server_socket);
204 if ((state = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) {
205 DEBUG(LOG_ERR, "couldn't get option from packet, ignoring");
209 /* ADDME: look for a static lease */
210 lease = find_lease_by_chaddr(packet.chaddr);
213 DEBUG(LOG_INFO,"received DISCOVER");
215 if (sendOffer(&packet) < 0) {
216 LOG(LOG_ERR, "send OFFER failed");
220 DEBUG(LOG_INFO, "received REQUEST");
222 requested = get_option(&packet, DHCP_REQUESTED_IP);
223 server_id = get_option(&packet, DHCP_SERVER_ID);
225 if (requested) memcpy(&requested_align, requested, 4);
226 if (server_id) memcpy(&server_id_align, server_id, 4);
228 if (lease) { /*ADDME: or static lease */
230 /* SELECTING State */
231 DEBUG(LOG_INFO, "server_id = %08x", ntohl(server_id_align));
232 if (server_id_align == server_config.server && requested &&
233 requested_align == lease->yiaddr) {
234 sendACK(&packet, lease->yiaddr);
238 /* INIT-REBOOT State */
239 if (lease->yiaddr == requested_align)
240 sendACK(&packet, lease->yiaddr);
241 else sendNAK(&packet);
243 /* RENEWING or REBINDING State */
244 if (lease->yiaddr == packet.ciaddr)
245 sendACK(&packet, lease->yiaddr);
247 /* don't know what to do!!!! */
253 /* what to do if we have no record of the client */
254 } else if (server_id) {
255 /* SELECTING State */
257 } else if (requested) {
258 /* INIT-REBOOT State */
259 if ((lease = find_lease_by_yiaddr(requested_align))) {
260 if (lease_expired(lease)) {
261 /* probably best if we drop this lease */
262 memset(lease->chaddr, 0, 16);
263 /* make some contention for this address */
264 } else sendNAK(&packet);
265 } else if (requested_align < server_config.start ||
266 requested_align > server_config.end) {
268 } /* else remain silent */
271 /* RENEWING or REBINDING State */
275 DEBUG(LOG_INFO,"received DECLINE");
277 memset(lease->chaddr, 0, 16);
278 lease->expires = time(0) + server_config.decline_time;
282 DEBUG(LOG_INFO,"received RELEASE");
283 if (lease) lease->expires = time(0);
286 DEBUG(LOG_INFO,"received INFORM");
287 send_inform(&packet);
290 LOG(LOG_WARNING, "unsupported DHCP message (%02x) -- ignoring", state[0]);