blob: 9d6604943e180fc5353ada2703031eeec1294e1b [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 */
Denys Vlasenkof7683cd2016-11-23 18:54:59 +010023//applet:IF_UDHCPD(APPLET(udhcpd, BB_DIR_USR_SBIN, BB_SUID_DROP))
24
25//kbuild:lib-$(CONFIG_UDHCPD) += common.o packet.o signalpipe.o socket.o
26//kbuild:lib-$(CONFIG_UDHCPD) += dhcpd.o arpping.o
27//kbuild:lib-$(CONFIG_FEATURE_UDHCP_RFC3397) += domain_codec.o
Pere Orga5bc8c002011-04-11 03:29:49 +020028
29//usage:#define udhcpd_trivial_usage
Denys Vlasenko7b5d5c12013-03-14 02:18:52 +010030//usage: "[-fS] [-I ADDR]" IF_FEATURE_UDHCP_PORT(" [-P N]") " [CONFFILE]"
Pere Orga5bc8c002011-04-11 03:29:49 +020031//usage:#define udhcpd_full_usage "\n\n"
32//usage: "DHCP server\n"
33//usage: "\n -f Run in foreground"
34//usage: "\n -S Log to syslog too"
Denys Vlasenkoe3f5b732013-03-13 22:27:37 +010035//usage: "\n -I ADDR Local address"
Michel Stam9f412712014-10-30 11:59:04 +010036//usage: "\n -a MSEC Timeout for ARP ping (default 2000)"
Pere Orga5bc8c002011-04-11 03:29:49 +020037//usage: IF_FEATURE_UDHCP_PORT(
38//usage: "\n -P N Use port N (default 67)"
39//usage: )
Denys Vlasenko65c34c52019-05-31 23:39:22 +020040//usage: "\nSignals:"
41//usage: "\n USR1 Update lease file"
Pere Orga5bc8c002011-04-11 03:29:49 +020042
Denys Vlasenko2bf29312016-10-04 00:37:50 +020043#include <netinet/ether.h>
Denis Vlasenkoaf1c8432007-03-26 13:22:35 +000044#include <syslog.h>
Mike Frysinger7031f622006-05-08 03:20:50 +000045#include "common.h"
Denis Vlasenkod55fe3e2008-02-04 13:12:16 +000046#include "dhcpc.h"
Denis Vlasenko5a3395b2006-11-18 19:51:32 +000047#include "dhcpd.h"
Mike Frysinger7031f622006-05-08 03:20:50 +000048
Denys Vlasenkoa85740c2016-10-04 00:51:38 +020049/* globals */
Denys Vlasenko06076492018-02-01 10:41:14 +010050#define g_leases ((struct dyn_lease*)ptr_to_globals)
Denys Vlasenko91755cb2019-05-30 16:23:34 +020051/* struct server_data_t server_data is in bb_common_bufsiz1 */
Denys Vlasenkoa85740c2016-10-04 00:51:38 +020052
Denys Vlasenkoa8408842019-05-16 11:18:49 +020053struct static_lease {
54 struct static_lease *next;
55 uint32_t nip;
56 uint8_t mac[6];
57 uint8_t opt[1];
58};
59
Denys Vlasenkod2ae66c2016-10-04 00:43:14 +020060/* Takes the address of the pointer to the static_leases linked list,
61 * address to a 6 byte mac address,
62 * 4 byte IP address */
63static void add_static_lease(struct static_lease **st_lease_pp,
64 uint8_t *mac,
Denys Vlasenkoa8408842019-05-16 11:18:49 +020065 uint32_t nip,
66 const char *opts)
Denys Vlasenkod2ae66c2016-10-04 00:43:14 +020067{
68 struct static_lease *st_lease;
Denys Vlasenkoa8408842019-05-16 11:18:49 +020069 unsigned optlen;
Denys Vlasenkod2ae66c2016-10-04 00:43:14 +020070
Denys Vlasenko831844c2019-05-21 16:06:34 +020071 optlen = (opts ? 1+1+strnlen(opts, 120) : 0);
72
Denys Vlasenkod2ae66c2016-10-04 00:43:14 +020073 /* Find the tail of the list */
74 while ((st_lease = *st_lease_pp) != NULL) {
75 st_lease_pp = &st_lease->next;
76 }
77
78 /* Add new node */
Denys Vlasenkoa8408842019-05-16 11:18:49 +020079 *st_lease_pp = st_lease = xzalloc(sizeof(*st_lease) + optlen);
Denys Vlasenkod2ae66c2016-10-04 00:43:14 +020080 memcpy(st_lease->mac, mac, 6);
81 st_lease->nip = nip;
82 /*st_lease->next = NULL;*/
Denys Vlasenkoa8408842019-05-16 11:18:49 +020083 if (optlen) {
84 st_lease->opt[OPT_CODE] = DHCP_HOST_NAME;
85 optlen -= 2;
86 st_lease->opt[OPT_LEN] = optlen;
87 memcpy(&st_lease->opt[OPT_DATA], opts, optlen);
88 }
Denys Vlasenko831844c2019-05-21 16:06:34 +020089
90#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 2
91 /* Print out static leases just to check what's going on */
92 if (dhcp_verbose >= 2) {
93 bb_info_msg("static lease: mac:%02x:%02x:%02x:%02x:%02x:%02x nip:%x",
94 st_lease->mac[0], st_lease->mac[1], st_lease->mac[2],
95 st_lease->mac[3], st_lease->mac[4], st_lease->mac[5],
96 st_lease->nip
97 );
98 }
99#endif
Denys Vlasenkod2ae66c2016-10-04 00:43:14 +0200100}
101
102/* Find static lease IP by mac */
Denys Vlasenko15021f32019-05-10 15:55:12 +0200103static uint32_t get_static_nip_by_mac(void *mac)
Denys Vlasenkod2ae66c2016-10-04 00:43:14 +0200104{
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200105 struct static_lease *st_lease = server_data.static_leases;
Denys Vlasenko15021f32019-05-10 15:55:12 +0200106
Denys Vlasenkod2ae66c2016-10-04 00:43:14 +0200107 while (st_lease) {
108 if (memcmp(st_lease->mac, mac, 6) == 0)
109 return st_lease->nip;
110 st_lease = st_lease->next;
111 }
112
113 return 0;
114}
115
Denys Vlasenko15021f32019-05-10 15:55:12 +0200116static int is_nip_reserved_as_static(uint32_t nip)
Denys Vlasenkoa85740c2016-10-04 00:51:38 +0200117{
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200118 struct static_lease *st_lease = server_data.static_leases;
Denys Vlasenko15021f32019-05-10 15:55:12 +0200119
Denys Vlasenkoa85740c2016-10-04 00:51:38 +0200120 while (st_lease) {
121 if (st_lease->nip == nip)
122 return 1;
123 st_lease = st_lease->next;
124 }
125
126 return 0;
127}
128
Denys Vlasenkoa85740c2016-10-04 00:51:38 +0200129/* Find the oldest expired lease, NULL if there are no expired leases */
130static struct dyn_lease *oldest_expired_lease(void)
131{
132 struct dyn_lease *oldest_lease = NULL;
133 leasetime_t oldest_time = time(NULL);
134 unsigned i;
135
136 /* Unexpired leases have g_leases[i].expires >= current time
137 * and therefore can't ever match */
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200138 for (i = 0; i < server_data.max_leases; i++) {
Denys Vlasenkoa85740c2016-10-04 00:51:38 +0200139 if (g_leases[i].expires == 0 /* empty entry */
140 || g_leases[i].expires < oldest_time
141 ) {
142 oldest_time = g_leases[i].expires;
143 oldest_lease = &g_leases[i];
144 }
145 }
146 return oldest_lease;
147}
148
149/* Clear out all leases with matching nonzero chaddr OR yiaddr.
150 * If chaddr == NULL, this is a conflict lease.
151 */
152static void clear_leases(const uint8_t *chaddr, uint32_t yiaddr)
153{
154 unsigned i;
155
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200156 for (i = 0; i < server_data.max_leases; i++) {
Denys Vlasenkoa85740c2016-10-04 00:51:38 +0200157 if ((chaddr && memcmp(g_leases[i].lease_mac, chaddr, 6) == 0)
158 || (yiaddr && g_leases[i].lease_nip == yiaddr)
159 ) {
160 memset(&g_leases[i], 0, sizeof(g_leases[i]));
161 }
162 }
163}
164
165/* Add a lease into the table, clearing out any old ones.
166 * If chaddr == NULL, this is a conflict lease.
167 */
168static struct dyn_lease *add_lease(
169 const uint8_t *chaddr, uint32_t yiaddr,
170 leasetime_t leasetime,
171 const char *hostname, int hostname_len)
172{
173 struct dyn_lease *oldest;
174
175 /* clean out any old ones */
176 clear_leases(chaddr, yiaddr);
177
178 oldest = oldest_expired_lease();
179
180 if (oldest) {
181 memset(oldest, 0, sizeof(*oldest));
182 if (hostname) {
183 char *p;
184
185 hostname_len++; /* include NUL */
186 if (hostname_len > sizeof(oldest->hostname))
187 hostname_len = sizeof(oldest->hostname);
188 p = safe_strncpy(oldest->hostname, hostname, hostname_len);
189 /*
190 * Sanitization (s/bad_char/./g).
191 * The intent is not to allow only "DNS-valid" hostnames,
192 * but merely make dumpleases output safe for shells to use.
193 * We accept "0-9A-Za-z._-", all other chars turn to dots.
194 */
Denys Vlasenko020abc82020-01-14 17:05:48 +0100195 if (*p == '-')
196 *p = '.'; /* defeat "-option" attacks too */
Denys Vlasenkoa85740c2016-10-04 00:51:38 +0200197 while (*p) {
198 if (!isalnum(*p) && *p != '-' && *p != '_')
199 *p = '.';
200 p++;
201 }
202 }
203 if (chaddr)
204 memcpy(oldest->lease_mac, chaddr, 6);
205 oldest->lease_nip = yiaddr;
206 oldest->expires = time(NULL) + leasetime;
207 }
208
209 return oldest;
210}
211
212/* True if a lease has expired */
213static int is_expired_lease(struct dyn_lease *lease)
214{
215 return (lease->expires < (leasetime_t) time(NULL));
216}
217
218/* Find the first lease that matches MAC, NULL if no match */
219static struct dyn_lease *find_lease_by_mac(const uint8_t *mac)
220{
221 unsigned i;
222
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200223 for (i = 0; i < server_data.max_leases; i++)
Denys Vlasenkoa85740c2016-10-04 00:51:38 +0200224 if (memcmp(g_leases[i].lease_mac, mac, 6) == 0)
225 return &g_leases[i];
226
227 return NULL;
228}
229
230/* Find the first lease that matches IP, NULL is no match */
231static struct dyn_lease *find_lease_by_nip(uint32_t nip)
232{
233 unsigned i;
234
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200235 for (i = 0; i < server_data.max_leases; i++)
Denys Vlasenkoa85740c2016-10-04 00:51:38 +0200236 if (g_leases[i].lease_nip == nip)
237 return &g_leases[i];
238
239 return NULL;
240}
241
242/* Check if the IP is taken; if it is, add it to the lease table */
243static int nobody_responds_to_arp(uint32_t nip, const uint8_t *safe_mac, unsigned arpping_ms)
244{
245 struct in_addr temp;
246 int r;
247
248 r = arpping(nip, safe_mac,
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200249 server_data.server_nip,
250 server_data.server_mac,
251 server_data.interface,
Denys Vlasenkoa85740c2016-10-04 00:51:38 +0200252 arpping_ms);
253 if (r)
254 return r;
255
256 temp.s_addr = nip;
James Byrne253c4e72019-04-12 17:01:51 +0000257 bb_info_msg("%s belongs to someone, reserving it for %u seconds",
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200258 inet_ntoa(temp), (unsigned)server_data.conflict_time);
259 add_lease(NULL, nip, server_data.conflict_time, NULL, 0);
Denys Vlasenkoa85740c2016-10-04 00:51:38 +0200260 return 0;
261}
262
263/* Find a new usable (we think) address */
264static uint32_t find_free_or_expired_nip(const uint8_t *safe_mac, unsigned arpping_ms)
265{
266 uint32_t addr;
267 struct dyn_lease *oldest_lease = NULL;
268
269#if ENABLE_FEATURE_UDHCPD_BASE_IP_ON_MAC
270 uint32_t stop;
271 unsigned i, hash;
272
273 /* hash hwaddr: use the SDBM hashing algorithm. Seems to give good
274 * dispersal even with similarly-valued "strings".
275 */
276 hash = 0;
277 for (i = 0; i < 6; i++)
278 hash += safe_mac[i] + (hash << 6) + (hash << 16) - hash;
279
280 /* pick a seed based on hwaddr then iterate until we find a free address. */
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200281 addr = server_data.start_ip
282 + (hash % (1 + server_data.end_ip - server_data.start_ip));
Denys Vlasenkoa85740c2016-10-04 00:51:38 +0200283 stop = addr;
284#else
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200285 addr = server_data.start_ip;
286#define stop (server_data.end_ip + 1)
Denys Vlasenkoa85740c2016-10-04 00:51:38 +0200287#endif
288 do {
289 uint32_t nip;
290 struct dyn_lease *lease;
291
292 /* ie, 192.168.55.0 */
293 if ((addr & 0xff) == 0)
294 goto next_addr;
295 /* ie, 192.168.55.255 */
296 if ((addr & 0xff) == 0xff)
297 goto next_addr;
298 nip = htonl(addr);
299 /* skip our own address */
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200300 if (nip == server_data.server_nip)
Denys Vlasenkoa85740c2016-10-04 00:51:38 +0200301 goto next_addr;
302 /* is this a static lease addr? */
Denys Vlasenko15021f32019-05-10 15:55:12 +0200303 if (is_nip_reserved_as_static(nip))
Denys Vlasenkoa85740c2016-10-04 00:51:38 +0200304 goto next_addr;
305
306 lease = find_lease_by_nip(nip);
307 if (!lease) {
308//TODO: DHCP servers do not always sit on the same subnet as clients: should *ping*, not arp-ping!
309 if (nobody_responds_to_arp(nip, safe_mac, arpping_ms))
310 return nip;
311 } else {
312 if (!oldest_lease || lease->expires < oldest_lease->expires)
313 oldest_lease = lease;
314 }
315
316 next_addr:
317 addr++;
318#if ENABLE_FEATURE_UDHCPD_BASE_IP_ON_MAC
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200319 if (addr > server_data.end_ip)
320 addr = server_data.start_ip;
Denys Vlasenkoa85740c2016-10-04 00:51:38 +0200321#endif
322 } while (addr != stop);
323
324 if (oldest_lease
325 && is_expired_lease(oldest_lease)
326 && nobody_responds_to_arp(oldest_lease->lease_nip, safe_mac, arpping_ms)
327 ) {
328 return oldest_lease->lease_nip;
329 }
330
331 return 0;
332}
333
Denys Vlasenkod2ae66c2016-10-04 00:43:14 +0200334/* On these functions, make sure your datatype matches */
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200335static int FAST_FUNC read_str(const char *line, void *arg)
336{
337 char **dest = arg;
338
339 free(*dest);
340 *dest = xstrdup(line);
341 return 1;
342}
343
344static int FAST_FUNC read_u32(const char *line, void *arg)
345{
346 *(uint32_t*)arg = bb_strtou32(line, NULL, 10);
347 return errno == 0;
348}
349
350static int FAST_FUNC read_staticlease(const char *const_line, void *arg)
351{
352 char *line;
353 char *mac_string;
354 char *ip_string;
Denys Vlasenkoa8408842019-05-16 11:18:49 +0200355 char *opts;
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200356 struct ether_addr mac_bytes; /* it's "struct { uint8_t mac[6]; }" */
357 uint32_t nip;
358
359 /* Read mac */
360 line = (char *) const_line;
361 mac_string = strtok_r(line, " \t", &line);
362 if (!mac_string || !ether_aton_r(mac_string, &mac_bytes))
363 return 0;
364
365 /* Read ip */
366 ip_string = strtok_r(NULL, " \t", &line);
367 if (!ip_string || !udhcp_str2nip(ip_string, &nip))
368 return 0;
369
Denys Vlasenkoa8408842019-05-16 11:18:49 +0200370 opts = strtok_r(NULL, " \t", &line);
371 /* opts might be NULL, that's not an error */
372
373 add_static_lease(arg, (uint8_t*) &mac_bytes, nip, opts);
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200374
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200375 return 1;
376}
377
Denys Vlasenko8c317f02019-05-14 17:26:47 +0200378static int FAST_FUNC read_optset(const char *line, void *arg)
379{
Denys Vlasenko60275972018-05-14 11:06:35 +0200380 return udhcp_str2optset(line, arg,
381 dhcp_optflags, dhcp_option_strings,
382 /*dhcpv6:*/ 0
383 );
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +0200384}
385
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200386struct config_keyword {
387 const char *keyword;
388 int (*handler)(const char *line, void *var) FAST_FUNC;
389 unsigned ofs;
390 const char *def;
391};
392
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200393#define OFS(field) offsetof(struct server_data_t, field)
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200394
395static const struct config_keyword keywords[] = {
Denys Vlasenko3d27d432018-12-27 18:03:20 +0100396 /* keyword handler variable address default */
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200397 {"start" , udhcp_str2nip , OFS(start_ip ), "192.168.0.20"},
398 {"end" , udhcp_str2nip , OFS(end_ip ), "192.168.0.254"},
399 {"interface" , read_str , OFS(interface ), "eth0"},
400 /* Avoid "max_leases value not sane" warning by setting default
401 * to default_end_ip - default_start_ip + 1: */
402 {"max_leases" , read_u32 , OFS(max_leases ), "235"},
403 {"auto_time" , read_u32 , OFS(auto_time ), "7200"},
404 {"decline_time" , read_u32 , OFS(decline_time ), "3600"},
405 {"conflict_time", read_u32 , OFS(conflict_time), "3600"},
406 {"offer_time" , read_u32 , OFS(offer_time ), "60"},
407 {"min_lease" , read_u32 , OFS(min_lease_sec), "60"},
408 {"lease_file" , read_str , OFS(lease_file ), LEASES_FILE},
409 {"pidfile" , read_str , OFS(pidfile ), "/var/run/udhcpd.pid"},
410 {"siaddr" , udhcp_str2nip , OFS(siaddr_nip ), "0.0.0.0"},
411 /* keywords with no defaults must be last! */
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +0200412 {"option" , read_optset , OFS(options ), ""},
413 {"opt" , read_optset , OFS(options ), ""},
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200414 {"notify_file" , read_str , OFS(notify_file ), NULL},
415 {"sname" , read_str , OFS(sname ), NULL},
416 {"boot_file" , read_str , OFS(boot_file ), NULL},
417 {"static_lease" , read_staticlease, OFS(static_leases), ""},
418};
419enum { KWS_WITH_DEFAULTS = ARRAY_SIZE(keywords) - 6 };
420
421static NOINLINE void read_config(const char *file)
422{
423 parser_t *parser;
424 const struct config_keyword *k;
425 unsigned i;
426 char *token[2];
427
428 for (i = 0; i < KWS_WITH_DEFAULTS; i++)
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200429 keywords[i].handler(keywords[i].def, (char*)&server_data + keywords[i].ofs);
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200430
431 parser = config_open(file);
432 while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL)) {
433 for (k = keywords, i = 0; i < ARRAY_SIZE(keywords); k++, i++) {
434 if (strcasecmp(token[0], k->keyword) == 0) {
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200435 if (!k->handler(token[1], (char*)&server_data + k->ofs)) {
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200436 bb_error_msg("can't parse line %u in %s",
437 parser->lineno, file);
438 /* reset back to the default value */
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200439 k->handler(k->def, (char*)&server_data + k->ofs);
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200440 }
441 break;
442 }
443 }
444 }
445 config_close(parser);
446
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200447 server_data.start_ip = ntohl(server_data.start_ip);
448 server_data.end_ip = ntohl(server_data.end_ip);
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200449}
450
451static void write_leases(void)
452{
453 int fd;
454 unsigned i;
455 leasetime_t curr;
456 int64_t written_at;
457
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200458 fd = open_or_warn(server_data.lease_file, O_WRONLY|O_CREAT|O_TRUNC);
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200459 if (fd < 0)
460 return;
461
462 curr = written_at = time(NULL);
463
464 written_at = SWAP_BE64(written_at);
465 full_write(fd, &written_at, sizeof(written_at));
466
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200467 for (i = 0; i < server_data.max_leases; i++) {
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200468 leasetime_t tmp_time;
469
470 if (g_leases[i].lease_nip == 0)
471 continue;
472
473 /* Screw with the time in the struct, for easier writing */
474 tmp_time = g_leases[i].expires;
475
476 g_leases[i].expires -= curr;
477 if ((signed_leasetime_t) g_leases[i].expires < 0)
478 g_leases[i].expires = 0;
479 g_leases[i].expires = htonl(g_leases[i].expires);
480
481 /* No error check. If the file gets truncated,
482 * we lose some leases on restart. Oh well. */
483 full_write(fd, &g_leases[i], sizeof(g_leases[i]));
484
485 /* Then restore it when done */
486 g_leases[i].expires = tmp_time;
487 }
488 close(fd);
489
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200490 if (server_data.notify_file) {
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200491 char *argv[3];
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200492 argv[0] = server_data.notify_file;
493 argv[1] = server_data.lease_file;
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200494 argv[2] = NULL;
495 spawn_and_wait(argv);
496 }
497}
498
499static NOINLINE void read_leases(const char *file)
500{
501 struct dyn_lease lease;
502 int64_t written_at, time_passed;
503 int fd;
504#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
505 unsigned i = 0;
506#endif
507
508 fd = open_or_warn(file, O_RDONLY);
509 if (fd < 0)
510 return;
511
512 if (full_read(fd, &written_at, sizeof(written_at)) != sizeof(written_at))
513 goto ret;
514 written_at = SWAP_BE64(written_at);
515
516 time_passed = time(NULL) - written_at;
517 /* Strange written_at, or lease file from old version of udhcpd
518 * which had no "written_at" field? */
519 if ((uint64_t)time_passed > 12 * 60 * 60)
520 goto ret;
521
522 while (full_read(fd, &lease, sizeof(lease)) == sizeof(lease)) {
523 uint32_t y = ntohl(lease.lease_nip);
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200524 if (y >= server_data.start_ip && y <= server_data.end_ip) {
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200525 signed_leasetime_t expires = ntohl(lease.expires) - (signed_leasetime_t)time_passed;
526 uint32_t static_nip;
527
528 if (expires <= 0)
529 /* We keep expired leases: add_lease() will add
530 * a lease with 0 seconds remaining.
531 * Fewer IP address changes this way for mass reboot scenario.
532 */
533 expires = 0;
534
535 /* Check if there is a different static lease for this IP or MAC */
Denys Vlasenko15021f32019-05-10 15:55:12 +0200536 static_nip = get_static_nip_by_mac(lease.lease_mac);
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200537 if (static_nip) {
538 /* NB: we do not add lease even if static_nip == lease.lease_nip.
539 */
540 continue;
541 }
Denys Vlasenko15021f32019-05-10 15:55:12 +0200542 if (is_nip_reserved_as_static(lease.lease_nip))
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200543 continue;
544
545 /* NB: add_lease takes "relative time", IOW,
546 * lease duration, not lease deadline. */
547 if (add_lease(lease.lease_mac, lease.lease_nip,
548 expires,
549 lease.hostname, sizeof(lease.hostname)
550 ) == 0
551 ) {
552 bb_error_msg("too many leases while loading %s", file);
553 break;
554 }
555#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
556 i++;
557#endif
558 }
559 }
560 log1("read %d leases", i);
561 ret:
562 close(fd);
563}
Mike Frysinger7031f622006-05-08 03:20:50 +0000564
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100565/* Send a packet to a specific mac address and ip address by creating our own ip packet */
566static void send_packet_to_client(struct dhcp_packet *dhcp_pkt, int force_broadcast)
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100567{
568 const uint8_t *chaddr;
569 uint32_t ciaddr;
570
571 // Was:
572 //if (force_broadcast) { /* broadcast */ }
573 //else if (dhcp_pkt->ciaddr) { /* unicast to dhcp_pkt->ciaddr */ }
574 //else if (dhcp_pkt->flags & htons(BROADCAST_FLAG)) { /* broadcast */ }
575 //else { /* unicast to dhcp_pkt->yiaddr */ }
576 // But this is wrong: yiaddr is _our_ idea what client's IP is
577 // (for example, from lease file). Client may not know that,
578 // and may not have UDP socket listening on that IP!
579 // We should never unicast to dhcp_pkt->yiaddr!
580 // dhcp_pkt->ciaddr, OTOH, comes from client's request packet,
581 // and can be used.
582
583 if (force_broadcast
584 || (dhcp_pkt->flags & htons(BROADCAST_FLAG))
Denys Vlasenko53f72bb2010-03-21 06:46:09 +0100585 || dhcp_pkt->ciaddr == 0
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100586 ) {
James Byrne69374872019-07-02 11:35:03 +0200587 log1s("broadcasting packet to client");
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100588 ciaddr = INADDR_BROADCAST;
589 chaddr = MAC_BCAST_ADDR;
590 } else {
James Byrne69374872019-07-02 11:35:03 +0200591 log1s("unicasting packet to client ciaddr");
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100592 ciaddr = dhcp_pkt->ciaddr;
593 chaddr = dhcp_pkt->chaddr;
594 }
595
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100596 udhcp_send_raw_packet(dhcp_pkt,
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200597 /*src*/ server_data.server_nip, SERVER_PORT,
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100598 /*dst*/ ciaddr, CLIENT_PORT, chaddr,
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200599 server_data.ifindex);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100600}
601
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100602/* Send a packet to gateway_nip using the kernel ip stack */
603static void send_packet_to_relay(struct dhcp_packet *dhcp_pkt)
604{
James Byrne69374872019-07-02 11:35:03 +0200605 log1s("forwarding packet to relay");
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100606
607 udhcp_send_kernel_packet(dhcp_pkt,
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200608 server_data.server_nip, SERVER_PORT,
Denys Vlasenko148788e2018-06-21 17:36:22 +0200609 dhcp_pkt->gateway_nip, SERVER_PORT);
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100610}
611
612static void send_packet(struct dhcp_packet *dhcp_pkt, int force_broadcast)
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100613{
614 if (dhcp_pkt->gateway_nip)
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100615 send_packet_to_relay(dhcp_pkt);
616 else
617 send_packet_to_client(dhcp_pkt, force_broadcast);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100618}
619
Denys Vlasenko25393fb2019-05-16 11:27:28 +0200620static void send_packet_verbose(struct dhcp_packet *dhcp_pkt, const char *fmt)
621{
622 struct in_addr addr;
623 addr.s_addr = dhcp_pkt->yiaddr;
624 bb_info_msg(fmt, inet_ntoa(addr));
625 /* send_packet emits error message itself if it detects failure */
626 send_packet(dhcp_pkt, /*force_bcast:*/ 0);
627}
628
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100629static void init_packet(struct dhcp_packet *packet, struct dhcp_packet *oldpacket, char type)
630{
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100631 /* Sets op, htype, hlen, cookie fields
632 * and adds DHCP_MESSAGE_TYPE option */
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100633 udhcp_init_header(packet, type);
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100634
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100635 packet->xid = oldpacket->xid;
636 memcpy(packet->chaddr, oldpacket->chaddr, sizeof(oldpacket->chaddr));
637 packet->flags = oldpacket->flags;
638 packet->gateway_nip = oldpacket->gateway_nip;
639 packet->ciaddr = oldpacket->ciaddr;
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200640 udhcp_add_simple_option(packet, DHCP_SERVER_ID, server_data.server_nip);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100641}
642
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100643/* Fill options field, siaddr_nip, and sname and boot_file fields.
644 * TODO: teach this code to use overload option.
645 */
646static void add_server_options(struct dhcp_packet *packet)
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100647{
Denys Vlasenkoa8408842019-05-16 11:18:49 +0200648 struct option_set *config_opts;
649 uint8_t *client_hostname_opt;
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100650
Denys Vlasenkoa8408842019-05-16 11:18:49 +0200651 client_hostname_opt = NULL;
652 if (packet->yiaddr) { /* if we aren't from send_inform()... */
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200653 struct static_lease *st_lease = server_data.static_leases;
Denys Vlasenkoa8408842019-05-16 11:18:49 +0200654 while (st_lease) {
655 if (st_lease->nip == packet->yiaddr) {
656 if (st_lease->opt[0] != 0)
657 client_hostname_opt = st_lease->opt;
658 break;
659 }
660 st_lease = st_lease->next;
661 }
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100662 }
663
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200664 config_opts = server_data.options;
Denys Vlasenkoa8408842019-05-16 11:18:49 +0200665 while (config_opts) {
666 if (config_opts->data[OPT_CODE] != DHCP_LEASE_TIME) {
667 /* ^^^^
668 * DHCP_LEASE_TIME is already filled, or in case of
669 * send_inform(), should not be filled at all.
670 */
671 if (config_opts->data[OPT_CODE] != DHCP_HOST_NAME
672 || !client_hostname_opt
673 ) {
674 /* Why "!client_hostname_opt":
675 * add hostname only if client has no hostname
676 * on its static lease line.
677 * (Not that "opt hostname HOST"
678 * makes much sense in udhcpd.conf,
679 * that'd give all clients the same hostname,
680 * but it's a valid configuration).
681 */
682 udhcp_add_binary_option(packet, config_opts->data);
683 }
684 }
685 config_opts = config_opts->next;
686 }
687
688 if (client_hostname_opt)
689 udhcp_add_binary_option(packet, client_hostname_opt);
690
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200691 packet->siaddr_nip = server_data.siaddr_nip;
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100692
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200693 if (server_data.sname)
694 strncpy((char*)packet->sname, server_data.sname, sizeof(packet->sname) - 1);
695 if (server_data.boot_file)
696 strncpy((char*)packet->file, server_data.boot_file, sizeof(packet->file) - 1);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100697}
698
699static uint32_t select_lease_time(struct dhcp_packet *packet)
700{
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200701 uint32_t lease_time_sec = server_data.max_lease_sec;
Denys Vlasenko6d3b4bb2018-12-17 18:07:18 +0100702 uint8_t *lease_time_opt = udhcp_get_option32(packet, DHCP_LEASE_TIME);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100703 if (lease_time_opt) {
704 move_from_unaligned32(lease_time_sec, lease_time_opt);
705 lease_time_sec = ntohl(lease_time_sec);
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200706 if (lease_time_sec > server_data.max_lease_sec)
707 lease_time_sec = server_data.max_lease_sec;
708 if (lease_time_sec < server_data.min_lease_sec)
709 lease_time_sec = server_data.min_lease_sec;
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100710 }
711 return lease_time_sec;
712}
713
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100714/* We got a DHCP DISCOVER. Send an OFFER. */
Denys Vlasenko0bb35e12010-10-21 12:33:10 +0200715/* NOINLINE: limit stack usage in caller */
Denys Vlasenkofa5e2952010-11-28 01:10:51 +0100716static NOINLINE void send_offer(struct dhcp_packet *oldpacket,
717 uint32_t static_lease_nip,
718 struct dyn_lease *lease,
Denys Vlasenko84029692019-05-15 13:08:48 +0200719 uint32_t requested_nip,
Michel Stam9f412712014-10-30 11:59:04 +0100720 unsigned arpping_ms)
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100721{
722 struct dhcp_packet packet;
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100723 uint32_t lease_time_sec;
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100724
725 init_packet(&packet, oldpacket, DHCPOFFER);
726
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100727 /* If it is a static lease, use its IP */
728 packet.yiaddr = static_lease_nip;
729 /* Else: */
Denys Vlasenkoa9539872010-03-20 03:49:27 +0100730 if (!static_lease_nip) {
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100731 /* We have no static lease for client's chaddr */
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100732 const char *p_host_name;
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100733
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100734 if (lease) {
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100735 /* We have a dynamic lease for client's chaddr.
736 * Reuse its IP (even if lease is expired).
737 * Note that we ignore requested IP in this case.
738 */
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100739 packet.yiaddr = lease->lease_nip;
740 }
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100741 /* Or: if client has requested an IP */
Denys Vlasenko84029692019-05-15 13:08:48 +0200742 else if (requested_nip != 0
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100743 /* and the IP is in the lease range */
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200744 && ntohl(requested_nip) >= server_data.start_ip
745 && ntohl(requested_nip) <= server_data.end_ip
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100746 /* and */
Denys Vlasenko84029692019-05-15 13:08:48 +0200747 && ( !(lease = find_lease_by_nip(requested_nip)) /* is not already taken */
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100748 || is_expired_lease(lease) /* or is taken, but expired */
749 )
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100750 ) {
Denys Vlasenko84029692019-05-15 13:08:48 +0200751 packet.yiaddr = requested_nip;
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100752 }
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100753 else {
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100754 /* Otherwise, find a free IP */
Michel Stam9f412712014-10-30 11:59:04 +0100755 packet.yiaddr = find_free_or_expired_nip(oldpacket->chaddr, arpping_ms);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100756 }
757
758 if (!packet.yiaddr) {
James Byrne69374872019-07-02 11:35:03 +0200759 bb_simple_error_msg("no free IP addresses. OFFER abandoned");
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100760 return;
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100761 }
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100762 /* Reserve the IP for a short time hoping to get DHCPREQUEST soon */
Denys Vlasenkodde8bdc2010-03-22 14:29:13 +0100763 p_host_name = (const char*) udhcp_get_option(oldpacket, DHCP_HOST_NAME);
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100764 lease = add_lease(packet.chaddr, packet.yiaddr,
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200765 server_data.offer_time,
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100766 p_host_name,
767 p_host_name ? (unsigned char)p_host_name[OPT_LEN - OPT_DATA] : 0
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100768 );
769 if (!lease) {
James Byrne69374872019-07-02 11:35:03 +0200770 bb_simple_error_msg("no free IP addresses. OFFER abandoned");
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100771 return;
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100772 }
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100773 }
774
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100775 lease_time_sec = select_lease_time(oldpacket);
Denys Vlasenko7724c762010-03-26 09:32:09 +0100776 udhcp_add_simple_option(&packet, DHCP_LEASE_TIME, htonl(lease_time_sec));
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100777 add_server_options(&packet);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100778
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100779 /* send_packet emits error message itself if it detects failure */
Denys Vlasenko25393fb2019-05-16 11:27:28 +0200780 send_packet_verbose(&packet, "sending OFFER to %s");
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100781}
782
Denys Vlasenko0bb35e12010-10-21 12:33:10 +0200783/* NOINLINE: limit stack usage in caller */
784static NOINLINE void send_NAK(struct dhcp_packet *oldpacket)
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100785{
786 struct dhcp_packet packet;
787
788 init_packet(&packet, oldpacket, DHCPNAK);
789
Denys Vlasenko16efe192016-03-30 18:44:52 +0200790 log1("sending %s", "NAK");
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100791 send_packet(&packet, /*force_bcast:*/ 1);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100792}
793
Denys Vlasenko0bb35e12010-10-21 12:33:10 +0200794/* NOINLINE: limit stack usage in caller */
795static NOINLINE void send_ACK(struct dhcp_packet *oldpacket, uint32_t yiaddr)
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100796{
797 struct dhcp_packet packet;
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100798 uint32_t lease_time_sec;
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100799 const char *p_host_name;
800
801 init_packet(&packet, oldpacket, DHCPACK);
802 packet.yiaddr = yiaddr;
803
804 lease_time_sec = select_lease_time(oldpacket);
Denys Vlasenko7724c762010-03-26 09:32:09 +0100805 udhcp_add_simple_option(&packet, DHCP_LEASE_TIME, htonl(lease_time_sec));
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100806 add_server_options(&packet);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100807
Denys Vlasenko25393fb2019-05-16 11:27:28 +0200808 send_packet_verbose(&packet, "sending ACK to %s");
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100809
Denys Vlasenkodde8bdc2010-03-22 14:29:13 +0100810 p_host_name = (const char*) udhcp_get_option(oldpacket, DHCP_HOST_NAME);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100811 add_lease(packet.chaddr, packet.yiaddr,
812 lease_time_sec,
813 p_host_name,
814 p_host_name ? (unsigned char)p_host_name[OPT_LEN - OPT_DATA] : 0
815 );
816 if (ENABLE_FEATURE_UDHCPD_WRITE_LEASES_EARLY) {
817 /* rewrite the file with leases at every new acceptance */
818 write_leases();
819 }
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100820}
821
Denys Vlasenko0bb35e12010-10-21 12:33:10 +0200822/* NOINLINE: limit stack usage in caller */
823static NOINLINE void send_inform(struct dhcp_packet *oldpacket)
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100824{
825 struct dhcp_packet packet;
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100826
Denys Vlasenkof8fcc182010-04-04 22:36:34 +0200827 /* "If a client has obtained a network address through some other means
828 * (e.g., manual configuration), it may use a DHCPINFORM request message
829 * to obtain other local configuration parameters. Servers receiving a
830 * DHCPINFORM message construct a DHCPACK message with any local
831 * configuration parameters appropriate for the client without:
832 * allocating a new address, checking for an existing binding, filling
833 * in 'yiaddr' or including lease time parameters. The servers SHOULD
834 * unicast the DHCPACK reply to the address given in the 'ciaddr' field
835 * of the DHCPINFORM message.
836 * ...
837 * The server responds to a DHCPINFORM message by sending a DHCPACK
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100838 * message directly to the address given in the 'ciaddr' field
839 * of the DHCPINFORM message. The server MUST NOT send a lease
840 * expiration time to the client and SHOULD NOT fill in 'yiaddr'."
841 */
Denys Vlasenkof8fcc182010-04-04 22:36:34 +0200842//TODO: do a few sanity checks: is ciaddr set?
843//Better yet: is ciaddr == IP source addr?
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100844 init_packet(&packet, oldpacket, DHCPACK);
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100845 add_server_options(&packet);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100846
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100847 send_packet(&packet, /*force_bcast:*/ 0);
Denys Vlasenko25393fb2019-05-16 11:27:28 +0200848 // or maybe? send_packet_verbose(&packet, "sending ACK to %s");
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100849}
850
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000851int udhcpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000852int udhcpd_main(int argc UNUSED_PARAM, char **argv)
Mike Frysinger7031f622006-05-08 03:20:50 +0000853{
Denys Vlasenko52a515d2017-02-16 23:25:44 +0100854 int server_socket = -1, retval;
Denys Vlasenko6947d2c2009-06-17 13:24:03 +0200855 uint8_t *state;
Denis Vlasenko42b3dea2007-07-03 15:47:50 +0000856 unsigned timeout_end;
857 unsigned num_ips;
Denis Vlasenko3d17d2b2007-08-14 16:45:29 +0000858 unsigned opt;
Mike Frysinger7031f622006-05-08 03:20:50 +0000859 struct option_set *option;
Denys Vlasenkoe3f5b732013-03-13 22:27:37 +0100860 char *str_I = str_I;
Michel Stam9f412712014-10-30 11:59:04 +0100861 const char *str_a = "2000";
862 unsigned arpping_ms;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000863 IF_FEATURE_UDHCP_PORT(char *str_P;)
Mike Frysinger7031f622006-05-08 03:20:50 +0000864
Denys Vlasenkodf70a432016-04-21 18:54:36 +0200865 setup_common_bufsiz();
866
867 IF_FEATURE_UDHCP_PORT(SERVER_PORT = 67;)
868 IF_FEATURE_UDHCP_PORT(CLIENT_PORT = 68;)
Denis Vlasenkod55fe3e2008-02-04 13:12:16 +0000869
Denys Vlasenko65c34c52019-05-31 23:39:22 +0200870 /* Make sure fd 0,1,2 are open */
871 /* Setup the signal pipe on fds 3,4 - must be before openlog() */
872 udhcp_sp_setup();
873
Denys Vlasenko22542ec2017-08-08 21:55:02 +0200874 opt = getopt32(argv, "^"
875 "fSI:va:"IF_FEATURE_UDHCP_PORT("P:")
876 "\0"
Denys Vlasenkoac906fa2009-06-17 11:54:52 +0200877#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
Denys Vlasenko22542ec2017-08-08 21:55:02 +0200878 "vv"
Denys Vlasenkoac906fa2009-06-17 11:54:52 +0200879#endif
Denys Vlasenkoe3f5b732013-03-13 22:27:37 +0100880 , &str_I
Michel Stam9f412712014-10-30 11:59:04 +0100881 , &str_a
Denys Vlasenkoe3f5b732013-03-13 22:27:37 +0100882 IF_FEATURE_UDHCP_PORT(, &str_P)
Leonid Lisovskiy6c9c0a12011-10-18 00:35:47 +0200883 IF_UDHCP_VERBOSE(, &dhcp_verbose)
Denys Vlasenkoac906fa2009-06-17 11:54:52 +0200884 );
Denis Vlasenko3d17d2b2007-08-14 16:45:29 +0000885 if (!(opt & 1)) { /* no -f */
Denis Vlasenkoc82b5102007-07-01 17:05:57 +0000886 bb_daemonize_or_rexec(0, argv);
Denis Vlasenkoa19e6492009-03-11 14:40:00 +0000887 logmode = LOGMODE_NONE;
Denis Vlasenkoaf1c8432007-03-26 13:22:35 +0000888 }
Mike Frysinger6db13732010-06-04 13:24:50 -0400889 /* update argv after the possible vfork+exec in daemonize */
890 argv += optind;
Denis Vlasenko3d17d2b2007-08-14 16:45:29 +0000891 if (opt & 2) { /* -S */
Denis Vlasenko5e4fda02009-03-08 23:46:48 +0000892 openlog(applet_name, LOG_PID, LOG_DAEMON);
Denis Vlasenkoaf1c8432007-03-26 13:22:35 +0000893 logmode |= LOGMODE_SYSLOG;
894 }
Denys Vlasenkoe3f5b732013-03-13 22:27:37 +0100895 if (opt & 4) { /* -I */
896 len_and_sockaddr *lsa = xhost_and_af2sockaddr(str_I, 0, AF_INET);
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200897 server_data.server_nip = lsa->u.sin.sin_addr.s_addr;
Denys Vlasenkoe3f5b732013-03-13 22:27:37 +0100898 free(lsa);
899 }
Denis Vlasenkod55fe3e2008-02-04 13:12:16 +0000900#if ENABLE_FEATURE_UDHCP_PORT
Michel Stam9f412712014-10-30 11:59:04 +0100901 if (opt & 32) { /* -P */
Denis Vlasenkod55fe3e2008-02-04 13:12:16 +0000902 SERVER_PORT = xatou16(str_P);
903 CLIENT_PORT = SERVER_PORT + 1;
904 }
905#endif
Michel Stam9f412712014-10-30 11:59:04 +0100906 arpping_ms = xatou(str_a);
907
Denis Vlasenkoaf1c8432007-03-26 13:22:35 +0000908 /* Would rather not do read_config before daemonization -
909 * otherwise NOMMU machines will parse config twice */
Denis Vlasenko9f7b92a2007-08-15 20:03:36 +0000910 read_config(argv[0] ? argv[0] : DHCPD_CONF_FILE);
Denys Vlasenko3d306bf2018-03-11 11:34:44 +0100911 /* prevent poll timeout overflow */
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200912 if (server_data.auto_time > INT_MAX / 1000)
913 server_data.auto_time = INT_MAX / 1000;
Mike Frysinger7031f622006-05-08 03:20:50 +0000914
Denis Vlasenko80edead2007-08-02 22:31:05 +0000915 /* Create pidfile */
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200916 write_pidfile(server_data.pidfile);
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100917 /* if (!..) bb_perror_msg("can't create pidfile %s", pidfile); */
Denis Vlasenko80edead2007-08-02 22:31:05 +0000918
James Byrne69374872019-07-02 11:35:03 +0200919 bb_simple_info_msg("started, v"BB_VER);
Mike Frysinger7031f622006-05-08 03:20:50 +0000920
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200921 option = udhcp_find_option(server_data.options, DHCP_LEASE_TIME);
922 server_data.max_lease_sec = DEFAULT_LEASE_TIME;
Denis Vlasenkoaf1c8432007-03-26 13:22:35 +0000923 if (option) {
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200924 move_from_unaligned32(server_data.max_lease_sec, option->data + OPT_DATA);
925 server_data.max_lease_sec = ntohl(server_data.max_lease_sec);
Denis Vlasenko6e6d3312007-05-03 23:39:35 +0000926 }
Mike Frysinger7031f622006-05-08 03:20:50 +0000927
928 /* Sanity check */
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200929 num_ips = server_data.end_ip - server_data.start_ip + 1;
930 if (server_data.max_leases > num_ips) {
Denis Vlasenko42b3dea2007-07-03 15:47:50 +0000931 bb_error_msg("max_leases=%u is too big, setting to %u",
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200932 (unsigned)server_data.max_leases, num_ips);
933 server_data.max_leases = num_ips;
Mike Frysinger7031f622006-05-08 03:20:50 +0000934 }
935
Denys Vlasenko06076492018-02-01 10:41:14 +0100936 /* this sets g_leases */
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200937 SET_PTR_TO_GLOBALS(xzalloc(server_data.max_leases * sizeof(g_leases[0])));
Denys Vlasenko06076492018-02-01 10:41:14 +0100938
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200939 read_leases(server_data.lease_file);
Mike Frysinger7031f622006-05-08 03:20:50 +0000940
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200941 if (udhcp_read_interface(server_data.interface,
942 &server_data.ifindex,
943 (server_data.server_nip == 0 ? &server_data.server_nip : NULL),
944 server_data.server_mac)
Denys Vlasenko26918dd2009-06-16 12:04:23 +0200945 ) {
Denis Vlasenko6e6d3312007-05-03 23:39:35 +0000946 retval = 1;
947 goto ret;
948 }
Mike Frysinger7031f622006-05-08 03:20:50 +0000949
Denys Vlasenko71045cc2012-07-24 17:21:26 +0200950 continue_with_autotime:
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200951 timeout_end = monotonic_sec() + server_data.auto_time;
Denis Vlasenko5a3395b2006-11-18 19:51:32 +0000952 while (1) { /* loop until universe collapses */
Denys Vlasenko52a515d2017-02-16 23:25:44 +0100953 struct pollfd pfds[2];
Denys Vlasenkofa5e2952010-11-28 01:10:51 +0100954 struct dhcp_packet packet;
Denis Vlasenko0416e3d2009-01-01 17:52:09 +0000955 int bytes;
Denys Vlasenko52a515d2017-02-16 23:25:44 +0100956 int tv;
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100957 uint8_t *server_id_opt;
Denys Vlasenkofa5e2952010-11-28 01:10:51 +0100958 uint8_t *requested_ip_opt;
Denys Vlasenko84029692019-05-15 13:08:48 +0200959 uint32_t requested_nip;
Denys Vlasenkofa5e2952010-11-28 01:10:51 +0100960 uint32_t static_lease_nip;
961 struct dyn_lease *lease, fake_lease;
Mike Frysinger7031f622006-05-08 03:20:50 +0000962
Denis Vlasenkoe2d3ded2006-11-27 23:43:28 +0000963 if (server_socket < 0) {
Denis Vlasenkof1980f62008-09-26 09:34:59 +0000964 server_socket = udhcp_listen_socket(/*INADDR_ANY,*/ SERVER_PORT,
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200965 server_data.interface);
Denis Vlasenkoe2d3ded2006-11-27 23:43:28 +0000966 }
Mike Frysinger7031f622006-05-08 03:20:50 +0000967
Denys Vlasenko52a515d2017-02-16 23:25:44 +0100968 udhcp_sp_fd_set(pfds, server_socket);
Denys Vlasenko3d306bf2018-03-11 11:34:44 +0100969
970 new_tv:
971 tv = -1;
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200972 if (server_data.auto_time) {
Denys Vlasenko3d306bf2018-03-11 11:34:44 +0100973 tv = timeout_end - monotonic_sec();
974 if (tv <= 0) {
975 write_leases:
Denys Vlasenko3293bc12018-03-10 19:01:48 +0100976 write_leases();
977 goto continue_with_autotime;
978 }
Denys Vlasenko3d306bf2018-03-11 11:34:44 +0100979 tv *= 1000;
980 }
981
982 /* Block here waiting for either signal or packet */
983 retval = poll(pfds, 2, tv);
984 if (retval <= 0) {
985 if (retval == 0)
986 goto write_leases;
987 if (errno == EINTR)
988 goto new_tv;
Denys Vlasenko3293bc12018-03-10 19:01:48 +0100989 /* < 0 and not EINTR: should not happen */
James Byrne69374872019-07-02 11:35:03 +0200990 bb_simple_perror_msg_and_die("poll");
Mike Frysinger7031f622006-05-08 03:20:50 +0000991 }
992
Denys Vlasenko3293bc12018-03-10 19:01:48 +0100993 if (pfds[0].revents) switch (udhcp_sp_read()) {
Mike Frysinger7031f622006-05-08 03:20:50 +0000994 case SIGUSR1:
James Byrne253c4e72019-04-12 17:01:51 +0000995 bb_info_msg("received %s", "SIGUSR1");
Mike Frysinger7031f622006-05-08 03:20:50 +0000996 write_leases();
997 /* why not just reset the timeout, eh */
Denys Vlasenko71045cc2012-07-24 17:21:26 +0200998 goto continue_with_autotime;
Mike Frysinger7031f622006-05-08 03:20:50 +0000999 case SIGTERM:
James Byrne253c4e72019-04-12 17:01:51 +00001000 bb_info_msg("received %s", "SIGTERM");
Denys Vlasenko71045cc2012-07-24 17:21:26 +02001001 write_leases();
Denis Vlasenko6e6d3312007-05-03 23:39:35 +00001002 goto ret0;
Mike Frysinger7031f622006-05-08 03:20:50 +00001003 }
1004
Denys Vlasenko3293bc12018-03-10 19:01:48 +01001005 /* Is it a packet? */
1006 if (!pfds[1].revents)
1007 continue; /* no */
1008
1009 /* Note: we do not block here, we block on poll() instead.
1010 * Blocking here would prevent SIGTERM from working:
1011 * socket read inside this call is restarted on caught signals.
1012 */
Denis Vlasenko0416e3d2009-01-01 17:52:09 +00001013 bytes = udhcp_recv_kernel_packet(&packet, server_socket);
Denis Vlasenkoaf1c8432007-03-26 13:22:35 +00001014 if (bytes < 0) {
Denis Vlasenko0416e3d2009-01-01 17:52:09 +00001015 /* bytes can also be -2 ("bad packet data") */
Mike Frysinger7031f622006-05-08 03:20:50 +00001016 if (bytes == -1 && errno != EINTR) {
Denys Vlasenko6f97b302017-09-29 18:17:25 +02001017 log1("read error: "STRERROR_FMT", reopening socket" STRERROR_ERRNO);
Mike Frysinger7031f622006-05-08 03:20:50 +00001018 close(server_socket);
1019 server_socket = -1;
1020 }
1021 continue;
1022 }
Denys Vlasenko31af3d52009-06-17 11:57:09 +02001023 if (packet.hlen != 6) {
James Byrne69374872019-07-02 11:35:03 +02001024 bb_info_msg("MAC length != 6%s", ", ignoring packet");
Denys Vlasenko31af3d52009-06-17 11:57:09 +02001025 continue;
1026 }
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001027 if (packet.op != BOOTREQUEST) {
James Byrne69374872019-07-02 11:35:03 +02001028 bb_info_msg("not a REQUEST%s", ", ignoring packet");
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001029 continue;
1030 }
Denys Vlasenkodde8bdc2010-03-22 14:29:13 +01001031 state = udhcp_get_option(&packet, DHCP_MESSAGE_TYPE);
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001032 if (state == NULL || state[0] < DHCP_MINTYPE || state[0] > DHCP_MAXTYPE) {
James Byrne69374872019-07-02 11:35:03 +02001033 bb_info_msg("no or bad message type option%s", ", ignoring packet");
Mike Frysinger7031f622006-05-08 03:20:50 +00001034 continue;
1035 }
1036
Denys Vlasenkofa5e2952010-11-28 01:10:51 +01001037 /* Get SERVER_ID if present */
Denys Vlasenko6d3b4bb2018-12-17 18:07:18 +01001038 server_id_opt = udhcp_get_option32(&packet, DHCP_SERVER_ID);
Denys Vlasenkofa5e2952010-11-28 01:10:51 +01001039 if (server_id_opt) {
Denys Vlasenko713d2412010-11-28 21:51:44 +01001040 uint32_t server_id_network_order;
1041 move_from_unaligned32(server_id_network_order, server_id_opt);
Denys Vlasenko91755cb2019-05-30 16:23:34 +02001042 if (server_id_network_order != server_data.server_nip) {
Denys Vlasenkofa5e2952010-11-28 01:10:51 +01001043 /* client talks to somebody else */
James Byrne69374872019-07-02 11:35:03 +02001044 log1("server ID doesn't match%s", ", ignoring");
Denys Vlasenkofa5e2952010-11-28 01:10:51 +01001045 continue;
1046 }
1047 }
1048
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001049 /* Look for a static/dynamic lease */
Denys Vlasenko15021f32019-05-10 15:55:12 +02001050 static_lease_nip = get_static_nip_by_mac(&packet.chaddr);
Denys Vlasenkoa9539872010-03-20 03:49:27 +01001051 if (static_lease_nip) {
James Byrne253c4e72019-04-12 17:01:51 +00001052 bb_info_msg("found static lease: %x", static_lease_nip);
Denys Vlasenko6947d2c2009-06-17 13:24:03 +02001053 memcpy(&fake_lease.lease_mac, &packet.chaddr, 6);
Denys Vlasenkoa9539872010-03-20 03:49:27 +01001054 fake_lease.lease_nip = static_lease_nip;
Denys Vlasenko6947d2c2009-06-17 13:24:03 +02001055 fake_lease.expires = 0;
Denys Vlasenko6947d2c2009-06-17 13:24:03 +02001056 lease = &fake_lease;
Denis Vlasenko5a3395b2006-11-18 19:51:32 +00001057 } else {
Denys Vlasenko6947d2c2009-06-17 13:24:03 +02001058 lease = find_lease_by_mac(packet.chaddr);
Mike Frysinger7031f622006-05-08 03:20:50 +00001059 }
1060
Denys Vlasenkofa5e2952010-11-28 01:10:51 +01001061 /* Get REQUESTED_IP if present */
Denys Vlasenko84029692019-05-15 13:08:48 +02001062 requested_nip = 0;
Denys Vlasenko6d3b4bb2018-12-17 18:07:18 +01001063 requested_ip_opt = udhcp_get_option32(&packet, DHCP_REQUESTED_IP);
Denys Vlasenkofa5e2952010-11-28 01:10:51 +01001064 if (requested_ip_opt) {
1065 move_from_unaligned32(requested_nip, requested_ip_opt);
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001066 }
1067
Mike Frysinger7031f622006-05-08 03:20:50 +00001068 switch (state[0]) {
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001069
Mike Frysinger7031f622006-05-08 03:20:50 +00001070 case DHCPDISCOVER:
Denys Vlasenko8f2e99c2016-03-30 18:41:23 +02001071 log1("received %s", "DISCOVER");
Mike Frysinger7031f622006-05-08 03:20:50 +00001072
Denys Vlasenko84029692019-05-15 13:08:48 +02001073 send_offer(&packet, static_lease_nip, lease, requested_nip, arpping_ms);
Mike Frysinger7031f622006-05-08 03:20:50 +00001074 break;
Denys Vlasenko6947d2c2009-06-17 13:24:03 +02001075
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001076 case DHCPREQUEST:
Denys Vlasenko8f2e99c2016-03-30 18:41:23 +02001077 log1("received %s", "REQUEST");
Denys Vlasenko53f72bb2010-03-21 06:46:09 +01001078/* RFC 2131:
Mike Frysinger7031f622006-05-08 03:20:50 +00001079
Denys Vlasenko53f72bb2010-03-21 06:46:09 +01001080o DHCPREQUEST generated during SELECTING state:
1081
1082 Client inserts the address of the selected server in 'server
1083 identifier', 'ciaddr' MUST be zero, 'requested IP address' MUST be
1084 filled in with the yiaddr value from the chosen DHCPOFFER.
1085
1086 Note that the client may choose to collect several DHCPOFFER
1087 messages and select the "best" offer. The client indicates its
1088 selection by identifying the offering server in the DHCPREQUEST
1089 message. If the client receives no acceptable offers, the client
1090 may choose to try another DHCPDISCOVER message. Therefore, the
1091 servers may not receive a specific DHCPREQUEST from which they can
1092 decide whether or not the client has accepted the offer.
1093
1094o DHCPREQUEST generated during INIT-REBOOT state:
1095
1096 'server identifier' MUST NOT be filled in, 'requested IP address'
1097 option MUST be filled in with client's notion of its previously
1098 assigned address. 'ciaddr' MUST be zero. The client is seeking to
1099 verify a previously allocated, cached configuration. Server SHOULD
1100 send a DHCPNAK message to the client if the 'requested IP address'
1101 is incorrect, or is on the wrong network.
1102
1103 Determining whether a client in the INIT-REBOOT state is on the
1104 correct network is done by examining the contents of 'giaddr', the
1105 'requested IP address' option, and a database lookup. If the DHCP
1106 server detects that the client is on the wrong net (i.e., the
1107 result of applying the local subnet mask or remote subnet mask (if
1108 'giaddr' is not zero) to 'requested IP address' option value
1109 doesn't match reality), then the server SHOULD send a DHCPNAK
1110 message to the client.
1111
1112 If the network is correct, then the DHCP server should check if
1113 the client's notion of its IP address is correct. If not, then the
1114 server SHOULD send a DHCPNAK message to the client. If the DHCP
1115 server has no record of this client, then it MUST remain silent,
1116 and MAY output a warning to the network administrator. This
1117 behavior is necessary for peaceful coexistence of non-
1118 communicating DHCP servers on the same wire.
1119
1120 If 'giaddr' is 0x0 in the DHCPREQUEST message, the client is on
1121 the same subnet as the server. The server MUST broadcast the
1122 DHCPNAK message to the 0xffffffff broadcast address because the
1123 client may not have a correct network address or subnet mask, and
1124 the client may not be answering ARP requests.
1125
1126 If 'giaddr' is set in the DHCPREQUEST message, the client is on a
1127 different subnet. The server MUST set the broadcast bit in the
1128 DHCPNAK, so that the relay agent will broadcast the DHCPNAK to the
1129 client, because the client may not have a correct network address
1130 or subnet mask, and the client may not be answering ARP requests.
1131
1132o DHCPREQUEST generated during RENEWING state:
1133
1134 'server identifier' MUST NOT be filled in, 'requested IP address'
1135 option MUST NOT be filled in, 'ciaddr' MUST be filled in with
1136 client's IP address. In this situation, the client is completely
1137 configured, and is trying to extend its lease. This message will
1138 be unicast, so no relay agents will be involved in its
1139 transmission. Because 'giaddr' is therefore not filled in, the
1140 DHCP server will trust the value in 'ciaddr', and use it when
1141 replying to the client.
1142
1143 A client MAY choose to renew or extend its lease prior to T1. The
1144 server may choose not to extend the lease (as a policy decision by
1145 the network administrator), but should return a DHCPACK message
1146 regardless.
1147
1148o DHCPREQUEST generated during REBINDING state:
1149
1150 'server identifier' MUST NOT be filled in, 'requested IP address'
1151 option MUST NOT be filled in, 'ciaddr' MUST be filled in with
1152 client's IP address. In this situation, the client is completely
1153 configured, and is trying to extend its lease. This message MUST
1154 be broadcast to the 0xffffffff IP broadcast address. The DHCP
1155 server SHOULD check 'ciaddr' for correctness before replying to
1156 the DHCPREQUEST.
1157
1158 The DHCPREQUEST from a REBINDING client is intended to accommodate
1159 sites that have multiple DHCP servers and a mechanism for
1160 maintaining consistency among leases managed by multiple servers.
1161 A DHCP server MAY extend a client's lease only if it has local
1162 administrative authority to do so.
1163*/
Denys Vlasenkofa5e2952010-11-28 01:10:51 +01001164 if (!requested_ip_opt) {
Denys Vlasenko53f72bb2010-03-21 06:46:09 +01001165 requested_nip = packet.ciaddr;
1166 if (requested_nip == 0) {
James Byrne69374872019-07-02 11:35:03 +02001167 log1("no requested IP and no ciaddr%s", ", ignoring");
Denys Vlasenko53f72bb2010-03-21 06:46:09 +01001168 break;
1169 }
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001170 }
1171 if (lease && requested_nip == lease->lease_nip) {
Denys Vlasenko53f72bb2010-03-21 06:46:09 +01001172 /* client requested or configured IP matches the lease.
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001173 * ACK it, and bump lease expiration time. */
1174 send_ACK(&packet, lease->lease_nip);
1175 break;
1176 }
Denys Vlasenko713d2412010-11-28 21:51:44 +01001177 /* No lease for this MAC, or lease IP != requested IP */
1178
1179 if (server_id_opt /* client is in SELECTING state */
1180 || requested_ip_opt /* client is in INIT-REBOOT state */
1181 ) {
1182 /* "No, we don't have this IP for you" */
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001183 send_NAK(&packet);
Denys Vlasenko713d2412010-11-28 21:51:44 +01001184 } /* else: client is in RENEWING or REBINDING, do not answer */
1185
Mike Frysinger7031f622006-05-08 03:20:50 +00001186 break;
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001187
Mike Frysinger7031f622006-05-08 03:20:50 +00001188 case DHCPDECLINE:
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001189 /* RFC 2131:
1190 * "If the server receives a DHCPDECLINE message,
1191 * the client has discovered through some other means
1192 * that the suggested network address is already
1193 * in use. The server MUST mark the network address
1194 * as not available and SHOULD notify the local
1195 * sysadmin of a possible configuration problem."
1196 *
1197 * SERVER_ID must be present,
1198 * REQUESTED_IP must be present,
1199 * chaddr must be filled in,
1200 * ciaddr must be 0 (we do not check this)
1201 */
Denys Vlasenko8f2e99c2016-03-30 18:41:23 +02001202 log1("received %s", "DECLINE");
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001203 if (server_id_opt
Denys Vlasenkofa5e2952010-11-28 01:10:51 +01001204 && requested_ip_opt
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001205 && lease /* chaddr matches this lease */
1206 && requested_nip == lease->lease_nip
1207 ) {
Denys Vlasenko31af3d52009-06-17 11:57:09 +02001208 memset(lease->lease_mac, 0, sizeof(lease->lease_mac));
Denys Vlasenko91755cb2019-05-30 16:23:34 +02001209 lease->expires = time(NULL) + server_data.decline_time;
Mike Frysinger7031f622006-05-08 03:20:50 +00001210 }
1211 break;
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001212
Mike Frysinger7031f622006-05-08 03:20:50 +00001213 case DHCPRELEASE:
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001214 /* "Upon receipt of a DHCPRELEASE message, the server
1215 * marks the network address as not allocated."
1216 *
1217 * SERVER_ID must be present,
1218 * REQUESTED_IP must not be present (we do not check this),
1219 * chaddr must be filled in,
1220 * ciaddr must be filled in
1221 */
Denys Vlasenko8f2e99c2016-03-30 18:41:23 +02001222 log1("received %s", "RELEASE");
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001223 if (server_id_opt
1224 && lease /* chaddr matches this lease */
1225 && packet.ciaddr == lease->lease_nip
1226 ) {
Denis Vlasenko04158e02009-02-02 10:48:06 +00001227 lease->expires = time(NULL);
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001228 }
Mike Frysinger7031f622006-05-08 03:20:50 +00001229 break;
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001230
Mike Frysinger7031f622006-05-08 03:20:50 +00001231 case DHCPINFORM:
Denys Vlasenko8f2e99c2016-03-30 18:41:23 +02001232 log1("received %s", "INFORM");
Mike Frysinger7031f622006-05-08 03:20:50 +00001233 send_inform(&packet);
1234 break;
Mike Frysinger7031f622006-05-08 03:20:50 +00001235 }
1236 }
Denis Vlasenko6e6d3312007-05-03 23:39:35 +00001237 ret0:
1238 retval = 0;
1239 ret:
Denys Vlasenko91755cb2019-05-30 16:23:34 +02001240 /*if (server_data.pidfile) - server_data.pidfile is never NULL */
1241 remove_pidfile(server_data.pidfile);
Denis Vlasenko6e6d3312007-05-03 23:39:35 +00001242 return retval;
Mike Frysinger7031f622006-05-08 03:20:50 +00001243}