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