1 /* vi: set sw=4 ts=4: */
4 * Copyright (C) 1999 Matthew Ramsay <matthewr@moreton.com.au>
5 * Chris Trew <ctrew@moreton.com.au>
7 * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
9 * 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.
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.
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.
23 //applet:IF_UDHCPD(APPLET(udhcpd, BB_DIR_USR_SBIN, BB_SUID_DROP))
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
29 //usage:#define udhcpd_trivial_usage
30 //usage: "[-fS] [-I ADDR]" IF_FEATURE_UDHCP_PORT(" [-P N]") " [CONFFILE]"
31 //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"
35 //usage: "\n -I ADDR Local address"
36 //usage: "\n -a MSEC Timeout for ARP ping (default 2000)"
37 //usage: IF_FEATURE_UDHCP_PORT(
38 //usage: "\n -P N Use port N (default 67)"
41 #include <netinet/ether.h>
48 #define g_leases ((struct dyn_lease*)ptr_to_globals)
49 /* struct server_config_t server_config is in bb_common_bufsiz1 */
51 /* Takes the address of the pointer to the static_leases linked list,
52 * address to a 6 byte mac address,
53 * 4 byte IP address */
54 static void add_static_lease(struct static_lease **st_lease_pp,
58 struct static_lease *st_lease;
60 /* Find the tail of the list */
61 while ((st_lease = *st_lease_pp) != NULL) {
62 st_lease_pp = &st_lease->next;
66 *st_lease_pp = st_lease = xzalloc(sizeof(*st_lease));
67 memcpy(st_lease->mac, mac, 6);
69 /*st_lease->next = NULL;*/
72 /* Find static lease IP by mac */
73 static uint32_t get_static_nip_by_mac(struct static_lease *st_lease, void *mac)
76 if (memcmp(st_lease->mac, mac, 6) == 0)
78 st_lease = st_lease->next;
84 static int is_nip_reserved(struct static_lease *st_lease, uint32_t nip)
87 if (st_lease->nip == nip)
89 st_lease = st_lease->next;
95 #if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 2
96 /* Print out static leases just to check what's going on */
97 /* Takes the address of the pointer to the static_leases linked list */
98 static void log_static_leases(struct static_lease **st_lease_pp)
100 struct static_lease *cur;
102 if (dhcp_verbose < 2)
107 bb_error_msg("static lease: mac:%02x:%02x:%02x:%02x:%02x:%02x nip:%x",
108 cur->mac[0], cur->mac[1], cur->mac[2],
109 cur->mac[3], cur->mac[4], cur->mac[5],
116 # define log_static_leases(st_lease_pp) ((void)0)
119 /* Find the oldest expired lease, NULL if there are no expired leases */
120 static struct dyn_lease *oldest_expired_lease(void)
122 struct dyn_lease *oldest_lease = NULL;
123 leasetime_t oldest_time = time(NULL);
126 /* Unexpired leases have g_leases[i].expires >= current time
127 * and therefore can't ever match */
128 for (i = 0; i < server_config.max_leases; i++) {
129 if (g_leases[i].expires == 0 /* empty entry */
130 || g_leases[i].expires < oldest_time
132 oldest_time = g_leases[i].expires;
133 oldest_lease = &g_leases[i];
139 /* Clear out all leases with matching nonzero chaddr OR yiaddr.
140 * If chaddr == NULL, this is a conflict lease.
142 static void clear_leases(const uint8_t *chaddr, uint32_t yiaddr)
146 for (i = 0; i < server_config.max_leases; i++) {
147 if ((chaddr && memcmp(g_leases[i].lease_mac, chaddr, 6) == 0)
148 || (yiaddr && g_leases[i].lease_nip == yiaddr)
150 memset(&g_leases[i], 0, sizeof(g_leases[i]));
155 /* Add a lease into the table, clearing out any old ones.
156 * If chaddr == NULL, this is a conflict lease.
158 static struct dyn_lease *add_lease(
159 const uint8_t *chaddr, uint32_t yiaddr,
160 leasetime_t leasetime,
161 const char *hostname, int hostname_len)
163 struct dyn_lease *oldest;
165 /* clean out any old ones */
166 clear_leases(chaddr, yiaddr);
168 oldest = oldest_expired_lease();
171 memset(oldest, 0, sizeof(*oldest));
175 hostname_len++; /* include NUL */
176 if (hostname_len > sizeof(oldest->hostname))
177 hostname_len = sizeof(oldest->hostname);
178 p = safe_strncpy(oldest->hostname, hostname, hostname_len);
180 * Sanitization (s/bad_char/./g).
181 * The intent is not to allow only "DNS-valid" hostnames,
182 * but merely make dumpleases output safe for shells to use.
183 * We accept "0-9A-Za-z._-", all other chars turn to dots.
186 if (!isalnum(*p) && *p != '-' && *p != '_')
192 memcpy(oldest->lease_mac, chaddr, 6);
193 oldest->lease_nip = yiaddr;
194 oldest->expires = time(NULL) + leasetime;
200 /* True if a lease has expired */
201 static int is_expired_lease(struct dyn_lease *lease)
203 return (lease->expires < (leasetime_t) time(NULL));
206 /* Find the first lease that matches MAC, NULL if no match */
207 static struct dyn_lease *find_lease_by_mac(const uint8_t *mac)
211 for (i = 0; i < server_config.max_leases; i++)
212 if (memcmp(g_leases[i].lease_mac, mac, 6) == 0)
218 /* Find the first lease that matches IP, NULL is no match */
219 static struct dyn_lease *find_lease_by_nip(uint32_t nip)
223 for (i = 0; i < server_config.max_leases; i++)
224 if (g_leases[i].lease_nip == nip)
230 /* Check if the IP is taken; if it is, add it to the lease table */
231 static int nobody_responds_to_arp(uint32_t nip, const uint8_t *safe_mac, unsigned arpping_ms)
236 r = arpping(nip, safe_mac,
237 server_config.server_nip,
238 server_config.server_mac,
239 server_config.interface,
245 bb_error_msg("%s belongs to someone, reserving it for %u seconds",
246 inet_ntoa(temp), (unsigned)server_config.conflict_time);
247 add_lease(NULL, nip, server_config.conflict_time, NULL, 0);
251 /* Find a new usable (we think) address */
252 static uint32_t find_free_or_expired_nip(const uint8_t *safe_mac, unsigned arpping_ms)
255 struct dyn_lease *oldest_lease = NULL;
257 #if ENABLE_FEATURE_UDHCPD_BASE_IP_ON_MAC
261 /* hash hwaddr: use the SDBM hashing algorithm. Seems to give good
262 * dispersal even with similarly-valued "strings".
265 for (i = 0; i < 6; i++)
266 hash += safe_mac[i] + (hash << 6) + (hash << 16) - hash;
268 /* pick a seed based on hwaddr then iterate until we find a free address. */
269 addr = server_config.start_ip
270 + (hash % (1 + server_config.end_ip - server_config.start_ip));
273 addr = server_config.start_ip;
274 #define stop (server_config.end_ip + 1)
278 struct dyn_lease *lease;
280 /* ie, 192.168.55.0 */
281 if ((addr & 0xff) == 0)
283 /* ie, 192.168.55.255 */
284 if ((addr & 0xff) == 0xff)
287 /* skip our own address */
288 if (nip == server_config.server_nip)
290 /* is this a static lease addr? */
291 if (is_nip_reserved(server_config.static_leases, nip))
294 lease = find_lease_by_nip(nip);
296 //TODO: DHCP servers do not always sit on the same subnet as clients: should *ping*, not arp-ping!
297 if (nobody_responds_to_arp(nip, safe_mac, arpping_ms))
300 if (!oldest_lease || lease->expires < oldest_lease->expires)
301 oldest_lease = lease;
306 #if ENABLE_FEATURE_UDHCPD_BASE_IP_ON_MAC
307 if (addr > server_config.end_ip)
308 addr = server_config.start_ip;
310 } while (addr != stop);
313 && is_expired_lease(oldest_lease)
314 && nobody_responds_to_arp(oldest_lease->lease_nip, safe_mac, arpping_ms)
316 return oldest_lease->lease_nip;
322 /* On these functions, make sure your datatype matches */
323 static int FAST_FUNC read_str(const char *line, void *arg)
328 *dest = xstrdup(line);
332 static int FAST_FUNC read_u32(const char *line, void *arg)
334 *(uint32_t*)arg = bb_strtou32(line, NULL, 10);
338 static int FAST_FUNC read_staticlease(const char *const_line, void *arg)
343 struct ether_addr mac_bytes; /* it's "struct { uint8_t mac[6]; }" */
347 line = (char *) const_line;
348 mac_string = strtok_r(line, " \t", &line);
349 if (!mac_string || !ether_aton_r(mac_string, &mac_bytes))
353 ip_string = strtok_r(NULL, " \t", &line);
354 if (!ip_string || !udhcp_str2nip(ip_string, &nip))
357 add_static_lease(arg, (uint8_t*) &mac_bytes, nip);
359 log_static_leases(arg);
364 static int FAST_FUNC read_optset(const char *line, void *arg) {
365 return udhcp_str2optset(line, arg,
366 dhcp_optflags, dhcp_option_strings,
371 struct config_keyword {
373 int (*handler)(const char *line, void *var) FAST_FUNC;
378 #define OFS(field) offsetof(struct server_config_t, field)
380 static const struct config_keyword keywords[] = {
381 /* keyword handler variable address default */
382 {"start" , udhcp_str2nip , OFS(start_ip ), "192.168.0.20"},
383 {"end" , udhcp_str2nip , OFS(end_ip ), "192.168.0.254"},
384 {"interface" , read_str , OFS(interface ), "eth0"},
385 /* Avoid "max_leases value not sane" warning by setting default
386 * to default_end_ip - default_start_ip + 1: */
387 {"max_leases" , read_u32 , OFS(max_leases ), "235"},
388 {"auto_time" , read_u32 , OFS(auto_time ), "7200"},
389 {"decline_time" , read_u32 , OFS(decline_time ), "3600"},
390 {"conflict_time", read_u32 , OFS(conflict_time), "3600"},
391 {"offer_time" , read_u32 , OFS(offer_time ), "60"},
392 {"min_lease" , read_u32 , OFS(min_lease_sec), "60"},
393 {"lease_file" , read_str , OFS(lease_file ), LEASES_FILE},
394 {"pidfile" , read_str , OFS(pidfile ), "/var/run/udhcpd.pid"},
395 {"siaddr" , udhcp_str2nip , OFS(siaddr_nip ), "0.0.0.0"},
396 /* keywords with no defaults must be last! */
397 {"option" , read_optset , OFS(options ), ""},
398 {"opt" , read_optset , OFS(options ), ""},
399 {"notify_file" , read_str , OFS(notify_file ), NULL},
400 {"sname" , read_str , OFS(sname ), NULL},
401 {"boot_file" , read_str , OFS(boot_file ), NULL},
402 {"static_lease" , read_staticlease, OFS(static_leases), ""},
404 enum { KWS_WITH_DEFAULTS = ARRAY_SIZE(keywords) - 6 };
406 static NOINLINE void read_config(const char *file)
409 const struct config_keyword *k;
413 for (i = 0; i < KWS_WITH_DEFAULTS; i++)
414 keywords[i].handler(keywords[i].def, (char*)&server_config + keywords[i].ofs);
416 parser = config_open(file);
417 while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL)) {
418 for (k = keywords, i = 0; i < ARRAY_SIZE(keywords); k++, i++) {
419 if (strcasecmp(token[0], k->keyword) == 0) {
420 if (!k->handler(token[1], (char*)&server_config + k->ofs)) {
421 bb_error_msg("can't parse line %u in %s",
422 parser->lineno, file);
423 /* reset back to the default value */
424 k->handler(k->def, (char*)&server_config + k->ofs);
430 config_close(parser);
432 server_config.start_ip = ntohl(server_config.start_ip);
433 server_config.end_ip = ntohl(server_config.end_ip);
436 static void write_leases(void)
443 fd = open_or_warn(server_config.lease_file, O_WRONLY|O_CREAT|O_TRUNC);
447 curr = written_at = time(NULL);
449 written_at = SWAP_BE64(written_at);
450 full_write(fd, &written_at, sizeof(written_at));
452 for (i = 0; i < server_config.max_leases; i++) {
453 leasetime_t tmp_time;
455 if (g_leases[i].lease_nip == 0)
458 /* Screw with the time in the struct, for easier writing */
459 tmp_time = g_leases[i].expires;
461 g_leases[i].expires -= curr;
462 if ((signed_leasetime_t) g_leases[i].expires < 0)
463 g_leases[i].expires = 0;
464 g_leases[i].expires = htonl(g_leases[i].expires);
466 /* No error check. If the file gets truncated,
467 * we lose some leases on restart. Oh well. */
468 full_write(fd, &g_leases[i], sizeof(g_leases[i]));
470 /* Then restore it when done */
471 g_leases[i].expires = tmp_time;
475 if (server_config.notify_file) {
477 argv[0] = server_config.notify_file;
478 argv[1] = server_config.lease_file;
480 spawn_and_wait(argv);
484 static NOINLINE void read_leases(const char *file)
486 struct dyn_lease lease;
487 int64_t written_at, time_passed;
489 #if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
493 fd = open_or_warn(file, O_RDONLY);
497 if (full_read(fd, &written_at, sizeof(written_at)) != sizeof(written_at))
499 written_at = SWAP_BE64(written_at);
501 time_passed = time(NULL) - written_at;
502 /* Strange written_at, or lease file from old version of udhcpd
503 * which had no "written_at" field? */
504 if ((uint64_t)time_passed > 12 * 60 * 60)
507 while (full_read(fd, &lease, sizeof(lease)) == sizeof(lease)) {
508 uint32_t y = ntohl(lease.lease_nip);
509 if (y >= server_config.start_ip && y <= server_config.end_ip) {
510 signed_leasetime_t expires = ntohl(lease.expires) - (signed_leasetime_t)time_passed;
514 /* We keep expired leases: add_lease() will add
515 * a lease with 0 seconds remaining.
516 * Fewer IP address changes this way for mass reboot scenario.
520 /* Check if there is a different static lease for this IP or MAC */
521 static_nip = get_static_nip_by_mac(server_config.static_leases, lease.lease_mac);
523 /* NB: we do not add lease even if static_nip == lease.lease_nip.
527 if (is_nip_reserved(server_config.static_leases, lease.lease_nip))
530 /* NB: add_lease takes "relative time", IOW,
531 * lease duration, not lease deadline. */
532 if (add_lease(lease.lease_mac, lease.lease_nip,
534 lease.hostname, sizeof(lease.hostname)
537 bb_error_msg("too many leases while loading %s", file);
540 #if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
545 log1("read %d leases", i);
550 /* Send a packet to a specific mac address and ip address by creating our own ip packet */
551 static void send_packet_to_client(struct dhcp_packet *dhcp_pkt, int force_broadcast)
553 const uint8_t *chaddr;
557 //if (force_broadcast) { /* broadcast */ }
558 //else if (dhcp_pkt->ciaddr) { /* unicast to dhcp_pkt->ciaddr */ }
559 //else if (dhcp_pkt->flags & htons(BROADCAST_FLAG)) { /* broadcast */ }
560 //else { /* unicast to dhcp_pkt->yiaddr */ }
561 // But this is wrong: yiaddr is _our_ idea what client's IP is
562 // (for example, from lease file). Client may not know that,
563 // and may not have UDP socket listening on that IP!
564 // We should never unicast to dhcp_pkt->yiaddr!
565 // dhcp_pkt->ciaddr, OTOH, comes from client's request packet,
569 || (dhcp_pkt->flags & htons(BROADCAST_FLAG))
570 || dhcp_pkt->ciaddr == 0
572 log1("broadcasting packet to client");
573 ciaddr = INADDR_BROADCAST;
574 chaddr = MAC_BCAST_ADDR;
576 log1("unicasting packet to client ciaddr");
577 ciaddr = dhcp_pkt->ciaddr;
578 chaddr = dhcp_pkt->chaddr;
581 udhcp_send_raw_packet(dhcp_pkt,
582 /*src*/ server_config.server_nip, SERVER_PORT,
583 /*dst*/ ciaddr, CLIENT_PORT, chaddr,
584 server_config.ifindex);
587 /* Send a packet to gateway_nip using the kernel ip stack */
588 static void send_packet_to_relay(struct dhcp_packet *dhcp_pkt)
590 log1("forwarding packet to relay");
592 udhcp_send_kernel_packet(dhcp_pkt,
593 server_config.server_nip, SERVER_PORT,
594 dhcp_pkt->gateway_nip, SERVER_PORT);
597 static void send_packet(struct dhcp_packet *dhcp_pkt, int force_broadcast)
599 if (dhcp_pkt->gateway_nip)
600 send_packet_to_relay(dhcp_pkt);
602 send_packet_to_client(dhcp_pkt, force_broadcast);
605 static void init_packet(struct dhcp_packet *packet, struct dhcp_packet *oldpacket, char type)
607 /* Sets op, htype, hlen, cookie fields
608 * and adds DHCP_MESSAGE_TYPE option */
609 udhcp_init_header(packet, type);
611 packet->xid = oldpacket->xid;
612 memcpy(packet->chaddr, oldpacket->chaddr, sizeof(oldpacket->chaddr));
613 packet->flags = oldpacket->flags;
614 packet->gateway_nip = oldpacket->gateway_nip;
615 packet->ciaddr = oldpacket->ciaddr;
616 udhcp_add_simple_option(packet, DHCP_SERVER_ID, server_config.server_nip);
619 /* Fill options field, siaddr_nip, and sname and boot_file fields.
620 * TODO: teach this code to use overload option.
622 static void add_server_options(struct dhcp_packet *packet)
624 struct option_set *curr = server_config.options;
627 if (curr->data[OPT_CODE] != DHCP_LEASE_TIME)
628 udhcp_add_binary_option(packet, curr->data);
632 packet->siaddr_nip = server_config.siaddr_nip;
634 if (server_config.sname)
635 strncpy((char*)packet->sname, server_config.sname, sizeof(packet->sname) - 1);
636 if (server_config.boot_file)
637 strncpy((char*)packet->file, server_config.boot_file, sizeof(packet->file) - 1);
640 static uint32_t select_lease_time(struct dhcp_packet *packet)
642 uint32_t lease_time_sec = server_config.max_lease_sec;
643 uint8_t *lease_time_opt = udhcp_get_option32(packet, DHCP_LEASE_TIME);
644 if (lease_time_opt) {
645 move_from_unaligned32(lease_time_sec, lease_time_opt);
646 lease_time_sec = ntohl(lease_time_sec);
647 if (lease_time_sec > server_config.max_lease_sec)
648 lease_time_sec = server_config.max_lease_sec;
649 if (lease_time_sec < server_config.min_lease_sec)
650 lease_time_sec = server_config.min_lease_sec;
652 return lease_time_sec;
655 /* We got a DHCP DISCOVER. Send an OFFER. */
656 /* NOINLINE: limit stack usage in caller */
657 static NOINLINE void send_offer(struct dhcp_packet *oldpacket,
658 uint32_t static_lease_nip,
659 struct dyn_lease *lease,
660 uint8_t *requested_ip_opt,
663 struct dhcp_packet packet;
664 uint32_t lease_time_sec;
667 init_packet(&packet, oldpacket, DHCPOFFER);
669 /* If it is a static lease, use its IP */
670 packet.yiaddr = static_lease_nip;
672 if (!static_lease_nip) {
673 /* We have no static lease for client's chaddr */
675 const char *p_host_name;
678 /* We have a dynamic lease for client's chaddr.
679 * Reuse its IP (even if lease is expired).
680 * Note that we ignore requested IP in this case.
682 packet.yiaddr = lease->lease_nip;
684 /* Or: if client has requested an IP */
685 else if (requested_ip_opt != NULL
687 && (move_from_unaligned32(req_nip, requested_ip_opt), 1)
688 /* and the IP is in the lease range */
689 && ntohl(req_nip) >= server_config.start_ip
690 && ntohl(req_nip) <= server_config.end_ip
692 && ( !(lease = find_lease_by_nip(req_nip)) /* is not already taken */
693 || is_expired_lease(lease) /* or is taken, but expired */
696 packet.yiaddr = req_nip;
699 /* Otherwise, find a free IP */
700 packet.yiaddr = find_free_or_expired_nip(oldpacket->chaddr, arpping_ms);
703 if (!packet.yiaddr) {
704 bb_error_msg("no free IP addresses. OFFER abandoned");
707 /* Reserve the IP for a short time hoping to get DHCPREQUEST soon */
708 p_host_name = (const char*) udhcp_get_option(oldpacket, DHCP_HOST_NAME);
709 lease = add_lease(packet.chaddr, packet.yiaddr,
710 server_config.offer_time,
712 p_host_name ? (unsigned char)p_host_name[OPT_LEN - OPT_DATA] : 0
715 bb_error_msg("no free IP addresses. OFFER abandoned");
720 lease_time_sec = select_lease_time(oldpacket);
721 udhcp_add_simple_option(&packet, DHCP_LEASE_TIME, htonl(lease_time_sec));
722 add_server_options(&packet);
724 addr.s_addr = packet.yiaddr;
725 bb_error_msg("sending OFFER of %s", inet_ntoa(addr));
726 /* send_packet emits error message itself if it detects failure */
727 send_packet(&packet, /*force_bcast:*/ 0);
730 /* NOINLINE: limit stack usage in caller */
731 static NOINLINE void send_NAK(struct dhcp_packet *oldpacket)
733 struct dhcp_packet packet;
735 init_packet(&packet, oldpacket, DHCPNAK);
737 log1("sending %s", "NAK");
738 send_packet(&packet, /*force_bcast:*/ 1);
741 /* NOINLINE: limit stack usage in caller */
742 static NOINLINE void send_ACK(struct dhcp_packet *oldpacket, uint32_t yiaddr)
744 struct dhcp_packet packet;
745 uint32_t lease_time_sec;
747 const char *p_host_name;
749 init_packet(&packet, oldpacket, DHCPACK);
750 packet.yiaddr = yiaddr;
752 lease_time_sec = select_lease_time(oldpacket);
753 udhcp_add_simple_option(&packet, DHCP_LEASE_TIME, htonl(lease_time_sec));
755 add_server_options(&packet);
757 addr.s_addr = yiaddr;
758 bb_error_msg("sending ACK to %s", inet_ntoa(addr));
759 send_packet(&packet, /*force_bcast:*/ 0);
761 p_host_name = (const char*) udhcp_get_option(oldpacket, DHCP_HOST_NAME);
762 add_lease(packet.chaddr, packet.yiaddr,
765 p_host_name ? (unsigned char)p_host_name[OPT_LEN - OPT_DATA] : 0
767 if (ENABLE_FEATURE_UDHCPD_WRITE_LEASES_EARLY) {
768 /* rewrite the file with leases at every new acceptance */
773 /* NOINLINE: limit stack usage in caller */
774 static NOINLINE void send_inform(struct dhcp_packet *oldpacket)
776 struct dhcp_packet packet;
778 /* "If a client has obtained a network address through some other means
779 * (e.g., manual configuration), it may use a DHCPINFORM request message
780 * to obtain other local configuration parameters. Servers receiving a
781 * DHCPINFORM message construct a DHCPACK message with any local
782 * configuration parameters appropriate for the client without:
783 * allocating a new address, checking for an existing binding, filling
784 * in 'yiaddr' or including lease time parameters. The servers SHOULD
785 * unicast the DHCPACK reply to the address given in the 'ciaddr' field
786 * of the DHCPINFORM message.
788 * The server responds to a DHCPINFORM message by sending a DHCPACK
789 * message directly to the address given in the 'ciaddr' field
790 * of the DHCPINFORM message. The server MUST NOT send a lease
791 * expiration time to the client and SHOULD NOT fill in 'yiaddr'."
793 //TODO: do a few sanity checks: is ciaddr set?
794 //Better yet: is ciaddr == IP source addr?
795 init_packet(&packet, oldpacket, DHCPACK);
796 add_server_options(&packet);
798 send_packet(&packet, /*force_bcast:*/ 0);
801 int udhcpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
802 int udhcpd_main(int argc UNUSED_PARAM, char **argv)
804 int server_socket = -1, retval;
806 unsigned timeout_end;
809 struct option_set *option;
811 const char *str_a = "2000";
813 IF_FEATURE_UDHCP_PORT(char *str_P;)
815 setup_common_bufsiz();
817 IF_FEATURE_UDHCP_PORT(SERVER_PORT = 67;)
818 IF_FEATURE_UDHCP_PORT(CLIENT_PORT = 68;)
820 opt = getopt32(argv, "^"
821 "fSI:va:"IF_FEATURE_UDHCP_PORT("P:")
823 #if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
828 IF_FEATURE_UDHCP_PORT(, &str_P)
829 IF_UDHCP_VERBOSE(, &dhcp_verbose)
831 if (!(opt & 1)) { /* no -f */
832 bb_daemonize_or_rexec(0, argv);
833 logmode = LOGMODE_NONE;
835 /* update argv after the possible vfork+exec in daemonize */
837 if (opt & 2) { /* -S */
838 openlog(applet_name, LOG_PID, LOG_DAEMON);
839 logmode |= LOGMODE_SYSLOG;
841 if (opt & 4) { /* -I */
842 len_and_sockaddr *lsa = xhost_and_af2sockaddr(str_I, 0, AF_INET);
843 server_config.server_nip = lsa->u.sin.sin_addr.s_addr;
846 #if ENABLE_FEATURE_UDHCP_PORT
847 if (opt & 32) { /* -P */
848 SERVER_PORT = xatou16(str_P);
849 CLIENT_PORT = SERVER_PORT + 1;
852 arpping_ms = xatou(str_a);
854 /* Would rather not do read_config before daemonization -
855 * otherwise NOMMU machines will parse config twice */
856 read_config(argv[0] ? argv[0] : DHCPD_CONF_FILE);
857 /* prevent poll timeout overflow */
858 if (server_config.auto_time > INT_MAX / 1000)
859 server_config.auto_time = INT_MAX / 1000;
861 /* Make sure fd 0,1,2 are open */
865 write_pidfile(server_config.pidfile);
866 /* if (!..) bb_perror_msg("can't create pidfile %s", pidfile); */
868 bb_error_msg("started, v"BB_VER);
870 option = udhcp_find_option(server_config.options, DHCP_LEASE_TIME);
871 server_config.max_lease_sec = DEFAULT_LEASE_TIME;
873 move_from_unaligned32(server_config.max_lease_sec, option->data + OPT_DATA);
874 server_config.max_lease_sec = ntohl(server_config.max_lease_sec);
878 num_ips = server_config.end_ip - server_config.start_ip + 1;
879 if (server_config.max_leases > num_ips) {
880 bb_error_msg("max_leases=%u is too big, setting to %u",
881 (unsigned)server_config.max_leases, num_ips);
882 server_config.max_leases = num_ips;
885 /* this sets g_leases */
886 SET_PTR_TO_GLOBALS(xzalloc(server_config.max_leases * sizeof(g_leases[0])));
888 read_leases(server_config.lease_file);
890 if (udhcp_read_interface(server_config.interface,
891 &server_config.ifindex,
892 (server_config.server_nip == 0 ? &server_config.server_nip : NULL),
893 server_config.server_mac)
899 /* Setup the signal pipe */
902 continue_with_autotime:
903 timeout_end = monotonic_sec() + server_config.auto_time;
904 while (1) { /* loop until universe collapses */
905 struct pollfd pfds[2];
906 struct dhcp_packet packet;
909 uint8_t *server_id_opt;
910 uint8_t *requested_ip_opt;
911 uint32_t requested_nip = requested_nip; /* for compiler */
912 uint32_t static_lease_nip;
913 struct dyn_lease *lease, fake_lease;
915 if (server_socket < 0) {
916 server_socket = udhcp_listen_socket(/*INADDR_ANY,*/ SERVER_PORT,
917 server_config.interface);
920 udhcp_sp_fd_set(pfds, server_socket);
924 if (server_config.auto_time) {
925 tv = timeout_end - monotonic_sec();
929 goto continue_with_autotime;
934 /* Block here waiting for either signal or packet */
935 retval = poll(pfds, 2, tv);
941 /* < 0 and not EINTR: should not happen */
942 bb_perror_msg_and_die("poll");
945 if (pfds[0].revents) switch (udhcp_sp_read()) {
947 bb_error_msg("received %s", "SIGUSR1");
949 /* why not just reset the timeout, eh */
950 goto continue_with_autotime;
952 bb_error_msg("received %s", "SIGTERM");
957 /* Is it a packet? */
958 if (!pfds[1].revents)
961 /* Note: we do not block here, we block on poll() instead.
962 * Blocking here would prevent SIGTERM from working:
963 * socket read inside this call is restarted on caught signals.
965 bytes = udhcp_recv_kernel_packet(&packet, server_socket);
967 /* bytes can also be -2 ("bad packet data") */
968 if (bytes == -1 && errno != EINTR) {
969 log1("read error: "STRERROR_FMT", reopening socket" STRERROR_ERRNO);
970 close(server_socket);
975 if (packet.hlen != 6) {
976 bb_error_msg("MAC length != 6, ignoring packet");
979 if (packet.op != BOOTREQUEST) {
980 bb_error_msg("not a REQUEST, ignoring packet");
983 state = udhcp_get_option(&packet, DHCP_MESSAGE_TYPE);
984 if (state == NULL || state[0] < DHCP_MINTYPE || state[0] > DHCP_MAXTYPE) {
985 bb_error_msg("no or bad message type option, ignoring packet");
989 /* Get SERVER_ID if present */
990 server_id_opt = udhcp_get_option32(&packet, DHCP_SERVER_ID);
992 uint32_t server_id_network_order;
993 move_from_unaligned32(server_id_network_order, server_id_opt);
994 if (server_id_network_order != server_config.server_nip) {
995 /* client talks to somebody else */
996 log1("server ID doesn't match, ignoring");
1001 /* Look for a static/dynamic lease */
1002 static_lease_nip = get_static_nip_by_mac(server_config.static_leases, &packet.chaddr);
1003 if (static_lease_nip) {
1004 bb_error_msg("found static lease: %x", static_lease_nip);
1005 memcpy(&fake_lease.lease_mac, &packet.chaddr, 6);
1006 fake_lease.lease_nip = static_lease_nip;
1007 fake_lease.expires = 0;
1008 lease = &fake_lease;
1010 lease = find_lease_by_mac(packet.chaddr);
1013 /* Get REQUESTED_IP if present */
1014 requested_ip_opt = udhcp_get_option32(&packet, DHCP_REQUESTED_IP);
1015 if (requested_ip_opt) {
1016 move_from_unaligned32(requested_nip, requested_ip_opt);
1022 log1("received %s", "DISCOVER");
1024 send_offer(&packet, static_lease_nip, lease, requested_ip_opt, arpping_ms);
1028 log1("received %s", "REQUEST");
1031 o DHCPREQUEST generated during SELECTING state:
1033 Client inserts the address of the selected server in 'server
1034 identifier', 'ciaddr' MUST be zero, 'requested IP address' MUST be
1035 filled in with the yiaddr value from the chosen DHCPOFFER.
1037 Note that the client may choose to collect several DHCPOFFER
1038 messages and select the "best" offer. The client indicates its
1039 selection by identifying the offering server in the DHCPREQUEST
1040 message. If the client receives no acceptable offers, the client
1041 may choose to try another DHCPDISCOVER message. Therefore, the
1042 servers may not receive a specific DHCPREQUEST from which they can
1043 decide whether or not the client has accepted the offer.
1045 o DHCPREQUEST generated during INIT-REBOOT state:
1047 'server identifier' MUST NOT be filled in, 'requested IP address'
1048 option MUST be filled in with client's notion of its previously
1049 assigned address. 'ciaddr' MUST be zero. The client is seeking to
1050 verify a previously allocated, cached configuration. Server SHOULD
1051 send a DHCPNAK message to the client if the 'requested IP address'
1052 is incorrect, or is on the wrong network.
1054 Determining whether a client in the INIT-REBOOT state is on the
1055 correct network is done by examining the contents of 'giaddr', the
1056 'requested IP address' option, and a database lookup. If the DHCP
1057 server detects that the client is on the wrong net (i.e., the
1058 result of applying the local subnet mask or remote subnet mask (if
1059 'giaddr' is not zero) to 'requested IP address' option value
1060 doesn't match reality), then the server SHOULD send a DHCPNAK
1061 message to the client.
1063 If the network is correct, then the DHCP server should check if
1064 the client's notion of its IP address is correct. If not, then the
1065 server SHOULD send a DHCPNAK message to the client. If the DHCP
1066 server has no record of this client, then it MUST remain silent,
1067 and MAY output a warning to the network administrator. This
1068 behavior is necessary for peaceful coexistence of non-
1069 communicating DHCP servers on the same wire.
1071 If 'giaddr' is 0x0 in the DHCPREQUEST message, the client is on
1072 the same subnet as the server. The server MUST broadcast the
1073 DHCPNAK message to the 0xffffffff broadcast address because the
1074 client may not have a correct network address or subnet mask, and
1075 the client may not be answering ARP requests.
1077 If 'giaddr' is set in the DHCPREQUEST message, the client is on a
1078 different subnet. The server MUST set the broadcast bit in the
1079 DHCPNAK, so that the relay agent will broadcast the DHCPNAK to the
1080 client, because the client may not have a correct network address
1081 or subnet mask, and the client may not be answering ARP requests.
1083 o DHCPREQUEST generated during RENEWING state:
1085 'server identifier' MUST NOT be filled in, 'requested IP address'
1086 option MUST NOT be filled in, 'ciaddr' MUST be filled in with
1087 client's IP address. In this situation, the client is completely
1088 configured, and is trying to extend its lease. This message will
1089 be unicast, so no relay agents will be involved in its
1090 transmission. Because 'giaddr' is therefore not filled in, the
1091 DHCP server will trust the value in 'ciaddr', and use it when
1092 replying to the client.
1094 A client MAY choose to renew or extend its lease prior to T1. The
1095 server may choose not to extend the lease (as a policy decision by
1096 the network administrator), but should return a DHCPACK message
1099 o DHCPREQUEST generated during REBINDING state:
1101 'server identifier' MUST NOT be filled in, 'requested IP address'
1102 option MUST NOT be filled in, 'ciaddr' MUST be filled in with
1103 client's IP address. In this situation, the client is completely
1104 configured, and is trying to extend its lease. This message MUST
1105 be broadcast to the 0xffffffff IP broadcast address. The DHCP
1106 server SHOULD check 'ciaddr' for correctness before replying to
1109 The DHCPREQUEST from a REBINDING client is intended to accommodate
1110 sites that have multiple DHCP servers and a mechanism for
1111 maintaining consistency among leases managed by multiple servers.
1112 A DHCP server MAY extend a client's lease only if it has local
1113 administrative authority to do so.
1115 if (!requested_ip_opt) {
1116 requested_nip = packet.ciaddr;
1117 if (requested_nip == 0) {
1118 log1("no requested IP and no ciaddr, ignoring");
1122 if (lease && requested_nip == lease->lease_nip) {
1123 /* client requested or configured IP matches the lease.
1124 * ACK it, and bump lease expiration time. */
1125 send_ACK(&packet, lease->lease_nip);
1128 /* No lease for this MAC, or lease IP != requested IP */
1130 if (server_id_opt /* client is in SELECTING state */
1131 || requested_ip_opt /* client is in INIT-REBOOT state */
1133 /* "No, we don't have this IP for you" */
1135 } /* else: client is in RENEWING or REBINDING, do not answer */
1141 * "If the server receives a DHCPDECLINE message,
1142 * the client has discovered through some other means
1143 * that the suggested network address is already
1144 * in use. The server MUST mark the network address
1145 * as not available and SHOULD notify the local
1146 * sysadmin of a possible configuration problem."
1148 * SERVER_ID must be present,
1149 * REQUESTED_IP must be present,
1150 * chaddr must be filled in,
1151 * ciaddr must be 0 (we do not check this)
1153 log1("received %s", "DECLINE");
1156 && lease /* chaddr matches this lease */
1157 && requested_nip == lease->lease_nip
1159 memset(lease->lease_mac, 0, sizeof(lease->lease_mac));
1160 lease->expires = time(NULL) + server_config.decline_time;
1165 /* "Upon receipt of a DHCPRELEASE message, the server
1166 * marks the network address as not allocated."
1168 * SERVER_ID must be present,
1169 * REQUESTED_IP must not be present (we do not check this),
1170 * chaddr must be filled in,
1171 * ciaddr must be filled in
1173 log1("received %s", "RELEASE");
1175 && lease /* chaddr matches this lease */
1176 && packet.ciaddr == lease->lease_nip
1178 lease->expires = time(NULL);
1183 log1("received %s", "INFORM");
1184 send_inform(&packet);
1191 /*if (server_config.pidfile) - server_config.pidfile is never NULL */
1192 remove_pidfile(server_config.pidfile);