blob: a529e1b58d21f294e68942b8533354477742c416 [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 Vlasenkoc7dc79e2010-03-21 06:15:28 +010031/* Send a packet to a specific mac address and ip address by creating our own ip packet */
32static void send_packet_to_client(struct dhcp_packet *dhcp_pkt, int force_broadcast)
Denys Vlasenko8a7c1662010-03-20 03:48:11 +010033{
34 const uint8_t *chaddr;
35 uint32_t ciaddr;
36
37 // Was:
38 //if (force_broadcast) { /* broadcast */ }
39 //else if (dhcp_pkt->ciaddr) { /* unicast to dhcp_pkt->ciaddr */ }
40 //else if (dhcp_pkt->flags & htons(BROADCAST_FLAG)) { /* broadcast */ }
41 //else { /* unicast to dhcp_pkt->yiaddr */ }
42 // But this is wrong: yiaddr is _our_ idea what client's IP is
43 // (for example, from lease file). Client may not know that,
44 // and may not have UDP socket listening on that IP!
45 // We should never unicast to dhcp_pkt->yiaddr!
46 // dhcp_pkt->ciaddr, OTOH, comes from client's request packet,
47 // and can be used.
48
49 if (force_broadcast
50 || (dhcp_pkt->flags & htons(BROADCAST_FLAG))
51 || !dhcp_pkt->ciaddr
52 ) {
53 log1("Broadcasting packet to client");
54 ciaddr = INADDR_BROADCAST;
55 chaddr = MAC_BCAST_ADDR;
56 } else {
57 log1("Unicasting packet to client ciaddr");
58 ciaddr = dhcp_pkt->ciaddr;
59 chaddr = dhcp_pkt->chaddr;
60 }
61
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +010062 udhcp_send_raw_packet(dhcp_pkt,
Denys Vlasenko8a7c1662010-03-20 03:48:11 +010063 /*src*/ server_config.server_nip, SERVER_PORT,
64 /*dst*/ ciaddr, CLIENT_PORT, chaddr,
65 server_config.ifindex);
66}
67
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +010068/* Send a packet to gateway_nip using the kernel ip stack */
69static void send_packet_to_relay(struct dhcp_packet *dhcp_pkt)
70{
71 log1("Forwarding packet to relay");
72
73 udhcp_send_kernel_packet(dhcp_pkt,
74 server_config.server_nip, SERVER_PORT,
75 dhcp_pkt->gateway_nip, SERVER_PORT);
76}
77
78static void send_packet(struct dhcp_packet *dhcp_pkt, int force_broadcast)
Denys Vlasenko8a7c1662010-03-20 03:48:11 +010079{
80 if (dhcp_pkt->gateway_nip)
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +010081 send_packet_to_relay(dhcp_pkt);
82 else
83 send_packet_to_client(dhcp_pkt, force_broadcast);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +010084}
85
86static void init_packet(struct dhcp_packet *packet, struct dhcp_packet *oldpacket, char type)
87{
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +010088 /* Sets op, htype, hlen, cookie fields
89 * and adds DHCP_MESSAGE_TYPE option */
Denys Vlasenko8a7c1662010-03-20 03:48:11 +010090 udhcp_init_header(packet, type);
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +010091
Denys Vlasenko8a7c1662010-03-20 03:48:11 +010092 packet->xid = oldpacket->xid;
93 memcpy(packet->chaddr, oldpacket->chaddr, sizeof(oldpacket->chaddr));
94 packet->flags = oldpacket->flags;
95 packet->gateway_nip = oldpacket->gateway_nip;
96 packet->ciaddr = oldpacket->ciaddr;
97 add_simple_option(packet->options, DHCP_SERVER_ID, server_config.server_nip);
98}
99
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100100/* Fill options field, siaddr_nip, and sname and boot_file fields.
101 * TODO: teach this code to use overload option.
102 */
103static void add_server_options(struct dhcp_packet *packet)
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100104{
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100105 struct option_set *curr = server_config.options;
106
107 while (curr) {
108 if (curr->data[OPT_CODE] != DHCP_LEASE_TIME)
109 add_option_string(packet->options, curr->data);
110 curr = curr->next;
111 }
112
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100113 packet->siaddr_nip = server_config.siaddr_nip;
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100114
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100115 if (server_config.sname)
116 strncpy((char*)packet->sname, server_config.sname, sizeof(packet->sname) - 1);
117 if (server_config.boot_file)
118 strncpy((char*)packet->file, server_config.boot_file, sizeof(packet->file) - 1);
119}
120
121static uint32_t select_lease_time(struct dhcp_packet *packet)
122{
123 uint32_t lease_time_sec = server_config.max_lease_sec;
124 uint8_t *lease_time_opt = get_option(packet, DHCP_LEASE_TIME);
125 if (lease_time_opt) {
126 move_from_unaligned32(lease_time_sec, lease_time_opt);
127 lease_time_sec = ntohl(lease_time_sec);
128 if (lease_time_sec > server_config.max_lease_sec)
129 lease_time_sec = server_config.max_lease_sec;
130 if (lease_time_sec < server_config.min_lease_sec)
131 lease_time_sec = server_config.min_lease_sec;
132 }
133 return lease_time_sec;
134}
135
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100136/* We got a DHCP DISCOVER. Send an OFFER. */
137static void send_offer(struct dhcp_packet *oldpacket, uint32_t static_lease_nip, struct dyn_lease *lease)
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100138{
139 struct dhcp_packet packet;
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100140 uint32_t lease_time_sec;
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100141 struct in_addr addr;
142
143 init_packet(&packet, oldpacket, DHCPOFFER);
144
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100145 /* If it is a static lease, use its IP */
146 packet.yiaddr = static_lease_nip;
147 /* Else: */
Denys Vlasenkoa9539872010-03-20 03:49:27 +0100148 if (!static_lease_nip) {
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100149 /* We have no static lease for client's chaddr */
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100150 uint32_t req_nip;
151 uint8_t *req_ip_opt;
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100152 const char *p_host_name;
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100153
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100154 if (lease) {
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100155 /* We have a dynamic lease for client's chaddr.
156 * Reuse its IP (even if lease is expired).
157 * Note that we ignore requested IP in this case.
158 */
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100159 packet.yiaddr = lease->lease_nip;
160 }
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100161 /* Or: if client has requested an IP */
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100162 else if ((req_ip_opt = get_option(oldpacket, DHCP_REQUESTED_IP)) != NULL
163 /* (read IP) */
164 && (move_from_unaligned32(req_nip, req_ip_opt), 1)
165 /* and the IP is in the lease range */
166 && ntohl(req_nip) >= server_config.start_ip
167 && ntohl(req_nip) <= server_config.end_ip
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100168 /* and */
169 && ( !(lease = find_lease_by_nip(req_nip)) /* is not already taken */
170 || is_expired_lease(lease) /* or is taken, but expired */
171 )
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100172 ) {
173 packet.yiaddr = req_nip;
174 }
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100175 else {
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100176 /* Otherwise, find a free IP */
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100177 packet.yiaddr = find_free_or_expired_nip(oldpacket->chaddr);
178 }
179
180 if (!packet.yiaddr) {
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100181 bb_error_msg("no free IP addresses. OFFER abandoned");
182 return;
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100183 }
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100184 /* Reserve the IP for a short time hoping to get DHCPREQUEST soon */
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100185 p_host_name = (const char*) get_option(oldpacket, DHCP_HOST_NAME);
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100186 lease = add_lease(packet.chaddr, packet.yiaddr,
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100187 server_config.offer_time,
188 p_host_name,
189 p_host_name ? (unsigned char)p_host_name[OPT_LEN - OPT_DATA] : 0
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100190 );
191 if (!lease) {
192 bb_error_msg("no free IP addresses. OFFER abandoned");
193 return;
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100194 }
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100195 }
196
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100197 lease_time_sec = select_lease_time(oldpacket);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100198 add_simple_option(packet.options, DHCP_LEASE_TIME, htonl(lease_time_sec));
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100199 add_server_options(&packet);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100200
201 addr.s_addr = packet.yiaddr;
202 bb_info_msg("Sending OFFER of %s", inet_ntoa(addr));
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100203 /* send_packet emits error message itself if it detects failure */
204 send_packet(&packet, /*force_bcast:*/ 0);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100205}
206
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100207static void send_NAK(struct dhcp_packet *oldpacket)
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100208{
209 struct dhcp_packet packet;
210
211 init_packet(&packet, oldpacket, DHCPNAK);
212
213 log1("Sending NAK");
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100214 send_packet(&packet, /*force_bcast:*/ 1);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100215}
216
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100217static void send_ACK(struct dhcp_packet *oldpacket, uint32_t yiaddr)
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100218{
219 struct dhcp_packet packet;
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100220 uint32_t lease_time_sec;
221 struct in_addr addr;
222 const char *p_host_name;
223
224 init_packet(&packet, oldpacket, DHCPACK);
225 packet.yiaddr = yiaddr;
226
227 lease_time_sec = select_lease_time(oldpacket);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100228 add_simple_option(packet.options, DHCP_LEASE_TIME, htonl(lease_time_sec));
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100229
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100230 add_server_options(&packet);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100231
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100232 addr.s_addr = yiaddr;
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100233 bb_info_msg("Sending ACK to %s", inet_ntoa(addr));
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100234 send_packet(&packet, /*force_bcast:*/ 0);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100235
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 }
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100246}
247
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100248static void send_inform(struct dhcp_packet *oldpacket)
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100249{
250 struct dhcp_packet packet;
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100251
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100252 /* "The server responds to a DHCPINFORM message by sending a DHCPACK
253 * message directly to the address given in the 'ciaddr' field
254 * of the DHCPINFORM message. The server MUST NOT send a lease
255 * expiration time to the client and SHOULD NOT fill in 'yiaddr'."
256 */
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100257 init_packet(&packet, oldpacket, DHCPACK);
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100258 add_server_options(&packet);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100259
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100260 send_packet(&packet, /*force_bcast:*/ 0);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100261}
262
263
Mike Frysinger7031f622006-05-08 03:20:50 +0000264/* globals */
Denys Vlasenko6947d2c2009-06-17 13:24:03 +0200265struct dyn_lease *g_leases;
Denis Vlasenkodeabacd2007-09-30 17:55:43 +0000266/* struct server_config_t server_config is in bb_common_bufsiz1 */
Mike Frysinger7031f622006-05-08 03:20:50 +0000267
268
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000269int udhcpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000270int udhcpd_main(int argc UNUSED_PARAM, char **argv)
Mike Frysinger7031f622006-05-08 03:20:50 +0000271{
272 fd_set rfds;
Denis Vlasenko0416e3d2009-01-01 17:52:09 +0000273 int server_socket = -1, retval, max_sock;
Denys Vlasenko31af3d52009-06-17 11:57:09 +0200274 struct dhcp_packet packet;
Denys Vlasenko6947d2c2009-06-17 13:24:03 +0200275 uint8_t *state;
Denys Vlasenkoa9539872010-03-20 03:49:27 +0100276 uint32_t static_lease_nip;
Denis Vlasenko42b3dea2007-07-03 15:47:50 +0000277 unsigned timeout_end;
278 unsigned num_ips;
Denis Vlasenko3d17d2b2007-08-14 16:45:29 +0000279 unsigned opt;
Mike Frysinger7031f622006-05-08 03:20:50 +0000280 struct option_set *option;
Denys Vlasenko6947d2c2009-06-17 13:24:03 +0200281 struct dyn_lease *lease, fake_lease;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000282 IF_FEATURE_UDHCP_PORT(char *str_P;)
Mike Frysinger7031f622006-05-08 03:20:50 +0000283
Denis Vlasenkod55fe3e2008-02-04 13:12:16 +0000284#if ENABLE_FEATURE_UDHCP_PORT
285 SERVER_PORT = 67;
286 CLIENT_PORT = 68;
287#endif
288
Denys Vlasenkoac906fa2009-06-17 11:54:52 +0200289#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
290 opt_complementary = "vv";
291#endif
292 opt = getopt32(argv, "fSv"
293 IF_FEATURE_UDHCP_PORT("P:", &str_P)
294#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
295 , &dhcp_verbose
296#endif
297 );
Denis Vlasenko9f7b92a2007-08-15 20:03:36 +0000298 argv += optind;
Denis Vlasenko3d17d2b2007-08-14 16:45:29 +0000299 if (!(opt & 1)) { /* no -f */
Denis Vlasenkoc82b5102007-07-01 17:05:57 +0000300 bb_daemonize_or_rexec(0, argv);
Denis Vlasenkoa19e6492009-03-11 14:40:00 +0000301 logmode = LOGMODE_NONE;
Denis Vlasenkoaf1c8432007-03-26 13:22:35 +0000302 }
Denis Vlasenko3d17d2b2007-08-14 16:45:29 +0000303 if (opt & 2) { /* -S */
Denis Vlasenko5e4fda02009-03-08 23:46:48 +0000304 openlog(applet_name, LOG_PID, LOG_DAEMON);
Denis Vlasenkoaf1c8432007-03-26 13:22:35 +0000305 logmode |= LOGMODE_SYSLOG;
306 }
Denis Vlasenkod55fe3e2008-02-04 13:12:16 +0000307#if ENABLE_FEATURE_UDHCP_PORT
308 if (opt & 4) { /* -P */
309 SERVER_PORT = xatou16(str_P);
310 CLIENT_PORT = SERVER_PORT + 1;
311 }
312#endif
Denis Vlasenkoaf1c8432007-03-26 13:22:35 +0000313 /* Would rather not do read_config before daemonization -
314 * otherwise NOMMU machines will parse config twice */
Denis Vlasenko9f7b92a2007-08-15 20:03:36 +0000315 read_config(argv[0] ? argv[0] : DHCPD_CONF_FILE);
Mike Frysinger7031f622006-05-08 03:20:50 +0000316
Denis Vlasenko80edead2007-08-02 22:31:05 +0000317 /* Make sure fd 0,1,2 are open */
318 bb_sanitize_stdio();
319 /* Equivalent of doing a fflush after every \n */
320 setlinebuf(stdout);
321
322 /* Create pidfile */
323 write_pidfile(server_config.pidfile);
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100324 /* if (!..) bb_perror_msg("can't create pidfile %s", pidfile); */
Denis Vlasenko80edead2007-08-02 22:31:05 +0000325
Denis Vlasenkodef88982007-10-07 17:06:01 +0000326 bb_info_msg("%s (v"BB_VER") started", applet_name);
Mike Frysinger7031f622006-05-08 03:20:50 +0000327
Denis Vlasenkoaf1c8432007-03-26 13:22:35 +0000328 option = find_option(server_config.options, DHCP_LEASE_TIME);
Denys Vlasenko2e7aa922010-03-21 02:22:07 +0100329 server_config.max_lease_sec = DEFAULT_LEASE_TIME;
Denis Vlasenkoaf1c8432007-03-26 13:22:35 +0000330 if (option) {
Denys Vlasenko6947d2c2009-06-17 13:24:03 +0200331 move_from_unaligned32(server_config.max_lease_sec, option->data + OPT_DATA);
332 server_config.max_lease_sec = ntohl(server_config.max_lease_sec);
Denis Vlasenko6e6d3312007-05-03 23:39:35 +0000333 }
Mike Frysinger7031f622006-05-08 03:20:50 +0000334
335 /* Sanity check */
Denis Vlasenkoc82b5102007-07-01 17:05:57 +0000336 num_ips = server_config.end_ip - server_config.start_ip + 1;
Mike Frysinger7031f622006-05-08 03:20:50 +0000337 if (server_config.max_leases > num_ips) {
Denis Vlasenko42b3dea2007-07-03 15:47:50 +0000338 bb_error_msg("max_leases=%u is too big, setting to %u",
339 (unsigned)server_config.max_leases, num_ips);
Mike Frysinger7031f622006-05-08 03:20:50 +0000340 server_config.max_leases = num_ips;
341 }
342
Denys Vlasenko6947d2c2009-06-17 13:24:03 +0200343 g_leases = xzalloc(server_config.max_leases * sizeof(g_leases[0]));
Mike Frysinger7031f622006-05-08 03:20:50 +0000344 read_leases(server_config.lease_file);
345
Denys Vlasenko26918dd2009-06-16 12:04:23 +0200346 if (udhcp_read_interface(server_config.interface,
347 &server_config.ifindex,
348 &server_config.server_nip,
349 server_config.server_mac)
350 ) {
Denis Vlasenko6e6d3312007-05-03 23:39:35 +0000351 retval = 1;
352 goto ret;
353 }
Mike Frysinger7031f622006-05-08 03:20:50 +0000354
Mike Frysinger7031f622006-05-08 03:20:50 +0000355 /* Setup the signal pipe */
356 udhcp_sp_setup();
357
Denis Vlasenko42b3dea2007-07-03 15:47:50 +0000358 timeout_end = monotonic_sec() + server_config.auto_time;
Denis Vlasenko5a3395b2006-11-18 19:51:32 +0000359 while (1) { /* loop until universe collapses */
Denis Vlasenko0416e3d2009-01-01 17:52:09 +0000360 int bytes;
361 struct timeval tv;
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100362 uint8_t *server_id_opt;
363 uint8_t *requested_opt;
364 uint32_t requested_nip = requested_nip; /* for compiler */
Mike Frysinger7031f622006-05-08 03:20:50 +0000365
Denis Vlasenkoe2d3ded2006-11-27 23:43:28 +0000366 if (server_socket < 0) {
Denis Vlasenkof1980f62008-09-26 09:34:59 +0000367 server_socket = udhcp_listen_socket(/*INADDR_ANY,*/ SERVER_PORT,
Denis Vlasenko6e6d3312007-05-03 23:39:35 +0000368 server_config.interface);
Denis Vlasenkoe2d3ded2006-11-27 23:43:28 +0000369 }
Mike Frysinger7031f622006-05-08 03:20:50 +0000370
371 max_sock = udhcp_sp_fd_set(&rfds, server_socket);
372 if (server_config.auto_time) {
Denis Vlasenko42b3dea2007-07-03 15:47:50 +0000373 tv.tv_sec = timeout_end - monotonic_sec();
Mike Frysinger7031f622006-05-08 03:20:50 +0000374 tv.tv_usec = 0;
375 }
Denis Vlasenko6e6d3312007-05-03 23:39:35 +0000376 retval = 0;
Mike Frysinger7031f622006-05-08 03:20:50 +0000377 if (!server_config.auto_time || tv.tv_sec > 0) {
378 retval = select(max_sock + 1, &rfds, NULL, NULL,
379 server_config.auto_time ? &tv : NULL);
Denis Vlasenko6e6d3312007-05-03 23:39:35 +0000380 }
Mike Frysinger7031f622006-05-08 03:20:50 +0000381 if (retval == 0) {
382 write_leases();
Denis Vlasenko42b3dea2007-07-03 15:47:50 +0000383 timeout_end = monotonic_sec() + server_config.auto_time;
Mike Frysinger7031f622006-05-08 03:20:50 +0000384 continue;
Denis Vlasenko6e6d3312007-05-03 23:39:35 +0000385 }
386 if (retval < 0 && errno != EINTR) {
Denys Vlasenkoac906fa2009-06-17 11:54:52 +0200387 log1("Error on select");
Mike Frysinger7031f622006-05-08 03:20:50 +0000388 continue;
389 }
390
391 switch (udhcp_sp_read(&rfds)) {
392 case SIGUSR1:
Denis Vlasenko3538b9a2006-09-06 18:36:50 +0000393 bb_info_msg("Received a SIGUSR1");
Mike Frysinger7031f622006-05-08 03:20:50 +0000394 write_leases();
395 /* why not just reset the timeout, eh */
Denis Vlasenko42b3dea2007-07-03 15:47:50 +0000396 timeout_end = monotonic_sec() + server_config.auto_time;
Mike Frysinger7031f622006-05-08 03:20:50 +0000397 continue;
398 case SIGTERM:
Denis Vlasenko3538b9a2006-09-06 18:36:50 +0000399 bb_info_msg("Received a SIGTERM");
Denis Vlasenko6e6d3312007-05-03 23:39:35 +0000400 goto ret0;
Denis Vlasenko0416e3d2009-01-01 17:52:09 +0000401 case 0: /* no signal: read a packet */
402 break;
403 default: /* signal or error (probably EINTR): back to select */
404 continue;
Mike Frysinger7031f622006-05-08 03:20:50 +0000405 }
406
Denis Vlasenko0416e3d2009-01-01 17:52:09 +0000407 bytes = udhcp_recv_kernel_packet(&packet, server_socket);
Denis Vlasenkoaf1c8432007-03-26 13:22:35 +0000408 if (bytes < 0) {
Denis Vlasenko0416e3d2009-01-01 17:52:09 +0000409 /* bytes can also be -2 ("bad packet data") */
Mike Frysinger7031f622006-05-08 03:20:50 +0000410 if (bytes == -1 && errno != EINTR) {
Denys Vlasenkoac906fa2009-06-17 11:54:52 +0200411 log1("Read error: %s, reopening socket", strerror(errno));
Mike Frysinger7031f622006-05-08 03:20:50 +0000412 close(server_socket);
413 server_socket = -1;
414 }
415 continue;
416 }
Denys Vlasenko31af3d52009-06-17 11:57:09 +0200417 if (packet.hlen != 6) {
418 bb_error_msg("MAC length != 6, ignoring packet");
419 continue;
420 }
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100421 if (packet.op != BOOTREQUEST) {
422 bb_error_msg("not a REQUEST, ignoring packet");
423 continue;
424 }
Denis Vlasenkoaf1c8432007-03-26 13:22:35 +0000425 state = get_option(&packet, DHCP_MESSAGE_TYPE);
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100426 if (state == NULL || state[0] < DHCP_MINTYPE || state[0] > DHCP_MAXTYPE) {
427 bb_error_msg("no or bad message type option, ignoring packet");
Mike Frysinger7031f622006-05-08 03:20:50 +0000428 continue;
429 }
430
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100431 /* Look for a static/dynamic lease */
Denys Vlasenkoa9539872010-03-20 03:49:27 +0100432 static_lease_nip = get_static_nip_by_mac(server_config.static_leases, &packet.chaddr);
433 if (static_lease_nip) {
434 bb_info_msg("Found static lease: %x", static_lease_nip);
Denys Vlasenko6947d2c2009-06-17 13:24:03 +0200435 memcpy(&fake_lease.lease_mac, &packet.chaddr, 6);
Denys Vlasenkoa9539872010-03-20 03:49:27 +0100436 fake_lease.lease_nip = static_lease_nip;
Denys Vlasenko6947d2c2009-06-17 13:24:03 +0200437 fake_lease.expires = 0;
Denys Vlasenko6947d2c2009-06-17 13:24:03 +0200438 lease = &fake_lease;
Denis Vlasenko5a3395b2006-11-18 19:51:32 +0000439 } else {
Denys Vlasenko6947d2c2009-06-17 13:24:03 +0200440 lease = find_lease_by_mac(packet.chaddr);
Mike Frysinger7031f622006-05-08 03:20:50 +0000441 }
442
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100443 /* Get REQUESTED_IP and SERVER_ID if present */
444 server_id_opt = get_option(&packet, DHCP_SERVER_ID);
445 if (server_id_opt) {
446 uint32_t server_id_net;
447 move_from_unaligned32(server_id_net, server_id_opt);
448 if (server_id_net != server_config.server_nip) {
449 /* client talks to somebody else */
450 log1("server ID doesn't match, ignoring");
451 continue;
452 }
453 }
454 requested_opt = get_option(&packet, DHCP_REQUESTED_IP);
455 if (requested_opt) {
456 move_from_unaligned32(requested_nip, requested_opt);
457 }
458
Mike Frysinger7031f622006-05-08 03:20:50 +0000459 switch (state[0]) {
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100460
Mike Frysinger7031f622006-05-08 03:20:50 +0000461 case DHCPDISCOVER:
Denys Vlasenkoac906fa2009-06-17 11:54:52 +0200462 log1("Received DISCOVER");
Mike Frysinger7031f622006-05-08 03:20:50 +0000463
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100464 send_offer(&packet, static_lease_nip, lease);
Mike Frysinger7031f622006-05-08 03:20:50 +0000465 break;
Denys Vlasenko6947d2c2009-06-17 13:24:03 +0200466
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100467 case DHCPREQUEST:
Denys Vlasenkoac906fa2009-06-17 11:54:52 +0200468 log1("Received REQUEST");
Mike Frysinger7031f622006-05-08 03:20:50 +0000469
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100470 /* RFC 2131: "The REQUESTED_IP option MUST be set
471 * to the value of 'yiaddr' in the DHCPOFFER message
472 * from the server." */
473 if (!requested_opt) {
474 log1("no requested IP, ignoring");
475 break;
476 }
477 if (lease && requested_nip == lease->lease_nip) {
478 /* client requests IP which matches the lease.
479 * ACK it, and bump lease expiration time. */
480 send_ACK(&packet, lease->lease_nip);
481 break;
482 }
483 if (server_id_opt) {
484 /* client was talking specifically to us.
485 * "No, we don't have this IP for you". */
486 send_NAK(&packet);
Mike Frysinger7031f622006-05-08 03:20:50 +0000487 }
488 break;
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100489
Mike Frysinger7031f622006-05-08 03:20:50 +0000490 case DHCPDECLINE:
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100491 /* RFC 2131:
492 * "If the server receives a DHCPDECLINE message,
493 * the client has discovered through some other means
494 * that the suggested network address is already
495 * in use. The server MUST mark the network address
496 * as not available and SHOULD notify the local
497 * sysadmin of a possible configuration problem."
498 *
499 * SERVER_ID must be present,
500 * REQUESTED_IP must be present,
501 * chaddr must be filled in,
502 * ciaddr must be 0 (we do not check this)
503 */
Denys Vlasenkoac906fa2009-06-17 11:54:52 +0200504 log1("Received DECLINE");
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100505 if (server_id_opt
506 && requested_opt
507 && lease /* chaddr matches this lease */
508 && requested_nip == lease->lease_nip
509 ) {
Denys Vlasenko31af3d52009-06-17 11:57:09 +0200510 memset(lease->lease_mac, 0, sizeof(lease->lease_mac));
Denis Vlasenko04158e02009-02-02 10:48:06 +0000511 lease->expires = time(NULL) + server_config.decline_time;
Mike Frysinger7031f622006-05-08 03:20:50 +0000512 }
513 break;
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100514
Mike Frysinger7031f622006-05-08 03:20:50 +0000515 case DHCPRELEASE:
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100516 /* "Upon receipt of a DHCPRELEASE message, the server
517 * marks the network address as not allocated."
518 *
519 * SERVER_ID must be present,
520 * REQUESTED_IP must not be present (we do not check this),
521 * chaddr must be filled in,
522 * ciaddr must be filled in
523 */
Denys Vlasenkoac906fa2009-06-17 11:54:52 +0200524 log1("Received RELEASE");
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100525 if (server_id_opt
526 && lease /* chaddr matches this lease */
527 && packet.ciaddr == lease->lease_nip
528 ) {
Denis Vlasenko04158e02009-02-02 10:48:06 +0000529 lease->expires = time(NULL);
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100530 }
Mike Frysinger7031f622006-05-08 03:20:50 +0000531 break;
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100532
Mike Frysinger7031f622006-05-08 03:20:50 +0000533 case DHCPINFORM:
Denys Vlasenkoac906fa2009-06-17 11:54:52 +0200534 log1("Received INFORM");
Mike Frysinger7031f622006-05-08 03:20:50 +0000535 send_inform(&packet);
536 break;
Mike Frysinger7031f622006-05-08 03:20:50 +0000537 }
538 }
Denis Vlasenko6e6d3312007-05-03 23:39:35 +0000539 ret0:
540 retval = 0;
541 ret:
Denis Vlasenko42b3dea2007-07-03 15:47:50 +0000542 /*if (server_config.pidfile) - server_config.pidfile is never NULL */
Denis Vlasenko6e6d3312007-05-03 23:39:35 +0000543 remove_pidfile(server_config.pidfile);
544 return retval;
Mike Frysinger7031f622006-05-08 03:20:50 +0000545}