www.usr.com/support/gpl/USR9113_release1.0.tar.gz
[bcm963xx.git] / userapps / opensource / udhcp / dhcpc.c
1 /* dhcpd.c
2  *
3  * udhcp DHCP client
4  *
5  * Russ Dill <Russ.Dill@asu.edu> July 2001
6  *
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.
11  *
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.
16  *
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.
20  */
21  
22 #include <stdio.h>
23 #include <sys/time.h>
24 #include <sys/types.h>
25 #include <sys/file.h>
26 #include <unistd.h>
27 #include <getopt.h>
28 #include <stdlib.h>
29 #include <sys/socket.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
32 #include <signal.h>
33 #include <time.h>
34 #include <string.h>
35 #include <sys/ioctl.h>
36 #include <net/if.h>
37 #include <errno.h>
38
39 #include "dhcpd.h"
40 #include "dhcpc.h"
41 #include "options.h"
42 #include "clientpacket.h"
43 #include "packet.h"
44 #include "script.h"
45 #include "socket.h"
46 #include "debug.h"
47 #include "pidfile.h"
48
49 // brcm
50 #include "board_api.h"
51
52 static int state;
53 static unsigned long requested_ip; /* = 0 */
54 static unsigned long server_addr;
55 static unsigned long timeout;
56 static int packet_num; /* = 0 */
57
58 // brcm
59 char session_path[64];
60 static char status_path[128]="";
61 static char pid_path[128]="";
62 char vendor_class_id[128]="";
63
64 #define LISTEN_NONE 0
65 #define LISTEN_KERNEL 1
66 #define LISTEN_RAW 2
67 static int listen_mode = LISTEN_RAW;
68 // brcm
69 static int old_mode = LISTEN_RAW;
70 #define INIT_TIMEOUT 5
71 #define REQ_TIMEOUT 4
72
73 #define DEFAULT_SCRIPT  "/etc/dhcp/dhcp_getdata"
74
75 struct client_config_t client_config = {
76         /* Default options. */
77         abort_if_no_lease: 0,
78         foreground: 0,
79         quit_after_lease: 0,
80         interface: "eth0",
81         pidfile: NULL,
82         script: DEFAULT_SCRIPT,
83         clientid: NULL,
84         hostname: NULL,
85         ifindex: 0,
86         arp: "\0\0\0\0\0\0",            /* appease gcc-3.0 */
87 };
88
89 // brcm
90 void setStatus(int status) {
91     char cmd[128] = "";
92     int f;
93     
94     sprintf(cmd, "echo %d > %s", status, status_path);
95     system(cmd); 
96
97     if( (f = open( "/dev/brcmboard", O_RDWR )) != -1 )
98     {
99         ioctl( f, BOARD_IOCTL_WAKEUP_MONITOR_TASK, NULL);
100         close(f);
101     }
102 }
103
104 void setPid() {
105     char cmd[128] = "";
106     
107     sprintf(cmd, "echo %d > %s", getpid(), pid_path);
108     system(cmd); 
109 }
110
111 static void print_usage(void)
112 {
113         printf(
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"
127         );
128 }
129
130
131 /* SIGUSR1 handler (renew) */
132 static void renew_requested(int sig)
133 {
134         sig = 0;
135         LOG(LOG_INFO, "Received SIGUSR1");
136         if (state == BOUND || state == RENEWING || state == REBINDING ||
137             state == RELEASED) {
138                 listen_mode = LISTEN_KERNEL;
139                 server_addr = 0;
140                 packet_num = 0;
141                 state = RENEW_REQUESTED;
142         }
143
144         if (state == RELEASED) {
145                 listen_mode = LISTEN_RAW;
146                 state = INIT_SELECTING;
147         }
148
149         /* Kill any timeouts because the user wants this to hurry along */
150         timeout = 0;
151 }
152
153
154 /* SIGUSR2 handler (release) */
155 static void release_requested(int sig)
156 {
157         sig = 0;
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");
163         }
164
165         listen_mode = 0;
166         state = RELEASED;
167         timeout = 0xffffffff;
168 }
169
170
171 /* Exit and cleanup */
172 static void exit_client(int retval)
173 {
174         pidfile_delete(client_config.pidfile);
175         CLOSE_LOG();
176         exit(retval);
177 }
178
179
180 /* SIGTERM handler */
181 static void terminate(int sig)
182 {
183         sig = 0;
184         LOG(LOG_INFO, "Received SIGTERM");
185         exit_client(0);
186 }
187
188
189 static void background(void)
190 {
191         int pid_fd;
192         if (client_config.quit_after_lease) {
193                 exit_client(0);
194         } else if (!client_config.foreground) {
195                 pid_fd = pidfile_acquire(client_config.pidfile); /* hold lock during fork. */
196                 switch(fork()) {
197                 case -1:
198                         perror("fork");
199                         exit_client(1);
200                         /*NOTREACHED*/
201                 case 0:
202                         // brcm
203                         setPid();
204                         break; /* child continues */
205                 default:
206                         exit(0); /* parent exits */
207                         /*NOTREACHED*/
208                 }
209                 close(0);
210                 close(1);
211                 close(2);
212                 setsid();
213                 client_config.foreground = 1; /* Do not fork again. */
214                 pidfile_write_release(pid_fd);
215         }
216 }
217
218
219
220 #ifdef COMBINED_BINARY
221 int udhcpc(int argc, char *argv[])
222 #else
223 int main(int argc, char *argv[])
224 #endif
225 {
226         char *temp, *message;
227         unsigned long t1 = 0, t2 = 0, xid = 0;
228         unsigned long start = 0, lease;
229         fd_set rfds;
230         int fd, retval;
231         struct timeval tv;
232         int c, len;
233         struct ifreq ifr;
234         struct dhcpMessage packet;
235         struct in_addr temp_addr;
236         int pid_fd;
237
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'},
250                 {0, 0, 0, 0}
251         };
252
253         /* get options */
254         while (1) {
255                 int option_index = 0;
256 // brcm
257                 c = getopt_long(argc, argv, "c:fH:hi:np:qr:s:d:v", options, &option_index);
258                 if (c == -1) break;
259                 
260                 switch (c) {
261                 case 'c':
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);
268                         break;
269                 case 'f':
270                         client_config.foreground = 1;
271                         break;
272                 case 'H':
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);
279                         break;
280                 case 'h':
281                         print_usage();
282                         return 0;
283                 case 'i':
284                         client_config.interface =  optarg;
285 // brcm
286                         strcpy(session_path, optarg);
287                         break;
288                 case 'n':
289                         client_config.abort_if_no_lease = 1;
290                         break;
291                 case 'p':
292                         client_config.pidfile = optarg;
293                         break;
294                 case 'q':
295                         client_config.quit_after_lease = 1;
296                         break;
297                 case 'r':
298                         requested_ip = inet_addr(optarg);
299                         break;
300 // brcm
301                 case 'd':
302                         strcpy(vendor_class_id, optarg);
303                         break;
304                 case 's':
305                         client_config.script = optarg;
306                         break;
307                 case 'v':
308                         printf("udhcpcd, version %s\n\n", VERSION);
309                         break;
310                 }
311         }
312
313         // brcm
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);
317         }
318
319         OPEN_LOG("udhcpc");
320         LOG(LOG_INFO, "udhcp client (v%s) started", VERSION);
321
322         pid_fd = pidfile_acquire(client_config.pidfile);
323         pidfile_write_release(pid_fd);
324
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;
330                 } else {
331                         LOG(LOG_ERR, "SIOCGIFINDEX failed! %s", strerror(errno));
332                         exit_client(1);
333                 }
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]);
339                 } else {
340                         LOG(LOG_ERR, "SIOCGIFHWADDR failed! %s", strerror(errno));
341                         exit_client(1);
342                 }
343         } else {
344                 LOG(LOG_ERR, "socket failed! %s", strerror(errno));
345                 exit_client(1);
346         }
347         close(fd);
348         fd = -1;
349
350         /* setup signal handlers */
351         signal(SIGUSR1, renew_requested);
352         signal(SIGUSR2, release_requested);
353         signal(SIGTERM, terminate);
354         
355         state = INIT_SELECTING;
356         // brcm
357         // run_script(NULL, "deconfig");
358
359         // brcm
360         setStatus(0);
361
362         for (;;) {
363
364                 // brcm
365                 if ((old_mode != listen_mode) || (fd == -1)) {
366                     old_mode = listen_mode;
367                 
368                     if (fd > 0) {
369                             close(fd);
370                             fd = -1;
371                     }
372                 
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");
376                                     exit_client(0);
377                             }
378                     }
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");
382                                     exit_client(0);
383                             }                   
384                     } else 
385                         fd = -1;
386                 }
387
388                 tv.tv_sec = timeout - time(0);
389                 tv.tv_usec = 0;
390                 FD_ZERO(&rfds);
391                 if (listen_mode) FD_SET(fd, &rfds);
392                 
393                 if (tv.tv_sec > 0) {
394                         retval = select(fd + 1, &rfds, NULL, NULL, &tv);
395                 } else retval = 0; /* If we already timed out, fall through */
396                 
397                 if (retval == 0) {
398                         /* timeout dropped to zero */
399                         switch (state) {
400                         case INIT_SELECTING:
401                                 // brcm
402                                 setStatus(0);
403                                 if (packet_num < 3) {
404                                         if (packet_num == 0)
405                                                 xid = random_xid();
406
407                                         /* send discover packet */
408                                         send_discover(xid, requested_ip); /* broadcast */
409                                         
410                                         timeout = time(0) + ((packet_num == 2) ? REQ_TIMEOUT : 2);
411                                         packet_num++;
412                                 } else {
413                                         if (client_config.abort_if_no_lease) {
414                                                 LOG(LOG_INFO,
415                                                     "No lease, failing.");
416                                                 exit_client(1);
417                                         }
418                                         /* wait to try again */
419                                         packet_num = 0;
420                                         timeout = time(0) + INIT_TIMEOUT;
421                                 }
422                                 break;
423                         case RENEW_REQUESTED:
424                         case REQUESTING:
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 */
430                                         
431                                         timeout = time(0) + ((packet_num == 2) ? REQ_TIMEOUT : 2);
432                                         packet_num++;
433                                 } else {
434                                         /* timed out, go back to init state */
435                                         state = INIT_SELECTING;
436                                         timeout = time(0);
437                                         packet_num = 0;
438                                         listen_mode = LISTEN_RAW;
439                                         
440                                 }
441                                 break;
442                         case BOUND:
443                                 /* Lease is starting to run out, time to enter renewing state */
444                                 state = RENEWING;
445                                 listen_mode = LISTEN_KERNEL;
446                                 DEBUG(LOG_INFO, "Entering renew state");
447                                 /* fall right through */
448                         case RENEWING:
449                                 /* Either set a new T1, or enter REBINDING state */
450                                 if ((t2 - t1) <= (lease / 14400 + 1)) {
451                                         /* timed out, enter rebinding state */
452                                         state = REBINDING;
453                                         timeout = time(0) + (t2 - t1);
454                                         DEBUG(LOG_INFO, "Entering rebinding state");
455                                 } else {
456                                         /* send a request packet */
457                                         send_renew(xid, server_addr, requested_ip); /* unicast */
458                                         
459                                         t1 = (t2 - t1) / 2 + t1;
460                                         timeout = t1 + start;
461                                 }
462                                 break;
463                         case REBINDING:
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");
470                                         timeout = time(0);
471                                         packet_num = 0;
472                                         listen_mode = LISTEN_RAW;
473                                 } else {
474                                         /* send a request packet */
475                                         send_renew(xid, 0, requested_ip); /* broadcast */
476
477                                         t2 = (lease - t2) / 2 + t2;
478                                         timeout = t2 + start;
479                                 }
480                                 break;
481                         case RELEASED:
482                                 /* yah, I know, *you* say it would never happen */
483                                 timeout = 0xffffffff;
484                                 break;
485                         }
486                 } else if (retval > 0 && listen_mode != LISTEN_NONE && FD_ISSET(fd, &rfds)) {
487                         /* a packet is ready, read it */
488                         
489                         if (listen_mode == LISTEN_KERNEL) {
490                                 if (get_packet(&packet, fd) < 0) continue;
491                         } else {
492                                 if (get_raw_packet(&packet, fd) < 0) continue;
493                         } 
494                         
495                         if (packet.xid != xid) {
496                                 DEBUG(LOG_INFO, "Ignoring XID %lx (our xid is %lx)",
497                                         (unsigned long) packet.xid, xid);
498                                 continue;
499                         }
500                         
501                         if ((message = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) {
502                                 DEBUG(LOG_ERR, "couldnt get option from packet -- ignoring");
503                                 continue;
504                         }
505                         
506                         switch (state) {
507                         case INIT_SELECTING:
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);
512                                                 xid = packet.xid;
513                                                 requested_ip = packet.yiaddr;
514                                                 
515                                                 /* enter requesting state */
516                                                 state = REQUESTING;
517                                                 timeout = time(0);
518                                                 packet_num = 0;
519                                         } else {
520                                                 DEBUG(LOG_ERR, "No server ID in message");
521                                         }
522                                 }
523                                 break;
524                         case RENEW_REQUESTED:
525                         case REQUESTING:
526                         case RENEWING:
527                         case REBINDING:
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");
531                                                 lease = 60*60;
532                                         } else {
533                                                 memcpy(&lease, temp, 4);
534                                                 lease = ntohl(lease);
535                                         }
536                                                 
537                                         /* enter bound state */
538                                         t1 = lease / 2;
539                                         
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);
545                                         start = time(0);
546                                         timeout = t1 + start;
547                                         requested_ip = packet.yiaddr;
548                                         run_script(&packet,
549                                                    ((state == RENEWING || state == REBINDING) ? "renew" : "bound"));
550
551                                         state = BOUND;
552                                         listen_mode = LISTEN_NONE;
553                                         
554                                         // brcm
555                                         setStatus(1);
556                                         background();
557                                         
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;
564                                         timeout = time(0);
565                                         requested_ip = 0;
566                                         packet_num = 0;
567                                         listen_mode = LISTEN_RAW;
568
569                                         // brcm
570                                         setStatus(0);
571                                 }
572                                 break;
573                         case BOUND:
574                         case RELEASED:
575                                 /* ignore all packets */
576                                 break;
577                         }                                       
578                 } else if (retval == -1 && errno == EINTR) {
579                         /* a signal was caught */
580                         
581                 } else {
582                         /* An error occured */
583                         DEBUG(LOG_ERR, "Error on select");
584                 }
585                 
586         }
587         return 0;
588 }
589