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.
31 /* Send a packet to a specific mac address and ip address by creating our own ip packet */
32 static void send_packet_to_client(struct dhcp_packet *dhcp_pkt, int force_broadcast)
34 const uint8_t *chaddr;
38 //if (force_broadcast) { /* broadcast */ }
39 //else if (dhcp_pkt->ciaddr) { /* unicast to dhcp_pkt->ciaddr */ }
40 //else if (dhcp_pkt->flags & htons(BROADCAST_FLAG)) { /* broadcast */ }
41 //else { /* unicast to dhcp_pkt->yiaddr */ }
42 // But this is wrong: yiaddr is _our_ idea what client's IP is
43 // (for example, from lease file). Client may not know that,
44 // and may not have UDP socket listening on that IP!
45 // We should never unicast to dhcp_pkt->yiaddr!
46 // dhcp_pkt->ciaddr, OTOH, comes from client's request packet,
50 || (dhcp_pkt->flags & htons(BROADCAST_FLAG))
51 || dhcp_pkt->ciaddr == 0
53 log1("Broadcasting packet to client");
54 ciaddr = INADDR_BROADCAST;
55 chaddr = MAC_BCAST_ADDR;
57 log1("Unicasting packet to client ciaddr");
58 ciaddr = dhcp_pkt->ciaddr;
59 chaddr = dhcp_pkt->chaddr;
62 udhcp_send_raw_packet(dhcp_pkt,
63 /*src*/ server_config.server_nip, SERVER_PORT,
64 /*dst*/ ciaddr, CLIENT_PORT, chaddr,
65 server_config.ifindex);
68 /* Send a packet to gateway_nip using the kernel ip stack */
69 static void send_packet_to_relay(struct dhcp_packet *dhcp_pkt)
71 log1("Forwarding packet to relay");
73 udhcp_send_kernel_packet(dhcp_pkt,
74 server_config.server_nip, SERVER_PORT,
75 dhcp_pkt->gateway_nip, SERVER_PORT);
78 static void send_packet(struct dhcp_packet *dhcp_pkt, int force_broadcast)
80 if (dhcp_pkt->gateway_nip)
81 send_packet_to_relay(dhcp_pkt);
83 send_packet_to_client(dhcp_pkt, force_broadcast);
86 static void init_packet(struct dhcp_packet *packet, struct dhcp_packet *oldpacket, char type)
88 /* Sets op, htype, hlen, cookie fields
89 * and adds DHCP_MESSAGE_TYPE option */
90 udhcp_init_header(packet, type);
92 packet->xid = oldpacket->xid;
93 memcpy(packet->chaddr, oldpacket->chaddr, sizeof(oldpacket->chaddr));
94 packet->flags = oldpacket->flags;
95 packet->gateway_nip = oldpacket->gateway_nip;
96 packet->ciaddr = oldpacket->ciaddr;
97 add_simple_option(packet->options, DHCP_SERVER_ID, server_config.server_nip);
100 /* Fill options field, siaddr_nip, and sname and boot_file fields.
101 * TODO: teach this code to use overload option.
103 static void add_server_options(struct dhcp_packet *packet)
105 struct option_set *curr = server_config.options;
108 if (curr->data[OPT_CODE] != DHCP_LEASE_TIME)
109 add_option_string(packet->options, curr->data);
113 packet->siaddr_nip = server_config.siaddr_nip;
115 if (server_config.sname)
116 strncpy((char*)packet->sname, server_config.sname, sizeof(packet->sname) - 1);
117 if (server_config.boot_file)
118 strncpy((char*)packet->file, server_config.boot_file, sizeof(packet->file) - 1);
121 static uint32_t select_lease_time(struct dhcp_packet *packet)
123 uint32_t lease_time_sec = server_config.max_lease_sec;
124 uint8_t *lease_time_opt = get_option(packet, DHCP_LEASE_TIME);
125 if (lease_time_opt) {
126 move_from_unaligned32(lease_time_sec, lease_time_opt);
127 lease_time_sec = ntohl(lease_time_sec);
128 if (lease_time_sec > server_config.max_lease_sec)
129 lease_time_sec = server_config.max_lease_sec;
130 if (lease_time_sec < server_config.min_lease_sec)
131 lease_time_sec = server_config.min_lease_sec;
133 return lease_time_sec;
136 /* We got a DHCP DISCOVER. Send an OFFER. */
137 static void send_offer(struct dhcp_packet *oldpacket, uint32_t static_lease_nip, struct dyn_lease *lease)
139 struct dhcp_packet packet;
140 uint32_t lease_time_sec;
143 init_packet(&packet, oldpacket, DHCPOFFER);
145 /* If it is a static lease, use its IP */
146 packet.yiaddr = static_lease_nip;
148 if (!static_lease_nip) {
149 /* We have no static lease for client's chaddr */
152 const char *p_host_name;
155 /* We have a dynamic lease for client's chaddr.
156 * Reuse its IP (even if lease is expired).
157 * Note that we ignore requested IP in this case.
159 packet.yiaddr = lease->lease_nip;
161 /* Or: if client has requested an IP */
162 else if ((req_ip_opt = get_option(oldpacket, DHCP_REQUESTED_IP)) != NULL
164 && (move_from_unaligned32(req_nip, req_ip_opt), 1)
165 /* and the IP is in the lease range */
166 && ntohl(req_nip) >= server_config.start_ip
167 && ntohl(req_nip) <= server_config.end_ip
169 && ( !(lease = find_lease_by_nip(req_nip)) /* is not already taken */
170 || is_expired_lease(lease) /* or is taken, but expired */
173 packet.yiaddr = req_nip;
176 /* Otherwise, find a free IP */
177 packet.yiaddr = find_free_or_expired_nip(oldpacket->chaddr);
180 if (!packet.yiaddr) {
181 bb_error_msg("no free IP addresses. OFFER abandoned");
184 /* Reserve the IP for a short time hoping to get DHCPREQUEST soon */
185 p_host_name = (const char*) get_option(oldpacket, DHCP_HOST_NAME);
186 lease = add_lease(packet.chaddr, packet.yiaddr,
187 server_config.offer_time,
189 p_host_name ? (unsigned char)p_host_name[OPT_LEN - OPT_DATA] : 0
192 bb_error_msg("no free IP addresses. OFFER abandoned");
197 lease_time_sec = select_lease_time(oldpacket);
198 add_simple_option(packet.options, DHCP_LEASE_TIME, htonl(lease_time_sec));
199 add_server_options(&packet);
201 addr.s_addr = packet.yiaddr;
202 bb_info_msg("Sending OFFER of %s", inet_ntoa(addr));
203 /* send_packet emits error message itself if it detects failure */
204 send_packet(&packet, /*force_bcast:*/ 0);
207 static void send_NAK(struct dhcp_packet *oldpacket)
209 struct dhcp_packet packet;
211 init_packet(&packet, oldpacket, DHCPNAK);
214 send_packet(&packet, /*force_bcast:*/ 1);
217 static void send_ACK(struct dhcp_packet *oldpacket, uint32_t yiaddr)
219 struct dhcp_packet packet;
220 uint32_t lease_time_sec;
222 const char *p_host_name;
224 init_packet(&packet, oldpacket, DHCPACK);
225 packet.yiaddr = yiaddr;
227 lease_time_sec = select_lease_time(oldpacket);
228 add_simple_option(packet.options, DHCP_LEASE_TIME, htonl(lease_time_sec));
230 add_server_options(&packet);
232 addr.s_addr = yiaddr;
233 bb_info_msg("Sending ACK to %s", inet_ntoa(addr));
234 send_packet(&packet, /*force_bcast:*/ 0);
236 p_host_name = (const char*) get_option(oldpacket, DHCP_HOST_NAME);
237 add_lease(packet.chaddr, packet.yiaddr,
240 p_host_name ? (unsigned char)p_host_name[OPT_LEN - OPT_DATA] : 0
242 if (ENABLE_FEATURE_UDHCPD_WRITE_LEASES_EARLY) {
243 /* rewrite the file with leases at every new acceptance */
248 static void send_inform(struct dhcp_packet *oldpacket)
250 struct dhcp_packet packet;
252 /* "The server responds to a DHCPINFORM message by sending a DHCPACK
253 * message directly to the address given in the 'ciaddr' field
254 * of the DHCPINFORM message. The server MUST NOT send a lease
255 * expiration time to the client and SHOULD NOT fill in 'yiaddr'."
257 init_packet(&packet, oldpacket, DHCPACK);
258 add_server_options(&packet);
260 send_packet(&packet, /*force_bcast:*/ 0);
265 struct dyn_lease *g_leases;
266 /* struct server_config_t server_config is in bb_common_bufsiz1 */
269 int udhcpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
270 int udhcpd_main(int argc UNUSED_PARAM, char **argv)
273 int server_socket = -1, retval, max_sock;
274 struct dhcp_packet packet;
276 uint32_t static_lease_nip;
277 unsigned timeout_end;
280 struct option_set *option;
281 struct dyn_lease *lease, fake_lease;
282 IF_FEATURE_UDHCP_PORT(char *str_P;)
284 #if ENABLE_FEATURE_UDHCP_PORT
289 #if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
290 opt_complementary = "vv";
292 opt = getopt32(argv, "fSv"
293 IF_FEATURE_UDHCP_PORT("P:", &str_P)
294 #if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
299 if (!(opt & 1)) { /* no -f */
300 bb_daemonize_or_rexec(0, argv);
301 logmode = LOGMODE_NONE;
303 if (opt & 2) { /* -S */
304 openlog(applet_name, LOG_PID, LOG_DAEMON);
305 logmode |= LOGMODE_SYSLOG;
307 #if ENABLE_FEATURE_UDHCP_PORT
308 if (opt & 4) { /* -P */
309 SERVER_PORT = xatou16(str_P);
310 CLIENT_PORT = SERVER_PORT + 1;
313 /* Would rather not do read_config before daemonization -
314 * otherwise NOMMU machines will parse config twice */
315 read_config(argv[0] ? argv[0] : DHCPD_CONF_FILE);
317 /* Make sure fd 0,1,2 are open */
319 /* Equivalent of doing a fflush after every \n */
323 write_pidfile(server_config.pidfile);
324 /* if (!..) bb_perror_msg("can't create pidfile %s", pidfile); */
326 bb_info_msg("%s (v"BB_VER") started", applet_name);
328 option = find_option(server_config.options, DHCP_LEASE_TIME);
329 server_config.max_lease_sec = DEFAULT_LEASE_TIME;
331 move_from_unaligned32(server_config.max_lease_sec, option->data + OPT_DATA);
332 server_config.max_lease_sec = ntohl(server_config.max_lease_sec);
336 num_ips = server_config.end_ip - server_config.start_ip + 1;
337 if (server_config.max_leases > num_ips) {
338 bb_error_msg("max_leases=%u is too big, setting to %u",
339 (unsigned)server_config.max_leases, num_ips);
340 server_config.max_leases = num_ips;
343 g_leases = xzalloc(server_config.max_leases * sizeof(g_leases[0]));
344 read_leases(server_config.lease_file);
346 if (udhcp_read_interface(server_config.interface,
347 &server_config.ifindex,
348 &server_config.server_nip,
349 server_config.server_mac)
355 /* Setup the signal pipe */
358 timeout_end = monotonic_sec() + server_config.auto_time;
359 while (1) { /* loop until universe collapses */
362 uint8_t *server_id_opt;
363 uint8_t *requested_opt;
364 uint32_t requested_nip = requested_nip; /* for compiler */
366 if (server_socket < 0) {
367 server_socket = udhcp_listen_socket(/*INADDR_ANY,*/ SERVER_PORT,
368 server_config.interface);
371 max_sock = udhcp_sp_fd_set(&rfds, server_socket);
372 if (server_config.auto_time) {
373 tv.tv_sec = timeout_end - monotonic_sec();
377 if (!server_config.auto_time || tv.tv_sec > 0) {
378 retval = select(max_sock + 1, &rfds, NULL, NULL,
379 server_config.auto_time ? &tv : NULL);
383 timeout_end = monotonic_sec() + server_config.auto_time;
386 if (retval < 0 && errno != EINTR) {
387 log1("Error on select");
391 switch (udhcp_sp_read(&rfds)) {
393 bb_info_msg("Received a SIGUSR1");
395 /* why not just reset the timeout, eh */
396 timeout_end = monotonic_sec() + server_config.auto_time;
399 bb_info_msg("Received a SIGTERM");
401 case 0: /* no signal: read a packet */
403 default: /* signal or error (probably EINTR): back to select */
407 bytes = udhcp_recv_kernel_packet(&packet, server_socket);
409 /* bytes can also be -2 ("bad packet data") */
410 if (bytes == -1 && errno != EINTR) {
411 log1("Read error: %s, reopening socket", strerror(errno));
412 close(server_socket);
417 if (packet.hlen != 6) {
418 bb_error_msg("MAC length != 6, ignoring packet");
421 if (packet.op != BOOTREQUEST) {
422 bb_error_msg("not a REQUEST, ignoring packet");
425 state = get_option(&packet, DHCP_MESSAGE_TYPE);
426 if (state == NULL || state[0] < DHCP_MINTYPE || state[0] > DHCP_MAXTYPE) {
427 bb_error_msg("no or bad message type option, ignoring packet");
431 /* Look for a static/dynamic lease */
432 static_lease_nip = get_static_nip_by_mac(server_config.static_leases, &packet.chaddr);
433 if (static_lease_nip) {
434 bb_info_msg("Found static lease: %x", static_lease_nip);
435 memcpy(&fake_lease.lease_mac, &packet.chaddr, 6);
436 fake_lease.lease_nip = static_lease_nip;
437 fake_lease.expires = 0;
440 lease = find_lease_by_mac(packet.chaddr);
443 /* Get REQUESTED_IP and SERVER_ID if present */
444 server_id_opt = get_option(&packet, DHCP_SERVER_ID);
446 uint32_t server_id_net;
447 move_from_unaligned32(server_id_net, server_id_opt);
448 if (server_id_net != server_config.server_nip) {
449 /* client talks to somebody else */
450 log1("server ID doesn't match, ignoring");
454 requested_opt = get_option(&packet, DHCP_REQUESTED_IP);
456 move_from_unaligned32(requested_nip, requested_opt);
462 log1("Received DISCOVER");
464 send_offer(&packet, static_lease_nip, lease);
468 log1("Received REQUEST");
471 o DHCPREQUEST generated during SELECTING state:
473 Client inserts the address of the selected server in 'server
474 identifier', 'ciaddr' MUST be zero, 'requested IP address' MUST be
475 filled in with the yiaddr value from the chosen DHCPOFFER.
477 Note that the client may choose to collect several DHCPOFFER
478 messages and select the "best" offer. The client indicates its
479 selection by identifying the offering server in the DHCPREQUEST
480 message. If the client receives no acceptable offers, the client
481 may choose to try another DHCPDISCOVER message. Therefore, the
482 servers may not receive a specific DHCPREQUEST from which they can
483 decide whether or not the client has accepted the offer.
485 o DHCPREQUEST generated during INIT-REBOOT state:
487 'server identifier' MUST NOT be filled in, 'requested IP address'
488 option MUST be filled in with client's notion of its previously
489 assigned address. 'ciaddr' MUST be zero. The client is seeking to
490 verify a previously allocated, cached configuration. Server SHOULD
491 send a DHCPNAK message to the client if the 'requested IP address'
492 is incorrect, or is on the wrong network.
494 Determining whether a client in the INIT-REBOOT state is on the
495 correct network is done by examining the contents of 'giaddr', the
496 'requested IP address' option, and a database lookup. If the DHCP
497 server detects that the client is on the wrong net (i.e., the
498 result of applying the local subnet mask or remote subnet mask (if
499 'giaddr' is not zero) to 'requested IP address' option value
500 doesn't match reality), then the server SHOULD send a DHCPNAK
501 message to the client.
503 If the network is correct, then the DHCP server should check if
504 the client's notion of its IP address is correct. If not, then the
505 server SHOULD send a DHCPNAK message to the client. If the DHCP
506 server has no record of this client, then it MUST remain silent,
507 and MAY output a warning to the network administrator. This
508 behavior is necessary for peaceful coexistence of non-
509 communicating DHCP servers on the same wire.
511 If 'giaddr' is 0x0 in the DHCPREQUEST message, the client is on
512 the same subnet as the server. The server MUST broadcast the
513 DHCPNAK message to the 0xffffffff broadcast address because the
514 client may not have a correct network address or subnet mask, and
515 the client may not be answering ARP requests.
517 If 'giaddr' is set in the DHCPREQUEST message, the client is on a
518 different subnet. The server MUST set the broadcast bit in the
519 DHCPNAK, so that the relay agent will broadcast the DHCPNAK to the
520 client, because the client may not have a correct network address
521 or subnet mask, and the client may not be answering ARP requests.
523 o DHCPREQUEST generated during RENEWING state:
525 'server identifier' MUST NOT be filled in, 'requested IP address'
526 option MUST NOT be filled in, 'ciaddr' MUST be filled in with
527 client's IP address. In this situation, the client is completely
528 configured, and is trying to extend its lease. This message will
529 be unicast, so no relay agents will be involved in its
530 transmission. Because 'giaddr' is therefore not filled in, the
531 DHCP server will trust the value in 'ciaddr', and use it when
532 replying to the client.
534 A client MAY choose to renew or extend its lease prior to T1. The
535 server may choose not to extend the lease (as a policy decision by
536 the network administrator), but should return a DHCPACK message
539 o DHCPREQUEST generated during REBINDING state:
541 'server identifier' MUST NOT be filled in, 'requested IP address'
542 option MUST NOT be filled in, 'ciaddr' MUST be filled in with
543 client's IP address. In this situation, the client is completely
544 configured, and is trying to extend its lease. This message MUST
545 be broadcast to the 0xffffffff IP broadcast address. The DHCP
546 server SHOULD check 'ciaddr' for correctness before replying to
549 The DHCPREQUEST from a REBINDING client is intended to accommodate
550 sites that have multiple DHCP servers and a mechanism for
551 maintaining consistency among leases managed by multiple servers.
552 A DHCP server MAY extend a client's lease only if it has local
553 administrative authority to do so.
555 if (!requested_opt) {
556 requested_nip = packet.ciaddr;
557 if (requested_nip == 0) {
558 log1("no requested IP and no ciaddr, ignoring");
562 if (lease && requested_nip == lease->lease_nip) {
563 /* client requested or configured IP matches the lease.
564 * ACK it, and bump lease expiration time. */
565 send_ACK(&packet, lease->lease_nip);
569 /* client was talking specifically to us.
570 * "No, we don't have this IP for you". */
577 * "If the server receives a DHCPDECLINE message,
578 * the client has discovered through some other means
579 * that the suggested network address is already
580 * in use. The server MUST mark the network address
581 * as not available and SHOULD notify the local
582 * sysadmin of a possible configuration problem."
584 * SERVER_ID must be present,
585 * REQUESTED_IP must be present,
586 * chaddr must be filled in,
587 * ciaddr must be 0 (we do not check this)
589 log1("Received DECLINE");
592 && lease /* chaddr matches this lease */
593 && requested_nip == lease->lease_nip
595 memset(lease->lease_mac, 0, sizeof(lease->lease_mac));
596 lease->expires = time(NULL) + server_config.decline_time;
601 /* "Upon receipt of a DHCPRELEASE message, the server
602 * marks the network address as not allocated."
604 * SERVER_ID must be present,
605 * REQUESTED_IP must not be present (we do not check this),
606 * chaddr must be filled in,
607 * ciaddr must be filled in
609 log1("Received RELEASE");
611 && lease /* chaddr matches this lease */
612 && packet.ciaddr == lease->lease_nip
614 lease->expires = time(NULL);
619 log1("Received INFORM");
620 send_inform(&packet);
627 /*if (server_config.pidfile) - server_config.pidfile is never NULL */
628 remove_pidfile(server_config.pidfile);