5 * Russ Dill <Russ.Dill@asu.edu> July 2001
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 #include <sys/socket.h>
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
33 #include <sys/ioctl.h>
40 #include "clientpacket.h"
41 #include "clientsocket.h"
45 #include "signalpipe.h"
48 static unsigned long requested_ip; /* = 0 */
49 static unsigned long server_addr;
50 static unsigned long timeout;
51 static int packet_num; /* = 0 */
55 #define LISTEN_KERNEL 1
57 static int listen_mode;
59 struct client_config_t client_config = {
60 /* Default options. */
61 .abort_if_no_lease = 0,
63 .quit_after_lease = 0,
64 .background_if_no_lease = 0,
67 .script = DEFAULT_SCRIPT,
72 .arp = "\0\0\0\0\0\0", /* appease gcc-3.0 */
76 static void __attribute__ ((noreturn)) show_usage(void)
79 "Usage: udhcpc [OPTIONS]\n\n"
80 " -c, --clientid=CLIENTID Client identifier\n"
81 " -H, --hostname=HOSTNAME Client hostname\n"
83 " -F, --fqdn=FQDN Client fully qualified domain name\n"
84 " -f, --foreground Do not fork after getting lease\n"
85 " -b, --background Fork to background if lease cannot be\n"
86 " immediately negotiated.\n"
87 " -i, --interface=INTERFACE Interface to use (default: eth0)\n"
88 " -n, --now Exit with failure if lease cannot be\n"
89 " immediately negotiated.\n"
90 " -p, --pidfile=file Store process ID of daemon in file\n"
91 " -q, --quit Quit after obtaining lease\n"
92 " -r, --request=IP IP address to request (default: none)\n"
93 " -s, --script=file Run file at dhcp events (default:\n"
94 " " DEFAULT_SCRIPT ")\n"
95 " -v, --version Display version\n"
100 #define show_usage bb_show_usage
101 extern void show_usage(void) __attribute__ ((noreturn));
105 /* just a little helper */
106 static void change_mode(int new_mode)
108 DEBUG(LOG_INFO, "entering %s listen mode",
109 new_mode ? (new_mode == 1 ? "kernel" : "raw") : "none");
110 if (fd >= 0) close(fd);
112 listen_mode = new_mode;
116 /* perform a renew */
117 static void perform_renew(void)
119 LOG(LOG_INFO, "Performing a DHCP renew");
122 change_mode(LISTEN_KERNEL);
125 state = RENEW_REQUESTED;
127 case RENEW_REQUESTED: /* impatient are we? fine, square 1 */
128 run_script(NULL, "deconfig");
131 change_mode(LISTEN_RAW);
132 state = INIT_SELECTING;
138 /* start things over */
141 /* Kill any timeouts because the user wants this to hurry along */
146 /* perform a release */
147 static void perform_release(void)
150 struct in_addr temp_addr;
152 /* send release packet */
153 if (state == BOUND || state == RENEWING || state == REBINDING) {
154 temp_addr.s_addr = server_addr;
155 sprintf(buffer, "%s", inet_ntoa(temp_addr));
156 temp_addr.s_addr = requested_ip;
157 LOG(LOG_INFO, "Unicasting a release of %s to %s",
158 inet_ntoa(temp_addr), buffer);
159 send_release(server_addr, requested_ip); /* unicast */
160 run_script(NULL, "deconfig");
162 LOG(LOG_INFO, "Entering released state");
164 change_mode(LISTEN_NONE);
166 timeout = 0x7fffffff;
170 static void client_background(void)
172 background(client_config.pidfile);
173 client_config.foreground = 1; /* Do not fork again. */
174 client_config.background_if_no_lease = 0;
178 #ifdef COMBINED_BINARY
179 int udhcpc_main(int argc, char *argv[])
181 int main(int argc, char *argv[])
184 uint8_t *temp, *message;
185 unsigned long t1 = 0, t2 = 0, xid = 0;
186 unsigned long start = 0, lease;
191 struct dhcpMessage packet;
192 struct in_addr temp_addr;
197 static const struct option arg_options[] = {
198 {"clientid", required_argument, 0, 'c'},
199 {"foreground", no_argument, 0, 'f'},
200 {"background", no_argument, 0, 'b'},
201 {"hostname", required_argument, 0, 'H'},
202 {"hostname", required_argument, 0, 'h'},
203 {"fqdn", required_argument, 0, 'F'},
204 {"interface", required_argument, 0, 'i'},
205 {"now", no_argument, 0, 'n'},
206 {"pidfile", required_argument, 0, 'p'},
207 {"quit", no_argument, 0, 'q'},
208 {"request", required_argument, 0, 'r'},
209 {"script", required_argument, 0, 's'},
210 {"version", no_argument, 0, 'v'},
216 int option_index = 0;
217 c = getopt_long(argc, argv, "c:fbH:h:F:i:np:qr:s:v", arg_options, &option_index);
222 len = strlen(optarg) > 255 ? 255 : strlen(optarg);
223 if (client_config.clientid) free(client_config.clientid);
224 client_config.clientid = xmalloc(len + 2);
225 client_config.clientid[OPT_CODE] = DHCP_CLIENT_ID;
226 client_config.clientid[OPT_LEN] = len;
227 client_config.clientid[OPT_DATA] = '\0';
228 strncpy(client_config.clientid + OPT_DATA, optarg, len);
231 client_config.foreground = 1;
234 client_config.background_if_no_lease = 1;
238 len = strlen(optarg) > 255 ? 255 : strlen(optarg);
239 if (client_config.hostname) free(client_config.hostname);
240 client_config.hostname = xmalloc(len + 2);
241 client_config.hostname[OPT_CODE] = DHCP_HOST_NAME;
242 client_config.hostname[OPT_LEN] = len;
243 strncpy(client_config.hostname + 2, optarg, len);
246 len = strlen(optarg) > 255 ? 255 : strlen(optarg);
247 if (client_config.fqdn) free(client_config.fqdn);
248 client_config.fqdn = xmalloc(len + 5);
249 client_config.fqdn[OPT_CODE] = DHCP_FQDN;
250 client_config.fqdn[OPT_LEN] = len + 3;
252 S: 1 => Client requests Server to update A RR in DNS as well as PTR
253 O: 1 => Server indicates to client that DNS has been updated regardless
254 E: 1 => Name data is DNS format, i.e. <4>host<6>domain<4>com<0> not "host.domain.com"
255 N: 1 => Client requests Server to not update DNS
257 client_config.fqdn[OPT_LEN + 1] = 0x1;
258 client_config.fqdn[OPT_LEN + 2] = 0;
259 client_config.fqdn[OPT_LEN + 3] = 0;
260 strncpy(client_config.fqdn + 5, optarg, len);
263 client_config.interface = optarg;
266 client_config.abort_if_no_lease = 1;
269 client_config.pidfile = optarg;
272 client_config.quit_after_lease = 1;
275 requested_ip = inet_addr(optarg);
278 client_config.script = optarg;
281 printf("udhcpcd, version %s\n\n", VERSION);
289 /* Start the log, sanitize fd's, and write a pid file */
290 start_log_and_pid("udhcpc", client_config.pidfile);
292 if (read_interface(client_config.interface, &client_config.ifindex,
293 NULL, client_config.arp) < 0)
296 if (!client_config.clientid) {
297 client_config.clientid = xmalloc(6 + 3);
298 client_config.clientid[OPT_CODE] = DHCP_CLIENT_ID;
299 client_config.clientid[OPT_LEN] = 7;
300 client_config.clientid[OPT_DATA] = 1;
301 memcpy(client_config.clientid + 3, client_config.arp, 6);
304 /* setup the signal pipe */
307 state = INIT_SELECTING;
308 run_script(NULL, "deconfig");
309 change_mode(LISTEN_RAW);
313 tv.tv_sec = timeout - uptime();
316 if (listen_mode != LISTEN_NONE && fd < 0) {
317 if (listen_mode == LISTEN_KERNEL)
318 fd = listen_socket(INADDR_ANY, CLIENT_PORT, client_config.interface);
320 fd = raw_socket(client_config.ifindex);
322 LOG(LOG_ERR, "FATAL: couldn't listen on socket, %m");
326 max_fd = udhcp_sp_fd_set(&rfds, fd);
329 DEBUG(LOG_INFO, "Waiting on select...");
330 retval = select(max_fd + 1, &rfds, NULL, NULL, &tv);
331 } else retval = 0; /* If we already timed out, fall through */
335 /* timeout dropped to zero */
338 if (packet_num < 3) {
342 /* send discover packet */
343 send_discover(xid, requested_ip); /* broadcast */
345 timeout = now + ((packet_num == 2) ? 4 : 2);
348 run_script(NULL, "leasefail");
349 if (client_config.background_if_no_lease) {
350 LOG(LOG_INFO, "No lease, forking to background.");
352 } else if (client_config.abort_if_no_lease) {
353 LOG(LOG_INFO, "No lease, failing.");
356 /* wait to try again */
361 case RENEW_REQUESTED:
363 if (packet_num < 3) {
364 /* send request packet */
365 if (state == RENEW_REQUESTED)
366 send_renew(xid, server_addr, requested_ip); /* unicast */
367 else send_selecting(xid, server_addr, requested_ip); /* broadcast */
369 timeout = now + ((packet_num == 2) ? 10 : 2);
372 /* timed out, go back to init state */
373 if (state == RENEW_REQUESTED) run_script(NULL, "deconfig");
374 state = INIT_SELECTING;
377 change_mode(LISTEN_RAW);
381 /* Lease is starting to run out, time to enter renewing state */
383 change_mode(LISTEN_KERNEL);
384 DEBUG(LOG_INFO, "Entering renew state");
385 /* fall right through */
387 /* Either set a new T1, or enter REBINDING state */
388 if ((t2 - t1) <= (lease / 14400 + 1)) {
389 /* timed out, enter rebinding state */
391 timeout = now + (t2 - t1);
392 DEBUG(LOG_INFO, "Entering rebinding state");
394 /* send a request packet */
395 send_renew(xid, server_addr, requested_ip); /* unicast */
397 t1 = (t2 - t1) / 2 + t1;
398 timeout = t1 + start;
402 /* Either set a new T2, or enter INIT state */
403 if ((lease - t2) <= (lease / 14400 + 1)) {
404 /* timed out, enter init state */
405 state = INIT_SELECTING;
406 LOG(LOG_INFO, "Lease lost, entering init state");
407 run_script(NULL, "deconfig");
410 change_mode(LISTEN_RAW);
412 /* send a request packet */
413 send_renew(xid, 0, requested_ip); /* broadcast */
415 t2 = (lease - t2) / 2 + t2;
416 timeout = t2 + start;
420 /* yah, I know, *you* say it would never happen */
421 timeout = 0x7fffffff;
424 } else if (retval > 0 && listen_mode != LISTEN_NONE && FD_ISSET(fd, &rfds)) {
425 /* a packet is ready, read it */
427 if (listen_mode == LISTEN_KERNEL)
428 len = get_packet(&packet, fd);
429 else len = get_raw_packet(&packet, fd);
431 if (len == -1 && errno != EINTR) {
432 DEBUG(LOG_INFO, "error on read, %m, reopening socket");
433 change_mode(listen_mode); /* just close and reopen */
435 if (len < 0) continue;
437 if (packet.xid != xid) {
438 DEBUG(LOG_INFO, "Ignoring XID %lx (our xid is %lx)",
439 (unsigned long) packet.xid, xid);
443 if ((message = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) {
444 DEBUG(LOG_ERR, "couldnt get option from packet -- ignoring");
450 /* Must be a DHCPOFFER to one of our xid's */
451 if (*message == DHCPOFFER) {
452 if ((temp = get_option(&packet, DHCP_SERVER_ID))) {
453 memcpy(&server_addr, temp, 4);
455 requested_ip = packet.yiaddr;
457 /* enter requesting state */
462 DEBUG(LOG_ERR, "No server ID in message");
466 case RENEW_REQUESTED:
470 if (*message == DHCPACK) {
471 if (!(temp = get_option(&packet, DHCP_LEASE_TIME))) {
472 LOG(LOG_ERR, "No lease time with ACK, using 1 hour lease");
475 memcpy(&lease, temp, 4);
476 lease = ntohl(lease);
479 /* enter bound state */
482 /* little fixed point for n * .875 */
483 t2 = (lease * 0x7) >> 3;
484 temp_addr.s_addr = packet.yiaddr;
485 LOG(LOG_INFO, "Lease of %s obtained, lease time %ld",
486 inet_ntoa(temp_addr), lease);
488 timeout = t1 + start;
489 requested_ip = packet.yiaddr;
491 ((state == RENEWING || state == REBINDING) ? "renew" : "bound"));
494 change_mode(LISTEN_NONE);
495 if (client_config.quit_after_lease)
497 if (!client_config.foreground)
500 } else if (*message == DHCPNAK) {
501 /* return to init state */
502 LOG(LOG_INFO, "Received DHCP NAK");
503 run_script(&packet, "nak");
504 if (state != REQUESTING)
505 run_script(NULL, "deconfig");
506 state = INIT_SELECTING;
510 change_mode(LISTEN_RAW);
511 sleep(3); /* avoid excessive network traffic */
514 /* case BOUND, RELEASED: - ignore all packets */
516 } else if (retval > 0 && (sig = udhcp_sp_read(&rfds))) {
525 LOG(LOG_INFO, "Received SIGTERM");
528 } else if (retval == -1 && errno == EINTR) {
529 /* a signal was caught */
531 /* An error occured */
532 DEBUG(LOG_ERR, "Error on select");