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.
24 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
35 #include <sys/ioctl.h>
42 #include "clientpacket.h"
50 static unsigned long requested_ip; /* = 0 */
51 static unsigned long server_addr;
52 static unsigned long timeout;
53 static int packet_num; /* = 0 */
56 char session_path[64];
57 static char status_path[128]="";
58 static char pid_path[128]="";
59 char vendor_class_id[128]="";
62 #define LISTEN_KERNEL 1
64 static int listen_mode = LISTEN_RAW;
66 #define DEFAULT_SCRIPT "/etc/dhcp/dhcp_getdata"
68 struct client_config_t client_config = {
69 /* Default options. */
75 script: DEFAULT_SCRIPT,
79 arp: "\0\0\0\0\0\0", /* appease gcc-3.0 */
83 void setStatus(int status) {
86 sprintf(cmd, "echo %d > %s", status, status_path);
93 sprintf(cmd, "echo %d > %s", getpid(), pid_path);
97 static void print_usage(void)
100 "Usage: udhcpcd [OPTIONS]\n\n"
101 " -c, --clientid=CLIENTID Client identifier\n"
102 " -H, --hostname=HOSTNAME Client hostname\n"
103 " -f, --foreground Do not fork after getting lease\n"
104 " -i, --interface=INTERFACE Interface to use (default: eth0)\n"
105 " -n, --now Exit with failure if lease cannot be\n"
106 " immediately negotiated.\n"
107 " -p, --pidfile=file Store process ID of daemon in file\n"
108 " -q, --quit Quit after obtaining lease\n"
109 " -r, --request=IP IP address to request (default: none)\n"
110 " -s, --script=file Run file at dhcp events (default:\n"
111 " " DEFAULT_SCRIPT ")\n"
112 " -v, --version Display version\n"
117 /* SIGUSR1 handler (renew) */
118 static void renew_requested(int sig)
121 LOG(LOG_INFO, "Received SIGUSR1");
122 if (state == BOUND || state == RENEWING || state == REBINDING ||
124 listen_mode = LISTEN_KERNEL;
127 state = RENEW_REQUESTED;
130 if (state == RELEASED) {
131 listen_mode = LISTEN_RAW;
132 state = INIT_SELECTING;
135 /* Kill any timeouts because the user wants this to hurry along */
140 /* SIGUSR2 handler (release) */
141 static void release_requested(int sig)
144 LOG(LOG_INFO, "Received SIGUSR2");
145 /* send release packet */
146 if (state == BOUND || state == RENEWING || state == REBINDING) {
147 send_release(server_addr, requested_ip); /* unicast */
148 run_script(NULL, "deconfig");
153 timeout = 0xffffffff;
157 /* Exit and cleanup */
158 static void exit_client(int retval)
160 pidfile_delete(client_config.pidfile);
166 /* SIGTERM handler */
167 static void terminate(int sig)
170 LOG(LOG_INFO, "Received SIGTERM");
175 static void background(void)
178 if (client_config.quit_after_lease) {
180 } else if (!client_config.foreground) {
181 pid_fd = pidfile_acquire(client_config.pidfile); /* hold lock during fork. */
190 break; /* child continues */
192 exit(0); /* parent exits */
199 client_config.foreground = 1; /* Do not fork again. */
200 pidfile_write_release(pid_fd);
206 #ifdef COMBINED_BINARY
207 int udhcpc(int argc, char *argv[])
209 int main(int argc, char *argv[])
212 char *temp, *message;
213 unsigned long t1 = 0, t2 = 0, xid = 0;
214 unsigned long start = 0, lease;
220 struct dhcpMessage packet;
221 struct in_addr temp_addr;
224 static struct option options[] = {
225 {"clientid", required_argument, 0, 'c'},
226 {"foreground", no_argument, 0, 'f'},
227 {"hostname", required_argument, 0, 'H'},
228 {"help", no_argument, 0, 'h'},
229 {"interface", required_argument, 0, 'i'},
230 {"now", no_argument, 0, 'n'},
231 {"pidfile", required_argument, 0, 'p'},
232 {"quit", no_argument, 0, 'q'},
233 {"request", required_argument, 0, 'r'},
234 {"script", required_argument, 0, 's'},
235 {"version", no_argument, 0, 'v'},
241 int option_index = 0;
243 c = getopt_long(argc, argv, "c:fH:hi:np:qr:s:d:v", options, &option_index);
248 len = strlen(optarg) > 255 ? 255 : strlen(optarg);
249 if (client_config.clientid) free(client_config.clientid);
250 client_config.clientid = malloc(len + 2);
251 client_config.clientid[OPT_CODE] = DHCP_CLIENT_ID;
252 client_config.clientid[OPT_LEN] = len;
253 strncpy(client_config.clientid + 2, optarg, len);
256 client_config.foreground = 1;
259 len = strlen(optarg) > 255 ? 255 : strlen(optarg);
260 if (client_config.hostname) free(client_config.hostname);
261 client_config.hostname = malloc(len + 2);
262 client_config.hostname[OPT_CODE] = DHCP_HOST_NAME;
263 client_config.hostname[OPT_LEN] = len;
264 strncpy(client_config.hostname + 2, optarg, len);
270 client_config.interface = optarg;
272 strcpy(session_path, optarg);
275 client_config.abort_if_no_lease = 1;
278 client_config.pidfile = optarg;
281 client_config.quit_after_lease = 1;
284 requested_ip = inet_addr(optarg);
288 strcpy(vendor_class_id, optarg);
291 client_config.script = optarg;
294 printf("udhcpcd, version %s\n\n", VERSION);
300 if (strlen(session_path) > 0) {
301 sprintf(status_path, "%s/%s/%s", _PATH_WAN_DIR, session_path, _PATH_MSG);
302 sprintf(pid_path, "%s/%s/%s", _PATH_WAN_DIR, session_path, _PATH_PID);
306 LOG(LOG_INFO, "udhcp client (v%s) started", VERSION);
308 pid_fd = pidfile_acquire(client_config.pidfile);
309 pidfile_write_release(pid_fd);
311 if ((fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) >= 0) {
312 strcpy(ifr.ifr_name, client_config.interface);
313 if (ioctl(fd, SIOCGIFINDEX, &ifr) == 0) {
314 DEBUG(LOG_INFO, "adapter index %d", ifr.ifr_ifindex);
315 client_config.ifindex = ifr.ifr_ifindex;
317 LOG(LOG_ERR, "SIOCGIFINDEX failed! %s", strerror(errno));
320 if (ioctl(fd, SIOCGIFHWADDR, &ifr) == 0) {
321 memcpy(client_config.arp, ifr.ifr_hwaddr.sa_data, 6);
322 DEBUG(LOG_INFO, "adapter hardware address %02x:%02x:%02x:%02x:%02x:%02x",
323 client_config.arp[0], client_config.arp[1], client_config.arp[2],
324 client_config.arp[3], client_config.arp[4], client_config.arp[5]);
326 LOG(LOG_ERR, "SIOCGIFHWADDR failed! %s", strerror(errno));
330 LOG(LOG_ERR, "socket failed! %s", strerror(errno));
336 /* setup signal handlers */
337 signal(SIGUSR1, renew_requested);
338 signal(SIGUSR2, release_requested);
339 signal(SIGTERM, terminate);
341 state = INIT_SELECTING;
343 // run_script(NULL, "deconfig");
354 if (listen_mode == LISTEN_KERNEL) {
355 if ((fd = listen_socket(INADDR_ANY, CLIENT_PORT, client_config.interface)) < 0) {
356 LOG(LOG_ERR, "couldn't create server socket -- au revoir");
359 } else if (listen_mode == LISTEN_RAW) {
360 if ((fd = raw_socket(client_config.ifindex)) < 0) {
361 LOG(LOG_ERR, "couldn't create raw socket -- au revoir");
366 tv.tv_sec = timeout - time(0);
369 if (listen_mode) FD_SET(fd, &rfds);
372 retval = select(fd + 1, &rfds, NULL, NULL, &tv);
373 } else retval = 0; /* If we already timed out, fall through */
376 /* timeout dropped to zero */
381 if (packet_num < 3) {
385 /* send discover packet */
386 send_discover(xid, requested_ip); /* broadcast */
388 timeout = time(0) + ((packet_num == 2) ? 10 : 2);
391 if (client_config.abort_if_no_lease) {
393 "No lease, failing.");
396 /* wait to try again */
398 timeout = time(0) + 60;
401 case RENEW_REQUESTED:
403 if (packet_num < 3) {
404 /* send request packet */
405 if (state == RENEW_REQUESTED)
406 send_renew(xid, server_addr, requested_ip); /* unicast */
407 else send_selecting(xid, server_addr, requested_ip); /* broadcast */
409 timeout = time(0) + ((packet_num == 2) ? 10 : 2);
412 /* timed out, go back to init state */
413 state = INIT_SELECTING;
416 listen_mode = LISTEN_RAW;
421 /* Lease is starting to run out, time to enter renewing state */
423 listen_mode = LISTEN_KERNEL;
424 DEBUG(LOG_INFO, "Entering renew state");
425 /* fall right through */
427 /* Either set a new T1, or enter REBINDING state */
428 if ((t2 - t1) <= (lease / 14400 + 1)) {
429 /* timed out, enter rebinding state */
431 timeout = time(0) + (t2 - t1);
432 DEBUG(LOG_INFO, "Entering rebinding state");
434 /* send a request packet */
435 send_renew(xid, server_addr, requested_ip); /* unicast */
437 t1 = (t2 - t1) / 2 + t1;
438 timeout = t1 + start;
442 /* Either set a new T2, or enter INIT state */
443 if ((lease - t2) <= (lease / 14400 + 1)) {
444 /* timed out, enter init state */
445 state = INIT_SELECTING;
446 LOG(LOG_INFO, "Lease lost, entering init state");
447 run_script(NULL, "deconfig");
450 listen_mode = LISTEN_RAW;
452 /* send a request packet */
453 send_renew(xid, 0, requested_ip); /* broadcast */
455 t2 = (lease - t2) / 2 + t2;
456 timeout = t2 + start;
460 /* yah, I know, *you* say it would never happen */
461 timeout = 0xffffffff;
464 } else if (retval > 0 && listen_mode != LISTEN_NONE && FD_ISSET(fd, &rfds)) {
465 /* a packet is ready, read it */
467 if (listen_mode == LISTEN_KERNEL) {
468 if (get_packet(&packet, fd) < 0) continue;
470 if (get_raw_packet(&packet, fd) < 0) continue;
473 if (packet.xid != xid) {
474 DEBUG(LOG_INFO, "Ignoring XID %lx (our xid is %lx)",
475 (unsigned long) packet.xid, xid);
479 if ((message = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) {
480 DEBUG(LOG_ERR, "couldnt get option from packet -- ignoring");
486 /* Must be a DHCPOFFER to one of our xid's */
487 if (*message == DHCPOFFER) {
488 if ((temp = get_option(&packet, DHCP_SERVER_ID))) {
489 memcpy(&server_addr, temp, 4);
491 requested_ip = packet.yiaddr;
493 /* enter requesting state */
498 DEBUG(LOG_ERR, "No server ID in message");
502 case RENEW_REQUESTED:
506 if (*message == DHCPACK) {
507 if (!(temp = get_option(&packet, DHCP_LEASE_TIME))) {
508 LOG(LOG_ERR, "No lease time with ACK, using 1 hour lease");
511 memcpy(&lease, temp, 4);
512 lease = ntohl(lease);
515 /* enter bound state */
518 /* little fixed point for n * .875 */
519 t2 = (lease * 0x7) >> 3;
520 temp_addr.s_addr = packet.yiaddr;
521 LOG(LOG_INFO, "Lease of %s obtained, lease time %ld",
522 inet_ntoa(temp_addr), lease);
524 timeout = t1 + start;
525 requested_ip = packet.yiaddr;
527 ((state == RENEWING || state == REBINDING) ? "renew" : "bound"));
530 listen_mode = LISTEN_NONE;
536 } else if (*message == DHCPNAK) {
537 /* return to init state */
538 LOG(LOG_INFO, "Received DHCP NAK");
539 if (state != REQUESTING)
540 run_script(NULL, "deconfig");
541 state = INIT_SELECTING;
545 listen_mode = LISTEN_RAW;
553 /* ignore all packets */
556 } else if (retval == -1 && errno == EINTR) {
557 /* a signal was caught */
560 /* An error occured */
561 DEBUG(LOG_ERR, "Error on select");