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 bigtime_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 if (tries == UINT8_MAX) 452 return false; 453 454 tries++; 455 456 if (tries > MAX_RETRIES) { 457 bigtime_t now = system_time(); 458 if (stateMaxTime == -1 || stateMaxTime < now) 459 return false; 460 bigtime_t remaining = (stateMaxTime - now) / 2 + 1; 461 timeout = std::max(remaining, bigtime_t(AS_USECS(60))); 462 } else 463 timeout += timeout; 464 465 if (timeout > AS_USECS(MAX_TIMEOUT)) 466 timeout = AS_USECS(MAX_TIMEOUT); 467 468 syslog(LOG_DEBUG, "%s: Timeout shift: %" B_PRIdTIME " msecs (try %" B_PRIu8 ")\n", 469 device, timeout / 1000, tries); 470 471 UpdateSocket(socket); 472 return true; 473 } 474 475 476 // #pragma mark - 477 478 479 DHCPClient::DHCPClient(BMessenger target, const char* device) 480 : 481 AutoconfigClient("dhcp", target, device), 482 fConfiguration(kMsgConfigureInterface), 483 fResolverConfiguration(kMsgConfigureResolver), 484 fRunner(NULL), 485 fAssignedAddress(0), 486 fServer(AF_INET, NULL, DHCP_SERVER_PORT, B_UNCONFIGURED_ADDRESS_FAMILIES), 487 fStartTime(0), 488 fRequestTime(0), 489 fRenewalTime(0), 490 fRebindingTime(0), 491 fLeaseTime(0) 492 { 493 fTransactionID = (uint32)system_time() ^ rand(); 494 495 BNetworkAddress link; 496 BNetworkInterface interface(device); 497 fStatus = interface.GetHardwareAddress(link); 498 if (fStatus != B_OK) 499 return; 500 501 memcpy(fMAC, link.LinkLevelAddress(), sizeof(fMAC)); 502 503 if ((interface.Flags() & IFF_AUTO_CONFIGURED) != 0) { 504 // Check for interface previous auto-configured address, if any. 505 BNetworkInterfaceAddress interfaceAddress; 506 int index = interface.FindFirstAddress(AF_INET); 507 if (index >= 0 508 && interface.GetAddressAt(index, interfaceAddress) == B_OK) { 509 BNetworkAddress address = interfaceAddress.Address(); 510 const sockaddr_in& addr = (sockaddr_in&)address.SockAddr(); 511 fAssignedAddress = addr.sin_addr.s_addr; 512 513 if ((ntohl(fAssignedAddress) & IN_CLASSB_NET) == 0xa9fe0000) { 514 // previous auto-configured address is a link-local one: 515 // there is no point asking a DHCP server to renew such 516 // server-less assigned address... 517 fAssignedAddress = 0; 518 } 519 } 520 } 521 522 openlog_thread("DHCP", 0, LOG_DAEMON); 523 } 524 525 526 DHCPClient::~DHCPClient() 527 { 528 if (fStatus != B_OK) 529 return; 530 531 delete fRunner; 532 533 int socket = ::socket(AF_INET, SOCK_DGRAM, 0); 534 if (socket < 0) 535 return; 536 537 // release lease 538 539 dhcp_message release(DHCP_RELEASE); 540 _PrepareMessage(release, BOUND); 541 542 _SendMessage(socket, release, fServer); 543 close(socket); 544 545 closelog_thread(); 546 } 547 548 549 status_t 550 DHCPClient::Initialize() 551 { 552 fStatus = _Negotiate(fAssignedAddress == 0 ? INIT : INIT_REBOOT); 553 syslog(LOG_DEBUG, "%s: DHCP status = %s\n", Device(), strerror(fStatus)); 554 return fStatus; 555 } 556 557 558 status_t 559 DHCPClient::_Negotiate(dhcp_state state) 560 { 561 if (state == BOUND) 562 return B_OK; 563 564 fStartTime = system_time(); 565 fTransactionID++; 566 567 char hostName[MAXHOSTNAMELEN]; 568 if (gethostname(hostName, MAXHOSTNAMELEN) == 0) 569 fHostName.SetTo(hostName, MAXHOSTNAMELEN); 570 else 571 fHostName.Truncate(0); 572 573 int socket = ::socket(AF_INET, SOCK_DGRAM, 0); 574 if (socket < 0) 575 return errno; 576 577 // Enable reusing the port. This is needed in case there is more 578 // than 1 interface that needs to be configured. Note that the only reason 579 // this works is because there is code below to bind to a specific 580 // interface. 581 int option = 1; 582 setsockopt(socket, SOL_SOCKET, SO_REUSEPORT, &option, sizeof(option)); 583 584 BNetworkAddress local; 585 local.SetToWildcard(AF_INET, DHCP_CLIENT_PORT); 586 587 option = 1; 588 setsockopt(socket, SOL_SOCKET, SO_BROADCAST, &option, sizeof(option)); 589 590 if (bind(socket, local, local.Length()) < 0) { 591 close(socket); 592 return errno; 593 } 594 595 bigtime_t previousLeaseTime = fLeaseTime; 596 597 status_t status = B_OK; 598 while (state != BOUND) { 599 status = _StateTransition(socket, state); 600 if (status != B_OK && (state == SELECTING || state == REBOOTING)) 601 break; 602 } 603 604 close(socket); 605 606 if (fLeaseTime == 0) 607 fLeaseTime = previousLeaseTime; 608 if (fLeaseTime == 0) 609 fLeaseTime = 60; 610 611 if (fRenewalTime == 0) 612 fRenewalTime = fLeaseTime / 2; 613 if (fRebindingTime == 0) 614 fRebindingTime = fLeaseTime * 7 / 8; 615 fLeaseTime += fRequestTime; 616 fRenewalTime += fRequestTime; 617 fRebindingTime += fRequestTime; 618 _RestartLease(fRenewalTime); 619 620 fStatus = status; 621 if (status) 622 return status; 623 624 // configure interface 625 BMessage reply; 626 status = Target().SendMessage(&fConfiguration, &reply); 627 if (status == B_OK) 628 status = reply.FindInt32("status", &fStatus); 629 630 // configure resolver 631 reply.MakeEmpty(); 632 fResolverConfiguration.AddString("device", Device()); 633 status = Target().SendMessage(&fResolverConfiguration, &reply); 634 if (status == B_OK) 635 status = reply.FindInt32("status", &fStatus); 636 return status; 637 } 638 639 640 status_t 641 DHCPClient::_GotMessage(dhcp_state& state, dhcp_message* message) 642 { 643 switch (state) { 644 case SELECTING: 645 if (message->Type() == DHCP_OFFER) { 646 state = REQUESTING; 647 648 fAssignedAddress = message->your_address; 649 syslog(LOG_INFO, " your_address: %s\n", 650 _AddressToString(fAssignedAddress).String()); 651 652 fConfiguration.MakeEmpty(); 653 fConfiguration.AddString("device", Device()); 654 fConfiguration.AddBool("auto_configured", true); 655 656 BMessage address; 657 address.AddString("family", "inet"); 658 address.AddString("address", _AddressToString(fAssignedAddress)); 659 fResolverConfiguration.MakeEmpty(); 660 _ParseOptions(*message, address, fResolverConfiguration); 661 662 fConfiguration.AddMessage("address", &address); 663 return B_OK; 664 } 665 666 return B_BAD_VALUE; 667 668 case REBOOTING: 669 case REBINDING: 670 case RENEWING: 671 case REQUESTING: 672 if (message->Type() == DHCP_ACK) { 673 // TODO: we might want to configure the stuff, don't we? 674 BMessage address; 675 fResolverConfiguration.MakeEmpty(); 676 _ParseOptions(*message, address, fResolverConfiguration); 677 // TODO: currently, only lease time and DNS is updated this 678 // way 679 680 // our address request has been acknowledged 681 state = BOUND; 682 683 return B_OK; 684 } 685 686 if (message->Type() == DHCP_NACK) { 687 // server reject our request on previous assigned address 688 // back to square one... 689 fAssignedAddress = 0; 690 state = INIT; 691 return B_OK; 692 } 693 694 default: 695 return B_BAD_VALUE; 696 } 697 } 698 699 700 status_t 701 DHCPClient::_StateTransition(int socket, dhcp_state& state) 702 { 703 if (state == INIT) { 704 // The local interface does not have an address yet, bind the socket 705 // to the device directly. 706 BNetworkDevice device(Device()); 707 int index = device.Index(); 708 709 setsockopt(socket, SOL_SOCKET, SO_BINDTODEVICE, &index, sizeof(int)); 710 } 711 712 BNetworkAddress broadcast; 713 broadcast.SetToBroadcast(AF_INET, DHCP_SERVER_PORT); 714 715 socket_timeout timeout(socket); 716 717 dhcp_message discover(DHCP_DISCOVER); 718 _PrepareMessage(discover, state); 719 720 dhcp_message request(DHCP_REQUEST); 721 _PrepareMessage(request, state); 722 723 bool skipRequest = false; 724 dhcp_state originalState = state; 725 fRequestTime = system_time(); 726 while (true) { 727 if (!skipRequest) { 728 _SendMessage(socket, originalState == INIT ? discover : request, 729 originalState == RENEWING ? fServer : broadcast); 730 731 if (originalState == INIT) 732 state = SELECTING; 733 else if (originalState == INIT_REBOOT) 734 state = REBOOTING; 735 } 736 737 char buffer[2048]; 738 struct sockaddr_in from; 739 socklen_t fromLength = sizeof(from); 740 ssize_t bytesReceived = recvfrom(socket, buffer, sizeof(buffer), 741 0, (struct sockaddr*)&from, &fromLength); 742 if (bytesReceived < 0 && errno == B_TIMED_OUT) { 743 // depending on the state, we'll just try again 744 if (!_TimeoutShift(socket, state, timeout)) 745 return B_TIMED_OUT; 746 skipRequest = false; 747 continue; 748 } else if (bytesReceived < 0) 749 return errno; 750 751 skipRequest = true; 752 dhcp_message* message = (dhcp_message*)buffer; 753 if (message->transaction_id != htonl(fTransactionID) 754 || !message->HasOptions() 755 || memcmp(message->mac_address, discover.mac_address, 756 discover.hardware_address_length)) { 757 // this message is not for us 758 syslog(LOG_DEBUG, "%s: Ignoring %s not for us from %s\n", 759 Device(), dhcp_message::TypeToString(message->Type()), 760 _AddressToString(from.sin_addr.s_addr).String()); 761 continue; 762 } 763 764 syslog(LOG_DEBUG, "%s: Received %s from %s\n", 765 Device(), dhcp_message::TypeToString(message->Type()), 766 _AddressToString(from.sin_addr.s_addr).String()); 767 768 if (_GotMessage(state, message) == B_OK) 769 break; 770 } 771 772 return B_OK; 773 } 774 775 776 void 777 DHCPClient::_RestartLease(bigtime_t leaseTime) 778 { 779 if (leaseTime == 0) 780 return; 781 782 BMessage lease(kMsgLeaseTime); 783 fRunner = new BMessageRunner(this, &lease, leaseTime - system_time(), 1); 784 } 785 786 787 void 788 DHCPClient::_ParseOptions(dhcp_message& message, BMessage& address, 789 BMessage& resolverConfiguration) 790 { 791 dhcp_option_cookie cookie; 792 message_option option; 793 const uint8* data; 794 size_t size; 795 while (message.NextOption(cookie, option, data, size)) { 796 // iterate through all options 797 switch (option) { 798 case OPTION_ROUTER_ADDRESS: 799 syslog(LOG_DEBUG, " gateway: %s\n", 800 _AddressToString(data).String()); 801 address.AddString("gateway", _AddressToString(data)); 802 break; 803 case OPTION_SUBNET_MASK: 804 syslog(LOG_DEBUG, " subnet: %s\n", 805 _AddressToString(data).String()); 806 address.AddString("mask", _AddressToString(data)); 807 break; 808 case OPTION_BROADCAST_ADDRESS: 809 syslog(LOG_DEBUG, " broadcast: %s\n", 810 _AddressToString(data).String()); 811 address.AddString("broadcast", _AddressToString(data)); 812 break; 813 case OPTION_DOMAIN_NAME_SERVER: 814 { 815 for (uint32 i = 0; i < size / 4; i++) { 816 syslog(LOG_DEBUG, " nameserver[%d]: %s\n", i, 817 _AddressToString(&data[i * 4]).String()); 818 resolverConfiguration.AddString("nameserver", 819 _AddressToString(&data[i * 4]).String()); 820 } 821 resolverConfiguration.AddInt32("nameserver_count", 822 size / 4); 823 break; 824 } 825 case OPTION_SERVER_ADDRESS: 826 { 827 syslog(LOG_DEBUG, " server: %s\n", 828 _AddressToString(data).String()); 829 status_t status = fServer.SetAddress(*(in_addr_t*)data); 830 if (status != B_OK) { 831 syslog(LOG_ERR, " BNetworkAddress::SetAddress failed with %s!\n", 832 strerror(status)); 833 fServer.Unset(); 834 } 835 break; 836 } 837 838 case OPTION_ADDRESS_LEASE_TIME: 839 syslog(LOG_DEBUG, " lease time: %lu seconds\n", 840 ntohl(*(uint32*)data)); 841 fLeaseTime = ntohl(*(uint32*)data) * 1000000LL; 842 break; 843 case OPTION_RENEWAL_TIME: 844 syslog(LOG_DEBUG, " renewal time: %lu seconds\n", 845 ntohl(*(uint32*)data)); 846 fRenewalTime = ntohl(*(uint32*)data) * 1000000LL; 847 break; 848 case OPTION_REBINDING_TIME: 849 syslog(LOG_DEBUG, " rebinding time: %lu seconds\n", 850 ntohl(*(uint32*)data)); 851 fRebindingTime = ntohl(*(uint32*)data) * 1000000LL; 852 break; 853 854 case OPTION_HOST_NAME: 855 syslog(LOG_DEBUG, " host name: \"%.*s\"\n", (int)size, 856 (const char*)data); 857 break; 858 859 case OPTION_DOMAIN_NAME: 860 { 861 char domain[256]; 862 strlcpy(domain, (const char*)data, 863 min_c(size + 1, sizeof(domain))); 864 865 syslog(LOG_DEBUG, " domain name: \"%s\"\n", domain); 866 867 resolverConfiguration.AddString("domain", domain); 868 break; 869 } 870 871 case OPTION_MESSAGE_TYPE: 872 break; 873 874 case OPTION_ERROR_MESSAGE: 875 syslog(LOG_INFO, " error message: \"%.*s\"\n", (int)size, 876 (const char*)data); 877 break; 878 879 default: 880 syslog(LOG_DEBUG, " UNKNOWN OPTION %" B_PRIu32 " (0x%" B_PRIx32 ")\n", 881 (uint32)option, (uint32)option); 882 break; 883 } 884 } 885 } 886 887 888 void 889 DHCPClient::_PrepareMessage(dhcp_message& message, dhcp_state state) 890 { 891 message.opcode = BOOT_REQUEST; 892 message.hardware_type = ARP_HARDWARE_TYPE_ETHER; 893 message.hardware_address_length = 6; 894 message.transaction_id = htonl(fTransactionID); 895 message.seconds_since_start = htons(min_c((system_time() - fStartTime) 896 / 1000000LL, 65535)); 897 memcpy(message.mac_address, fMAC, 6); 898 899 message_type type = message.Type(); 900 901 uint8 *next = message.PrepareMessage(type); 902 903 switch (type) { 904 case DHCP_DISCOVER: 905 next = message.PutOption(next, OPTION_REQUEST_PARAMETERS, 906 kRequestParameters, sizeof(kRequestParameters)); 907 908 if (fHostName.Length() > 0) { 909 next = message.PutOption(next, OPTION_HOST_NAME, 910 reinterpret_cast<const uint8*>(fHostName.String()), 911 fHostName.Length()); 912 } 913 break; 914 915 case DHCP_REQUEST: 916 next = message.PutOption(next, OPTION_REQUEST_PARAMETERS, 917 kRequestParameters, sizeof(kRequestParameters)); 918 919 if (fHostName.Length() > 0) { 920 next = message.PutOption(next, OPTION_HOST_NAME, 921 reinterpret_cast<const uint8*>(fHostName.String()), 922 fHostName.Length()); 923 } 924 925 if (state == REQUESTING) { 926 const sockaddr_in& server = (sockaddr_in&)fServer.SockAddr(); 927 next = message.PutOption(next, OPTION_SERVER_ADDRESS, 928 (uint32)server.sin_addr.s_addr); 929 } 930 931 if (state == INIT || state == INIT_REBOOT 932 || state == REQUESTING) { 933 next = message.PutOption(next, OPTION_REQUEST_IP_ADDRESS, 934 (uint32)fAssignedAddress); 935 } else 936 message.client_address = fAssignedAddress; 937 break; 938 939 case DHCP_RELEASE: { 940 const sockaddr_in& server = (sockaddr_in&)fServer.SockAddr(); 941 next = message.PutOption(next, OPTION_SERVER_ADDRESS, 942 (uint32)server.sin_addr.s_addr); 943 944 message.client_address = fAssignedAddress; 945 break; 946 } 947 948 default: 949 break; 950 } 951 952 message.FinishOptions(next); 953 } 954 955 956 bool 957 DHCPClient::_TimeoutShift(int socket, dhcp_state& state, 958 socket_timeout& timeout) 959 { 960 bigtime_t stateMaxTime = -1; 961 962 // Compute the date at which we must consider the DHCP negociation failed. 963 // This varies depending on the current state. In renewing and rebinding 964 // states, it is based on the lease expiration. 965 // We can stay for up to 1 minute in the selecting and requesting states 966 // (these correspond to sending DHCP_DISCOVER and DHCP_REQUEST, 967 // respectively). 968 // All other states time out immediately after a single try. 969 // If this timeout expires, the DHCP negociation is aborted and starts 970 // over. As long as the timeout is not expired, we repeat the message with 971 // increasing delays (the delay is computed in timeout.shift below, and is 972 // at most equal to the timeout, but usually much shorter). 973 if (state == RENEWING) 974 stateMaxTime = fRebindingTime; 975 else if (state == REBINDING) 976 stateMaxTime = fLeaseTime; 977 else if (state == SELECTING || state == REQUESTING) 978 stateMaxTime = fRequestTime + AS_USECS(MAX_TIMEOUT); 979 980 if (system_time() > stateMaxTime) { 981 state = state == REBINDING ? INIT : REBINDING; 982 return false; 983 } 984 985 return timeout.Shift(socket, stateMaxTime, Device()); 986 } 987 988 989 /*static*/ BString 990 DHCPClient::_AddressToString(const uint8* data) 991 { 992 BString target = inet_ntoa(*(in_addr*)data); 993 return target; 994 } 995 996 997 /*static*/ BString 998 DHCPClient::_AddressToString(in_addr_t address) 999 { 1000 BString target = inet_ntoa(*(in_addr*)&address); 1001 return target; 1002 } 1003 1004 1005 status_t 1006 DHCPClient::_SendMessage(int socket, dhcp_message& message, 1007 const BNetworkAddress& address) const 1008 { 1009 message_type type = message.Type(); 1010 BString text; 1011 text << dhcp_message::TypeToString(type); 1012 1013 const uint8* requestAddress = message.FindOption(OPTION_REQUEST_IP_ADDRESS); 1014 if (type == DHCP_REQUEST && requestAddress != NULL) 1015 text << " for " << _AddressToString(requestAddress).String(); 1016 1017 syslog(LOG_DEBUG, "%s: Send %s to %s\n", Device(), text.String(), 1018 address.ToString().String()); 1019 1020 ssize_t bytesSent = sendto(socket, &message, message.Size(), 1021 address.IsBroadcast() ? MSG_BCAST : 0, address, address.Length()); 1022 if (bytesSent < 0) 1023 return errno; 1024 1025 return B_OK; 1026 } 1027 1028 1029 dhcp_state 1030 DHCPClient::_CurrentState() const 1031 { 1032 bigtime_t now = system_time(); 1033 1034 if (now > fLeaseTime || fStatus != B_OK) 1035 return INIT; 1036 if (now >= fRebindingTime) 1037 return REBINDING; 1038 if (now >= fRenewalTime) 1039 return RENEWING; 1040 return BOUND; 1041 } 1042 1043 1044 void 1045 DHCPClient::MessageReceived(BMessage* message) 1046 { 1047 switch (message->what) { 1048 case kMsgLeaseTime: 1049 _Negotiate(_CurrentState()); 1050 break; 1051 1052 default: 1053 BHandler::MessageReceived(message); 1054 break; 1055 } 1056 } 1057