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