blob: 6fb48a19a9e8509a38edbf199f7c04420f706b05 [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/*
Denys Vlasenko385b4562010-03-26 10:09:34 +01003 * udhcp server
Mike Frysinger7031f622006-05-08 03:20:50 +00004 * 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 */
Denis Vlasenkoaf1c8432007-03-26 13:22:35 +000023#include <syslog.h>
Mike Frysinger7031f622006-05-08 03:20:50 +000024#include "common.h"
Denis Vlasenkod55fe3e2008-02-04 13:12:16 +000025#include "dhcpc.h"
Denis Vlasenko5a3395b2006-11-18 19:51:32 +000026#include "dhcpd.h"
Mike Frysinger7031f622006-05-08 03:20:50 +000027
28
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +010029/* Send a packet to a specific mac address and ip address by creating our own ip packet */
30static void send_packet_to_client(struct dhcp_packet *dhcp_pkt, int force_broadcast)
Denys Vlasenko8a7c1662010-03-20 03:48:11 +010031{
32 const uint8_t *chaddr;
33 uint32_t ciaddr;
34
35 // Was:
36 //if (force_broadcast) { /* broadcast */ }
37 //else if (dhcp_pkt->ciaddr) { /* unicast to dhcp_pkt->ciaddr */ }
38 //else if (dhcp_pkt->flags & htons(BROADCAST_FLAG)) { /* broadcast */ }
39 //else { /* unicast to dhcp_pkt->yiaddr */ }
40 // But this is wrong: yiaddr is _our_ idea what client's IP is
41 // (for example, from lease file). Client may not know that,
42 // and may not have UDP socket listening on that IP!
43 // We should never unicast to dhcp_pkt->yiaddr!
44 // dhcp_pkt->ciaddr, OTOH, comes from client's request packet,
45 // and can be used.
46
47 if (force_broadcast
48 || (dhcp_pkt->flags & htons(BROADCAST_FLAG))
Denys Vlasenko53f72bb2010-03-21 06:46:09 +010049 || dhcp_pkt->ciaddr == 0
Denys Vlasenko8a7c1662010-03-20 03:48:11 +010050 ) {
51 log1("Broadcasting packet to client");
52 ciaddr = INADDR_BROADCAST;
53 chaddr = MAC_BCAST_ADDR;
54 } else {
55 log1("Unicasting packet to client ciaddr");
56 ciaddr = dhcp_pkt->ciaddr;
57 chaddr = dhcp_pkt->chaddr;
58 }
59
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +010060 udhcp_send_raw_packet(dhcp_pkt,
Denys Vlasenko8a7c1662010-03-20 03:48:11 +010061 /*src*/ server_config.server_nip, SERVER_PORT,
62 /*dst*/ ciaddr, CLIENT_PORT, chaddr,
63 server_config.ifindex);
64}
65
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +010066/* Send a packet to gateway_nip using the kernel ip stack */
67static void send_packet_to_relay(struct dhcp_packet *dhcp_pkt)
68{
69 log1("Forwarding packet to relay");
70
71 udhcp_send_kernel_packet(dhcp_pkt,
72 server_config.server_nip, SERVER_PORT,
73 dhcp_pkt->gateway_nip, SERVER_PORT);
74}
75
76static void send_packet(struct dhcp_packet *dhcp_pkt, int force_broadcast)
Denys Vlasenko8a7c1662010-03-20 03:48:11 +010077{
78 if (dhcp_pkt->gateway_nip)
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +010079 send_packet_to_relay(dhcp_pkt);
80 else
81 send_packet_to_client(dhcp_pkt, force_broadcast);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +010082}
83
84static void init_packet(struct dhcp_packet *packet, struct dhcp_packet *oldpacket, char type)
85{
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +010086 /* Sets op, htype, hlen, cookie fields
87 * and adds DHCP_MESSAGE_TYPE option */
Denys Vlasenko8a7c1662010-03-20 03:48:11 +010088 udhcp_init_header(packet, type);
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +010089
Denys Vlasenko8a7c1662010-03-20 03:48:11 +010090 packet->xid = oldpacket->xid;
91 memcpy(packet->chaddr, oldpacket->chaddr, sizeof(oldpacket->chaddr));
92 packet->flags = oldpacket->flags;
93 packet->gateway_nip = oldpacket->gateway_nip;
94 packet->ciaddr = oldpacket->ciaddr;
Denys Vlasenko7724c762010-03-26 09:32:09 +010095 udhcp_add_simple_option(packet, DHCP_SERVER_ID, server_config.server_nip);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +010096}
97
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +010098/* Fill options field, siaddr_nip, and sname and boot_file fields.
99 * TODO: teach this code to use overload option.
100 */
101static void add_server_options(struct dhcp_packet *packet)
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100102{
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100103 struct option_set *curr = server_config.options;
104
105 while (curr) {
106 if (curr->data[OPT_CODE] != DHCP_LEASE_TIME)
Denys Vlasenko7724c762010-03-26 09:32:09 +0100107 udhcp_add_binary_option(packet, curr->data);
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100108 curr = curr->next;
109 }
110
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100111 packet->siaddr_nip = server_config.siaddr_nip;
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100112
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100113 if (server_config.sname)
114 strncpy((char*)packet->sname, server_config.sname, sizeof(packet->sname) - 1);
115 if (server_config.boot_file)
116 strncpy((char*)packet->file, server_config.boot_file, sizeof(packet->file) - 1);
117}
118
119static uint32_t select_lease_time(struct dhcp_packet *packet)
120{
121 uint32_t lease_time_sec = server_config.max_lease_sec;
Denys Vlasenkodde8bdc2010-03-22 14:29:13 +0100122 uint8_t *lease_time_opt = udhcp_get_option(packet, DHCP_LEASE_TIME);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100123 if (lease_time_opt) {
124 move_from_unaligned32(lease_time_sec, lease_time_opt);
125 lease_time_sec = ntohl(lease_time_sec);
126 if (lease_time_sec > server_config.max_lease_sec)
127 lease_time_sec = server_config.max_lease_sec;
128 if (lease_time_sec < server_config.min_lease_sec)
129 lease_time_sec = server_config.min_lease_sec;
130 }
131 return lease_time_sec;
132}
133
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100134/* We got a DHCP DISCOVER. Send an OFFER. */
Denys Vlasenko0bb35e12010-10-21 12:33:10 +0200135/* NOINLINE: limit stack usage in caller */
Denys Vlasenkofa5e2952010-11-28 01:10:51 +0100136static NOINLINE void send_offer(struct dhcp_packet *oldpacket,
137 uint32_t static_lease_nip,
138 struct dyn_lease *lease,
139 uint8_t *requested_ip_opt)
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100140{
141 struct dhcp_packet packet;
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100142 uint32_t lease_time_sec;
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100143 struct in_addr addr;
144
145 init_packet(&packet, oldpacket, DHCPOFFER);
146
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100147 /* If it is a static lease, use its IP */
148 packet.yiaddr = static_lease_nip;
149 /* Else: */
Denys Vlasenkoa9539872010-03-20 03:49:27 +0100150 if (!static_lease_nip) {
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100151 /* We have no static lease for client's chaddr */
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100152 uint32_t req_nip;
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100153 const char *p_host_name;
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100154
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100155 if (lease) {
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100156 /* We have a dynamic lease for client's chaddr.
157 * Reuse its IP (even if lease is expired).
158 * Note that we ignore requested IP in this case.
159 */
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100160 packet.yiaddr = lease->lease_nip;
161 }
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100162 /* Or: if client has requested an IP */
Denys Vlasenkofa5e2952010-11-28 01:10:51 +0100163 else if (requested_ip_opt != NULL
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100164 /* (read IP) */
Denys Vlasenkofa5e2952010-11-28 01:10:51 +0100165 && (move_from_unaligned32(req_nip, requested_ip_opt), 1)
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100166 /* and the IP is in the lease range */
167 && ntohl(req_nip) >= server_config.start_ip
168 && ntohl(req_nip) <= server_config.end_ip
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100169 /* and */
170 && ( !(lease = find_lease_by_nip(req_nip)) /* is not already taken */
171 || is_expired_lease(lease) /* or is taken, but expired */
172 )
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100173 ) {
174 packet.yiaddr = req_nip;
175 }
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100176 else {
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100177 /* Otherwise, find a free IP */
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100178 packet.yiaddr = find_free_or_expired_nip(oldpacket->chaddr);
179 }
180
181 if (!packet.yiaddr) {
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100182 bb_error_msg("no free IP addresses. OFFER abandoned");
183 return;
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100184 }
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100185 /* Reserve the IP for a short time hoping to get DHCPREQUEST soon */
Denys Vlasenkodde8bdc2010-03-22 14:29:13 +0100186 p_host_name = (const char*) udhcp_get_option(oldpacket, DHCP_HOST_NAME);
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100187 lease = add_lease(packet.chaddr, packet.yiaddr,
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100188 server_config.offer_time,
189 p_host_name,
190 p_host_name ? (unsigned char)p_host_name[OPT_LEN - OPT_DATA] : 0
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100191 );
192 if (!lease) {
193 bb_error_msg("no free IP addresses. OFFER abandoned");
194 return;
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100195 }
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100196 }
197
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100198 lease_time_sec = select_lease_time(oldpacket);
Denys Vlasenko7724c762010-03-26 09:32:09 +0100199 udhcp_add_simple_option(&packet, DHCP_LEASE_TIME, htonl(lease_time_sec));
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100200 add_server_options(&packet);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100201
202 addr.s_addr = packet.yiaddr;
203 bb_info_msg("Sending OFFER of %s", inet_ntoa(addr));
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100204 /* send_packet emits error message itself if it detects failure */
205 send_packet(&packet, /*force_bcast:*/ 0);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100206}
207
Denys Vlasenko0bb35e12010-10-21 12:33:10 +0200208/* NOINLINE: limit stack usage in caller */
209static NOINLINE void send_NAK(struct dhcp_packet *oldpacket)
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100210{
211 struct dhcp_packet packet;
212
213 init_packet(&packet, oldpacket, DHCPNAK);
214
215 log1("Sending NAK");
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100216 send_packet(&packet, /*force_bcast:*/ 1);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100217}
218
Denys Vlasenko0bb35e12010-10-21 12:33:10 +0200219/* NOINLINE: limit stack usage in caller */
220static NOINLINE void send_ACK(struct dhcp_packet *oldpacket, uint32_t yiaddr)
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100221{
222 struct dhcp_packet packet;
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100223 uint32_t lease_time_sec;
224 struct in_addr addr;
225 const char *p_host_name;
226
227 init_packet(&packet, oldpacket, DHCPACK);
228 packet.yiaddr = yiaddr;
229
230 lease_time_sec = select_lease_time(oldpacket);
Denys Vlasenko7724c762010-03-26 09:32:09 +0100231 udhcp_add_simple_option(&packet, DHCP_LEASE_TIME, htonl(lease_time_sec));
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100232
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100233 add_server_options(&packet);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100234
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100235 addr.s_addr = yiaddr;
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100236 bb_info_msg("Sending ACK to %s", inet_ntoa(addr));
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100237 send_packet(&packet, /*force_bcast:*/ 0);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100238
Denys Vlasenkodde8bdc2010-03-22 14:29:13 +0100239 p_host_name = (const char*) udhcp_get_option(oldpacket, DHCP_HOST_NAME);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100240 add_lease(packet.chaddr, packet.yiaddr,
241 lease_time_sec,
242 p_host_name,
243 p_host_name ? (unsigned char)p_host_name[OPT_LEN - OPT_DATA] : 0
244 );
245 if (ENABLE_FEATURE_UDHCPD_WRITE_LEASES_EARLY) {
246 /* rewrite the file with leases at every new acceptance */
247 write_leases();
248 }
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100249}
250
Denys Vlasenko0bb35e12010-10-21 12:33:10 +0200251/* NOINLINE: limit stack usage in caller */
252static NOINLINE void send_inform(struct dhcp_packet *oldpacket)
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100253{
254 struct dhcp_packet packet;
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100255
Denys Vlasenkof8fcc182010-04-04 22:36:34 +0200256 /* "If a client has obtained a network address through some other means
257 * (e.g., manual configuration), it may use a DHCPINFORM request message
258 * to obtain other local configuration parameters. Servers receiving a
259 * DHCPINFORM message construct a DHCPACK message with any local
260 * configuration parameters appropriate for the client without:
261 * allocating a new address, checking for an existing binding, filling
262 * in 'yiaddr' or including lease time parameters. The servers SHOULD
263 * unicast the DHCPACK reply to the address given in the 'ciaddr' field
264 * of the DHCPINFORM message.
265 * ...
266 * The server responds to a DHCPINFORM message by sending a DHCPACK
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100267 * message directly to the address given in the 'ciaddr' field
268 * of the DHCPINFORM message. The server MUST NOT send a lease
269 * expiration time to the client and SHOULD NOT fill in 'yiaddr'."
270 */
Denys Vlasenkof8fcc182010-04-04 22:36:34 +0200271//TODO: do a few sanity checks: is ciaddr set?
272//Better yet: is ciaddr == IP source addr?
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100273 init_packet(&packet, oldpacket, DHCPACK);
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100274 add_server_options(&packet);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100275
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100276 send_packet(&packet, /*force_bcast:*/ 0);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100277}
278
279
Mike Frysinger7031f622006-05-08 03:20:50 +0000280/* globals */
Denys Vlasenko6947d2c2009-06-17 13:24:03 +0200281struct dyn_lease *g_leases;
Denis Vlasenkodeabacd2007-09-30 17:55:43 +0000282/* struct server_config_t server_config is in bb_common_bufsiz1 */
Mike Frysinger7031f622006-05-08 03:20:50 +0000283
284
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000285int udhcpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000286int udhcpd_main(int argc UNUSED_PARAM, char **argv)
Mike Frysinger7031f622006-05-08 03:20:50 +0000287{
Denis Vlasenko0416e3d2009-01-01 17:52:09 +0000288 int server_socket = -1, retval, max_sock;
Denys Vlasenko6947d2c2009-06-17 13:24:03 +0200289 uint8_t *state;
Denis Vlasenko42b3dea2007-07-03 15:47:50 +0000290 unsigned timeout_end;
291 unsigned num_ips;
Denis Vlasenko3d17d2b2007-08-14 16:45:29 +0000292 unsigned opt;
Mike Frysinger7031f622006-05-08 03:20:50 +0000293 struct option_set *option;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000294 IF_FEATURE_UDHCP_PORT(char *str_P;)
Mike Frysinger7031f622006-05-08 03:20:50 +0000295
Denis Vlasenkod55fe3e2008-02-04 13:12:16 +0000296#if ENABLE_FEATURE_UDHCP_PORT
297 SERVER_PORT = 67;
298 CLIENT_PORT = 68;
299#endif
300
Denys Vlasenkoac906fa2009-06-17 11:54:52 +0200301#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
302 opt_complementary = "vv";
303#endif
304 opt = getopt32(argv, "fSv"
305 IF_FEATURE_UDHCP_PORT("P:", &str_P)
306#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
307 , &dhcp_verbose
308#endif
309 );
Denis Vlasenko3d17d2b2007-08-14 16:45:29 +0000310 if (!(opt & 1)) { /* no -f */
Denis Vlasenkoc82b5102007-07-01 17:05:57 +0000311 bb_daemonize_or_rexec(0, argv);
Denis Vlasenkoa19e6492009-03-11 14:40:00 +0000312 logmode = LOGMODE_NONE;
Denis Vlasenkoaf1c8432007-03-26 13:22:35 +0000313 }
Mike Frysinger6db13732010-06-04 13:24:50 -0400314 /* update argv after the possible vfork+exec in daemonize */
315 argv += optind;
Denis Vlasenko3d17d2b2007-08-14 16:45:29 +0000316 if (opt & 2) { /* -S */
Denis Vlasenko5e4fda02009-03-08 23:46:48 +0000317 openlog(applet_name, LOG_PID, LOG_DAEMON);
Denis Vlasenkoaf1c8432007-03-26 13:22:35 +0000318 logmode |= LOGMODE_SYSLOG;
319 }
Denis Vlasenkod55fe3e2008-02-04 13:12:16 +0000320#if ENABLE_FEATURE_UDHCP_PORT
Denys Vlasenko406bd142010-03-27 23:24:57 +0100321 if (opt & 8) { /* -P */
Denis Vlasenkod55fe3e2008-02-04 13:12:16 +0000322 SERVER_PORT = xatou16(str_P);
323 CLIENT_PORT = SERVER_PORT + 1;
324 }
325#endif
Denis Vlasenkoaf1c8432007-03-26 13:22:35 +0000326 /* Would rather not do read_config before daemonization -
327 * otherwise NOMMU machines will parse config twice */
Denis Vlasenko9f7b92a2007-08-15 20:03:36 +0000328 read_config(argv[0] ? argv[0] : DHCPD_CONF_FILE);
Mike Frysinger7031f622006-05-08 03:20:50 +0000329
Denis Vlasenko80edead2007-08-02 22:31:05 +0000330 /* Make sure fd 0,1,2 are open */
331 bb_sanitize_stdio();
332 /* Equivalent of doing a fflush after every \n */
333 setlinebuf(stdout);
334
335 /* Create pidfile */
336 write_pidfile(server_config.pidfile);
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100337 /* if (!..) bb_perror_msg("can't create pidfile %s", pidfile); */
Denis Vlasenko80edead2007-08-02 22:31:05 +0000338
Denis Vlasenkodef88982007-10-07 17:06:01 +0000339 bb_info_msg("%s (v"BB_VER") started", applet_name);
Mike Frysinger7031f622006-05-08 03:20:50 +0000340
Denys Vlasenko7724c762010-03-26 09:32:09 +0100341 option = udhcp_find_option(server_config.options, DHCP_LEASE_TIME);
Denys Vlasenko2e7aa922010-03-21 02:22:07 +0100342 server_config.max_lease_sec = DEFAULT_LEASE_TIME;
Denis Vlasenkoaf1c8432007-03-26 13:22:35 +0000343 if (option) {
Denys Vlasenko6947d2c2009-06-17 13:24:03 +0200344 move_from_unaligned32(server_config.max_lease_sec, option->data + OPT_DATA);
345 server_config.max_lease_sec = ntohl(server_config.max_lease_sec);
Denis Vlasenko6e6d3312007-05-03 23:39:35 +0000346 }
Mike Frysinger7031f622006-05-08 03:20:50 +0000347
348 /* Sanity check */
Denis Vlasenkoc82b5102007-07-01 17:05:57 +0000349 num_ips = server_config.end_ip - server_config.start_ip + 1;
Mike Frysinger7031f622006-05-08 03:20:50 +0000350 if (server_config.max_leases > num_ips) {
Denis Vlasenko42b3dea2007-07-03 15:47:50 +0000351 bb_error_msg("max_leases=%u is too big, setting to %u",
352 (unsigned)server_config.max_leases, num_ips);
Mike Frysinger7031f622006-05-08 03:20:50 +0000353 server_config.max_leases = num_ips;
354 }
355
Denys Vlasenko6947d2c2009-06-17 13:24:03 +0200356 g_leases = xzalloc(server_config.max_leases * sizeof(g_leases[0]));
Mike Frysinger7031f622006-05-08 03:20:50 +0000357 read_leases(server_config.lease_file);
358
Denys Vlasenko26918dd2009-06-16 12:04:23 +0200359 if (udhcp_read_interface(server_config.interface,
360 &server_config.ifindex,
361 &server_config.server_nip,
362 server_config.server_mac)
363 ) {
Denis Vlasenko6e6d3312007-05-03 23:39:35 +0000364 retval = 1;
365 goto ret;
366 }
Mike Frysinger7031f622006-05-08 03:20:50 +0000367
Mike Frysinger7031f622006-05-08 03:20:50 +0000368 /* Setup the signal pipe */
369 udhcp_sp_setup();
370
Denis Vlasenko42b3dea2007-07-03 15:47:50 +0000371 timeout_end = monotonic_sec() + server_config.auto_time;
Denis Vlasenko5a3395b2006-11-18 19:51:32 +0000372 while (1) { /* loop until universe collapses */
Denys Vlasenkofa5e2952010-11-28 01:10:51 +0100373 fd_set rfds;
374 struct dhcp_packet packet;
Denis Vlasenko0416e3d2009-01-01 17:52:09 +0000375 int bytes;
376 struct timeval tv;
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100377 uint8_t *server_id_opt;
Denys Vlasenkofa5e2952010-11-28 01:10:51 +0100378 uint8_t *requested_ip_opt;
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100379 uint32_t requested_nip = requested_nip; /* for compiler */
Denys Vlasenkofa5e2952010-11-28 01:10:51 +0100380 uint32_t static_lease_nip;
381 struct dyn_lease *lease, fake_lease;
Mike Frysinger7031f622006-05-08 03:20:50 +0000382
Denis Vlasenkoe2d3ded2006-11-27 23:43:28 +0000383 if (server_socket < 0) {
Denis Vlasenkof1980f62008-09-26 09:34:59 +0000384 server_socket = udhcp_listen_socket(/*INADDR_ANY,*/ SERVER_PORT,
Denis Vlasenko6e6d3312007-05-03 23:39:35 +0000385 server_config.interface);
Denis Vlasenkoe2d3ded2006-11-27 23:43:28 +0000386 }
Mike Frysinger7031f622006-05-08 03:20:50 +0000387
388 max_sock = udhcp_sp_fd_set(&rfds, server_socket);
389 if (server_config.auto_time) {
Denis Vlasenko42b3dea2007-07-03 15:47:50 +0000390 tv.tv_sec = timeout_end - monotonic_sec();
Mike Frysinger7031f622006-05-08 03:20:50 +0000391 tv.tv_usec = 0;
392 }
Denis Vlasenko6e6d3312007-05-03 23:39:35 +0000393 retval = 0;
Mike Frysinger7031f622006-05-08 03:20:50 +0000394 if (!server_config.auto_time || tv.tv_sec > 0) {
395 retval = select(max_sock + 1, &rfds, NULL, NULL,
396 server_config.auto_time ? &tv : NULL);
Denis Vlasenko6e6d3312007-05-03 23:39:35 +0000397 }
Mike Frysinger7031f622006-05-08 03:20:50 +0000398 if (retval == 0) {
399 write_leases();
Denis Vlasenko42b3dea2007-07-03 15:47:50 +0000400 timeout_end = monotonic_sec() + server_config.auto_time;
Mike Frysinger7031f622006-05-08 03:20:50 +0000401 continue;
Denis Vlasenko6e6d3312007-05-03 23:39:35 +0000402 }
403 if (retval < 0 && errno != EINTR) {
Denys Vlasenkoac906fa2009-06-17 11:54:52 +0200404 log1("Error on select");
Mike Frysinger7031f622006-05-08 03:20:50 +0000405 continue;
406 }
407
408 switch (udhcp_sp_read(&rfds)) {
409 case SIGUSR1:
Denys Vlasenko651a2692010-03-23 16:25:17 +0100410 bb_info_msg("Received SIGUSR1");
Mike Frysinger7031f622006-05-08 03:20:50 +0000411 write_leases();
412 /* why not just reset the timeout, eh */
Denis Vlasenko42b3dea2007-07-03 15:47:50 +0000413 timeout_end = monotonic_sec() + server_config.auto_time;
Mike Frysinger7031f622006-05-08 03:20:50 +0000414 continue;
415 case SIGTERM:
Denys Vlasenko651a2692010-03-23 16:25:17 +0100416 bb_info_msg("Received SIGTERM");
Denis Vlasenko6e6d3312007-05-03 23:39:35 +0000417 goto ret0;
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +0200418 case 0: /* no signal: read a packet */
Denis Vlasenko0416e3d2009-01-01 17:52:09 +0000419 break;
420 default: /* signal or error (probably EINTR): back to select */
421 continue;
Mike Frysinger7031f622006-05-08 03:20:50 +0000422 }
423
Denis Vlasenko0416e3d2009-01-01 17:52:09 +0000424 bytes = udhcp_recv_kernel_packet(&packet, server_socket);
Denis Vlasenkoaf1c8432007-03-26 13:22:35 +0000425 if (bytes < 0) {
Denis Vlasenko0416e3d2009-01-01 17:52:09 +0000426 /* bytes can also be -2 ("bad packet data") */
Mike Frysinger7031f622006-05-08 03:20:50 +0000427 if (bytes == -1 && errno != EINTR) {
Denys Vlasenkoac906fa2009-06-17 11:54:52 +0200428 log1("Read error: %s, reopening socket", strerror(errno));
Mike Frysinger7031f622006-05-08 03:20:50 +0000429 close(server_socket);
430 server_socket = -1;
431 }
432 continue;
433 }
Denys Vlasenko31af3d52009-06-17 11:57:09 +0200434 if (packet.hlen != 6) {
435 bb_error_msg("MAC length != 6, ignoring packet");
436 continue;
437 }
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100438 if (packet.op != BOOTREQUEST) {
439 bb_error_msg("not a REQUEST, ignoring packet");
440 continue;
441 }
Denys Vlasenkodde8bdc2010-03-22 14:29:13 +0100442 state = udhcp_get_option(&packet, DHCP_MESSAGE_TYPE);
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100443 if (state == NULL || state[0] < DHCP_MINTYPE || state[0] > DHCP_MAXTYPE) {
444 bb_error_msg("no or bad message type option, ignoring packet");
Mike Frysinger7031f622006-05-08 03:20:50 +0000445 continue;
446 }
447
Denys Vlasenkofa5e2952010-11-28 01:10:51 +0100448 /* Get SERVER_ID if present */
449 server_id_opt = udhcp_get_option(&packet, DHCP_SERVER_ID);
450 if (server_id_opt) {
Denys Vlasenko713d2412010-11-28 21:51:44 +0100451 uint32_t server_id_network_order;
452 move_from_unaligned32(server_id_network_order, server_id_opt);
453 if (server_id_network_order != server_config.server_nip) {
Denys Vlasenkofa5e2952010-11-28 01:10:51 +0100454 /* client talks to somebody else */
455 log1("server ID doesn't match, ignoring");
456 continue;
457 }
458 }
459
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100460 /* Look for a static/dynamic lease */
Denys Vlasenkoa9539872010-03-20 03:49:27 +0100461 static_lease_nip = get_static_nip_by_mac(server_config.static_leases, &packet.chaddr);
462 if (static_lease_nip) {
463 bb_info_msg("Found static lease: %x", static_lease_nip);
Denys Vlasenko6947d2c2009-06-17 13:24:03 +0200464 memcpy(&fake_lease.lease_mac, &packet.chaddr, 6);
Denys Vlasenkoa9539872010-03-20 03:49:27 +0100465 fake_lease.lease_nip = static_lease_nip;
Denys Vlasenko6947d2c2009-06-17 13:24:03 +0200466 fake_lease.expires = 0;
Denys Vlasenko6947d2c2009-06-17 13:24:03 +0200467 lease = &fake_lease;
Denis Vlasenko5a3395b2006-11-18 19:51:32 +0000468 } else {
Denys Vlasenko6947d2c2009-06-17 13:24:03 +0200469 lease = find_lease_by_mac(packet.chaddr);
Mike Frysinger7031f622006-05-08 03:20:50 +0000470 }
471
Denys Vlasenkofa5e2952010-11-28 01:10:51 +0100472 /* Get REQUESTED_IP if present */
473 requested_ip_opt = udhcp_get_option(&packet, DHCP_REQUESTED_IP);
474 if (requested_ip_opt) {
475 move_from_unaligned32(requested_nip, requested_ip_opt);
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100476 }
477
Mike Frysinger7031f622006-05-08 03:20:50 +0000478 switch (state[0]) {
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100479
Mike Frysinger7031f622006-05-08 03:20:50 +0000480 case DHCPDISCOVER:
Denys Vlasenkoac906fa2009-06-17 11:54:52 +0200481 log1("Received DISCOVER");
Mike Frysinger7031f622006-05-08 03:20:50 +0000482
Denys Vlasenkofa5e2952010-11-28 01:10:51 +0100483 send_offer(&packet, static_lease_nip, lease, requested_ip_opt);
Mike Frysinger7031f622006-05-08 03:20:50 +0000484 break;
Denys Vlasenko6947d2c2009-06-17 13:24:03 +0200485
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100486 case DHCPREQUEST:
Denys Vlasenkoac906fa2009-06-17 11:54:52 +0200487 log1("Received REQUEST");
Denys Vlasenko53f72bb2010-03-21 06:46:09 +0100488/* RFC 2131:
Mike Frysinger7031f622006-05-08 03:20:50 +0000489
Denys Vlasenko53f72bb2010-03-21 06:46:09 +0100490o DHCPREQUEST generated during SELECTING state:
491
492 Client inserts the address of the selected server in 'server
493 identifier', 'ciaddr' MUST be zero, 'requested IP address' MUST be
494 filled in with the yiaddr value from the chosen DHCPOFFER.
495
496 Note that the client may choose to collect several DHCPOFFER
497 messages and select the "best" offer. The client indicates its
498 selection by identifying the offering server in the DHCPREQUEST
499 message. If the client receives no acceptable offers, the client
500 may choose to try another DHCPDISCOVER message. Therefore, the
501 servers may not receive a specific DHCPREQUEST from which they can
502 decide whether or not the client has accepted the offer.
503
504o DHCPREQUEST generated during INIT-REBOOT state:
505
506 'server identifier' MUST NOT be filled in, 'requested IP address'
507 option MUST be filled in with client's notion of its previously
508 assigned address. 'ciaddr' MUST be zero. The client is seeking to
509 verify a previously allocated, cached configuration. Server SHOULD
510 send a DHCPNAK message to the client if the 'requested IP address'
511 is incorrect, or is on the wrong network.
512
513 Determining whether a client in the INIT-REBOOT state is on the
514 correct network is done by examining the contents of 'giaddr', the
515 'requested IP address' option, and a database lookup. If the DHCP
516 server detects that the client is on the wrong net (i.e., the
517 result of applying the local subnet mask or remote subnet mask (if
518 'giaddr' is not zero) to 'requested IP address' option value
519 doesn't match reality), then the server SHOULD send a DHCPNAK
520 message to the client.
521
522 If the network is correct, then the DHCP server should check if
523 the client's notion of its IP address is correct. If not, then the
524 server SHOULD send a DHCPNAK message to the client. If the DHCP
525 server has no record of this client, then it MUST remain silent,
526 and MAY output a warning to the network administrator. This
527 behavior is necessary for peaceful coexistence of non-
528 communicating DHCP servers on the same wire.
529
530 If 'giaddr' is 0x0 in the DHCPREQUEST message, the client is on
531 the same subnet as the server. The server MUST broadcast the
532 DHCPNAK message to the 0xffffffff broadcast address because the
533 client may not have a correct network address or subnet mask, and
534 the client may not be answering ARP requests.
535
536 If 'giaddr' is set in the DHCPREQUEST message, the client is on a
537 different subnet. The server MUST set the broadcast bit in the
538 DHCPNAK, so that the relay agent will broadcast the DHCPNAK to the
539 client, because the client may not have a correct network address
540 or subnet mask, and the client may not be answering ARP requests.
541
542o DHCPREQUEST generated during RENEWING state:
543
544 'server identifier' MUST NOT be filled in, 'requested IP address'
545 option MUST NOT be filled in, 'ciaddr' MUST be filled in with
546 client's IP address. In this situation, the client is completely
547 configured, and is trying to extend its lease. This message will
548 be unicast, so no relay agents will be involved in its
549 transmission. Because 'giaddr' is therefore not filled in, the
550 DHCP server will trust the value in 'ciaddr', and use it when
551 replying to the client.
552
553 A client MAY choose to renew or extend its lease prior to T1. The
554 server may choose not to extend the lease (as a policy decision by
555 the network administrator), but should return a DHCPACK message
556 regardless.
557
558o DHCPREQUEST generated during REBINDING state:
559
560 'server identifier' MUST NOT be filled in, 'requested IP address'
561 option MUST NOT be filled in, 'ciaddr' MUST be filled in with
562 client's IP address. In this situation, the client is completely
563 configured, and is trying to extend its lease. This message MUST
564 be broadcast to the 0xffffffff IP broadcast address. The DHCP
565 server SHOULD check 'ciaddr' for correctness before replying to
566 the DHCPREQUEST.
567
568 The DHCPREQUEST from a REBINDING client is intended to accommodate
569 sites that have multiple DHCP servers and a mechanism for
570 maintaining consistency among leases managed by multiple servers.
571 A DHCP server MAY extend a client's lease only if it has local
572 administrative authority to do so.
573*/
Denys Vlasenkofa5e2952010-11-28 01:10:51 +0100574 if (!requested_ip_opt) {
Denys Vlasenko53f72bb2010-03-21 06:46:09 +0100575 requested_nip = packet.ciaddr;
576 if (requested_nip == 0) {
577 log1("no requested IP and no ciaddr, ignoring");
578 break;
579 }
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100580 }
581 if (lease && requested_nip == lease->lease_nip) {
Denys Vlasenko53f72bb2010-03-21 06:46:09 +0100582 /* client requested or configured IP matches the lease.
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100583 * ACK it, and bump lease expiration time. */
584 send_ACK(&packet, lease->lease_nip);
585 break;
586 }
Denys Vlasenko713d2412010-11-28 21:51:44 +0100587 /* No lease for this MAC, or lease IP != requested IP */
588
589 if (server_id_opt /* client is in SELECTING state */
590 || requested_ip_opt /* client is in INIT-REBOOT state */
591 ) {
592 /* "No, we don't have this IP for you" */
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100593 send_NAK(&packet);
Denys Vlasenko713d2412010-11-28 21:51:44 +0100594 } /* else: client is in RENEWING or REBINDING, do not answer */
595
Mike Frysinger7031f622006-05-08 03:20:50 +0000596 break;
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100597
Mike Frysinger7031f622006-05-08 03:20:50 +0000598 case DHCPDECLINE:
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100599 /* RFC 2131:
600 * "If the server receives a DHCPDECLINE message,
601 * the client has discovered through some other means
602 * that the suggested network address is already
603 * in use. The server MUST mark the network address
604 * as not available and SHOULD notify the local
605 * sysadmin of a possible configuration problem."
606 *
607 * SERVER_ID must be present,
608 * REQUESTED_IP must be present,
609 * chaddr must be filled in,
610 * ciaddr must be 0 (we do not check this)
611 */
Denys Vlasenkoac906fa2009-06-17 11:54:52 +0200612 log1("Received DECLINE");
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100613 if (server_id_opt
Denys Vlasenkofa5e2952010-11-28 01:10:51 +0100614 && requested_ip_opt
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100615 && lease /* chaddr matches this lease */
616 && requested_nip == lease->lease_nip
617 ) {
Denys Vlasenko31af3d52009-06-17 11:57:09 +0200618 memset(lease->lease_mac, 0, sizeof(lease->lease_mac));
Denis Vlasenko04158e02009-02-02 10:48:06 +0000619 lease->expires = time(NULL) + server_config.decline_time;
Mike Frysinger7031f622006-05-08 03:20:50 +0000620 }
621 break;
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100622
Mike Frysinger7031f622006-05-08 03:20:50 +0000623 case DHCPRELEASE:
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100624 /* "Upon receipt of a DHCPRELEASE message, the server
625 * marks the network address as not allocated."
626 *
627 * SERVER_ID must be present,
628 * REQUESTED_IP must not be present (we do not check this),
629 * chaddr must be filled in,
630 * ciaddr must be filled in
631 */
Denys Vlasenkoac906fa2009-06-17 11:54:52 +0200632 log1("Received RELEASE");
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100633 if (server_id_opt
634 && lease /* chaddr matches this lease */
635 && packet.ciaddr == lease->lease_nip
636 ) {
Denis Vlasenko04158e02009-02-02 10:48:06 +0000637 lease->expires = time(NULL);
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100638 }
Mike Frysinger7031f622006-05-08 03:20:50 +0000639 break;
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100640
Mike Frysinger7031f622006-05-08 03:20:50 +0000641 case DHCPINFORM:
Denys Vlasenkoac906fa2009-06-17 11:54:52 +0200642 log1("Received INFORM");
Mike Frysinger7031f622006-05-08 03:20:50 +0000643 send_inform(&packet);
644 break;
Mike Frysinger7031f622006-05-08 03:20:50 +0000645 }
646 }
Denis Vlasenko6e6d3312007-05-03 23:39:35 +0000647 ret0:
648 retval = 0;
649 ret:
Denis Vlasenko42b3dea2007-07-03 15:47:50 +0000650 /*if (server_config.pidfile) - server_config.pidfile is never NULL */
Denis Vlasenko6e6d3312007-05-03 23:39:35 +0000651 remove_pidfile(server_config.pidfile);
652 return retval;
Mike Frysinger7031f622006-05-08 03:20:50 +0000653}