summaryrefslogtreecommitdiff
path: root/networking/udhcp/dhcpc.c
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2007-11-22 01:00:00 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2007-11-22 01:00:00 +0000
commit9cdfd14223e58a73b2005ca8b432af439c8fe197 (patch)
tree68326e357c4b7ce59040c0a1e0ddafa6fcb1ee2c /networking/udhcp/dhcpc.c
parent223bc97f61f2fd5efbccede0837a374c1669e864 (diff)
downloadbusybox-w32-9cdfd14223e58a73b2005ca8b432af439c8fe197.tar.gz
busybox-w32-9cdfd14223e58a73b2005ca8b432af439c8fe197.tar.bz2
busybox-w32-9cdfd14223e58a73b2005ca8b432af439c8fe197.zip
dhcpc: cleanup and comments; fix buggy timeout handling in corner cases.
-25 bytes.
Diffstat (limited to 'networking/udhcp/dhcpc.c')
-rw-r--r--networking/udhcp/dhcpc.c232
1 files changed, 125 insertions, 107 deletions
diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c
index b3b89459e..234ecfd13 100644
--- a/networking/udhcp/dhcpc.c
+++ b/networking/udhcp/dhcpc.c
@@ -24,7 +24,7 @@
24 * in the code. Manpage says that struct in_addr has a member of type long (!) 24 * in the code. Manpage says that struct in_addr has a member of type long (!)
25 * which holds IPv4 address, and the struct is passed by value (!!) 25 * which holds IPv4 address, and the struct is passed by value (!!)
26 */ 26 */
27static unsigned timeout; 27static int timeout; /* = 0. Must be signed */
28static uint32_t requested_ip; /* = 0 */ 28static uint32_t requested_ip; /* = 0 */
29static uint32_t server_addr; 29static uint32_t server_addr;
30static int packet_num; /* = 0 */ 30static int packet_num; /* = 0 */
@@ -41,7 +41,7 @@ static smallint state;
41 41
42 42
43/* just a little helper */ 43/* just a little helper */
44static void change_mode(int new_mode) 44static void change_listen_mode(int new_mode)
45{ 45{
46 DEBUG("entering %s listen mode", 46 DEBUG("entering %s listen mode",
47 new_mode ? (new_mode == 1 ? "kernel" : "raw") : "none"); 47 new_mode ? (new_mode == 1 ? "kernel" : "raw") : "none");
@@ -59,7 +59,7 @@ static void perform_renew(void)
59 bb_info_msg("Performing a DHCP renew"); 59 bb_info_msg("Performing a DHCP renew");
60 switch (state) { 60 switch (state) {
61 case BOUND: 61 case BOUND:
62 change_mode(LISTEN_KERNEL); 62 change_listen_mode(LISTEN_KERNEL);
63 case RENEWING: 63 case RENEWING:
64 case REBINDING: 64 case REBINDING:
65 state = RENEW_REQUESTED; 65 state = RENEW_REQUESTED;
@@ -68,7 +68,7 @@ static void perform_renew(void)
68 udhcp_run_script(NULL, "deconfig"); 68 udhcp_run_script(NULL, "deconfig");
69 case REQUESTING: 69 case REQUESTING:
70 case RELEASED: 70 case RELEASED:
71 change_mode(LISTEN_RAW); 71 change_listen_mode(LISTEN_RAW);
72 state = INIT_SELECTING; 72 state = INIT_SELECTING;
73 break; 73 break;
74 case INIT_SELECTING: 74 case INIT_SELECTING:
@@ -101,7 +101,7 @@ static void perform_release(void)
101 } 101 }
102 bb_info_msg("Entering released state"); 102 bb_info_msg("Entering released state");
103 103
104 change_mode(LISTEN_NONE); 104 change_listen_mode(LISTEN_NONE);
105 state = RELEASED; 105 state = RELEASED;
106 timeout = INT_MAX; 106 timeout = INT_MAX;
107} 107}
@@ -153,10 +153,9 @@ int udhcpc_main(int argc, char **argv)
153 char *str_W; 153 char *str_W;
154#endif 154#endif
155 uint32_t xid = 0; 155 uint32_t xid = 0;
156 uint32_t lease = 0; /* can be given as 32-bit quantity */ 156 uint32_t lease_seconds = 0; /* can be given as 32-bit quantity */
157 unsigned t1 = 0, t2 = 0; /* what a wonderful names */ 157 unsigned t1 = 0, t2 = 0; /* what a wonderful names */
158 unsigned start = 0; 158 unsigned timestamp_got_lease = 0; /* for gcc */
159 unsigned now;
160 unsigned opt; 159 unsigned opt;
161 int max_fd; 160 int max_fd;
162 int retval; 161 int retval;
@@ -324,13 +323,14 @@ int udhcpc_main(int argc, char **argv)
324 323
325 state = INIT_SELECTING; 324 state = INIT_SELECTING;
326 udhcp_run_script(NULL, "deconfig"); 325 udhcp_run_script(NULL, "deconfig");
327 change_mode(LISTEN_RAW); 326 change_listen_mode(LISTEN_RAW);
328 tv.tv_sec = 0;
329 goto jump_in;
330 327
328 /* Main event loop. select() waits on signal pipe and possibly
329 * on sockfd.
330 * "continue" statements in code below jump to the top of the loop.
331 */
331 for (;;) { 332 for (;;) {
332 tv.tv_sec = timeout - monotonic_sec(); 333 tv.tv_sec = timeout;
333 jump_in:
334 tv.tv_usec = 0; 334 tv.tv_usec = 0;
335 335
336 if (listen_mode != LISTEN_NONE && sockfd < 0) { 336 if (listen_mode != LISTEN_NONE && sockfd < 0) {
@@ -347,15 +347,19 @@ int udhcpc_main(int argc, char **argv)
347 retval = select(max_fd + 1, &rfds, NULL, NULL, &tv); 347 retval = select(max_fd + 1, &rfds, NULL, NULL, &tv);
348 } 348 }
349 349
350 now = monotonic_sec();
351 if (retval < 0) { 350 if (retval < 0) {
352 /* EINTR? signal was caught, don't panic */ 351 /* EINTR? signal was caught, don't panic */
353 if (errno != EINTR) { 352 if (errno != EINTR) {
354 /* Else: an error occured, panic! */ 353 /* Else: an error occured, panic! */
355 bb_perror_msg_and_die("select"); 354 bb_perror_msg_and_die("select");
356 } 355 }
357 } else if (retval == 0) { 356 continue;
358 /* timeout dropped to zero */ 357 }
358
359 /* If timeout dropped to zero, time to become active:
360 * resend discover/renew/whatever
361 */
362 if (retval == 0) {
359 switch (state) { 363 switch (state) {
360 case INIT_SELECTING: 364 case INIT_SELECTING:
361 if (packet_num < discover_retries) { 365 if (packet_num < discover_retries) {
@@ -365,23 +369,23 @@ int udhcpc_main(int argc, char **argv)
365 /* send discover packet */ 369 /* send discover packet */
366 send_discover(xid, requested_ip); /* broadcast */ 370 send_discover(xid, requested_ip); /* broadcast */
367 371
368 timeout = now + discover_timeout; 372 timeout = discover_timeout;
369 packet_num++; 373 packet_num++;
370 } else { 374 continue;
371 udhcp_run_script(NULL, "leasefail");
372 if (client_config.background_if_no_lease) {
373 bb_info_msg("No lease, forking to background");
374 client_background();
375 } else if (client_config.abort_if_no_lease) {
376 bb_info_msg("No lease, failing");
377 retval = 1;
378 goto ret;
379 }
380 /* wait to try again */
381 packet_num = 0;
382 timeout = now + tryagain_timeout;
383 } 375 }
384 break; 376 udhcp_run_script(NULL, "leasefail");
377 if (client_config.background_if_no_lease) {
378 bb_info_msg("No lease, forking to background");
379 client_background();
380 } else if (client_config.abort_if_no_lease) {
381 bb_info_msg("No lease, failing");
382 retval = 1;
383 goto ret;
384 }
385 /* wait to try again */
386 timeout = tryagain_timeout;
387 packet_num = 0;
388 continue;
385 case RENEW_REQUESTED: 389 case RENEW_REQUESTED:
386 case REQUESTING: 390 case REQUESTING:
387 if (packet_num < discover_retries) { 391 if (packet_num < discover_retries) {
@@ -390,62 +394,67 @@ int udhcpc_main(int argc, char **argv)
390 send_renew(xid, server_addr, requested_ip); /* unicast */ 394 send_renew(xid, server_addr, requested_ip); /* unicast */
391 else send_selecting(xid, server_addr, requested_ip); /* broadcast */ 395 else send_selecting(xid, server_addr, requested_ip); /* broadcast */
392 396
393 timeout = now + ((packet_num == 2) ? 10 : 2); 397 timeout = ((packet_num == 2) ? 10 : 2);
394 packet_num++; 398 packet_num++;
395 } else { 399 continue;
396 /* timed out, go back to init state */
397 if (state == RENEW_REQUESTED)
398 udhcp_run_script(NULL, "deconfig");
399 state = INIT_SELECTING;
400 timeout = now;
401 packet_num = 0;
402 change_mode(LISTEN_RAW);
403 } 400 }
404 break; 401 /* timed out, go back to init state */
402 if (state == RENEW_REQUESTED)
403 udhcp_run_script(NULL, "deconfig");
404 change_listen_mode(LISTEN_RAW);
405 state = INIT_SELECTING;
406 timeout = 0;
407 packet_num = 0;
408 continue;
405 case BOUND: 409 case BOUND:
406 /* Lease is starting to run out, time to enter renewing state */ 410 /* Lease is starting to run out, time to enter renewing state */
407 state = RENEWING; 411 change_listen_mode(LISTEN_KERNEL);
408 change_mode(LISTEN_KERNEL);
409 DEBUG("Entering renew state"); 412 DEBUG("Entering renew state");
413 state = RENEWING;
410 /* fall right through */ 414 /* fall right through */
411 case RENEWING: 415 case RENEWING:
412 /* Either set a new T1, or enter REBINDING state */ 416 /* Either set a new T1, or enter REBINDING state */
413 if ((t2 - t1) <= (lease / 14400 + 1)) { 417 if ((t2 - t1) > (lease_seconds / (4*60*60) + 1)) {
414 /* timed out, enter rebinding state */
415 state = REBINDING;
416 timeout = now + (t2 - t1);
417 DEBUG("Entering rebinding state");
418 } else {
419 /* send a request packet */ 418 /* send a request packet */
420 send_renew(xid, server_addr, requested_ip); /* unicast */ 419 send_renew(xid, server_addr, requested_ip); /* unicast */
421 t1 = (t2 - t1) / 2 + t1; 420 t1 += (t2 - t1) / 2;
422 timeout = start + t1; 421 timeout = t1 - ((int)monotonic_sec() - timestamp_got_lease);
422 continue;
423 } 423 }
424 break; 424 /* Timed out, enter rebinding state */
425 DEBUG("Entering rebinding state");
426 state = REBINDING;
427 timeout = (t2 - t1);
428 continue;
425 case REBINDING: 429 case REBINDING:
426 /* Either set a new T2, or enter INIT state */ 430 /* Lease is *really* about to run out,
427 if ((lease - t2) <= (lease / 14400 + 1)) { 431 * try to find DHCP server using broadcast */
428 /* timed out, enter init state */ 432 if ((lease_seconds - t2) > (lease_seconds / (4*60*60) + 1)) {
429 state = INIT_SELECTING;
430 bb_info_msg("Lease lost, entering init state");
431 udhcp_run_script(NULL, "deconfig");
432 timeout = now;
433 packet_num = 0;
434 change_mode(LISTEN_RAW);
435 } else {
436 /* send a request packet */ 433 /* send a request packet */
437 send_renew(xid, 0, requested_ip); /* broadcast */ 434 send_renew(xid, 0, requested_ip); /* broadcast */
438 t2 = (lease - t2) / 2 + t2; 435 t2 += (lease_seconds - t2) / 2;
439 timeout = start + t2; 436 timeout = t2 - ((int)monotonic_sec() - timestamp_got_lease);
437 continue;
440 } 438 }
441 break; 439 /* Timed out, enter init state */
442 case RELEASED: 440 bb_info_msg("Lease lost, entering init state");
443 /* yah, I know, *you* say it would never happen */ 441 udhcp_run_script(NULL, "deconfig");
444 timeout = INT_MAX; 442 change_listen_mode(LISTEN_RAW);
445 break; 443 state = INIT_SELECTING;
444 timeout = 0;
445 packet_num = 0;
446 continue;
447 /* case RELEASED: */
446 } 448 }
447 } else if (listen_mode != LISTEN_NONE && FD_ISSET(sockfd, &rfds)) { 449 /* yah, I know, *you* say it would never happen */
448 /* a packet is ready, read it */ 450 timeout = INT_MAX;
451 continue; /* back to main loop */
452 }
453
454 /* select() didn't timeout, something did happen. */
455 /* Is is a packet? */
456 if (listen_mode != LISTEN_NONE && FD_ISSET(sockfd, &rfds)) {
457 /* A packet is ready, read it */
449 458
450 if (listen_mode == LISTEN_KERNEL) 459 if (listen_mode == LISTEN_KERNEL)
451 len = udhcp_get_packet(&packet, sockfd); 460 len = udhcp_get_packet(&packet, sockfd);
@@ -453,7 +462,7 @@ int udhcpc_main(int argc, char **argv)
453 462
454 if (len == -1 && errno != EINTR) { 463 if (len == -1 && errno != EINTR) {
455 DEBUG("error on read, %s, reopening socket", strerror(errno)); 464 DEBUG("error on read, %s, reopening socket", strerror(errno));
456 change_mode(listen_mode); /* just close and reopen */ 465 change_listen_mode(listen_mode); /* just close and reopen */
457 } 466 }
458 if (len < 0) continue; 467 if (len < 0) continue;
459 468
@@ -471,7 +480,7 @@ int udhcpc_main(int argc, char **argv)
471 480
472 message = get_option(&packet, DHCP_MESSAGE_TYPE); 481 message = get_option(&packet, DHCP_MESSAGE_TYPE);
473 if (message == NULL) { 482 if (message == NULL) {
474 bb_error_msg("cannot get option from packet - ignoring"); 483 bb_error_msg("cannot get message type from packet - ignoring");
475 continue; 484 continue;
476 } 485 }
477 486
@@ -479,22 +488,24 @@ int udhcpc_main(int argc, char **argv)
479 case INIT_SELECTING: 488 case INIT_SELECTING:
480 /* Must be a DHCPOFFER to one of our xid's */ 489 /* Must be a DHCPOFFER to one of our xid's */
481 if (*message == DHCPOFFER) { 490 if (*message == DHCPOFFER) {
491 /* TODO: why we don't just fetch server's IP from IP header? */
482 temp = get_option(&packet, DHCP_SERVER_ID); 492 temp = get_option(&packet, DHCP_SERVER_ID);
483 if (temp) { 493 if (!temp) {
484 /* can be misaligned, thus memcpy */
485 memcpy(&server_addr, temp, 4);
486 xid = packet.xid;
487 requested_ip = packet.yiaddr;
488
489 /* enter requesting state */
490 state = REQUESTING;
491 timeout = now;
492 packet_num = 0;
493 } else {
494 bb_error_msg("no server ID in message"); 494 bb_error_msg("no server ID in message");
495 continue;
496 /* still selecting - this server looks bad */
495 } 497 }
498 /* can be misaligned, thus memcpy */
499 memcpy(&server_addr, temp, 4);
500 xid = packet.xid;
501 requested_ip = packet.yiaddr;
502
503 /* enter requesting state */
504 state = REQUESTING;
505 timeout = 0;
506 packet_num = 0;
496 } 507 }
497 break; 508 continue;
498 case RENEW_REQUESTED: 509 case RENEW_REQUESTED:
499 case REQUESTING: 510 case REQUESTING:
500 case RENEWING: 511 case RENEWING:
@@ -503,13 +514,12 @@ int udhcpc_main(int argc, char **argv)
503 temp = get_option(&packet, DHCP_LEASE_TIME); 514 temp = get_option(&packet, DHCP_LEASE_TIME);
504 if (!temp) { 515 if (!temp) {
505 bb_error_msg("no lease time with ACK, using 1 hour lease"); 516 bb_error_msg("no lease time with ACK, using 1 hour lease");
506 lease = 60 * 60; 517 lease_seconds = 60 * 60;
507 } else { 518 } else {
508 /* can be misaligned, thus memcpy */ 519 /* can be misaligned, thus memcpy */
509 memcpy(&lease, temp, 4); 520 memcpy(&lease_seconds, temp, 4);
510 lease = ntohl(lease); 521 lease_seconds = ntohl(lease_seconds);
511 } 522 }
512
513#if ENABLE_FEATURE_UDHCPC_ARPING 523#if ENABLE_FEATURE_UDHCPC_ARPING
514 if (opt & OPT_a) { 524 if (opt & OPT_a) {
515 if (!arpping(packet.yiaddr, 525 if (!arpping(packet.yiaddr,
@@ -517,37 +527,37 @@ int udhcpc_main(int argc, char **argv)
517 client_config.arp, 527 client_config.arp,
518 client_config.interface) 528 client_config.interface)
519 ) { 529 ) {
520 bb_info_msg("offered address is in use," 530 bb_info_msg("offered address is in use "
521 " declining"); 531 "(got ARP reply), declining");
522 send_decline(xid, server_addr); 532 send_decline(xid, server_addr);
523 533
524 if (state != REQUESTING) 534 if (state != REQUESTING)
525 udhcp_run_script(NULL, "deconfig"); 535 udhcp_run_script(NULL, "deconfig");
536 change_listen_mode(LISTEN_RAW);
526 state = INIT_SELECTING; 537 state = INIT_SELECTING;
527 requested_ip = 0; 538 requested_ip = 0;
539 timeout = decline_wait;
528 packet_num = 0; 540 packet_num = 0;
529 change_mode(LISTEN_RAW); 541 continue; /* back to main loop */
530 timeout = now + decline_wait;
531 break;
532 } 542 }
533 } 543 }
534#endif 544#endif
535 /* enter bound state */ 545 /* enter bound state */
536 t1 = lease / 2; 546 t1 = lease_seconds / 2;
537 547
538 /* little fixed point for n * .875 */ 548 /* little fixed point for n * .875 */
539 t2 = (lease * 7) >> 3; 549 t2 = (lease_seconds * 7) >> 3;
540 temp_addr.s_addr = packet.yiaddr; 550 temp_addr.s_addr = packet.yiaddr;
541 bb_info_msg("Lease of %s obtained, lease time %u", 551 bb_info_msg("Lease of %s obtained, lease time %u",
542 inet_ntoa(temp_addr), (unsigned)lease); 552 inet_ntoa(temp_addr), (unsigned)lease_seconds);
543 start = now; 553 timestamp_got_lease = monotonic_sec();
544 timeout = start + t1; 554 timeout = t1;
545 requested_ip = packet.yiaddr; 555 requested_ip = packet.yiaddr;
546 udhcp_run_script(&packet, 556 udhcp_run_script(&packet,
547 ((state == RENEWING || state == REBINDING) ? "renew" : "bound")); 557 ((state == RENEWING || state == REBINDING) ? "renew" : "bound"));
548 558
549 state = BOUND; 559 state = BOUND;
550 change_mode(LISTEN_NONE); 560 change_listen_mode(LISTEN_NONE);
551 if (client_config.quit_after_lease) { 561 if (client_config.quit_after_lease) {
552 if (client_config.release_on_quit) 562 if (client_config.release_on_quit)
553 perform_release(); 563 perform_release();
@@ -556,23 +566,30 @@ int udhcpc_main(int argc, char **argv)
556 if (!client_config.foreground) 566 if (!client_config.foreground)
557 client_background(); 567 client_background();
558 568
559 } else if (*message == DHCPNAK) { 569 continue; /* back to main loop */
570 }
571 if (*message == DHCPNAK) {
560 /* return to init state */ 572 /* return to init state */
561 bb_info_msg("Received DHCP NAK"); 573 bb_info_msg("Received DHCP NAK");
562 udhcp_run_script(&packet, "nak"); 574 udhcp_run_script(&packet, "nak");
563 if (state != REQUESTING) 575 if (state != REQUESTING)
564 udhcp_run_script(NULL, "deconfig"); 576 udhcp_run_script(NULL, "deconfig");
577 change_listen_mode(LISTEN_RAW);
578 sleep(3); /* avoid excessive network traffic */
565 state = INIT_SELECTING; 579 state = INIT_SELECTING;
566 timeout = now;
567 requested_ip = 0; 580 requested_ip = 0;
581 timeout = 0;
568 packet_num = 0; 582 packet_num = 0;
569 change_mode(LISTEN_RAW);
570 sleep(3); /* avoid excessive network traffic */
571 } 583 }
572 break; 584 continue;
573 /* case BOUND, RELEASED: - ignore all packets */ 585 /* case BOUND, RELEASED: - ignore all packets */
574 } 586 }
575 } else { 587 continue; /* back to main loop */
588 }
589
590 /* select() didn't timeout, something did happen.
591 * But it wasn't a packet. It's a signal pipe then. */
592 {
576 int signo = udhcp_sp_read(&rfds); 593 int signo = udhcp_sp_read(&rfds);
577 switch (signo) { 594 switch (signo) {
578 case SIGUSR1: 595 case SIGUSR1:
@@ -588,7 +605,8 @@ int udhcpc_main(int argc, char **argv)
588 goto ret0; 605 goto ret0;
589 } 606 }
590 } 607 }
591 } /* for (;;) */ 608 } /* for (;;) - main loop ends */
609
592 ret0: 610 ret0:
593 retval = 0; 611 retval = 0;
594 ret: 612 ret: