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