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