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 #include "board_api.h"
53 static unsigned long requested_ip; /* = 0 */
54 static unsigned long server_addr;
55 static unsigned long timeout;
56 static int packet_num; /* = 0 */
59 char session_path[64];
60 static char status_path[128]="";
61 static char pid_path[128]="";
62 char vendor_class_id[128]="";
65 #define LISTEN_KERNEL 1
67 static int listen_mode = LISTEN_RAW;
69 static int old_mode = LISTEN_RAW;
70 #define INIT_TIMEOUT 5
73 #define DEFAULT_SCRIPT "/etc/dhcp/dhcp_getdata"
75 struct client_config_t client_config = {
76 /* Default options. */
82 script: DEFAULT_SCRIPT,
86 arp: "\0\0\0\0\0\0", /* appease gcc-3.0 */
90 void setStatus(int status) {
94 sprintf(cmd, "echo %d > %s", status, status_path);
97 if( (f = open( "/dev/brcmboard", O_RDWR )) != -1 )
99 ioctl( f, BOARD_IOCTL_WAKEUP_MONITOR_TASK, NULL);
107 sprintf(cmd, "echo %d > %s", getpid(), pid_path);
111 static void print_usage(void)
114 "Usage: udhcpcd [OPTIONS]\n\n"
115 " -c, --clientid=CLIENTID Client identifier\n"
116 " -H, --hostname=HOSTNAME Client hostname\n"
117 " -f, --foreground Do not fork after getting lease\n"
118 " -i, --interface=INTERFACE Interface to use (default: eth0)\n"
119 " -n, --now Exit with failure if lease cannot be\n"
120 " immediately negotiated.\n"
121 " -p, --pidfile=file Store process ID of daemon in file\n"
122 " -q, --quit Quit after obtaining lease\n"
123 " -r, --request=IP IP address to request (default: none)\n"
124 " -s, --script=file Run file at dhcp events (default:\n"
125 " " DEFAULT_SCRIPT ")\n"
126 " -v, --version Display version\n"
131 /* SIGUSR1 handler (renew) */
132 static void renew_requested(int sig)
135 LOG(LOG_INFO, "Received SIGUSR1");
136 if (state == BOUND || state == RENEWING || state == REBINDING ||
138 listen_mode = LISTEN_KERNEL;
141 state = RENEW_REQUESTED;
144 if (state == RELEASED) {
145 listen_mode = LISTEN_RAW;
146 state = INIT_SELECTING;
149 /* Kill any timeouts because the user wants this to hurry along */
154 /* SIGUSR2 handler (release) */
155 static void release_requested(int sig)
158 LOG(LOG_INFO, "Received SIGUSR2");
159 /* send release packet */
160 if (state == BOUND || state == RENEWING || state == REBINDING) {
161 send_release(server_addr, requested_ip); /* unicast */
162 run_script(NULL, "deconfig");
167 timeout = 0xffffffff;
171 /* Exit and cleanup */
172 static void exit_client(int retval)
174 pidfile_delete(client_config.pidfile);
180 /* SIGTERM handler */
181 static void terminate(int sig)
184 LOG(LOG_INFO, "Received SIGTERM");
189 static void background(void)
192 if (client_config.quit_after_lease) {
194 } else if (!client_config.foreground) {
195 pid_fd = pidfile_acquire(client_config.pidfile); /* hold lock during fork. */
204 break; /* child continues */
206 exit(0); /* parent exits */
213 client_config.foreground = 1; /* Do not fork again. */
214 pidfile_write_release(pid_fd);
220 #ifdef COMBINED_BINARY
221 int udhcpc(int argc, char *argv[])
223 int main(int argc, char *argv[])
226 char *temp, *message;
227 unsigned long t1 = 0, t2 = 0, xid = 0;
228 unsigned long start = 0, lease;
234 struct dhcpMessage packet;
235 struct in_addr temp_addr;
238 static struct option options[] = {
239 {"clientid", required_argument, 0, 'c'},
240 {"foreground", no_argument, 0, 'f'},
241 {"hostname", required_argument, 0, 'H'},
242 {"help", no_argument, 0, 'h'},
243 {"interface", required_argument, 0, 'i'},
244 {"now", no_argument, 0, 'n'},
245 {"pidfile", required_argument, 0, 'p'},
246 {"quit", no_argument, 0, 'q'},
247 {"request", required_argument, 0, 'r'},
248 {"script", required_argument, 0, 's'},
249 {"version", no_argument, 0, 'v'},
255 int option_index = 0;
257 c = getopt_long(argc, argv, "c:fH:hi:np:qr:s:d:v", options, &option_index);
262 len = strlen(optarg) > 255 ? 255 : strlen(optarg);
263 if (client_config.clientid) free(client_config.clientid);
264 client_config.clientid = malloc(len + 2);
265 client_config.clientid[OPT_CODE] = DHCP_CLIENT_ID;
266 client_config.clientid[OPT_LEN] = len;
267 strncpy(client_config.clientid + 2, optarg, len);
270 client_config.foreground = 1;
273 len = strlen(optarg) > 255 ? 255 : strlen(optarg);
274 if (client_config.hostname) free(client_config.hostname);
275 client_config.hostname = malloc(len + 2);
276 client_config.hostname[OPT_CODE] = DHCP_HOST_NAME;
277 client_config.hostname[OPT_LEN] = len;
278 strncpy(client_config.hostname + 2, optarg, len);
284 client_config.interface = optarg;
286 strcpy(session_path, optarg);
289 client_config.abort_if_no_lease = 1;
292 client_config.pidfile = optarg;
295 client_config.quit_after_lease = 1;
298 requested_ip = inet_addr(optarg);
302 strcpy(vendor_class_id, optarg);
305 client_config.script = optarg;
308 printf("udhcpcd, version %s\n\n", VERSION);
314 if (strlen(session_path) > 0) {
315 sprintf(status_path, "%s/%s/%s", _PATH_WAN_DIR, session_path, _PATH_MSG);
316 sprintf(pid_path, "%s/%s/%s", _PATH_WAN_DIR, session_path, _PATH_PID);
320 LOG(LOG_INFO, "udhcp client (v%s) started", VERSION);
322 pid_fd = pidfile_acquire(client_config.pidfile);
323 pidfile_write_release(pid_fd);
325 if ((fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) >= 0) {
326 strcpy(ifr.ifr_name, client_config.interface);
327 if (ioctl(fd, SIOCGIFINDEX, &ifr) == 0) {
328 DEBUG(LOG_INFO, "adapter index %d", ifr.ifr_ifindex);
329 client_config.ifindex = ifr.ifr_ifindex;
331 LOG(LOG_ERR, "SIOCGIFINDEX failed! %s", strerror(errno));
334 if (ioctl(fd, SIOCGIFHWADDR, &ifr) == 0) {
335 memcpy(client_config.arp, ifr.ifr_hwaddr.sa_data, 6);
336 DEBUG(LOG_INFO, "adapter hardware address %02x:%02x:%02x:%02x:%02x:%02x",
337 client_config.arp[0], client_config.arp[1], client_config.arp[2],
338 client_config.arp[3], client_config.arp[4], client_config.arp[5]);
340 LOG(LOG_ERR, "SIOCGIFHWADDR failed! %s", strerror(errno));
344 LOG(LOG_ERR, "socket failed! %s", strerror(errno));
350 /* setup signal handlers */
351 signal(SIGUSR1, renew_requested);
352 signal(SIGUSR2, release_requested);
353 signal(SIGTERM, terminate);
355 state = INIT_SELECTING;
357 // run_script(NULL, "deconfig");
365 if ((old_mode != listen_mode) || (fd == -1)) {
366 old_mode = listen_mode;
373 if (listen_mode == LISTEN_RAW) {
374 if ((fd = raw_socket(client_config.ifindex)) < 0) {
375 LOG(LOG_ERR, "couldn't create raw socket -- au revoir");
379 else if (listen_mode == LISTEN_KERNEL) {
380 if ((fd = listen_socket(INADDR_ANY, CLIENT_PORT, client_config.interface)) < 0) {
381 LOG(LOG_ERR, "couldn't create server socket -- au revoir");
388 tv.tv_sec = timeout - time(0);
391 if (listen_mode) FD_SET(fd, &rfds);
394 retval = select(fd + 1, &rfds, NULL, NULL, &tv);
395 } else retval = 0; /* If we already timed out, fall through */
398 /* timeout dropped to zero */
403 if (packet_num < 3) {
407 /* send discover packet */
408 send_discover(xid, requested_ip); /* broadcast */
410 timeout = time(0) + ((packet_num == 2) ? REQ_TIMEOUT : 2);
413 if (client_config.abort_if_no_lease) {
415 "No lease, failing.");
418 /* wait to try again */
420 timeout = time(0) + INIT_TIMEOUT;
423 case RENEW_REQUESTED:
425 if (packet_num < 3) {
426 /* send request packet */
427 if (state == RENEW_REQUESTED)
428 send_renew(xid, server_addr, requested_ip); /* unicast */
429 else send_selecting(xid, server_addr, requested_ip); /* broadcast */
431 timeout = time(0) + ((packet_num == 2) ? REQ_TIMEOUT : 2);
434 /* timed out, go back to init state */
435 state = INIT_SELECTING;
438 listen_mode = LISTEN_RAW;
443 /* Lease is starting to run out, time to enter renewing state */
445 listen_mode = LISTEN_KERNEL;
446 DEBUG(LOG_INFO, "Entering renew state");
447 /* fall right through */
449 /* Either set a new T1, or enter REBINDING state */
450 if ((t2 - t1) <= (lease / 14400 + 1)) {
451 /* timed out, enter rebinding state */
453 timeout = time(0) + (t2 - t1);
454 DEBUG(LOG_INFO, "Entering rebinding state");
456 /* send a request packet */
457 send_renew(xid, server_addr, requested_ip); /* unicast */
459 t1 = (t2 - t1) / 2 + t1;
460 timeout = t1 + start;
464 /* Either set a new T2, or enter INIT state */
465 if ((lease - t2) <= (lease / 14400 + 1)) {
466 /* timed out, enter init state */
467 state = INIT_SELECTING;
468 LOG(LOG_INFO, "Lease lost, entering init state");
469 run_script(NULL, "deconfig");
472 listen_mode = LISTEN_RAW;
474 /* send a request packet */
475 send_renew(xid, 0, requested_ip); /* broadcast */
477 t2 = (lease - t2) / 2 + t2;
478 timeout = t2 + start;
482 /* yah, I know, *you* say it would never happen */
483 timeout = 0xffffffff;
486 } else if (retval > 0 && listen_mode != LISTEN_NONE && FD_ISSET(fd, &rfds)) {
487 /* a packet is ready, read it */
489 if (listen_mode == LISTEN_KERNEL) {
490 if (get_packet(&packet, fd) < 0) continue;
492 if (get_raw_packet(&packet, fd) < 0) continue;
495 if (packet.xid != xid) {
496 DEBUG(LOG_INFO, "Ignoring XID %lx (our xid is %lx)",
497 (unsigned long) packet.xid, xid);
501 if ((message = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) {
502 DEBUG(LOG_ERR, "couldnt get option from packet -- ignoring");
508 /* Must be a DHCPOFFER to one of our xid's */
509 if (*message == DHCPOFFER) {
510 if ((temp = get_option(&packet, DHCP_SERVER_ID))) {
511 memcpy(&server_addr, temp, 4);
513 requested_ip = packet.yiaddr;
515 /* enter requesting state */
520 DEBUG(LOG_ERR, "No server ID in message");
524 case RENEW_REQUESTED:
528 if (*message == DHCPACK) {
529 if (!(temp = get_option(&packet, DHCP_LEASE_TIME))) {
530 LOG(LOG_ERR, "No lease time with ACK, using 1 hour lease");
533 memcpy(&lease, temp, 4);
534 lease = ntohl(lease);
537 /* enter bound state */
540 /* little fixed point for n * .875 */
541 t2 = (lease * 0x7) >> 3;
542 temp_addr.s_addr = packet.yiaddr;
543 LOG(LOG_INFO, "Lease of %s obtained, lease time %ld",
544 inet_ntoa(temp_addr), lease);
546 timeout = t1 + start;
547 requested_ip = packet.yiaddr;
549 ((state == RENEWING || state == REBINDING) ? "renew" : "bound"));
552 listen_mode = LISTEN_NONE;
558 } else if (*message == DHCPNAK) {
559 /* return to init state */
560 LOG(LOG_INFO, "Received DHCP NAK");
561 if (state != REQUESTING)
562 run_script(NULL, "deconfig");
563 state = INIT_SELECTING;
567 listen_mode = LISTEN_RAW;
575 /* ignore all packets */
578 } else if (retval == -1 && errno == EINTR) {
579 /* a signal was caught */
582 /* An error occured */
583 DEBUG(LOG_ERR, "Error on select");