# BRCM_VERSION=3
[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 static int state;
50 static unsigned long requested_ip; /* = 0 */
51 static unsigned long server_addr;
52 static unsigned long timeout;
53 static int packet_num; /* = 0 */
54
55 // brcm
56 char session_path[64];
57 static char status_path[128]="";
58 static char pid_path[128]="";
59 char vendor_class_id[128]="";
60
61 #define LISTEN_NONE 0
62 #define LISTEN_KERNEL 1
63 #define LISTEN_RAW 2
64 static int listen_mode = LISTEN_RAW;
65
66 #define DEFAULT_SCRIPT  "/etc/dhcp/dhcp_getdata"
67
68 struct client_config_t client_config = {
69         /* Default options. */
70         abort_if_no_lease: 0,
71         foreground: 0,
72         quit_after_lease: 0,
73         interface: "eth0",
74         pidfile: NULL,
75         script: DEFAULT_SCRIPT,
76         clientid: NULL,
77         hostname: NULL,
78         ifindex: 0,
79         arp: "\0\0\0\0\0\0",            /* appease gcc-3.0 */
80 };
81
82 // brcm
83 void setStatus(int status) {
84     char cmd[128] = "";
85     
86     sprintf(cmd, "echo %d > %s", status, status_path);
87     system(cmd); 
88 }
89
90 void setPid() {
91     char cmd[128] = "";
92     
93     sprintf(cmd, "echo %d > %s", getpid(), pid_path);
94     system(cmd); 
95 }
96
97 static void print_usage(void)
98 {
99         printf(
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"
113         );
114 }
115
116
117 /* SIGUSR1 handler (renew) */
118 static void renew_requested(int sig)
119 {
120         sig = 0;
121         LOG(LOG_INFO, "Received SIGUSR1");
122         if (state == BOUND || state == RENEWING || state == REBINDING ||
123             state == RELEASED) {
124                 listen_mode = LISTEN_KERNEL;
125                 server_addr = 0;
126                 packet_num = 0;
127                 state = RENEW_REQUESTED;
128         }
129
130         if (state == RELEASED) {
131                 listen_mode = LISTEN_RAW;
132                 state = INIT_SELECTING;
133         }
134
135         /* Kill any timeouts because the user wants this to hurry along */
136         timeout = 0;
137 }
138
139
140 /* SIGUSR2 handler (release) */
141 static void release_requested(int sig)
142 {
143         sig = 0;
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");
149         }
150
151         listen_mode = 0;
152         state = RELEASED;
153         timeout = 0xffffffff;
154 }
155
156
157 /* Exit and cleanup */
158 static void exit_client(int retval)
159 {
160         pidfile_delete(client_config.pidfile);
161         CLOSE_LOG();
162         exit(retval);
163 }
164
165
166 /* SIGTERM handler */
167 static void terminate(int sig)
168 {
169         sig = 0;
170         LOG(LOG_INFO, "Received SIGTERM");
171         exit_client(0);
172 }
173
174
175 static void background(void)
176 {
177         int pid_fd;
178         if (client_config.quit_after_lease) {
179                 exit_client(0);
180         } else if (!client_config.foreground) {
181                 pid_fd = pidfile_acquire(client_config.pidfile); /* hold lock during fork. */
182                 switch(fork()) {
183                 case -1:
184                         perror("fork");
185                         exit_client(1);
186                         /*NOTREACHED*/
187                 case 0:
188                         // brcm
189                         setPid();
190                         break; /* child continues */
191                 default:
192                         exit(0); /* parent exits */
193                         /*NOTREACHED*/
194                 }
195                 close(0);
196                 close(1);
197                 close(2);
198                 setsid();
199                 client_config.foreground = 1; /* Do not fork again. */
200                 pidfile_write_release(pid_fd);
201         }
202 }
203
204
205
206 #ifdef COMBINED_BINARY
207 int udhcpc(int argc, char *argv[])
208 #else
209 int main(int argc, char *argv[])
210 #endif
211 {
212         char *temp, *message;
213         unsigned long t1 = 0, t2 = 0, xid = 0;
214         unsigned long start = 0, lease;
215         fd_set rfds;
216         int fd, retval;
217         struct timeval tv;
218         int c, len;
219         struct ifreq ifr;
220         struct dhcpMessage packet;
221         struct in_addr temp_addr;
222         int pid_fd;
223
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'},
236                 {0, 0, 0, 0}
237         };
238
239         /* get options */
240         while (1) {
241                 int option_index = 0;
242 // brcm
243                 c = getopt_long(argc, argv, "c:fH:hi:np:qr:s:d:v", options, &option_index);
244                 if (c == -1) break;
245                 
246                 switch (c) {
247                 case 'c':
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);
254                         break;
255                 case 'f':
256                         client_config.foreground = 1;
257                         break;
258                 case 'H':
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);
265                         break;
266                 case 'h':
267                         print_usage();
268                         return 0;
269                 case 'i':
270                         client_config.interface =  optarg;
271 // brcm
272                         strcpy(session_path, optarg);
273                         break;
274                 case 'n':
275                         client_config.abort_if_no_lease = 1;
276                         break;
277                 case 'p':
278                         client_config.pidfile = optarg;
279                         break;
280                 case 'q':
281                         client_config.quit_after_lease = 1;
282                         break;
283                 case 'r':
284                         requested_ip = inet_addr(optarg);
285                         break;
286 // brcm
287                 case 'd':
288                         strcpy(vendor_class_id, optarg);
289                         break;
290                 case 's':
291                         client_config.script = optarg;
292                         break;
293                 case 'v':
294                         printf("udhcpcd, version %s\n\n", VERSION);
295                         break;
296                 }
297         }
298
299         // brcm
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);
303         }
304
305         OPEN_LOG("udhcpc");
306         LOG(LOG_INFO, "udhcp client (v%s) started", VERSION);
307
308         pid_fd = pidfile_acquire(client_config.pidfile);
309         pidfile_write_release(pid_fd);
310
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;
316                 } else {
317                         LOG(LOG_ERR, "SIOCGIFINDEX failed! %s", strerror(errno));
318                         exit_client(1);
319                 }
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]);
325                 } else {
326                         LOG(LOG_ERR, "SIOCGIFHWADDR failed! %s", strerror(errno));
327                         exit_client(1);
328                 }
329         } else {
330                 LOG(LOG_ERR, "socket failed! %s", strerror(errno));
331                 exit_client(1);
332         }
333         close(fd);
334         fd = -1;
335
336         /* setup signal handlers */
337         signal(SIGUSR1, renew_requested);
338         signal(SIGUSR2, release_requested);
339         signal(SIGTERM, terminate);
340         
341         state = INIT_SELECTING;
342         // brcm
343         // run_script(NULL, "deconfig");
344
345         // brcm
346         setStatus(0);
347
348         for (;;) {
349                 if (fd > 0) {
350                         close(fd);
351                         fd = -1;
352                 }
353                 
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");
357                                 exit_client(0);
358                         }                       
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");
362                                 exit_client(0);
363                         }                       
364                 } else fd = -1;
365
366                 tv.tv_sec = timeout - time(0);
367                 tv.tv_usec = 0;
368                 FD_ZERO(&rfds);
369                 if (listen_mode) FD_SET(fd, &rfds);
370                 
371                 if (tv.tv_sec > 0) {
372                         retval = select(fd + 1, &rfds, NULL, NULL, &tv);
373                 } else retval = 0; /* If we already timed out, fall through */
374                 
375                 if (retval == 0) {
376                         /* timeout dropped to zero */
377                         switch (state) {
378                         case INIT_SELECTING:
379                                 // brcm
380                                 setStatus(0);
381                                 if (packet_num < 3) {
382                                         if (packet_num == 0)
383                                                 xid = random_xid();
384
385                                         /* send discover packet */
386                                         send_discover(xid, requested_ip); /* broadcast */
387                                         
388                                         timeout = time(0) + ((packet_num == 2) ? 10 : 2);
389                                         packet_num++;
390                                 } else {
391                                         if (client_config.abort_if_no_lease) {
392                                                 LOG(LOG_INFO,
393                                                     "No lease, failing.");
394                                                 exit_client(1);
395                                         }
396                                         /* wait to try again */
397                                         packet_num = 0;
398                                         timeout = time(0) + 60;
399                                 }
400                                 break;
401                         case RENEW_REQUESTED:
402                         case REQUESTING:
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 */
408                                         
409                                         timeout = time(0) + ((packet_num == 2) ? 10 : 2);
410                                         packet_num++;
411                                 } else {
412                                         /* timed out, go back to init state */
413                                         state = INIT_SELECTING;
414                                         timeout = time(0);
415                                         packet_num = 0;
416                                         listen_mode = LISTEN_RAW;
417                                         
418                                 }
419                                 break;
420                         case BOUND:
421                                 /* Lease is starting to run out, time to enter renewing state */
422                                 state = RENEWING;
423                                 listen_mode = LISTEN_KERNEL;
424                                 DEBUG(LOG_INFO, "Entering renew state");
425                                 /* fall right through */
426                         case RENEWING:
427                                 /* Either set a new T1, or enter REBINDING state */
428                                 if ((t2 - t1) <= (lease / 14400 + 1)) {
429                                         /* timed out, enter rebinding state */
430                                         state = REBINDING;
431                                         timeout = time(0) + (t2 - t1);
432                                         DEBUG(LOG_INFO, "Entering rebinding state");
433                                 } else {
434                                         /* send a request packet */
435                                         send_renew(xid, server_addr, requested_ip); /* unicast */
436                                         
437                                         t1 = (t2 - t1) / 2 + t1;
438                                         timeout = t1 + start;
439                                 }
440                                 break;
441                         case REBINDING:
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");
448                                         timeout = time(0);
449                                         packet_num = 0;
450                                         listen_mode = LISTEN_RAW;
451                                 } else {
452                                         /* send a request packet */
453                                         send_renew(xid, 0, requested_ip); /* broadcast */
454
455                                         t2 = (lease - t2) / 2 + t2;
456                                         timeout = t2 + start;
457                                 }
458                                 break;
459                         case RELEASED:
460                                 /* yah, I know, *you* say it would never happen */
461                                 timeout = 0xffffffff;
462                                 break;
463                         }
464                 } else if (retval > 0 && listen_mode != LISTEN_NONE && FD_ISSET(fd, &rfds)) {
465                         /* a packet is ready, read it */
466                         
467                         if (listen_mode == LISTEN_KERNEL) {
468                                 if (get_packet(&packet, fd) < 0) continue;
469                         } else {
470                                 if (get_raw_packet(&packet, fd) < 0) continue;
471                         } 
472                         
473                         if (packet.xid != xid) {
474                                 DEBUG(LOG_INFO, "Ignoring XID %lx (our xid is %lx)",
475                                         (unsigned long) packet.xid, xid);
476                                 continue;
477                         }
478                         
479                         if ((message = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) {
480                                 DEBUG(LOG_ERR, "couldnt get option from packet -- ignoring");
481                                 continue;
482                         }
483                         
484                         switch (state) {
485                         case INIT_SELECTING:
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);
490                                                 xid = packet.xid;
491                                                 requested_ip = packet.yiaddr;
492                                                 
493                                                 /* enter requesting state */
494                                                 state = REQUESTING;
495                                                 timeout = time(0);
496                                                 packet_num = 0;
497                                         } else {
498                                                 DEBUG(LOG_ERR, "No server ID in message");
499                                         }
500                                 }
501                                 break;
502                         case RENEW_REQUESTED:
503                         case REQUESTING:
504                         case RENEWING:
505                         case REBINDING:
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");
509                                                 lease = 60*60;
510                                         } else {
511                                                 memcpy(&lease, temp, 4);
512                                                 lease = ntohl(lease);
513                                         }
514                                                 
515                                         /* enter bound state */
516                                         t1 = lease / 2;
517                                         
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);
523                                         start = time(0);
524                                         timeout = t1 + start;
525                                         requested_ip = packet.yiaddr;
526                                         run_script(&packet,
527                                                    ((state == RENEWING || state == REBINDING) ? "renew" : "bound"));
528
529                                         state = BOUND;
530                                         listen_mode = LISTEN_NONE;
531                                         
532                                         // brcm
533                                         setStatus(1);
534                                         background();
535                                         
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;
542                                         timeout = time(0);
543                                         requested_ip = 0;
544                                         packet_num = 0;
545                                         listen_mode = LISTEN_RAW;
546
547                                         // brcm
548                                         setStatus(0);
549                                 }
550                                 break;
551                         case BOUND:
552                         case RELEASED:
553                                 /* ignore all packets */
554                                 break;
555                         }                                       
556                 } else if (retval == -1 && errno == EINTR) {
557                         /* a signal was caught */
558                         
559                 } else {
560                         /* An error occured */
561                         DEBUG(LOG_ERR, "Error on select");
562                 }
563                 
564         }
565         return 0;
566 }
567