blob: 90d8f0d1731965020a0b06b7fa3a09080f7cb69d [file] [log] [blame]
"Robert P. J. Day"63fc1a92006-07-02 19:47:05 +00001/* vi: set sw=4 ts=4: */
Mike Frysinger7031f622006-05-08 03:20:50 +00002/* 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 *
Rob Landley3f785612006-05-28 01:06:36 +000010 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
Mike Frysinger7031f622006-05-08 03:20:50 +000011 */
12
Mike Frysinger7031f622006-05-08 03:20:50 +000013#include "common.h"
Denis Vlasenko5a3395b2006-11-18 19:51:32 +000014#include "dhcpd.h"
15#include "options.h"
Mike Frysinger7031f622006-05-08 03:20:50 +000016
17
18/* globals */
19struct dhcpOfferedAddr *leases;
20struct server_config_t server_config;
21
22
Denis Vlasenko06af2162007-02-03 17:28:39 +000023int udhcpd_main(int argc, char *argv[]);
Mike Frysinger7031f622006-05-08 03:20:50 +000024int udhcpd_main(int argc, char *argv[])
Mike Frysinger7031f622006-05-08 03:20:50 +000025{
26 fd_set rfds;
27 struct timeval tv;
Rob Landley9ffd4232006-05-21 18:30:35 +000028 int server_socket = -1, bytes, retval, max_sock;
Mike Frysinger7031f622006-05-08 03:20:50 +000029 struct dhcpMessage packet;
Rob Landley9ffd4232006-05-21 18:30:35 +000030 uint8_t *state, *server_id, *requested;
31 uint32_t server_id_align, requested_align, static_lease_ip;
32 unsigned long timeout_end, num_ips;
Mike Frysinger7031f622006-05-08 03:20:50 +000033 struct option_set *option;
Rob Landley9ffd4232006-05-21 18:30:35 +000034 struct dhcpOfferedAddr *lease, static_lease;
Mike Frysinger7031f622006-05-08 03:20:50 +000035
Mike Frysinger7031f622006-05-08 03:20:50 +000036 read_config(argc < 2 ? DHCPD_CONF_FILE : argv[1]);
37
38 /* Start the log, sanitize fd's, and write a pid file */
Denis Vlasenko239369b2006-09-07 17:05:44 +000039 udhcp_start_log_and_pid(server_config.pidfile);
Mike Frysinger7031f622006-05-08 03:20:50 +000040
41 if ((option = find_option(server_config.options, DHCP_LEASE_TIME))) {
42 memcpy(&server_config.lease, option->data + 2, 4);
43 server_config.lease = ntohl(server_config.lease);
44 }
45 else server_config.lease = LEASE_TIME;
46
47 /* Sanity check */
48 num_ips = ntohl(server_config.end) - ntohl(server_config.start) + 1;
49 if (server_config.max_leases > num_ips) {
Denis Vlasenko3538b9a2006-09-06 18:36:50 +000050 bb_error_msg("max_leases value (%lu) not sane, "
Mike Frysinger7031f622006-05-08 03:20:50 +000051 "setting to %lu instead",
52 server_config.max_leases, num_ips);
53 server_config.max_leases = num_ips;
54 }
55
Rob Landley9ffd4232006-05-21 18:30:35 +000056 leases = xzalloc(server_config.max_leases * sizeof(struct dhcpOfferedAddr));
Mike Frysinger7031f622006-05-08 03:20:50 +000057 read_leases(server_config.lease_file);
58
59 if (read_interface(server_config.interface, &server_config.ifindex,
60 &server_config.server, server_config.arp) < 0)
61 return 1;
62
Rob Landley3f785612006-05-28 01:06:36 +000063 if (!ENABLE_FEATURE_UDHCP_DEBUG)
64 udhcp_background(server_config.pidfile); /* hold lock during fork. */
Mike Frysinger7031f622006-05-08 03:20:50 +000065
66 /* Setup the signal pipe */
67 udhcp_sp_setup();
68
69 timeout_end = time(0) + server_config.auto_time;
Denis Vlasenko5a3395b2006-11-18 19:51:32 +000070 while (1) { /* loop until universe collapses */
Mike Frysinger7031f622006-05-08 03:20:50 +000071
Denis Vlasenkoe2d3ded2006-11-27 23:43:28 +000072 if (server_socket < 0) {
73 server_socket = listen_socket(INADDR_ANY, SERVER_PORT, server_config.interface);
Denis Vlasenkoe2d3ded2006-11-27 23:43:28 +000074 }
Mike Frysinger7031f622006-05-08 03:20:50 +000075
76 max_sock = udhcp_sp_fd_set(&rfds, server_socket);
77 if (server_config.auto_time) {
78 tv.tv_sec = timeout_end - time(0);
79 tv.tv_usec = 0;
80 }
81 if (!server_config.auto_time || tv.tv_sec > 0) {
82 retval = select(max_sock + 1, &rfds, NULL, NULL,
83 server_config.auto_time ? &tv : NULL);
84 } else retval = 0; /* If we already timed out, fall through */
85
86 if (retval == 0) {
87 write_leases();
88 timeout_end = time(0) + server_config.auto_time;
89 continue;
90 } else if (retval < 0 && errno != EINTR) {
Denis Vlasenko3538b9a2006-09-06 18:36:50 +000091 DEBUG("error on select");
Mike Frysinger7031f622006-05-08 03:20:50 +000092 continue;
93 }
94
95 switch (udhcp_sp_read(&rfds)) {
96 case SIGUSR1:
Denis Vlasenko3538b9a2006-09-06 18:36:50 +000097 bb_info_msg("Received a SIGUSR1");
Mike Frysinger7031f622006-05-08 03:20:50 +000098 write_leases();
99 /* why not just reset the timeout, eh */
100 timeout_end = time(0) + server_config.auto_time;
101 continue;
102 case SIGTERM:
Denis Vlasenko3538b9a2006-09-06 18:36:50 +0000103 bb_info_msg("Received a SIGTERM");
Mike Frysinger7031f622006-05-08 03:20:50 +0000104 return 0;
105 case 0: break; /* no signal */
106 default: continue; /* signal or error (probably EINTR) */
107 }
108
Rob Landley3f785612006-05-28 01:06:36 +0000109 if ((bytes = udhcp_get_packet(&packet, server_socket)) < 0) { /* this waits for a packet - idle */
Mike Frysinger7031f622006-05-08 03:20:50 +0000110 if (bytes == -1 && errno != EINTR) {
Denis Vlasenko3538b9a2006-09-06 18:36:50 +0000111 DEBUG("error on read, %s, reopening socket", strerror(errno));
Mike Frysinger7031f622006-05-08 03:20:50 +0000112 close(server_socket);
113 server_socket = -1;
114 }
115 continue;
116 }
117
118 if ((state = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) {
Denis Vlasenkoa9595882006-09-29 21:30:43 +0000119 bb_error_msg("cannot get option from packet, ignoring");
Mike Frysinger7031f622006-05-08 03:20:50 +0000120 continue;
121 }
122
123 /* Look for a static lease */
124 static_lease_ip = getIpByMac(server_config.static_leases, &packet.chaddr);
125
Denis Vlasenko5a3395b2006-11-18 19:51:32 +0000126 if (static_lease_ip) {
Denis Vlasenko3538b9a2006-09-06 18:36:50 +0000127 bb_info_msg("Found static lease: %x", static_lease_ip);
Mike Frysinger7031f622006-05-08 03:20:50 +0000128
129 memcpy(&static_lease.chaddr, &packet.chaddr, 16);
130 static_lease.yiaddr = static_lease_ip;
131 static_lease.expires = 0;
132
133 lease = &static_lease;
134
Denis Vlasenko5a3395b2006-11-18 19:51:32 +0000135 } else {
136 lease = find_lease_by_chaddr(packet.chaddr);
Mike Frysinger7031f622006-05-08 03:20:50 +0000137 }
138
139 switch (state[0]) {
140 case DHCPDISCOVER:
Denis Vlasenko3538b9a2006-09-06 18:36:50 +0000141 DEBUG("Received DISCOVER");
Mike Frysinger7031f622006-05-08 03:20:50 +0000142
143 if (sendOffer(&packet) < 0) {
Denis Vlasenkoa9595882006-09-29 21:30:43 +0000144 bb_error_msg("send OFFER failed");
Mike Frysinger7031f622006-05-08 03:20:50 +0000145 }
146 break;
147 case DHCPREQUEST:
Denis Vlasenko3538b9a2006-09-06 18:36:50 +0000148 DEBUG("received REQUEST");
Mike Frysinger7031f622006-05-08 03:20:50 +0000149
150 requested = get_option(&packet, DHCP_REQUESTED_IP);
151 server_id = get_option(&packet, DHCP_SERVER_ID);
152
153 if (requested) memcpy(&requested_align, requested, 4);
154 if (server_id) memcpy(&server_id_align, server_id, 4);
155
156 if (lease) {
157 if (server_id) {
158 /* SELECTING State */
Denis Vlasenko3538b9a2006-09-06 18:36:50 +0000159 DEBUG("server_id = %08x", ntohl(server_id_align));
Mike Frysinger7031f622006-05-08 03:20:50 +0000160 if (server_id_align == server_config.server && requested &&
161 requested_align == lease->yiaddr) {
162 sendACK(&packet, lease->yiaddr);
163 }
164 } else {
165 if (requested) {
166 /* INIT-REBOOT State */
167 if (lease->yiaddr == requested_align)
168 sendACK(&packet, lease->yiaddr);
169 else sendNAK(&packet);
170 } else {
171 /* RENEWING or REBINDING State */
172 if (lease->yiaddr == packet.ciaddr)
173 sendACK(&packet, lease->yiaddr);
174 else {
175 /* don't know what to do!!!! */
176 sendNAK(&packet);
177 }
178 }
179 }
180
181 /* what to do if we have no record of the client */
182 } else if (server_id) {
183 /* SELECTING State */
184
185 } else if (requested) {
186 /* INIT-REBOOT State */
187 if ((lease = find_lease_by_yiaddr(requested_align))) {
188 if (lease_expired(lease)) {
189 /* probably best if we drop this lease */
190 memset(lease->chaddr, 0, 16);
191 /* make some contention for this address */
192 } else sendNAK(&packet);
193 } else if (requested_align < server_config.start ||
194 requested_align > server_config.end) {
195 sendNAK(&packet);
196 } /* else remain silent */
197
198 } else {
199 /* RENEWING or REBINDING State */
200 }
201 break;
202 case DHCPDECLINE:
Denis Vlasenko3538b9a2006-09-06 18:36:50 +0000203 DEBUG("Received DECLINE");
Mike Frysinger7031f622006-05-08 03:20:50 +0000204 if (lease) {
205 memset(lease->chaddr, 0, 16);
206 lease->expires = time(0) + server_config.decline_time;
207 }
208 break;
209 case DHCPRELEASE:
Denis Vlasenko3538b9a2006-09-06 18:36:50 +0000210 DEBUG("Received RELEASE");
Mike Frysinger7031f622006-05-08 03:20:50 +0000211 if (lease) lease->expires = time(0);
212 break;
213 case DHCPINFORM:
Denis Vlasenko3538b9a2006-09-06 18:36:50 +0000214 DEBUG("Received INFORM");
Mike Frysinger7031f622006-05-08 03:20:50 +0000215 send_inform(&packet);
216 break;
217 default:
Denis Vlasenko3538b9a2006-09-06 18:36:50 +0000218 bb_info_msg("Unsupported DHCP message (%02x) - ignoring", state[0]);
Mike Frysinger7031f622006-05-08 03:20:50 +0000219 }
220 }
221
222 return 0;
223}