8c795bc62002b86ceed984eb24b80e18f1b384ea
[platform/upstream/busybox.git] / networking / udhcp / dhcpc.c
1 /* dhcpc.c
2  *
3  * udhcp DHCP client
4  *
5  * Russ Dill <Russ.Dill@asu.edu> July 2001
6  *
7  * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
8  */
9
10 #include <sys/file.h>
11 #include <unistd.h>
12 #include <getopt.h>
13 #include <stdlib.h>
14 #include <sys/socket.h>
15 #include <netinet/in.h>
16 #include <arpa/inet.h>
17 #include <signal.h>
18 #include <time.h>
19 #include <string.h>
20 #include <sys/ioctl.h>
21 #include <net/if.h>
22 #include <errno.h>
23
24 #include "common.h"
25 #include "dhcpd.h"
26 #include "dhcpc.h"
27 #include "options.h"
28 #include "clientpacket.h"
29 #include "clientsocket.h"
30 #include "socket.h"
31 #include "signalpipe.h"
32
33 static int state;
34 static unsigned long requested_ip; /* = 0 */
35 static unsigned long server_addr;
36 static unsigned long timeout;
37 static int packet_num; /* = 0 */
38 static int fd = -1;
39
40 #define LISTEN_NONE 0
41 #define LISTEN_KERNEL 1
42 #define LISTEN_RAW 2
43 static int listen_mode;
44
45 struct client_config_t client_config = {
46         /* Default options. */
47         .abort_if_no_lease = 0,
48         .foreground = 0,
49         .quit_after_lease = 0,
50         .background_if_no_lease = 0,
51         .interface = "eth0",
52         .pidfile = NULL,
53         .script = DEFAULT_SCRIPT,
54         .clientid = NULL,
55         .vendorclass = NULL,
56         .hostname = NULL,
57         .fqdn = NULL,
58         .ifindex = 0,
59         .retries = 3,
60         .timeout = 3,
61         .arp = "\0\0\0\0\0\0",          /* appease gcc-3.0 */
62 };
63
64 /* just a little helper */
65 static void change_mode(int new_mode)
66 {
67         DEBUG(LOG_INFO, "entering %s listen mode",
68                 new_mode ? (new_mode == 1 ? "kernel" : "raw") : "none");
69         if (fd >= 0) close(fd);
70         fd = -1;
71         listen_mode = new_mode;
72 }
73
74
75 /* perform a renew */
76 static void perform_renew(void)
77 {
78         LOG(LOG_INFO, "Performing a DHCP renew");
79         switch (state) {
80         case BOUND:
81                 change_mode(LISTEN_KERNEL);
82         case RENEWING:
83         case REBINDING:
84                 state = RENEW_REQUESTED;
85                 break;
86         case RENEW_REQUESTED: /* impatient are we? fine, square 1 */
87                 udhcp_run_script(NULL, "deconfig");
88         case REQUESTING:
89         case RELEASED:
90                 change_mode(LISTEN_RAW);
91                 state = INIT_SELECTING;
92                 break;
93         case INIT_SELECTING:
94                 break;
95         }
96
97         /* start things over */
98         packet_num = 0;
99
100         /* Kill any timeouts because the user wants this to hurry along */
101         timeout = 0;
102 }
103
104
105 /* perform a release */
106 static void perform_release(void)
107 {
108         char buffer[16];
109         struct in_addr temp_addr;
110
111         /* send release packet */
112         if (state == BOUND || state == RENEWING || state == REBINDING) {
113                 temp_addr.s_addr = server_addr;
114                 sprintf(buffer, "%s", inet_ntoa(temp_addr));
115                 temp_addr.s_addr = requested_ip;
116                 LOG(LOG_INFO, "Unicasting a release of %s to %s",
117                                 inet_ntoa(temp_addr), buffer);
118                 send_release(server_addr, requested_ip); /* unicast */
119                 udhcp_run_script(NULL, "deconfig");
120         }
121         LOG(LOG_INFO, "Entering released state");
122
123         change_mode(LISTEN_NONE);
124         state = RELEASED;
125         timeout = 0x7fffffff;
126 }
127
128
129 static void client_background(void)
130 {
131         udhcp_background(client_config.pidfile);
132         client_config.foreground = 1; /* Do not fork again. */
133         client_config.background_if_no_lease = 0;
134 }
135
136
137 int udhcpc_main(int argc, char *argv[])
138 {
139         uint8_t *temp, *message;
140         unsigned long t1 = 0, t2 = 0, xid = 0;
141         unsigned long start = 0, lease;
142         fd_set rfds;
143         int retval;
144         struct timeval tv;
145         int c, len;
146         struct dhcpMessage packet;
147         struct in_addr temp_addr;
148         long now;
149         int max_fd;
150         int sig;
151         int no_clientid = 0;
152
153         static const struct option arg_options[] = {
154                 {"clientid",    required_argument,      0, 'c'},
155                 {"clientid-none", no_argument,          0, 'C'},
156                 {"vendorclass", required_argument,      0, 'V'},
157                 {"foreground",  no_argument,            0, 'f'},
158                 {"background",  no_argument,            0, 'b'},
159                 {"hostname",    required_argument,      0, 'H'},
160                 {"hostname",    required_argument,      0, 'h'},
161                 {"fqdn",        required_argument,      0, 'F'},
162                 {"interface",   required_argument,      0, 'i'},
163                 {"now",         no_argument,            0, 'n'},
164                 {"pidfile",     required_argument,      0, 'p'},
165                 {"quit",        no_argument,            0, 'q'},
166                 {"request",     required_argument,      0, 'r'},
167                 {"script",      required_argument,      0, 's'},
168                 {"timeout",     required_argument,      0, 'T'},
169                 {"version",     no_argument,            0, 'v'},
170                 {"retries",     required_argument,      0, 't'},
171                 {0, 0, 0, 0}
172         };
173
174         /* get options */
175         while (1) {
176                 int option_index = 0;
177                 c = getopt_long(argc, argv, "c:CV:fbH:h:F:i:np:qr:s:T:t:v", arg_options, &option_index);
178                 if (c == -1) break;
179
180                 switch (c) {
181                 case 'c':
182                         if (no_clientid) bb_show_usage();
183                         len = strlen(optarg) > 255 ? 255 : strlen(optarg);
184                         free(client_config.clientid);
185                         client_config.clientid = xmalloc(len + 2);
186                         client_config.clientid[OPT_CODE] = DHCP_CLIENT_ID;
187                         client_config.clientid[OPT_LEN] = len;
188                         client_config.clientid[OPT_DATA] = '\0';
189                         strncpy((char*)client_config.clientid + OPT_DATA, optarg, len);
190                         break;
191                 case 'C':
192                         if (client_config.clientid) bb_show_usage();
193                         no_clientid = 1;
194                         break;
195                 case 'V':
196                         len = strlen(optarg) > 255 ? 255 : strlen(optarg);
197                         free(client_config.vendorclass);
198                         client_config.vendorclass = xmalloc(len + 2);
199                         client_config.vendorclass[OPT_CODE] = DHCP_VENDOR;
200                         client_config.vendorclass[OPT_LEN] = len;
201                         strncpy((char*)client_config.vendorclass + OPT_DATA, optarg, len);
202                         break;
203                 case 'f':
204                         client_config.foreground = 1;
205                         break;
206                 case 'b':
207                         client_config.background_if_no_lease = 1;
208                         break;
209                 case 'h':
210                 case 'H':
211                         len = strlen(optarg) > 255 ? 255 : strlen(optarg);
212                         free(client_config.hostname);
213                         client_config.hostname = xmalloc(len + 2);
214                         client_config.hostname[OPT_CODE] = DHCP_HOST_NAME;
215                         client_config.hostname[OPT_LEN] = len;
216                         strncpy((char*)client_config.hostname + 2, optarg, len);
217                         break;
218                 case 'F':
219                         len = strlen(optarg) > 255 ? 255 : strlen(optarg);
220                         free(client_config.fqdn);
221                         client_config.fqdn = xmalloc(len + 5);
222                         client_config.fqdn[OPT_CODE] = DHCP_FQDN;
223                         client_config.fqdn[OPT_LEN] = len + 3;
224                         /* Flags: 0000NEOS
225                         S: 1 => Client requests Server to update A RR in DNS as well as PTR
226                         O: 1 => Server indicates to client that DNS has been updated regardless
227                         E: 1 => Name data is DNS format, i.e. <4>host<6>domain<4>com<0> not "host.domain.com"
228                         N: 1 => Client requests Server to not update DNS
229                         */
230                         client_config.fqdn[OPT_LEN + 1] = 0x1;
231                         client_config.fqdn[OPT_LEN + 2] = 0;
232                         client_config.fqdn[OPT_LEN + 3] = 0;
233                         strncpy((char*)client_config.fqdn + 5, optarg, len);
234                         break;
235                 case 'i':
236                         client_config.interface =  optarg;
237                         break;
238                 case 'n':
239                         client_config.abort_if_no_lease = 1;
240                         break;
241                 case 'p':
242                         client_config.pidfile = optarg;
243                         break;
244                 case 'q':
245                         client_config.quit_after_lease = 1;
246                         break;
247                 case 'r':
248                         requested_ip = inet_addr(optarg);
249                         break;
250                 case 's':
251                         client_config.script = optarg;
252                         break;
253                 case 'T':
254                         client_config.timeout = atoi(optarg);
255                         break;
256                 case 't':
257                         client_config.retries = atoi(optarg);
258                         break;
259                 case 'v':
260                         printf("version %s\n\n", BB_VER);
261                         return 0;
262                         break;
263                 default:
264                         bb_show_usage();
265                 }
266         }
267
268         /* Start the log, sanitize fd's, and write a pid file */
269         udhcp_start_log_and_pid("udhcpc", client_config.pidfile);
270
271         if (read_interface(client_config.interface, &client_config.ifindex,
272                            NULL, client_config.arp) < 0)
273                 return 1;
274
275         /* if not set, and not suppressed, setup the default client ID */
276         if (!client_config.clientid && !no_clientid) {
277                 client_config.clientid = xmalloc(6 + 3);
278                 client_config.clientid[OPT_CODE] = DHCP_CLIENT_ID;
279                 client_config.clientid[OPT_LEN] = 7;
280                 client_config.clientid[OPT_DATA] = 1;
281                 memcpy(client_config.clientid + 3, client_config.arp, 6);
282         }
283
284         if (!client_config.vendorclass) {
285                 client_config.vendorclass = xmalloc(sizeof("udhcp "BB_VER) + 2);
286                 client_config.vendorclass[OPT_CODE] = DHCP_VENDOR;
287                 client_config.vendorclass[OPT_LEN] = sizeof("udhcp "BB_VER) - 1;
288                 client_config.vendorclass[OPT_DATA] = 1;
289                 memcpy(&client_config.vendorclass[OPT_DATA],
290                         "udhcp "BB_VER, sizeof("udhcp "BB_VER) - 1);
291         }
292
293
294         /* setup the signal pipe */
295         udhcp_sp_setup();
296
297         state = INIT_SELECTING;
298         udhcp_run_script(NULL, "deconfig");
299         change_mode(LISTEN_RAW);
300
301         for (;;) {
302
303                 tv.tv_sec = timeout - uptime();
304                 tv.tv_usec = 0;
305
306                 if (listen_mode != LISTEN_NONE && fd < 0) {
307                         if (listen_mode == LISTEN_KERNEL)
308                                 fd = listen_socket(INADDR_ANY, CLIENT_PORT, client_config.interface);
309                         else
310                                 fd = raw_socket(client_config.ifindex);
311                         if (fd < 0) {
312                                 LOG(LOG_ERR, "FATAL: couldn't listen on socket, %m");
313                                 return 0;
314                         }
315                 }
316                 max_fd = udhcp_sp_fd_set(&rfds, fd);
317
318                 if (tv.tv_sec > 0) {
319                         DEBUG(LOG_INFO, "Waiting on select...");
320                         retval = select(max_fd + 1, &rfds, NULL, NULL, &tv);
321                 } else retval = 0; /* If we already timed out, fall through */
322
323                 now = uptime();
324                 if (retval == 0) {
325                         /* timeout dropped to zero */
326                         switch (state) {
327                         case INIT_SELECTING:
328                                 if (packet_num < client_config.retries) {
329                                         if (packet_num == 0)
330                                                 xid = random_xid();
331
332                                         /* send discover packet */
333                                         send_discover(xid, requested_ip); /* broadcast */
334
335                                         timeout = now + client_config.timeout;
336                                         packet_num++;
337                                 } else {
338                                         udhcp_run_script(NULL, "leasefail");
339                                         if (client_config.background_if_no_lease) {
340                                                 LOG(LOG_INFO, "No lease, forking to background.");
341                                                 client_background();
342                                         } else if (client_config.abort_if_no_lease) {
343                                                 LOG(LOG_INFO, "No lease, failing.");
344                                                 return 1;
345                                         }
346                                         /* wait to try again */
347                                         packet_num = 0;
348                                         timeout = now + 60;
349                                 }
350                                 break;
351                         case RENEW_REQUESTED:
352                         case REQUESTING:
353                                 if (packet_num < client_config.retries) {
354                                         /* send request packet */
355                                         if (state == RENEW_REQUESTED)
356                                                 send_renew(xid, server_addr, requested_ip); /* unicast */
357                                         else send_selecting(xid, server_addr, requested_ip); /* broadcast */
358
359                                         timeout = now + ((packet_num == 2) ? 10 : 2);
360                                         packet_num++;
361                                 } else {
362                                         /* timed out, go back to init state */
363                                         if (state == RENEW_REQUESTED) udhcp_run_script(NULL, "deconfig");
364                                         state = INIT_SELECTING;
365                                         timeout = now;
366                                         packet_num = 0;
367                                         change_mode(LISTEN_RAW);
368                                 }
369                                 break;
370                         case BOUND:
371                                 /* Lease is starting to run out, time to enter renewing state */
372                                 state = RENEWING;
373                                 change_mode(LISTEN_KERNEL);
374                                 DEBUG(LOG_INFO, "Entering renew state");
375                                 /* fall right through */
376                         case RENEWING:
377                                 /* Either set a new T1, or enter REBINDING state */
378                                 if ((t2 - t1) <= (lease / 14400 + 1)) {
379                                         /* timed out, enter rebinding state */
380                                         state = REBINDING;
381                                         timeout = now + (t2 - t1);
382                                         DEBUG(LOG_INFO, "Entering rebinding state");
383                                 } else {
384                                         /* send a request packet */
385                                         send_renew(xid, server_addr, requested_ip); /* unicast */
386
387                                         t1 = (t2 - t1) / 2 + t1;
388                                         timeout = t1 + start;
389                                 }
390                                 break;
391                         case REBINDING:
392                                 /* Either set a new T2, or enter INIT state */
393                                 if ((lease - t2) <= (lease / 14400 + 1)) {
394                                         /* timed out, enter init state */
395                                         state = INIT_SELECTING;
396                                         LOG(LOG_INFO, "Lease lost, entering init state");
397                                         udhcp_run_script(NULL, "deconfig");
398                                         timeout = now;
399                                         packet_num = 0;
400                                         change_mode(LISTEN_RAW);
401                                 } else {
402                                         /* send a request packet */
403                                         send_renew(xid, 0, requested_ip); /* broadcast */
404
405                                         t2 = (lease - t2) / 2 + t2;
406                                         timeout = t2 + start;
407                                 }
408                                 break;
409                         case RELEASED:
410                                 /* yah, I know, *you* say it would never happen */
411                                 timeout = 0x7fffffff;
412                                 break;
413                         }
414                 } else if (retval > 0 && listen_mode != LISTEN_NONE && FD_ISSET(fd, &rfds)) {
415                         /* a packet is ready, read it */
416
417                         if (listen_mode == LISTEN_KERNEL)
418                                 len = udhcp_get_packet(&packet, fd);
419                         else len = get_raw_packet(&packet, fd);
420
421                         if (len == -1 && errno != EINTR) {
422                                 DEBUG(LOG_INFO, "error on read, %m, reopening socket");
423                                 change_mode(listen_mode); /* just close and reopen */
424                         }
425                         if (len < 0) continue;
426
427                         if (packet.xid != xid) {
428                                 DEBUG(LOG_INFO, "Ignoring XID %lx (our xid is %lx)",
429                                         (unsigned long) packet.xid, xid);
430                                 continue;
431                         }
432
433                         /* Ignore packets that aren't for us */
434                         if (memcmp(packet.chaddr, client_config.arp, 6)) {
435                                 DEBUG(LOG_INFO, "packet does not have our chaddr -- ignoring");
436                                 continue;
437                         }
438
439                         if ((message = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) {
440                                 DEBUG(LOG_ERR, "couldnt get option from packet -- ignoring");
441                                 continue;
442                         }
443
444                         switch (state) {
445                         case INIT_SELECTING:
446                                 /* Must be a DHCPOFFER to one of our xid's */
447                                 if (*message == DHCPOFFER) {
448                                         if ((temp = get_option(&packet, DHCP_SERVER_ID))) {
449                                                 memcpy(&server_addr, temp, 4);
450                                                 xid = packet.xid;
451                                                 requested_ip = packet.yiaddr;
452
453                                                 /* enter requesting state */
454                                                 state = REQUESTING;
455                                                 timeout = now;
456                                                 packet_num = 0;
457                                         } else {
458                                                 DEBUG(LOG_ERR, "No server ID in message");
459                                         }
460                                 }
461                                 break;
462                         case RENEW_REQUESTED:
463                         case REQUESTING:
464                         case RENEWING:
465                         case REBINDING:
466                                 if (*message == DHCPACK) {
467                                         if (!(temp = get_option(&packet, DHCP_LEASE_TIME))) {
468                                                 LOG(LOG_ERR, "No lease time with ACK, using 1 hour lease");
469                                                 lease = 60 * 60;
470                                         } else {
471                                                 memcpy(&lease, temp, 4);
472                                                 lease = ntohl(lease);
473                                         }
474
475                                         /* enter bound state */
476                                         t1 = lease / 2;
477
478                                         /* little fixed point for n * .875 */
479                                         t2 = (lease * 0x7) >> 3;
480                                         temp_addr.s_addr = packet.yiaddr;
481                                         LOG(LOG_INFO, "Lease of %s obtained, lease time %ld",
482                                                 inet_ntoa(temp_addr), lease);
483                                         start = now;
484                                         timeout = t1 + start;
485                                         requested_ip = packet.yiaddr;
486                                         udhcp_run_script(&packet,
487                                                    ((state == RENEWING || state == REBINDING) ? "renew" : "bound"));
488
489                                         state = BOUND;
490                                         change_mode(LISTEN_NONE);
491                                         if (client_config.quit_after_lease)
492                                                 return 0;
493                                         if (!client_config.foreground)
494                                                 client_background();
495
496                                 } else if (*message == DHCPNAK) {
497                                         /* return to init state */
498                                         LOG(LOG_INFO, "Received DHCP NAK");
499                                         udhcp_run_script(&packet, "nak");
500                                         if (state != REQUESTING)
501                                                 udhcp_run_script(NULL, "deconfig");
502                                         state = INIT_SELECTING;
503                                         timeout = now;
504                                         requested_ip = 0;
505                                         packet_num = 0;
506                                         change_mode(LISTEN_RAW);
507                                         sleep(3); /* avoid excessive network traffic */
508                                 }
509                                 break;
510                         /* case BOUND, RELEASED: - ignore all packets */
511                         }
512                 } else if (retval > 0 && (sig = udhcp_sp_read(&rfds))) {
513                         switch (sig) {
514                         case SIGUSR1:
515                                 perform_renew();
516                                 break;
517                         case SIGUSR2:
518                                 perform_release();
519                                 break;
520                         case SIGTERM:
521                                 LOG(LOG_INFO, "Received SIGTERM");
522                                 return 0;
523                         }
524                 } else if (retval == -1 && errno == EINTR) {
525                         /* a signal was caught */
526                 } else {
527                         /* An error occured */
528                         DEBUG(LOG_ERR, "Error on select");
529                 }
530
531         }
532         return 0;
533 }