diff options
-rw-r--r-- | networking/udhcp/dhcpc.c | 299 |
1 files changed, 151 insertions, 148 deletions
diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c index fca5c2a03..077098f42 100644 --- a/networking/udhcp/dhcpc.c +++ b/networking/udhcp/dhcpc.c | |||
@@ -307,7 +307,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) | |||
307 | /* Goes to stdout (unless NOMMU) and possibly syslog */ | 307 | /* Goes to stdout (unless NOMMU) and possibly syslog */ |
308 | bb_info_msg("%s (v"BB_VER") started", applet_name); | 308 | bb_info_msg("%s (v"BB_VER") started", applet_name); |
309 | 309 | ||
310 | /* if not set, and not suppressed, setup the default client ID */ | 310 | /* If not set, and not suppressed, set up the default client ID */ |
311 | if (!client_config.clientid && !(opt & OPT_C)) { | 311 | if (!client_config.clientid && !(opt & OPT_C)) { |
312 | client_config.clientid = alloc_dhcp_option(DHCP_CLIENT_ID, "", 7); | 312 | client_config.clientid = alloc_dhcp_option(DHCP_CLIENT_ID, "", 7); |
313 | client_config.clientid[OPT_DATA] = 1; | 313 | client_config.clientid[OPT_DATA] = 1; |
@@ -317,7 +317,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) | |||
317 | if (!client_config.vendorclass) | 317 | if (!client_config.vendorclass) |
318 | client_config.vendorclass = alloc_dhcp_option(DHCP_VENDOR, "udhcp "BB_VER, 0); | 318 | client_config.vendorclass = alloc_dhcp_option(DHCP_VENDOR, "udhcp "BB_VER, 0); |
319 | 319 | ||
320 | /* setup the signal pipe */ | 320 | /* Set up the signal pipe */ |
321 | udhcp_sp_setup(); | 321 | udhcp_sp_setup(); |
322 | 322 | ||
323 | state = INIT_SELECTING; | 323 | state = INIT_SELECTING; |
@@ -467,19 +467,45 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) | |||
467 | /* yah, I know, *you* say it would never happen */ | 467 | /* yah, I know, *you* say it would never happen */ |
468 | timeout = INT_MAX; | 468 | timeout = INT_MAX; |
469 | continue; /* back to main loop */ | 469 | continue; /* back to main loop */ |
470 | } /* if select timed out */ | ||
471 | |||
472 | /* select() didn't timeout, something happened */ | ||
473 | |||
474 | /* Is it a signal? */ | ||
475 | /* note: udhcp_sp_read checks FD_ISSET before reading */ | ||
476 | switch (udhcp_sp_read(&rfds)) { | ||
477 | case SIGUSR1: | ||
478 | perform_renew(); | ||
479 | /* Start things over */ | ||
480 | packet_num = 0; | ||
481 | /* Kill any timeouts because the user wants this to hurry along */ | ||
482 | timeout = 0; | ||
483 | continue; | ||
484 | case SIGUSR2: | ||
485 | perform_release(requested_ip, server_addr); | ||
486 | timeout = INT_MAX; | ||
487 | continue; | ||
488 | case SIGTERM: | ||
489 | bb_info_msg("Received SIGTERM"); | ||
490 | if (opt & OPT_R) /* release on quit */ | ||
491 | perform_release(requested_ip, server_addr); | ||
492 | goto ret0; | ||
470 | } | 493 | } |
471 | 494 | ||
472 | /* select() didn't timeout, something did happen. */ | ||
473 | /* Is it a packet? */ | 495 | /* Is it a packet? */ |
474 | if (listen_mode != LISTEN_NONE && FD_ISSET(sockfd, &rfds)) { | 496 | if (listen_mode == LISTEN_NONE || !FD_ISSET(sockfd, &rfds)) |
497 | continue; /* no */ | ||
498 | |||
499 | { | ||
475 | int len; | 500 | int len; |
476 | /* A packet is ready, read it */ | ||
477 | 501 | ||
502 | /* A packet is ready, read it */ | ||
478 | if (listen_mode == LISTEN_KERNEL) | 503 | if (listen_mode == LISTEN_KERNEL) |
479 | len = udhcp_recv_kernel_packet(&packet, sockfd); | 504 | len = udhcp_recv_kernel_packet(&packet, sockfd); |
480 | else | 505 | else |
481 | len = udhcp_recv_raw_packet(&packet, sockfd); | 506 | len = udhcp_recv_raw_packet(&packet, sockfd); |
482 | if (len == -1) { /* error is severe, reopen socket */ | 507 | if (len == -1) { |
508 | /* Error is severe, reopen socket */ | ||
483 | bb_info_msg("Read error: %s, reopening socket", strerror(errno)); | 509 | bb_info_msg("Read error: %s, reopening socket", strerror(errno)); |
484 | sleep(discover_timeout); /* 3 seconds by default */ | 510 | sleep(discover_timeout); /* 3 seconds by default */ |
485 | change_listen_mode(listen_mode); /* just close and reopen */ | 511 | change_listen_mode(listen_mode); /* just close and reopen */ |
@@ -490,70 +516,71 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) | |||
490 | already_waited_sec += (unsigned)monotonic_sec() - timestamp_before_wait; | 516 | already_waited_sec += (unsigned)monotonic_sec() - timestamp_before_wait; |
491 | if (len < 0) | 517 | if (len < 0) |
492 | continue; | 518 | continue; |
519 | } | ||
493 | 520 | ||
494 | if (packet.xid != xid) { | 521 | if (packet.xid != xid) { |
495 | log1("xid %x (our is %x), ignoring packet", | 522 | log1("xid %x (our is %x), ignoring packet", |
496 | (unsigned)packet.xid, (unsigned)xid); | 523 | (unsigned)packet.xid, (unsigned)xid); |
497 | continue; | 524 | continue; |
498 | } | 525 | } |
499 | 526 | ||
500 | /* Ignore packets that aren't for us */ | 527 | /* Ignore packets that aren't for us */ |
501 | if (packet.hlen != 6 | 528 | if (packet.hlen != 6 |
502 | || memcmp(packet.chaddr, client_config.client_mac, 6) | 529 | || memcmp(packet.chaddr, client_config.client_mac, 6) |
503 | ) { | 530 | ) { |
504 | //FIXME: need to also check that last 10 bytes are zero | 531 | //FIXME: need to also check that last 10 bytes are zero |
505 | log1("chaddr does not match, ignoring packet"); // log2? | 532 | log1("chaddr does not match, ignoring packet"); // log2? |
506 | continue; | 533 | continue; |
507 | } | 534 | } |
508 | 535 | ||
509 | message = get_option(&packet, DHCP_MESSAGE_TYPE); | 536 | message = get_option(&packet, DHCP_MESSAGE_TYPE); |
510 | if (message == NULL) { | 537 | if (message == NULL) { |
511 | bb_error_msg("no message type option, ignoring packet"); | 538 | bb_error_msg("no message type option, ignoring packet"); |
512 | continue; | 539 | continue; |
513 | } | 540 | } |
514 | 541 | ||
515 | switch (state) { | 542 | switch (state) { |
516 | case INIT_SELECTING: | 543 | case INIT_SELECTING: |
517 | /* Must be a DHCPOFFER to one of our xid's */ | 544 | /* Must be a DHCPOFFER to one of our xid's */ |
518 | if (*message == DHCPOFFER) { | 545 | if (*message == DHCPOFFER) { |
519 | /* TODO: why we don't just fetch server's IP from IP header? */ | 546 | /* TODO: why we don't just fetch server's IP from IP header? */ |
520 | temp = get_option(&packet, DHCP_SERVER_ID); | 547 | temp = get_option(&packet, DHCP_SERVER_ID); |
521 | if (!temp) { | 548 | if (!temp) { |
522 | bb_error_msg("no server ID in message"); | 549 | bb_error_msg("no server ID in message"); |
523 | continue; | 550 | continue; |
524 | /* still selecting - this server looks bad */ | 551 | /* still selecting - this server looks bad */ |
525 | } | 552 | } |
553 | /* it IS unaligned sometimes, don't "optimize" */ | ||
554 | move_from_unaligned32(server_addr, temp); | ||
555 | xid = packet.xid; | ||
556 | requested_ip = packet.yiaddr; | ||
557 | |||
558 | /* enter requesting state */ | ||
559 | state = REQUESTING; | ||
560 | timeout = 0; | ||
561 | packet_num = 0; | ||
562 | already_waited_sec = 0; | ||
563 | } | ||
564 | continue; | ||
565 | case RENEW_REQUESTED: | ||
566 | case REQUESTING: | ||
567 | case RENEWING: | ||
568 | case REBINDING: | ||
569 | if (*message == DHCPACK) { | ||
570 | temp = get_option(&packet, DHCP_LEASE_TIME); | ||
571 | if (!temp) { | ||
572 | bb_error_msg("no lease time with ACK, using 1 hour lease"); | ||
573 | lease_seconds = 60 * 60; | ||
574 | } else { | ||
526 | /* it IS unaligned sometimes, don't "optimize" */ | 575 | /* it IS unaligned sometimes, don't "optimize" */ |
527 | move_from_unaligned32(server_addr, temp); | 576 | move_from_unaligned32(lease_seconds, temp); |
528 | xid = packet.xid; | 577 | lease_seconds = ntohl(lease_seconds); |
529 | requested_ip = packet.yiaddr; | 578 | lease_seconds &= 0x0fffffff; /* paranoia: must not be prone to overflows */ |
530 | 579 | if (lease_seconds < 10) /* and not too small */ | |
531 | /* enter requesting state */ | 580 | lease_seconds = 10; |
532 | state = REQUESTING; | ||
533 | timeout = 0; | ||
534 | packet_num = 0; | ||
535 | already_waited_sec = 0; | ||
536 | } | 581 | } |
537 | continue; | ||
538 | case RENEW_REQUESTED: | ||
539 | case REQUESTING: | ||
540 | case RENEWING: | ||
541 | case REBINDING: | ||
542 | if (*message == DHCPACK) { | ||
543 | temp = get_option(&packet, DHCP_LEASE_TIME); | ||
544 | if (!temp) { | ||
545 | bb_error_msg("no lease time with ACK, using 1 hour lease"); | ||
546 | lease_seconds = 60 * 60; | ||
547 | } else { | ||
548 | /* it IS unaligned sometimes, don't "optimize" */ | ||
549 | move_from_unaligned32(lease_seconds, temp); | ||
550 | lease_seconds = ntohl(lease_seconds); | ||
551 | lease_seconds &= 0x0fffffff; /* paranoia: must not be prone to overflows */ | ||
552 | if (lease_seconds < 10) /* and not too small */ | ||
553 | lease_seconds = 10; | ||
554 | } | ||
555 | #if ENABLE_FEATURE_UDHCPC_ARPING | 582 | #if ENABLE_FEATURE_UDHCPC_ARPING |
556 | if (opt & OPT_a) { | 583 | if (opt & OPT_a) { |
557 | /* RFC 2131 3.1 paragraph 5: | 584 | /* RFC 2131 3.1 paragraph 5: |
558 | * "The client receives the DHCPACK message with configuration | 585 | * "The client receives the DHCPACK message with configuration |
559 | * parameters. The client SHOULD perform a final check on the | 586 | * parameters. The client SHOULD perform a final check on the |
@@ -563,100 +590,76 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) | |||
563 | * address is already in use (e.g., through the use of ARP), | 590 | * address is already in use (e.g., through the use of ARP), |
564 | * the client MUST send a DHCPDECLINE message to the server and restarts | 591 | * the client MUST send a DHCPDECLINE message to the server and restarts |
565 | * the configuration process..." */ | 592 | * the configuration process..." */ |
566 | if (!arpping(packet.yiaddr, | 593 | if (!arpping(packet.yiaddr, |
567 | NULL, | 594 | NULL, |
568 | (uint32_t) 0, | 595 | (uint32_t) 0, |
569 | client_config.client_mac, | 596 | client_config.client_mac, |
570 | client_config.interface) | 597 | client_config.interface) |
571 | ) { | 598 | ) { |
572 | bb_info_msg("Offered address is in use " | 599 | bb_info_msg("Offered address is in use " |
573 | "(got ARP reply), declining"); | 600 | "(got ARP reply), declining"); |
574 | send_decline(xid, server_addr, packet.yiaddr); | 601 | send_decline(xid, server_addr, packet.yiaddr); |
575 | 602 | ||
576 | if (state != REQUESTING) | 603 | if (state != REQUESTING) |
577 | udhcp_run_script(NULL, "deconfig"); | 604 | udhcp_run_script(NULL, "deconfig"); |
578 | change_listen_mode(LISTEN_RAW); | 605 | change_listen_mode(LISTEN_RAW); |
579 | state = INIT_SELECTING; | 606 | state = INIT_SELECTING; |
580 | requested_ip = 0; | 607 | requested_ip = 0; |
581 | timeout = tryagain_timeout; | 608 | timeout = tryagain_timeout; |
582 | packet_num = 0; | 609 | packet_num = 0; |
583 | already_waited_sec = 0; | 610 | already_waited_sec = 0; |
584 | continue; /* back to main loop */ | 611 | continue; /* back to main loop */ |
585 | } | ||
586 | } | ||
587 | #endif | ||
588 | /* enter bound state */ | ||
589 | timeout = lease_seconds / 2; | ||
590 | { | ||
591 | struct in_addr temp_addr; | ||
592 | temp_addr.s_addr = packet.yiaddr; | ||
593 | bb_info_msg("Lease of %s obtained, lease time %u", | ||
594 | inet_ntoa(temp_addr), (unsigned)lease_seconds); | ||
595 | } | ||
596 | requested_ip = packet.yiaddr; | ||
597 | udhcp_run_script(&packet, | ||
598 | ((state == RENEWING || state == REBINDING) ? "renew" : "bound")); | ||
599 | |||
600 | state = BOUND; | ||
601 | change_listen_mode(LISTEN_NONE); | ||
602 | if (opt & OPT_q) { /* quit after lease */ | ||
603 | if (opt & OPT_R) /* release on quit */ | ||
604 | perform_release(requested_ip, server_addr); | ||
605 | goto ret0; | ||
606 | } | ||
607 | #if BB_MMU /* NOMMU case backgrounded earlier */ | ||
608 | if (!(opt & OPT_f)) { | ||
609 | client_background(); | ||
610 | /* do not background again! */ | ||
611 | opt = ((opt & ~OPT_b) | OPT_f); | ||
612 | } | 612 | } |
613 | } | ||
613 | #endif | 614 | #endif |
614 | already_waited_sec = 0; | 615 | /* enter bound state */ |
615 | continue; /* back to main loop */ | 616 | timeout = lease_seconds / 2; |
617 | { | ||
618 | struct in_addr temp_addr; | ||
619 | temp_addr.s_addr = packet.yiaddr; | ||
620 | bb_info_msg("Lease of %s obtained, lease time %u", | ||
621 | inet_ntoa(temp_addr), (unsigned)lease_seconds); | ||
616 | } | 622 | } |
617 | if (*message == DHCPNAK) { | 623 | requested_ip = packet.yiaddr; |
618 | /* return to init state */ | 624 | udhcp_run_script(&packet, |
619 | bb_info_msg("Received DHCP NAK"); | 625 | ((state == RENEWING || state == REBINDING) ? "renew" : "bound")); |
620 | udhcp_run_script(&packet, "nak"); | 626 | |
621 | if (state != REQUESTING) | 627 | state = BOUND; |
622 | udhcp_run_script(NULL, "deconfig"); | 628 | change_listen_mode(LISTEN_NONE); |
623 | change_listen_mode(LISTEN_RAW); | 629 | if (opt & OPT_q) { /* quit after lease */ |
624 | sleep(3); /* avoid excessive network traffic */ | 630 | if (opt & OPT_R) /* release on quit */ |
625 | state = INIT_SELECTING; | 631 | perform_release(requested_ip, server_addr); |
626 | requested_ip = 0; | 632 | goto ret0; |
627 | timeout = 0; | ||
628 | packet_num = 0; | ||
629 | already_waited_sec = 0; | ||
630 | } | 633 | } |
631 | continue; | 634 | #if BB_MMU /* NOMMU case backgrounded earlier */ |
632 | /* case BOUND, RELEASED: - ignore all packets */ | 635 | if (!(opt & OPT_f)) { |
636 | client_background(); | ||
637 | /* do not background again! */ | ||
638 | opt = ((opt & ~OPT_b) | OPT_f); | ||
639 | } | ||
640 | #endif | ||
641 | already_waited_sec = 0; | ||
642 | continue; /* back to main loop */ | ||
633 | } | 643 | } |
634 | continue; /* back to main loop */ | 644 | if (*message == DHCPNAK) { |
635 | } | 645 | /* return to init state */ |
636 | 646 | bb_info_msg("Received DHCP NAK"); | |
637 | /* select() didn't timeout, something did happen. | 647 | udhcp_run_script(&packet, "nak"); |
638 | * But it wasn't a packet. It's a signal pipe then. */ | 648 | if (state != REQUESTING) |
639 | { | 649 | udhcp_run_script(NULL, "deconfig"); |
640 | int signo = udhcp_sp_read(&rfds); | 650 | change_listen_mode(LISTEN_RAW); |
641 | switch (signo) { | 651 | sleep(3); /* avoid excessive network traffic */ |
642 | case SIGUSR1: | 652 | state = INIT_SELECTING; |
643 | perform_renew(); | 653 | requested_ip = 0; |
644 | /* start things over */ | ||
645 | packet_num = 0; | ||
646 | /* Kill any timeouts because the user wants this to hurry along */ | ||
647 | timeout = 0; | 654 | timeout = 0; |
648 | break; | 655 | packet_num = 0; |
649 | case SIGUSR2: | 656 | already_waited_sec = 0; |
650 | perform_release(requested_ip, server_addr); | ||
651 | timeout = INT_MAX; | ||
652 | break; | ||
653 | case SIGTERM: | ||
654 | bb_info_msg("Received SIGTERM"); | ||
655 | if (opt & OPT_R) /* release on quit */ | ||
656 | perform_release(requested_ip, server_addr); | ||
657 | goto ret0; | ||
658 | } | 657 | } |
658 | continue; | ||
659 | /* case BOUND: - ignore all packets */ | ||
660 | /* case RELEASED: - ignore all packets */ | ||
659 | } | 661 | } |
662 | /* back to main loop */ | ||
660 | } /* for (;;) - main loop ends */ | 663 | } /* for (;;) - main loop ends */ |
661 | 664 | ||
662 | ret0: | 665 | ret0: |