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 19f9af6566SAxel Dörfler #include <arpa/inet.h> 20fb81684fSAxel Dörfler #include <errno.h> 21fb81684fSAxel Dörfler #include <stdio.h> 22fb81684fSAxel Dörfler #include <string.h> 23d09c1f8eSPhilippe Houdoin #include <stdlib.h> 24293ed4feSAxel Dörfler #include <syslog.h> 2536bde12dSAxel Dörfler #include <sys/sockio.h> 26f9af6566SAxel Dörfler #include <sys/time.h> 27fb81684fSAxel Dörfler 284ac66051SAxel Dörfler #include <Debug.h> 294ac66051SAxel Dörfler #include <Message.h> 304ac66051SAxel Dörfler #include <MessageRunner.h> 314ac66051SAxel Dörfler 324ac66051SAxel Dörfler #include "NetServer.h" 334ac66051SAxel Dörfler 34fb81684fSAxel Dörfler 35fb81684fSAxel Dörfler // See RFC 2131 for DHCP, see RFC 1533 for BOOTP/DHCP options 36fb81684fSAxel Dörfler 37fb81684fSAxel Dörfler #define DHCP_CLIENT_PORT 68 38fb81684fSAxel Dörfler #define DHCP_SERVER_PORT 67 39fb81684fSAxel Dörfler 40f9af6566SAxel Dörfler #define DEFAULT_TIMEOUT 2 // secs 41f9af6566SAxel Dörfler #define MAX_TIMEOUT 15 // secs 42f9af6566SAxel Dörfler 43fb81684fSAxel Dörfler enum message_opcode { 44fb81684fSAxel Dörfler BOOT_REQUEST = 1, 45fb81684fSAxel Dörfler BOOT_REPLY 46fb81684fSAxel Dörfler }; 47fb81684fSAxel Dörfler 48fb81684fSAxel Dörfler enum message_option { 49fb81684fSAxel Dörfler OPTION_MAGIC = 0x63825363, 50fb81684fSAxel Dörfler 51fb81684fSAxel Dörfler // generic options 52fb81684fSAxel Dörfler OPTION_PAD = 0, 53f9af6566SAxel Dörfler OPTION_END = 255, 54f9af6566SAxel Dörfler OPTION_SUBNET_MASK = 1, 5565186fecSAxel Dörfler OPTION_TIME_OFFSET = 2, 56f9af6566SAxel Dörfler OPTION_ROUTER_ADDRESS = 3, 57f9af6566SAxel Dörfler OPTION_DOMAIN_NAME_SERVER = 6, 58f9af6566SAxel Dörfler OPTION_HOST_NAME = 12, 595782c5a3SAxel Dörfler OPTION_DOMAIN_NAME = 15, 60d5d2fdf0SPhilippe Houdoin OPTION_MAX_DATAGRAM_SIZE = 22, 61d5d2fdf0SPhilippe Houdoin OPTION_INTERFACE_MTU = 26, 62fb81684fSAxel Dörfler OPTION_BROADCAST_ADDRESS = 28, 63d5d2fdf0SPhilippe Houdoin OPTION_NETWORK_TIME_PROTOCOL_SERVERS = 42, 6465186fecSAxel Dörfler OPTION_NETBIOS_NAME_SERVER = 44, 6565186fecSAxel Dörfler OPTION_NETBIOS_SCOPE = 47, 66fb81684fSAxel Dörfler 67fb81684fSAxel Dörfler // DHCP specific options 68fb81684fSAxel Dörfler OPTION_REQUEST_IP_ADDRESS = 50, 69fb81684fSAxel Dörfler OPTION_ADDRESS_LEASE_TIME = 51, 70fb81684fSAxel Dörfler OPTION_OVERLOAD = 52, 71fb81684fSAxel Dörfler OPTION_MESSAGE_TYPE = 53, 72cefe2a40SPhilippe Houdoin OPTION_SERVER_ADDRESS = 54, 73fb81684fSAxel Dörfler OPTION_REQUEST_PARAMETERS = 55, 74fb81684fSAxel Dörfler OPTION_ERROR_MESSAGE = 56, 75d5d2fdf0SPhilippe Houdoin OPTION_MAX_MESSAGE_SIZE = 57, 76fb81684fSAxel Dörfler OPTION_RENEWAL_TIME = 58, 77fb81684fSAxel Dörfler OPTION_REBINDING_TIME = 59, 78fb81684fSAxel Dörfler OPTION_CLASS_IDENTIFIER = 60, 79fb81684fSAxel Dörfler OPTION_CLIENT_IDENTIFIER = 61, 80fb81684fSAxel Dörfler }; 81fb81684fSAxel Dörfler 82fb81684fSAxel Dörfler enum message_type { 83f9af6566SAxel Dörfler DHCP_NONE = 0, 84f9af6566SAxel Dörfler DHCP_DISCOVER, 85fb81684fSAxel Dörfler DHCP_OFFER, 86fb81684fSAxel Dörfler DHCP_REQUEST, 87fb81684fSAxel Dörfler DHCP_DECLINE, 88fb81684fSAxel Dörfler DHCP_ACK, 89f9af6566SAxel Dörfler DHCP_NACK, 90fb81684fSAxel Dörfler DHCP_RELEASE, 91fb81684fSAxel Dörfler DHCP_INFORM 92fb81684fSAxel Dörfler }; 93fb81684fSAxel Dörfler 94fb81684fSAxel Dörfler struct dhcp_option_cookie { 9515ab0bcfSAxel Dörfler dhcp_option_cookie() 9615ab0bcfSAxel Dörfler : 9715ab0bcfSAxel Dörfler state(0), 9815ab0bcfSAxel Dörfler file_has_options(false), 9915ab0bcfSAxel Dörfler server_name_has_options(false) 10015ab0bcfSAxel Dörfler { 10115ab0bcfSAxel Dörfler } 102fb81684fSAxel Dörfler 103fb81684fSAxel Dörfler const uint8* next; 104fb81684fSAxel Dörfler uint8 state; 105fb81684fSAxel Dörfler bool file_has_options; 106fb81684fSAxel Dörfler bool server_name_has_options; 107fb81684fSAxel Dörfler }; 108fb81684fSAxel Dörfler 109fb81684fSAxel Dörfler struct dhcp_message { 110fb81684fSAxel Dörfler dhcp_message(message_type type); 111fb81684fSAxel Dörfler 112fb81684fSAxel Dörfler uint8 opcode; 113fb81684fSAxel Dörfler uint8 hardware_type; 114fb81684fSAxel Dörfler uint8 hardware_address_length; 115fb81684fSAxel Dörfler uint8 hop_count; 116fb81684fSAxel Dörfler uint32 transaction_id; 11746ff5400SAxel Dörfler uint16 seconds_since_start; 118fb81684fSAxel Dörfler uint16 flags; 119fb81684fSAxel Dörfler in_addr_t client_address; 120fb81684fSAxel Dörfler in_addr_t your_address; 121fb81684fSAxel Dörfler in_addr_t server_address; 122fb81684fSAxel Dörfler in_addr_t gateway_address; 123fb81684fSAxel Dörfler uint8 mac_address[16]; 124fb81684fSAxel Dörfler uint8 server_name[64]; 125fb81684fSAxel Dörfler uint8 file[128]; 126fb81684fSAxel Dörfler uint32 options_magic; 127fb81684fSAxel Dörfler uint8 options[1260]; 128fb81684fSAxel Dörfler 129fb81684fSAxel Dörfler size_t MinSize() const { return 576; } 130fb81684fSAxel Dörfler size_t Size() const; 131fb81684fSAxel Dörfler 132fb81684fSAxel Dörfler bool HasOptions() const; 133fb81684fSAxel Dörfler bool NextOption(dhcp_option_cookie& cookie, message_option& option, 134fb81684fSAxel Dörfler const uint8*& data, size_t& size) const; 13503f6ab7fSPhilippe Houdoin const uint8* FindOption(message_option which) const; 136fb81684fSAxel Dörfler const uint8* LastOption() const; 13703f6ab7fSPhilippe Houdoin message_type Type() const; 138fb81684fSAxel Dörfler 139a073ba1aSHugo Santos uint8* PrepareMessage(uint8 type); 140fb81684fSAxel Dörfler uint8* PutOption(uint8* options, message_option option); 141fb81684fSAxel Dörfler uint8* PutOption(uint8* options, message_option option, uint8 data); 142fb81684fSAxel Dörfler uint8* PutOption(uint8* options, message_option option, uint16 data); 143fb81684fSAxel Dörfler uint8* PutOption(uint8* options, message_option option, uint32 data); 14415ab0bcfSAxel Dörfler uint8* PutOption(uint8* options, message_option option, const uint8* data, 14515ab0bcfSAxel Dörfler uint32 size); 14637a68cb1SAxel Dörfler uint8* FinishOptions(uint8* options); 1474ac66051SAxel Dörfler 1484ac66051SAxel Dörfler static const char* TypeToString(message_type type); 149fb81684fSAxel Dörfler } _PACKED; 150fb81684fSAxel Dörfler 1510cf5e6acSAxel Dörfler #define DHCP_FLAG_BROADCAST 0x8000 152fb81684fSAxel Dörfler 153fb81684fSAxel Dörfler #define ARP_HARDWARE_TYPE_ETHER 1 154fb81684fSAxel Dörfler 1556cc7630fSAxel Dörfler const uint32 kMsgLeaseTime = 'lstm'; 1566cc7630fSAxel Dörfler 15737a68cb1SAxel Dörfler static const uint8 kRequestParameters[] = { 15865186fecSAxel Dörfler OPTION_SUBNET_MASK, OPTION_ROUTER_ADDRESS, 159bfd479b3SAxel Dörfler OPTION_DOMAIN_NAME_SERVER, OPTION_BROADCAST_ADDRESS, 160bfd479b3SAxel Dörfler OPTION_DOMAIN_NAME 1615d4d5313SHugo Santos }; 1625d4d5313SHugo Santos 163fb81684fSAxel Dörfler 164fb81684fSAxel Dörfler dhcp_message::dhcp_message(message_type type) 165fb81684fSAxel Dörfler { 166fb4527d2SPhilippe Houdoin // ASSERT(this == offsetof(this, opcode)); 167fb81684fSAxel Dörfler memset(this, 0, sizeof(*this)); 168fb81684fSAxel Dörfler options_magic = htonl(OPTION_MAGIC); 169fb81684fSAxel Dörfler 170a073ba1aSHugo Santos uint8* next = PrepareMessage(type); 171a073ba1aSHugo Santos FinishOptions(next); 172fb81684fSAxel Dörfler } 173fb81684fSAxel Dörfler 174fb81684fSAxel Dörfler 175fb81684fSAxel Dörfler bool 176fb81684fSAxel Dörfler dhcp_message::HasOptions() const 177fb81684fSAxel Dörfler { 178fb81684fSAxel Dörfler return options_magic == htonl(OPTION_MAGIC); 179fb81684fSAxel Dörfler } 180fb81684fSAxel Dörfler 181fb81684fSAxel Dörfler 182fb81684fSAxel Dörfler bool 183fb81684fSAxel Dörfler dhcp_message::NextOption(dhcp_option_cookie& cookie, 184fb81684fSAxel Dörfler message_option& option, const uint8*& data, size_t& size) const 185fb81684fSAxel Dörfler { 186fb81684fSAxel Dörfler if (!HasOptions()) 187fb81684fSAxel Dörfler return false; 188fb81684fSAxel Dörfler 189fb4527d2SPhilippe Houdoin if (cookie.state == 0) { 190fb81684fSAxel Dörfler cookie.state++; 191fb81684fSAxel Dörfler cookie.next = options; 192fb81684fSAxel Dörfler } 193fb81684fSAxel Dörfler 194fb81684fSAxel Dörfler uint32 bytesLeft = 0; 195fb81684fSAxel Dörfler 196fb81684fSAxel Dörfler switch (cookie.state) { 197fb81684fSAxel Dörfler case 1: 198ce36f054SPhilippe Houdoin // options from "options" 199fb4527d2SPhilippe Houdoin bytesLeft = sizeof(options) - (cookie.next - options); 200fb81684fSAxel Dörfler break; 201fb81684fSAxel Dörfler 202fb81684fSAxel Dörfler case 2: 203fb81684fSAxel Dörfler // options from "file" 204fb4527d2SPhilippe Houdoin bytesLeft = sizeof(file) - (cookie.next - file); 205fb81684fSAxel Dörfler break; 206fb81684fSAxel Dörfler 207fb81684fSAxel Dörfler case 3: 208fb81684fSAxel Dörfler // options from "server_name" 209fb4527d2SPhilippe Houdoin bytesLeft = sizeof(server_name) - (cookie.next - server_name); 210fb81684fSAxel Dörfler break; 211fb81684fSAxel Dörfler } 212fb81684fSAxel Dörfler 213fb81684fSAxel Dörfler while (true) { 214fb81684fSAxel Dörfler if (bytesLeft == 0) { 215fb4527d2SPhilippe Houdoin cookie.state++; 216fb4527d2SPhilippe Houdoin 217ce36f054SPhilippe Houdoin // handle option overload in file and/or server_name fields. 218fb4527d2SPhilippe Houdoin switch (cookie.state) { 219fb4527d2SPhilippe Houdoin case 2: 220d5d2fdf0SPhilippe Houdoin // options from "file" field 221fb4527d2SPhilippe Houdoin if (cookie.file_has_options) { 222fb4527d2SPhilippe Houdoin bytesLeft = sizeof(file); 223fb4527d2SPhilippe Houdoin cookie.next = file; 224fb4527d2SPhilippe Houdoin } 225fb4527d2SPhilippe Houdoin break; 226fb4527d2SPhilippe Houdoin 227fb4527d2SPhilippe Houdoin case 3: 228d5d2fdf0SPhilippe Houdoin // options from "server_name" field 229fb4527d2SPhilippe Houdoin if (cookie.server_name_has_options) { 230fb4527d2SPhilippe Houdoin bytesLeft = sizeof(server_name); 231fb4527d2SPhilippe Houdoin cookie.next = server_name; 232fb4527d2SPhilippe Houdoin } 233fb4527d2SPhilippe Houdoin break; 234fb4527d2SPhilippe Houdoin 235d5d2fdf0SPhilippe Houdoin default: 236ce36f054SPhilippe Houdoin // no more place to look for options 237d5d2fdf0SPhilippe Houdoin // if last option is not OPTION_END, 238d5d2fdf0SPhilippe Houdoin // there is no space left for other option! 239d5d2fdf0SPhilippe Houdoin if (option != OPTION_END) 240d5d2fdf0SPhilippe Houdoin cookie.next = NULL; 241fb81684fSAxel Dörfler return false; 242fb81684fSAxel Dörfler } 243fb81684fSAxel Dörfler 244fb4527d2SPhilippe Houdoin if (bytesLeft == 0) { 245d5d2fdf0SPhilippe Houdoin // no options in this state, try next one 246fb4527d2SPhilippe Houdoin continue; 247fb4527d2SPhilippe Houdoin } 248fb4527d2SPhilippe Houdoin } 249fb4527d2SPhilippe Houdoin 250fb81684fSAxel Dörfler option = (message_option)cookie.next[0]; 251fb81684fSAxel Dörfler if (option == OPTION_END) { 252fb4527d2SPhilippe Houdoin bytesLeft = 0; 253fb4527d2SPhilippe Houdoin continue; 254fb81684fSAxel Dörfler } else if (option == OPTION_PAD) { 255fb81684fSAxel Dörfler bytesLeft--; 256fb81684fSAxel Dörfler cookie.next++; 257fb81684fSAxel Dörfler continue; 258fb81684fSAxel Dörfler } 259fb81684fSAxel Dörfler 260fb81684fSAxel Dörfler size = cookie.next[1]; 261fb81684fSAxel Dörfler data = &cookie.next[2]; 262fb81684fSAxel Dörfler cookie.next += 2 + size; 263fb4527d2SPhilippe Houdoin bytesLeft -= 2 + size; 264fb81684fSAxel Dörfler 265fb81684fSAxel Dörfler if (option == OPTION_OVERLOAD) { 266fb81684fSAxel Dörfler cookie.file_has_options = data[0] & 1; 267fb81684fSAxel Dörfler cookie.server_name_has_options = data[0] & 2; 268fb81684fSAxel Dörfler continue; 269fb81684fSAxel Dörfler } 270fb81684fSAxel Dörfler 271fb81684fSAxel Dörfler return true; 272fb81684fSAxel Dörfler } 273fb81684fSAxel Dörfler } 274fb81684fSAxel Dörfler 275fb81684fSAxel Dörfler 27603f6ab7fSPhilippe Houdoin const uint8* 27703f6ab7fSPhilippe Houdoin dhcp_message::FindOption(message_option which) const 278f9af6566SAxel Dörfler { 279f9af6566SAxel Dörfler dhcp_option_cookie cookie; 280f9af6566SAxel Dörfler message_option option; 281f9af6566SAxel Dörfler const uint8* data; 282f9af6566SAxel Dörfler size_t size; 283f9af6566SAxel Dörfler while (NextOption(cookie, option, data, size)) { 284f9af6566SAxel Dörfler // iterate through all options 28503f6ab7fSPhilippe Houdoin if (option == which) 28603f6ab7fSPhilippe Houdoin return data; 287f9af6566SAxel Dörfler } 288f9af6566SAxel Dörfler 28903f6ab7fSPhilippe Houdoin return NULL; 290f9af6566SAxel Dörfler } 291f9af6566SAxel Dörfler 292f9af6566SAxel Dörfler 293fb81684fSAxel Dörfler const uint8* 294fb81684fSAxel Dörfler dhcp_message::LastOption() const 295fb81684fSAxel Dörfler { 296fb81684fSAxel Dörfler dhcp_option_cookie cookie; 297fb81684fSAxel Dörfler message_option option; 298fb81684fSAxel Dörfler const uint8* data; 299fb81684fSAxel Dörfler size_t size; 300fb81684fSAxel Dörfler while (NextOption(cookie, option, data, size)) { 301fb81684fSAxel Dörfler // iterate through all options 302fb81684fSAxel Dörfler } 303fb81684fSAxel Dörfler 304fb81684fSAxel Dörfler return cookie.next; 305fb81684fSAxel Dörfler } 306fb81684fSAxel Dörfler 307fb81684fSAxel Dörfler 30803f6ab7fSPhilippe Houdoin message_type 30903f6ab7fSPhilippe Houdoin dhcp_message::Type() const 31003f6ab7fSPhilippe Houdoin { 31103f6ab7fSPhilippe Houdoin const uint8* data = FindOption(OPTION_MESSAGE_TYPE); 31203f6ab7fSPhilippe Houdoin if (data) 31303f6ab7fSPhilippe Houdoin return (message_type)data[0]; 31403f6ab7fSPhilippe Houdoin 31503f6ab7fSPhilippe Houdoin return DHCP_NONE; 31603f6ab7fSPhilippe Houdoin } 31703f6ab7fSPhilippe Houdoin 31803f6ab7fSPhilippe Houdoin 319fb81684fSAxel Dörfler size_t 320fb81684fSAxel Dörfler dhcp_message::Size() const 321fb81684fSAxel Dörfler { 322fb81684fSAxel Dörfler const uint8* last = LastOption(); 323d5d2fdf0SPhilippe Houdoin 324d5d2fdf0SPhilippe Houdoin if (last < options) { 325d5d2fdf0SPhilippe Houdoin // if last option is stored above "options" field, it means 326d5d2fdf0SPhilippe Houdoin // the whole options field and message is already filled... 327d5d2fdf0SPhilippe Houdoin return sizeof(dhcp_message); 328d5d2fdf0SPhilippe Houdoin } 329d5d2fdf0SPhilippe Houdoin 330fb81684fSAxel Dörfler return sizeof(dhcp_message) - sizeof(options) + last + 1 - options; 331fb81684fSAxel Dörfler } 332fb81684fSAxel Dörfler 333fb81684fSAxel Dörfler 334fb81684fSAxel Dörfler uint8* 335a073ba1aSHugo Santos dhcp_message::PrepareMessage(uint8 type) 336a073ba1aSHugo Santos { 337a073ba1aSHugo Santos uint8* next = options; 338a073ba1aSHugo Santos next = PutOption(next, OPTION_MESSAGE_TYPE, type); 339d5d2fdf0SPhilippe Houdoin next = PutOption(next, OPTION_MAX_MESSAGE_SIZE, 34015ab0bcfSAxel Dörfler (uint16)htons(sizeof(dhcp_message))); 341a073ba1aSHugo Santos return next; 342a073ba1aSHugo Santos } 343a073ba1aSHugo Santos 34465186fecSAxel Dörfler 345a073ba1aSHugo Santos uint8* 346fb81684fSAxel Dörfler dhcp_message::PutOption(uint8* options, message_option option) 347fb81684fSAxel Dörfler { 348fb81684fSAxel Dörfler options[0] = option; 349fb81684fSAxel Dörfler return options + 1; 350fb81684fSAxel Dörfler } 351fb81684fSAxel Dörfler 352fb81684fSAxel Dörfler 353fb81684fSAxel Dörfler uint8* 354fb81684fSAxel Dörfler dhcp_message::PutOption(uint8* options, message_option option, uint8 data) 355fb81684fSAxel Dörfler { 356fb81684fSAxel Dörfler return PutOption(options, option, &data, 1); 357fb81684fSAxel Dörfler } 358fb81684fSAxel Dörfler 359fb81684fSAxel Dörfler 360fb81684fSAxel Dörfler uint8* 361fb81684fSAxel Dörfler dhcp_message::PutOption(uint8* options, message_option option, uint16 data) 362fb81684fSAxel Dörfler { 363fb81684fSAxel Dörfler return PutOption(options, option, (uint8*)&data, sizeof(data)); 364fb81684fSAxel Dörfler } 365fb81684fSAxel Dörfler 366fb81684fSAxel Dörfler 367fb81684fSAxel Dörfler uint8* 368fb81684fSAxel Dörfler dhcp_message::PutOption(uint8* options, message_option option, uint32 data) 369fb81684fSAxel Dörfler { 370fb81684fSAxel Dörfler return PutOption(options, option, (uint8*)&data, sizeof(data)); 371fb81684fSAxel Dörfler } 372fb81684fSAxel Dörfler 373fb81684fSAxel Dörfler 374fb81684fSAxel Dörfler uint8* 37515ab0bcfSAxel Dörfler dhcp_message::PutOption(uint8* options, message_option option, 37615ab0bcfSAxel Dörfler const uint8* data, uint32 size) 377fb81684fSAxel Dörfler { 378d5d2fdf0SPhilippe Houdoin // TODO: check enough space is available 379d5d2fdf0SPhilippe Houdoin 380fb81684fSAxel Dörfler options[0] = option; 381fb81684fSAxel Dörfler options[1] = size; 382fb81684fSAxel Dörfler memcpy(&options[2], data, size); 383fb81684fSAxel Dörfler 384fb81684fSAxel Dörfler return options + 2 + size; 385fb81684fSAxel Dörfler } 386fb81684fSAxel Dörfler 387fb81684fSAxel Dörfler 388a073ba1aSHugo Santos uint8* 38937a68cb1SAxel Dörfler dhcp_message::FinishOptions(uint8* options) 390a073ba1aSHugo Santos { 39137a68cb1SAxel Dörfler return PutOption(options, OPTION_END); 392a073ba1aSHugo Santos } 393a073ba1aSHugo Santos 394a073ba1aSHugo Santos 3954ac66051SAxel Dörfler /*static*/ const char* 3964ac66051SAxel Dörfler dhcp_message::TypeToString(message_type type) 3974ac66051SAxel Dörfler { 3984ac66051SAxel Dörfler switch (type) { 3994ac66051SAxel Dörfler #define CASE(x) case x: return #x; 4004ac66051SAxel Dörfler CASE(DHCP_NONE) 4014ac66051SAxel Dörfler CASE(DHCP_DISCOVER) 4024ac66051SAxel Dörfler CASE(DHCP_OFFER) 4034ac66051SAxel Dörfler CASE(DHCP_REQUEST) 4044ac66051SAxel Dörfler CASE(DHCP_DECLINE) 4054ac66051SAxel Dörfler CASE(DHCP_ACK) 4064ac66051SAxel Dörfler CASE(DHCP_NACK) 4074ac66051SAxel Dörfler CASE(DHCP_RELEASE) 4084ac66051SAxel Dörfler CASE(DHCP_INFORM) 4094ac66051SAxel Dörfler #undef CASE 4104ac66051SAxel Dörfler } 4114ac66051SAxel Dörfler 4124ac66051SAxel Dörfler return "<unknown>"; 4134ac66051SAxel Dörfler } 4144ac66051SAxel Dörfler 4154ac66051SAxel Dörfler 416fb81684fSAxel Dörfler // #pragma mark - 417fb81684fSAxel Dörfler 418fb81684fSAxel Dörfler 419f9af6566SAxel Dörfler DHCPClient::DHCPClient(BMessenger target, const char* device) 420af074561SAxel Dörfler : 421af074561SAxel Dörfler AutoconfigClient("dhcp", target, device), 4226cc7630fSAxel Dörfler fConfiguration(kMsgConfigureInterface), 42316e8f13aSAxel Dörfler fResolverConfiguration(kMsgConfigureResolver), 4246cc7630fSAxel Dörfler fRunner(NULL), 4250892800fSPhilippe Houdoin fAssignedAddress(0), 426af074561SAxel Dörfler fServer(AF_INET, NULL, DHCP_SERVER_PORT), 427c1264353SStephan Aßmus fLeaseTime(0) 428fb81684fSAxel Dörfler { 42946ff5400SAxel Dörfler fStartTime = system_time(); 43046ff5400SAxel Dörfler fTransactionID = (uint32)fStartTime; 431fb81684fSAxel Dörfler 432*7ca2da76SFredrik Holmqvist srand(fTransactionID); 433*7ca2da76SFredrik Holmqvist 434af074561SAxel Dörfler BNetworkAddress link; 435af074561SAxel Dörfler BNetworkInterface interface(device); 436af074561SAxel Dörfler fStatus = interface.GetHardwareAddress(link); 437af074561SAxel Dörfler if (fStatus != B_OK) 438fb81684fSAxel Dörfler return; 439fb81684fSAxel Dörfler 440af074561SAxel Dörfler memcpy(fMAC, link.LinkLevelAddress(), sizeof(fMAC)); 441293ed4feSAxel Dörfler 4420892800fSPhilippe Houdoin if ((interface.Flags() & IFF_AUTO_CONFIGURED) != 0) { 4430892800fSPhilippe Houdoin // Check for interface previous auto-configured address, if any. 4440892800fSPhilippe Houdoin BNetworkInterfaceAddress interfaceAddress; 4450892800fSPhilippe Houdoin int index = interface.FindFirstAddress(AF_INET); 4460892800fSPhilippe Houdoin if (index >= 0 4470892800fSPhilippe Houdoin && interface.GetAddressAt(index, interfaceAddress) == B_OK) { 4480892800fSPhilippe Houdoin BNetworkAddress address = interfaceAddress.Address(); 4490892800fSPhilippe Houdoin const sockaddr_in& addr = (sockaddr_in&)address.SockAddr(); 4500892800fSPhilippe Houdoin fAssignedAddress = addr.sin_addr.s_addr; 451b6ba1daaSPhilippe Houdoin 452b6ba1daaSPhilippe Houdoin if ((ntohl(fAssignedAddress) & IN_CLASSB_NET) == 0xa9fe0000) { 453b6ba1daaSPhilippe Houdoin // previous auto-configured address is a link-local one: 454b6ba1daaSPhilippe Houdoin // there is no point asking a DHCP server to renew such 455b6ba1daaSPhilippe Houdoin // server-less assigned address... 456b6ba1daaSPhilippe Houdoin fAssignedAddress = 0; 457b6ba1daaSPhilippe Houdoin } 4580892800fSPhilippe Houdoin } 4590892800fSPhilippe Houdoin } 4600892800fSPhilippe Houdoin 461293ed4feSAxel Dörfler openlog_thread("DHCP", 0, LOG_DAEMON); 4626cc7630fSAxel Dörfler } 4636cc7630fSAxel Dörfler 4646cc7630fSAxel Dörfler 4656cc7630fSAxel Dörfler DHCPClient::~DHCPClient() 4666cc7630fSAxel Dörfler { 4676cc7630fSAxel Dörfler if (fStatus != B_OK) 4686cc7630fSAxel Dörfler return; 4696cc7630fSAxel Dörfler 4706cc7630fSAxel Dörfler delete fRunner; 4710ce7725eSAxel Dörfler 472fb81684fSAxel Dörfler int socket = ::socket(AF_INET, SOCK_DGRAM, 0); 4736cc7630fSAxel Dörfler if (socket < 0) 474fb81684fSAxel Dörfler return; 4756cc7630fSAxel Dörfler 4766cc7630fSAxel Dörfler // release lease 4776cc7630fSAxel Dörfler 4786cc7630fSAxel Dörfler dhcp_message release(DHCP_RELEASE); 47946ff5400SAxel Dörfler _PrepareMessage(release, BOUND); 4806cc7630fSAxel Dörfler 4816cc7630fSAxel Dörfler _SendMessage(socket, release, fServer); 4826cc7630fSAxel Dörfler close(socket); 483293ed4feSAxel Dörfler 484293ed4feSAxel Dörfler closelog_thread(); 485fb81684fSAxel Dörfler } 486fb81684fSAxel Dörfler 4876cc7630fSAxel Dörfler 4886cc7630fSAxel Dörfler status_t 4896cc7630fSAxel Dörfler DHCPClient::Initialize() 4906cc7630fSAxel Dörfler { 4910892800fSPhilippe Houdoin fStatus = _Negotiate(fAssignedAddress == 0 ? INIT : INIT_REBOOT); 4920892800fSPhilippe Houdoin syslog(LOG_DEBUG, "%s: DHCP status = %s\n", Device(), strerror(fStatus)); 4936cc7630fSAxel Dörfler return fStatus; 4946cc7630fSAxel Dörfler } 4956cc7630fSAxel Dörfler 4966cc7630fSAxel Dörfler 4976cc7630fSAxel Dörfler status_t 4986cc7630fSAxel Dörfler DHCPClient::_Negotiate(dhcp_state state) 4996cc7630fSAxel Dörfler { 5006cc7630fSAxel Dörfler int socket = ::socket(AF_INET, SOCK_DGRAM, 0); 5016cc7630fSAxel Dörfler if (socket < 0) 5026cc7630fSAxel Dörfler return errno; 5036cc7630fSAxel Dörfler 504fb4527d2SPhilippe Houdoin BNetworkAddress local; 505fb4527d2SPhilippe Houdoin local.SetToWildcard(AF_INET, DHCP_CLIENT_PORT); 506fb81684fSAxel Dörfler 5072b829b04SBruno G. Albuquerque // Enable reusing the port. This is needed in case there is more 5082b829b04SBruno G. Albuquerque // than 1 interface that needs to be configured. Note that the only reason 5092b829b04SBruno G. Albuquerque // this works is because there is code below to bind to a specific 5102b829b04SBruno G. Albuquerque // interface. 5112b829b04SBruno G. Albuquerque int option = 1; 5122b829b04SBruno G. Albuquerque setsockopt(socket, SOL_SOCKET, SO_REUSEPORT, &option, sizeof(option)); 5132b829b04SBruno G. Albuquerque 514af074561SAxel Dörfler if (bind(socket, local, local.Length()) < 0) { 515fb81684fSAxel Dörfler close(socket); 5166cc7630fSAxel Dörfler return errno; 517fb81684fSAxel Dörfler } 518fb81684fSAxel Dörfler 519af074561SAxel Dörfler BNetworkAddress broadcast; 520af074561SAxel Dörfler broadcast.SetToBroadcast(AF_INET, DHCP_SERVER_PORT); 521fb81684fSAxel Dörfler 5222b829b04SBruno G. Albuquerque option = 1; 523fb81684fSAxel Dörfler setsockopt(socket, SOL_SOCKET, SO_BROADCAST, &option, sizeof(option)); 524fb81684fSAxel Dörfler 52536bde12dSAxel Dörfler if (state == INIT) { 52636bde12dSAxel Dörfler // The local interface does not have an address yet, bind the socket 52736bde12dSAxel Dörfler // to the device directly. 528af074561SAxel Dörfler BNetworkDevice device(Device()); 529af074561SAxel Dörfler int index = device.Index(); 53036bde12dSAxel Dörfler 531af074561SAxel Dörfler setsockopt(socket, SOL_SOCKET, SO_BINDTODEVICE, &index, sizeof(int)); 53236bde12dSAxel Dörfler } 53336bde12dSAxel Dörfler 5346cc7630fSAxel Dörfler bigtime_t previousLeaseTime = fLeaseTime; 5356cc7630fSAxel Dörfler fLeaseTime = 0; 53646ff5400SAxel Dörfler fRenewalTime = 0; 53746ff5400SAxel Dörfler fRebindingTime = 0; 538fb81684fSAxel Dörfler 5396cc7630fSAxel Dörfler status_t status = B_ERROR; 5406cc7630fSAxel Dörfler time_t timeout; 5416cc7630fSAxel Dörfler uint32 tries; 5426cc7630fSAxel Dörfler _ResetTimeout(socket, timeout, tries); 5436cc7630fSAxel Dörfler 5446cc7630fSAxel Dörfler dhcp_message discover(DHCP_DISCOVER); 54546ff5400SAxel Dörfler _PrepareMessage(discover, state); 5466cc7630fSAxel Dörfler 5476cc7630fSAxel Dörfler dhcp_message request(DHCP_REQUEST); 54846ff5400SAxel Dörfler _PrepareMessage(request, state); 5496cc7630fSAxel Dörfler 55046ff5400SAxel Dörfler // send discover/request message 551593a0d2dSAxel Dörfler _SendMessage(socket, state == INIT ? discover : request, 5520892800fSPhilippe Houdoin state != RENEWING ? broadcast : fServer); 553593a0d2dSAxel Dörfler // no need to check the status; in case of an error we'll just send 554593a0d2dSAxel Dörfler // the message again 555fb81684fSAxel Dörfler 556f9af6566SAxel Dörfler // receive loop until we've got an offer and acknowledged it 557f9af6566SAxel Dörfler 5580892800fSPhilippe Houdoin while (state != BOUND) { 559f9af6566SAxel Dörfler char buffer[2048]; 56069b5cacbSPhilippe Houdoin struct sockaddr_in from; 56169b5cacbSPhilippe Houdoin socklen_t fromLength = sizeof(from); 562f9af6566SAxel Dörfler ssize_t bytesReceived = recvfrom(socket, buffer, sizeof(buffer), 56369b5cacbSPhilippe Houdoin 0, (struct sockaddr*)&from, &fromLength); 5644b661a95SAxel Dörfler if (bytesReceived < 0 && errno == B_TIMED_OUT) { 565f9af6566SAxel Dörfler // depending on the state, we'll just try again 5666cc7630fSAxel Dörfler if (!_TimeoutShift(socket, timeout, tries)) { 5676cc7630fSAxel Dörfler close(socket); 5686cc7630fSAxel Dörfler return B_TIMED_OUT; 569f9af6566SAxel Dörfler } 570f9af6566SAxel Dörfler 5710892800fSPhilippe Houdoin _SendMessage(socket, state == INIT ? discover : request, 5720892800fSPhilippe Houdoin state != RENEWING ? broadcast : fServer); 573d77fe260SAxel Dörfler continue; 574b6ba1daaSPhilippe Houdoin 575593a0d2dSAxel Dörfler } else if (bytesReceived < 0) 576f9af6566SAxel Dörfler break; 577f9af6566SAxel Dörfler 578f9af6566SAxel Dörfler dhcp_message* message = (dhcp_message*)buffer; 579f9af6566SAxel Dörfler if (message->transaction_id != htonl(fTransactionID) 580f9af6566SAxel Dörfler || !message->HasOptions() 581f9af6566SAxel Dörfler || memcmp(message->mac_address, discover.mac_address, 582f9af6566SAxel Dörfler discover.hardware_address_length)) { 583f9af6566SAxel Dörfler // this message is not for us 584f9af6566SAxel Dörfler continue; 585f9af6566SAxel Dörfler } 586f9af6566SAxel Dörfler 5870892800fSPhilippe Houdoin // advance from startup state 5880892800fSPhilippe Houdoin if (state == INIT) 5890892800fSPhilippe Houdoin state = SELECTING; 5900892800fSPhilippe Houdoin else if (state == INIT_REBOOT) 5910892800fSPhilippe Houdoin state = REBOOTING; 5920892800fSPhilippe Houdoin 5930892800fSPhilippe Houdoin syslog(LOG_DEBUG, "%s: Received %s from %s\n", 5940892800fSPhilippe Houdoin Device(), dhcp_message::TypeToString(message->Type()), 5950892800fSPhilippe Houdoin _AddressToString(from.sin_addr.s_addr).String()); 5964ac66051SAxel Dörfler 597f9af6566SAxel Dörfler switch (message->Type()) { 598f9af6566SAxel Dörfler case DHCP_NONE: 599f9af6566SAxel Dörfler default: 600f9af6566SAxel Dörfler // ignore this message 601f9af6566SAxel Dörfler break; 602f9af6566SAxel Dörfler 603f9af6566SAxel Dörfler case DHCP_OFFER: 604f9af6566SAxel Dörfler { 605f9af6566SAxel Dörfler // first offer wins 6060892800fSPhilippe Houdoin if (state != SELECTING) 607f9af6566SAxel Dörfler break; 608f9af6566SAxel Dörfler 609f9af6566SAxel Dörfler // collect interface options 610f9af6566SAxel Dörfler 611f9af6566SAxel Dörfler fAssignedAddress = message->your_address; 61269b5cacbSPhilippe Houdoin syslog(LOG_INFO, " your_address: %s\n", 61369b5cacbSPhilippe Houdoin _AddressToString(fAssignedAddress).String()); 614f9af6566SAxel Dörfler 61510cc12daSAxel Dörfler fConfiguration.MakeEmpty(); 616293ed4feSAxel Dörfler fConfiguration.AddString("device", Device()); 6170892800fSPhilippe Houdoin fConfiguration.AddBool("auto_configured", true); 618f9af6566SAxel Dörfler 619f9af6566SAxel Dörfler BMessage address; 620f9af6566SAxel Dörfler address.AddString("family", "inet"); 621d5d2fdf0SPhilippe Houdoin address.AddString("address", _AddressToString(fAssignedAddress)); 62216e8f13aSAxel Dörfler fResolverConfiguration.MakeEmpty(); 62316e8f13aSAxel Dörfler _ParseOptions(*message, address, fResolverConfiguration); 624f9af6566SAxel Dörfler 62510cc12daSAxel Dörfler fConfiguration.AddMessage("address", &address); 626f9af6566SAxel Dörfler 62710cc12daSAxel Dörfler // request configuration from the server 628f9af6566SAxel Dörfler 6296cc7630fSAxel Dörfler _ResetTimeout(socket, timeout, tries); 630f9af6566SAxel Dörfler state = REQUESTING; 63146ff5400SAxel Dörfler _PrepareMessage(request, state); 632f9af6566SAxel Dörfler 6336cc7630fSAxel Dörfler status = _SendMessage(socket, request, broadcast); 63415ab0bcfSAxel Dörfler // we're sending a broadcast so that all potential offers 63515ab0bcfSAxel Dörfler // get an answer 63610cc12daSAxel Dörfler break; 637f9af6566SAxel Dörfler } 638f9af6566SAxel Dörfler 639f9af6566SAxel Dörfler case DHCP_ACK: 64010cc12daSAxel Dörfler { 6410892800fSPhilippe Houdoin if (state != REQUESTING 6420892800fSPhilippe Houdoin && state != REBOOTING 6430892800fSPhilippe Houdoin && state != REBINDING 6440892800fSPhilippe Houdoin && state != RENEWING) 645f9af6566SAxel Dörfler continue; 646f9af6566SAxel Dörfler 6476cc7630fSAxel Dörfler // TODO: we might want to configure the stuff, don't we? 6486cc7630fSAxel Dörfler BMessage address; 64916e8f13aSAxel Dörfler fResolverConfiguration.MakeEmpty(); 65016e8f13aSAxel Dörfler _ParseOptions(*message, address, fResolverConfiguration); 6514ac66051SAxel Dörfler // TODO: currently, only lease time and DNS is updated this 6524ac66051SAxel Dörfler // way 6536cc7630fSAxel Dörfler 654f9af6566SAxel Dörfler // our address request has been acknowledged 6550892800fSPhilippe Houdoin state = BOUND; 65610cc12daSAxel Dörfler 65710cc12daSAxel Dörfler // configure interface 65810cc12daSAxel Dörfler BMessage reply; 659d23fdd96SOliver Tappe status = Target().SendMessage(&fConfiguration, &reply); 660d23fdd96SOliver Tappe if (status == B_OK) 661d23fdd96SOliver Tappe status = reply.FindInt32("status", &fStatus); 66216e8f13aSAxel Dörfler 66316e8f13aSAxel Dörfler // configure resolver 66416e8f13aSAxel Dörfler reply.MakeEmpty(); 665d5d2fdf0SPhilippe Houdoin fResolverConfiguration.AddString("device", Device()); 66616e8f13aSAxel Dörfler status = Target().SendMessage(&fResolverConfiguration, &reply); 66716e8f13aSAxel Dörfler if (status == B_OK) 66816e8f13aSAxel Dörfler status = reply.FindInt32("status", &fStatus); 669f9af6566SAxel Dörfler break; 67010cc12daSAxel Dörfler } 671f9af6566SAxel Dörfler 672f9af6566SAxel Dörfler case DHCP_NACK: 6730892800fSPhilippe Houdoin if (state != REQUESTING 6740892800fSPhilippe Houdoin && state != REBOOTING 6750892800fSPhilippe Houdoin && state != REBINDING 6760892800fSPhilippe Houdoin && state != RENEWING) 677f9af6566SAxel Dörfler continue; 678f9af6566SAxel Dörfler 6790892800fSPhilippe Houdoin if (state == REBOOTING) { 6800892800fSPhilippe Houdoin // server reject our request on previous assigned address 6810892800fSPhilippe Houdoin // back to square one... 6820892800fSPhilippe Houdoin fAssignedAddress = 0; 6830892800fSPhilippe Houdoin } 6840892800fSPhilippe Houdoin 68515ab0bcfSAxel Dörfler // try again (maybe we should prefer other servers if this 68615ab0bcfSAxel Dörfler // happens more than once) 6876cc7630fSAxel Dörfler status = _SendMessage(socket, discover, broadcast); 6886cc7630fSAxel Dörfler if (status == B_OK) 689f9af6566SAxel Dörfler state = INIT; 690f9af6566SAxel Dörfler break; 691f9af6566SAxel Dörfler } 692f9af6566SAxel Dörfler } 693f9af6566SAxel Dörfler 694fb81684fSAxel Dörfler close(socket); 6956cc7630fSAxel Dörfler 6966cc7630fSAxel Dörfler if (status == B_OK && fLeaseTime > 0) { 6976cc7630fSAxel Dörfler // notify early enough when the lease is 69846ff5400SAxel Dörfler if (fRenewalTime == 0) 69946ff5400SAxel Dörfler fRenewalTime = fLeaseTime * 2/3; 70046ff5400SAxel Dörfler if (fRebindingTime == 0) 70146ff5400SAxel Dörfler fRebindingTime = fLeaseTime * 5/6; 70246ff5400SAxel Dörfler 703168cc3dfSRene Gollent bigtime_t now = system_time(); 70446ff5400SAxel Dörfler _RestartLease(fRenewalTime); 70546ff5400SAxel Dörfler 70646ff5400SAxel Dörfler fLeaseTime += now; 70746ff5400SAxel Dörfler fRenewalTime += now; 70846ff5400SAxel Dörfler fRebindingTime += now; 70946ff5400SAxel Dörfler // make lease times absolute 71046ff5400SAxel Dörfler } else { 7116cc7630fSAxel Dörfler fLeaseTime = previousLeaseTime; 71246ff5400SAxel Dörfler bigtime_t now = system_time(); 71346ff5400SAxel Dörfler fRenewalTime = (fLeaseTime - now) * 2/3 + now; 71446ff5400SAxel Dörfler fRebindingTime = (fLeaseTime - now) * 5/6 + now; 71546ff5400SAxel Dörfler } 7166cc7630fSAxel Dörfler 7176cc7630fSAxel Dörfler return status; 718fb81684fSAxel Dörfler } 719fb81684fSAxel Dörfler 720fb81684fSAxel Dörfler 7216cc7630fSAxel Dörfler void 7226cc7630fSAxel Dörfler DHCPClient::_RestartLease(bigtime_t leaseTime) 723fb81684fSAxel Dörfler { 7246cc7630fSAxel Dörfler if (leaseTime == 0) 725f9af6566SAxel Dörfler return; 726f9af6566SAxel Dörfler 7276cc7630fSAxel Dörfler BMessage lease(kMsgLeaseTime); 72846ff5400SAxel Dörfler fRunner = new BMessageRunner(this, &lease, leaseTime, 1); 729f9af6566SAxel Dörfler } 730f9af6566SAxel Dörfler 731f9af6566SAxel Dörfler 732f9af6566SAxel Dörfler void 73316e8f13aSAxel Dörfler DHCPClient::_ParseOptions(dhcp_message& message, BMessage& address, 73416e8f13aSAxel Dörfler BMessage& resolverConfiguration) 7350ce7725eSAxel Dörfler { 7360ce7725eSAxel Dörfler dhcp_option_cookie cookie; 7370ce7725eSAxel Dörfler message_option option; 7380ce7725eSAxel Dörfler const uint8* data; 7390ce7725eSAxel Dörfler size_t size; 7400ce7725eSAxel Dörfler while (message.NextOption(cookie, option, data, size)) { 7410ce7725eSAxel Dörfler // iterate through all options 7420ce7725eSAxel Dörfler switch (option) { 7430ce7725eSAxel Dörfler case OPTION_ROUTER_ADDRESS: 744cefe2a40SPhilippe Houdoin syslog(LOG_DEBUG, " gateway: %s\n", 74569b5cacbSPhilippe Houdoin _AddressToString(data).String()); 746d5d2fdf0SPhilippe Houdoin address.AddString("gateway", _AddressToString(data)); 7470ce7725eSAxel Dörfler break; 7480ce7725eSAxel Dörfler case OPTION_SUBNET_MASK: 749cefe2a40SPhilippe Houdoin syslog(LOG_DEBUG, " subnet: %s\n", 75069b5cacbSPhilippe Houdoin _AddressToString(data).String()); 751d5d2fdf0SPhilippe Houdoin address.AddString("mask", _AddressToString(data)); 7520ce7725eSAxel Dörfler break; 7535782c5a3SAxel Dörfler case OPTION_BROADCAST_ADDRESS: 754cefe2a40SPhilippe Houdoin syslog(LOG_DEBUG, " broadcast: %s\n", 75569b5cacbSPhilippe Houdoin _AddressToString(data).String()); 756d5d2fdf0SPhilippe Houdoin address.AddString("broadcast", _AddressToString(data)); 7575782c5a3SAxel Dörfler break; 7580ce7725eSAxel Dörfler case OPTION_DOMAIN_NAME_SERVER: 759a552ec13SAxel Dörfler { 7600ce7725eSAxel Dörfler for (uint32 i = 0; i < size / 4; i++) { 761cefe2a40SPhilippe Houdoin syslog(LOG_DEBUG, " nameserver[%d]: %s\n", i, 762d5d2fdf0SPhilippe Houdoin _AddressToString(&data[i * 4]).String()); 76316e8f13aSAxel Dörfler resolverConfiguration.AddString("nameserver", 764d5d2fdf0SPhilippe Houdoin _AddressToString(&data[i * 4]).String()); 76515ab0bcfSAxel Dörfler } 76616e8f13aSAxel Dörfler resolverConfiguration.AddInt32("nameserver_count", 76716e8f13aSAxel Dörfler size / 4); 7680ce7725eSAxel Dörfler break; 769a552ec13SAxel Dörfler } 770cefe2a40SPhilippe Houdoin case OPTION_SERVER_ADDRESS: 771cefe2a40SPhilippe Houdoin syslog(LOG_DEBUG, " server: %s\n", 77269b5cacbSPhilippe Houdoin _AddressToString(data).String()); 773ddf57b6cSAxel Dörfler fServer.SetAddress(*(in_addr_t*)data); 7740ce7725eSAxel Dörfler break; 77546ff5400SAxel Dörfler 7760ce7725eSAxel Dörfler case OPTION_ADDRESS_LEASE_TIME: 777cefe2a40SPhilippe Houdoin syslog(LOG_DEBUG, " lease time: %lu seconds\n", 77869b5cacbSPhilippe Houdoin ntohl(*(uint32*)data)); 77969b5cacbSPhilippe Houdoin fLeaseTime = ntohl(*(uint32*)data) * 1000000LL; 7800ce7725eSAxel Dörfler break; 7811a4e8e7bSAxel Dörfler case OPTION_RENEWAL_TIME: 782cefe2a40SPhilippe Houdoin syslog(LOG_DEBUG, " renewal time: %lu seconds\n", 78369b5cacbSPhilippe Houdoin ntohl(*(uint32*)data)); 78469b5cacbSPhilippe Houdoin fRenewalTime = ntohl(*(uint32*)data) * 1000000LL; 78546ff5400SAxel Dörfler break; 7861a4e8e7bSAxel Dörfler case OPTION_REBINDING_TIME: 787cefe2a40SPhilippe Houdoin syslog(LOG_DEBUG, " rebinding time: %lu seconds\n", 78869b5cacbSPhilippe Houdoin ntohl(*(uint32*)data)); 78969b5cacbSPhilippe Houdoin fRebindingTime = ntohl(*(uint32*)data) * 1000000LL; 7901a4e8e7bSAxel Dörfler break; 7911a4e8e7bSAxel Dörfler 7920ce7725eSAxel Dörfler case OPTION_HOST_NAME: 793cefe2a40SPhilippe Houdoin syslog(LOG_DEBUG, " host name: \"%.*s\"\n", (int)size, 79416e8f13aSAxel Dörfler (const char*)data); 7950ce7725eSAxel Dörfler break; 7965782c5a3SAxel Dörfler 7975782c5a3SAxel Dörfler case OPTION_DOMAIN_NAME: 7985782c5a3SAxel Dörfler { 79916e8f13aSAxel Dörfler char domain[256]; 80016e8f13aSAxel Dörfler strlcpy(domain, (const char*)data, 80116e8f13aSAxel Dörfler min_c(size + 1, sizeof(domain))); 8020f87f52fSStephan Aßmus 803cefe2a40SPhilippe Houdoin syslog(LOG_DEBUG, " domain name: \"%s\"\n", domain); 8040f87f52fSStephan Aßmus 80516e8f13aSAxel Dörfler resolverConfiguration.AddString("domain", domain); 8065782c5a3SAxel Dörfler break; 8075782c5a3SAxel Dörfler } 8080ce7725eSAxel Dörfler 8090ce7725eSAxel Dörfler case OPTION_MESSAGE_TYPE: 8100ce7725eSAxel Dörfler break; 8110ce7725eSAxel Dörfler 812d5d2fdf0SPhilippe Houdoin case OPTION_ERROR_MESSAGE: 81369b5cacbSPhilippe Houdoin syslog(LOG_INFO, " error message: \"%.*s\"\n", (int)size, 814d5d2fdf0SPhilippe Houdoin (const char*)data); 815d5d2fdf0SPhilippe Houdoin break; 816d5d2fdf0SPhilippe Houdoin 8170ce7725eSAxel Dörfler default: 818cefe2a40SPhilippe Houdoin syslog(LOG_DEBUG, " UNKNOWN OPTION %lu (0x%x)\n", 81969b5cacbSPhilippe Houdoin (uint32)option, (uint32)option); 8200ce7725eSAxel Dörfler break; 8210ce7725eSAxel Dörfler } 8220ce7725eSAxel Dörfler } 8230ce7725eSAxel Dörfler } 8240ce7725eSAxel Dörfler 8250ce7725eSAxel Dörfler 8260ce7725eSAxel Dörfler void 82746ff5400SAxel Dörfler DHCPClient::_PrepareMessage(dhcp_message& message, dhcp_state state) 8280ce7725eSAxel Dörfler { 8290ce7725eSAxel Dörfler message.opcode = BOOT_REQUEST; 8300ce7725eSAxel Dörfler message.hardware_type = ARP_HARDWARE_TYPE_ETHER; 8310ce7725eSAxel Dörfler message.hardware_address_length = 6; 8320ce7725eSAxel Dörfler message.transaction_id = htonl(fTransactionID); 833bcc8dadaSAxel Dörfler message.seconds_since_start = htons(min_c((system_time() - fStartTime) 83415ab0bcfSAxel Dörfler / 1000000LL, 65535)); 8350ce7725eSAxel Dörfler memcpy(message.mac_address, fMAC, 6); 8360ce7725eSAxel Dörfler 83746ff5400SAxel Dörfler message_type type = message.Type(); 83846ff5400SAxel Dörfler 839a073ba1aSHugo Santos uint8 *next = message.PrepareMessage(type); 840d5d2fdf0SPhilippe Houdoin 841d5d2fdf0SPhilippe Houdoin switch (type) { 842d5d2fdf0SPhilippe Houdoin case DHCP_DISCOVER: 843d5d2fdf0SPhilippe Houdoin next = message.PutOption(next, OPTION_REQUEST_PARAMETERS, 844d5d2fdf0SPhilippe Houdoin kRequestParameters, sizeof(kRequestParameters)); 845d5d2fdf0SPhilippe Houdoin break; 846d5d2fdf0SPhilippe Houdoin 847d5d2fdf0SPhilippe Houdoin case DHCP_REQUEST: 848d5d2fdf0SPhilippe Houdoin next = message.PutOption(next, OPTION_REQUEST_PARAMETERS, 849d5d2fdf0SPhilippe Houdoin kRequestParameters, sizeof(kRequestParameters)); 850d5d2fdf0SPhilippe Houdoin 851d5d2fdf0SPhilippe Houdoin if (state == REQUESTING) { 852d5d2fdf0SPhilippe Houdoin const sockaddr_in& server = (sockaddr_in&)fServer.SockAddr(); 853cefe2a40SPhilippe Houdoin next = message.PutOption(next, OPTION_SERVER_ADDRESS, 854d5d2fdf0SPhilippe Houdoin (uint32)server.sin_addr.s_addr); 855d5d2fdf0SPhilippe Houdoin } 856d5d2fdf0SPhilippe Houdoin 8570892800fSPhilippe Houdoin if (state == INIT || state == INIT_REBOOT 8580892800fSPhilippe Houdoin || state == REQUESTING) { 859d5d2fdf0SPhilippe Houdoin next = message.PutOption(next, OPTION_REQUEST_IP_ADDRESS, 860d5d2fdf0SPhilippe Houdoin (uint32)fAssignedAddress); 861d5d2fdf0SPhilippe Houdoin } else 862d5d2fdf0SPhilippe Houdoin message.client_address = fAssignedAddress; 863d5d2fdf0SPhilippe Houdoin break; 864d5d2fdf0SPhilippe Houdoin 865d5d2fdf0SPhilippe Houdoin case DHCP_RELEASE: { 866d5d2fdf0SPhilippe Houdoin const sockaddr_in& server = (sockaddr_in&)fServer.SockAddr(); 867cefe2a40SPhilippe Houdoin next = message.PutOption(next, OPTION_SERVER_ADDRESS, 868af074561SAxel Dörfler (uint32)server.sin_addr.s_addr); 86946ff5400SAxel Dörfler 87046ff5400SAxel Dörfler message.client_address = fAssignedAddress; 8710ce7725eSAxel Dörfler break; 8720ce7725eSAxel Dörfler } 8730ce7725eSAxel Dörfler 8740ce7725eSAxel Dörfler default: 8750ce7725eSAxel Dörfler break; 8760ce7725eSAxel Dörfler } 877d5d2fdf0SPhilippe Houdoin 878d5d2fdf0SPhilippe Houdoin message.FinishOptions(next); 8790ce7725eSAxel Dörfler } 8800ce7725eSAxel Dörfler 8810ce7725eSAxel Dörfler 8820ce7725eSAxel Dörfler void 8836cc7630fSAxel Dörfler DHCPClient::_ResetTimeout(int socket, time_t& timeout, uint32& tries) 884f9af6566SAxel Dörfler { 8856cc7630fSAxel Dörfler timeout = DEFAULT_TIMEOUT; 8866cc7630fSAxel Dörfler tries = 0; 887f9af6566SAxel Dörfler 888f9af6566SAxel Dörfler struct timeval value; 8896cc7630fSAxel Dörfler value.tv_sec = timeout; 890d09c1f8eSPhilippe Houdoin value.tv_usec = rand() % 1000000; 891f9af6566SAxel Dörfler setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &value, sizeof(value)); 892f9af6566SAxel Dörfler } 893f9af6566SAxel Dörfler 894f9af6566SAxel Dörfler 895f9af6566SAxel Dörfler bool 8966cc7630fSAxel Dörfler DHCPClient::_TimeoutShift(int socket, time_t& timeout, uint32& tries) 897f9af6566SAxel Dörfler { 8986cc7630fSAxel Dörfler timeout += timeout; 8996cc7630fSAxel Dörfler if (timeout > MAX_TIMEOUT) { 9006cc7630fSAxel Dörfler timeout = DEFAULT_TIMEOUT; 901f9af6566SAxel Dörfler 9026cc7630fSAxel Dörfler if (++tries > 2) 903f9af6566SAxel Dörfler return false; 904f9af6566SAxel Dörfler } 9050892800fSPhilippe Houdoin syslog(LOG_DEBUG, "%s: Timeout shift: %lu secs (try %lu)\n", 90661729d93SAxel Dörfler Device(), timeout, tries); 907f9af6566SAxel Dörfler 908f9af6566SAxel Dörfler struct timeval value; 9096cc7630fSAxel Dörfler value.tv_sec = timeout; 910d09c1f8eSPhilippe Houdoin value.tv_usec = rand() % 1000000; 911f9af6566SAxel Dörfler setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &value, sizeof(value)); 912f9af6566SAxel Dörfler 913f9af6566SAxel Dörfler return true; 914f9af6566SAxel Dörfler } 915f9af6566SAxel Dörfler 916f9af6566SAxel Dörfler 91784fdf2c3SPhilippe Houdoin /*static*/ BString 91884fdf2c3SPhilippe Houdoin DHCPClient::_AddressToString(const uint8* data) 919f9af6566SAxel Dörfler { 920f9af6566SAxel Dörfler BString target = inet_ntoa(*(in_addr*)data); 921f9af6566SAxel Dörfler return target; 922f9af6566SAxel Dörfler } 923f9af6566SAxel Dörfler 924f9af6566SAxel Dörfler 92584fdf2c3SPhilippe Houdoin /*static*/ BString 92684fdf2c3SPhilippe Houdoin DHCPClient::_AddressToString(in_addr_t address) 927f9af6566SAxel Dörfler { 928f9af6566SAxel Dörfler BString target = inet_ntoa(*(in_addr*)&address); 929f9af6566SAxel Dörfler return target; 930f9af6566SAxel Dörfler } 931f9af6566SAxel Dörfler 932f9af6566SAxel Dörfler 933f9af6566SAxel Dörfler status_t 9341a905a76SAxel Dörfler DHCPClient::_SendMessage(int socket, dhcp_message& message, 935af074561SAxel Dörfler const BNetworkAddress& address) const 936f9af6566SAxel Dörfler { 9370892800fSPhilippe Houdoin message_type type = message.Type(); 9380892800fSPhilippe Houdoin BString text; 9390892800fSPhilippe Houdoin text << dhcp_message::TypeToString(type); 9400892800fSPhilippe Houdoin 9410892800fSPhilippe Houdoin const uint8* requestAddress = message.FindOption(OPTION_REQUEST_IP_ADDRESS); 9420892800fSPhilippe Houdoin if (type == DHCP_REQUEST && requestAddress != NULL) 9430892800fSPhilippe Houdoin text << " for " << _AddressToString(requestAddress).String(); 9440892800fSPhilippe Houdoin 9450892800fSPhilippe Houdoin syslog(LOG_DEBUG, "%s: Send %s to %s\n", Device(), text.String(), 9460892800fSPhilippe Houdoin address.ToString().String()); 94761729d93SAxel Dörfler 948f9af6566SAxel Dörfler ssize_t bytesSent = sendto(socket, &message, message.Size(), 949af074561SAxel Dörfler address.IsBroadcast() ? MSG_BCAST : 0, address, address.Length()); 950f9af6566SAxel Dörfler if (bytesSent < 0) 951f9af6566SAxel Dörfler return errno; 952f9af6566SAxel Dörfler 953f9af6566SAxel Dörfler return B_OK; 954fb81684fSAxel Dörfler } 955fb81684fSAxel Dörfler 956fb81684fSAxel Dörfler 95746ff5400SAxel Dörfler dhcp_state 95846ff5400SAxel Dörfler DHCPClient::_CurrentState() const 95946ff5400SAxel Dörfler { 96046ff5400SAxel Dörfler bigtime_t now = system_time(); 96146ff5400SAxel Dörfler 96246ff5400SAxel Dörfler if (now > fLeaseTime || fStatus < B_OK) 96346ff5400SAxel Dörfler return INIT; 96446ff5400SAxel Dörfler if (now >= fRebindingTime) 96546ff5400SAxel Dörfler return REBINDING; 96646ff5400SAxel Dörfler if (now >= fRenewalTime) 9670892800fSPhilippe Houdoin return RENEWING; 96846ff5400SAxel Dörfler 96946ff5400SAxel Dörfler return BOUND; 97046ff5400SAxel Dörfler } 97146ff5400SAxel Dörfler 97246ff5400SAxel Dörfler 973fb81684fSAxel Dörfler void 974fb81684fSAxel Dörfler DHCPClient::MessageReceived(BMessage* message) 975fb81684fSAxel Dörfler { 9766cc7630fSAxel Dörfler switch (message->what) { 9776cc7630fSAxel Dörfler case kMsgLeaseTime: 97846ff5400SAxel Dörfler { 97946ff5400SAxel Dörfler dhcp_state state = _CurrentState(); 98046ff5400SAxel Dörfler 98146ff5400SAxel Dörfler bigtime_t next; 98246ff5400SAxel Dörfler if (_Negotiate(state) == B_OK) { 98346ff5400SAxel Dörfler switch (state) { 9840892800fSPhilippe Houdoin case RENEWING: 98546ff5400SAxel Dörfler next = fRebindingTime; 9866cc7630fSAxel Dörfler break; 98746ff5400SAxel Dörfler case REBINDING: 98846ff5400SAxel Dörfler default: 98946ff5400SAxel Dörfler next = fRenewalTime; 99046ff5400SAxel Dörfler break; 99146ff5400SAxel Dörfler } 99246ff5400SAxel Dörfler } else { 99346ff5400SAxel Dörfler switch (state) { 9940892800fSPhilippe Houdoin case RENEWING: 99546ff5400SAxel Dörfler next = (fLeaseTime - fRebindingTime) / 4 + system_time(); 99646ff5400SAxel Dörfler break; 99746ff5400SAxel Dörfler case REBINDING: 99846ff5400SAxel Dörfler default: 99946ff5400SAxel Dörfler next = (fLeaseTime - fRenewalTime) / 4 + system_time(); 100046ff5400SAxel Dörfler break; 100146ff5400SAxel Dörfler } 100246ff5400SAxel Dörfler } 100346ff5400SAxel Dörfler 100446ff5400SAxel Dörfler _RestartLease(next - system_time()); 100546ff5400SAxel Dörfler break; 100646ff5400SAxel Dörfler } 10076cc7630fSAxel Dörfler 10086cc7630fSAxel Dörfler default: 1009fb81684fSAxel Dörfler BHandler::MessageReceived(message); 10106cc7630fSAxel Dörfler break; 10116cc7630fSAxel Dörfler } 1012fb81684fSAxel Dörfler } 1013