blob: d7887d9146cf76709f7bfacf32af7a3ae9a7226a [file] [log] [blame]
"Robert P. J. Day"63fc1a92006-07-02 19:47:05 +00001/* vi: set sw=4 ts=4: */
Denys Vlasenko8a7c1662010-03-20 03:48:11 +01002/*
Mike Frysinger7031f622006-05-08 03:20:50 +00003 * 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 *
Denys Vlasenko8a7c1662010-03-20 03:48:11 +01009 * 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.
Mike Frysinger7031f622006-05-08 03:20:50 +000022 */
23
Denis Vlasenkoaf1c8432007-03-26 13:22:35 +000024#include <syslog.h>
Mike Frysinger7031f622006-05-08 03:20:50 +000025#include "common.h"
Denis Vlasenkod55fe3e2008-02-04 13:12:16 +000026#include "dhcpc.h"
Denis Vlasenko5a3395b2006-11-18 19:51:32 +000027#include "dhcpd.h"
28#include "options.h"
Mike Frysinger7031f622006-05-08 03:20:50 +000029
30
Denys Vlasenko8a7c1662010-03-20 03:48:11 +010031/* send a packet to gateway_nip using the kernel ip stack */
32static int send_packet_to_relay(struct dhcp_packet *dhcp_pkt)
33{
34 log1("Forwarding packet to relay");
35
36 return udhcp_send_kernel_packet(dhcp_pkt,
37 server_config.server_nip, SERVER_PORT,
38 dhcp_pkt->gateway_nip, SERVER_PORT);
39}
40
41/* send a packet to a specific mac address and ip address by creating our own ip packet */
42static int send_packet_to_client(struct dhcp_packet *dhcp_pkt, int force_broadcast)
43{
44 const uint8_t *chaddr;
45 uint32_t ciaddr;
46
47 // Was:
48 //if (force_broadcast) { /* broadcast */ }
49 //else if (dhcp_pkt->ciaddr) { /* unicast to dhcp_pkt->ciaddr */ }
50 //else if (dhcp_pkt->flags & htons(BROADCAST_FLAG)) { /* broadcast */ }
51 //else { /* unicast to dhcp_pkt->yiaddr */ }
52 // But this is wrong: yiaddr is _our_ idea what client's IP is
53 // (for example, from lease file). Client may not know that,
54 // and may not have UDP socket listening on that IP!
55 // We should never unicast to dhcp_pkt->yiaddr!
56 // dhcp_pkt->ciaddr, OTOH, comes from client's request packet,
57 // and can be used.
58
59 if (force_broadcast
60 || (dhcp_pkt->flags & htons(BROADCAST_FLAG))
61 || !dhcp_pkt->ciaddr
62 ) {
63 log1("Broadcasting packet to client");
64 ciaddr = INADDR_BROADCAST;
65 chaddr = MAC_BCAST_ADDR;
66 } else {
67 log1("Unicasting packet to client ciaddr");
68 ciaddr = dhcp_pkt->ciaddr;
69 chaddr = dhcp_pkt->chaddr;
70 }
71
72 return udhcp_send_raw_packet(dhcp_pkt,
73 /*src*/ server_config.server_nip, SERVER_PORT,
74 /*dst*/ ciaddr, CLIENT_PORT, chaddr,
75 server_config.ifindex);
76}
77
78/* send a dhcp packet, if force broadcast is set, the packet will be broadcast to the client */
79static int send_packet(struct dhcp_packet *dhcp_pkt, int force_broadcast)
80{
81 if (dhcp_pkt->gateway_nip)
82 return send_packet_to_relay(dhcp_pkt);
83 return send_packet_to_client(dhcp_pkt, force_broadcast);
84}
85
86static void init_packet(struct dhcp_packet *packet, struct dhcp_packet *oldpacket, char type)
87{
88 udhcp_init_header(packet, type);
89 packet->xid = oldpacket->xid;
90 memcpy(packet->chaddr, oldpacket->chaddr, sizeof(oldpacket->chaddr));
91 packet->flags = oldpacket->flags;
92 packet->gateway_nip = oldpacket->gateway_nip;
93 packet->ciaddr = oldpacket->ciaddr;
94 add_simple_option(packet->options, DHCP_SERVER_ID, server_config.server_nip);
95}
96
97/* add in the bootp options */
98static void add_bootp_options(struct dhcp_packet *packet)
99{
100 packet->siaddr_nip = server_config.siaddr_nip;
101 if (server_config.sname)
102 strncpy((char*)packet->sname, server_config.sname, sizeof(packet->sname) - 1);
103 if (server_config.boot_file)
104 strncpy((char*)packet->file, server_config.boot_file, sizeof(packet->file) - 1);
105}
106
107static uint32_t select_lease_time(struct dhcp_packet *packet)
108{
109 uint32_t lease_time_sec = server_config.max_lease_sec;
110 uint8_t *lease_time_opt = get_option(packet, DHCP_LEASE_TIME);
111 if (lease_time_opt) {
112 move_from_unaligned32(lease_time_sec, lease_time_opt);
113 lease_time_sec = ntohl(lease_time_sec);
114 if (lease_time_sec > server_config.max_lease_sec)
115 lease_time_sec = server_config.max_lease_sec;
116 if (lease_time_sec < server_config.min_lease_sec)
117 lease_time_sec = server_config.min_lease_sec;
118 }
119 return lease_time_sec;
120}
121
122/* send a DHCP OFFER to a DHCP DISCOVER */
Denys Vlasenkoa9539872010-03-20 03:49:27 +0100123static int send_offer(struct dhcp_packet *oldpacket, uint32_t static_lease_nip, struct dyn_lease *lease)
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100124{
125 struct dhcp_packet packet;
126 uint32_t req_nip;
127 uint32_t lease_time_sec = server_config.max_lease_sec;
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100128 uint8_t *req_ip_opt;
129 const char *p_host_name;
130 struct option_set *curr;
131 struct in_addr addr;
132
133 init_packet(&packet, oldpacket, DHCPOFFER);
134
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100135 /* ADDME: if static, short circuit */
Denys Vlasenkoa9539872010-03-20 03:49:27 +0100136 if (!static_lease_nip) {
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100137 /* The client is in our lease/offered table */
138 if (lease) {
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100139 packet.yiaddr = lease->lease_nip;
140 }
141 /* Or the client has requested an IP */
142 else if ((req_ip_opt = get_option(oldpacket, DHCP_REQUESTED_IP)) != NULL
143 /* (read IP) */
144 && (move_from_unaligned32(req_nip, req_ip_opt), 1)
145 /* and the IP is in the lease range */
146 && ntohl(req_nip) >= server_config.start_ip
147 && ntohl(req_nip) <= server_config.end_ip
148 /* and is not already taken/offered */
149 && (!(lease = find_lease_by_nip(req_nip))
150 /* or its taken, but expired */
151 || is_expired_lease(lease))
152 ) {
153 packet.yiaddr = req_nip;
154 }
155 /* Otherwise, find a free IP */
156 else {
157 packet.yiaddr = find_free_or_expired_nip(oldpacket->chaddr);
158 }
159
160 if (!packet.yiaddr) {
161 bb_error_msg("no IP addresses to give - OFFER abandoned");
162 return -1;
163 }
164 p_host_name = (const char*) get_option(oldpacket, DHCP_HOST_NAME);
165 if (add_lease(packet.chaddr, packet.yiaddr,
166 server_config.offer_time,
167 p_host_name,
168 p_host_name ? (unsigned char)p_host_name[OPT_LEN - OPT_DATA] : 0
169 ) == 0
170 ) {
171 bb_error_msg("lease pool is full - OFFER abandoned");
172 return -1;
173 }
174 lease_time_sec = select_lease_time(oldpacket);
175 } else {
176 /* It is a static lease... use it */
Denys Vlasenkoa9539872010-03-20 03:49:27 +0100177 packet.yiaddr = static_lease_nip;
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100178 }
179
180 add_simple_option(packet.options, DHCP_LEASE_TIME, htonl(lease_time_sec));
181
182 curr = server_config.options;
183 while (curr) {
184 if (curr->data[OPT_CODE] != DHCP_LEASE_TIME)
185 add_option_string(packet.options, curr->data);
186 curr = curr->next;
187 }
188
189 add_bootp_options(&packet);
190
191 addr.s_addr = packet.yiaddr;
192 bb_info_msg("Sending OFFER of %s", inet_ntoa(addr));
193 return send_packet(&packet, 0);
194}
195
196static int send_NAK(struct dhcp_packet *oldpacket)
197{
198 struct dhcp_packet packet;
199
200 init_packet(&packet, oldpacket, DHCPNAK);
201
202 log1("Sending NAK");
203 return send_packet(&packet, 1);
204}
205
206static int send_ACK(struct dhcp_packet *oldpacket, uint32_t yiaddr)
207{
208 struct dhcp_packet packet;
209 struct option_set *curr;
210 uint32_t lease_time_sec;
211 struct in_addr addr;
212 const char *p_host_name;
213
214 init_packet(&packet, oldpacket, DHCPACK);
215 packet.yiaddr = yiaddr;
216
217 lease_time_sec = select_lease_time(oldpacket);
218
219 add_simple_option(packet.options, DHCP_LEASE_TIME, htonl(lease_time_sec));
220
221 curr = server_config.options;
222 while (curr) {
223 if (curr->data[OPT_CODE] != DHCP_LEASE_TIME)
224 add_option_string(packet.options, curr->data);
225 curr = curr->next;
226 }
227
228 add_bootp_options(&packet);
229
230 addr.s_addr = packet.yiaddr;
231 bb_info_msg("Sending ACK to %s", inet_ntoa(addr));
232
233 if (send_packet(&packet, 0) < 0)
234 return -1;
235
236 p_host_name = (const char*) get_option(oldpacket, DHCP_HOST_NAME);
237 add_lease(packet.chaddr, packet.yiaddr,
238 lease_time_sec,
239 p_host_name,
240 p_host_name ? (unsigned char)p_host_name[OPT_LEN - OPT_DATA] : 0
241 );
242 if (ENABLE_FEATURE_UDHCPD_WRITE_LEASES_EARLY) {
243 /* rewrite the file with leases at every new acceptance */
244 write_leases();
245 }
246
247 return 0;
248}
249
250static int send_inform(struct dhcp_packet *oldpacket)
251{
252 struct dhcp_packet packet;
253 struct option_set *curr;
254
255 init_packet(&packet, oldpacket, DHCPACK);
256
257 curr = server_config.options;
258 while (curr) {
259 if (curr->data[OPT_CODE] != DHCP_LEASE_TIME)
260 add_option_string(packet.options, curr->data);
261 curr = curr->next;
262 }
263
264 add_bootp_options(&packet);
265
266 return send_packet(&packet, 0);
267}
268
269
Mike Frysinger7031f622006-05-08 03:20:50 +0000270/* globals */
Denys Vlasenko6947d2c2009-06-17 13:24:03 +0200271struct dyn_lease *g_leases;
Denis Vlasenkodeabacd2007-09-30 17:55:43 +0000272/* struct server_config_t server_config is in bb_common_bufsiz1 */
Mike Frysinger7031f622006-05-08 03:20:50 +0000273
274
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000275int udhcpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000276int udhcpd_main(int argc UNUSED_PARAM, char **argv)
Mike Frysinger7031f622006-05-08 03:20:50 +0000277{
278 fd_set rfds;
Denis Vlasenko0416e3d2009-01-01 17:52:09 +0000279 int server_socket = -1, retval, max_sock;
Denys Vlasenko31af3d52009-06-17 11:57:09 +0200280 struct dhcp_packet packet;
Denys Vlasenko6947d2c2009-06-17 13:24:03 +0200281 uint8_t *state;
Denys Vlasenkoa9539872010-03-20 03:49:27 +0100282 uint32_t static_lease_nip;
Denis Vlasenko42b3dea2007-07-03 15:47:50 +0000283 unsigned timeout_end;
284 unsigned num_ips;
Denis Vlasenko3d17d2b2007-08-14 16:45:29 +0000285 unsigned opt;
Mike Frysinger7031f622006-05-08 03:20:50 +0000286 struct option_set *option;
Denys Vlasenko6947d2c2009-06-17 13:24:03 +0200287 struct dyn_lease *lease, fake_lease;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000288 IF_FEATURE_UDHCP_PORT(char *str_P;)
Mike Frysinger7031f622006-05-08 03:20:50 +0000289
Denis Vlasenkod55fe3e2008-02-04 13:12:16 +0000290#if ENABLE_FEATURE_UDHCP_PORT
291 SERVER_PORT = 67;
292 CLIENT_PORT = 68;
293#endif
294
Denys Vlasenkoac906fa2009-06-17 11:54:52 +0200295#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
296 opt_complementary = "vv";
297#endif
298 opt = getopt32(argv, "fSv"
299 IF_FEATURE_UDHCP_PORT("P:", &str_P)
300#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
301 , &dhcp_verbose
302#endif
303 );
Denis Vlasenko9f7b92a2007-08-15 20:03:36 +0000304 argv += optind;
Denis Vlasenko3d17d2b2007-08-14 16:45:29 +0000305 if (!(opt & 1)) { /* no -f */
Denis Vlasenkoc82b5102007-07-01 17:05:57 +0000306 bb_daemonize_or_rexec(0, argv);
Denis Vlasenkoa19e6492009-03-11 14:40:00 +0000307 logmode = LOGMODE_NONE;
Denis Vlasenkoaf1c8432007-03-26 13:22:35 +0000308 }
Denis Vlasenko3d17d2b2007-08-14 16:45:29 +0000309 if (opt & 2) { /* -S */
Denis Vlasenko5e4fda02009-03-08 23:46:48 +0000310 openlog(applet_name, LOG_PID, LOG_DAEMON);
Denis Vlasenkoaf1c8432007-03-26 13:22:35 +0000311 logmode |= LOGMODE_SYSLOG;
312 }
Denis Vlasenkod55fe3e2008-02-04 13:12:16 +0000313#if ENABLE_FEATURE_UDHCP_PORT
314 if (opt & 4) { /* -P */
315 SERVER_PORT = xatou16(str_P);
316 CLIENT_PORT = SERVER_PORT + 1;
317 }
318#endif
Denis Vlasenkoaf1c8432007-03-26 13:22:35 +0000319 /* Would rather not do read_config before daemonization -
320 * otherwise NOMMU machines will parse config twice */
Denis Vlasenko9f7b92a2007-08-15 20:03:36 +0000321 read_config(argv[0] ? argv[0] : DHCPD_CONF_FILE);
Mike Frysinger7031f622006-05-08 03:20:50 +0000322
Denis Vlasenko80edead2007-08-02 22:31:05 +0000323 /* Make sure fd 0,1,2 are open */
324 bb_sanitize_stdio();
325 /* Equivalent of doing a fflush after every \n */
326 setlinebuf(stdout);
327
328 /* Create pidfile */
329 write_pidfile(server_config.pidfile);
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100330 /* if (!..) bb_perror_msg("can't create pidfile %s", pidfile); */
Denis Vlasenko80edead2007-08-02 22:31:05 +0000331
Denis Vlasenkodef88982007-10-07 17:06:01 +0000332 bb_info_msg("%s (v"BB_VER") started", applet_name);
Mike Frysinger7031f622006-05-08 03:20:50 +0000333
Denis Vlasenkoaf1c8432007-03-26 13:22:35 +0000334 option = find_option(server_config.options, DHCP_LEASE_TIME);
Denys Vlasenko6947d2c2009-06-17 13:24:03 +0200335 server_config.max_lease_sec = LEASE_TIME;
Denis Vlasenkoaf1c8432007-03-26 13:22:35 +0000336 if (option) {
Denys Vlasenko6947d2c2009-06-17 13:24:03 +0200337 move_from_unaligned32(server_config.max_lease_sec, option->data + OPT_DATA);
338 server_config.max_lease_sec = ntohl(server_config.max_lease_sec);
Denis Vlasenko6e6d3312007-05-03 23:39:35 +0000339 }
Mike Frysinger7031f622006-05-08 03:20:50 +0000340
341 /* Sanity check */
Denis Vlasenkoc82b5102007-07-01 17:05:57 +0000342 num_ips = server_config.end_ip - server_config.start_ip + 1;
Mike Frysinger7031f622006-05-08 03:20:50 +0000343 if (server_config.max_leases > num_ips) {
Denis Vlasenko42b3dea2007-07-03 15:47:50 +0000344 bb_error_msg("max_leases=%u is too big, setting to %u",
345 (unsigned)server_config.max_leases, num_ips);
Mike Frysinger7031f622006-05-08 03:20:50 +0000346 server_config.max_leases = num_ips;
347 }
348
Denys Vlasenko6947d2c2009-06-17 13:24:03 +0200349 g_leases = xzalloc(server_config.max_leases * sizeof(g_leases[0]));
Mike Frysinger7031f622006-05-08 03:20:50 +0000350 read_leases(server_config.lease_file);
351
Denys Vlasenko26918dd2009-06-16 12:04:23 +0200352 if (udhcp_read_interface(server_config.interface,
353 &server_config.ifindex,
354 &server_config.server_nip,
355 server_config.server_mac)
356 ) {
Denis Vlasenko6e6d3312007-05-03 23:39:35 +0000357 retval = 1;
358 goto ret;
359 }
Mike Frysinger7031f622006-05-08 03:20:50 +0000360
Mike Frysinger7031f622006-05-08 03:20:50 +0000361 /* Setup the signal pipe */
362 udhcp_sp_setup();
363
Denis Vlasenko42b3dea2007-07-03 15:47:50 +0000364 timeout_end = monotonic_sec() + server_config.auto_time;
Denis Vlasenko5a3395b2006-11-18 19:51:32 +0000365 while (1) { /* loop until universe collapses */
Denis Vlasenko0416e3d2009-01-01 17:52:09 +0000366 int bytes;
367 struct timeval tv;
Mike Frysinger7031f622006-05-08 03:20:50 +0000368
Denis Vlasenkoe2d3ded2006-11-27 23:43:28 +0000369 if (server_socket < 0) {
Denis Vlasenkof1980f62008-09-26 09:34:59 +0000370 server_socket = udhcp_listen_socket(/*INADDR_ANY,*/ SERVER_PORT,
Denis Vlasenko6e6d3312007-05-03 23:39:35 +0000371 server_config.interface);
Denis Vlasenkoe2d3ded2006-11-27 23:43:28 +0000372 }
Mike Frysinger7031f622006-05-08 03:20:50 +0000373
374 max_sock = udhcp_sp_fd_set(&rfds, server_socket);
375 if (server_config.auto_time) {
Denis Vlasenko42b3dea2007-07-03 15:47:50 +0000376 tv.tv_sec = timeout_end - monotonic_sec();
Mike Frysinger7031f622006-05-08 03:20:50 +0000377 tv.tv_usec = 0;
378 }
Denis Vlasenko6e6d3312007-05-03 23:39:35 +0000379 retval = 0;
Mike Frysinger7031f622006-05-08 03:20:50 +0000380 if (!server_config.auto_time || tv.tv_sec > 0) {
381 retval = select(max_sock + 1, &rfds, NULL, NULL,
382 server_config.auto_time ? &tv : NULL);
Denis Vlasenko6e6d3312007-05-03 23:39:35 +0000383 }
Mike Frysinger7031f622006-05-08 03:20:50 +0000384 if (retval == 0) {
385 write_leases();
Denis Vlasenko42b3dea2007-07-03 15:47:50 +0000386 timeout_end = monotonic_sec() + server_config.auto_time;
Mike Frysinger7031f622006-05-08 03:20:50 +0000387 continue;
Denis Vlasenko6e6d3312007-05-03 23:39:35 +0000388 }
389 if (retval < 0 && errno != EINTR) {
Denys Vlasenkoac906fa2009-06-17 11:54:52 +0200390 log1("Error on select");
Mike Frysinger7031f622006-05-08 03:20:50 +0000391 continue;
392 }
393
394 switch (udhcp_sp_read(&rfds)) {
395 case SIGUSR1:
Denis Vlasenko3538b9a2006-09-06 18:36:50 +0000396 bb_info_msg("Received a SIGUSR1");
Mike Frysinger7031f622006-05-08 03:20:50 +0000397 write_leases();
398 /* why not just reset the timeout, eh */
Denis Vlasenko42b3dea2007-07-03 15:47:50 +0000399 timeout_end = monotonic_sec() + server_config.auto_time;
Mike Frysinger7031f622006-05-08 03:20:50 +0000400 continue;
401 case SIGTERM:
Denis Vlasenko3538b9a2006-09-06 18:36:50 +0000402 bb_info_msg("Received a SIGTERM");
Denis Vlasenko6e6d3312007-05-03 23:39:35 +0000403 goto ret0;
Denis Vlasenko0416e3d2009-01-01 17:52:09 +0000404 case 0: /* no signal: read a packet */
405 break;
406 default: /* signal or error (probably EINTR): back to select */
407 continue;
Mike Frysinger7031f622006-05-08 03:20:50 +0000408 }
409
Denis Vlasenko0416e3d2009-01-01 17:52:09 +0000410 bytes = udhcp_recv_kernel_packet(&packet, server_socket);
Denis Vlasenkoaf1c8432007-03-26 13:22:35 +0000411 if (bytes < 0) {
Denis Vlasenko0416e3d2009-01-01 17:52:09 +0000412 /* bytes can also be -2 ("bad packet data") */
Mike Frysinger7031f622006-05-08 03:20:50 +0000413 if (bytes == -1 && errno != EINTR) {
Denys Vlasenkoac906fa2009-06-17 11:54:52 +0200414 log1("Read error: %s, reopening socket", strerror(errno));
Mike Frysinger7031f622006-05-08 03:20:50 +0000415 close(server_socket);
416 server_socket = -1;
417 }
418 continue;
419 }
420
Denys Vlasenko31af3d52009-06-17 11:57:09 +0200421 if (packet.hlen != 6) {
422 bb_error_msg("MAC length != 6, ignoring packet");
423 continue;
424 }
425
Denis Vlasenkoaf1c8432007-03-26 13:22:35 +0000426 state = get_option(&packet, DHCP_MESSAGE_TYPE);
427 if (state == NULL) {
Denys Vlasenkoac906fa2009-06-17 11:54:52 +0200428 bb_error_msg("no message type option, ignoring packet");
Mike Frysinger7031f622006-05-08 03:20:50 +0000429 continue;
430 }
431
432 /* Look for a static lease */
Denys Vlasenkoa9539872010-03-20 03:49:27 +0100433 static_lease_nip = get_static_nip_by_mac(server_config.static_leases, &packet.chaddr);
434 if (static_lease_nip) {
435 bb_info_msg("Found static lease: %x", static_lease_nip);
Mike Frysinger7031f622006-05-08 03:20:50 +0000436
Denys Vlasenko6947d2c2009-06-17 13:24:03 +0200437 memcpy(&fake_lease.lease_mac, &packet.chaddr, 6);
Denys Vlasenkoa9539872010-03-20 03:49:27 +0100438 fake_lease.lease_nip = static_lease_nip;
Denys Vlasenko6947d2c2009-06-17 13:24:03 +0200439 fake_lease.expires = 0;
Mike Frysinger7031f622006-05-08 03:20:50 +0000440
Denys Vlasenko6947d2c2009-06-17 13:24:03 +0200441 lease = &fake_lease;
Denis Vlasenko5a3395b2006-11-18 19:51:32 +0000442 } else {
Denys Vlasenko6947d2c2009-06-17 13:24:03 +0200443 lease = find_lease_by_mac(packet.chaddr);
Mike Frysinger7031f622006-05-08 03:20:50 +0000444 }
445
446 switch (state[0]) {
447 case DHCPDISCOVER:
Denys Vlasenkoac906fa2009-06-17 11:54:52 +0200448 log1("Received DISCOVER");
Mike Frysinger7031f622006-05-08 03:20:50 +0000449
Denys Vlasenkoa9539872010-03-20 03:49:27 +0100450 if (send_offer(&packet, static_lease_nip, lease) < 0) {
Denis Vlasenkoa9595882006-09-29 21:30:43 +0000451 bb_error_msg("send OFFER failed");
Mike Frysinger7031f622006-05-08 03:20:50 +0000452 }
453 break;
Denys Vlasenko6947d2c2009-06-17 13:24:03 +0200454 case DHCPREQUEST: {
455 uint8_t *server_id_opt, *requested_opt;
456 uint32_t server_id_net = server_id_net; /* for compiler */
457 uint32_t requested_nip = requested_nip; /* for compiler */
458
Denys Vlasenkoac906fa2009-06-17 11:54:52 +0200459 log1("Received REQUEST");
Mike Frysinger7031f622006-05-08 03:20:50 +0000460
Denys Vlasenko6947d2c2009-06-17 13:24:03 +0200461 requested_opt = get_option(&packet, DHCP_REQUESTED_IP);
462 server_id_opt = get_option(&packet, DHCP_SERVER_ID);
463 if (requested_opt)
464 move_from_unaligned32(requested_nip, requested_opt);
465 if (server_id_opt)
466 move_from_unaligned32(server_id_net, server_id_opt);
Mike Frysinger7031f622006-05-08 03:20:50 +0000467
468 if (lease) {
Denys Vlasenko6947d2c2009-06-17 13:24:03 +0200469 if (server_id_opt) {
Mike Frysinger7031f622006-05-08 03:20:50 +0000470 /* SELECTING State */
Denys Vlasenko6947d2c2009-06-17 13:24:03 +0200471 if (server_id_net == server_config.server_nip
472 && requested_opt
473 && requested_nip == lease->lease_nip
Denis Vlasenkoaf1c8432007-03-26 13:22:35 +0000474 ) {
Denys Vlasenko1d924f52009-06-16 10:23:01 +0200475 send_ACK(&packet, lease->lease_nip);
Mike Frysinger7031f622006-05-08 03:20:50 +0000476 }
Denys Vlasenko6947d2c2009-06-17 13:24:03 +0200477 } else if (requested_opt) {
Denis Vlasenkoaf1c8432007-03-26 13:22:35 +0000478 /* INIT-REBOOT State */
Denys Vlasenko6947d2c2009-06-17 13:24:03 +0200479 if (lease->lease_nip == requested_nip)
Denys Vlasenko1d924f52009-06-16 10:23:01 +0200480 send_ACK(&packet, lease->lease_nip);
Denis Vlasenkoaf1c8432007-03-26 13:22:35 +0000481 else
Denis Vlasenko6de89942008-05-21 07:05:06 +0000482 send_NAK(&packet);
Denys Vlasenko1d924f52009-06-16 10:23:01 +0200483 } else if (lease->lease_nip == packet.ciaddr) {
Denis Vlasenkoaf1c8432007-03-26 13:22:35 +0000484 /* RENEWING or REBINDING State */
Denys Vlasenko1d924f52009-06-16 10:23:01 +0200485 send_ACK(&packet, lease->lease_nip);
Denis Vlasenko6de89942008-05-21 07:05:06 +0000486 } else { /* don't know what to do!!!! */
487 send_NAK(&packet);
Mike Frysinger7031f622006-05-08 03:20:50 +0000488 }
489
490 /* what to do if we have no record of the client */
Denys Vlasenko6947d2c2009-06-17 13:24:03 +0200491 } else if (server_id_opt) {
Mike Frysinger7031f622006-05-08 03:20:50 +0000492 /* SELECTING State */
493
Denys Vlasenko6947d2c2009-06-17 13:24:03 +0200494 } else if (requested_opt) {
Mike Frysinger7031f622006-05-08 03:20:50 +0000495 /* INIT-REBOOT State */
Denys Vlasenko6947d2c2009-06-17 13:24:03 +0200496 lease = find_lease_by_nip(requested_nip);
Denis Vlasenkoaf1c8432007-03-26 13:22:35 +0000497 if (lease) {
Denys Vlasenko6947d2c2009-06-17 13:24:03 +0200498 if (is_expired_lease(lease)) {
Mike Frysinger7031f622006-05-08 03:20:50 +0000499 /* probably best if we drop this lease */
Denys Vlasenko31af3d52009-06-17 11:57:09 +0200500 memset(lease->lease_mac, 0, sizeof(lease->lease_mac));
Denys Vlasenko1d924f52009-06-16 10:23:01 +0200501 } else {
502 /* make some contention for this address */
Denis Vlasenko6de89942008-05-21 07:05:06 +0000503 send_NAK(&packet);
Denys Vlasenko1d924f52009-06-16 10:23:01 +0200504 }
Denis Vlasenkoc82b5102007-07-01 17:05:57 +0000505 } else {
Denys Vlasenko6947d2c2009-06-17 13:24:03 +0200506 uint32_t r = ntohl(requested_nip);
Denis Vlasenkoc82b5102007-07-01 17:05:57 +0000507 if (r < server_config.start_ip
508 || r > server_config.end_ip
509 ) {
Denis Vlasenko6de89942008-05-21 07:05:06 +0000510 send_NAK(&packet);
Denis Vlasenkoc82b5102007-07-01 17:05:57 +0000511 }
Denis Vlasenkoe6bce972007-07-01 17:11:54 +0000512 /* else remain silent */
513 }
Mike Frysinger7031f622006-05-08 03:20:50 +0000514
515 } else {
Denis Vlasenkoaf1c8432007-03-26 13:22:35 +0000516 /* RENEWING or REBINDING State */
Mike Frysinger7031f622006-05-08 03:20:50 +0000517 }
518 break;
Denys Vlasenko6947d2c2009-06-17 13:24:03 +0200519 }
Mike Frysinger7031f622006-05-08 03:20:50 +0000520 case DHCPDECLINE:
Denys Vlasenkoac906fa2009-06-17 11:54:52 +0200521 log1("Received DECLINE");
Mike Frysinger7031f622006-05-08 03:20:50 +0000522 if (lease) {
Denys Vlasenko31af3d52009-06-17 11:57:09 +0200523 memset(lease->lease_mac, 0, sizeof(lease->lease_mac));
Denis Vlasenko04158e02009-02-02 10:48:06 +0000524 lease->expires = time(NULL) + server_config.decline_time;
Mike Frysinger7031f622006-05-08 03:20:50 +0000525 }
526 break;
527 case DHCPRELEASE:
Denys Vlasenkoac906fa2009-06-17 11:54:52 +0200528 log1("Received RELEASE");
Denis Vlasenko6e6d3312007-05-03 23:39:35 +0000529 if (lease)
Denis Vlasenko04158e02009-02-02 10:48:06 +0000530 lease->expires = time(NULL);
Mike Frysinger7031f622006-05-08 03:20:50 +0000531 break;
532 case DHCPINFORM:
Denys Vlasenkoac906fa2009-06-17 11:54:52 +0200533 log1("Received INFORM");
Mike Frysinger7031f622006-05-08 03:20:50 +0000534 send_inform(&packet);
535 break;
536 default:
Denis Vlasenko3538b9a2006-09-06 18:36:50 +0000537 bb_info_msg("Unsupported DHCP message (%02x) - ignoring", state[0]);
Mike Frysinger7031f622006-05-08 03:20:50 +0000538 }
539 }
Denis Vlasenko6e6d3312007-05-03 23:39:35 +0000540 ret0:
541 retval = 0;
542 ret:
Denis Vlasenko42b3dea2007-07-03 15:47:50 +0000543 /*if (server_config.pidfile) - server_config.pidfile is never NULL */
Denis Vlasenko6e6d3312007-05-03 23:39:35 +0000544 remove_pidfile(server_config.pidfile);
545 return retval;
Mike Frysinger7031f622006-05-08 03:20:50 +0000546}