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 <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: %lu secs (try %lu)\n", 464 device, timeout, 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 continue; 750 } 751 752 syslog(LOG_DEBUG, "%s: Received %s from %s\n", 753 Device(), dhcp_message::TypeToString(message->Type()), 754 _AddressToString(from.sin_addr.s_addr).String()); 755 756 if (_GotMessage(state, message) == B_OK) 757 break; 758 } 759 760 return B_OK; 761 } 762 763 764 void 765 DHCPClient::_RestartLease(bigtime_t leaseTime) 766 { 767 if (leaseTime == 0) 768 return; 769 770 BMessage lease(kMsgLeaseTime); 771 fRunner = new BMessageRunner(this, &lease, leaseTime - system_time(), 1); 772 } 773 774 775 void 776 DHCPClient::_ParseOptions(dhcp_message& message, BMessage& address, 777 BMessage& resolverConfiguration) 778 { 779 dhcp_option_cookie cookie; 780 message_option option; 781 const uint8* data; 782 size_t size; 783 while (message.NextOption(cookie, option, data, size)) { 784 // iterate through all options 785 switch (option) { 786 case OPTION_ROUTER_ADDRESS: 787 syslog(LOG_DEBUG, " gateway: %s\n", 788 _AddressToString(data).String()); 789 address.AddString("gateway", _AddressToString(data)); 790 break; 791 case OPTION_SUBNET_MASK: 792 syslog(LOG_DEBUG, " subnet: %s\n", 793 _AddressToString(data).String()); 794 address.AddString("mask", _AddressToString(data)); 795 break; 796 case OPTION_BROADCAST_ADDRESS: 797 syslog(LOG_DEBUG, " broadcast: %s\n", 798 _AddressToString(data).String()); 799 address.AddString("broadcast", _AddressToString(data)); 800 break; 801 case OPTION_DOMAIN_NAME_SERVER: 802 { 803 for (uint32 i = 0; i < size / 4; i++) { 804 syslog(LOG_DEBUG, " nameserver[%d]: %s\n", i, 805 _AddressToString(&data[i * 4]).String()); 806 resolverConfiguration.AddString("nameserver", 807 _AddressToString(&data[i * 4]).String()); 808 } 809 resolverConfiguration.AddInt32("nameserver_count", 810 size / 4); 811 break; 812 } 813 case OPTION_SERVER_ADDRESS: 814 { 815 syslog(LOG_DEBUG, " server: %s\n", 816 _AddressToString(data).String()); 817 status_t status = fServer.SetAddress(*(in_addr_t*)data); 818 if (status != B_OK) { 819 syslog(LOG_ERR, " BNetworkAddress::SetAddress failed with %s!\n", 820 strerror(status)); 821 fServer.Unset(); 822 } 823 break; 824 } 825 826 case OPTION_ADDRESS_LEASE_TIME: 827 syslog(LOG_DEBUG, " lease time: %lu seconds\n", 828 ntohl(*(uint32*)data)); 829 fLeaseTime = ntohl(*(uint32*)data) * 1000000LL; 830 break; 831 case OPTION_RENEWAL_TIME: 832 syslog(LOG_DEBUG, " renewal time: %lu seconds\n", 833 ntohl(*(uint32*)data)); 834 fRenewalTime = ntohl(*(uint32*)data) * 1000000LL; 835 break; 836 case OPTION_REBINDING_TIME: 837 syslog(LOG_DEBUG, " rebinding time: %lu seconds\n", 838 ntohl(*(uint32*)data)); 839 fRebindingTime = ntohl(*(uint32*)data) * 1000000LL; 840 break; 841 842 case OPTION_HOST_NAME: 843 syslog(LOG_DEBUG, " host name: \"%.*s\"\n", (int)size, 844 (const char*)data); 845 break; 846 847 case OPTION_DOMAIN_NAME: 848 { 849 char domain[256]; 850 strlcpy(domain, (const char*)data, 851 min_c(size + 1, sizeof(domain))); 852 853 syslog(LOG_DEBUG, " domain name: \"%s\"\n", domain); 854 855 resolverConfiguration.AddString("domain", domain); 856 break; 857 } 858 859 case OPTION_MESSAGE_TYPE: 860 break; 861 862 case OPTION_ERROR_MESSAGE: 863 syslog(LOG_INFO, " error message: \"%.*s\"\n", (int)size, 864 (const char*)data); 865 break; 866 867 default: 868 syslog(LOG_DEBUG, " UNKNOWN OPTION %lu (0x%x)\n", 869 (uint32)option, (uint32)option); 870 break; 871 } 872 } 873 } 874 875 876 void 877 DHCPClient::_PrepareMessage(dhcp_message& message, dhcp_state state) 878 { 879 message.opcode = BOOT_REQUEST; 880 message.hardware_type = ARP_HARDWARE_TYPE_ETHER; 881 message.hardware_address_length = 6; 882 message.transaction_id = htonl(fTransactionID); 883 message.seconds_since_start = htons(min_c((system_time() - fStartTime) 884 / 1000000LL, 65535)); 885 memcpy(message.mac_address, fMAC, 6); 886 887 message_type type = message.Type(); 888 889 uint8 *next = message.PrepareMessage(type); 890 891 switch (type) { 892 case DHCP_DISCOVER: 893 next = message.PutOption(next, OPTION_REQUEST_PARAMETERS, 894 kRequestParameters, sizeof(kRequestParameters)); 895 896 if (fHostName.Length() > 0) { 897 next = message.PutOption(next, OPTION_HOST_NAME, 898 reinterpret_cast<const uint8*>(fHostName.String()), 899 fHostName.Length()); 900 } 901 break; 902 903 case DHCP_REQUEST: 904 next = message.PutOption(next, OPTION_REQUEST_PARAMETERS, 905 kRequestParameters, sizeof(kRequestParameters)); 906 907 if (fHostName.Length() > 0) { 908 next = message.PutOption(next, OPTION_HOST_NAME, 909 reinterpret_cast<const uint8*>(fHostName.String()), 910 fHostName.Length()); 911 } 912 913 if (state == REQUESTING) { 914 const sockaddr_in& server = (sockaddr_in&)fServer.SockAddr(); 915 next = message.PutOption(next, OPTION_SERVER_ADDRESS, 916 (uint32)server.sin_addr.s_addr); 917 } 918 919 if (state == INIT || state == INIT_REBOOT 920 || state == REQUESTING) { 921 next = message.PutOption(next, OPTION_REQUEST_IP_ADDRESS, 922 (uint32)fAssignedAddress); 923 } else 924 message.client_address = fAssignedAddress; 925 break; 926 927 case DHCP_RELEASE: { 928 const sockaddr_in& server = (sockaddr_in&)fServer.SockAddr(); 929 next = message.PutOption(next, OPTION_SERVER_ADDRESS, 930 (uint32)server.sin_addr.s_addr); 931 932 message.client_address = fAssignedAddress; 933 break; 934 } 935 936 default: 937 break; 938 } 939 940 message.FinishOptions(next); 941 } 942 943 944 bool 945 DHCPClient::_TimeoutShift(int socket, dhcp_state& state, 946 socket_timeout& timeout) 947 { 948 bigtime_t stateMaxTime = -1; 949 if (state == RENEWING) 950 stateMaxTime = fRebindingTime; 951 else if (state == REBINDING) 952 stateMaxTime = fLeaseTime; 953 954 if (system_time() > stateMaxTime) { 955 state = state == REBINDING ? INIT : REBINDING; 956 return false; 957 } 958 959 return timeout.Shift(socket, stateMaxTime, Device()); 960 } 961 962 963 /*static*/ BString 964 DHCPClient::_AddressToString(const uint8* data) 965 { 966 BString target = inet_ntoa(*(in_addr*)data); 967 return target; 968 } 969 970 971 /*static*/ BString 972 DHCPClient::_AddressToString(in_addr_t address) 973 { 974 BString target = inet_ntoa(*(in_addr*)&address); 975 return target; 976 } 977 978 979 status_t 980 DHCPClient::_SendMessage(int socket, dhcp_message& message, 981 const BNetworkAddress& address) const 982 { 983 message_type type = message.Type(); 984 BString text; 985 text << dhcp_message::TypeToString(type); 986 987 const uint8* requestAddress = message.FindOption(OPTION_REQUEST_IP_ADDRESS); 988 if (type == DHCP_REQUEST && requestAddress != NULL) 989 text << " for " << _AddressToString(requestAddress).String(); 990 991 syslog(LOG_DEBUG, "%s: Send %s to %s\n", Device(), text.String(), 992 address.ToString().String()); 993 994 ssize_t bytesSent = sendto(socket, &message, message.Size(), 995 address.IsBroadcast() ? MSG_BCAST : 0, address, address.Length()); 996 if (bytesSent < 0) 997 return errno; 998 999 return B_OK; 1000 } 1001 1002 1003 dhcp_state 1004 DHCPClient::_CurrentState() const 1005 { 1006 bigtime_t now = system_time(); 1007 1008 if (now > fLeaseTime || fStatus != B_OK) 1009 return INIT; 1010 if (now >= fRebindingTime) 1011 return REBINDING; 1012 if (now >= fRenewalTime) 1013 return RENEWING; 1014 return BOUND; 1015 } 1016 1017 1018 void 1019 DHCPClient::MessageReceived(BMessage* message) 1020 { 1021 switch (message->what) { 1022 case kMsgLeaseTime: 1023 _Negotiate(_CurrentState()); 1024 break; 1025 1026 default: 1027 BHandler::MessageReceived(message); 1028 break; 1029 } 1030 } 1031