1 /* 2 * Copyright 2006-2009, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Axel Dörfler, axeld@pinc-software.de 7 */ 8 9 10 #include "DHCPClient.h" 11 #include "NetServer.h" 12 13 #include <FindDirectory.h> 14 #include <Message.h> 15 #include <MessageRunner.h> 16 #include <Path.h> 17 18 #include <arpa/inet.h> 19 #include <errno.h> 20 #include <stdio.h> 21 #include <string.h> 22 #include <syslog.h> 23 #include <sys/sockio.h> 24 #include <sys/time.h> 25 26 27 // See RFC 2131 for DHCP, see RFC 1533 for BOOTP/DHCP options 28 29 #define DHCP_CLIENT_PORT 68 30 #define DHCP_SERVER_PORT 67 31 32 #define DEFAULT_TIMEOUT 2 // secs 33 #define MAX_TIMEOUT 15 // secs 34 35 enum message_opcode { 36 BOOT_REQUEST = 1, 37 BOOT_REPLY 38 }; 39 40 enum message_option { 41 OPTION_MAGIC = 0x63825363, 42 43 // generic options 44 OPTION_PAD = 0, 45 OPTION_END = 255, 46 OPTION_SUBNET_MASK = 1, 47 OPTION_TIME_OFFSET = 2, 48 OPTION_ROUTER_ADDRESS = 3, 49 OPTION_DOMAIN_NAME_SERVER = 6, 50 OPTION_HOST_NAME = 12, 51 OPTION_DOMAIN_NAME = 15, 52 OPTION_DATAGRAM_SIZE = 22, 53 OPTION_MTU = 26, 54 OPTION_BROADCAST_ADDRESS = 28, 55 OPTION_NETWORK_TIME_SERVERS = 42, 56 OPTION_NETBIOS_NAME_SERVER = 44, 57 OPTION_NETBIOS_SCOPE = 47, 58 59 // DHCP specific options 60 OPTION_REQUEST_IP_ADDRESS = 50, 61 OPTION_ADDRESS_LEASE_TIME = 51, 62 OPTION_OVERLOAD = 52, 63 OPTION_MESSAGE_TYPE = 53, 64 OPTION_SERVER_ADDRESS = 54, 65 OPTION_REQUEST_PARAMETERS = 55, 66 OPTION_ERROR_MESSAGE = 56, 67 OPTION_MESSAGE_SIZE = 57, 68 OPTION_RENEWAL_TIME = 58, 69 OPTION_REBINDING_TIME = 59, 70 OPTION_CLASS_IDENTIFIER = 60, 71 OPTION_CLIENT_IDENTIFIER = 61, 72 }; 73 74 enum message_type { 75 DHCP_NONE = 0, 76 DHCP_DISCOVER, 77 DHCP_OFFER, 78 DHCP_REQUEST, 79 DHCP_DECLINE, 80 DHCP_ACK, 81 DHCP_NACK, 82 DHCP_RELEASE, 83 DHCP_INFORM 84 }; 85 86 struct dhcp_option_cookie { 87 dhcp_option_cookie() 88 : 89 state(0), 90 file_has_options(false), 91 server_name_has_options(false) 92 { 93 } 94 95 const uint8* next; 96 uint8 state; 97 bool file_has_options; 98 bool server_name_has_options; 99 }; 100 101 struct dhcp_message { 102 dhcp_message(message_type type); 103 104 uint8 opcode; 105 uint8 hardware_type; 106 uint8 hardware_address_length; 107 uint8 hop_count; 108 uint32 transaction_id; 109 uint16 seconds_since_start; 110 uint16 flags; 111 in_addr_t client_address; 112 in_addr_t your_address; 113 in_addr_t server_address; 114 in_addr_t gateway_address; 115 uint8 mac_address[16]; 116 uint8 server_name[64]; 117 uint8 file[128]; 118 uint32 options_magic; 119 uint8 options[1260]; 120 121 size_t MinSize() const { return 576; } 122 size_t Size() const; 123 124 bool HasOptions() const; 125 bool NextOption(dhcp_option_cookie& cookie, message_option& option, 126 const uint8*& data, size_t& size) const; 127 message_type Type() const; 128 const uint8* LastOption() const; 129 130 uint8* PrepareMessage(uint8 type); 131 uint8* PutOption(uint8* options, message_option option); 132 uint8* PutOption(uint8* options, message_option option, uint8 data); 133 uint8* PutOption(uint8* options, message_option option, uint16 data); 134 uint8* PutOption(uint8* options, message_option option, uint32 data); 135 uint8* PutOption(uint8* options, message_option option, const uint8* data, 136 uint32 size); 137 uint8* FinishOptions(uint8 *); 138 } _PACKED; 139 140 #define DHCP_FLAG_BROADCAST 0x8000 141 142 #define ARP_HARDWARE_TYPE_ETHER 1 143 144 const uint32 kMsgLeaseTime = 'lstm'; 145 146 static const uint8 kRequiredParameters[] = { 147 OPTION_SUBNET_MASK, OPTION_ROUTER_ADDRESS, 148 OPTION_DOMAIN_NAME_SERVER, OPTION_BROADCAST_ADDRESS 149 }; 150 151 152 dhcp_message::dhcp_message(message_type type) 153 { 154 memset(this, 0, sizeof(*this)); 155 options_magic = htonl(OPTION_MAGIC); 156 157 uint8* next = PrepareMessage(type); 158 FinishOptions(next); 159 } 160 161 162 bool 163 dhcp_message::HasOptions() const 164 { 165 return options_magic == htonl(OPTION_MAGIC); 166 } 167 168 169 bool 170 dhcp_message::NextOption(dhcp_option_cookie& cookie, 171 message_option& option, const uint8*& data, size_t& size) const 172 { 173 if (cookie.state == 0) { 174 if (!HasOptions()) 175 return false; 176 177 cookie.state++; 178 cookie.next = options; 179 } 180 181 uint32 bytesLeft = 0; 182 183 switch (cookie.state) { 184 case 1: 185 // options from "options" 186 bytesLeft = sizeof(options) + cookie.next - options; 187 break; 188 189 case 2: 190 // options from "file" 191 bytesLeft = sizeof(options) + cookie.next - options; 192 break; 193 194 case 3: 195 // options from "server_name" 196 bytesLeft = sizeof(options) + cookie.next - options; 197 break; 198 } 199 200 while (true) { 201 if (bytesLeft == 0) { 202 // TODO: suppport OPTION_OVERLOAD! 203 cookie.state = 4; 204 return false; 205 } 206 207 option = (message_option)cookie.next[0]; 208 if (option == OPTION_END) { 209 cookie.state = 4; 210 return false; 211 } else if (option == OPTION_PAD) { 212 bytesLeft--; 213 cookie.next++; 214 continue; 215 } 216 217 size = cookie.next[1]; 218 data = &cookie.next[2]; 219 cookie.next += 2 + size; 220 221 if (option == OPTION_OVERLOAD) { 222 cookie.file_has_options = data[0] & 1; 223 cookie.server_name_has_options = data[0] & 2; 224 continue; 225 } 226 227 return true; 228 } 229 } 230 231 232 message_type 233 dhcp_message::Type() const 234 { 235 dhcp_option_cookie cookie; 236 message_option option; 237 const uint8* data; 238 size_t size; 239 while (NextOption(cookie, option, data, size)) { 240 // iterate through all options 241 if (option == OPTION_MESSAGE_TYPE) 242 return (message_type)data[0]; 243 } 244 245 return DHCP_NONE; 246 } 247 248 249 const uint8* 250 dhcp_message::LastOption() const 251 { 252 dhcp_option_cookie cookie; 253 message_option option; 254 const uint8* data; 255 size_t size; 256 while (NextOption(cookie, option, data, size)) { 257 // iterate through all options 258 } 259 260 return cookie.next; 261 } 262 263 264 size_t 265 dhcp_message::Size() const 266 { 267 const uint8* last = LastOption(); 268 return sizeof(dhcp_message) - sizeof(options) + last + 1 - options; 269 } 270 271 272 uint8* 273 dhcp_message::PrepareMessage(uint8 type) 274 { 275 uint8 *next = options; 276 next = PutOption(next, OPTION_MESSAGE_TYPE, type); 277 next = PutOption(next, OPTION_MESSAGE_SIZE, 278 (uint16)htons(sizeof(dhcp_message))); 279 return next; 280 } 281 282 283 uint8* 284 dhcp_message::PutOption(uint8* options, message_option option) 285 { 286 options[0] = option; 287 return options + 1; 288 } 289 290 291 uint8* 292 dhcp_message::PutOption(uint8* options, message_option option, uint8 data) 293 { 294 return PutOption(options, option, &data, 1); 295 } 296 297 298 uint8* 299 dhcp_message::PutOption(uint8* options, message_option option, uint16 data) 300 { 301 return PutOption(options, option, (uint8*)&data, sizeof(data)); 302 } 303 304 305 uint8* 306 dhcp_message::PutOption(uint8* options, message_option option, uint32 data) 307 { 308 return PutOption(options, option, (uint8*)&data, sizeof(data)); 309 } 310 311 312 uint8* 313 dhcp_message::PutOption(uint8* options, message_option option, 314 const uint8* data, uint32 size) 315 { 316 options[0] = option; 317 options[1] = size; 318 memcpy(&options[2], data, size); 319 320 return options + 2 + size; 321 } 322 323 324 uint8* 325 dhcp_message::FinishOptions(uint8 *next) 326 { 327 return PutOption(next, OPTION_END); 328 } 329 330 331 // #pragma mark - 332 333 334 DHCPClient::DHCPClient(BMessenger target, const char* device) 335 : AutoconfigClient("dhcp", target, device), 336 fConfiguration(kMsgConfigureInterface), 337 fRunner(NULL), 338 fLeaseTime(0) 339 { 340 fStartTime = system_time(); 341 fTransactionID = (uint32)fStartTime; 342 343 fStatus = get_mac_address(device, fMAC); 344 if (fStatus < B_OK) 345 return; 346 347 memset(&fServer, 0, sizeof(struct sockaddr_in)); 348 fServer.sin_family = AF_INET; 349 fServer.sin_len = sizeof(struct sockaddr_in); 350 fServer.sin_port = htons(DHCP_SERVER_PORT); 351 352 openlog_thread("DHCP", 0, LOG_DAEMON); 353 } 354 355 356 DHCPClient::~DHCPClient() 357 { 358 if (fStatus != B_OK) 359 return; 360 361 delete fRunner; 362 363 int socket = ::socket(AF_INET, SOCK_DGRAM, 0); 364 if (socket < 0) 365 return; 366 367 // release lease 368 369 dhcp_message release(DHCP_RELEASE); 370 _PrepareMessage(release, BOUND); 371 372 _SendMessage(socket, release, fServer); 373 close(socket); 374 375 closelog_thread(); 376 } 377 378 379 status_t 380 DHCPClient::Initialize() 381 { 382 fStatus = _Negotiate(INIT); 383 syslog(LOG_DEBUG, "DHCP for %s, status: %s\n", Device(), strerror(fStatus)); 384 return fStatus; 385 } 386 387 388 status_t 389 DHCPClient::_Negotiate(dhcp_state state) 390 { 391 int socket = ::socket(AF_INET, SOCK_DGRAM, 0); 392 if (socket < 0) 393 return errno; 394 395 sockaddr_in local; 396 memset(&local, 0, sizeof(struct sockaddr_in)); 397 local.sin_family = AF_INET; 398 local.sin_len = sizeof(struct sockaddr_in); 399 local.sin_port = htons(DHCP_CLIENT_PORT); 400 local.sin_addr.s_addr = INADDR_ANY; 401 402 // Enable reusing the port . This is needed in case there is more 403 // than 1 interface that needs to be configured. Note that the only reason 404 // this works is because there is code below to bind to a specific 405 // interface. 406 int option = 1; 407 setsockopt(socket, SOL_SOCKET, SO_REUSEPORT, &option, sizeof(option)); 408 409 if (bind(socket, (struct sockaddr *)&local, sizeof(local)) < 0) { 410 close(socket); 411 return errno; 412 } 413 414 sockaddr_in broadcast; 415 memset(&broadcast, 0, sizeof(struct sockaddr_in)); 416 broadcast.sin_family = AF_INET; 417 broadcast.sin_len = sizeof(struct sockaddr_in); 418 broadcast.sin_port = htons(DHCP_SERVER_PORT); 419 broadcast.sin_addr.s_addr = INADDR_BROADCAST; 420 421 option = 1; 422 setsockopt(socket, SOL_SOCKET, SO_BROADCAST, &option, sizeof(option)); 423 424 if (state == INIT) { 425 // The local interface does not have an address yet, bind the socket 426 // to the device directly. 427 int linkSocket = ::socket(AF_LINK, SOCK_DGRAM, 0); 428 if (linkSocket >= 0) { 429 // we need to know the index of the device to be able to bind to it 430 ifreq request; 431 prepare_request(request, Device()); 432 if (ioctl(linkSocket, SIOCGIFINDEX, &request, sizeof(struct ifreq)) 433 == 0) { 434 setsockopt(socket, SOL_SOCKET, SO_BINDTODEVICE, 435 &request.ifr_index, sizeof(int)); 436 } 437 438 close(linkSocket); 439 } 440 } 441 442 bigtime_t previousLeaseTime = fLeaseTime; 443 fLeaseTime = 0; 444 fRenewalTime = 0; 445 fRebindingTime = 0; 446 447 status_t status = B_ERROR; 448 time_t timeout; 449 uint32 tries; 450 _ResetTimeout(socket, timeout, tries); 451 452 dhcp_message discover(DHCP_DISCOVER); 453 _PrepareMessage(discover, state); 454 455 dhcp_message request(DHCP_REQUEST); 456 _PrepareMessage(request, state); 457 458 // send discover/request message 459 _SendMessage(socket, state == INIT ? discover : request, 460 state != RENEWAL ? broadcast : fServer); 461 // no need to check the status; in case of an error we'll just send 462 // the message again 463 464 // receive loop until we've got an offer and acknowledged it 465 466 while (state != ACKNOWLEDGED) { 467 char buffer[2048]; 468 ssize_t bytesReceived = recvfrom(socket, buffer, sizeof(buffer), 469 0, NULL, NULL); 470 if (bytesReceived < 0 && errno == B_TIMED_OUT) { 471 // depending on the state, we'll just try again 472 if (!_TimeoutShift(socket, timeout, tries)) { 473 close(socket); 474 return B_TIMED_OUT; 475 } 476 477 if (state == INIT) 478 _SendMessage(socket, discover, broadcast); 479 else { 480 _SendMessage(socket, request, 481 state != RENEWAL ? broadcast : fServer); 482 } 483 484 continue; 485 } else if (bytesReceived < 0) 486 break; 487 488 dhcp_message *message = (dhcp_message *)buffer; 489 if (message->transaction_id != htonl(fTransactionID) 490 || !message->HasOptions() 491 || memcmp(message->mac_address, discover.mac_address, 492 discover.hardware_address_length)) { 493 // this message is not for us 494 continue; 495 } 496 497 switch (message->Type()) { 498 case DHCP_NONE: 499 default: 500 // ignore this message 501 break; 502 503 case DHCP_OFFER: 504 { 505 // first offer wins 506 if (state != INIT) 507 break; 508 509 // collect interface options 510 511 fAssignedAddress = message->your_address; 512 513 fConfiguration.MakeEmpty(); 514 fConfiguration.AddString("device", Device()); 515 fConfiguration.AddBool("auto", true); 516 517 BMessage address; 518 address.AddString("family", "inet"); 519 address.AddString("address", _ToString(fAssignedAddress)); 520 _ParseOptions(*message, address); 521 522 fConfiguration.AddMessage("address", &address); 523 524 // request configuration from the server 525 526 _ResetTimeout(socket, timeout, tries); 527 state = REQUESTING; 528 _PrepareMessage(request, state); 529 530 status = _SendMessage(socket, request, broadcast); 531 // we're sending a broadcast so that all potential offers 532 // get an answer 533 break; 534 } 535 536 case DHCP_ACK: 537 { 538 if (state != REQUESTING && state != REBINDING 539 && state != RENEWAL) 540 continue; 541 542 // TODO: we might want to configure the stuff, don't we? 543 BMessage address; 544 _ParseOptions(*message, address); 545 // TODO: currently, only lease time and DNS is updated this way 546 547 // our address request has been acknowledged 548 state = ACKNOWLEDGED; 549 550 // configure interface 551 BMessage reply; 552 status = Target().SendMessage(&fConfiguration, &reply); 553 if (status == B_OK) 554 status = reply.FindInt32("status", &fStatus); 555 break; 556 } 557 558 case DHCP_NACK: 559 if (state != REQUESTING) 560 continue; 561 562 // try again (maybe we should prefer other servers if this 563 // happens more than once) 564 status = _SendMessage(socket, discover, broadcast); 565 if (status == B_OK) 566 state = INIT; 567 break; 568 } 569 } 570 571 close(socket); 572 573 if (status == B_OK && fLeaseTime > 0) { 574 // notify early enough when the lease is 575 if (fRenewalTime == 0) 576 fRenewalTime = fLeaseTime * 2/3; 577 if (fRebindingTime == 0) 578 fRebindingTime = fLeaseTime * 5/6; 579 580 bigtime_t now = system_time(); 581 _RestartLease(fRenewalTime); 582 583 fLeaseTime += now; 584 fRenewalTime += now; 585 fRebindingTime += now; 586 // make lease times absolute 587 } else { 588 fLeaseTime = previousLeaseTime; 589 bigtime_t now = system_time(); 590 fRenewalTime = (fLeaseTime - now) * 2/3 + now; 591 fRebindingTime = (fLeaseTime - now) * 5/6 + now; 592 } 593 594 return status; 595 } 596 597 598 void 599 DHCPClient::_RestartLease(bigtime_t leaseTime) 600 { 601 if (leaseTime == 0) 602 return; 603 604 BMessage lease(kMsgLeaseTime); 605 fRunner = new BMessageRunner(this, &lease, leaseTime, 1); 606 } 607 608 609 void 610 DHCPClient::_ParseOptions(dhcp_message& message, BMessage& address) 611 { 612 dhcp_option_cookie cookie; 613 message_option option; 614 const uint8* data; 615 size_t size; 616 while (message.NextOption(cookie, option, data, size)) { 617 // iterate through all options 618 switch (option) { 619 case OPTION_ROUTER_ADDRESS: 620 address.AddString("gateway", _ToString(data)); 621 break; 622 case OPTION_SUBNET_MASK: 623 address.AddString("mask", _ToString(data)); 624 break; 625 case OPTION_BROADCAST_ADDRESS: 626 address.AddString("broadcast", _ToString(data)); 627 break; 628 case OPTION_DOMAIN_NAME_SERVER: 629 { 630 // TODO: for now, we write it just out to resolv.conf 631 BPath path; 632 if (find_directory(B_COMMON_SETTINGS_DIRECTORY, &path) != B_OK) 633 break; 634 635 path.Append("network/resolv.conf"); 636 637 FILE* file = fopen(path.Path(), "w"); 638 for (uint32 i = 0; i < size / 4; i++) { 639 syslog(LOG_INFO, "DNS: %s\n", 640 _ToString(&data[i * 4]).String()); 641 if (file != NULL) { 642 fprintf(file, "nameserver %s\n", 643 _ToString(&data[i * 4]).String()); 644 } 645 } 646 fclose(file); 647 break; 648 } 649 case OPTION_SERVER_ADDRESS: 650 fServer.sin_addr.s_addr = *(in_addr_t*)data; 651 break; 652 653 case OPTION_ADDRESS_LEASE_TIME: 654 syslog(LOG_INFO, "lease time of %lu seconds\n", 655 htonl(*(uint32*)data)); 656 fLeaseTime = htonl(*(uint32*)data) * 1000000LL; 657 break; 658 case OPTION_RENEWAL_TIME: 659 syslog(LOG_INFO, "renewal time of %lu seconds\n", 660 htonl(*(uint32*)data)); 661 fRenewalTime = htonl(*(uint32*)data) * 1000000LL; 662 break; 663 case OPTION_REBINDING_TIME: 664 syslog(LOG_INFO, "rebinding time of %lu seconds\n", 665 htonl(*(uint32*)data)); 666 fRebindingTime = htonl(*(uint32*)data) * 1000000LL; 667 break; 668 669 case OPTION_HOST_NAME: 670 { 671 char name[256]; 672 memcpy(name, data, size); 673 name[size] = '\0'; 674 syslog(LOG_INFO, "DHCP host name: \"%s\"\n", name); 675 break; 676 } 677 678 case OPTION_DOMAIN_NAME: 679 { 680 char name[256]; 681 memcpy(name, data, size); 682 name[size] = '\0'; 683 syslog(LOG_INFO, "DHCP domain name: \"%s\"\n", name); 684 break; 685 } 686 687 case OPTION_MESSAGE_TYPE: 688 break; 689 690 default: 691 syslog(LOG_INFO, "unknown option %lu\n", (uint32)option); 692 break; 693 } 694 } 695 } 696 697 698 void 699 DHCPClient::_PrepareMessage(dhcp_message& message, dhcp_state state) 700 { 701 message.opcode = BOOT_REQUEST; 702 message.hardware_type = ARP_HARDWARE_TYPE_ETHER; 703 message.hardware_address_length = 6; 704 message.transaction_id = htonl(fTransactionID); 705 message.seconds_since_start = htons(min_c((system_time() - fStartTime) 706 / 1000000LL, 65535)); 707 memcpy(message.mac_address, fMAC, 6); 708 709 message_type type = message.Type(); 710 711 switch (type) { 712 case DHCP_REQUEST: 713 case DHCP_RELEASE: 714 { 715 // add server identifier option 716 uint8* next = message.PrepareMessage(type); 717 next = message.PutOption(next, OPTION_SERVER_ADDRESS, 718 (uint32)fServer.sin_addr.s_addr); 719 720 // In RENEWAL or REBINDING state, we must set the client_address field, and not 721 // use OPTION_REQUEST_IP_ADDRESS for DHCP_REQUEST messages 722 if (type == DHCP_REQUEST && (state == INIT || state == REQUESTING)) { 723 next = message.PutOption(next, OPTION_REQUEST_IP_ADDRESS, 724 (uint32)fAssignedAddress); 725 next = message.PutOption(next, OPTION_REQUEST_PARAMETERS, 726 kRequiredParameters, sizeof(kRequiredParameters)); 727 } else 728 message.client_address = fAssignedAddress; 729 730 message.FinishOptions(next); 731 break; 732 } 733 734 case DHCP_DISCOVER: 735 { 736 uint8 *next = message.PrepareMessage(type); 737 next = message.PutOption(next, OPTION_REQUEST_PARAMETERS, 738 kRequiredParameters, sizeof(kRequiredParameters)); 739 message.FinishOptions(next); 740 break; 741 } 742 743 default: 744 // the default options are fine 745 break; 746 } 747 } 748 749 750 void 751 DHCPClient::_ResetTimeout(int socket, time_t& timeout, uint32& tries) 752 { 753 timeout = DEFAULT_TIMEOUT; 754 tries = 0; 755 756 struct timeval value; 757 value.tv_sec = timeout; 758 value.tv_usec = 0; 759 setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &value, sizeof(value)); 760 } 761 762 763 bool 764 DHCPClient::_TimeoutShift(int socket, time_t& timeout, uint32& tries) 765 { 766 timeout += timeout; 767 if (timeout > MAX_TIMEOUT) { 768 timeout = DEFAULT_TIMEOUT; 769 770 if (++tries > 2) 771 return false; 772 } 773 syslog(LOG_DEBUG, "DHCP timeout shift: %lu secs (try %lu)\n", timeout, 774 tries); 775 776 struct timeval value; 777 value.tv_sec = timeout; 778 value.tv_usec = 0; 779 setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &value, sizeof(value)); 780 781 return true; 782 } 783 784 785 BString 786 DHCPClient::_ToString(const uint8* data) const 787 { 788 BString target = inet_ntoa(*(in_addr*)data); 789 return target; 790 } 791 792 793 BString 794 DHCPClient::_ToString(in_addr_t address) const 795 { 796 BString target = inet_ntoa(*(in_addr*)&address); 797 return target; 798 } 799 800 801 status_t 802 DHCPClient::_SendMessage(int socket, dhcp_message& message, 803 sockaddr_in& address) const 804 { 805 ssize_t bytesSent = sendto(socket, &message, message.Size(), 806 address.sin_addr.s_addr == INADDR_BROADCAST ? MSG_BCAST : 0, 807 (struct sockaddr*)&address, sizeof(sockaddr_in)); 808 if (bytesSent < 0) 809 return errno; 810 811 return B_OK; 812 } 813 814 815 dhcp_state 816 DHCPClient::_CurrentState() const 817 { 818 bigtime_t now = system_time(); 819 820 if (now > fLeaseTime || fStatus < B_OK) 821 return INIT; 822 if (now >= fRebindingTime) 823 return REBINDING; 824 if (now >= fRenewalTime) 825 return RENEWAL; 826 827 return BOUND; 828 } 829 830 831 void 832 DHCPClient::MessageReceived(BMessage* message) 833 { 834 switch (message->what) { 835 case kMsgLeaseTime: 836 { 837 dhcp_state state = _CurrentState(); 838 839 bigtime_t next; 840 if (_Negotiate(state) == B_OK) { 841 switch (state) { 842 case RENEWAL: 843 next = fRebindingTime; 844 break; 845 case REBINDING: 846 default: 847 next = fRenewalTime; 848 break; 849 } 850 } else { 851 switch (state) { 852 case RENEWAL: 853 next = (fLeaseTime - fRebindingTime) / 4 + system_time(); 854 break; 855 case REBINDING: 856 default: 857 next = (fLeaseTime - fRenewalTime) / 4 + system_time(); 858 break; 859 } 860 } 861 862 _RestartLease(next - system_time()); 863 break; 864 } 865 866 default: 867 BHandler::MessageReceived(message); 868 break; 869 } 870 } 871