blob: 56ddaa94273fa100b34d854bc83461796dfabfb9 [file] [log] [blame]
Russ Dill61fb4892002-10-14 21:41:28 +00001/* dhcpd.c
2 *
3 * udhcp Server
4 * Copyright (C) 1999 Matthew Ramsay <matthewr@moreton.com.au>
5 * Chris Trew <ctrew@moreton.com.au>
6 *
7 * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
8 *
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.
13 *
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.
18 *
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.
22 */
23
24#include <fcntl.h>
25#include <string.h>
26#include <stdlib.h>
27#include <sys/wait.h>
28#include <sys/stat.h>
29#include <arpa/inet.h>
30#include <netdb.h>
31#include <netinet/in.h>
32#include <stdio.h>
33#include <sys/types.h>
34#include <sys/socket.h>
35#include <unistd.h>
36#include <signal.h>
37#include <errno.h>
38#include <sys/ioctl.h>
39#include <time.h>
40#include <sys/time.h>
41
42#include "debug.h"
43#include "dhcpd.h"
44#include "arpping.h"
45#include "socket.h"
46#include "options.h"
47#include "files.h"
48#include "leases.h"
49#include "packet.h"
50#include "serverpacket.h"
51#include "pidfile.h"
52
53
54/* globals */
55struct dhcpOfferedAddr *leases;
56struct server_config_t server_config;
57static int signal_pipe[2];
58
59/* Exit and cleanup */
60static void exit_server(int retval)
61{
62 pidfile_delete(server_config.pidfile);
63 CLOSE_LOG();
64 exit(retval);
65}
66
67
68/* Signal handler */
69static void signal_handler(int sig)
70{
71 if (send(signal_pipe[1], &sig, sizeof(sig), MSG_DONTWAIT) < 0) {
72 LOG(LOG_ERR, "Could not send signal: %s",
73 strerror(errno));
74 }
75}
76
77
78#ifdef COMBINED_BINARY
79int udhcpd_main(int argc, char *argv[])
80#else
81int main(int argc, char *argv[])
82#endif
83{
84 fd_set rfds;
85 struct timeval tv;
86 int server_socket = -1;
87 int bytes, retval;
88 struct dhcpMessage packet;
89 unsigned char *state;
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;
95 int pid_fd;
96 int max_sock;
97 int sig;
Russ Dill9f4395c2002-12-11 21:40:46 +000098 unsigned long num_ips;
Russ Dill61fb4892002-10-14 21:41:28 +000099
100 OPEN_LOG("udhcpd");
101 LOG(LOG_INFO, "udhcp server (v%s) started", VERSION);
102
103 memset(&server_config, 0, sizeof(struct server_config_t));
104
105 if (argc < 2)
106 read_config(DHCPD_CONF_FILE);
107 else read_config(argv[1]);
108
109 pid_fd = pidfile_acquire(server_config.pidfile);
110 pidfile_write_release(pid_fd);
111
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);
115 }
116 else server_config.lease = LEASE_TIME;
117
Russ Dill9f4395c2002-12-11 21:40:46 +0000118 /* Sanity check */
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;
124 }
125
126 leases = xmalloc(sizeof(struct dhcpOfferedAddr) * server_config.max_leases);
Russ Dill61fb4892002-10-14 21:41:28 +0000127 memset(leases, 0, sizeof(struct dhcpOfferedAddr) * server_config.max_leases);
128 read_leases(server_config.lease_file);
129
130 if (read_interface(server_config.interface, &server_config.ifindex,
131 &server_config.server, server_config.arp) < 0)
132 exit_server(1);
133
134#ifndef DEBUGGING
135 pid_fd = pidfile_acquire(server_config.pidfile); /* hold lock during fork. */
136 if (daemon(0, 0) == -1) {
137 perror("fork");
138 exit_server(1);
139 }
140 pidfile_write_release(pid_fd);
141#endif
142
143
144 socketpair(AF_UNIX, SOCK_STREAM, 0, signal_pipe);
145 signal(SIGUSR1, signal_handler);
146 signal(SIGTERM, signal_handler);
147
148 timeout_end = time(0) + server_config.auto_time;
149 while(1) { /* loop until universe collapses */
150
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));
154 exit_server(0);
155 }
156
157 FD_ZERO(&rfds);
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);
162 tv.tv_usec = 0;
163 }
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 */
169
170 if (retval == 0) {
171 write_leases();
172 timeout_end = time(0) + server_config.auto_time;
173 continue;
174 } else if (retval < 0 && errno != EINTR) {
175 DEBUG(LOG_INFO, "error on select");
176 continue;
177 }
178
179 if (FD_ISSET(signal_pipe[0], &rfds)) {
180 if (read(signal_pipe[0], &sig, sizeof(sig)) < 0)
181 continue; /* probably just EINTR */
182 switch (sig) {
183 case SIGUSR1:
184 LOG(LOG_INFO, "Received a SIGUSR1");
185 write_leases();
186 /* why not just reset the timeout, eh */
187 timeout_end = time(0) + server_config.auto_time;
188 continue;
189 case SIGTERM:
190 LOG(LOG_INFO, "Received a SIGTERM");
191 exit_server(0);
192 }
193 }
194
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);
199 server_socket = -1;
200 }
201 continue;
202 }
203
204 if ((state = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) {
205 DEBUG(LOG_ERR, "couldn't get option from packet, ignoring");
206 continue;
207 }
208
209 /* ADDME: look for a static lease */
210 lease = find_lease_by_chaddr(packet.chaddr);
211 switch (state[0]) {
212 case DHCPDISCOVER:
213 DEBUG(LOG_INFO,"received DISCOVER");
214
215 if (sendOffer(&packet) < 0) {
216 LOG(LOG_ERR, "send OFFER failed");
217 }
218 break;
219 case DHCPREQUEST:
220 DEBUG(LOG_INFO, "received REQUEST");
221
222 requested = get_option(&packet, DHCP_REQUESTED_IP);
223 server_id = get_option(&packet, DHCP_SERVER_ID);
224
225 if (requested) memcpy(&requested_align, requested, 4);
226 if (server_id) memcpy(&server_id_align, server_id, 4);
227
228 if (lease) { /*ADDME: or static lease */
229 if (server_id) {
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);
235 }
236 } else {
237 if (requested) {
238 /* INIT-REBOOT State */
239 if (lease->yiaddr == requested_align)
240 sendACK(&packet, lease->yiaddr);
241 else sendNAK(&packet);
242 } else {
243 /* RENEWING or REBINDING State */
244 if (lease->yiaddr == packet.ciaddr)
245 sendACK(&packet, lease->yiaddr);
246 else {
247 /* don't know what to do!!!! */
248 sendNAK(&packet);
249 }
250 }
251 }
252
253 /* what to do if we have no record of the client */
254 } else if (server_id) {
255 /* SELECTING State */
256
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) {
267 sendNAK(&packet);
268 } /* else remain silent */
269
270 } else {
271 /* RENEWING or REBINDING State */
272 }
273 break;
274 case DHCPDECLINE:
275 DEBUG(LOG_INFO,"received DECLINE");
276 if (lease) {
277 memset(lease->chaddr, 0, 16);
278 lease->expires = time(0) + server_config.decline_time;
279 }
280 break;
281 case DHCPRELEASE:
282 DEBUG(LOG_INFO,"received RELEASE");
283 if (lease) lease->expires = time(0);
284 break;
285 case DHCPINFORM:
286 DEBUG(LOG_INFO,"received INFORM");
287 send_inform(&packet);
288 break;
289 default:
290 LOG(LOG_WARNING, "unsupported DHCP message (%02x) -- ignoring", state[0]);
291 }
292 }
293
294 return 0;
295}
296