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, 89 DHCP_OFFER, 90 DHCP_REQUEST, 91 DHCP_DECLINE, 92 DHCP_ACK, 93 DHCP_NACK, 94 DHCP_RELEASE, 95 DHCP_INFORM 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), 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 syslog(LOG_DEBUG, " server: %s\n", 767 _AddressToString(data).String()); 768 fServer.SetAddress(*(in_addr_t*)data); 769 break; 770 771 case OPTION_ADDRESS_LEASE_TIME: 772 syslog(LOG_DEBUG, " lease time: %lu seconds\n", 773 ntohl(*(uint32*)data)); 774 fLeaseTime = ntohl(*(uint32*)data) * 1000000LL; 775 break; 776 case OPTION_RENEWAL_TIME: 777 syslog(LOG_DEBUG, " renewal time: %lu seconds\n", 778 ntohl(*(uint32*)data)); 779 fRenewalTime = ntohl(*(uint32*)data) * 1000000LL; 780 break; 781 case OPTION_REBINDING_TIME: 782 syslog(LOG_DEBUG, " rebinding time: %lu seconds\n", 783 ntohl(*(uint32*)data)); 784 fRebindingTime = ntohl(*(uint32*)data) * 1000000LL; 785 break; 786 787 case OPTION_HOST_NAME: 788 syslog(LOG_DEBUG, " host name: \"%.*s\"\n", (int)size, 789 (const char*)data); 790 break; 791 792 case OPTION_DOMAIN_NAME: 793 { 794 char domain[256]; 795 strlcpy(domain, (const char*)data, 796 min_c(size + 1, sizeof(domain))); 797 798 syslog(LOG_DEBUG, " domain name: \"%s\"\n", domain); 799 800 resolverConfiguration.AddString("domain", domain); 801 break; 802 } 803 804 case OPTION_MESSAGE_TYPE: 805 break; 806 807 case OPTION_ERROR_MESSAGE: 808 syslog(LOG_INFO, " error message: \"%.*s\"\n", (int)size, 809 (const char*)data); 810 break; 811 812 default: 813 syslog(LOG_DEBUG, " UNKNOWN OPTION %lu (0x%x)\n", 814 (uint32)option, (uint32)option); 815 break; 816 } 817 } 818 } 819 820 821 void 822 DHCPClient::_PrepareMessage(dhcp_message& message, dhcp_state state) 823 { 824 message.opcode = BOOT_REQUEST; 825 message.hardware_type = ARP_HARDWARE_TYPE_ETHER; 826 message.hardware_address_length = 6; 827 message.transaction_id = htonl(fTransactionID); 828 message.seconds_since_start = htons(min_c((system_time() - fStartTime) 829 / 1000000LL, 65535)); 830 memcpy(message.mac_address, fMAC, 6); 831 832 message_type type = message.Type(); 833 834 uint8 *next = message.PrepareMessage(type); 835 836 switch (type) { 837 case DHCP_DISCOVER: 838 next = message.PutOption(next, OPTION_REQUEST_PARAMETERS, 839 kRequestParameters, sizeof(kRequestParameters)); 840 841 if (fHostName.Length() > 0) { 842 next = message.PutOption(next, OPTION_HOST_NAME, 843 reinterpret_cast<const uint8*>(fHostName.String()), 844 fHostName.Length()); 845 } 846 break; 847 848 case DHCP_REQUEST: 849 next = message.PutOption(next, OPTION_REQUEST_PARAMETERS, 850 kRequestParameters, sizeof(kRequestParameters)); 851 852 if (fHostName.Length() > 0) { 853 next = message.PutOption(next, OPTION_HOST_NAME, 854 reinterpret_cast<const uint8*>(fHostName.String()), 855 fHostName.Length()); 856 } 857 858 if (state == REQUESTING) { 859 const sockaddr_in& server = (sockaddr_in&)fServer.SockAddr(); 860 next = message.PutOption(next, OPTION_SERVER_ADDRESS, 861 (uint32)server.sin_addr.s_addr); 862 } 863 864 if (state == INIT || state == INIT_REBOOT 865 || state == REQUESTING) { 866 next = message.PutOption(next, OPTION_REQUEST_IP_ADDRESS, 867 (uint32)fAssignedAddress); 868 } else 869 message.client_address = fAssignedAddress; 870 break; 871 872 case DHCP_RELEASE: { 873 const sockaddr_in& server = (sockaddr_in&)fServer.SockAddr(); 874 next = message.PutOption(next, OPTION_SERVER_ADDRESS, 875 (uint32)server.sin_addr.s_addr); 876 877 message.client_address = fAssignedAddress; 878 break; 879 } 880 881 default: 882 break; 883 } 884 885 message.FinishOptions(next); 886 } 887 888 889 void 890 DHCPClient::_ResetTimeout(int socket, dhcp_state& state, time_t& timeout, 891 uint32& tries) 892 { 893 timeout = DEFAULT_TIMEOUT; 894 tries = 0; 895 896 struct timeval value; 897 value.tv_sec = timeout; 898 value.tv_usec = rand() % 1000000; 899 setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &value, sizeof(value)); 900 } 901 902 903 bool 904 DHCPClient::_TimeoutShift(int socket, dhcp_state& state, time_t& timeout, 905 uint32& tries) 906 { 907 if (state == RENEWING && system_time() > fRebindingTime) { 908 state = REBINDING; 909 return false; 910 } 911 912 if (state == REBINDING && system_time() > fLeaseTime) { 913 state = INIT; 914 return false; 915 } 916 917 tries++; 918 timeout += timeout; 919 if (timeout > MAX_TIMEOUT) 920 timeout = MAX_TIMEOUT; 921 922 if (tries > MAX_RETRIES) { 923 bigtime_t remaining = 0; 924 if (state == RENEWING) 925 remaining = (fRebindingTime - system_time()) / 2 + 1; 926 else if (state == REBINDING) 927 remaining = (fLeaseTime - system_time()) / 2 + 1; 928 else 929 return false; 930 931 timeout = std::max(remaining / 1000000, bigtime_t(60)); 932 } 933 934 syslog(LOG_DEBUG, "%s: Timeout shift: %lu secs (try %lu)\n", 935 Device(), timeout, tries); 936 937 struct timeval value; 938 value.tv_sec = timeout; 939 value.tv_usec = rand() % 1000000; 940 setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &value, sizeof(value)); 941 942 return true; 943 } 944 945 946 /*static*/ BString 947 DHCPClient::_AddressToString(const uint8* data) 948 { 949 BString target = inet_ntoa(*(in_addr*)data); 950 return target; 951 } 952 953 954 /*static*/ BString 955 DHCPClient::_AddressToString(in_addr_t address) 956 { 957 BString target = inet_ntoa(*(in_addr*)&address); 958 return target; 959 } 960 961 962 status_t 963 DHCPClient::_SendMessage(int socket, dhcp_message& message, 964 const BNetworkAddress& address) const 965 { 966 message_type type = message.Type(); 967 BString text; 968 text << dhcp_message::TypeToString(type); 969 970 const uint8* requestAddress = message.FindOption(OPTION_REQUEST_IP_ADDRESS); 971 if (type == DHCP_REQUEST && requestAddress != NULL) 972 text << " for " << _AddressToString(requestAddress).String(); 973 974 syslog(LOG_DEBUG, "%s: Send %s to %s\n", Device(), text.String(), 975 address.ToString().String()); 976 977 ssize_t bytesSent = sendto(socket, &message, message.Size(), 978 address.IsBroadcast() ? MSG_BCAST : 0, address, address.Length()); 979 if (bytesSent < 0) 980 return errno; 981 982 return B_OK; 983 } 984 985 986 dhcp_state 987 DHCPClient::_CurrentState() const 988 { 989 bigtime_t now = system_time(); 990 991 if (now > fLeaseTime || fStatus != B_OK) 992 return INIT; 993 if (now >= fRebindingTime) 994 return REBINDING; 995 if (now >= fRenewalTime) 996 return RENEWING; 997 return BOUND; 998 } 999 1000 1001 void 1002 DHCPClient::MessageReceived(BMessage* message) 1003 { 1004 switch (message->what) { 1005 case kMsgLeaseTime: 1006 _Negotiate(_CurrentState()); 1007 break; 1008 1009 default: 1010 BHandler::MessageReceived(message); 1011 break; 1012 } 1013 } 1014