1fb81684fSAxel Dörfler /* 2fb81684fSAxel Dörfler * Copyright 2006, Haiku, Inc. All Rights Reserved. 3fb81684fSAxel Dörfler * Distributed under the terms of the MIT License. 4fb81684fSAxel Dörfler * 5fb81684fSAxel Dörfler * Authors: 6fb81684fSAxel Dörfler * Axel Dörfler, axeld@pinc-software.de 7fb81684fSAxel Dörfler */ 8fb81684fSAxel Dörfler 9fb81684fSAxel Dörfler 10fb81684fSAxel Dörfler #include "DHCPClient.h" 11fb81684fSAxel Dörfler #include "NetServer.h" 12fb81684fSAxel Dörfler 13f9af6566SAxel Dörfler #include <Message.h> 146cc7630fSAxel Dörfler #include <MessageRunner.h> 15f9af6566SAxel Dörfler 16f9af6566SAxel Dörfler #include <arpa/inet.h> 17fb81684fSAxel Dörfler #include <errno.h> 18fb81684fSAxel Dörfler #include <stdio.h> 19fb81684fSAxel Dörfler #include <string.h> 20f9af6566SAxel Dörfler #include <sys/time.h> 21fb81684fSAxel Dörfler 22fb81684fSAxel Dörfler 23fb81684fSAxel Dörfler // See RFC 2131 for DHCP, see RFC 1533 for BOOTP/DHCP options 24fb81684fSAxel Dörfler 25fb81684fSAxel Dörfler #define DHCP_CLIENT_PORT 68 26fb81684fSAxel Dörfler #define DHCP_SERVER_PORT 67 27fb81684fSAxel Dörfler 28f9af6566SAxel Dörfler #define DEFAULT_TIMEOUT 2 // secs 29f9af6566SAxel Dörfler #define MAX_TIMEOUT 15 // secs 30f9af6566SAxel Dörfler 31fb81684fSAxel Dörfler enum message_opcode { 32fb81684fSAxel Dörfler BOOT_REQUEST = 1, 33fb81684fSAxel Dörfler BOOT_REPLY 34fb81684fSAxel Dörfler }; 35fb81684fSAxel Dörfler 36fb81684fSAxel Dörfler enum message_option { 37fb81684fSAxel Dörfler OPTION_MAGIC = 0x63825363, 38fb81684fSAxel Dörfler 39fb81684fSAxel Dörfler // generic options 40fb81684fSAxel Dörfler OPTION_PAD = 0, 41f9af6566SAxel Dörfler OPTION_END = 255, 42f9af6566SAxel Dörfler OPTION_SUBNET_MASK = 1, 43f9af6566SAxel Dörfler OPTION_ROUTER_ADDRESS = 3, 44f9af6566SAxel Dörfler OPTION_DOMAIN_NAME_SERVER = 6, 45f9af6566SAxel Dörfler OPTION_HOST_NAME = 12, 465782c5a3SAxel Dörfler OPTION_DOMAIN_NAME = 15, 47fb81684fSAxel Dörfler OPTION_DATAGRAM_SIZE = 22, 48fb81684fSAxel Dörfler OPTION_MTU = 26, 49fb81684fSAxel Dörfler OPTION_BROADCAST_ADDRESS = 28, 50fb81684fSAxel Dörfler OPTION_NETWORK_TIME_SERVERS = 42, 51fb81684fSAxel Dörfler 52fb81684fSAxel Dörfler // DHCP specific options 53fb81684fSAxel Dörfler OPTION_REQUEST_IP_ADDRESS = 50, 54fb81684fSAxel Dörfler OPTION_ADDRESS_LEASE_TIME = 51, 55fb81684fSAxel Dörfler OPTION_OVERLOAD = 52, 56fb81684fSAxel Dörfler OPTION_MESSAGE_TYPE = 53, 57fb81684fSAxel Dörfler OPTION_SERVER_ADDRESS = 54, 58fb81684fSAxel Dörfler OPTION_REQUEST_PARAMETERS = 55, 59fb81684fSAxel Dörfler OPTION_ERROR_MESSAGE = 56, 60fb81684fSAxel Dörfler OPTION_MESSAGE_SIZE = 57, 61fb81684fSAxel Dörfler OPTION_RENEWAL_TIME = 58, 62fb81684fSAxel Dörfler OPTION_REBINDING_TIME = 59, 63fb81684fSAxel Dörfler OPTION_CLASS_IDENTIFIER = 60, 64fb81684fSAxel Dörfler OPTION_CLIENT_IDENTIFIER = 61, 65fb81684fSAxel Dörfler }; 66fb81684fSAxel Dörfler 67fb81684fSAxel Dörfler enum message_type { 68f9af6566SAxel Dörfler DHCP_NONE = 0, 69f9af6566SAxel Dörfler DHCP_DISCOVER, 70fb81684fSAxel Dörfler DHCP_OFFER, 71fb81684fSAxel Dörfler DHCP_REQUEST, 72fb81684fSAxel Dörfler DHCP_DECLINE, 73fb81684fSAxel Dörfler DHCP_ACK, 74f9af6566SAxel Dörfler DHCP_NACK, 75fb81684fSAxel Dörfler DHCP_RELEASE, 76fb81684fSAxel Dörfler DHCP_INFORM 77fb81684fSAxel Dörfler }; 78fb81684fSAxel Dörfler 79fb81684fSAxel Dörfler struct dhcp_option_cookie { 80fb81684fSAxel Dörfler dhcp_option_cookie() : state(0), file_has_options(false), server_name_has_options(false) {} 81fb81684fSAxel Dörfler 82fb81684fSAxel Dörfler const uint8* next; 83fb81684fSAxel Dörfler uint8 state; 84fb81684fSAxel Dörfler bool file_has_options; 85fb81684fSAxel Dörfler bool server_name_has_options; 86fb81684fSAxel Dörfler }; 87fb81684fSAxel Dörfler 88fb81684fSAxel Dörfler struct dhcp_message { 89fb81684fSAxel Dörfler dhcp_message(message_type type); 90fb81684fSAxel Dörfler 91fb81684fSAxel Dörfler uint8 opcode; 92fb81684fSAxel Dörfler uint8 hardware_type; 93fb81684fSAxel Dörfler uint8 hardware_address_length; 94fb81684fSAxel Dörfler uint8 hop_count; 95fb81684fSAxel Dörfler uint32 transaction_id; 96*46ff5400SAxel Dörfler uint16 seconds_since_start; 97fb81684fSAxel Dörfler uint16 flags; 98fb81684fSAxel Dörfler in_addr_t client_address; 99fb81684fSAxel Dörfler in_addr_t your_address; 100fb81684fSAxel Dörfler in_addr_t server_address; 101fb81684fSAxel Dörfler in_addr_t gateway_address; 102fb81684fSAxel Dörfler uint8 mac_address[16]; 103fb81684fSAxel Dörfler uint8 server_name[64]; 104fb81684fSAxel Dörfler uint8 file[128]; 105fb81684fSAxel Dörfler uint32 options_magic; 106fb81684fSAxel Dörfler uint8 options[1260]; 107fb81684fSAxel Dörfler 108fb81684fSAxel Dörfler size_t MinSize() const { return 576; } 109fb81684fSAxel Dörfler size_t Size() const; 110fb81684fSAxel Dörfler 111fb81684fSAxel Dörfler bool HasOptions() const; 112fb81684fSAxel Dörfler bool NextOption(dhcp_option_cookie& cookie, message_option& option, 113fb81684fSAxel Dörfler const uint8*& data, size_t& size) const; 114f9af6566SAxel Dörfler message_type Type() const; 115fb81684fSAxel Dörfler const uint8* LastOption() const; 116fb81684fSAxel Dörfler 117fb81684fSAxel Dörfler uint8* PutOption(uint8* options, message_option option); 118fb81684fSAxel Dörfler uint8* PutOption(uint8* options, message_option option, uint8 data); 119fb81684fSAxel Dörfler uint8* PutOption(uint8* options, message_option option, uint16 data); 120fb81684fSAxel Dörfler uint8* PutOption(uint8* options, message_option option, uint32 data); 121fb81684fSAxel Dörfler uint8* PutOption(uint8* options, message_option option, uint8* data, uint32 size); 122fb81684fSAxel Dörfler } _PACKED; 123fb81684fSAxel Dörfler 1240cf5e6acSAxel Dörfler #define DHCP_FLAG_BROADCAST 0x8000 125fb81684fSAxel Dörfler 126fb81684fSAxel Dörfler #define ARP_HARDWARE_TYPE_ETHER 1 127fb81684fSAxel Dörfler 1286cc7630fSAxel Dörfler const uint32 kMsgLeaseTime = 'lstm'; 1296cc7630fSAxel Dörfler 130fb81684fSAxel Dörfler 131fb81684fSAxel Dörfler dhcp_message::dhcp_message(message_type type) 132fb81684fSAxel Dörfler { 133fb81684fSAxel Dörfler memset(this, 0, sizeof(*this)); 134fb81684fSAxel Dörfler options_magic = htonl(OPTION_MAGIC); 135fb81684fSAxel Dörfler 136fb81684fSAxel Dörfler uint8* next = options; 137fb81684fSAxel Dörfler next = PutOption(next, OPTION_MESSAGE_TYPE, (uint8)type); 1380cf5e6acSAxel Dörfler next = PutOption(next, OPTION_MESSAGE_SIZE, (uint16)htons(sizeof(dhcp_message))); 139fb81684fSAxel Dörfler next = PutOption(next, OPTION_END); 140fb81684fSAxel Dörfler } 141fb81684fSAxel Dörfler 142fb81684fSAxel Dörfler 143fb81684fSAxel Dörfler bool 144fb81684fSAxel Dörfler dhcp_message::HasOptions() const 145fb81684fSAxel Dörfler { 146fb81684fSAxel Dörfler return options_magic == htonl(OPTION_MAGIC); 147fb81684fSAxel Dörfler } 148fb81684fSAxel Dörfler 149fb81684fSAxel Dörfler 150fb81684fSAxel Dörfler bool 151fb81684fSAxel Dörfler dhcp_message::NextOption(dhcp_option_cookie& cookie, 152fb81684fSAxel Dörfler message_option& option, const uint8*& data, size_t& size) const 153fb81684fSAxel Dörfler { 154fb81684fSAxel Dörfler if (cookie.state == 0) { 155fb81684fSAxel Dörfler if (!HasOptions()) 156fb81684fSAxel Dörfler return false; 157fb81684fSAxel Dörfler 158fb81684fSAxel Dörfler cookie.state++; 159fb81684fSAxel Dörfler cookie.next = options; 160fb81684fSAxel Dörfler } 161fb81684fSAxel Dörfler 162fb81684fSAxel Dörfler uint32 bytesLeft = 0; 163fb81684fSAxel Dörfler 164fb81684fSAxel Dörfler switch (cookie.state) { 165fb81684fSAxel Dörfler case 1: 166fb81684fSAxel Dörfler // options from "options" 167fb81684fSAxel Dörfler bytesLeft = sizeof(options) + cookie.next - options; 168fb81684fSAxel Dörfler break; 169fb81684fSAxel Dörfler 170fb81684fSAxel Dörfler case 2: 171fb81684fSAxel Dörfler // options from "file" 172fb81684fSAxel Dörfler bytesLeft = sizeof(options) + cookie.next - options; 173fb81684fSAxel Dörfler break; 174fb81684fSAxel Dörfler 175fb81684fSAxel Dörfler case 3: 176fb81684fSAxel Dörfler // options from "server_name" 177fb81684fSAxel Dörfler bytesLeft = sizeof(options) + cookie.next - options; 178fb81684fSAxel Dörfler break; 179fb81684fSAxel Dörfler } 180fb81684fSAxel Dörfler 181fb81684fSAxel Dörfler while (true) { 182fb81684fSAxel Dörfler if (bytesLeft == 0) { 183fb81684fSAxel Dörfler // TODO: suppport OPTION_OVERLOAD! 184fb81684fSAxel Dörfler cookie.state = 4; 185fb81684fSAxel Dörfler return false; 186fb81684fSAxel Dörfler } 187fb81684fSAxel Dörfler 188fb81684fSAxel Dörfler option = (message_option)cookie.next[0]; 189fb81684fSAxel Dörfler if (option == OPTION_END) { 190fb81684fSAxel Dörfler cookie.state = 4; 191fb81684fSAxel Dörfler return false; 192fb81684fSAxel Dörfler } else if (option == OPTION_PAD) { 193fb81684fSAxel Dörfler bytesLeft--; 194fb81684fSAxel Dörfler cookie.next++; 195fb81684fSAxel Dörfler continue; 196fb81684fSAxel Dörfler } 197fb81684fSAxel Dörfler 198fb81684fSAxel Dörfler size = cookie.next[1]; 199fb81684fSAxel Dörfler data = &cookie.next[2]; 200fb81684fSAxel Dörfler cookie.next += 2 + size; 201fb81684fSAxel Dörfler 202fb81684fSAxel Dörfler if (option == OPTION_OVERLOAD) { 203fb81684fSAxel Dörfler cookie.file_has_options = data[0] & 1; 204fb81684fSAxel Dörfler cookie.server_name_has_options = data[0] & 2; 205fb81684fSAxel Dörfler continue; 206fb81684fSAxel Dörfler } 207fb81684fSAxel Dörfler 208fb81684fSAxel Dörfler return true; 209fb81684fSAxel Dörfler } 210fb81684fSAxel Dörfler } 211fb81684fSAxel Dörfler 212fb81684fSAxel Dörfler 213f9af6566SAxel Dörfler message_type 214f9af6566SAxel Dörfler dhcp_message::Type() const 215f9af6566SAxel Dörfler { 216f9af6566SAxel Dörfler dhcp_option_cookie cookie; 217f9af6566SAxel Dörfler message_option option; 218f9af6566SAxel Dörfler const uint8* data; 219f9af6566SAxel Dörfler size_t size; 220f9af6566SAxel Dörfler while (NextOption(cookie, option, data, size)) { 221f9af6566SAxel Dörfler // iterate through all options 222f9af6566SAxel Dörfler if (option == OPTION_MESSAGE_TYPE) 223f9af6566SAxel Dörfler return (message_type)data[0]; 224f9af6566SAxel Dörfler } 225f9af6566SAxel Dörfler 226f9af6566SAxel Dörfler return DHCP_NONE; 227f9af6566SAxel Dörfler } 228f9af6566SAxel Dörfler 229f9af6566SAxel Dörfler 230fb81684fSAxel Dörfler const uint8* 231fb81684fSAxel Dörfler dhcp_message::LastOption() const 232fb81684fSAxel Dörfler { 233fb81684fSAxel Dörfler dhcp_option_cookie cookie; 234fb81684fSAxel Dörfler message_option option; 235fb81684fSAxel Dörfler const uint8* data; 236fb81684fSAxel Dörfler size_t size; 237fb81684fSAxel Dörfler while (NextOption(cookie, option, data, size)) { 238fb81684fSAxel Dörfler // iterate through all options 239fb81684fSAxel Dörfler } 240fb81684fSAxel Dörfler 241fb81684fSAxel Dörfler return cookie.next; 242fb81684fSAxel Dörfler } 243fb81684fSAxel Dörfler 244fb81684fSAxel Dörfler 245fb81684fSAxel Dörfler size_t 246fb81684fSAxel Dörfler dhcp_message::Size() const 247fb81684fSAxel Dörfler { 248fb81684fSAxel Dörfler const uint8* last = LastOption(); 249fb81684fSAxel Dörfler return sizeof(dhcp_message) - sizeof(options) + last + 1 - options; 250fb81684fSAxel Dörfler } 251fb81684fSAxel Dörfler 252fb81684fSAxel Dörfler 253fb81684fSAxel Dörfler uint8* 254fb81684fSAxel Dörfler dhcp_message::PutOption(uint8* options, message_option option) 255fb81684fSAxel Dörfler { 256fb81684fSAxel Dörfler options[0] = option; 257fb81684fSAxel Dörfler return options + 1; 258fb81684fSAxel Dörfler } 259fb81684fSAxel Dörfler 260fb81684fSAxel Dörfler 261fb81684fSAxel Dörfler uint8* 262fb81684fSAxel Dörfler dhcp_message::PutOption(uint8* options, message_option option, uint8 data) 263fb81684fSAxel Dörfler { 264fb81684fSAxel Dörfler return PutOption(options, option, &data, 1); 265fb81684fSAxel Dörfler } 266fb81684fSAxel Dörfler 267fb81684fSAxel Dörfler 268fb81684fSAxel Dörfler uint8* 269fb81684fSAxel Dörfler dhcp_message::PutOption(uint8* options, message_option option, uint16 data) 270fb81684fSAxel Dörfler { 271fb81684fSAxel Dörfler return PutOption(options, option, (uint8*)&data, sizeof(data)); 272fb81684fSAxel Dörfler } 273fb81684fSAxel Dörfler 274fb81684fSAxel Dörfler 275fb81684fSAxel Dörfler uint8* 276fb81684fSAxel Dörfler dhcp_message::PutOption(uint8* options, message_option option, uint32 data) 277fb81684fSAxel Dörfler { 278fb81684fSAxel Dörfler return PutOption(options, option, (uint8*)&data, sizeof(data)); 279fb81684fSAxel Dörfler } 280fb81684fSAxel Dörfler 281fb81684fSAxel Dörfler 282fb81684fSAxel Dörfler uint8* 283fb81684fSAxel Dörfler dhcp_message::PutOption(uint8* options, message_option option, uint8* data, uint32 size) 284fb81684fSAxel Dörfler { 285fb81684fSAxel Dörfler options[0] = option; 286fb81684fSAxel Dörfler options[1] = size; 287fb81684fSAxel Dörfler memcpy(&options[2], data, size); 288fb81684fSAxel Dörfler 289fb81684fSAxel Dörfler return options + 2 + size; 290fb81684fSAxel Dörfler } 291fb81684fSAxel Dörfler 292fb81684fSAxel Dörfler 293fb81684fSAxel Dörfler // #pragma mark - 294fb81684fSAxel Dörfler 295fb81684fSAxel Dörfler 296f9af6566SAxel Dörfler DHCPClient::DHCPClient(BMessenger target, const char* device) 297fb81684fSAxel Dörfler : BHandler("dhcp"), 2986cc7630fSAxel Dörfler fTarget(target), 29910cc12daSAxel Dörfler fDevice(device), 3006cc7630fSAxel Dörfler fConfiguration(kMsgConfigureInterface), 3016cc7630fSAxel Dörfler fRunner(NULL), 3026cc7630fSAxel Dörfler fLeaseTime(0) 303fb81684fSAxel Dörfler { 304*46ff5400SAxel Dörfler fStartTime = system_time(); 305*46ff5400SAxel Dörfler fTransactionID = (uint32)fStartTime; 306fb81684fSAxel Dörfler 3070ce7725eSAxel Dörfler fStatus = get_mac_address(device, fMAC); 308fb81684fSAxel Dörfler if (fStatus < B_OK) 309fb81684fSAxel Dörfler return; 310fb81684fSAxel Dörfler 3116cc7630fSAxel Dörfler memset(&fServer, 0, sizeof(struct sockaddr_in)); 3126cc7630fSAxel Dörfler fServer.sin_family = AF_INET; 3136cc7630fSAxel Dörfler fServer.sin_len = sizeof(struct sockaddr_in); 3146cc7630fSAxel Dörfler fServer.sin_port = htons(DHCP_SERVER_PORT); 3156cc7630fSAxel Dörfler } 3166cc7630fSAxel Dörfler 3176cc7630fSAxel Dörfler 3186cc7630fSAxel Dörfler DHCPClient::~DHCPClient() 3196cc7630fSAxel Dörfler { 3206cc7630fSAxel Dörfler if (fStatus != B_OK) 3216cc7630fSAxel Dörfler return; 3226cc7630fSAxel Dörfler 3236cc7630fSAxel Dörfler delete fRunner; 3240ce7725eSAxel Dörfler 325fb81684fSAxel Dörfler int socket = ::socket(AF_INET, SOCK_DGRAM, 0); 3266cc7630fSAxel Dörfler if (socket < 0) 327fb81684fSAxel Dörfler return; 3286cc7630fSAxel Dörfler 3296cc7630fSAxel Dörfler // release lease 3306cc7630fSAxel Dörfler 3316cc7630fSAxel Dörfler dhcp_message release(DHCP_RELEASE); 332*46ff5400SAxel Dörfler _PrepareMessage(release, BOUND); 3336cc7630fSAxel Dörfler 3346cc7630fSAxel Dörfler _SendMessage(socket, release, fServer); 3356cc7630fSAxel Dörfler close(socket); 336fb81684fSAxel Dörfler } 337fb81684fSAxel Dörfler 3386cc7630fSAxel Dörfler 3396cc7630fSAxel Dörfler status_t 3406cc7630fSAxel Dörfler DHCPClient::Initialize() 3416cc7630fSAxel Dörfler { 3426cc7630fSAxel Dörfler fStatus = _Negotiate(INIT); 3436cc7630fSAxel Dörfler printf("DHCP for %s, status: %s\n", fDevice.String(), strerror(fStatus)); 3446cc7630fSAxel Dörfler return fStatus; 3456cc7630fSAxel Dörfler } 3466cc7630fSAxel Dörfler 3476cc7630fSAxel Dörfler 3486cc7630fSAxel Dörfler status_t 3496cc7630fSAxel Dörfler DHCPClient::_Negotiate(dhcp_state state) 3506cc7630fSAxel Dörfler { 3516cc7630fSAxel Dörfler int socket = ::socket(AF_INET, SOCK_DGRAM, 0); 3526cc7630fSAxel Dörfler if (socket < 0) 3536cc7630fSAxel Dörfler return errno; 3546cc7630fSAxel Dörfler 355fb81684fSAxel Dörfler sockaddr_in local; 356fb81684fSAxel Dörfler memset(&local, 0, sizeof(struct sockaddr_in)); 357fb81684fSAxel Dörfler local.sin_family = AF_INET; 358fb81684fSAxel Dörfler local.sin_len = sizeof(struct sockaddr_in); 359fb81684fSAxel Dörfler local.sin_port = htons(DHCP_CLIENT_PORT); 360fb81684fSAxel Dörfler local.sin_addr.s_addr = INADDR_ANY; 361fb81684fSAxel Dörfler 362fb81684fSAxel Dörfler if (bind(socket, (struct sockaddr *)&local, sizeof(local)) < 0) { 363fb81684fSAxel Dörfler close(socket); 3646cc7630fSAxel Dörfler return errno; 365fb81684fSAxel Dörfler } 366fb81684fSAxel Dörfler 367fb81684fSAxel Dörfler sockaddr_in broadcast; 368fb81684fSAxel Dörfler memset(&broadcast, 0, sizeof(struct sockaddr_in)); 369fb81684fSAxel Dörfler broadcast.sin_family = AF_INET; 370fb81684fSAxel Dörfler broadcast.sin_len = sizeof(struct sockaddr_in); 371fb81684fSAxel Dörfler broadcast.sin_port = htons(DHCP_SERVER_PORT); 372fb81684fSAxel Dörfler broadcast.sin_addr.s_addr = INADDR_BROADCAST; 373fb81684fSAxel Dörfler 374fb81684fSAxel Dörfler int option = 1; 375fb81684fSAxel Dörfler setsockopt(socket, SOL_SOCKET, SO_BROADCAST, &option, sizeof(option)); 376fb81684fSAxel Dörfler 3776cc7630fSAxel Dörfler bigtime_t previousLeaseTime = fLeaseTime; 3786cc7630fSAxel Dörfler fLeaseTime = 0; 379*46ff5400SAxel Dörfler fRenewalTime = 0; 380*46ff5400SAxel Dörfler fRebindingTime = 0; 381fb81684fSAxel Dörfler 3826cc7630fSAxel Dörfler status_t status = B_ERROR; 3836cc7630fSAxel Dörfler time_t timeout; 3846cc7630fSAxel Dörfler uint32 tries; 3856cc7630fSAxel Dörfler _ResetTimeout(socket, timeout, tries); 3866cc7630fSAxel Dörfler 3876cc7630fSAxel Dörfler dhcp_message discover(DHCP_DISCOVER); 388*46ff5400SAxel Dörfler _PrepareMessage(discover, state); 3896cc7630fSAxel Dörfler 3906cc7630fSAxel Dörfler dhcp_message request(DHCP_REQUEST); 391*46ff5400SAxel Dörfler _PrepareMessage(request, state); 3926cc7630fSAxel Dörfler 393*46ff5400SAxel Dörfler // send discover/request message 3946cc7630fSAxel Dörfler status = _SendMessage(socket, state == INIT ? discover : request, 395*46ff5400SAxel Dörfler state != RENEWAL ? broadcast : fServer); 3966cc7630fSAxel Dörfler if (status < B_OK) { 397fb81684fSAxel Dörfler close(socket); 3986cc7630fSAxel Dörfler return status; 3996cc7630fSAxel Dörfler } 400fb81684fSAxel Dörfler 401f9af6566SAxel Dörfler // receive loop until we've got an offer and acknowledged it 402f9af6566SAxel Dörfler 403f9af6566SAxel Dörfler while (state != ACKNOWLEDGED) { 404f9af6566SAxel Dörfler char buffer[2048]; 405f9af6566SAxel Dörfler ssize_t bytesReceived = recvfrom(socket, buffer, sizeof(buffer), 406f9af6566SAxel Dörfler 0, NULL, NULL); 4076cc7630fSAxel Dörfler printf("recvfrom returned: %ld, %s\n", bytesReceived, strerror(errno)); 408f9af6566SAxel Dörfler if (bytesReceived == B_TIMED_OUT) { 409f9af6566SAxel Dörfler // depending on the state, we'll just try again 4106cc7630fSAxel Dörfler if (!_TimeoutShift(socket, timeout, tries)) { 4116cc7630fSAxel Dörfler close(socket); 4126cc7630fSAxel Dörfler return B_TIMED_OUT; 413f9af6566SAxel Dörfler } 414f9af6566SAxel Dörfler 415f9af6566SAxel Dörfler if (state == INIT) 4166cc7630fSAxel Dörfler status = _SendMessage(socket, discover, broadcast); 417*46ff5400SAxel Dörfler else 418*46ff5400SAxel Dörfler status = _SendMessage(socket, request, state != RENEWAL 4196cc7630fSAxel Dörfler ? broadcast : fServer); 420f9af6566SAxel Dörfler 4216cc7630fSAxel Dörfler if (status < B_OK) 422f9af6566SAxel Dörfler break; 423f9af6566SAxel Dörfler } else if (bytesReceived < B_OK) 424f9af6566SAxel Dörfler break; 425f9af6566SAxel Dörfler 426f9af6566SAxel Dörfler dhcp_message *message = (dhcp_message *)buffer; 427f9af6566SAxel Dörfler if (message->transaction_id != htonl(fTransactionID) 428f9af6566SAxel Dörfler || !message->HasOptions() 429f9af6566SAxel Dörfler || memcmp(message->mac_address, discover.mac_address, 430f9af6566SAxel Dörfler discover.hardware_address_length)) { 431f9af6566SAxel Dörfler // this message is not for us 432f9af6566SAxel Dörfler continue; 433f9af6566SAxel Dörfler } 434f9af6566SAxel Dörfler 435f9af6566SAxel Dörfler switch (message->Type()) { 436f9af6566SAxel Dörfler case DHCP_NONE: 437f9af6566SAxel Dörfler default: 438f9af6566SAxel Dörfler // ignore this message 439f9af6566SAxel Dörfler break; 440f9af6566SAxel Dörfler 441f9af6566SAxel Dörfler case DHCP_OFFER: 442f9af6566SAxel Dörfler { 443f9af6566SAxel Dörfler // first offer wins 444f9af6566SAxel Dörfler if (state != INIT) 445f9af6566SAxel Dörfler break; 446f9af6566SAxel Dörfler 447f9af6566SAxel Dörfler // collect interface options 448f9af6566SAxel Dörfler 449f9af6566SAxel Dörfler fAssignedAddress = message->your_address; 450f9af6566SAxel Dörfler 45110cc12daSAxel Dörfler fConfiguration.MakeEmpty(); 45210cc12daSAxel Dörfler fConfiguration.AddString("device", fDevice.String()); 453f9af6566SAxel Dörfler 454f9af6566SAxel Dörfler BMessage address; 455f9af6566SAxel Dörfler address.AddString("family", "inet"); 456f9af6566SAxel Dörfler address.AddString("address", _ToString(fAssignedAddress)); 4570ce7725eSAxel Dörfler _ParseOptions(*message, address); 458f9af6566SAxel Dörfler 45910cc12daSAxel Dörfler fConfiguration.AddMessage("address", &address); 460f9af6566SAxel Dörfler 46110cc12daSAxel Dörfler // request configuration from the server 462f9af6566SAxel Dörfler 4636cc7630fSAxel Dörfler _ResetTimeout(socket, timeout, tries); 464f9af6566SAxel Dörfler state = REQUESTING; 465*46ff5400SAxel Dörfler _PrepareMessage(request, state); 466f9af6566SAxel Dörfler 4676cc7630fSAxel Dörfler status = _SendMessage(socket, request, broadcast); 46810cc12daSAxel Dörfler // we're sending a broadcast so that all potential offers get an answer 46910cc12daSAxel Dörfler break; 470f9af6566SAxel Dörfler } 471f9af6566SAxel Dörfler 472f9af6566SAxel Dörfler case DHCP_ACK: 47310cc12daSAxel Dörfler { 474*46ff5400SAxel Dörfler if (state != REQUESTING && state != REBINDING && state != RENEWAL) 475f9af6566SAxel Dörfler continue; 476f9af6566SAxel Dörfler 4776cc7630fSAxel Dörfler // TODO: we might want to configure the stuff, don't we? 4786cc7630fSAxel Dörfler BMessage address; 4796cc7630fSAxel Dörfler _ParseOptions(*message, address); 4806cc7630fSAxel Dörfler // TODO: currently, only lease time and DNS is updated this way 4816cc7630fSAxel Dörfler 482f9af6566SAxel Dörfler // our address request has been acknowledged 483f9af6566SAxel Dörfler state = ACKNOWLEDGED; 48410cc12daSAxel Dörfler 48510cc12daSAxel Dörfler // configure interface 48610cc12daSAxel Dörfler BMessage reply; 4876cc7630fSAxel Dörfler fTarget.SendMessage(&fConfiguration, &reply); 48810cc12daSAxel Dörfler 48910cc12daSAxel Dörfler if (reply.FindInt32("status", &fStatus) != B_OK) 4906cc7630fSAxel Dörfler status = B_OK; 491f9af6566SAxel Dörfler break; 49210cc12daSAxel Dörfler } 493f9af6566SAxel Dörfler 494f9af6566SAxel Dörfler case DHCP_NACK: 495f9af6566SAxel Dörfler if (state != REQUESTING) 496f9af6566SAxel Dörfler continue; 497f9af6566SAxel Dörfler 498a552ec13SAxel Dörfler // try again (maybe we should prefer other servers if this happens more than once) 4996cc7630fSAxel Dörfler status = _SendMessage(socket, discover, broadcast); 5006cc7630fSAxel Dörfler if (status == B_OK) 501f9af6566SAxel Dörfler state = INIT; 502f9af6566SAxel Dörfler break; 503f9af6566SAxel Dörfler } 504f9af6566SAxel Dörfler } 505f9af6566SAxel Dörfler 506fb81684fSAxel Dörfler close(socket); 5076cc7630fSAxel Dörfler 5086cc7630fSAxel Dörfler if (status == B_OK && fLeaseTime > 0) { 5096cc7630fSAxel Dörfler // notify early enough when the lease is 510*46ff5400SAxel Dörfler if (fRenewalTime == 0) 511*46ff5400SAxel Dörfler fRenewalTime = fLeaseTime * 2/3; 512*46ff5400SAxel Dörfler if (fRebindingTime == 0) 513*46ff5400SAxel Dörfler fRebindingTime = fLeaseTime * 5/6; 514*46ff5400SAxel Dörfler 515*46ff5400SAxel Dörfler _RestartLease(fRenewalTime); 516*46ff5400SAxel Dörfler 517*46ff5400SAxel Dörfler bigtime_t now = system_time(); 518*46ff5400SAxel Dörfler fLeaseTime += now; 519*46ff5400SAxel Dörfler fRenewalTime += now; 520*46ff5400SAxel Dörfler fRebindingTime += now; 521*46ff5400SAxel Dörfler // make lease times absolute 522*46ff5400SAxel Dörfler } else { 5236cc7630fSAxel Dörfler fLeaseTime = previousLeaseTime; 524*46ff5400SAxel Dörfler bigtime_t now = system_time(); 525*46ff5400SAxel Dörfler fRenewalTime = (fLeaseTime - now) * 2/3 + now; 526*46ff5400SAxel Dörfler fRebindingTime = (fLeaseTime - now) * 5/6 + now; 527*46ff5400SAxel Dörfler } 5286cc7630fSAxel Dörfler 5296cc7630fSAxel Dörfler return status; 530fb81684fSAxel Dörfler } 531fb81684fSAxel Dörfler 532fb81684fSAxel Dörfler 5336cc7630fSAxel Dörfler void 5346cc7630fSAxel Dörfler DHCPClient::_RestartLease(bigtime_t leaseTime) 535fb81684fSAxel Dörfler { 5366cc7630fSAxel Dörfler if (leaseTime == 0) 537f9af6566SAxel Dörfler return; 538f9af6566SAxel Dörfler 5396cc7630fSAxel Dörfler BMessage lease(kMsgLeaseTime); 540*46ff5400SAxel Dörfler fRunner = new BMessageRunner(this, &lease, leaseTime, 1); 541f9af6566SAxel Dörfler } 542f9af6566SAxel Dörfler 543f9af6566SAxel Dörfler 544f9af6566SAxel Dörfler void 5450ce7725eSAxel Dörfler DHCPClient::_ParseOptions(dhcp_message& message, BMessage& address) 5460ce7725eSAxel Dörfler { 5470ce7725eSAxel Dörfler dhcp_option_cookie cookie; 5480ce7725eSAxel Dörfler message_option option; 5490ce7725eSAxel Dörfler const uint8* data; 5500ce7725eSAxel Dörfler size_t size; 5510ce7725eSAxel Dörfler while (message.NextOption(cookie, option, data, size)) { 5520ce7725eSAxel Dörfler // iterate through all options 5530ce7725eSAxel Dörfler switch (option) { 5540ce7725eSAxel Dörfler case OPTION_ROUTER_ADDRESS: 5550ce7725eSAxel Dörfler address.AddString("gateway", _ToString(data)); 5560ce7725eSAxel Dörfler break; 5570ce7725eSAxel Dörfler case OPTION_SUBNET_MASK: 5580ce7725eSAxel Dörfler address.AddString("mask", _ToString(data)); 5590ce7725eSAxel Dörfler break; 5605782c5a3SAxel Dörfler case OPTION_BROADCAST_ADDRESS: 5615782c5a3SAxel Dörfler address.AddString("broadcast", _ToString(data)); 5625782c5a3SAxel Dörfler break; 5630ce7725eSAxel Dörfler case OPTION_DOMAIN_NAME_SERVER: 564a552ec13SAxel Dörfler { 565a552ec13SAxel Dörfler // TODO: for now, we write it just out to /etc/resolv.conf 566a552ec13SAxel Dörfler FILE* file = fopen("/etc/resolv.conf", "w"); 5670ce7725eSAxel Dörfler for (uint32 i = 0; i < size / 4; i++) { 5680ce7725eSAxel Dörfler printf("DNS: %s\n", _ToString(&data[i*4]).String()); 569a552ec13SAxel Dörfler if (file != NULL) 570a552ec13SAxel Dörfler fprintf(file, "nameserver %s\n", _ToString(&data[i*4]).String()); 5710ce7725eSAxel Dörfler } 572a552ec13SAxel Dörfler fclose(file); 5730ce7725eSAxel Dörfler break; 574a552ec13SAxel Dörfler } 5750ce7725eSAxel Dörfler case OPTION_SERVER_ADDRESS: 5760ce7725eSAxel Dörfler fServer.sin_addr.s_addr = *(in_addr_t*)data; 5770ce7725eSAxel Dörfler break; 578*46ff5400SAxel Dörfler 5790ce7725eSAxel Dörfler case OPTION_ADDRESS_LEASE_TIME: 5800ce7725eSAxel Dörfler printf("lease time of %lu seconds\n", htonl(*(uint32*)data)); 5816cc7630fSAxel Dörfler fLeaseTime = htonl(*(uint32*)data) * 1000000LL; 5820ce7725eSAxel Dörfler break; 5831a4e8e7bSAxel Dörfler case OPTION_RENEWAL_TIME: 584*46ff5400SAxel Dörfler printf("renewal time of %lu seconds\n", 585*46ff5400SAxel Dörfler htonl(*(uint32*)data)); 586*46ff5400SAxel Dörfler fRenewalTime = htonl(*(uint32*)data) * 1000000LL; 587*46ff5400SAxel Dörfler break; 5881a4e8e7bSAxel Dörfler case OPTION_REBINDING_TIME: 589*46ff5400SAxel Dörfler printf("rebinding time of %lu seconds\n", 590*46ff5400SAxel Dörfler htonl(*(uint32*)data)); 591*46ff5400SAxel Dörfler fRebindingTime = htonl(*(uint32*)data) * 1000000LL; 5921a4e8e7bSAxel Dörfler break; 5931a4e8e7bSAxel Dörfler 5940ce7725eSAxel Dörfler case OPTION_HOST_NAME: 5955782c5a3SAxel Dörfler { 5960ce7725eSAxel Dörfler char name[256]; 5970ce7725eSAxel Dörfler memcpy(name, data, size); 5980ce7725eSAxel Dörfler name[size] = '\0'; 5990ce7725eSAxel Dörfler printf("DHCP host name: \"%s\"\n", name); 6000ce7725eSAxel Dörfler break; 6015782c5a3SAxel Dörfler } 6025782c5a3SAxel Dörfler 6035782c5a3SAxel Dörfler case OPTION_DOMAIN_NAME: 6045782c5a3SAxel Dörfler { 6055782c5a3SAxel Dörfler char name[256]; 6065782c5a3SAxel Dörfler memcpy(name, data, size); 6075782c5a3SAxel Dörfler name[size] = '\0'; 6085782c5a3SAxel Dörfler printf("DHCP domain name: \"%s\"\n", name); 6095782c5a3SAxel Dörfler break; 6105782c5a3SAxel Dörfler } 6110ce7725eSAxel Dörfler 6120ce7725eSAxel Dörfler case OPTION_MESSAGE_TYPE: 6130ce7725eSAxel Dörfler break; 6140ce7725eSAxel Dörfler 6150ce7725eSAxel Dörfler default: 6160ce7725eSAxel Dörfler printf("unknown option %lu\n", (uint32)option); 6170ce7725eSAxel Dörfler break; 6180ce7725eSAxel Dörfler } 6190ce7725eSAxel Dörfler } 6200ce7725eSAxel Dörfler } 6210ce7725eSAxel Dörfler 6220ce7725eSAxel Dörfler 6230ce7725eSAxel Dörfler void 624*46ff5400SAxel Dörfler DHCPClient::_PrepareMessage(dhcp_message& message, dhcp_state state) 6250ce7725eSAxel Dörfler { 6260ce7725eSAxel Dörfler message.opcode = BOOT_REQUEST; 6270ce7725eSAxel Dörfler message.hardware_type = ARP_HARDWARE_TYPE_ETHER; 6280ce7725eSAxel Dörfler message.hardware_address_length = 6; 6290ce7725eSAxel Dörfler message.transaction_id = htonl(fTransactionID); 630*46ff5400SAxel Dörfler message.seconds_since_start = htons(min_c((fStartTime - system_time()) / 1000000LL, 65535)); 6310ce7725eSAxel Dörfler memcpy(message.mac_address, fMAC, 6); 6320ce7725eSAxel Dörfler 633*46ff5400SAxel Dörfler message_type type = message.Type(); 634*46ff5400SAxel Dörfler 635*46ff5400SAxel Dörfler switch (type) { 6360ce7725eSAxel Dörfler case DHCP_REQUEST: 6370ce7725eSAxel Dörfler case DHCP_RELEASE: 6380ce7725eSAxel Dörfler { 6390ce7725eSAxel Dörfler // add server identifier option 6400ce7725eSAxel Dörfler uint8* next = message.options; 6410ce7725eSAxel Dörfler next = message.PutOption(next, OPTION_MESSAGE_TYPE, (uint8)DHCP_REQUEST); 6420ce7725eSAxel Dörfler next = message.PutOption(next, OPTION_MESSAGE_SIZE, 6430cf5e6acSAxel Dörfler (uint16)htons(sizeof(dhcp_message))); 6440ce7725eSAxel Dörfler next = message.PutOption(next, OPTION_SERVER_ADDRESS, 6450ce7725eSAxel Dörfler (uint32)fServer.sin_addr.s_addr); 646*46ff5400SAxel Dörfler 647*46ff5400SAxel Dörfler // In RENEWAL or REBINDING state, we must set the client_address field, and not 648*46ff5400SAxel Dörfler // use OPTION_REQUEST_IP_ADDRESS for DHCP_REQUEST messages 649*46ff5400SAxel Dörfler if (type == DHCP_REQUEST && (state == INIT || state == REQUESTING)) 6500ce7725eSAxel Dörfler next = message.PutOption(next, OPTION_REQUEST_IP_ADDRESS, fAssignedAddress); 651*46ff5400SAxel Dörfler else 652*46ff5400SAxel Dörfler message.client_address = fAssignedAddress; 653*46ff5400SAxel Dörfler 6540ce7725eSAxel Dörfler next = message.PutOption(next, OPTION_END); 6550ce7725eSAxel Dörfler break; 6560ce7725eSAxel Dörfler } 6570ce7725eSAxel Dörfler 6580ce7725eSAxel Dörfler default: 6590ce7725eSAxel Dörfler // the default options are fine 6600ce7725eSAxel Dörfler break; 6610ce7725eSAxel Dörfler } 6620ce7725eSAxel Dörfler } 6630ce7725eSAxel Dörfler 6640ce7725eSAxel Dörfler 6650ce7725eSAxel Dörfler void 6666cc7630fSAxel Dörfler DHCPClient::_ResetTimeout(int socket, time_t& timeout, uint32& tries) 667f9af6566SAxel Dörfler { 6686cc7630fSAxel Dörfler timeout = DEFAULT_TIMEOUT; 6696cc7630fSAxel Dörfler tries = 0; 670f9af6566SAxel Dörfler 671f9af6566SAxel Dörfler struct timeval value; 6726cc7630fSAxel Dörfler value.tv_sec = timeout; 673f9af6566SAxel Dörfler value.tv_usec = 0; 674f9af6566SAxel Dörfler setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &value, sizeof(value)); 675f9af6566SAxel Dörfler } 676f9af6566SAxel Dörfler 677f9af6566SAxel Dörfler 678f9af6566SAxel Dörfler bool 6796cc7630fSAxel Dörfler DHCPClient::_TimeoutShift(int socket, time_t& timeout, uint32& tries) 680f9af6566SAxel Dörfler { 6816cc7630fSAxel Dörfler timeout += timeout; 6826cc7630fSAxel Dörfler if (timeout > MAX_TIMEOUT) { 6836cc7630fSAxel Dörfler timeout = DEFAULT_TIMEOUT; 684f9af6566SAxel Dörfler 6856cc7630fSAxel Dörfler if (++tries > 2) 686f9af6566SAxel Dörfler return false; 687f9af6566SAxel Dörfler } 6886cc7630fSAxel Dörfler printf("DHCP timeout shift: %lu secs (try %lu)\n", timeout, tries); 689f9af6566SAxel Dörfler 690f9af6566SAxel Dörfler struct timeval value; 6916cc7630fSAxel Dörfler value.tv_sec = timeout; 692f9af6566SAxel Dörfler value.tv_usec = 0; 693f9af6566SAxel Dörfler setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &value, sizeof(value)); 694f9af6566SAxel Dörfler 695f9af6566SAxel Dörfler return true; 696f9af6566SAxel Dörfler } 697f9af6566SAxel Dörfler 698f9af6566SAxel Dörfler 699f9af6566SAxel Dörfler BString 700f9af6566SAxel Dörfler DHCPClient::_ToString(const uint8* data) const 701f9af6566SAxel Dörfler { 702f9af6566SAxel Dörfler BString target = inet_ntoa(*(in_addr*)data); 703f9af6566SAxel Dörfler return target; 704f9af6566SAxel Dörfler } 705f9af6566SAxel Dörfler 706f9af6566SAxel Dörfler 707f9af6566SAxel Dörfler BString 708f9af6566SAxel Dörfler DHCPClient::_ToString(in_addr_t address) const 709f9af6566SAxel Dörfler { 710f9af6566SAxel Dörfler BString target = inet_ntoa(*(in_addr*)&address); 711f9af6566SAxel Dörfler return target; 712f9af6566SAxel Dörfler } 713f9af6566SAxel Dörfler 714f9af6566SAxel Dörfler 715f9af6566SAxel Dörfler status_t 716f9af6566SAxel Dörfler DHCPClient::_SendMessage(int socket, dhcp_message& message, sockaddr_in& address) const 717f9af6566SAxel Dörfler { 718f9af6566SAxel Dörfler ssize_t bytesSent = sendto(socket, &message, message.Size(), 719f9af6566SAxel Dörfler address.sin_addr.s_addr == INADDR_BROADCAST ? MSG_BCAST : 0, 720f9af6566SAxel Dörfler (struct sockaddr*)&address, sizeof(sockaddr_in)); 721f9af6566SAxel Dörfler if (bytesSent < 0) 722f9af6566SAxel Dörfler return errno; 723f9af6566SAxel Dörfler 724f9af6566SAxel Dörfler return B_OK; 725fb81684fSAxel Dörfler } 726fb81684fSAxel Dörfler 727fb81684fSAxel Dörfler 728*46ff5400SAxel Dörfler dhcp_state 729*46ff5400SAxel Dörfler DHCPClient::_CurrentState() const 730*46ff5400SAxel Dörfler { 731*46ff5400SAxel Dörfler bigtime_t now = system_time(); 732*46ff5400SAxel Dörfler 733*46ff5400SAxel Dörfler if (now > fLeaseTime || fStatus < B_OK) 734*46ff5400SAxel Dörfler return INIT; 735*46ff5400SAxel Dörfler if (now >= fRebindingTime) 736*46ff5400SAxel Dörfler return REBINDING; 737*46ff5400SAxel Dörfler if (now >= fRenewalTime) 738*46ff5400SAxel Dörfler return RENEWAL; 739*46ff5400SAxel Dörfler 740*46ff5400SAxel Dörfler return BOUND; 741*46ff5400SAxel Dörfler } 742*46ff5400SAxel Dörfler 743*46ff5400SAxel Dörfler 744fb81684fSAxel Dörfler void 745fb81684fSAxel Dörfler DHCPClient::MessageReceived(BMessage* message) 746fb81684fSAxel Dörfler { 7476cc7630fSAxel Dörfler switch (message->what) { 7486cc7630fSAxel Dörfler case kMsgLeaseTime: 749*46ff5400SAxel Dörfler { 750*46ff5400SAxel Dörfler dhcp_state state = _CurrentState(); 751*46ff5400SAxel Dörfler 752*46ff5400SAxel Dörfler bigtime_t next; 753*46ff5400SAxel Dörfler if (_Negotiate(state) == B_OK) { 754*46ff5400SAxel Dörfler switch (state) { 755*46ff5400SAxel Dörfler case RENEWAL: 756*46ff5400SAxel Dörfler next = fRebindingTime; 7576cc7630fSAxel Dörfler break; 758*46ff5400SAxel Dörfler case REBINDING: 759*46ff5400SAxel Dörfler default: 760*46ff5400SAxel Dörfler next = fRenewalTime; 761*46ff5400SAxel Dörfler break; 762*46ff5400SAxel Dörfler } 763*46ff5400SAxel Dörfler } else { 764*46ff5400SAxel Dörfler switch (state) { 765*46ff5400SAxel Dörfler case RENEWAL: 766*46ff5400SAxel Dörfler next = (fLeaseTime - fRebindingTime) / 4 + system_time(); 767*46ff5400SAxel Dörfler break; 768*46ff5400SAxel Dörfler case REBINDING: 769*46ff5400SAxel Dörfler default: 770*46ff5400SAxel Dörfler next = (fLeaseTime - fRenewalTime) / 4 + system_time(); 771*46ff5400SAxel Dörfler break; 772*46ff5400SAxel Dörfler } 773*46ff5400SAxel Dörfler } 774*46ff5400SAxel Dörfler 775*46ff5400SAxel Dörfler _RestartLease(next - system_time()); 776*46ff5400SAxel Dörfler break; 777*46ff5400SAxel Dörfler } 7786cc7630fSAxel Dörfler 7796cc7630fSAxel Dörfler default: 780fb81684fSAxel Dörfler BHandler::MessageReceived(message); 7816cc7630fSAxel Dörfler break; 7826cc7630fSAxel Dörfler } 783fb81684fSAxel Dörfler } 784fb81684fSAxel Dörfler 785