1fb81684fSAxel Dörfler /* 2669afb50SAxel Dörfler * Copyright 2006-2011, 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 70f87f52fSStephan Aßmus * Vegard Wærp, vegarwa@online.no 8d5d2fdf0SPhilippe Houdoin * Philippe Houdoin, <phoudoin at haiku-os dot org> 9fb81684fSAxel Dörfler */ 10fb81684fSAxel Dörfler 11fb81684fSAxel Dörfler 12fb81684fSAxel Dörfler #include "DHCPClient.h" 13fb81684fSAxel Dörfler 14f9af6566SAxel Dörfler #include <Message.h> 156cc7630fSAxel Dörfler #include <MessageRunner.h> 16af074561SAxel Dörfler #include <NetworkDevice.h> 17af074561SAxel Dörfler #include <NetworkInterface.h> 18f9af6566SAxel Dörfler 196972b91eSPawel Dziepak #include <algorithm> 20f9af6566SAxel Dörfler #include <arpa/inet.h> 21fb81684fSAxel Dörfler #include <errno.h> 22fb81684fSAxel Dörfler #include <stdio.h> 23fb81684fSAxel Dörfler #include <string.h> 24d09c1f8eSPhilippe Houdoin #include <stdlib.h> 25293ed4feSAxel Dörfler #include <syslog.h> 2636bde12dSAxel Dörfler #include <sys/sockio.h> 27f9af6566SAxel Dörfler #include <sys/time.h> 285f25cf08SSiarzhuk Zharski #include <unistd.h> 29fb81684fSAxel Dörfler 304ac66051SAxel Dörfler #include <Debug.h> 314ac66051SAxel Dörfler #include <Message.h> 324ac66051SAxel Dörfler #include <MessageRunner.h> 334ac66051SAxel Dörfler 344ac66051SAxel Dörfler #include "NetServer.h" 354ac66051SAxel Dörfler 36fb81684fSAxel Dörfler 37fb81684fSAxel Dörfler // See RFC 2131 for DHCP, see RFC 1533 for BOOTP/DHCP options 38fb81684fSAxel Dörfler 39fb81684fSAxel Dörfler #define DHCP_CLIENT_PORT 68 40fb81684fSAxel Dörfler #define DHCP_SERVER_PORT 67 41fb81684fSAxel Dörfler 426972b91eSPawel Dziepak #define DEFAULT_TIMEOUT 4 // secs 436972b91eSPawel Dziepak #define MAX_TIMEOUT 64 // secs 446972b91eSPawel Dziepak 456972b91eSPawel Dziepak #define MAX_RETRIES 5 46f9af6566SAxel Dörfler 47fb81684fSAxel Dörfler enum message_opcode { 48fb81684fSAxel Dörfler BOOT_REQUEST = 1, 49fb81684fSAxel Dörfler BOOT_REPLY 50fb81684fSAxel Dörfler }; 51fb81684fSAxel Dörfler 52fb81684fSAxel Dörfler enum message_option { 53fb81684fSAxel Dörfler OPTION_MAGIC = 0x63825363, 54fb81684fSAxel Dörfler 55fb81684fSAxel Dörfler // generic options 56fb81684fSAxel Dörfler OPTION_PAD = 0, 57f9af6566SAxel Dörfler OPTION_END = 255, 58f9af6566SAxel Dörfler OPTION_SUBNET_MASK = 1, 5965186fecSAxel Dörfler OPTION_TIME_OFFSET = 2, 60f9af6566SAxel Dörfler OPTION_ROUTER_ADDRESS = 3, 61f9af6566SAxel Dörfler OPTION_DOMAIN_NAME_SERVER = 6, 62f9af6566SAxel Dörfler OPTION_HOST_NAME = 12, 635782c5a3SAxel Dörfler OPTION_DOMAIN_NAME = 15, 64d5d2fdf0SPhilippe Houdoin OPTION_MAX_DATAGRAM_SIZE = 22, 65d5d2fdf0SPhilippe Houdoin OPTION_INTERFACE_MTU = 26, 66fb81684fSAxel Dörfler OPTION_BROADCAST_ADDRESS = 28, 67d5d2fdf0SPhilippe Houdoin OPTION_NETWORK_TIME_PROTOCOL_SERVERS = 42, 6865186fecSAxel Dörfler OPTION_NETBIOS_NAME_SERVER = 44, 6965186fecSAxel Dörfler OPTION_NETBIOS_SCOPE = 47, 70fb81684fSAxel Dörfler 71fb81684fSAxel Dörfler // DHCP specific options 72fb81684fSAxel Dörfler OPTION_REQUEST_IP_ADDRESS = 50, 73fb81684fSAxel Dörfler OPTION_ADDRESS_LEASE_TIME = 51, 74fb81684fSAxel Dörfler OPTION_OVERLOAD = 52, 75fb81684fSAxel Dörfler OPTION_MESSAGE_TYPE = 53, 76cefe2a40SPhilippe Houdoin OPTION_SERVER_ADDRESS = 54, 77fb81684fSAxel Dörfler OPTION_REQUEST_PARAMETERS = 55, 78fb81684fSAxel Dörfler OPTION_ERROR_MESSAGE = 56, 79d5d2fdf0SPhilippe Houdoin OPTION_MAX_MESSAGE_SIZE = 57, 80fb81684fSAxel Dörfler OPTION_RENEWAL_TIME = 58, 81fb81684fSAxel Dörfler OPTION_REBINDING_TIME = 59, 82fb81684fSAxel Dörfler OPTION_CLASS_IDENTIFIER = 60, 83fb81684fSAxel Dörfler OPTION_CLIENT_IDENTIFIER = 61, 84fb81684fSAxel Dörfler }; 85fb81684fSAxel Dörfler 86fb81684fSAxel Dörfler enum message_type { 87f9af6566SAxel Dörfler DHCP_NONE = 0, 88d6f83df4SAugustin Cavalier DHCP_DISCOVER = 1, 89d6f83df4SAugustin Cavalier DHCP_OFFER = 2, 90d6f83df4SAugustin Cavalier DHCP_REQUEST = 3, 91d6f83df4SAugustin Cavalier DHCP_DECLINE = 4, 92d6f83df4SAugustin Cavalier DHCP_ACK = 5, 93d6f83df4SAugustin Cavalier DHCP_NACK = 6, 94d6f83df4SAugustin Cavalier DHCP_RELEASE = 7, 95d6f83df4SAugustin Cavalier DHCP_INFORM = 8 96fb81684fSAxel Dörfler }; 97fb81684fSAxel Dörfler 98fb81684fSAxel Dörfler struct dhcp_option_cookie { 9915ab0bcfSAxel Dörfler dhcp_option_cookie() 10015ab0bcfSAxel Dörfler : 10115ab0bcfSAxel Dörfler state(0), 10215ab0bcfSAxel Dörfler file_has_options(false), 10315ab0bcfSAxel Dörfler server_name_has_options(false) 10415ab0bcfSAxel Dörfler { 10515ab0bcfSAxel Dörfler } 106fb81684fSAxel Dörfler 107fb81684fSAxel Dörfler const uint8* next; 108fb81684fSAxel Dörfler uint8 state; 109fb81684fSAxel Dörfler bool file_has_options; 110fb81684fSAxel Dörfler bool server_name_has_options; 111fb81684fSAxel Dörfler }; 112fb81684fSAxel Dörfler 113fb81684fSAxel Dörfler struct dhcp_message { 114fb81684fSAxel Dörfler dhcp_message(message_type type); 115fb81684fSAxel Dörfler 116fb81684fSAxel Dörfler uint8 opcode; 117fb81684fSAxel Dörfler uint8 hardware_type; 118fb81684fSAxel Dörfler uint8 hardware_address_length; 119fb81684fSAxel Dörfler uint8 hop_count; 120fb81684fSAxel Dörfler uint32 transaction_id; 12146ff5400SAxel Dörfler uint16 seconds_since_start; 122fb81684fSAxel Dörfler uint16 flags; 123fb81684fSAxel Dörfler in_addr_t client_address; 124fb81684fSAxel Dörfler in_addr_t your_address; 125fb81684fSAxel Dörfler in_addr_t server_address; 126fb81684fSAxel Dörfler in_addr_t gateway_address; 127fb81684fSAxel Dörfler uint8 mac_address[16]; 128fb81684fSAxel Dörfler uint8 server_name[64]; 129fb81684fSAxel Dörfler uint8 file[128]; 130fb81684fSAxel Dörfler uint32 options_magic; 131fb81684fSAxel Dörfler uint8 options[1260]; 132fb81684fSAxel Dörfler 133fb81684fSAxel Dörfler size_t MinSize() const { return 576; } 134fb81684fSAxel Dörfler size_t Size() const; 135fb81684fSAxel Dörfler 136fb81684fSAxel Dörfler bool HasOptions() const; 137fb81684fSAxel Dörfler bool NextOption(dhcp_option_cookie& cookie, message_option& option, 138fb81684fSAxel Dörfler const uint8*& data, size_t& size) const; 13903f6ab7fSPhilippe Houdoin const uint8* FindOption(message_option which) const; 140fb81684fSAxel Dörfler const uint8* LastOption() const; 14103f6ab7fSPhilippe Houdoin message_type Type() const; 142fb81684fSAxel Dörfler 143a073ba1aSHugo Santos uint8* PrepareMessage(uint8 type); 144fb81684fSAxel Dörfler uint8* PutOption(uint8* options, message_option option); 145fb81684fSAxel Dörfler uint8* PutOption(uint8* options, message_option option, uint8 data); 146fb81684fSAxel Dörfler uint8* PutOption(uint8* options, message_option option, uint16 data); 147fb81684fSAxel Dörfler uint8* PutOption(uint8* options, message_option option, uint32 data); 14815ab0bcfSAxel Dörfler uint8* PutOption(uint8* options, message_option option, const uint8* data, 14915ab0bcfSAxel Dörfler uint32 size); 15037a68cb1SAxel Dörfler uint8* FinishOptions(uint8* options); 1514ac66051SAxel Dörfler 1524ac66051SAxel Dörfler static const char* TypeToString(message_type type); 153fb81684fSAxel Dörfler } _PACKED; 154fb81684fSAxel Dörfler 1550cf5e6acSAxel Dörfler #define DHCP_FLAG_BROADCAST 0x8000 156fb81684fSAxel Dörfler 157fb81684fSAxel Dörfler #define ARP_HARDWARE_TYPE_ETHER 1 158fb81684fSAxel Dörfler 1596cc7630fSAxel Dörfler const uint32 kMsgLeaseTime = 'lstm'; 1606cc7630fSAxel Dörfler 16137a68cb1SAxel Dörfler static const uint8 kRequestParameters[] = { 16265186fecSAxel Dörfler OPTION_SUBNET_MASK, OPTION_ROUTER_ADDRESS, 163bfd479b3SAxel Dörfler OPTION_DOMAIN_NAME_SERVER, OPTION_BROADCAST_ADDRESS, 164bfd479b3SAxel Dörfler OPTION_DOMAIN_NAME 1655d4d5313SHugo Santos }; 1665d4d5313SHugo Santos 167fb81684fSAxel Dörfler 168fb81684fSAxel Dörfler dhcp_message::dhcp_message(message_type type) 169fb81684fSAxel Dörfler { 170fb4527d2SPhilippe Houdoin // ASSERT(this == offsetof(this, opcode)); 171fb81684fSAxel Dörfler memset(this, 0, sizeof(*this)); 172fb81684fSAxel Dörfler options_magic = htonl(OPTION_MAGIC); 173fb81684fSAxel Dörfler 174a073ba1aSHugo Santos uint8* next = PrepareMessage(type); 175a073ba1aSHugo Santos FinishOptions(next); 176fb81684fSAxel Dörfler } 177fb81684fSAxel Dörfler 178fb81684fSAxel Dörfler 179fb81684fSAxel Dörfler bool 180fb81684fSAxel Dörfler dhcp_message::HasOptions() const 181fb81684fSAxel Dörfler { 182fb81684fSAxel Dörfler return options_magic == htonl(OPTION_MAGIC); 183fb81684fSAxel Dörfler } 184fb81684fSAxel Dörfler 185fb81684fSAxel Dörfler 186fb81684fSAxel Dörfler bool 187fb81684fSAxel Dörfler dhcp_message::NextOption(dhcp_option_cookie& cookie, 188fb81684fSAxel Dörfler message_option& option, const uint8*& data, size_t& size) const 189fb81684fSAxel Dörfler { 190fb81684fSAxel Dörfler if (!HasOptions()) 191fb81684fSAxel Dörfler return false; 192fb81684fSAxel Dörfler 193fb4527d2SPhilippe Houdoin if (cookie.state == 0) { 194fb81684fSAxel Dörfler cookie.state++; 195fb81684fSAxel Dörfler cookie.next = options; 196fb81684fSAxel Dörfler } 197fb81684fSAxel Dörfler 198fb81684fSAxel Dörfler uint32 bytesLeft = 0; 199fb81684fSAxel Dörfler 200fb81684fSAxel Dörfler switch (cookie.state) { 201fb81684fSAxel Dörfler case 1: 202ce36f054SPhilippe Houdoin // options from "options" 203fb4527d2SPhilippe Houdoin bytesLeft = sizeof(options) - (cookie.next - options); 204fb81684fSAxel Dörfler break; 205fb81684fSAxel Dörfler 206fb81684fSAxel Dörfler case 2: 207fb81684fSAxel Dörfler // options from "file" 208fb4527d2SPhilippe Houdoin bytesLeft = sizeof(file) - (cookie.next - file); 209fb81684fSAxel Dörfler break; 210fb81684fSAxel Dörfler 211fb81684fSAxel Dörfler case 3: 212fb81684fSAxel Dörfler // options from "server_name" 213fb4527d2SPhilippe Houdoin bytesLeft = sizeof(server_name) - (cookie.next - server_name); 214fb81684fSAxel Dörfler break; 215fb81684fSAxel Dörfler } 216fb81684fSAxel Dörfler 217fb81684fSAxel Dörfler while (true) { 218fb81684fSAxel Dörfler if (bytesLeft == 0) { 219fb4527d2SPhilippe Houdoin cookie.state++; 220fb4527d2SPhilippe Houdoin 221ce36f054SPhilippe Houdoin // handle option overload in file and/or server_name fields. 222fb4527d2SPhilippe Houdoin switch (cookie.state) { 223fb4527d2SPhilippe Houdoin case 2: 224d5d2fdf0SPhilippe Houdoin // options from "file" field 225fb4527d2SPhilippe Houdoin if (cookie.file_has_options) { 226fb4527d2SPhilippe Houdoin bytesLeft = sizeof(file); 227fb4527d2SPhilippe Houdoin cookie.next = file; 228fb4527d2SPhilippe Houdoin } 229fb4527d2SPhilippe Houdoin break; 230fb4527d2SPhilippe Houdoin 231fb4527d2SPhilippe Houdoin case 3: 232d5d2fdf0SPhilippe Houdoin // options from "server_name" field 233fb4527d2SPhilippe Houdoin if (cookie.server_name_has_options) { 234fb4527d2SPhilippe Houdoin bytesLeft = sizeof(server_name); 235fb4527d2SPhilippe Houdoin cookie.next = server_name; 236fb4527d2SPhilippe Houdoin } 237fb4527d2SPhilippe Houdoin break; 238fb4527d2SPhilippe Houdoin 239d5d2fdf0SPhilippe Houdoin default: 240ce36f054SPhilippe Houdoin // no more place to look for options 241d5d2fdf0SPhilippe Houdoin // if last option is not OPTION_END, 242d5d2fdf0SPhilippe Houdoin // there is no space left for other option! 243d5d2fdf0SPhilippe Houdoin if (option != OPTION_END) 244d5d2fdf0SPhilippe Houdoin cookie.next = NULL; 245fb81684fSAxel Dörfler return false; 246fb81684fSAxel Dörfler } 247fb81684fSAxel Dörfler 248fb4527d2SPhilippe Houdoin if (bytesLeft == 0) { 249d5d2fdf0SPhilippe Houdoin // no options in this state, try next one 250fb4527d2SPhilippe Houdoin continue; 251fb4527d2SPhilippe Houdoin } 252fb4527d2SPhilippe Houdoin } 253fb4527d2SPhilippe Houdoin 254fb81684fSAxel Dörfler option = (message_option)cookie.next[0]; 255fb81684fSAxel Dörfler if (option == OPTION_END) { 256fb4527d2SPhilippe Houdoin bytesLeft = 0; 257fb4527d2SPhilippe Houdoin continue; 258fb81684fSAxel Dörfler } else if (option == OPTION_PAD) { 259fb81684fSAxel Dörfler bytesLeft--; 260fb81684fSAxel Dörfler cookie.next++; 261fb81684fSAxel Dörfler continue; 262fb81684fSAxel Dörfler } 263fb81684fSAxel Dörfler 264fb81684fSAxel Dörfler size = cookie.next[1]; 265fb81684fSAxel Dörfler data = &cookie.next[2]; 266fb81684fSAxel Dörfler cookie.next += 2 + size; 267fb4527d2SPhilippe Houdoin bytesLeft -= 2 + size; 268fb81684fSAxel Dörfler 269fb81684fSAxel Dörfler if (option == OPTION_OVERLOAD) { 270fb81684fSAxel Dörfler cookie.file_has_options = data[0] & 1; 271fb81684fSAxel Dörfler cookie.server_name_has_options = data[0] & 2; 272fb81684fSAxel Dörfler continue; 273fb81684fSAxel Dörfler } 274fb81684fSAxel Dörfler 275fb81684fSAxel Dörfler return true; 276fb81684fSAxel Dörfler } 277fb81684fSAxel Dörfler } 278fb81684fSAxel Dörfler 279fb81684fSAxel Dörfler 28003f6ab7fSPhilippe Houdoin const uint8* 28103f6ab7fSPhilippe Houdoin dhcp_message::FindOption(message_option which) const 282f9af6566SAxel Dörfler { 283f9af6566SAxel Dörfler dhcp_option_cookie cookie; 284f9af6566SAxel Dörfler message_option option; 285f9af6566SAxel Dörfler const uint8* data; 286f9af6566SAxel Dörfler size_t size; 287f9af6566SAxel Dörfler while (NextOption(cookie, option, data, size)) { 288f9af6566SAxel Dörfler // iterate through all options 28903f6ab7fSPhilippe Houdoin if (option == which) 29003f6ab7fSPhilippe Houdoin return data; 291f9af6566SAxel Dörfler } 292f9af6566SAxel Dörfler 29303f6ab7fSPhilippe Houdoin return NULL; 294f9af6566SAxel Dörfler } 295f9af6566SAxel Dörfler 296f9af6566SAxel Dörfler 297fb81684fSAxel Dörfler const uint8* 298fb81684fSAxel Dörfler dhcp_message::LastOption() const 299fb81684fSAxel Dörfler { 300fb81684fSAxel Dörfler dhcp_option_cookie cookie; 301fb81684fSAxel Dörfler message_option option; 302fb81684fSAxel Dörfler const uint8* data; 303fb81684fSAxel Dörfler size_t size; 304fb81684fSAxel Dörfler while (NextOption(cookie, option, data, size)) { 305fb81684fSAxel Dörfler // iterate through all options 306fb81684fSAxel Dörfler } 307fb81684fSAxel Dörfler 308fb81684fSAxel Dörfler return cookie.next; 309fb81684fSAxel Dörfler } 310fb81684fSAxel Dörfler 311fb81684fSAxel Dörfler 31203f6ab7fSPhilippe Houdoin message_type 31303f6ab7fSPhilippe Houdoin dhcp_message::Type() const 31403f6ab7fSPhilippe Houdoin { 31503f6ab7fSPhilippe Houdoin const uint8* data = FindOption(OPTION_MESSAGE_TYPE); 31603f6ab7fSPhilippe Houdoin if (data) 31703f6ab7fSPhilippe Houdoin return (message_type)data[0]; 31803f6ab7fSPhilippe Houdoin 31903f6ab7fSPhilippe Houdoin return DHCP_NONE; 32003f6ab7fSPhilippe Houdoin } 32103f6ab7fSPhilippe Houdoin 32203f6ab7fSPhilippe Houdoin 323fb81684fSAxel Dörfler size_t 324fb81684fSAxel Dörfler dhcp_message::Size() const 325fb81684fSAxel Dörfler { 326fb81684fSAxel Dörfler const uint8* last = LastOption(); 327d5d2fdf0SPhilippe Houdoin 328d5d2fdf0SPhilippe Houdoin if (last < options) { 329d5d2fdf0SPhilippe Houdoin // if last option is stored above "options" field, it means 330d5d2fdf0SPhilippe Houdoin // the whole options field and message is already filled... 331d5d2fdf0SPhilippe Houdoin return sizeof(dhcp_message); 332d5d2fdf0SPhilippe Houdoin } 333d5d2fdf0SPhilippe Houdoin 334fb81684fSAxel Dörfler return sizeof(dhcp_message) - sizeof(options) + last + 1 - options; 335fb81684fSAxel Dörfler } 336fb81684fSAxel Dörfler 337fb81684fSAxel Dörfler 338fb81684fSAxel Dörfler uint8* 339a073ba1aSHugo Santos dhcp_message::PrepareMessage(uint8 type) 340a073ba1aSHugo Santos { 341a073ba1aSHugo Santos uint8* next = options; 342a073ba1aSHugo Santos next = PutOption(next, OPTION_MESSAGE_TYPE, type); 343d5d2fdf0SPhilippe Houdoin next = PutOption(next, OPTION_MAX_MESSAGE_SIZE, 34415ab0bcfSAxel Dörfler (uint16)htons(sizeof(dhcp_message))); 345a073ba1aSHugo Santos return next; 346a073ba1aSHugo Santos } 347a073ba1aSHugo Santos 34865186fecSAxel Dörfler 349a073ba1aSHugo Santos uint8* 350fb81684fSAxel Dörfler dhcp_message::PutOption(uint8* options, message_option option) 351fb81684fSAxel Dörfler { 352fb81684fSAxel Dörfler options[0] = option; 353fb81684fSAxel Dörfler return options + 1; 354fb81684fSAxel Dörfler } 355fb81684fSAxel Dörfler 356fb81684fSAxel Dörfler 357fb81684fSAxel Dörfler uint8* 358fb81684fSAxel Dörfler dhcp_message::PutOption(uint8* options, message_option option, uint8 data) 359fb81684fSAxel Dörfler { 360fb81684fSAxel Dörfler return PutOption(options, option, &data, 1); 361fb81684fSAxel Dörfler } 362fb81684fSAxel Dörfler 363fb81684fSAxel Dörfler 364fb81684fSAxel Dörfler uint8* 365fb81684fSAxel Dörfler dhcp_message::PutOption(uint8* options, message_option option, uint16 data) 366fb81684fSAxel Dörfler { 367fb81684fSAxel Dörfler return PutOption(options, option, (uint8*)&data, sizeof(data)); 368fb81684fSAxel Dörfler } 369fb81684fSAxel Dörfler 370fb81684fSAxel Dörfler 371fb81684fSAxel Dörfler uint8* 372fb81684fSAxel Dörfler dhcp_message::PutOption(uint8* options, message_option option, uint32 data) 373fb81684fSAxel Dörfler { 374fb81684fSAxel Dörfler return PutOption(options, option, (uint8*)&data, sizeof(data)); 375fb81684fSAxel Dörfler } 376fb81684fSAxel Dörfler 377fb81684fSAxel Dörfler 378fb81684fSAxel Dörfler uint8* 37915ab0bcfSAxel Dörfler dhcp_message::PutOption(uint8* options, message_option option, 38015ab0bcfSAxel Dörfler const uint8* data, uint32 size) 381fb81684fSAxel Dörfler { 382d5d2fdf0SPhilippe Houdoin // TODO: check enough space is available 383d5d2fdf0SPhilippe Houdoin 384fb81684fSAxel Dörfler options[0] = option; 385fb81684fSAxel Dörfler options[1] = size; 386fb81684fSAxel Dörfler memcpy(&options[2], data, size); 387fb81684fSAxel Dörfler 388fb81684fSAxel Dörfler return options + 2 + size; 389fb81684fSAxel Dörfler } 390fb81684fSAxel Dörfler 391fb81684fSAxel Dörfler 392a073ba1aSHugo Santos uint8* 39337a68cb1SAxel Dörfler dhcp_message::FinishOptions(uint8* options) 394a073ba1aSHugo Santos { 39537a68cb1SAxel Dörfler return PutOption(options, OPTION_END); 396a073ba1aSHugo Santos } 397a073ba1aSHugo Santos 398a073ba1aSHugo Santos 3994ac66051SAxel Dörfler /*static*/ const char* 4004ac66051SAxel Dörfler dhcp_message::TypeToString(message_type type) 4014ac66051SAxel Dörfler { 4024ac66051SAxel Dörfler switch (type) { 4034ac66051SAxel Dörfler #define CASE(x) case x: return #x; 4044ac66051SAxel Dörfler CASE(DHCP_NONE) 4054ac66051SAxel Dörfler CASE(DHCP_DISCOVER) 4064ac66051SAxel Dörfler CASE(DHCP_OFFER) 4074ac66051SAxel Dörfler CASE(DHCP_REQUEST) 4084ac66051SAxel Dörfler CASE(DHCP_DECLINE) 4094ac66051SAxel Dörfler CASE(DHCP_ACK) 4104ac66051SAxel Dörfler CASE(DHCP_NACK) 4114ac66051SAxel Dörfler CASE(DHCP_RELEASE) 4124ac66051SAxel Dörfler CASE(DHCP_INFORM) 4134ac66051SAxel Dörfler #undef CASE 4144ac66051SAxel Dörfler } 4154ac66051SAxel Dörfler 4164ac66051SAxel Dörfler return "<unknown>"; 4174ac66051SAxel Dörfler } 4184ac66051SAxel Dörfler 4194ac66051SAxel Dörfler 420fb81684fSAxel Dörfler // #pragma mark - 421fb81684fSAxel Dörfler 422fb81684fSAxel Dörfler 423f9af6566SAxel Dörfler DHCPClient::DHCPClient(BMessenger target, const char* device) 424af074561SAxel Dörfler : 425af074561SAxel Dörfler AutoconfigClient("dhcp", target, device), 4266cc7630fSAxel Dörfler fConfiguration(kMsgConfigureInterface), 42716e8f13aSAxel Dörfler fResolverConfiguration(kMsgConfigureResolver), 4286cc7630fSAxel Dörfler fRunner(NULL), 4290892800fSPhilippe Houdoin fAssignedAddress(0), 430af074561SAxel Dörfler fServer(AF_INET, NULL, DHCP_SERVER_PORT), 431c1264353SStephan Aßmus fLeaseTime(0) 432fb81684fSAxel Dörfler { 4336972b91eSPawel Dziepak fTransactionID = (uint32)system_time() ^ rand(); 4347ca2da76SFredrik Holmqvist 435af074561SAxel Dörfler BNetworkAddress link; 436af074561SAxel Dörfler BNetworkInterface interface(device); 437af074561SAxel Dörfler fStatus = interface.GetHardwareAddress(link); 438af074561SAxel Dörfler if (fStatus != B_OK) 439fb81684fSAxel Dörfler return; 440fb81684fSAxel Dörfler 441af074561SAxel Dörfler memcpy(fMAC, link.LinkLevelAddress(), sizeof(fMAC)); 442293ed4feSAxel Dörfler 4430892800fSPhilippe Houdoin if ((interface.Flags() & IFF_AUTO_CONFIGURED) != 0) { 4440892800fSPhilippe Houdoin // Check for interface previous auto-configured address, if any. 4450892800fSPhilippe Houdoin BNetworkInterfaceAddress interfaceAddress; 4460892800fSPhilippe Houdoin int index = interface.FindFirstAddress(AF_INET); 4470892800fSPhilippe Houdoin if (index >= 0 4480892800fSPhilippe Houdoin && interface.GetAddressAt(index, interfaceAddress) == B_OK) { 4490892800fSPhilippe Houdoin BNetworkAddress address = interfaceAddress.Address(); 4500892800fSPhilippe Houdoin const sockaddr_in& addr = (sockaddr_in&)address.SockAddr(); 4510892800fSPhilippe Houdoin fAssignedAddress = addr.sin_addr.s_addr; 452b6ba1daaSPhilippe Houdoin 453b6ba1daaSPhilippe Houdoin if ((ntohl(fAssignedAddress) & IN_CLASSB_NET) == 0xa9fe0000) { 454b6ba1daaSPhilippe Houdoin // previous auto-configured address is a link-local one: 455b6ba1daaSPhilippe Houdoin // there is no point asking a DHCP server to renew such 456b6ba1daaSPhilippe Houdoin // server-less assigned address... 457b6ba1daaSPhilippe Houdoin fAssignedAddress = 0; 458b6ba1daaSPhilippe Houdoin } 4590892800fSPhilippe Houdoin } 4600892800fSPhilippe Houdoin } 4610892800fSPhilippe Houdoin 462293ed4feSAxel Dörfler openlog_thread("DHCP", 0, LOG_DAEMON); 4636cc7630fSAxel Dörfler } 4646cc7630fSAxel Dörfler 4656cc7630fSAxel Dörfler 4666cc7630fSAxel Dörfler DHCPClient::~DHCPClient() 4676cc7630fSAxel Dörfler { 4686cc7630fSAxel Dörfler if (fStatus != B_OK) 4696cc7630fSAxel Dörfler return; 4706cc7630fSAxel Dörfler 4716cc7630fSAxel Dörfler delete fRunner; 4720ce7725eSAxel Dörfler 473fb81684fSAxel Dörfler int socket = ::socket(AF_INET, SOCK_DGRAM, 0); 4746cc7630fSAxel Dörfler if (socket < 0) 475fb81684fSAxel Dörfler return; 4766cc7630fSAxel Dörfler 4776cc7630fSAxel Dörfler // release lease 4786cc7630fSAxel Dörfler 4796cc7630fSAxel Dörfler dhcp_message release(DHCP_RELEASE); 48046ff5400SAxel Dörfler _PrepareMessage(release, BOUND); 4816cc7630fSAxel Dörfler 4826cc7630fSAxel Dörfler _SendMessage(socket, release, fServer); 4836cc7630fSAxel Dörfler close(socket); 484293ed4feSAxel Dörfler 485293ed4feSAxel Dörfler closelog_thread(); 486fb81684fSAxel Dörfler } 487fb81684fSAxel Dörfler 4886cc7630fSAxel Dörfler 4896cc7630fSAxel Dörfler status_t 4906cc7630fSAxel Dörfler DHCPClient::Initialize() 4916cc7630fSAxel Dörfler { 4920892800fSPhilippe Houdoin fStatus = _Negotiate(fAssignedAddress == 0 ? INIT : INIT_REBOOT); 4930892800fSPhilippe Houdoin syslog(LOG_DEBUG, "%s: DHCP status = %s\n", Device(), strerror(fStatus)); 4946cc7630fSAxel Dörfler return fStatus; 4956cc7630fSAxel Dörfler } 4966cc7630fSAxel Dörfler 4976cc7630fSAxel Dörfler 4986cc7630fSAxel Dörfler status_t 4996cc7630fSAxel Dörfler DHCPClient::_Negotiate(dhcp_state state) 5006cc7630fSAxel Dörfler { 5016972b91eSPawel Dziepak if (state == BOUND) 5026972b91eSPawel Dziepak return B_OK; 5036972b91eSPawel Dziepak 5046972b91eSPawel Dziepak fStartTime = system_time(); 5056972b91eSPawel Dziepak fTransactionID++; 5066972b91eSPawel Dziepak 5076972b91eSPawel Dziepak char hostName[MAXHOSTNAMELEN]; 5086972b91eSPawel Dziepak if (gethostname(hostName, MAXHOSTNAMELEN) == 0) 5096972b91eSPawel Dziepak fHostName.SetTo(hostName, MAXHOSTNAMELEN); 5106972b91eSPawel Dziepak else 5116972b91eSPawel Dziepak fHostName.Truncate(0); 5126972b91eSPawel Dziepak 5136cc7630fSAxel Dörfler int socket = ::socket(AF_INET, SOCK_DGRAM, 0); 5146cc7630fSAxel Dörfler if (socket < 0) 5156cc7630fSAxel Dörfler return errno; 5166cc7630fSAxel Dörfler 5172b829b04SBruno G. Albuquerque // Enable reusing the port. This is needed in case there is more 5182b829b04SBruno G. Albuquerque // than 1 interface that needs to be configured. Note that the only reason 5192b829b04SBruno G. Albuquerque // this works is because there is code below to bind to a specific 5202b829b04SBruno G. Albuquerque // interface. 5212b829b04SBruno G. Albuquerque int option = 1; 5222b829b04SBruno G. Albuquerque setsockopt(socket, SOL_SOCKET, SO_REUSEPORT, &option, sizeof(option)); 5232b829b04SBruno G. Albuquerque 5246972b91eSPawel Dziepak BNetworkAddress local; 5256972b91eSPawel Dziepak local.SetToWildcard(AF_INET, DHCP_CLIENT_PORT); 5266972b91eSPawel Dziepak 5276972b91eSPawel Dziepak option = 1; 5286972b91eSPawel Dziepak setsockopt(socket, SOL_SOCKET, SO_BROADCAST, &option, sizeof(option)); 5296972b91eSPawel Dziepak 53077206143SAxel Dörfler if (bind(socket, local, local.Length()) < 0) { 531fb81684fSAxel Dörfler close(socket); 5326cc7630fSAxel Dörfler return errno; 533fb81684fSAxel Dörfler } 534fb81684fSAxel Dörfler 5356cc7630fSAxel Dörfler bigtime_t previousLeaseTime = fLeaseTime; 536fb81684fSAxel Dörfler 5376972b91eSPawel Dziepak status_t status = B_OK; 5380892800fSPhilippe Houdoin while (state != BOUND) { 5396972b91eSPawel Dziepak status = _StateTransition(socket, state); 5406972b91eSPawel Dziepak if (status != B_OK && (state == SELECTING || state == REBOOTING)) 5416972b91eSPawel Dziepak break; 5426972b91eSPawel Dziepak } 5436972b91eSPawel Dziepak 5446cc7630fSAxel Dörfler close(socket); 5456972b91eSPawel Dziepak 5466972b91eSPawel Dziepak if (fLeaseTime == 0) 5476972b91eSPawel Dziepak fLeaseTime = previousLeaseTime; 5486972b91eSPawel Dziepak if (fLeaseTime == 0) 5496972b91eSPawel Dziepak fLeaseTime = 60; 5506972b91eSPawel Dziepak 5516972b91eSPawel Dziepak if (fRenewalTime == 0) 5526972b91eSPawel Dziepak fRenewalTime = fLeaseTime / 2; 5536972b91eSPawel Dziepak if (fRebindingTime == 0) 5546972b91eSPawel Dziepak fRebindingTime = fLeaseTime * 7 / 8; 5556972b91eSPawel Dziepak fLeaseTime += fRequestTime; 5566972b91eSPawel Dziepak fRenewalTime += fRequestTime; 5576972b91eSPawel Dziepak fRebindingTime += fRequestTime; 5586972b91eSPawel Dziepak _RestartLease(fRenewalTime); 5596972b91eSPawel Dziepak 5606972b91eSPawel Dziepak fStatus = status; 5616972b91eSPawel Dziepak if (status) 5626972b91eSPawel Dziepak return status; 5636972b91eSPawel Dziepak 5646972b91eSPawel Dziepak // configure interface 5656972b91eSPawel Dziepak BMessage reply; 5666972b91eSPawel Dziepak status = Target().SendMessage(&fConfiguration, &reply); 5676972b91eSPawel Dziepak if (status == B_OK) 5686972b91eSPawel Dziepak status = reply.FindInt32("status", &fStatus); 5696972b91eSPawel Dziepak 5706972b91eSPawel Dziepak // configure resolver 5716972b91eSPawel Dziepak reply.MakeEmpty(); 5726972b91eSPawel Dziepak fResolverConfiguration.AddString("device", Device()); 5736972b91eSPawel Dziepak status = Target().SendMessage(&fResolverConfiguration, &reply); 5746972b91eSPawel Dziepak if (status == B_OK) 5756972b91eSPawel Dziepak status = reply.FindInt32("status", &fStatus); 5766972b91eSPawel Dziepak return status; 577f9af6566SAxel Dörfler } 578f9af6566SAxel Dörfler 579b6ba1daaSPhilippe Houdoin 5806972b91eSPawel Dziepak status_t 5816972b91eSPawel Dziepak DHCPClient::_GotMessage(dhcp_state& state, dhcp_message* message) 582f9af6566SAxel Dörfler { 5836972b91eSPawel Dziepak switch (state) { 5846972b91eSPawel Dziepak case SELECTING: 5856972b91eSPawel Dziepak if (message->Type() == DHCP_OFFER) { 5866972b91eSPawel Dziepak state = REQUESTING; 587f9af6566SAxel Dörfler 588f9af6566SAxel Dörfler fAssignedAddress = message->your_address; 58969b5cacbSPhilippe Houdoin syslog(LOG_INFO, " your_address: %s\n", 59069b5cacbSPhilippe Houdoin _AddressToString(fAssignedAddress).String()); 591f9af6566SAxel Dörfler 59210cc12daSAxel Dörfler fConfiguration.MakeEmpty(); 593293ed4feSAxel Dörfler fConfiguration.AddString("device", Device()); 5940892800fSPhilippe Houdoin fConfiguration.AddBool("auto_configured", true); 595f9af6566SAxel Dörfler 596f9af6566SAxel Dörfler BMessage address; 597f9af6566SAxel Dörfler address.AddString("family", "inet"); 598d5d2fdf0SPhilippe Houdoin address.AddString("address", _AddressToString(fAssignedAddress)); 59916e8f13aSAxel Dörfler fResolverConfiguration.MakeEmpty(); 60016e8f13aSAxel Dörfler _ParseOptions(*message, address, fResolverConfiguration); 601f9af6566SAxel Dörfler 60210cc12daSAxel Dörfler fConfiguration.AddMessage("address", &address); 6036972b91eSPawel Dziepak return B_OK; 604f9af6566SAxel Dörfler } 605f9af6566SAxel Dörfler 6066972b91eSPawel Dziepak return B_BAD_VALUE; 607f9af6566SAxel Dörfler 6086972b91eSPawel Dziepak case REBOOTING: 6096972b91eSPawel Dziepak case REBINDING: 6106972b91eSPawel Dziepak case RENEWING: 6116972b91eSPawel Dziepak case REQUESTING: 6126972b91eSPawel Dziepak if (message->Type() == DHCP_ACK) { 6136cc7630fSAxel Dörfler // TODO: we might want to configure the stuff, don't we? 6146cc7630fSAxel Dörfler BMessage address; 61516e8f13aSAxel Dörfler fResolverConfiguration.MakeEmpty(); 61616e8f13aSAxel Dörfler _ParseOptions(*message, address, fResolverConfiguration); 6174ac66051SAxel Dörfler // TODO: currently, only lease time and DNS is updated this 6184ac66051SAxel Dörfler // way 6196cc7630fSAxel Dörfler 620f9af6566SAxel Dörfler // our address request has been acknowledged 6210892800fSPhilippe Houdoin state = BOUND; 62210cc12daSAxel Dörfler 6236972b91eSPawel Dziepak return B_OK; 62410cc12daSAxel Dörfler } 625f9af6566SAxel Dörfler 6266972b91eSPawel Dziepak if (message->Type() == DHCP_NACK) { 6270892800fSPhilippe Houdoin // server reject our request on previous assigned address 6280892800fSPhilippe Houdoin // back to square one... 6290892800fSPhilippe Houdoin fAssignedAddress = 0; 6306972b91eSPawel Dziepak state = INIT; 6316972b91eSPawel Dziepak return B_OK; 6320892800fSPhilippe Houdoin } 6330892800fSPhilippe Houdoin 6346972b91eSPawel Dziepak default: 6356972b91eSPawel Dziepak return B_BAD_VALUE; 6366972b91eSPawel Dziepak } 6376972b91eSPawel Dziepak } 6386972b91eSPawel Dziepak 6396972b91eSPawel Dziepak 6406972b91eSPawel Dziepak status_t 6416972b91eSPawel Dziepak DHCPClient::_StateTransition(int socket, dhcp_state& state) 6426972b91eSPawel Dziepak { 6436972b91eSPawel Dziepak if (state == INIT) { 6446972b91eSPawel Dziepak // The local interface does not have an address yet, bind the socket 6456972b91eSPawel Dziepak // to the device directly. 6466972b91eSPawel Dziepak BNetworkDevice device(Device()); 6476972b91eSPawel Dziepak int index = device.Index(); 6486972b91eSPawel Dziepak 6496972b91eSPawel Dziepak setsockopt(socket, SOL_SOCKET, SO_BINDTODEVICE, &index, sizeof(int)); 6506972b91eSPawel Dziepak } 6516972b91eSPawel Dziepak 6526972b91eSPawel Dziepak BNetworkAddress broadcast; 6536972b91eSPawel Dziepak broadcast.SetToBroadcast(AF_INET, DHCP_SERVER_PORT); 6546972b91eSPawel Dziepak 6556972b91eSPawel Dziepak time_t timeout; 6566972b91eSPawel Dziepak uint32 tries; 6576972b91eSPawel Dziepak _ResetTimeout(socket, state, timeout, tries); 6586972b91eSPawel Dziepak 6596972b91eSPawel Dziepak dhcp_message discover(DHCP_DISCOVER); 6606972b91eSPawel Dziepak _PrepareMessage(discover, state); 6616972b91eSPawel Dziepak 6626972b91eSPawel Dziepak dhcp_message request(DHCP_REQUEST); 6636972b91eSPawel Dziepak _PrepareMessage(request, state); 6646972b91eSPawel Dziepak 6656972b91eSPawel Dziepak bool skipRequest = false; 6666972b91eSPawel Dziepak dhcp_state originalState = state; 6676972b91eSPawel Dziepak fRequestTime = system_time(); 6686972b91eSPawel Dziepak while (true) { 6696972b91eSPawel Dziepak if (!skipRequest) { 6706972b91eSPawel Dziepak _SendMessage(socket, originalState == INIT ? discover : request, 6716972b91eSPawel Dziepak originalState == RENEWING ? fServer : broadcast); 6726972b91eSPawel Dziepak 6736972b91eSPawel Dziepak if (originalState == INIT) 6746972b91eSPawel Dziepak state = SELECTING; 6756972b91eSPawel Dziepak else if (originalState == INIT_REBOOT) 6766972b91eSPawel Dziepak state = REBOOTING; 6776972b91eSPawel Dziepak } 6786972b91eSPawel Dziepak 6796972b91eSPawel Dziepak char buffer[2048]; 6806972b91eSPawel Dziepak struct sockaddr_in from; 6816972b91eSPawel Dziepak socklen_t fromLength = sizeof(from); 6826972b91eSPawel Dziepak 6836972b91eSPawel Dziepak ssize_t bytesReceived = recvfrom(socket, buffer, sizeof(buffer), 6846972b91eSPawel Dziepak 0, (struct sockaddr*)&from, &fromLength); 6856972b91eSPawel Dziepak if (bytesReceived < 0 && errno == B_TIMED_OUT) { 6866972b91eSPawel Dziepak // depending on the state, we'll just try again 6876972b91eSPawel Dziepak if (!_TimeoutShift(socket, state, timeout, tries)) 6886972b91eSPawel Dziepak return B_TIMED_OUT; 6896972b91eSPawel Dziepak skipRequest = false; 6906972b91eSPawel Dziepak continue; 6916972b91eSPawel Dziepak } else if (bytesReceived < 0) 6926972b91eSPawel Dziepak return errno; 6936972b91eSPawel Dziepak 6946972b91eSPawel Dziepak skipRequest = true; 6956972b91eSPawel Dziepak dhcp_message* message = (dhcp_message*)buffer; 6966972b91eSPawel Dziepak if (message->transaction_id != htonl(fTransactionID) 6976972b91eSPawel Dziepak || !message->HasOptions() 6986972b91eSPawel Dziepak || memcmp(message->mac_address, discover.mac_address, 6996972b91eSPawel Dziepak discover.hardware_address_length)) { 7006972b91eSPawel Dziepak // this message is not for us 7016972b91eSPawel Dziepak continue; 7026972b91eSPawel Dziepak } 7036972b91eSPawel Dziepak 7046972b91eSPawel Dziepak syslog(LOG_DEBUG, "%s: Received %s from %s\n", 7056972b91eSPawel Dziepak Device(), dhcp_message::TypeToString(message->Type()), 7066972b91eSPawel Dziepak _AddressToString(from.sin_addr.s_addr).String()); 7076972b91eSPawel Dziepak 7086972b91eSPawel Dziepak if (_GotMessage(state, message) == B_OK) 709f9af6566SAxel Dörfler break; 710f9af6566SAxel Dörfler } 711f9af6566SAxel Dörfler 7126972b91eSPawel Dziepak return B_OK; 713fb81684fSAxel Dörfler } 714fb81684fSAxel Dörfler 715fb81684fSAxel Dörfler 7166cc7630fSAxel Dörfler void 7176cc7630fSAxel Dörfler DHCPClient::_RestartLease(bigtime_t leaseTime) 718fb81684fSAxel Dörfler { 7196cc7630fSAxel Dörfler if (leaseTime == 0) 720f9af6566SAxel Dörfler return; 721f9af6566SAxel Dörfler 7226cc7630fSAxel Dörfler BMessage lease(kMsgLeaseTime); 7236972b91eSPawel Dziepak fRunner = new BMessageRunner(this, &lease, leaseTime - system_time(), 1); 724f9af6566SAxel Dörfler } 725f9af6566SAxel Dörfler 726f9af6566SAxel Dörfler 727f9af6566SAxel Dörfler void 72816e8f13aSAxel Dörfler DHCPClient::_ParseOptions(dhcp_message& message, BMessage& address, 72916e8f13aSAxel Dörfler BMessage& resolverConfiguration) 7300ce7725eSAxel Dörfler { 7310ce7725eSAxel Dörfler dhcp_option_cookie cookie; 7320ce7725eSAxel Dörfler message_option option; 7330ce7725eSAxel Dörfler const uint8* data; 7340ce7725eSAxel Dörfler size_t size; 7350ce7725eSAxel Dörfler while (message.NextOption(cookie, option, data, size)) { 7360ce7725eSAxel Dörfler // iterate through all options 7370ce7725eSAxel Dörfler switch (option) { 7380ce7725eSAxel Dörfler case OPTION_ROUTER_ADDRESS: 739cefe2a40SPhilippe Houdoin syslog(LOG_DEBUG, " gateway: %s\n", 74069b5cacbSPhilippe Houdoin _AddressToString(data).String()); 741d5d2fdf0SPhilippe Houdoin address.AddString("gateway", _AddressToString(data)); 7420ce7725eSAxel Dörfler break; 7430ce7725eSAxel Dörfler case OPTION_SUBNET_MASK: 744cefe2a40SPhilippe Houdoin syslog(LOG_DEBUG, " subnet: %s\n", 74569b5cacbSPhilippe Houdoin _AddressToString(data).String()); 746d5d2fdf0SPhilippe Houdoin address.AddString("mask", _AddressToString(data)); 7470ce7725eSAxel Dörfler break; 7485782c5a3SAxel Dörfler case OPTION_BROADCAST_ADDRESS: 749cefe2a40SPhilippe Houdoin syslog(LOG_DEBUG, " broadcast: %s\n", 75069b5cacbSPhilippe Houdoin _AddressToString(data).String()); 751d5d2fdf0SPhilippe Houdoin address.AddString("broadcast", _AddressToString(data)); 7525782c5a3SAxel Dörfler break; 7530ce7725eSAxel Dörfler case OPTION_DOMAIN_NAME_SERVER: 754a552ec13SAxel Dörfler { 7550ce7725eSAxel Dörfler for (uint32 i = 0; i < size / 4; i++) { 756cefe2a40SPhilippe Houdoin syslog(LOG_DEBUG, " nameserver[%d]: %s\n", i, 757d5d2fdf0SPhilippe Houdoin _AddressToString(&data[i * 4]).String()); 75816e8f13aSAxel Dörfler resolverConfiguration.AddString("nameserver", 759d5d2fdf0SPhilippe Houdoin _AddressToString(&data[i * 4]).String()); 76015ab0bcfSAxel Dörfler } 76116e8f13aSAxel Dörfler resolverConfiguration.AddInt32("nameserver_count", 76216e8f13aSAxel Dörfler size / 4); 7630ce7725eSAxel Dörfler break; 764a552ec13SAxel Dörfler } 765cefe2a40SPhilippe Houdoin case OPTION_SERVER_ADDRESS: 766*f9ab315eSAugustin Cavalier { 767cefe2a40SPhilippe Houdoin syslog(LOG_DEBUG, " server: %s\n", 76869b5cacbSPhilippe Houdoin _AddressToString(data).String()); 769*f9ab315eSAugustin Cavalier status_t status = fServer.SetAddress(*(in_addr_t*)data); 770*f9ab315eSAugustin Cavalier if (status != B_OK) { 771*f9ab315eSAugustin Cavalier syslog(LOG_ERR, " BNetworkAddress::SetAddress failed with %s!\n", 772*f9ab315eSAugustin Cavalier strerror(status)); 773*f9ab315eSAugustin Cavalier fServer.Unset(); 774*f9ab315eSAugustin Cavalier } 7750ce7725eSAxel Dörfler break; 776*f9ab315eSAugustin Cavalier } 77746ff5400SAxel Dörfler 7780ce7725eSAxel Dörfler case OPTION_ADDRESS_LEASE_TIME: 779cefe2a40SPhilippe Houdoin syslog(LOG_DEBUG, " lease time: %lu seconds\n", 78069b5cacbSPhilippe Houdoin ntohl(*(uint32*)data)); 78169b5cacbSPhilippe Houdoin fLeaseTime = ntohl(*(uint32*)data) * 1000000LL; 7820ce7725eSAxel Dörfler break; 7831a4e8e7bSAxel Dörfler case OPTION_RENEWAL_TIME: 784cefe2a40SPhilippe Houdoin syslog(LOG_DEBUG, " renewal time: %lu seconds\n", 78569b5cacbSPhilippe Houdoin ntohl(*(uint32*)data)); 78669b5cacbSPhilippe Houdoin fRenewalTime = ntohl(*(uint32*)data) * 1000000LL; 78746ff5400SAxel Dörfler break; 7881a4e8e7bSAxel Dörfler case OPTION_REBINDING_TIME: 789cefe2a40SPhilippe Houdoin syslog(LOG_DEBUG, " rebinding time: %lu seconds\n", 79069b5cacbSPhilippe Houdoin ntohl(*(uint32*)data)); 79169b5cacbSPhilippe Houdoin fRebindingTime = ntohl(*(uint32*)data) * 1000000LL; 7921a4e8e7bSAxel Dörfler break; 7931a4e8e7bSAxel Dörfler 7940ce7725eSAxel Dörfler case OPTION_HOST_NAME: 795cefe2a40SPhilippe Houdoin syslog(LOG_DEBUG, " host name: \"%.*s\"\n", (int)size, 79616e8f13aSAxel Dörfler (const char*)data); 7970ce7725eSAxel Dörfler break; 7985782c5a3SAxel Dörfler 7995782c5a3SAxel Dörfler case OPTION_DOMAIN_NAME: 8005782c5a3SAxel Dörfler { 80116e8f13aSAxel Dörfler char domain[256]; 80216e8f13aSAxel Dörfler strlcpy(domain, (const char*)data, 80316e8f13aSAxel Dörfler min_c(size + 1, sizeof(domain))); 8040f87f52fSStephan Aßmus 805cefe2a40SPhilippe Houdoin syslog(LOG_DEBUG, " domain name: \"%s\"\n", domain); 8060f87f52fSStephan Aßmus 80716e8f13aSAxel Dörfler resolverConfiguration.AddString("domain", domain); 8085782c5a3SAxel Dörfler break; 8095782c5a3SAxel Dörfler } 8100ce7725eSAxel Dörfler 8110ce7725eSAxel Dörfler case OPTION_MESSAGE_TYPE: 8120ce7725eSAxel Dörfler break; 8130ce7725eSAxel Dörfler 814d5d2fdf0SPhilippe Houdoin case OPTION_ERROR_MESSAGE: 81569b5cacbSPhilippe Houdoin syslog(LOG_INFO, " error message: \"%.*s\"\n", (int)size, 816d5d2fdf0SPhilippe Houdoin (const char*)data); 817d5d2fdf0SPhilippe Houdoin break; 818d5d2fdf0SPhilippe Houdoin 8190ce7725eSAxel Dörfler default: 820cefe2a40SPhilippe Houdoin syslog(LOG_DEBUG, " UNKNOWN OPTION %lu (0x%x)\n", 82169b5cacbSPhilippe Houdoin (uint32)option, (uint32)option); 8220ce7725eSAxel Dörfler break; 8230ce7725eSAxel Dörfler } 8240ce7725eSAxel Dörfler } 8250ce7725eSAxel Dörfler } 8260ce7725eSAxel Dörfler 8270ce7725eSAxel Dörfler 8280ce7725eSAxel Dörfler void 82946ff5400SAxel Dörfler DHCPClient::_PrepareMessage(dhcp_message& message, dhcp_state state) 8300ce7725eSAxel Dörfler { 8310ce7725eSAxel Dörfler message.opcode = BOOT_REQUEST; 8320ce7725eSAxel Dörfler message.hardware_type = ARP_HARDWARE_TYPE_ETHER; 8330ce7725eSAxel Dörfler message.hardware_address_length = 6; 8340ce7725eSAxel Dörfler message.transaction_id = htonl(fTransactionID); 835bcc8dadaSAxel Dörfler message.seconds_since_start = htons(min_c((system_time() - fStartTime) 83615ab0bcfSAxel Dörfler / 1000000LL, 65535)); 8370ce7725eSAxel Dörfler memcpy(message.mac_address, fMAC, 6); 8380ce7725eSAxel Dörfler 83946ff5400SAxel Dörfler message_type type = message.Type(); 84046ff5400SAxel Dörfler 841a073ba1aSHugo Santos uint8 *next = message.PrepareMessage(type); 842d5d2fdf0SPhilippe Houdoin 843d5d2fdf0SPhilippe Houdoin switch (type) { 844d5d2fdf0SPhilippe Houdoin case DHCP_DISCOVER: 845d5d2fdf0SPhilippe Houdoin next = message.PutOption(next, OPTION_REQUEST_PARAMETERS, 846d5d2fdf0SPhilippe Houdoin kRequestParameters, sizeof(kRequestParameters)); 8475f25cf08SSiarzhuk Zharski 8485f25cf08SSiarzhuk Zharski if (fHostName.Length() > 0) { 8495f25cf08SSiarzhuk Zharski next = message.PutOption(next, OPTION_HOST_NAME, 8505f25cf08SSiarzhuk Zharski reinterpret_cast<const uint8*>(fHostName.String()), 8515f25cf08SSiarzhuk Zharski fHostName.Length()); 8525f25cf08SSiarzhuk Zharski } 853d5d2fdf0SPhilippe Houdoin break; 854d5d2fdf0SPhilippe Houdoin 855d5d2fdf0SPhilippe Houdoin case DHCP_REQUEST: 856d5d2fdf0SPhilippe Houdoin next = message.PutOption(next, OPTION_REQUEST_PARAMETERS, 857d5d2fdf0SPhilippe Houdoin kRequestParameters, sizeof(kRequestParameters)); 858d5d2fdf0SPhilippe Houdoin 8595f25cf08SSiarzhuk Zharski if (fHostName.Length() > 0) { 8605f25cf08SSiarzhuk Zharski next = message.PutOption(next, OPTION_HOST_NAME, 8615f25cf08SSiarzhuk Zharski reinterpret_cast<const uint8*>(fHostName.String()), 8625f25cf08SSiarzhuk Zharski fHostName.Length()); 8635f25cf08SSiarzhuk Zharski } 8645f25cf08SSiarzhuk Zharski 865d5d2fdf0SPhilippe Houdoin if (state == REQUESTING) { 866d5d2fdf0SPhilippe Houdoin const sockaddr_in& server = (sockaddr_in&)fServer.SockAddr(); 867cefe2a40SPhilippe Houdoin next = message.PutOption(next, OPTION_SERVER_ADDRESS, 868d5d2fdf0SPhilippe Houdoin (uint32)server.sin_addr.s_addr); 869d5d2fdf0SPhilippe Houdoin } 870d5d2fdf0SPhilippe Houdoin 8710892800fSPhilippe Houdoin if (state == INIT || state == INIT_REBOOT 8720892800fSPhilippe Houdoin || state == REQUESTING) { 873d5d2fdf0SPhilippe Houdoin next = message.PutOption(next, OPTION_REQUEST_IP_ADDRESS, 874d5d2fdf0SPhilippe Houdoin (uint32)fAssignedAddress); 875d5d2fdf0SPhilippe Houdoin } else 876d5d2fdf0SPhilippe Houdoin message.client_address = fAssignedAddress; 877d5d2fdf0SPhilippe Houdoin break; 878d5d2fdf0SPhilippe Houdoin 879d5d2fdf0SPhilippe Houdoin case DHCP_RELEASE: { 880d5d2fdf0SPhilippe Houdoin const sockaddr_in& server = (sockaddr_in&)fServer.SockAddr(); 881cefe2a40SPhilippe Houdoin next = message.PutOption(next, OPTION_SERVER_ADDRESS, 882af074561SAxel Dörfler (uint32)server.sin_addr.s_addr); 88346ff5400SAxel Dörfler 88446ff5400SAxel Dörfler message.client_address = fAssignedAddress; 8850ce7725eSAxel Dörfler break; 8860ce7725eSAxel Dörfler } 8870ce7725eSAxel Dörfler 8880ce7725eSAxel Dörfler default: 8890ce7725eSAxel Dörfler break; 8900ce7725eSAxel Dörfler } 891d5d2fdf0SPhilippe Houdoin 892d5d2fdf0SPhilippe Houdoin message.FinishOptions(next); 8930ce7725eSAxel Dörfler } 8940ce7725eSAxel Dörfler 8950ce7725eSAxel Dörfler 8960ce7725eSAxel Dörfler void 8976972b91eSPawel Dziepak DHCPClient::_ResetTimeout(int socket, dhcp_state& state, time_t& timeout, 8986972b91eSPawel Dziepak uint32& tries) 899f9af6566SAxel Dörfler { 9006cc7630fSAxel Dörfler timeout = DEFAULT_TIMEOUT; 9016cc7630fSAxel Dörfler tries = 0; 902f9af6566SAxel Dörfler 903f9af6566SAxel Dörfler struct timeval value; 9046cc7630fSAxel Dörfler value.tv_sec = timeout; 905d09c1f8eSPhilippe Houdoin value.tv_usec = rand() % 1000000; 906f9af6566SAxel Dörfler setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &value, sizeof(value)); 907f9af6566SAxel Dörfler } 908f9af6566SAxel Dörfler 909f9af6566SAxel Dörfler 910f9af6566SAxel Dörfler bool 9116972b91eSPawel Dziepak DHCPClient::_TimeoutShift(int socket, dhcp_state& state, time_t& timeout, 9126972b91eSPawel Dziepak uint32& tries) 913f9af6566SAxel Dörfler { 9146972b91eSPawel Dziepak if (state == RENEWING && system_time() > fRebindingTime) { 9156972b91eSPawel Dziepak state = REBINDING; 916f9af6566SAxel Dörfler return false; 917f9af6566SAxel Dörfler } 9186972b91eSPawel Dziepak 9196972b91eSPawel Dziepak if (state == REBINDING && system_time() > fLeaseTime) { 9206972b91eSPawel Dziepak state = INIT; 9216972b91eSPawel Dziepak return false; 9226972b91eSPawel Dziepak } 9236972b91eSPawel Dziepak 9246972b91eSPawel Dziepak tries++; 9256972b91eSPawel Dziepak timeout += timeout; 9266972b91eSPawel Dziepak if (timeout > MAX_TIMEOUT) 9276972b91eSPawel Dziepak timeout = MAX_TIMEOUT; 9286972b91eSPawel Dziepak 9296972b91eSPawel Dziepak if (tries > MAX_RETRIES) { 9306972b91eSPawel Dziepak bigtime_t remaining = 0; 9316972b91eSPawel Dziepak if (state == RENEWING) 9326972b91eSPawel Dziepak remaining = (fRebindingTime - system_time()) / 2 + 1; 9336972b91eSPawel Dziepak else if (state == REBINDING) 9346972b91eSPawel Dziepak remaining = (fLeaseTime - system_time()) / 2 + 1; 9356972b91eSPawel Dziepak else 9366972b91eSPawel Dziepak return false; 9376972b91eSPawel Dziepak 9386972b91eSPawel Dziepak timeout = std::max(remaining / 1000000, bigtime_t(60)); 9396972b91eSPawel Dziepak } 9406972b91eSPawel Dziepak 9410892800fSPhilippe Houdoin syslog(LOG_DEBUG, "%s: Timeout shift: %lu secs (try %lu)\n", 94261729d93SAxel Dörfler Device(), timeout, tries); 943f9af6566SAxel Dörfler 944f9af6566SAxel Dörfler struct timeval value; 9456cc7630fSAxel Dörfler value.tv_sec = timeout; 946d09c1f8eSPhilippe Houdoin value.tv_usec = rand() % 1000000; 947f9af6566SAxel Dörfler setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &value, sizeof(value)); 948f9af6566SAxel Dörfler 949f9af6566SAxel Dörfler return true; 950f9af6566SAxel Dörfler } 951f9af6566SAxel Dörfler 952f9af6566SAxel Dörfler 95384fdf2c3SPhilippe Houdoin /*static*/ BString 95484fdf2c3SPhilippe Houdoin DHCPClient::_AddressToString(const uint8* data) 955f9af6566SAxel Dörfler { 956f9af6566SAxel Dörfler BString target = inet_ntoa(*(in_addr*)data); 957f9af6566SAxel Dörfler return target; 958f9af6566SAxel Dörfler } 959f9af6566SAxel Dörfler 960f9af6566SAxel Dörfler 96184fdf2c3SPhilippe Houdoin /*static*/ BString 96284fdf2c3SPhilippe Houdoin DHCPClient::_AddressToString(in_addr_t address) 963f9af6566SAxel Dörfler { 964f9af6566SAxel Dörfler BString target = inet_ntoa(*(in_addr*)&address); 965f9af6566SAxel Dörfler return target; 966f9af6566SAxel Dörfler } 967f9af6566SAxel Dörfler 968f9af6566SAxel Dörfler 969f9af6566SAxel Dörfler status_t 9701a905a76SAxel Dörfler DHCPClient::_SendMessage(int socket, dhcp_message& message, 971af074561SAxel Dörfler const BNetworkAddress& address) const 972f9af6566SAxel Dörfler { 9730892800fSPhilippe Houdoin message_type type = message.Type(); 9740892800fSPhilippe Houdoin BString text; 9750892800fSPhilippe Houdoin text << dhcp_message::TypeToString(type); 9760892800fSPhilippe Houdoin 9770892800fSPhilippe Houdoin const uint8* requestAddress = message.FindOption(OPTION_REQUEST_IP_ADDRESS); 9780892800fSPhilippe Houdoin if (type == DHCP_REQUEST && requestAddress != NULL) 9790892800fSPhilippe Houdoin text << " for " << _AddressToString(requestAddress).String(); 9800892800fSPhilippe Houdoin 9810892800fSPhilippe Houdoin syslog(LOG_DEBUG, "%s: Send %s to %s\n", Device(), text.String(), 9820892800fSPhilippe Houdoin address.ToString().String()); 98361729d93SAxel Dörfler 984f9af6566SAxel Dörfler ssize_t bytesSent = sendto(socket, &message, message.Size(), 985af074561SAxel Dörfler address.IsBroadcast() ? MSG_BCAST : 0, address, address.Length()); 986f9af6566SAxel Dörfler if (bytesSent < 0) 987f9af6566SAxel Dörfler return errno; 988f9af6566SAxel Dörfler 989f9af6566SAxel Dörfler return B_OK; 990fb81684fSAxel Dörfler } 991fb81684fSAxel Dörfler 992fb81684fSAxel Dörfler 99346ff5400SAxel Dörfler dhcp_state 99446ff5400SAxel Dörfler DHCPClient::_CurrentState() const 99546ff5400SAxel Dörfler { 99646ff5400SAxel Dörfler bigtime_t now = system_time(); 99746ff5400SAxel Dörfler 9986972b91eSPawel Dziepak if (now > fLeaseTime || fStatus != B_OK) 99946ff5400SAxel Dörfler return INIT; 100046ff5400SAxel Dörfler if (now >= fRebindingTime) 100146ff5400SAxel Dörfler return REBINDING; 100246ff5400SAxel Dörfler if (now >= fRenewalTime) 10030892800fSPhilippe Houdoin return RENEWING; 100446ff5400SAxel Dörfler return BOUND; 100546ff5400SAxel Dörfler } 100646ff5400SAxel Dörfler 100746ff5400SAxel Dörfler 1008fb81684fSAxel Dörfler void 1009fb81684fSAxel Dörfler DHCPClient::MessageReceived(BMessage* message) 1010fb81684fSAxel Dörfler { 10116cc7630fSAxel Dörfler switch (message->what) { 10126cc7630fSAxel Dörfler case kMsgLeaseTime: 10136972b91eSPawel Dziepak _Negotiate(_CurrentState()); 10146cc7630fSAxel Dörfler break; 10156cc7630fSAxel Dörfler 10166cc7630fSAxel Dörfler default: 1017fb81684fSAxel Dörfler BHandler::MessageReceived(message); 10186cc7630fSAxel Dörfler break; 10196cc7630fSAxel Dörfler } 1020fb81684fSAxel Dörfler } 1021