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