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