blob: 91f70970af66ca9deafe42f76567dea907fda4ec [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
Seth David Schoen5a3d3b82021-02-03 16:19:18 -0800298 /* (Addresses ending in .0 or .255 can legitimately be allocated
299 * in various situations, so _don't_ skip these. The user needs
300 * to choose start_ip and end_ip correctly for a particular
301 * network environment.) */
302
Denys Vlasenkoa85740c2016-10-04 00:51:38 +0200303 nip = htonl(addr);
304 /* skip our own address */
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200305 if (nip == server_data.server_nip)
Denys Vlasenkoa85740c2016-10-04 00:51:38 +0200306 goto next_addr;
307 /* is this a static lease addr? */
Denys Vlasenko15021f32019-05-10 15:55:12 +0200308 if (is_nip_reserved_as_static(nip))
Denys Vlasenkoa85740c2016-10-04 00:51:38 +0200309 goto next_addr;
310
311 lease = find_lease_by_nip(nip);
312 if (!lease) {
313//TODO: DHCP servers do not always sit on the same subnet as clients: should *ping*, not arp-ping!
314 if (nobody_responds_to_arp(nip, safe_mac, arpping_ms))
315 return nip;
316 } else {
317 if (!oldest_lease || lease->expires < oldest_lease->expires)
318 oldest_lease = lease;
319 }
320
321 next_addr:
322 addr++;
323#if ENABLE_FEATURE_UDHCPD_BASE_IP_ON_MAC
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200324 if (addr > server_data.end_ip)
325 addr = server_data.start_ip;
Denys Vlasenkoa85740c2016-10-04 00:51:38 +0200326#endif
327 } while (addr != stop);
328
329 if (oldest_lease
330 && is_expired_lease(oldest_lease)
331 && nobody_responds_to_arp(oldest_lease->lease_nip, safe_mac, arpping_ms)
332 ) {
333 return oldest_lease->lease_nip;
334 }
335
336 return 0;
337}
338
Denys Vlasenkod2ae66c2016-10-04 00:43:14 +0200339/* On these functions, make sure your datatype matches */
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200340static int FAST_FUNC read_str(const char *line, void *arg)
341{
342 char **dest = arg;
343
344 free(*dest);
345 *dest = xstrdup(line);
346 return 1;
347}
348
349static int FAST_FUNC read_u32(const char *line, void *arg)
350{
351 *(uint32_t*)arg = bb_strtou32(line, NULL, 10);
352 return errno == 0;
353}
354
355static int FAST_FUNC read_staticlease(const char *const_line, void *arg)
356{
357 char *line;
358 char *mac_string;
359 char *ip_string;
Denys Vlasenkoa8408842019-05-16 11:18:49 +0200360 char *opts;
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200361 struct ether_addr mac_bytes; /* it's "struct { uint8_t mac[6]; }" */
362 uint32_t nip;
363
364 /* Read mac */
365 line = (char *) const_line;
366 mac_string = strtok_r(line, " \t", &line);
367 if (!mac_string || !ether_aton_r(mac_string, &mac_bytes))
368 return 0;
369
370 /* Read ip */
371 ip_string = strtok_r(NULL, " \t", &line);
372 if (!ip_string || !udhcp_str2nip(ip_string, &nip))
373 return 0;
374
Denys Vlasenkoa8408842019-05-16 11:18:49 +0200375 opts = strtok_r(NULL, " \t", &line);
376 /* opts might be NULL, that's not an error */
377
378 add_static_lease(arg, (uint8_t*) &mac_bytes, nip, opts);
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200379
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200380 return 1;
381}
382
Denys Vlasenko8c317f02019-05-14 17:26:47 +0200383static int FAST_FUNC read_optset(const char *line, void *arg)
384{
Denys Vlasenko60275972018-05-14 11:06:35 +0200385 return udhcp_str2optset(line, arg,
386 dhcp_optflags, dhcp_option_strings,
387 /*dhcpv6:*/ 0
388 );
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +0200389}
390
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200391struct config_keyword {
392 const char *keyword;
393 int (*handler)(const char *line, void *var) FAST_FUNC;
394 unsigned ofs;
395 const char *def;
396};
397
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200398#define OFS(field) offsetof(struct server_data_t, field)
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200399
Denys Vlasenko965b7952020-11-30 13:03:03 +0100400static const struct config_keyword keywords[] ALIGN_PTR = {
Denys Vlasenko3d27d432018-12-27 18:03:20 +0100401 /* keyword handler variable address default */
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200402 {"start" , udhcp_str2nip , OFS(start_ip ), "192.168.0.20"},
403 {"end" , udhcp_str2nip , OFS(end_ip ), "192.168.0.254"},
404 {"interface" , read_str , OFS(interface ), "eth0"},
405 /* Avoid "max_leases value not sane" warning by setting default
406 * to default_end_ip - default_start_ip + 1: */
407 {"max_leases" , read_u32 , OFS(max_leases ), "235"},
408 {"auto_time" , read_u32 , OFS(auto_time ), "7200"},
409 {"decline_time" , read_u32 , OFS(decline_time ), "3600"},
410 {"conflict_time", read_u32 , OFS(conflict_time), "3600"},
411 {"offer_time" , read_u32 , OFS(offer_time ), "60"},
412 {"min_lease" , read_u32 , OFS(min_lease_sec), "60"},
413 {"lease_file" , read_str , OFS(lease_file ), LEASES_FILE},
Norbert Lange05faa612020-02-14 22:16:10 +0100414 {"pidfile" , read_str , OFS(pidfile ), PID_FILE_PATH "/udhcpd.pid"},
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200415 {"siaddr" , udhcp_str2nip , OFS(siaddr_nip ), "0.0.0.0"},
416 /* keywords with no defaults must be last! */
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +0200417 {"option" , read_optset , OFS(options ), ""},
418 {"opt" , read_optset , OFS(options ), ""},
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200419 {"notify_file" , read_str , OFS(notify_file ), NULL},
420 {"sname" , read_str , OFS(sname ), NULL},
421 {"boot_file" , read_str , OFS(boot_file ), NULL},
422 {"static_lease" , read_staticlease, OFS(static_leases), ""},
423};
424enum { KWS_WITH_DEFAULTS = ARRAY_SIZE(keywords) - 6 };
425
426static NOINLINE void read_config(const char *file)
427{
428 parser_t *parser;
429 const struct config_keyword *k;
430 unsigned i;
431 char *token[2];
432
433 for (i = 0; i < KWS_WITH_DEFAULTS; i++)
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200434 keywords[i].handler(keywords[i].def, (char*)&server_data + keywords[i].ofs);
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200435
436 parser = config_open(file);
437 while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL)) {
438 for (k = keywords, i = 0; i < ARRAY_SIZE(keywords); k++, i++) {
439 if (strcasecmp(token[0], k->keyword) == 0) {
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200440 if (!k->handler(token[1], (char*)&server_data + k->ofs)) {
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200441 bb_error_msg("can't parse line %u in %s",
442 parser->lineno, file);
443 /* reset back to the default value */
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200444 k->handler(k->def, (char*)&server_data + k->ofs);
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200445 }
446 break;
447 }
448 }
449 }
450 config_close(parser);
451
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200452 server_data.start_ip = ntohl(server_data.start_ip);
453 server_data.end_ip = ntohl(server_data.end_ip);
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200454}
455
456static void write_leases(void)
457{
458 int fd;
459 unsigned i;
460 leasetime_t curr;
461 int64_t written_at;
462
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200463 fd = open_or_warn(server_data.lease_file, O_WRONLY|O_CREAT|O_TRUNC);
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200464 if (fd < 0)
465 return;
466
467 curr = written_at = time(NULL);
468
469 written_at = SWAP_BE64(written_at);
470 full_write(fd, &written_at, sizeof(written_at));
471
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200472 for (i = 0; i < server_data.max_leases; i++) {
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200473 leasetime_t tmp_time;
474
475 if (g_leases[i].lease_nip == 0)
476 continue;
477
478 /* Screw with the time in the struct, for easier writing */
479 tmp_time = g_leases[i].expires;
480
481 g_leases[i].expires -= curr;
482 if ((signed_leasetime_t) g_leases[i].expires < 0)
483 g_leases[i].expires = 0;
484 g_leases[i].expires = htonl(g_leases[i].expires);
485
486 /* No error check. If the file gets truncated,
487 * we lose some leases on restart. Oh well. */
488 full_write(fd, &g_leases[i], sizeof(g_leases[i]));
489
490 /* Then restore it when done */
491 g_leases[i].expires = tmp_time;
492 }
493 close(fd);
494
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200495 if (server_data.notify_file) {
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200496 char *argv[3];
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200497 argv[0] = server_data.notify_file;
498 argv[1] = server_data.lease_file;
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200499 argv[2] = NULL;
500 spawn_and_wait(argv);
501 }
502}
503
504static NOINLINE void read_leases(const char *file)
505{
506 struct dyn_lease lease;
507 int64_t written_at, time_passed;
508 int fd;
509#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
510 unsigned i = 0;
511#endif
512
513 fd = open_or_warn(file, O_RDONLY);
514 if (fd < 0)
515 return;
516
517 if (full_read(fd, &written_at, sizeof(written_at)) != sizeof(written_at))
518 goto ret;
519 written_at = SWAP_BE64(written_at);
520
521 time_passed = time(NULL) - written_at;
522 /* Strange written_at, or lease file from old version of udhcpd
523 * which had no "written_at" field? */
524 if ((uint64_t)time_passed > 12 * 60 * 60)
525 goto ret;
526
527 while (full_read(fd, &lease, sizeof(lease)) == sizeof(lease)) {
528 uint32_t y = ntohl(lease.lease_nip);
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200529 if (y >= server_data.start_ip && y <= server_data.end_ip) {
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200530 signed_leasetime_t expires = ntohl(lease.expires) - (signed_leasetime_t)time_passed;
531 uint32_t static_nip;
532
533 if (expires <= 0)
534 /* We keep expired leases: add_lease() will add
535 * a lease with 0 seconds remaining.
536 * Fewer IP address changes this way for mass reboot scenario.
537 */
538 expires = 0;
539
540 /* Check if there is a different static lease for this IP or MAC */
Denys Vlasenko15021f32019-05-10 15:55:12 +0200541 static_nip = get_static_nip_by_mac(lease.lease_mac);
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200542 if (static_nip) {
543 /* NB: we do not add lease even if static_nip == lease.lease_nip.
544 */
545 continue;
546 }
Denys Vlasenko15021f32019-05-10 15:55:12 +0200547 if (is_nip_reserved_as_static(lease.lease_nip))
Denys Vlasenko2bf29312016-10-04 00:37:50 +0200548 continue;
549
550 /* NB: add_lease takes "relative time", IOW,
551 * lease duration, not lease deadline. */
552 if (add_lease(lease.lease_mac, lease.lease_nip,
553 expires,
554 lease.hostname, sizeof(lease.hostname)
555 ) == 0
556 ) {
557 bb_error_msg("too many leases while loading %s", file);
558 break;
559 }
560#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
561 i++;
562#endif
563 }
564 }
565 log1("read %d leases", i);
566 ret:
567 close(fd);
568}
Mike Frysinger7031f622006-05-08 03:20:50 +0000569
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100570/* Send a packet to a specific mac address and ip address by creating our own ip packet */
571static void send_packet_to_client(struct dhcp_packet *dhcp_pkt, int force_broadcast)
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100572{
573 const uint8_t *chaddr;
574 uint32_t ciaddr;
575
576 // Was:
577 //if (force_broadcast) { /* broadcast */ }
578 //else if (dhcp_pkt->ciaddr) { /* unicast to dhcp_pkt->ciaddr */ }
579 //else if (dhcp_pkt->flags & htons(BROADCAST_FLAG)) { /* broadcast */ }
580 //else { /* unicast to dhcp_pkt->yiaddr */ }
581 // But this is wrong: yiaddr is _our_ idea what client's IP is
582 // (for example, from lease file). Client may not know that,
583 // and may not have UDP socket listening on that IP!
584 // We should never unicast to dhcp_pkt->yiaddr!
585 // dhcp_pkt->ciaddr, OTOH, comes from client's request packet,
586 // and can be used.
587
588 if (force_broadcast
589 || (dhcp_pkt->flags & htons(BROADCAST_FLAG))
Denys Vlasenko53f72bb2010-03-21 06:46:09 +0100590 || dhcp_pkt->ciaddr == 0
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100591 ) {
James Byrne69374872019-07-02 11:35:03 +0200592 log1s("broadcasting packet to client");
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100593 ciaddr = INADDR_BROADCAST;
594 chaddr = MAC_BCAST_ADDR;
595 } else {
James Byrne69374872019-07-02 11:35:03 +0200596 log1s("unicasting packet to client ciaddr");
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100597 ciaddr = dhcp_pkt->ciaddr;
598 chaddr = dhcp_pkt->chaddr;
599 }
600
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100601 udhcp_send_raw_packet(dhcp_pkt,
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200602 /*src*/ server_data.server_nip, SERVER_PORT,
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100603 /*dst*/ ciaddr, CLIENT_PORT, chaddr,
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200604 server_data.ifindex);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100605}
606
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100607/* Send a packet to gateway_nip using the kernel ip stack */
608static void send_packet_to_relay(struct dhcp_packet *dhcp_pkt)
609{
James Byrne69374872019-07-02 11:35:03 +0200610 log1s("forwarding packet to relay");
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100611
612 udhcp_send_kernel_packet(dhcp_pkt,
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200613 server_data.server_nip, SERVER_PORT,
Michal Kaziorb8176992020-12-15 09:53:40 +0000614 dhcp_pkt->gateway_nip, SERVER_PORT,
615 server_data.interface);
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100616}
617
618static void send_packet(struct dhcp_packet *dhcp_pkt, int force_broadcast)
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100619{
620 if (dhcp_pkt->gateway_nip)
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100621 send_packet_to_relay(dhcp_pkt);
622 else
623 send_packet_to_client(dhcp_pkt, force_broadcast);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100624}
625
Denys Vlasenko25393fb2019-05-16 11:27:28 +0200626static void send_packet_verbose(struct dhcp_packet *dhcp_pkt, const char *fmt)
627{
628 struct in_addr addr;
629 addr.s_addr = dhcp_pkt->yiaddr;
630 bb_info_msg(fmt, inet_ntoa(addr));
631 /* send_packet emits error message itself if it detects failure */
632 send_packet(dhcp_pkt, /*force_bcast:*/ 0);
633}
634
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100635static void init_packet(struct dhcp_packet *packet, struct dhcp_packet *oldpacket, char type)
636{
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100637 /* Sets op, htype, hlen, cookie fields
638 * and adds DHCP_MESSAGE_TYPE option */
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100639 udhcp_init_header(packet, type);
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100640
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100641 packet->xid = oldpacket->xid;
642 memcpy(packet->chaddr, oldpacket->chaddr, sizeof(oldpacket->chaddr));
643 packet->flags = oldpacket->flags;
644 packet->gateway_nip = oldpacket->gateway_nip;
645 packet->ciaddr = oldpacket->ciaddr;
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200646 udhcp_add_simple_option(packet, DHCP_SERVER_ID, server_data.server_nip);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100647}
648
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100649/* Fill options field, siaddr_nip, and sname and boot_file fields.
650 * TODO: teach this code to use overload option.
651 */
652static void add_server_options(struct dhcp_packet *packet)
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100653{
Denys Vlasenkoa8408842019-05-16 11:18:49 +0200654 struct option_set *config_opts;
655 uint8_t *client_hostname_opt;
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100656
Denys Vlasenkoa8408842019-05-16 11:18:49 +0200657 client_hostname_opt = NULL;
658 if (packet->yiaddr) { /* if we aren't from send_inform()... */
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200659 struct static_lease *st_lease = server_data.static_leases;
Denys Vlasenkoa8408842019-05-16 11:18:49 +0200660 while (st_lease) {
661 if (st_lease->nip == packet->yiaddr) {
662 if (st_lease->opt[0] != 0)
663 client_hostname_opt = st_lease->opt;
664 break;
665 }
666 st_lease = st_lease->next;
667 }
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100668 }
669
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200670 config_opts = server_data.options;
Denys Vlasenkoa8408842019-05-16 11:18:49 +0200671 while (config_opts) {
672 if (config_opts->data[OPT_CODE] != DHCP_LEASE_TIME) {
673 /* ^^^^
674 * DHCP_LEASE_TIME is already filled, or in case of
675 * send_inform(), should not be filled at all.
676 */
677 if (config_opts->data[OPT_CODE] != DHCP_HOST_NAME
678 || !client_hostname_opt
679 ) {
680 /* Why "!client_hostname_opt":
681 * add hostname only if client has no hostname
682 * on its static lease line.
683 * (Not that "opt hostname HOST"
684 * makes much sense in udhcpd.conf,
685 * that'd give all clients the same hostname,
686 * but it's a valid configuration).
687 */
688 udhcp_add_binary_option(packet, config_opts->data);
689 }
690 }
691 config_opts = config_opts->next;
692 }
693
694 if (client_hostname_opt)
695 udhcp_add_binary_option(packet, client_hostname_opt);
696
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200697 packet->siaddr_nip = server_data.siaddr_nip;
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100698
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200699 if (server_data.sname)
700 strncpy((char*)packet->sname, server_data.sname, sizeof(packet->sname) - 1);
701 if (server_data.boot_file)
702 strncpy((char*)packet->file, server_data.boot_file, sizeof(packet->file) - 1);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100703}
704
705static uint32_t select_lease_time(struct dhcp_packet *packet)
706{
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200707 uint32_t lease_time_sec = server_data.max_lease_sec;
Denys Vlasenko6d3b4bb2018-12-17 18:07:18 +0100708 uint8_t *lease_time_opt = udhcp_get_option32(packet, DHCP_LEASE_TIME);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100709 if (lease_time_opt) {
710 move_from_unaligned32(lease_time_sec, lease_time_opt);
711 lease_time_sec = ntohl(lease_time_sec);
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200712 if (lease_time_sec > server_data.max_lease_sec)
713 lease_time_sec = server_data.max_lease_sec;
714 if (lease_time_sec < server_data.min_lease_sec)
715 lease_time_sec = server_data.min_lease_sec;
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100716 }
717 return lease_time_sec;
718}
719
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100720/* We got a DHCP DISCOVER. Send an OFFER. */
Denys Vlasenko0bb35e12010-10-21 12:33:10 +0200721/* NOINLINE: limit stack usage in caller */
Denys Vlasenkofa5e2952010-11-28 01:10:51 +0100722static NOINLINE void send_offer(struct dhcp_packet *oldpacket,
723 uint32_t static_lease_nip,
724 struct dyn_lease *lease,
Denys Vlasenko84029692019-05-15 13:08:48 +0200725 uint32_t requested_nip,
Michel Stam9f412712014-10-30 11:59:04 +0100726 unsigned arpping_ms)
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100727{
728 struct dhcp_packet packet;
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100729 uint32_t lease_time_sec;
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100730
731 init_packet(&packet, oldpacket, DHCPOFFER);
732
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100733 /* If it is a static lease, use its IP */
734 packet.yiaddr = static_lease_nip;
735 /* Else: */
Denys Vlasenkoa9539872010-03-20 03:49:27 +0100736 if (!static_lease_nip) {
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100737 /* We have no static lease for client's chaddr */
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100738 const char *p_host_name;
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100739
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100740 if (lease) {
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100741 /* We have a dynamic lease for client's chaddr.
742 * Reuse its IP (even if lease is expired).
743 * Note that we ignore requested IP in this case.
744 */
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100745 packet.yiaddr = lease->lease_nip;
746 }
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100747 /* Or: if client has requested an IP */
Denys Vlasenko84029692019-05-15 13:08:48 +0200748 else if (requested_nip != 0
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100749 /* and the IP is in the lease range */
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200750 && ntohl(requested_nip) >= server_data.start_ip
751 && ntohl(requested_nip) <= server_data.end_ip
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100752 /* and */
Denys Vlasenko84029692019-05-15 13:08:48 +0200753 && ( !(lease = find_lease_by_nip(requested_nip)) /* is not already taken */
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100754 || is_expired_lease(lease) /* or is taken, but expired */
755 )
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100756 ) {
Denys Vlasenko84029692019-05-15 13:08:48 +0200757 packet.yiaddr = requested_nip;
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100758 }
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100759 else {
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100760 /* Otherwise, find a free IP */
Michel Stam9f412712014-10-30 11:59:04 +0100761 packet.yiaddr = find_free_or_expired_nip(oldpacket->chaddr, arpping_ms);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100762 }
763
764 if (!packet.yiaddr) {
James Byrne69374872019-07-02 11:35:03 +0200765 bb_simple_error_msg("no free IP addresses. OFFER abandoned");
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100766 return;
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100767 }
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100768 /* Reserve the IP for a short time hoping to get DHCPREQUEST soon */
Denys Vlasenkodde8bdc2010-03-22 14:29:13 +0100769 p_host_name = (const char*) udhcp_get_option(oldpacket, DHCP_HOST_NAME);
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100770 lease = add_lease(packet.chaddr, packet.yiaddr,
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200771 server_data.offer_time,
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100772 p_host_name,
773 p_host_name ? (unsigned char)p_host_name[OPT_LEN - OPT_DATA] : 0
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100774 );
775 if (!lease) {
James Byrne69374872019-07-02 11:35:03 +0200776 bb_simple_error_msg("no free IP addresses. OFFER abandoned");
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100777 return;
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100778 }
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100779 }
780
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100781 lease_time_sec = select_lease_time(oldpacket);
Denys Vlasenko7724c762010-03-26 09:32:09 +0100782 udhcp_add_simple_option(&packet, DHCP_LEASE_TIME, htonl(lease_time_sec));
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100783 add_server_options(&packet);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100784
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100785 /* send_packet emits error message itself if it detects failure */
Denys Vlasenko25393fb2019-05-16 11:27:28 +0200786 send_packet_verbose(&packet, "sending OFFER to %s");
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100787}
788
Denys Vlasenko0bb35e12010-10-21 12:33:10 +0200789/* NOINLINE: limit stack usage in caller */
790static NOINLINE void send_NAK(struct dhcp_packet *oldpacket)
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100791{
792 struct dhcp_packet packet;
793
794 init_packet(&packet, oldpacket, DHCPNAK);
795
Denys Vlasenko16efe192016-03-30 18:44:52 +0200796 log1("sending %s", "NAK");
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100797 send_packet(&packet, /*force_bcast:*/ 1);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100798}
799
Denys Vlasenko0bb35e12010-10-21 12:33:10 +0200800/* NOINLINE: limit stack usage in caller */
801static NOINLINE void send_ACK(struct dhcp_packet *oldpacket, uint32_t yiaddr)
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100802{
803 struct dhcp_packet packet;
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100804 uint32_t lease_time_sec;
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100805 const char *p_host_name;
806
807 init_packet(&packet, oldpacket, DHCPACK);
808 packet.yiaddr = yiaddr;
809
810 lease_time_sec = select_lease_time(oldpacket);
Denys Vlasenko7724c762010-03-26 09:32:09 +0100811 udhcp_add_simple_option(&packet, DHCP_LEASE_TIME, htonl(lease_time_sec));
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100812 add_server_options(&packet);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100813
Denys Vlasenko25393fb2019-05-16 11:27:28 +0200814 send_packet_verbose(&packet, "sending ACK to %s");
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100815
Denys Vlasenkodde8bdc2010-03-22 14:29:13 +0100816 p_host_name = (const char*) udhcp_get_option(oldpacket, DHCP_HOST_NAME);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100817 add_lease(packet.chaddr, packet.yiaddr,
818 lease_time_sec,
819 p_host_name,
820 p_host_name ? (unsigned char)p_host_name[OPT_LEN - OPT_DATA] : 0
821 );
822 if (ENABLE_FEATURE_UDHCPD_WRITE_LEASES_EARLY) {
823 /* rewrite the file with leases at every new acceptance */
824 write_leases();
825 }
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100826}
827
Denys Vlasenko0bb35e12010-10-21 12:33:10 +0200828/* NOINLINE: limit stack usage in caller */
829static NOINLINE void send_inform(struct dhcp_packet *oldpacket)
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100830{
831 struct dhcp_packet packet;
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100832
Denys Vlasenkof8fcc182010-04-04 22:36:34 +0200833 /* "If a client has obtained a network address through some other means
834 * (e.g., manual configuration), it may use a DHCPINFORM request message
835 * to obtain other local configuration parameters. Servers receiving a
836 * DHCPINFORM message construct a DHCPACK message with any local
837 * configuration parameters appropriate for the client without:
838 * allocating a new address, checking for an existing binding, filling
839 * in 'yiaddr' or including lease time parameters. The servers SHOULD
840 * unicast the DHCPACK reply to the address given in the 'ciaddr' field
841 * of the DHCPINFORM message.
842 * ...
843 * The server responds to a DHCPINFORM message by sending a DHCPACK
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100844 * message directly to the address given in the 'ciaddr' field
845 * of the DHCPINFORM message. The server MUST NOT send a lease
846 * expiration time to the client and SHOULD NOT fill in 'yiaddr'."
847 */
Denys Vlasenkof8fcc182010-04-04 22:36:34 +0200848//TODO: do a few sanity checks: is ciaddr set?
849//Better yet: is ciaddr == IP source addr?
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100850 init_packet(&packet, oldpacket, DHCPACK);
Denys Vlasenkoe5ce91b2010-03-21 00:43:11 +0100851 add_server_options(&packet);
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100852
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100853 send_packet(&packet, /*force_bcast:*/ 0);
Denys Vlasenko25393fb2019-05-16 11:27:28 +0200854 // or maybe? send_packet_verbose(&packet, "sending ACK to %s");
Denys Vlasenko8a7c1662010-03-20 03:48:11 +0100855}
856
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000857int udhcpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000858int udhcpd_main(int argc UNUSED_PARAM, char **argv)
Mike Frysinger7031f622006-05-08 03:20:50 +0000859{
Denys Vlasenko52a515d2017-02-16 23:25:44 +0100860 int server_socket = -1, retval;
Denys Vlasenko6947d2c2009-06-17 13:24:03 +0200861 uint8_t *state;
Denis Vlasenko42b3dea2007-07-03 15:47:50 +0000862 unsigned timeout_end;
863 unsigned num_ips;
Denis Vlasenko3d17d2b2007-08-14 16:45:29 +0000864 unsigned opt;
Mike Frysinger7031f622006-05-08 03:20:50 +0000865 struct option_set *option;
Denys Vlasenkoe3f5b732013-03-13 22:27:37 +0100866 char *str_I = str_I;
Michel Stam9f412712014-10-30 11:59:04 +0100867 const char *str_a = "2000";
868 unsigned arpping_ms;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000869 IF_FEATURE_UDHCP_PORT(char *str_P;)
Mike Frysinger7031f622006-05-08 03:20:50 +0000870
Denys Vlasenkodf70a432016-04-21 18:54:36 +0200871 setup_common_bufsiz();
872
873 IF_FEATURE_UDHCP_PORT(SERVER_PORT = 67;)
874 IF_FEATURE_UDHCP_PORT(CLIENT_PORT = 68;)
Denis Vlasenkod55fe3e2008-02-04 13:12:16 +0000875
Denys Vlasenko65c34c52019-05-31 23:39:22 +0200876 /* Make sure fd 0,1,2 are open */
877 /* Setup the signal pipe on fds 3,4 - must be before openlog() */
878 udhcp_sp_setup();
879
Denys Vlasenko22542ec2017-08-08 21:55:02 +0200880 opt = getopt32(argv, "^"
881 "fSI:va:"IF_FEATURE_UDHCP_PORT("P:")
882 "\0"
Denys Vlasenkoac906fa2009-06-17 11:54:52 +0200883#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
Denys Vlasenko22542ec2017-08-08 21:55:02 +0200884 "vv"
Denys Vlasenkoac906fa2009-06-17 11:54:52 +0200885#endif
Denys Vlasenkoe3f5b732013-03-13 22:27:37 +0100886 , &str_I
Michel Stam9f412712014-10-30 11:59:04 +0100887 , &str_a
Denys Vlasenkoe3f5b732013-03-13 22:27:37 +0100888 IF_FEATURE_UDHCP_PORT(, &str_P)
Leonid Lisovskiy6c9c0a12011-10-18 00:35:47 +0200889 IF_UDHCP_VERBOSE(, &dhcp_verbose)
Denys Vlasenkoac906fa2009-06-17 11:54:52 +0200890 );
Denis Vlasenko3d17d2b2007-08-14 16:45:29 +0000891 if (!(opt & 1)) { /* no -f */
Denis Vlasenkoc82b5102007-07-01 17:05:57 +0000892 bb_daemonize_or_rexec(0, argv);
Denis Vlasenkoa19e6492009-03-11 14:40:00 +0000893 logmode = LOGMODE_NONE;
Denis Vlasenkoaf1c8432007-03-26 13:22:35 +0000894 }
Mike Frysinger6db13732010-06-04 13:24:50 -0400895 /* update argv after the possible vfork+exec in daemonize */
896 argv += optind;
Denis Vlasenko3d17d2b2007-08-14 16:45:29 +0000897 if (opt & 2) { /* -S */
Denis Vlasenko5e4fda02009-03-08 23:46:48 +0000898 openlog(applet_name, LOG_PID, LOG_DAEMON);
Denis Vlasenkoaf1c8432007-03-26 13:22:35 +0000899 logmode |= LOGMODE_SYSLOG;
900 }
Denys Vlasenkoe3f5b732013-03-13 22:27:37 +0100901 if (opt & 4) { /* -I */
902 len_and_sockaddr *lsa = xhost_and_af2sockaddr(str_I, 0, AF_INET);
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200903 server_data.server_nip = lsa->u.sin.sin_addr.s_addr;
Denys Vlasenkoe3f5b732013-03-13 22:27:37 +0100904 free(lsa);
905 }
Denis Vlasenkod55fe3e2008-02-04 13:12:16 +0000906#if ENABLE_FEATURE_UDHCP_PORT
Michel Stam9f412712014-10-30 11:59:04 +0100907 if (opt & 32) { /* -P */
Denis Vlasenkod55fe3e2008-02-04 13:12:16 +0000908 SERVER_PORT = xatou16(str_P);
909 CLIENT_PORT = SERVER_PORT + 1;
910 }
911#endif
Michel Stam9f412712014-10-30 11:59:04 +0100912 arpping_ms = xatou(str_a);
913
Denis Vlasenkoaf1c8432007-03-26 13:22:35 +0000914 /* Would rather not do read_config before daemonization -
915 * otherwise NOMMU machines will parse config twice */
Denis Vlasenko9f7b92a2007-08-15 20:03:36 +0000916 read_config(argv[0] ? argv[0] : DHCPD_CONF_FILE);
Denys Vlasenko3d306bf2018-03-11 11:34:44 +0100917 /* prevent poll timeout overflow */
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200918 if (server_data.auto_time > INT_MAX / 1000)
919 server_data.auto_time = INT_MAX / 1000;
Mike Frysinger7031f622006-05-08 03:20:50 +0000920
Denis Vlasenko80edead2007-08-02 22:31:05 +0000921 /* Create pidfile */
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200922 write_pidfile(server_data.pidfile);
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100923 /* if (!..) bb_perror_msg("can't create pidfile %s", pidfile); */
Denis Vlasenko80edead2007-08-02 22:31:05 +0000924
James Byrne69374872019-07-02 11:35:03 +0200925 bb_simple_info_msg("started, v"BB_VER);
Mike Frysinger7031f622006-05-08 03:20:50 +0000926
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200927 option = udhcp_find_option(server_data.options, DHCP_LEASE_TIME);
928 server_data.max_lease_sec = DEFAULT_LEASE_TIME;
Denis Vlasenkoaf1c8432007-03-26 13:22:35 +0000929 if (option) {
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200930 move_from_unaligned32(server_data.max_lease_sec, option->data + OPT_DATA);
931 server_data.max_lease_sec = ntohl(server_data.max_lease_sec);
Denis Vlasenko6e6d3312007-05-03 23:39:35 +0000932 }
Mike Frysinger7031f622006-05-08 03:20:50 +0000933
934 /* Sanity check */
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200935 num_ips = server_data.end_ip - server_data.start_ip + 1;
936 if (server_data.max_leases > num_ips) {
Denis Vlasenko42b3dea2007-07-03 15:47:50 +0000937 bb_error_msg("max_leases=%u is too big, setting to %u",
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200938 (unsigned)server_data.max_leases, num_ips);
939 server_data.max_leases = num_ips;
Mike Frysinger7031f622006-05-08 03:20:50 +0000940 }
941
Denys Vlasenko06076492018-02-01 10:41:14 +0100942 /* this sets g_leases */
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200943 SET_PTR_TO_GLOBALS(xzalloc(server_data.max_leases * sizeof(g_leases[0])));
Denys Vlasenko06076492018-02-01 10:41:14 +0100944
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200945 read_leases(server_data.lease_file);
Mike Frysinger7031f622006-05-08 03:20:50 +0000946
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200947 if (udhcp_read_interface(server_data.interface,
948 &server_data.ifindex,
949 (server_data.server_nip == 0 ? &server_data.server_nip : NULL),
950 server_data.server_mac)
Denys Vlasenko26918dd2009-06-16 12:04:23 +0200951 ) {
Denis Vlasenko6e6d3312007-05-03 23:39:35 +0000952 retval = 1;
953 goto ret;
954 }
Mike Frysinger7031f622006-05-08 03:20:50 +0000955
Denys Vlasenko71045cc2012-07-24 17:21:26 +0200956 continue_with_autotime:
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200957 timeout_end = monotonic_sec() + server_data.auto_time;
Denis Vlasenko5a3395b2006-11-18 19:51:32 +0000958 while (1) { /* loop until universe collapses */
Denys Vlasenko52a515d2017-02-16 23:25:44 +0100959 struct pollfd pfds[2];
Denys Vlasenkofa5e2952010-11-28 01:10:51 +0100960 struct dhcp_packet packet;
Denis Vlasenko0416e3d2009-01-01 17:52:09 +0000961 int bytes;
Denys Vlasenko52a515d2017-02-16 23:25:44 +0100962 int tv;
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +0100963 uint8_t *server_id_opt;
Denys Vlasenkofa5e2952010-11-28 01:10:51 +0100964 uint8_t *requested_ip_opt;
Denys Vlasenko84029692019-05-15 13:08:48 +0200965 uint32_t requested_nip;
Denys Vlasenkofa5e2952010-11-28 01:10:51 +0100966 uint32_t static_lease_nip;
967 struct dyn_lease *lease, fake_lease;
Mike Frysinger7031f622006-05-08 03:20:50 +0000968
Denis Vlasenkoe2d3ded2006-11-27 23:43:28 +0000969 if (server_socket < 0) {
Denis Vlasenkof1980f62008-09-26 09:34:59 +0000970 server_socket = udhcp_listen_socket(/*INADDR_ANY,*/ SERVER_PORT,
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200971 server_data.interface);
Denis Vlasenkoe2d3ded2006-11-27 23:43:28 +0000972 }
Mike Frysinger7031f622006-05-08 03:20:50 +0000973
Denys Vlasenko52a515d2017-02-16 23:25:44 +0100974 udhcp_sp_fd_set(pfds, server_socket);
Denys Vlasenko3d306bf2018-03-11 11:34:44 +0100975
976 new_tv:
977 tv = -1;
Denys Vlasenko91755cb2019-05-30 16:23:34 +0200978 if (server_data.auto_time) {
Denys Vlasenko3d306bf2018-03-11 11:34:44 +0100979 tv = timeout_end - monotonic_sec();
980 if (tv <= 0) {
981 write_leases:
Denys Vlasenko3293bc12018-03-10 19:01:48 +0100982 write_leases();
983 goto continue_with_autotime;
984 }
Denys Vlasenko3d306bf2018-03-11 11:34:44 +0100985 tv *= 1000;
986 }
987
988 /* Block here waiting for either signal or packet */
989 retval = poll(pfds, 2, tv);
990 if (retval <= 0) {
991 if (retval == 0)
992 goto write_leases;
993 if (errno == EINTR)
994 goto new_tv;
Denys Vlasenko3293bc12018-03-10 19:01:48 +0100995 /* < 0 and not EINTR: should not happen */
James Byrne69374872019-07-02 11:35:03 +0200996 bb_simple_perror_msg_and_die("poll");
Mike Frysinger7031f622006-05-08 03:20:50 +0000997 }
998
Denys Vlasenko3293bc12018-03-10 19:01:48 +0100999 if (pfds[0].revents) switch (udhcp_sp_read()) {
Mike Frysinger7031f622006-05-08 03:20:50 +00001000 case SIGUSR1:
James Byrne253c4e72019-04-12 17:01:51 +00001001 bb_info_msg("received %s", "SIGUSR1");
Mike Frysinger7031f622006-05-08 03:20:50 +00001002 write_leases();
1003 /* why not just reset the timeout, eh */
Denys Vlasenko71045cc2012-07-24 17:21:26 +02001004 goto continue_with_autotime;
Mike Frysinger7031f622006-05-08 03:20:50 +00001005 case SIGTERM:
James Byrne253c4e72019-04-12 17:01:51 +00001006 bb_info_msg("received %s", "SIGTERM");
Denys Vlasenko71045cc2012-07-24 17:21:26 +02001007 write_leases();
Denis Vlasenko6e6d3312007-05-03 23:39:35 +00001008 goto ret0;
Mike Frysinger7031f622006-05-08 03:20:50 +00001009 }
1010
Denys Vlasenko3293bc12018-03-10 19:01:48 +01001011 /* Is it a packet? */
1012 if (!pfds[1].revents)
1013 continue; /* no */
1014
1015 /* Note: we do not block here, we block on poll() instead.
1016 * Blocking here would prevent SIGTERM from working:
1017 * socket read inside this call is restarted on caught signals.
1018 */
Denis Vlasenko0416e3d2009-01-01 17:52:09 +00001019 bytes = udhcp_recv_kernel_packet(&packet, server_socket);
Denis Vlasenkoaf1c8432007-03-26 13:22:35 +00001020 if (bytes < 0) {
Denis Vlasenko0416e3d2009-01-01 17:52:09 +00001021 /* bytes can also be -2 ("bad packet data") */
Mike Frysinger7031f622006-05-08 03:20:50 +00001022 if (bytes == -1 && errno != EINTR) {
Denys Vlasenko6f97b302017-09-29 18:17:25 +02001023 log1("read error: "STRERROR_FMT", reopening socket" STRERROR_ERRNO);
Mike Frysinger7031f622006-05-08 03:20:50 +00001024 close(server_socket);
1025 server_socket = -1;
1026 }
1027 continue;
1028 }
Denys Vlasenko31af3d52009-06-17 11:57:09 +02001029 if (packet.hlen != 6) {
James Byrne69374872019-07-02 11:35:03 +02001030 bb_info_msg("MAC length != 6%s", ", ignoring packet");
Denys Vlasenko31af3d52009-06-17 11:57:09 +02001031 continue;
1032 }
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001033 if (packet.op != BOOTREQUEST) {
James Byrne69374872019-07-02 11:35:03 +02001034 bb_info_msg("not a REQUEST%s", ", ignoring packet");
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001035 continue;
1036 }
Denys Vlasenkodde8bdc2010-03-22 14:29:13 +01001037 state = udhcp_get_option(&packet, DHCP_MESSAGE_TYPE);
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001038 if (state == NULL || state[0] < DHCP_MINTYPE || state[0] > DHCP_MAXTYPE) {
James Byrne69374872019-07-02 11:35:03 +02001039 bb_info_msg("no or bad message type option%s", ", ignoring packet");
Mike Frysinger7031f622006-05-08 03:20:50 +00001040 continue;
1041 }
1042
Denys Vlasenkofa5e2952010-11-28 01:10:51 +01001043 /* Get SERVER_ID if present */
Denys Vlasenko6d3b4bb2018-12-17 18:07:18 +01001044 server_id_opt = udhcp_get_option32(&packet, DHCP_SERVER_ID);
Denys Vlasenkofa5e2952010-11-28 01:10:51 +01001045 if (server_id_opt) {
Denys Vlasenko713d2412010-11-28 21:51:44 +01001046 uint32_t server_id_network_order;
1047 move_from_unaligned32(server_id_network_order, server_id_opt);
Denys Vlasenko91755cb2019-05-30 16:23:34 +02001048 if (server_id_network_order != server_data.server_nip) {
Denys Vlasenkofa5e2952010-11-28 01:10:51 +01001049 /* client talks to somebody else */
Denys Vlasenkoa4959ee2021-02-21 16:32:07 +01001050 log1("server ID doesn't match%s", ", ignoring packet");
Denys Vlasenkofa5e2952010-11-28 01:10:51 +01001051 continue;
1052 }
1053 }
1054
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001055 /* Look for a static/dynamic lease */
Denys Vlasenko15021f32019-05-10 15:55:12 +02001056 static_lease_nip = get_static_nip_by_mac(&packet.chaddr);
Denys Vlasenkoa9539872010-03-20 03:49:27 +01001057 if (static_lease_nip) {
James Byrne253c4e72019-04-12 17:01:51 +00001058 bb_info_msg("found static lease: %x", static_lease_nip);
Denys Vlasenko6947d2c2009-06-17 13:24:03 +02001059 memcpy(&fake_lease.lease_mac, &packet.chaddr, 6);
Denys Vlasenkoa9539872010-03-20 03:49:27 +01001060 fake_lease.lease_nip = static_lease_nip;
Denys Vlasenko6947d2c2009-06-17 13:24:03 +02001061 fake_lease.expires = 0;
Denys Vlasenko6947d2c2009-06-17 13:24:03 +02001062 lease = &fake_lease;
Denis Vlasenko5a3395b2006-11-18 19:51:32 +00001063 } else {
Denys Vlasenko6947d2c2009-06-17 13:24:03 +02001064 lease = find_lease_by_mac(packet.chaddr);
Mike Frysinger7031f622006-05-08 03:20:50 +00001065 }
1066
Denys Vlasenkofa5e2952010-11-28 01:10:51 +01001067 /* Get REQUESTED_IP if present */
Denys Vlasenko84029692019-05-15 13:08:48 +02001068 requested_nip = 0;
Denys Vlasenko6d3b4bb2018-12-17 18:07:18 +01001069 requested_ip_opt = udhcp_get_option32(&packet, DHCP_REQUESTED_IP);
Denys Vlasenkofa5e2952010-11-28 01:10:51 +01001070 if (requested_ip_opt) {
1071 move_from_unaligned32(requested_nip, requested_ip_opt);
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001072 }
1073
Mike Frysinger7031f622006-05-08 03:20:50 +00001074 switch (state[0]) {
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001075
Mike Frysinger7031f622006-05-08 03:20:50 +00001076 case DHCPDISCOVER:
Denys Vlasenko8f2e99c2016-03-30 18:41:23 +02001077 log1("received %s", "DISCOVER");
Mike Frysinger7031f622006-05-08 03:20:50 +00001078
Denys Vlasenko84029692019-05-15 13:08:48 +02001079 send_offer(&packet, static_lease_nip, lease, requested_nip, arpping_ms);
Mike Frysinger7031f622006-05-08 03:20:50 +00001080 break;
Denys Vlasenko6947d2c2009-06-17 13:24:03 +02001081
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001082 case DHCPREQUEST:
Denys Vlasenko8f2e99c2016-03-30 18:41:23 +02001083 log1("received %s", "REQUEST");
Denys Vlasenko53f72bb2010-03-21 06:46:09 +01001084/* RFC 2131:
Mike Frysinger7031f622006-05-08 03:20:50 +00001085
Denys Vlasenko53f72bb2010-03-21 06:46:09 +01001086o DHCPREQUEST generated during SELECTING state:
1087
1088 Client inserts the address of the selected server in 'server
1089 identifier', 'ciaddr' MUST be zero, 'requested IP address' MUST be
1090 filled in with the yiaddr value from the chosen DHCPOFFER.
1091
1092 Note that the client may choose to collect several DHCPOFFER
1093 messages and select the "best" offer. The client indicates its
1094 selection by identifying the offering server in the DHCPREQUEST
1095 message. If the client receives no acceptable offers, the client
1096 may choose to try another DHCPDISCOVER message. Therefore, the
1097 servers may not receive a specific DHCPREQUEST from which they can
1098 decide whether or not the client has accepted the offer.
1099
1100o DHCPREQUEST generated during INIT-REBOOT state:
1101
1102 'server identifier' MUST NOT be filled in, 'requested IP address'
1103 option MUST be filled in with client's notion of its previously
1104 assigned address. 'ciaddr' MUST be zero. The client is seeking to
1105 verify a previously allocated, cached configuration. Server SHOULD
1106 send a DHCPNAK message to the client if the 'requested IP address'
1107 is incorrect, or is on the wrong network.
1108
1109 Determining whether a client in the INIT-REBOOT state is on the
1110 correct network is done by examining the contents of 'giaddr', the
1111 'requested IP address' option, and a database lookup. If the DHCP
1112 server detects that the client is on the wrong net (i.e., the
1113 result of applying the local subnet mask or remote subnet mask (if
1114 'giaddr' is not zero) to 'requested IP address' option value
1115 doesn't match reality), then the server SHOULD send a DHCPNAK
1116 message to the client.
1117
1118 If the network is correct, then the DHCP server should check if
1119 the client's notion of its IP address is correct. If not, then the
1120 server SHOULD send a DHCPNAK message to the client. If the DHCP
1121 server has no record of this client, then it MUST remain silent,
1122 and MAY output a warning to the network administrator. This
1123 behavior is necessary for peaceful coexistence of non-
1124 communicating DHCP servers on the same wire.
1125
1126 If 'giaddr' is 0x0 in the DHCPREQUEST message, the client is on
1127 the same subnet as the server. The server MUST broadcast the
1128 DHCPNAK message to the 0xffffffff broadcast address because the
1129 client may not have a correct network address or subnet mask, and
1130 the client may not be answering ARP requests.
1131
1132 If 'giaddr' is set in the DHCPREQUEST message, the client is on a
1133 different subnet. The server MUST set the broadcast bit in the
1134 DHCPNAK, so that the relay agent will broadcast the DHCPNAK to the
1135 client, because the client may not have a correct network address
1136 or subnet mask, and the client may not be answering ARP requests.
1137
1138o DHCPREQUEST generated during RENEWING state:
1139
1140 'server identifier' MUST NOT be filled in, 'requested IP address'
1141 option MUST NOT be filled in, 'ciaddr' MUST be filled in with
1142 client's IP address. In this situation, the client is completely
1143 configured, and is trying to extend its lease. This message will
1144 be unicast, so no relay agents will be involved in its
1145 transmission. Because 'giaddr' is therefore not filled in, the
1146 DHCP server will trust the value in 'ciaddr', and use it when
1147 replying to the client.
1148
1149 A client MAY choose to renew or extend its lease prior to T1. The
1150 server may choose not to extend the lease (as a policy decision by
1151 the network administrator), but should return a DHCPACK message
1152 regardless.
1153
1154o DHCPREQUEST generated during REBINDING state:
1155
1156 'server identifier' MUST NOT be filled in, 'requested IP address'
1157 option MUST NOT be filled in, 'ciaddr' MUST be filled in with
1158 client's IP address. In this situation, the client is completely
1159 configured, and is trying to extend its lease. This message MUST
1160 be broadcast to the 0xffffffff IP broadcast address. The DHCP
1161 server SHOULD check 'ciaddr' for correctness before replying to
1162 the DHCPREQUEST.
1163
1164 The DHCPREQUEST from a REBINDING client is intended to accommodate
1165 sites that have multiple DHCP servers and a mechanism for
1166 maintaining consistency among leases managed by multiple servers.
1167 A DHCP server MAY extend a client's lease only if it has local
1168 administrative authority to do so.
1169*/
Denys Vlasenkofa5e2952010-11-28 01:10:51 +01001170 if (!requested_ip_opt) {
Denys Vlasenko53f72bb2010-03-21 06:46:09 +01001171 requested_nip = packet.ciaddr;
1172 if (requested_nip == 0) {
Denys Vlasenkoa4959ee2021-02-21 16:32:07 +01001173 log1("no requested IP and no ciaddr%s", ", ignoring packet");
Denys Vlasenko53f72bb2010-03-21 06:46:09 +01001174 break;
1175 }
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001176 }
1177 if (lease && requested_nip == lease->lease_nip) {
Denys Vlasenko53f72bb2010-03-21 06:46:09 +01001178 /* client requested or configured IP matches the lease.
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001179 * ACK it, and bump lease expiration time. */
1180 send_ACK(&packet, lease->lease_nip);
1181 break;
1182 }
Denys Vlasenko713d2412010-11-28 21:51:44 +01001183 /* No lease for this MAC, or lease IP != requested IP */
1184
1185 if (server_id_opt /* client is in SELECTING state */
1186 || requested_ip_opt /* client is in INIT-REBOOT state */
1187 ) {
1188 /* "No, we don't have this IP for you" */
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001189 send_NAK(&packet);
Denys Vlasenko713d2412010-11-28 21:51:44 +01001190 } /* else: client is in RENEWING or REBINDING, do not answer */
1191
Mike Frysinger7031f622006-05-08 03:20:50 +00001192 break;
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001193
Mike Frysinger7031f622006-05-08 03:20:50 +00001194 case DHCPDECLINE:
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001195 /* RFC 2131:
1196 * "If the server receives a DHCPDECLINE message,
1197 * the client has discovered through some other means
1198 * that the suggested network address is already
1199 * in use. The server MUST mark the network address
1200 * as not available and SHOULD notify the local
1201 * sysadmin of a possible configuration problem."
1202 *
1203 * SERVER_ID must be present,
1204 * REQUESTED_IP must be present,
1205 * chaddr must be filled in,
1206 * ciaddr must be 0 (we do not check this)
1207 */
Denys Vlasenko8f2e99c2016-03-30 18:41:23 +02001208 log1("received %s", "DECLINE");
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001209 if (server_id_opt
Denys Vlasenkofa5e2952010-11-28 01:10:51 +01001210 && requested_ip_opt
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001211 && lease /* chaddr matches this lease */
1212 && requested_nip == lease->lease_nip
1213 ) {
Denys Vlasenko31af3d52009-06-17 11:57:09 +02001214 memset(lease->lease_mac, 0, sizeof(lease->lease_mac));
Denys Vlasenko91755cb2019-05-30 16:23:34 +02001215 lease->expires = time(NULL) + server_data.decline_time;
Mike Frysinger7031f622006-05-08 03:20:50 +00001216 }
1217 break;
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001218
Mike Frysinger7031f622006-05-08 03:20:50 +00001219 case DHCPRELEASE:
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001220 /* "Upon receipt of a DHCPRELEASE message, the server
1221 * marks the network address as not allocated."
1222 *
1223 * SERVER_ID must be present,
1224 * REQUESTED_IP must not be present (we do not check this),
1225 * chaddr must be filled in,
1226 * ciaddr must be filled in
1227 */
Denys Vlasenko8f2e99c2016-03-30 18:41:23 +02001228 log1("received %s", "RELEASE");
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001229 if (server_id_opt
1230 && lease /* chaddr matches this lease */
1231 && packet.ciaddr == lease->lease_nip
1232 ) {
Denis Vlasenko04158e02009-02-02 10:48:06 +00001233 lease->expires = time(NULL);
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001234 }
Mike Frysinger7031f622006-05-08 03:20:50 +00001235 break;
Denys Vlasenkoc7dc79e2010-03-21 06:15:28 +01001236
Mike Frysinger7031f622006-05-08 03:20:50 +00001237 case DHCPINFORM:
Denys Vlasenko8f2e99c2016-03-30 18:41:23 +02001238 log1("received %s", "INFORM");
Mike Frysinger7031f622006-05-08 03:20:50 +00001239 send_inform(&packet);
1240 break;
Mike Frysinger7031f622006-05-08 03:20:50 +00001241 }
1242 }
Denis Vlasenko6e6d3312007-05-03 23:39:35 +00001243 ret0:
1244 retval = 0;
1245 ret:
Denys Vlasenko91755cb2019-05-30 16:23:34 +02001246 /*if (server_data.pidfile) - server_data.pidfile is never NULL */
1247 remove_pidfile(server_data.pidfile);
Denis Vlasenko6e6d3312007-05-03 23:39:35 +00001248 return retval;
Mike Frysinger7031f622006-05-08 03:20:50 +00001249}