blob: 260130507d6851473c9bb2a473cb50af3b12329c [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 Vlasenko8cc3a742020-12-18 22:51:46 +010030//usage: "[-fS] [-I ADDR]" IF_FEATURE_UDHCP_PORT(" [-P PORT]") " [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(
Denys Vlasenko8cc3a742020-12-18 22:51:46 +010038//usage: "\n -P PORT Use PORT (default 67)"
Pere Orga5bc8c002011-04-11 03:29:49 +020039//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
Norbert Lange79bd7c32020-02-14 22:16:11 +010049#if ENABLE_PID_FILE_PATH
Norbert Lange05faa612020-02-14 22:16:10 +010050#define PID_FILE_PATH CONFIG_PID_FILE_PATH
51#else
52#define PID_FILE_PATH "/var/run"
53#endif
54
Denys Vlasenkoa85740c2016-10-04 00:51:38 +020055/* globals */
Denys Vlasenko06076492018-02-01 10:41:14 +010056#define g_leases ((struct dyn_lease*)ptr_to_globals)
Denys Vlasenko91755cb2019-05-30 16:23:34 +020057/* struct server_data_t server_data is in bb_common_bufsiz1 */
Denys Vlasenkoa85740c2016-10-04 00:51:38 +020058
Denys Vlasenkoa8408842019-05-16 11:18:49 +020059struct static_lease {
60 struct static_lease *next;
61 uint32_t nip;
62 uint8_t mac[6];
63 uint8_t opt[1];
64};
65
Denys Vlasenkod2ae66c2016-10-04 00:43:14 +020066/* Takes the address of the pointer to the static_leases linked list,
67 * address to a 6 byte mac address,
68 * 4 byte IP address */
69static void add_static_lease(struct static_lease **st_lease_pp,
70 uint8_t *mac,
Denys Vlasenkoa8408842019-05-16 11:18:49 +020071 uint32_t nip,
72 const char *opts)
Denys Vlasenkod2ae66c2016-10-04 00:43:14 +020073{
74 struct static_lease *st_lease;
Denys Vlasenkoa8408842019-05-16 11:18:49 +020075 unsigned optlen;
Denys Vlasenkod2ae66c2016-10-04 00:43:14 +020076
Denys Vlasenko831844c2019-05-21 16:06:34 +020077 optlen = (opts ? 1+1+strnlen(opts, 120) : 0);
78
Denys Vlasenkod2ae66c2016-10-04 00:43:14 +020079 /* Find the tail of the list */
80 while ((st_lease = *st_lease_pp) != NULL) {
81 st_lease_pp = &st_lease->next;
82 }
83
84 /* Add new node */
Denys Vlasenkoa8408842019-05-16 11:18:49 +020085 *st_lease_pp = st_lease = xzalloc(sizeof(*st_lease) + optlen);
Denys Vlasenkod2ae66c2016-10-04 00:43:14 +020086 memcpy(st_lease->mac, mac, 6);
87 st_lease->nip = nip;
88 /*st_lease->next = NULL;*/
Denys Vlasenkoa8408842019-05-16 11:18:49 +020089 if (optlen) {
90 st_lease->opt[OPT_CODE] = DHCP_HOST_NAME;
91 optlen -= 2;
92 st_lease->opt[OPT_LEN] = optlen;
93 memcpy(&st_lease->opt[OPT_DATA], opts, optlen);
94 }
Denys Vlasenko831844c2019-05-21 16:06:34 +020095
96#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 2
97 /* Print out static leases just to check what's going on */
98 if (dhcp_verbose >= 2) {
99 bb_info_msg("static lease: mac:%02x:%02x:%02x:%02x:%02x:%02x nip:%x",
100 st_lease->mac[0], st_lease->mac[1], st_lease->mac[2],
101 st_lease->mac[3], st_lease->mac[4], st_lease->mac[5],
102 st_lease->nip
103 );
104 }
105#endif
Denys Vlasenkod2ae66c2016-10-04 00:43:14 +0200106}
107
108/* Find static lease IP by mac */
Denys Vlasenko15021f32019-05-10 15:55:12 +0200109static uint32_t get_static_nip_by_mac(void *mac)
Denys Vlasenkod2ae66c2016-10-04 00:43:14 +0200110{
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200111 struct static_lease *st_lease = server_data.static_leases;
Denys Vlasenko15021f32019-05-10 15:55:12 +0200112
Denys Vlasenkod2ae66c2016-10-04 00:43:14 +0200113 while (st_lease) {
114 if (memcmp(st_lease->mac, mac, 6) == 0)
115 return st_lease->nip;
116 st_lease = st_lease->next;
117 }
118
119 return 0;
120}
121
Denys Vlasenko15021f32019-05-10 15:55:12 +0200122static int is_nip_reserved_as_static(uint32_t nip)
Denys Vlasenkoa85740c2016-10-04 00:51:38 +0200123{
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200124 struct static_lease *st_lease = server_data.static_leases;
Denys Vlasenko15021f32019-05-10 15:55:12 +0200125
Denys Vlasenkoa85740c2016-10-04 00:51:38 +0200126 while (st_lease) {
127 if (st_lease->nip == nip)
128 return 1;
129 st_lease = st_lease->next;
130 }
131
132 return 0;
133}
134
Denys Vlasenkoa85740c2016-10-04 00:51:38 +0200135/* Find the oldest expired lease, NULL if there are no expired leases */
136static struct dyn_lease *oldest_expired_lease(void)
137{
138 struct dyn_lease *oldest_lease = NULL;
139 leasetime_t oldest_time = time(NULL);
140 unsigned i;
141
142 /* Unexpired leases have g_leases[i].expires >= current time
143 * and therefore can't ever match */
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200144 for (i = 0; i < server_data.max_leases; i++) {
Denys Vlasenkoa85740c2016-10-04 00:51:38 +0200145 if (g_leases[i].expires == 0 /* empty entry */
146 || g_leases[i].expires < oldest_time
147 ) {
148 oldest_time = g_leases[i].expires;
149 oldest_lease = &g_leases[i];
150 }
151 }
152 return oldest_lease;
153}
154
155/* Clear out all leases with matching nonzero chaddr OR yiaddr.
156 * If chaddr == NULL, this is a conflict lease.
157 */
158static void clear_leases(const uint8_t *chaddr, uint32_t yiaddr)
159{
160 unsigned i;
161
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200162 for (i = 0; i < server_data.max_leases; i++) {
Denys Vlasenkoa85740c2016-10-04 00:51:38 +0200163 if ((chaddr && memcmp(g_leases[i].lease_mac, chaddr, 6) == 0)
164 || (yiaddr && g_leases[i].lease_nip == yiaddr)
165 ) {
166 memset(&g_leases[i], 0, sizeof(g_leases[i]));
167 }
168 }
169}
170
171/* Add a lease into the table, clearing out any old ones.
172 * If chaddr == NULL, this is a conflict lease.
173 */
174static struct dyn_lease *add_lease(
175 const uint8_t *chaddr, uint32_t yiaddr,
176 leasetime_t leasetime,
177 const char *hostname, int hostname_len)
178{
179 struct dyn_lease *oldest;
180
181 /* clean out any old ones */
182 clear_leases(chaddr, yiaddr);
183
184 oldest = oldest_expired_lease();
185
186 if (oldest) {
187 memset(oldest, 0, sizeof(*oldest));
188 if (hostname) {
189 char *p;
190
191 hostname_len++; /* include NUL */
192 if (hostname_len > sizeof(oldest->hostname))
193 hostname_len = sizeof(oldest->hostname);
194 p = safe_strncpy(oldest->hostname, hostname, hostname_len);
195 /*
196 * Sanitization (s/bad_char/./g).
197 * The intent is not to allow only "DNS-valid" hostnames,
198 * but merely make dumpleases output safe for shells to use.
199 * We accept "0-9A-Za-z._-", all other chars turn to dots.
200 */
Denys Vlasenko020abc82020-01-14 17:05:48 +0100201 if (*p == '-')
202 *p = '.'; /* defeat "-option" attacks too */
Denys Vlasenkoa85740c2016-10-04 00:51:38 +0200203 while (*p) {
204 if (!isalnum(*p) && *p != '-' && *p != '_')
205 *p = '.';
206 p++;
207 }
208 }
209 if (chaddr)
210 memcpy(oldest->lease_mac, chaddr, 6);
211 oldest->lease_nip = yiaddr;
212 oldest->expires = time(NULL) + leasetime;
213 }
214
215 return oldest;
216}
217
218/* True if a lease has expired */
219static int is_expired_lease(struct dyn_lease *lease)
220{
221 return (lease->expires < (leasetime_t) time(NULL));
222}
223
224/* Find the first lease that matches MAC, NULL if no match */
225static struct dyn_lease *find_lease_by_mac(const uint8_t *mac)
226{
227 unsigned i;
228
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200229 for (i = 0; i < server_data.max_leases; i++)
Denys Vlasenkoa85740c2016-10-04 00:51:38 +0200230 if (memcmp(g_leases[i].lease_mac, mac, 6) == 0)
231 return &g_leases[i];
232
233 return NULL;
234}
235
236/* Find the first lease that matches IP, NULL is no match */
237static struct dyn_lease *find_lease_by_nip(uint32_t nip)
238{
239 unsigned i;
240
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200241 for (i = 0; i < server_data.max_leases; i++)
Denys Vlasenkoa85740c2016-10-04 00:51:38 +0200242 if (g_leases[i].lease_nip == nip)
243 return &g_leases[i];
244
245 return NULL;
246}
247
248/* Check if the IP is taken; if it is, add it to the lease table */
249static int nobody_responds_to_arp(uint32_t nip, const uint8_t *safe_mac, unsigned arpping_ms)
250{
251 struct in_addr temp;
252 int r;
253
254 r = arpping(nip, safe_mac,
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200255 server_data.server_nip,
256 server_data.server_mac,
257 server_data.interface,
Denys Vlasenkoa85740c2016-10-04 00:51:38 +0200258 arpping_ms);
259 if (r)
260 return r;
261
262 temp.s_addr = nip;
James Byrne253c4e72019-04-12 17:01:51 +0000263 bb_info_msg("%s belongs to someone, reserving it for %u seconds",
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200264 inet_ntoa(temp), (unsigned)server_data.conflict_time);
265 add_lease(NULL, nip, server_data.conflict_time, NULL, 0);
Denys Vlasenkoa85740c2016-10-04 00:51:38 +0200266 return 0;
267}
268
269/* Find a new usable (we think) address */
270static uint32_t find_free_or_expired_nip(const uint8_t *safe_mac, unsigned arpping_ms)
271{
272 uint32_t addr;
273 struct dyn_lease *oldest_lease = NULL;
274
275#if ENABLE_FEATURE_UDHCPD_BASE_IP_ON_MAC
276 uint32_t stop;
277 unsigned i, hash;
278
279 /* hash hwaddr: use the SDBM hashing algorithm. Seems to give good
280 * dispersal even with similarly-valued "strings".
281 */
282 hash = 0;
283 for (i = 0; i < 6; i++)
284 hash += safe_mac[i] + (hash << 6) + (hash << 16) - hash;
285
286 /* pick a seed based on hwaddr then iterate until we find a free address. */
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200287 addr = server_data.start_ip
288 + (hash % (1 + server_data.end_ip - server_data.start_ip));
Denys Vlasenkoa85740c2016-10-04 00:51:38 +0200289 stop = addr;
290#else
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200291 addr = server_data.start_ip;
292#define stop (server_data.end_ip + 1)
Denys Vlasenkoa85740c2016-10-04 00:51:38 +0200293#endif
294 do {
295 uint32_t nip;
296 struct dyn_lease *lease;
297
298 /* ie, 192.168.55.0 */
299 if ((addr & 0xff) == 0)
300 goto next_addr;
301 /* ie, 192.168.55.255 */
302 if ((addr & 0xff) == 0xff)
303 goto next_addr;
304 nip = htonl(addr);
305 /* skip our own address */
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200306 if (nip == server_data.server_nip)
Denys Vlasenkoa85740c2016-10-04 00:51:38 +0200307 goto next_addr;
308 /* is this a static lease addr? */
Denys Vlasenko15021f32019-05-10 15:55:12 +0200309 if (is_nip_reserved_as_static(nip))
Denys Vlasenkoa85740c2016-10-04 00:51:38 +0200310 goto next_addr;
311
312 lease = find_lease_by_nip(nip);
313 if (!lease) {
314//TODO: DHCP servers do not always sit on the same subnet as clients: should *ping*, not arp-ping!
315 if (nobody_responds_to_arp(nip, safe_mac, arpping_ms))
316 return nip;
317 } else {
318 if (!oldest_lease || lease->expires < oldest_lease->expires)
319 oldest_lease = lease;
320 }
321
322 next_addr:
323 addr++;
324#if ENABLE_FEATURE_UDHCPD_BASE_IP_ON_MAC
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200325 if (addr > server_data.end_ip)
326 addr = server_data.start_ip;
Denys Vlasenkoa85740c2016-10-04 00:51:38 +0200327#endif
328 } while (addr != stop);
329
330 if (oldest_lease
331 && is_expired_lease(oldest_lease)
332 && nobody_responds_to_arp(oldest_lease->lease_nip, safe_mac, arpping_ms)
333 ) {
334 return oldest_lease->lease_nip;
335 }
336
337 return 0;
338}
339
Denys Vlasenkod2ae66c2016-10-04 00:43:14 +0200340/* On these functions, make sure your datatype matches */
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200341static int FAST_FUNC read_str(const char *line, void *arg)
342{
343 char **dest = arg;
344
345 free(*dest);
346 *dest = xstrdup(line);
347 return 1;
348}
349
350static int FAST_FUNC read_u32(const char *line, void *arg)
351{
352 *(uint32_t*)arg = bb_strtou32(line, NULL, 10);
353 return errno == 0;
354}
355
356static int FAST_FUNC read_staticlease(const char *const_line, void *arg)
357{
358 char *line;
359 char *mac_string;
360 char *ip_string;
Denys Vlasenkoa8408842019-05-16 11:18:49 +0200361 char *opts;
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200362 struct ether_addr mac_bytes; /* it's "struct { uint8_t mac[6]; }" */
363 uint32_t nip;
364
365 /* Read mac */
366 line = (char *) const_line;
367 mac_string = strtok_r(line, " \t", &line);
368 if (!mac_string || !ether_aton_r(mac_string, &mac_bytes))
369 return 0;
370
371 /* Read ip */
372 ip_string = strtok_r(NULL, " \t", &line);
373 if (!ip_string || !udhcp_str2nip(ip_string, &nip))
374 return 0;
375
Denys Vlasenkoa8408842019-05-16 11:18:49 +0200376 opts = strtok_r(NULL, " \t", &line);
377 /* opts might be NULL, that's not an error */
378
379 add_static_lease(arg, (uint8_t*) &mac_bytes, nip, opts);
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200380
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200381 return 1;
382}
383
Denys Vlasenko8c317f02019-05-14 17:26:47 +0200384static int FAST_FUNC read_optset(const char *line, void *arg)
385{
Denys Vlasenko60275972018-05-14 11:06:35 +0200386 return udhcp_str2optset(line, arg,
387 dhcp_optflags, dhcp_option_strings,
388 /*dhcpv6:*/ 0
389 );
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +0200390}
391
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200392struct config_keyword {
393 const char *keyword;
394 int (*handler)(const char *line, void *var) FAST_FUNC;
395 unsigned ofs;
396 const char *def;
397};
398
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200399#define OFS(field) offsetof(struct server_data_t, field)
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200400
Denys Vlasenko965b7952020-11-30 13:03:03 +0100401static const struct config_keyword keywords[] ALIGN_PTR = {
Denys Vlasenko3d27d432018-12-27 18:03:20 +0100402 /* keyword handler variable address default */
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200403 {"start" , udhcp_str2nip , OFS(start_ip ), "192.168.0.20"},
404 {"end" , udhcp_str2nip , OFS(end_ip ), "192.168.0.254"},
405 {"interface" , read_str , OFS(interface ), "eth0"},
406 /* Avoid "max_leases value not sane" warning by setting default
407 * to default_end_ip - default_start_ip + 1: */
408 {"max_leases" , read_u32 , OFS(max_leases ), "235"},
409 {"auto_time" , read_u32 , OFS(auto_time ), "7200"},
410 {"decline_time" , read_u32 , OFS(decline_time ), "3600"},
411 {"conflict_time", read_u32 , OFS(conflict_time), "3600"},
412 {"offer_time" , read_u32 , OFS(offer_time ), "60"},
413 {"min_lease" , read_u32 , OFS(min_lease_sec), "60"},
414 {"lease_file" , read_str , OFS(lease_file ), LEASES_FILE},
Norbert Lange05faa612020-02-14 22:16:10 +0100415 {"pidfile" , read_str , OFS(pidfile ), PID_FILE_PATH "/udhcpd.pid"},
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200416 {"siaddr" , udhcp_str2nip , OFS(siaddr_nip ), "0.0.0.0"},
417 /* keywords with no defaults must be last! */
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +0200418 {"option" , read_optset , OFS(options ), ""},
419 {"opt" , read_optset , OFS(options ), ""},
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200420 {"notify_file" , read_str , OFS(notify_file ), NULL},
421 {"sname" , read_str , OFS(sname ), NULL},
422 {"boot_file" , read_str , OFS(boot_file ), NULL},
423 {"static_lease" , read_staticlease, OFS(static_leases), ""},
424};
425enum { KWS_WITH_DEFAULTS = ARRAY_SIZE(keywords) - 6 };
426
427static NOINLINE void read_config(const char *file)
428{
429 parser_t *parser;
430 const struct config_keyword *k;
431 unsigned i;
432 char *token[2];
433
434 for (i = 0; i < KWS_WITH_DEFAULTS; i++)
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200435 keywords[i].handler(keywords[i].def, (char*)&server_data + keywords[i].ofs);
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200436
437 parser = config_open(file);
438 while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL)) {
439 for (k = keywords, i = 0; i < ARRAY_SIZE(keywords); k++, i++) {
440 if (strcasecmp(token[0], k->keyword) == 0) {
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200441 if (!k->handler(token[1], (char*)&server_data + k->ofs)) {
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200442 bb_error_msg("can't parse line %u in %s",
443 parser->lineno, file);
444 /* reset back to the default value */
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200445 k->handler(k->def, (char*)&server_data + k->ofs);
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200446 }
447 break;
448 }
449 }
450 }
451 config_close(parser);
452
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200453 server_data.start_ip = ntohl(server_data.start_ip);
454 server_data.end_ip = ntohl(server_data.end_ip);
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200455}
456
457static void write_leases(void)
458{
459 int fd;
460 unsigned i;
461 leasetime_t curr;
462 int64_t written_at;
463
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200464 fd = open_or_warn(server_data.lease_file, O_WRONLY|O_CREAT|O_TRUNC);
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200465 if (fd < 0)
466 return;
467
468 curr = written_at = time(NULL);
469
470 written_at = SWAP_BE64(written_at);
471 full_write(fd, &written_at, sizeof(written_at));
472
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200473 for (i = 0; i < server_data.max_leases; i++) {
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200474 leasetime_t tmp_time;
475
476 if (g_leases[i].lease_nip == 0)
477 continue;
478
479 /* Screw with the time in the struct, for easier writing */
480 tmp_time = g_leases[i].expires;
481
482 g_leases[i].expires -= curr;
483 if ((signed_leasetime_t) g_leases[i].expires < 0)
484 g_leases[i].expires = 0;
485 g_leases[i].expires = htonl(g_leases[i].expires);
486
487 /* No error check. If the file gets truncated,
488 * we lose some leases on restart. Oh well. */
489 full_write(fd, &g_leases[i], sizeof(g_leases[i]));
490
491 /* Then restore it when done */
492 g_leases[i].expires = tmp_time;
493 }
494 close(fd);
495
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200496 if (server_data.notify_file) {
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200497 char *argv[3];
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200498 argv[0] = server_data.notify_file;
499 argv[1] = server_data.lease_file;
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200500 argv[2] = NULL;
501 spawn_and_wait(argv);
502 }
503}
504
505static NOINLINE void read_leases(const char *file)
506{
507 struct dyn_lease lease;
508 int64_t written_at, time_passed;
509 int fd;
510#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
511 unsigned i = 0;
512#endif
513
514 fd = open_or_warn(file, O_RDONLY);
515 if (fd < 0)
516 return;
517
518 if (full_read(fd, &written_at, sizeof(written_at)) != sizeof(written_at))
519 goto ret;
520 written_at = SWAP_BE64(written_at);
521
522 time_passed = time(NULL) - written_at;
523 /* Strange written_at, or lease file from old version of udhcpd
524 * which had no "written_at" field? */
525 if ((uint64_t)time_passed > 12 * 60 * 60)
526 goto ret;
527
528 while (full_read(fd, &lease, sizeof(lease)) == sizeof(lease)) {
529 uint32_t y = ntohl(lease.lease_nip);
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200530 if (y >= server_data.start_ip && y <= server_data.end_ip) {
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200531 signed_leasetime_t expires = ntohl(lease.expires) - (signed_leasetime_t)time_passed;
532 uint32_t static_nip;
533
534 if (expires <= 0)
535 /* We keep expired leases: add_lease() will add
536 * a lease with 0 seconds remaining.
537 * Fewer IP address changes this way for mass reboot scenario.
538 */
539 expires = 0;
540
541 /* Check if there is a different static lease for this IP or MAC */
Denys Vlasenko15021f32019-05-10 15:55:12 +0200542 static_nip = get_static_nip_by_mac(lease.lease_mac);
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200543 if (static_nip) {
544 /* NB: we do not add lease even if static_nip == lease.lease_nip.
545 */
546 continue;
547 }
Denys Vlasenko15021f32019-05-10 15:55:12 +0200548 if (is_nip_reserved_as_static(lease.lease_nip))
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200549 continue;
550
551 /* NB: add_lease takes "relative time", IOW,
552 * lease duration, not lease deadline. */
553 if (add_lease(lease.lease_mac, lease.lease_nip,
554 expires,
555 lease.hostname, sizeof(lease.hostname)
556 ) == 0
557 ) {
558 bb_error_msg("too many leases while loading %s", file);
559 break;
560 }
561#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
562 i++;
563#endif
564 }
565 }
566 log1("read %d leases", i);
567 ret:
568 close(fd);
569}
Mike Frysinger7031f622006-05-08 03:20:50 +0000570
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100571/* Send a packet to a specific mac address and ip address by creating our own ip packet */
572static void send_packet_to_client(struct dhcp_packet *dhcp_pkt, int force_broadcast)
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100573{
574 const uint8_t *chaddr;
575 uint32_t ciaddr;
576
577 // Was:
578 //if (force_broadcast) { /* broadcast */ }
579 //else if (dhcp_pkt->ciaddr) { /* unicast to dhcp_pkt->ciaddr */ }
580 //else if (dhcp_pkt->flags & htons(BROADCAST_FLAG)) { /* broadcast */ }
581 //else { /* unicast to dhcp_pkt->yiaddr */ }
582 // But this is wrong: yiaddr is _our_ idea what client's IP is
583 // (for example, from lease file). Client may not know that,
584 // and may not have UDP socket listening on that IP!
585 // We should never unicast to dhcp_pkt->yiaddr!
586 // dhcp_pkt->ciaddr, OTOH, comes from client's request packet,
587 // and can be used.
588
589 if (force_broadcast
590 || (dhcp_pkt->flags & htons(BROADCAST_FLAG))
Denys Vlasenko53f72bb2010-03-21 06:46:09 +0100591 || dhcp_pkt->ciaddr == 0
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100592 ) {
James Byrne69374872019-07-02 11:35:03 +0200593 log1s("broadcasting packet to client");
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100594 ciaddr = INADDR_BROADCAST;
595 chaddr = MAC_BCAST_ADDR;
596 } else {
James Byrne69374872019-07-02 11:35:03 +0200597 log1s("unicasting packet to client ciaddr");
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100598 ciaddr = dhcp_pkt->ciaddr;
599 chaddr = dhcp_pkt->chaddr;
600 }
601
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100602 udhcp_send_raw_packet(dhcp_pkt,
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200603 /*src*/ server_data.server_nip, SERVER_PORT,
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100604 /*dst*/ ciaddr, CLIENT_PORT, chaddr,
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200605 server_data.ifindex);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100606}
607
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100608/* Send a packet to gateway_nip using the kernel ip stack */
609static void send_packet_to_relay(struct dhcp_packet *dhcp_pkt)
610{
James Byrne69374872019-07-02 11:35:03 +0200611 log1s("forwarding packet to relay");
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100612
613 udhcp_send_kernel_packet(dhcp_pkt,
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200614 server_data.server_nip, SERVER_PORT,
Michal Kaziorb8176992020-12-15 09:53:40 +0000615 dhcp_pkt->gateway_nip, SERVER_PORT,
616 server_data.interface);
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100617}
618
619static void send_packet(struct dhcp_packet *dhcp_pkt, int force_broadcast)
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100620{
621 if (dhcp_pkt->gateway_nip)
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100622 send_packet_to_relay(dhcp_pkt);
623 else
624 send_packet_to_client(dhcp_pkt, force_broadcast);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100625}
626
Denys Vlasenko25393fb2019-05-16 11:27:28 +0200627static void send_packet_verbose(struct dhcp_packet *dhcp_pkt, const char *fmt)
628{
629 struct in_addr addr;
630 addr.s_addr = dhcp_pkt->yiaddr;
631 bb_info_msg(fmt, inet_ntoa(addr));
632 /* send_packet emits error message itself if it detects failure */
633 send_packet(dhcp_pkt, /*force_bcast:*/ 0);
634}
635
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100636static void init_packet(struct dhcp_packet *packet, struct dhcp_packet *oldpacket, char type)
637{
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100638 /* Sets op, htype, hlen, cookie fields
639 * and adds DHCP_MESSAGE_TYPE option */
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100640 udhcp_init_header(packet, type);
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100641
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100642 packet->xid = oldpacket->xid;
643 memcpy(packet->chaddr, oldpacket->chaddr, sizeof(oldpacket->chaddr));
644 packet->flags = oldpacket->flags;
645 packet->gateway_nip = oldpacket->gateway_nip;
646 packet->ciaddr = oldpacket->ciaddr;
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200647 udhcp_add_simple_option(packet, DHCP_SERVER_ID, server_data.server_nip);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100648}
649
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100650/* Fill options field, siaddr_nip, and sname and boot_file fields.
651 * TODO: teach this code to use overload option.
652 */
653static void add_server_options(struct dhcp_packet *packet)
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100654{
Denys Vlasenkoa8408842019-05-16 11:18:49 +0200655 struct option_set *config_opts;
656 uint8_t *client_hostname_opt;
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100657
Denys Vlasenkoa8408842019-05-16 11:18:49 +0200658 client_hostname_opt = NULL;
659 if (packet->yiaddr) { /* if we aren't from send_inform()... */
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200660 struct static_lease *st_lease = server_data.static_leases;
Denys Vlasenkoa8408842019-05-16 11:18:49 +0200661 while (st_lease) {
662 if (st_lease->nip == packet->yiaddr) {
663 if (st_lease->opt[0] != 0)
664 client_hostname_opt = st_lease->opt;
665 break;
666 }
667 st_lease = st_lease->next;
668 }
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100669 }
670
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200671 config_opts = server_data.options;
Denys Vlasenkoa8408842019-05-16 11:18:49 +0200672 while (config_opts) {
673 if (config_opts->data[OPT_CODE] != DHCP_LEASE_TIME) {
674 /* ^^^^
675 * DHCP_LEASE_TIME is already filled, or in case of
676 * send_inform(), should not be filled at all.
677 */
678 if (config_opts->data[OPT_CODE] != DHCP_HOST_NAME
679 || !client_hostname_opt
680 ) {
681 /* Why "!client_hostname_opt":
682 * add hostname only if client has no hostname
683 * on its static lease line.
684 * (Not that "opt hostname HOST"
685 * makes much sense in udhcpd.conf,
686 * that'd give all clients the same hostname,
687 * but it's a valid configuration).
688 */
689 udhcp_add_binary_option(packet, config_opts->data);
690 }
691 }
692 config_opts = config_opts->next;
693 }
694
695 if (client_hostname_opt)
696 udhcp_add_binary_option(packet, client_hostname_opt);
697
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200698 packet->siaddr_nip = server_data.siaddr_nip;
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100699
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200700 if (server_data.sname)
701 strncpy((char*)packet->sname, server_data.sname, sizeof(packet->sname) - 1);
702 if (server_data.boot_file)
703 strncpy((char*)packet->file, server_data.boot_file, sizeof(packet->file) - 1);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100704}
705
706static uint32_t select_lease_time(struct dhcp_packet *packet)
707{
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200708 uint32_t lease_time_sec = server_data.max_lease_sec;
Denys Vlasenko6d3b4bb2018-12-17 18:07:18 +0100709 uint8_t *lease_time_opt = udhcp_get_option32(packet, DHCP_LEASE_TIME);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100710 if (lease_time_opt) {
711 move_from_unaligned32(lease_time_sec, lease_time_opt);
712 lease_time_sec = ntohl(lease_time_sec);
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200713 if (lease_time_sec > server_data.max_lease_sec)
714 lease_time_sec = server_data.max_lease_sec;
715 if (lease_time_sec < server_data.min_lease_sec)
716 lease_time_sec = server_data.min_lease_sec;
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100717 }
718 return lease_time_sec;
719}
720
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100721/* We got a DHCP DISCOVER. Send an OFFER. */
Denys Vlasenko0bb35e12010-10-21 12:33:10 +0200722/* NOINLINE: limit stack usage in caller */
Denys Vlasenkofa5e2952010-11-28 01:10:51 +0100723static NOINLINE void send_offer(struct dhcp_packet *oldpacket,
724 uint32_t static_lease_nip,
725 struct dyn_lease *lease,
Denys Vlasenko84029692019-05-15 13:08:48 +0200726 uint32_t requested_nip,
Michel Stam9f412712014-10-30 11:59:04 +0100727 unsigned arpping_ms)
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100728{
729 struct dhcp_packet packet;
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100730 uint32_t lease_time_sec;
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100731
732 init_packet(&packet, oldpacket, DHCPOFFER);
733
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100734 /* If it is a static lease, use its IP */
735 packet.yiaddr = static_lease_nip;
736 /* Else: */
Denys Vlasenkoa9539872010-03-20 03:49:27 +0100737 if (!static_lease_nip) {
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100738 /* We have no static lease for client's chaddr */
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100739 const char *p_host_name;
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100740
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100741 if (lease) {
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100742 /* We have a dynamic lease for client's chaddr.
743 * Reuse its IP (even if lease is expired).
744 * Note that we ignore requested IP in this case.
745 */
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100746 packet.yiaddr = lease->lease_nip;
747 }
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100748 /* Or: if client has requested an IP */
Denys Vlasenko84029692019-05-15 13:08:48 +0200749 else if (requested_nip != 0
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100750 /* and the IP is in the lease range */
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200751 && ntohl(requested_nip) >= server_data.start_ip
752 && ntohl(requested_nip) <= server_data.end_ip
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100753 /* and */
Denys Vlasenko84029692019-05-15 13:08:48 +0200754 && ( !(lease = find_lease_by_nip(requested_nip)) /* is not already taken */
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100755 || is_expired_lease(lease) /* or is taken, but expired */
756 )
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100757 ) {
Denys Vlasenko84029692019-05-15 13:08:48 +0200758 packet.yiaddr = requested_nip;
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100759 }
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100760 else {
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100761 /* Otherwise, find a free IP */
Michel Stam9f412712014-10-30 11:59:04 +0100762 packet.yiaddr = find_free_or_expired_nip(oldpacket->chaddr, arpping_ms);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100763 }
764
765 if (!packet.yiaddr) {
James Byrne69374872019-07-02 11:35:03 +0200766 bb_simple_error_msg("no free IP addresses. OFFER abandoned");
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100767 return;
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100768 }
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100769 /* Reserve the IP for a short time hoping to get DHCPREQUEST soon */
Denys Vlasenkodde8bdc2010-03-22 14:29:13 +0100770 p_host_name = (const char*) udhcp_get_option(oldpacket, DHCP_HOST_NAME);
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100771 lease = add_lease(packet.chaddr, packet.yiaddr,
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200772 server_data.offer_time,
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100773 p_host_name,
774 p_host_name ? (unsigned char)p_host_name[OPT_LEN - OPT_DATA] : 0
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100775 );
776 if (!lease) {
James Byrne69374872019-07-02 11:35:03 +0200777 bb_simple_error_msg("no free IP addresses. OFFER abandoned");
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100778 return;
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100779 }
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100780 }
781
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100782 lease_time_sec = select_lease_time(oldpacket);
Denys Vlasenko7724c762010-03-26 09:32:09 +0100783 udhcp_add_simple_option(&packet, DHCP_LEASE_TIME, htonl(lease_time_sec));
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100784 add_server_options(&packet);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100785
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100786 /* send_packet emits error message itself if it detects failure */
Denys Vlasenko25393fb2019-05-16 11:27:28 +0200787 send_packet_verbose(&packet, "sending OFFER to %s");
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100788}
789
Denys Vlasenko0bb35e12010-10-21 12:33:10 +0200790/* NOINLINE: limit stack usage in caller */
791static NOINLINE void send_NAK(struct dhcp_packet *oldpacket)
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100792{
793 struct dhcp_packet packet;
794
795 init_packet(&packet, oldpacket, DHCPNAK);
796
Denys Vlasenko16efe192016-03-30 18:44:52 +0200797 log1("sending %s", "NAK");
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100798 send_packet(&packet, /*force_bcast:*/ 1);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100799}
800
Denys Vlasenko0bb35e12010-10-21 12:33:10 +0200801/* NOINLINE: limit stack usage in caller */
802static NOINLINE void send_ACK(struct dhcp_packet *oldpacket, uint32_t yiaddr)
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100803{
804 struct dhcp_packet packet;
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100805 uint32_t lease_time_sec;
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100806 const char *p_host_name;
807
808 init_packet(&packet, oldpacket, DHCPACK);
809 packet.yiaddr = yiaddr;
810
811 lease_time_sec = select_lease_time(oldpacket);
Denys Vlasenko7724c762010-03-26 09:32:09 +0100812 udhcp_add_simple_option(&packet, DHCP_LEASE_TIME, htonl(lease_time_sec));
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100813 add_server_options(&packet);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100814
Denys Vlasenko25393fb2019-05-16 11:27:28 +0200815 send_packet_verbose(&packet, "sending ACK to %s");
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100816
Denys Vlasenkodde8bdc2010-03-22 14:29:13 +0100817 p_host_name = (const char*) udhcp_get_option(oldpacket, DHCP_HOST_NAME);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100818 add_lease(packet.chaddr, packet.yiaddr,
819 lease_time_sec,
820 p_host_name,
821 p_host_name ? (unsigned char)p_host_name[OPT_LEN - OPT_DATA] : 0
822 );
823 if (ENABLE_FEATURE_UDHCPD_WRITE_LEASES_EARLY) {
824 /* rewrite the file with leases at every new acceptance */
825 write_leases();
826 }
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100827}
828
Denys Vlasenko0bb35e12010-10-21 12:33:10 +0200829/* NOINLINE: limit stack usage in caller */
830static NOINLINE void send_inform(struct dhcp_packet *oldpacket)
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100831{
832 struct dhcp_packet packet;
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100833
Denys Vlasenkof8fcc182010-04-04 22:36:34 +0200834 /* "If a client has obtained a network address through some other means
835 * (e.g., manual configuration), it may use a DHCPINFORM request message
836 * to obtain other local configuration parameters. Servers receiving a
837 * DHCPINFORM message construct a DHCPACK message with any local
838 * configuration parameters appropriate for the client without:
839 * allocating a new address, checking for an existing binding, filling
840 * in 'yiaddr' or including lease time parameters. The servers SHOULD
841 * unicast the DHCPACK reply to the address given in the 'ciaddr' field
842 * of the DHCPINFORM message.
843 * ...
844 * The server responds to a DHCPINFORM message by sending a DHCPACK
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100845 * message directly to the address given in the 'ciaddr' field
846 * of the DHCPINFORM message. The server MUST NOT send a lease
847 * expiration time to the client and SHOULD NOT fill in 'yiaddr'."
848 */
Denys Vlasenkof8fcc182010-04-04 22:36:34 +0200849//TODO: do a few sanity checks: is ciaddr set?
850//Better yet: is ciaddr == IP source addr?
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100851 init_packet(&packet, oldpacket, DHCPACK);
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100852 add_server_options(&packet);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100853
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100854 send_packet(&packet, /*force_bcast:*/ 0);
Denys Vlasenko25393fb2019-05-16 11:27:28 +0200855 // or maybe? send_packet_verbose(&packet, "sending ACK to %s");
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100856}
857
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000858int udhcpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000859int udhcpd_main(int argc UNUSED_PARAM, char **argv)
Mike Frysinger7031f622006-05-08 03:20:50 +0000860{
Denys Vlasenko52a515d2017-02-16 23:25:44 +0100861 int server_socket = -1, retval;
Denys Vlasenko6947d2c2009-06-17 13:24:03 +0200862 uint8_t *state;
Denis Vlasenko42b3dea2007-07-03 15:47:50 +0000863 unsigned timeout_end;
864 unsigned num_ips;
Denis Vlasenko3d17d2b2007-08-14 16:45:29 +0000865 unsigned opt;
Mike Frysinger7031f622006-05-08 03:20:50 +0000866 struct option_set *option;
Denys Vlasenkoe3f5b732013-03-13 22:27:37 +0100867 char *str_I = str_I;
Michel Stam9f412712014-10-30 11:59:04 +0100868 const char *str_a = "2000";
869 unsigned arpping_ms;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000870 IF_FEATURE_UDHCP_PORT(char *str_P;)
Mike Frysinger7031f622006-05-08 03:20:50 +0000871
Denys Vlasenkodf70a432016-04-21 18:54:36 +0200872 setup_common_bufsiz();
873
874 IF_FEATURE_UDHCP_PORT(SERVER_PORT = 67;)
875 IF_FEATURE_UDHCP_PORT(CLIENT_PORT = 68;)
Denis Vlasenkod55fe3e2008-02-04 13:12:16 +0000876
Denys Vlasenko65c34c52019-05-31 23:39:22 +0200877 /* Make sure fd 0,1,2 are open */
878 /* Setup the signal pipe on fds 3,4 - must be before openlog() */
879 udhcp_sp_setup();
880
Denys Vlasenko22542ec2017-08-08 21:55:02 +0200881 opt = getopt32(argv, "^"
882 "fSI:va:"IF_FEATURE_UDHCP_PORT("P:")
883 "\0"
Denys Vlasenkoac906fa2009-06-17 11:54:52 +0200884#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
Denys Vlasenko22542ec2017-08-08 21:55:02 +0200885 "vv"
Denys Vlasenkoac906fa2009-06-17 11:54:52 +0200886#endif
Denys Vlasenkoe3f5b732013-03-13 22:27:37 +0100887 , &str_I
Michel Stam9f412712014-10-30 11:59:04 +0100888 , &str_a
Denys Vlasenkoe3f5b732013-03-13 22:27:37 +0100889 IF_FEATURE_UDHCP_PORT(, &str_P)
Leonid Lisovskiy6c9c0a12011-10-18 00:35:47 +0200890 IF_UDHCP_VERBOSE(, &dhcp_verbose)
Denys Vlasenkoac906fa2009-06-17 11:54:52 +0200891 );
Denis Vlasenko3d17d2b2007-08-14 16:45:29 +0000892 if (!(opt & 1)) { /* no -f */
Denis Vlasenkoc82b5102007-07-01 17:05:57 +0000893 bb_daemonize_or_rexec(0, argv);
Denis Vlasenkoa19e6492009-03-11 14:40:00 +0000894 logmode = LOGMODE_NONE;
Denis Vlasenkoaf1c8432007-03-26 13:22:35 +0000895 }
Mike Frysinger6db13732010-06-04 13:24:50 -0400896 /* update argv after the possible vfork+exec in daemonize */
897 argv += optind;
Denis Vlasenko3d17d2b2007-08-14 16:45:29 +0000898 if (opt & 2) { /* -S */
Denis Vlasenko5e4fda02009-03-08 23:46:48 +0000899 openlog(applet_name, LOG_PID, LOG_DAEMON);
Denis Vlasenkoaf1c8432007-03-26 13:22:35 +0000900 logmode |= LOGMODE_SYSLOG;
901 }
Denys Vlasenkoe3f5b732013-03-13 22:27:37 +0100902 if (opt & 4) { /* -I */
903 len_and_sockaddr *lsa = xhost_and_af2sockaddr(str_I, 0, AF_INET);
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200904 server_data.server_nip = lsa->u.sin.sin_addr.s_addr;
Denys Vlasenkoe3f5b732013-03-13 22:27:37 +0100905 free(lsa);
906 }
Denis Vlasenkod55fe3e2008-02-04 13:12:16 +0000907#if ENABLE_FEATURE_UDHCP_PORT
Michel Stam9f412712014-10-30 11:59:04 +0100908 if (opt & 32) { /* -P */
Denis Vlasenkod55fe3e2008-02-04 13:12:16 +0000909 SERVER_PORT = xatou16(str_P);
910 CLIENT_PORT = SERVER_PORT + 1;
911 }
912#endif
Michel Stam9f412712014-10-30 11:59:04 +0100913 arpping_ms = xatou(str_a);
914
Denis Vlasenkoaf1c8432007-03-26 13:22:35 +0000915 /* Would rather not do read_config before daemonization -
916 * otherwise NOMMU machines will parse config twice */
Denis Vlasenko9f7b92a2007-08-15 20:03:36 +0000917 read_config(argv[0] ? argv[0] : DHCPD_CONF_FILE);
Denys Vlasenko3d306bf2018-03-11 11:34:44 +0100918 /* prevent poll timeout overflow */
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200919 if (server_data.auto_time > INT_MAX / 1000)
920 server_data.auto_time = INT_MAX / 1000;
Mike Frysinger7031f622006-05-08 03:20:50 +0000921
Denis Vlasenko80edead2007-08-02 22:31:05 +0000922 /* Create pidfile */
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200923 write_pidfile(server_data.pidfile);
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100924 /* if (!..) bb_perror_msg("can't create pidfile %s", pidfile); */
Denis Vlasenko80edead2007-08-02 22:31:05 +0000925
James Byrne69374872019-07-02 11:35:03 +0200926 bb_simple_info_msg("started, v"BB_VER);
Mike Frysinger7031f622006-05-08 03:20:50 +0000927
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200928 option = udhcp_find_option(server_data.options, DHCP_LEASE_TIME);
929 server_data.max_lease_sec = DEFAULT_LEASE_TIME;
Denis Vlasenkoaf1c8432007-03-26 13:22:35 +0000930 if (option) {
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200931 move_from_unaligned32(server_data.max_lease_sec, option->data + OPT_DATA);
932 server_data.max_lease_sec = ntohl(server_data.max_lease_sec);
Denis Vlasenko6e6d3312007-05-03 23:39:35 +0000933 }
Mike Frysinger7031f622006-05-08 03:20:50 +0000934
935 /* Sanity check */
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200936 num_ips = server_data.end_ip - server_data.start_ip + 1;
937 if (server_data.max_leases > num_ips) {
Denis Vlasenko42b3dea2007-07-03 15:47:50 +0000938 bb_error_msg("max_leases=%u is too big, setting to %u",
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200939 (unsigned)server_data.max_leases, num_ips);
940 server_data.max_leases = num_ips;
Mike Frysinger7031f622006-05-08 03:20:50 +0000941 }
942
Denys Vlasenko06076492018-02-01 10:41:14 +0100943 /* this sets g_leases */
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200944 SET_PTR_TO_GLOBALS(xzalloc(server_data.max_leases * sizeof(g_leases[0])));
Denys Vlasenko06076492018-02-01 10:41:14 +0100945
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200946 read_leases(server_data.lease_file);
Mike Frysinger7031f622006-05-08 03:20:50 +0000947
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200948 if (udhcp_read_interface(server_data.interface,
949 &server_data.ifindex,
950 (server_data.server_nip == 0 ? &server_data.server_nip : NULL),
951 server_data.server_mac)
Denys Vlasenko26918dd2009-06-16 12:04:23 +0200952 ) {
Denis Vlasenko6e6d3312007-05-03 23:39:35 +0000953 retval = 1;
954 goto ret;
955 }
Mike Frysinger7031f622006-05-08 03:20:50 +0000956
Denys Vlasenko71045cc2012-07-24 17:21:26 +0200957 continue_with_autotime:
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200958 timeout_end = monotonic_sec() + server_data.auto_time;
Denis Vlasenko5a3395b2006-11-18 19:51:32 +0000959 while (1) { /* loop until universe collapses */
Denys Vlasenko52a515d2017-02-16 23:25:44 +0100960 struct pollfd pfds[2];
Denys Vlasenkofa5e2952010-11-28 01:10:51 +0100961 struct dhcp_packet packet;
Denis Vlasenko0416e3d2009-01-01 17:52:09 +0000962 int bytes;
Denys Vlasenko52a515d2017-02-16 23:25:44 +0100963 int tv;
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100964 uint8_t *server_id_opt;
Denys Vlasenkofa5e2952010-11-28 01:10:51 +0100965 uint8_t *requested_ip_opt;
Denys Vlasenko84029692019-05-15 13:08:48 +0200966 uint32_t requested_nip;
Denys Vlasenkofa5e2952010-11-28 01:10:51 +0100967 uint32_t static_lease_nip;
968 struct dyn_lease *lease, fake_lease;
Mike Frysinger7031f622006-05-08 03:20:50 +0000969
Denis Vlasenkoe2d3ded2006-11-27 23:43:28 +0000970 if (server_socket < 0) {
Denis Vlasenkof1980f62008-09-26 09:34:59 +0000971 server_socket = udhcp_listen_socket(/*INADDR_ANY,*/ SERVER_PORT,
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200972 server_data.interface);
Denis Vlasenkoe2d3ded2006-11-27 23:43:28 +0000973 }
Mike Frysinger7031f622006-05-08 03:20:50 +0000974
Denys Vlasenko52a515d2017-02-16 23:25:44 +0100975 udhcp_sp_fd_set(pfds, server_socket);
Denys Vlasenko3d306bf2018-03-11 11:34:44 +0100976
977 new_tv:
978 tv = -1;
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200979 if (server_data.auto_time) {
Denys Vlasenko3d306bf2018-03-11 11:34:44 +0100980 tv = timeout_end - monotonic_sec();
981 if (tv <= 0) {
982 write_leases:
Denys Vlasenko3293bc12018-03-10 19:01:48 +0100983 write_leases();
984 goto continue_with_autotime;
985 }
Denys Vlasenko3d306bf2018-03-11 11:34:44 +0100986 tv *= 1000;
987 }
988
989 /* Block here waiting for either signal or packet */
990 retval = poll(pfds, 2, tv);
991 if (retval <= 0) {
992 if (retval == 0)
993 goto write_leases;
994 if (errno == EINTR)
995 goto new_tv;
Denys Vlasenko3293bc12018-03-10 19:01:48 +0100996 /* < 0 and not EINTR: should not happen */
James Byrne69374872019-07-02 11:35:03 +0200997 bb_simple_perror_msg_and_die("poll");
Mike Frysinger7031f622006-05-08 03:20:50 +0000998 }
999
Denys Vlasenko3293bc12018-03-10 19:01:48 +01001000 if (pfds[0].revents) switch (udhcp_sp_read()) {
Mike Frysinger7031f622006-05-08 03:20:50 +00001001 case SIGUSR1:
James Byrne253c4e72019-04-12 17:01:51 +00001002 bb_info_msg("received %s", "SIGUSR1");
Mike Frysinger7031f622006-05-08 03:20:50 +00001003 write_leases();
1004 /* why not just reset the timeout, eh */
Denys Vlasenko71045cc2012-07-24 17:21:26 +02001005 goto continue_with_autotime;
Mike Frysinger7031f622006-05-08 03:20:50 +00001006 case SIGTERM:
James Byrne253c4e72019-04-12 17:01:51 +00001007 bb_info_msg("received %s", "SIGTERM");
Denys Vlasenko71045cc2012-07-24 17:21:26 +02001008 write_leases();
Denis Vlasenko6e6d3312007-05-03 23:39:35 +00001009 goto ret0;
Mike Frysinger7031f622006-05-08 03:20:50 +00001010 }
1011
Denys Vlasenko3293bc12018-03-10 19:01:48 +01001012 /* Is it a packet? */
1013 if (!pfds[1].revents)
1014 continue; /* no */
1015
1016 /* Note: we do not block here, we block on poll() instead.
1017 * Blocking here would prevent SIGTERM from working:
1018 * socket read inside this call is restarted on caught signals.
1019 */
Denis Vlasenko0416e3d2009-01-01 17:52:09 +00001020 bytes = udhcp_recv_kernel_packet(&packet, server_socket);
Denis Vlasenkoaf1c8432007-03-26 13:22:35 +00001021 if (bytes < 0) {
Denis Vlasenko0416e3d2009-01-01 17:52:09 +00001022 /* bytes can also be -2 ("bad packet data") */
Mike Frysinger7031f622006-05-08 03:20:50 +00001023 if (bytes == -1 && errno != EINTR) {
Denys Vlasenko6f97b302017-09-29 18:17:25 +02001024 log1("read error: "STRERROR_FMT", reopening socket" STRERROR_ERRNO);
Mike Frysinger7031f622006-05-08 03:20:50 +00001025 close(server_socket);
1026 server_socket = -1;
1027 }
1028 continue;
1029 }
Denys Vlasenko31af3d52009-06-17 11:57:09 +02001030 if (packet.hlen != 6) {
James Byrne69374872019-07-02 11:35:03 +02001031 bb_info_msg("MAC length != 6%s", ", ignoring packet");
Denys Vlasenko31af3d52009-06-17 11:57:09 +02001032 continue;
1033 }
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001034 if (packet.op != BOOTREQUEST) {
James Byrne69374872019-07-02 11:35:03 +02001035 bb_info_msg("not a REQUEST%s", ", ignoring packet");
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001036 continue;
1037 }
Denys Vlasenkodde8bdc2010-03-22 14:29:13 +01001038 state = udhcp_get_option(&packet, DHCP_MESSAGE_TYPE);
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001039 if (state == NULL || state[0] < DHCP_MINTYPE || state[0] > DHCP_MAXTYPE) {
James Byrne69374872019-07-02 11:35:03 +02001040 bb_info_msg("no or bad message type option%s", ", ignoring packet");
Mike Frysinger7031f622006-05-08 03:20:50 +00001041 continue;
1042 }
1043
Denys Vlasenkofa5e2952010-11-28 01:10:51 +01001044 /* Get SERVER_ID if present */
Denys Vlasenko6d3b4bb2018-12-17 18:07:18 +01001045 server_id_opt = udhcp_get_option32(&packet, DHCP_SERVER_ID);
Denys Vlasenkofa5e2952010-11-28 01:10:51 +01001046 if (server_id_opt) {
Denys Vlasenko713d2412010-11-28 21:51:44 +01001047 uint32_t server_id_network_order;
1048 move_from_unaligned32(server_id_network_order, server_id_opt);
Denys Vlasenko91755cb2019-05-30 16:23:34 +02001049 if (server_id_network_order != server_data.server_nip) {
Denys Vlasenkofa5e2952010-11-28 01:10:51 +01001050 /* client talks to somebody else */
Denys Vlasenkoa4959ee2021-02-21 16:32:07 +01001051 log1("server ID doesn't match%s", ", ignoring packet");
Denys Vlasenkofa5e2952010-11-28 01:10:51 +01001052 continue;
1053 }
1054 }
1055
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001056 /* Look for a static/dynamic lease */
Denys Vlasenko15021f32019-05-10 15:55:12 +02001057 static_lease_nip = get_static_nip_by_mac(&packet.chaddr);
Denys Vlasenkoa9539872010-03-20 03:49:27 +01001058 if (static_lease_nip) {
James Byrne253c4e72019-04-12 17:01:51 +00001059 bb_info_msg("found static lease: %x", static_lease_nip);
Denys Vlasenko6947d2c2009-06-17 13:24:03 +02001060 memcpy(&fake_lease.lease_mac, &packet.chaddr, 6);
Denys Vlasenkoa9539872010-03-20 03:49:27 +01001061 fake_lease.lease_nip = static_lease_nip;
Denys Vlasenko6947d2c2009-06-17 13:24:03 +02001062 fake_lease.expires = 0;
Denys Vlasenko6947d2c2009-06-17 13:24:03 +02001063 lease = &fake_lease;
Denis Vlasenko5a3395b2006-11-18 19:51:32 +00001064 } else {
Denys Vlasenko6947d2c2009-06-17 13:24:03 +02001065 lease = find_lease_by_mac(packet.chaddr);
Mike Frysinger7031f622006-05-08 03:20:50 +00001066 }
1067
Denys Vlasenkofa5e2952010-11-28 01:10:51 +01001068 /* Get REQUESTED_IP if present */
Denys Vlasenko84029692019-05-15 13:08:48 +02001069 requested_nip = 0;
Denys Vlasenko6d3b4bb2018-12-17 18:07:18 +01001070 requested_ip_opt = udhcp_get_option32(&packet, DHCP_REQUESTED_IP);
Denys Vlasenkofa5e2952010-11-28 01:10:51 +01001071 if (requested_ip_opt) {
1072 move_from_unaligned32(requested_nip, requested_ip_opt);
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001073 }
1074
Mike Frysinger7031f622006-05-08 03:20:50 +00001075 switch (state[0]) {
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001076
Mike Frysinger7031f622006-05-08 03:20:50 +00001077 case DHCPDISCOVER:
Denys Vlasenko8f2e99c2016-03-30 18:41:23 +02001078 log1("received %s", "DISCOVER");
Mike Frysinger7031f622006-05-08 03:20:50 +00001079
Denys Vlasenko84029692019-05-15 13:08:48 +02001080 send_offer(&packet, static_lease_nip, lease, requested_nip, arpping_ms);
Mike Frysinger7031f622006-05-08 03:20:50 +00001081 break;
Denys Vlasenko6947d2c2009-06-17 13:24:03 +02001082
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001083 case DHCPREQUEST:
Denys Vlasenko8f2e99c2016-03-30 18:41:23 +02001084 log1("received %s", "REQUEST");
Denys Vlasenko53f72bb2010-03-21 06:46:09 +01001085/* RFC 2131:
Mike Frysinger7031f622006-05-08 03:20:50 +00001086
Denys Vlasenko53f72bb2010-03-21 06:46:09 +01001087o DHCPREQUEST generated during SELECTING state:
1088
1089 Client inserts the address of the selected server in 'server
1090 identifier', 'ciaddr' MUST be zero, 'requested IP address' MUST be
1091 filled in with the yiaddr value from the chosen DHCPOFFER.
1092
1093 Note that the client may choose to collect several DHCPOFFER
1094 messages and select the "best" offer. The client indicates its
1095 selection by identifying the offering server in the DHCPREQUEST
1096 message. If the client receives no acceptable offers, the client
1097 may choose to try another DHCPDISCOVER message. Therefore, the
1098 servers may not receive a specific DHCPREQUEST from which they can
1099 decide whether or not the client has accepted the offer.
1100
1101o DHCPREQUEST generated during INIT-REBOOT state:
1102
1103 'server identifier' MUST NOT be filled in, 'requested IP address'
1104 option MUST be filled in with client's notion of its previously
1105 assigned address. 'ciaddr' MUST be zero. The client is seeking to
1106 verify a previously allocated, cached configuration. Server SHOULD
1107 send a DHCPNAK message to the client if the 'requested IP address'
1108 is incorrect, or is on the wrong network.
1109
1110 Determining whether a client in the INIT-REBOOT state is on the
1111 correct network is done by examining the contents of 'giaddr', the
1112 'requested IP address' option, and a database lookup. If the DHCP
1113 server detects that the client is on the wrong net (i.e., the
1114 result of applying the local subnet mask or remote subnet mask (if
1115 'giaddr' is not zero) to 'requested IP address' option value
1116 doesn't match reality), then the server SHOULD send a DHCPNAK
1117 message to the client.
1118
1119 If the network is correct, then the DHCP server should check if
1120 the client's notion of its IP address is correct. If not, then the
1121 server SHOULD send a DHCPNAK message to the client. If the DHCP
1122 server has no record of this client, then it MUST remain silent,
1123 and MAY output a warning to the network administrator. This
1124 behavior is necessary for peaceful coexistence of non-
1125 communicating DHCP servers on the same wire.
1126
1127 If 'giaddr' is 0x0 in the DHCPREQUEST message, the client is on
1128 the same subnet as the server. The server MUST broadcast the
1129 DHCPNAK message to the 0xffffffff broadcast address because the
1130 client may not have a correct network address or subnet mask, and
1131 the client may not be answering ARP requests.
1132
1133 If 'giaddr' is set in the DHCPREQUEST message, the client is on a
1134 different subnet. The server MUST set the broadcast bit in the
1135 DHCPNAK, so that the relay agent will broadcast the DHCPNAK to the
1136 client, because the client may not have a correct network address
1137 or subnet mask, and the client may not be answering ARP requests.
1138
1139o DHCPREQUEST generated during RENEWING state:
1140
1141 'server identifier' MUST NOT be filled in, 'requested IP address'
1142 option MUST NOT be filled in, 'ciaddr' MUST be filled in with
1143 client's IP address. In this situation, the client is completely
1144 configured, and is trying to extend its lease. This message will
1145 be unicast, so no relay agents will be involved in its
1146 transmission. Because 'giaddr' is therefore not filled in, the
1147 DHCP server will trust the value in 'ciaddr', and use it when
1148 replying to the client.
1149
1150 A client MAY choose to renew or extend its lease prior to T1. The
1151 server may choose not to extend the lease (as a policy decision by
1152 the network administrator), but should return a DHCPACK message
1153 regardless.
1154
1155o DHCPREQUEST generated during REBINDING state:
1156
1157 'server identifier' MUST NOT be filled in, 'requested IP address'
1158 option MUST NOT be filled in, 'ciaddr' MUST be filled in with
1159 client's IP address. In this situation, the client is completely
1160 configured, and is trying to extend its lease. This message MUST
1161 be broadcast to the 0xffffffff IP broadcast address. The DHCP
1162 server SHOULD check 'ciaddr' for correctness before replying to
1163 the DHCPREQUEST.
1164
1165 The DHCPREQUEST from a REBINDING client is intended to accommodate
1166 sites that have multiple DHCP servers and a mechanism for
1167 maintaining consistency among leases managed by multiple servers.
1168 A DHCP server MAY extend a client's lease only if it has local
1169 administrative authority to do so.
1170*/
Denys Vlasenkofa5e2952010-11-28 01:10:51 +01001171 if (!requested_ip_opt) {
Denys Vlasenko53f72bb2010-03-21 06:46:09 +01001172 requested_nip = packet.ciaddr;
1173 if (requested_nip == 0) {
Denys Vlasenkoa4959ee2021-02-21 16:32:07 +01001174 log1("no requested IP and no ciaddr%s", ", ignoring packet");
Denys Vlasenko53f72bb2010-03-21 06:46:09 +01001175 break;
1176 }
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001177 }
1178 if (lease && requested_nip == lease->lease_nip) {
Denys Vlasenko53f72bb2010-03-21 06:46:09 +01001179 /* client requested or configured IP matches the lease.
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001180 * ACK it, and bump lease expiration time. */
1181 send_ACK(&packet, lease->lease_nip);
1182 break;
1183 }
Denys Vlasenko713d2412010-11-28 21:51:44 +01001184 /* No lease for this MAC, or lease IP != requested IP */
1185
1186 if (server_id_opt /* client is in SELECTING state */
1187 || requested_ip_opt /* client is in INIT-REBOOT state */
1188 ) {
1189 /* "No, we don't have this IP for you" */
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001190 send_NAK(&packet);
Denys Vlasenko713d2412010-11-28 21:51:44 +01001191 } /* else: client is in RENEWING or REBINDING, do not answer */
1192
Mike Frysinger7031f622006-05-08 03:20:50 +00001193 break;
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001194
Mike Frysinger7031f622006-05-08 03:20:50 +00001195 case DHCPDECLINE:
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001196 /* RFC 2131:
1197 * "If the server receives a DHCPDECLINE message,
1198 * the client has discovered through some other means
1199 * that the suggested network address is already
1200 * in use. The server MUST mark the network address
1201 * as not available and SHOULD notify the local
1202 * sysadmin of a possible configuration problem."
1203 *
1204 * SERVER_ID must be present,
1205 * REQUESTED_IP must be present,
1206 * chaddr must be filled in,
1207 * ciaddr must be 0 (we do not check this)
1208 */
Denys Vlasenko8f2e99c2016-03-30 18:41:23 +02001209 log1("received %s", "DECLINE");
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001210 if (server_id_opt
Denys Vlasenkofa5e2952010-11-28 01:10:51 +01001211 && requested_ip_opt
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001212 && lease /* chaddr matches this lease */
1213 && requested_nip == lease->lease_nip
1214 ) {
Denys Vlasenko31af3d52009-06-17 11:57:09 +02001215 memset(lease->lease_mac, 0, sizeof(lease->lease_mac));
Denys Vlasenko91755cb2019-05-30 16:23:34 +02001216 lease->expires = time(NULL) + server_data.decline_time;
Mike Frysinger7031f622006-05-08 03:20:50 +00001217 }
1218 break;
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001219
Mike Frysinger7031f622006-05-08 03:20:50 +00001220 case DHCPRELEASE:
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001221 /* "Upon receipt of a DHCPRELEASE message, the server
1222 * marks the network address as not allocated."
1223 *
1224 * SERVER_ID must be present,
1225 * REQUESTED_IP must not be present (we do not check this),
1226 * chaddr must be filled in,
1227 * ciaddr must be filled in
1228 */
Denys Vlasenko8f2e99c2016-03-30 18:41:23 +02001229 log1("received %s", "RELEASE");
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001230 if (server_id_opt
1231 && lease /* chaddr matches this lease */
1232 && packet.ciaddr == lease->lease_nip
1233 ) {
Denis Vlasenko04158e02009-02-02 10:48:06 +00001234 lease->expires = time(NULL);
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001235 }
Mike Frysinger7031f622006-05-08 03:20:50 +00001236 break;
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001237
Mike Frysinger7031f622006-05-08 03:20:50 +00001238 case DHCPINFORM:
Denys Vlasenko8f2e99c2016-03-30 18:41:23 +02001239 log1("received %s", "INFORM");
Mike Frysinger7031f622006-05-08 03:20:50 +00001240 send_inform(&packet);
1241 break;
Mike Frysinger7031f622006-05-08 03:20:50 +00001242 }
1243 }
Denis Vlasenko6e6d3312007-05-03 23:39:35 +00001244 ret0:
1245 retval = 0;
1246 ret:
Denys Vlasenko91755cb2019-05-30 16:23:34 +02001247 /*if (server_data.pidfile) - server_data.pidfile is never NULL */
1248 remove_pidfile(server_data.pidfile);
Denis Vlasenko6e6d3312007-05-03 23:39:35 +00001249 return retval;
Mike Frysinger7031f622006-05-08 03:20:50 +00001250}