blob: d6e90cd03e6f9abb5f721cc3751fb0fa1505ee9d [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))
Denys Vlasenko53f72bb2010-03-21 06:46:09 +010051 || dhcp_pkt->ciaddr == 0
Denys Vlasenko8a7c1662010-03-20 03:48:11 +010052 ) {
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");
Denys Vlasenko53f72bb2010-03-21 06:46:09 +0100469/* RFC 2131:
Mike Frysinger7031f622006-05-08 03:20:50 +0000470
Denys Vlasenko53f72bb2010-03-21 06:46:09 +0100471o DHCPREQUEST generated during SELECTING state:
472
473 Client inserts the address of the selected server in 'server
474 identifier', 'ciaddr' MUST be zero, 'requested IP address' MUST be
475 filled in with the yiaddr value from the chosen DHCPOFFER.
476
477 Note that the client may choose to collect several DHCPOFFER
478 messages and select the "best" offer. The client indicates its
479 selection by identifying the offering server in the DHCPREQUEST
480 message. If the client receives no acceptable offers, the client
481 may choose to try another DHCPDISCOVER message. Therefore, the
482 servers may not receive a specific DHCPREQUEST from which they can
483 decide whether or not the client has accepted the offer.
484
485o DHCPREQUEST generated during INIT-REBOOT state:
486
487 'server identifier' MUST NOT be filled in, 'requested IP address'
488 option MUST be filled in with client's notion of its previously
489 assigned address. 'ciaddr' MUST be zero. The client is seeking to
490 verify a previously allocated, cached configuration. Server SHOULD
491 send a DHCPNAK message to the client if the 'requested IP address'
492 is incorrect, or is on the wrong network.
493
494 Determining whether a client in the INIT-REBOOT state is on the
495 correct network is done by examining the contents of 'giaddr', the
496 'requested IP address' option, and a database lookup. If the DHCP
497 server detects that the client is on the wrong net (i.e., the
498 result of applying the local subnet mask or remote subnet mask (if
499 'giaddr' is not zero) to 'requested IP address' option value
500 doesn't match reality), then the server SHOULD send a DHCPNAK
501 message to the client.
502
503 If the network is correct, then the DHCP server should check if
504 the client's notion of its IP address is correct. If not, then the
505 server SHOULD send a DHCPNAK message to the client. If the DHCP
506 server has no record of this client, then it MUST remain silent,
507 and MAY output a warning to the network administrator. This
508 behavior is necessary for peaceful coexistence of non-
509 communicating DHCP servers on the same wire.
510
511 If 'giaddr' is 0x0 in the DHCPREQUEST message, the client is on
512 the same subnet as the server. The server MUST broadcast the
513 DHCPNAK message to the 0xffffffff broadcast address because the
514 client may not have a correct network address or subnet mask, and
515 the client may not be answering ARP requests.
516
517 If 'giaddr' is set in the DHCPREQUEST message, the client is on a
518 different subnet. The server MUST set the broadcast bit in the
519 DHCPNAK, so that the relay agent will broadcast the DHCPNAK to the
520 client, because the client may not have a correct network address
521 or subnet mask, and the client may not be answering ARP requests.
522
523o DHCPREQUEST generated during RENEWING state:
524
525 'server identifier' MUST NOT be filled in, 'requested IP address'
526 option MUST NOT be filled in, 'ciaddr' MUST be filled in with
527 client's IP address. In this situation, the client is completely
528 configured, and is trying to extend its lease. This message will
529 be unicast, so no relay agents will be involved in its
530 transmission. Because 'giaddr' is therefore not filled in, the
531 DHCP server will trust the value in 'ciaddr', and use it when
532 replying to the client.
533
534 A client MAY choose to renew or extend its lease prior to T1. The
535 server may choose not to extend the lease (as a policy decision by
536 the network administrator), but should return a DHCPACK message
537 regardless.
538
539o DHCPREQUEST generated during REBINDING state:
540
541 'server identifier' MUST NOT be filled in, 'requested IP address'
542 option MUST NOT be filled in, 'ciaddr' MUST be filled in with
543 client's IP address. In this situation, the client is completely
544 configured, and is trying to extend its lease. This message MUST
545 be broadcast to the 0xffffffff IP broadcast address. The DHCP
546 server SHOULD check 'ciaddr' for correctness before replying to
547 the DHCPREQUEST.
548
549 The DHCPREQUEST from a REBINDING client is intended to accommodate
550 sites that have multiple DHCP servers and a mechanism for
551 maintaining consistency among leases managed by multiple servers.
552 A DHCP server MAY extend a client's lease only if it has local
553 administrative authority to do so.
554*/
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100555 if (!requested_opt) {
Denys Vlasenko53f72bb2010-03-21 06:46:09 +0100556 requested_nip = packet.ciaddr;
557 if (requested_nip == 0) {
558 log1("no requested IP and no ciaddr, ignoring");
559 break;
560 }
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100561 }
562 if (lease && requested_nip == lease->lease_nip) {
Denys Vlasenko53f72bb2010-03-21 06:46:09 +0100563 /* client requested or configured IP matches the lease.
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100564 * ACK it, and bump lease expiration time. */
565 send_ACK(&packet, lease->lease_nip);
566 break;
567 }
568 if (server_id_opt) {
569 /* client was talking specifically to us.
570 * "No, we don't have this IP for you". */
571 send_NAK(&packet);
Mike Frysinger7031f622006-05-08 03:20:50 +0000572 }
573 break;
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100574
Mike Frysinger7031f622006-05-08 03:20:50 +0000575 case DHCPDECLINE:
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100576 /* RFC 2131:
577 * "If the server receives a DHCPDECLINE message,
578 * the client has discovered through some other means
579 * that the suggested network address is already
580 * in use. The server MUST mark the network address
581 * as not available and SHOULD notify the local
582 * sysadmin of a possible configuration problem."
583 *
584 * SERVER_ID must be present,
585 * REQUESTED_IP must be present,
586 * chaddr must be filled in,
587 * ciaddr must be 0 (we do not check this)
588 */
Denys Vlasenkoac906fa2009-06-17 11:54:52 +0200589 log1("Received DECLINE");
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100590 if (server_id_opt
591 && requested_opt
592 && lease /* chaddr matches this lease */
593 && requested_nip == lease->lease_nip
594 ) {
Denys Vlasenko31af3d52009-06-17 11:57:09 +0200595 memset(lease->lease_mac, 0, sizeof(lease->lease_mac));
Denis Vlasenko04158e02009-02-02 10:48:06 +0000596 lease->expires = time(NULL) + server_config.decline_time;
Mike Frysinger7031f622006-05-08 03:20:50 +0000597 }
598 break;
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100599
Mike Frysinger7031f622006-05-08 03:20:50 +0000600 case DHCPRELEASE:
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100601 /* "Upon receipt of a DHCPRELEASE message, the server
602 * marks the network address as not allocated."
603 *
604 * SERVER_ID must be present,
605 * REQUESTED_IP must not be present (we do not check this),
606 * chaddr must be filled in,
607 * ciaddr must be filled in
608 */
Denys Vlasenkoac906fa2009-06-17 11:54:52 +0200609 log1("Received RELEASE");
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100610 if (server_id_opt
611 && lease /* chaddr matches this lease */
612 && packet.ciaddr == lease->lease_nip
613 ) {
Denis Vlasenko04158e02009-02-02 10:48:06 +0000614 lease->expires = time(NULL);
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100615 }
Mike Frysinger7031f622006-05-08 03:20:50 +0000616 break;
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100617
Mike Frysinger7031f622006-05-08 03:20:50 +0000618 case DHCPINFORM:
Denys Vlasenkoac906fa2009-06-17 11:54:52 +0200619 log1("Received INFORM");
Mike Frysinger7031f622006-05-08 03:20:50 +0000620 send_inform(&packet);
621 break;
Mike Frysinger7031f622006-05-08 03:20:50 +0000622 }
623 }
Denis Vlasenko6e6d3312007-05-03 23:39:35 +0000624 ret0:
625 retval = 0;
626 ret:
Denis Vlasenko42b3dea2007-07-03 15:47:50 +0000627 /*if (server_config.pidfile) - server_config.pidfile is never NULL */
Denis Vlasenko6e6d3312007-05-03 23:39:35 +0000628 remove_pidfile(server_config.pidfile);
629 return retval;
Mike Frysinger7031f622006-05-08 03:20:50 +0000630}