btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / servers / net / DHCPClient.cpp
blob7ae8755cf399bb98ce63dbce59f3e5d1e79b4c17
1 /*
2 * Copyright 2006-2011, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Axel Dörfler, axeld@pinc-software.de
7 * Vegard Wærp, vegarwa@online.no
8 * Philippe Houdoin, <phoudoin at haiku-os dot org>
9 */
12 #include "DHCPClient.h"
14 #include <Message.h>
15 #include <MessageRunner.h>
16 #include <NetworkDevice.h>
17 #include <NetworkInterface.h>
19 #include <algorithm>
20 #include <arpa/inet.h>
21 #include <errno.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include <syslog.h>
26 #include <sys/sockio.h>
27 #include <sys/time.h>
28 #include <unistd.h>
30 #include <Debug.h>
31 #include <Message.h>
32 #include <MessageRunner.h>
34 #include "NetServer.h"
37 // See RFC 2131 for DHCP, see RFC 1533 for BOOTP/DHCP options
39 #define DHCP_CLIENT_PORT 68
40 #define DHCP_SERVER_PORT 67
42 #define DEFAULT_TIMEOUT 0.25 // secs
43 #define MAX_TIMEOUT 64 // secs
45 #define AS_USECS(t) (1000000 * t)
47 #define MAX_RETRIES 5
49 enum message_opcode {
50 BOOT_REQUEST = 1,
51 BOOT_REPLY
54 enum message_option {
55 OPTION_MAGIC = 0x63825363,
57 // generic options
58 OPTION_PAD = 0,
59 OPTION_END = 255,
60 OPTION_SUBNET_MASK = 1,
61 OPTION_TIME_OFFSET = 2,
62 OPTION_ROUTER_ADDRESS = 3,
63 OPTION_DOMAIN_NAME_SERVER = 6,
64 OPTION_HOST_NAME = 12,
65 OPTION_DOMAIN_NAME = 15,
66 OPTION_MAX_DATAGRAM_SIZE = 22,
67 OPTION_INTERFACE_MTU = 26,
68 OPTION_BROADCAST_ADDRESS = 28,
69 OPTION_NETWORK_TIME_PROTOCOL_SERVERS = 42,
70 OPTION_NETBIOS_NAME_SERVER = 44,
71 OPTION_NETBIOS_SCOPE = 47,
73 // DHCP specific options
74 OPTION_REQUEST_IP_ADDRESS = 50,
75 OPTION_ADDRESS_LEASE_TIME = 51,
76 OPTION_OVERLOAD = 52,
77 OPTION_MESSAGE_TYPE = 53,
78 OPTION_SERVER_ADDRESS = 54,
79 OPTION_REQUEST_PARAMETERS = 55,
80 OPTION_ERROR_MESSAGE = 56,
81 OPTION_MAX_MESSAGE_SIZE = 57,
82 OPTION_RENEWAL_TIME = 58,
83 OPTION_REBINDING_TIME = 59,
84 OPTION_CLASS_IDENTIFIER = 60,
85 OPTION_CLIENT_IDENTIFIER = 61,
88 enum message_type {
89 DHCP_NONE = 0,
90 DHCP_DISCOVER = 1,
91 DHCP_OFFER = 2,
92 DHCP_REQUEST = 3,
93 DHCP_DECLINE = 4,
94 DHCP_ACK = 5,
95 DHCP_NACK = 6,
96 DHCP_RELEASE = 7,
97 DHCP_INFORM = 8
100 struct dhcp_option_cookie {
101 dhcp_option_cookie()
103 state(0),
104 file_has_options(false),
105 server_name_has_options(false)
109 const uint8* next;
110 uint8 state;
111 bool file_has_options;
112 bool server_name_has_options;
115 struct dhcp_message {
116 dhcp_message(message_type type);
118 uint8 opcode;
119 uint8 hardware_type;
120 uint8 hardware_address_length;
121 uint8 hop_count;
122 uint32 transaction_id;
123 uint16 seconds_since_start;
124 uint16 flags;
125 in_addr_t client_address;
126 in_addr_t your_address;
127 in_addr_t server_address;
128 in_addr_t gateway_address;
129 uint8 mac_address[16];
130 uint8 server_name[64];
131 uint8 file[128];
132 uint32 options_magic;
133 uint8 options[1260];
135 size_t MinSize() const { return 576; }
136 size_t Size() const;
138 bool HasOptions() const;
139 bool NextOption(dhcp_option_cookie& cookie, message_option& option,
140 const uint8*& data, size_t& size) const;
141 const uint8* FindOption(message_option which) const;
142 const uint8* LastOption() const;
143 message_type Type() const;
145 uint8* PrepareMessage(uint8 type);
146 uint8* PutOption(uint8* options, message_option option);
147 uint8* PutOption(uint8* options, message_option option, uint8 data);
148 uint8* PutOption(uint8* options, message_option option, uint16 data);
149 uint8* PutOption(uint8* options, message_option option, uint32 data);
150 uint8* PutOption(uint8* options, message_option option, const uint8* data,
151 uint32 size);
152 uint8* FinishOptions(uint8* options);
154 static const char* TypeToString(message_type type);
155 } _PACKED;
157 struct socket_timeout {
158 socket_timeout(int socket)
160 timeout(time_t(AS_USECS(DEFAULT_TIMEOUT))),
161 tries(0)
163 UpdateSocket(socket);
166 time_t timeout; // in micro secs
167 uint8 tries;
169 bool Shift(int socket, bigtime_t stateMaxTime, const char* device);
170 void UpdateSocket(int socket) const;
173 #define DHCP_FLAG_BROADCAST 0x8000
175 #define ARP_HARDWARE_TYPE_ETHER 1
177 const uint32 kMsgLeaseTime = 'lstm';
179 static const uint8 kRequestParameters[] = {
180 OPTION_SUBNET_MASK, OPTION_ROUTER_ADDRESS,
181 OPTION_DOMAIN_NAME_SERVER, OPTION_BROADCAST_ADDRESS,
182 OPTION_DOMAIN_NAME
186 dhcp_message::dhcp_message(message_type type)
188 // ASSERT(this == offsetof(this, opcode));
189 memset(this, 0, sizeof(*this));
190 options_magic = htonl(OPTION_MAGIC);
192 uint8* next = PrepareMessage(type);
193 FinishOptions(next);
197 bool
198 dhcp_message::HasOptions() const
200 return options_magic == htonl(OPTION_MAGIC);
204 bool
205 dhcp_message::NextOption(dhcp_option_cookie& cookie,
206 message_option& option, const uint8*& data, size_t& size) const
208 if (!HasOptions())
209 return false;
211 if (cookie.state == 0) {
212 cookie.state++;
213 cookie.next = options;
216 uint32 bytesLeft = 0;
218 switch (cookie.state) {
219 case 1:
220 // options from "options"
221 bytesLeft = sizeof(options) - (cookie.next - options);
222 break;
224 case 2:
225 // options from "file"
226 bytesLeft = sizeof(file) - (cookie.next - file);
227 break;
229 case 3:
230 // options from "server_name"
231 bytesLeft = sizeof(server_name) - (cookie.next - server_name);
232 break;
235 while (true) {
236 if (bytesLeft == 0) {
237 cookie.state++;
239 // handle option overload in file and/or server_name fields.
240 switch (cookie.state) {
241 case 2:
242 // options from "file" field
243 if (cookie.file_has_options) {
244 bytesLeft = sizeof(file);
245 cookie.next = file;
247 break;
249 case 3:
250 // options from "server_name" field
251 if (cookie.server_name_has_options) {
252 bytesLeft = sizeof(server_name);
253 cookie.next = server_name;
255 break;
257 default:
258 // no more place to look for options
259 // if last option is not OPTION_END,
260 // there is no space left for other option!
261 if (option != OPTION_END)
262 cookie.next = NULL;
263 return false;
266 if (bytesLeft == 0) {
267 // no options in this state, try next one
268 continue;
272 option = (message_option)cookie.next[0];
273 if (option == OPTION_END) {
274 bytesLeft = 0;
275 continue;
276 } else if (option == OPTION_PAD) {
277 bytesLeft--;
278 cookie.next++;
279 continue;
282 size = cookie.next[1];
283 data = &cookie.next[2];
284 cookie.next += 2 + size;
285 bytesLeft -= 2 + size;
287 if (option == OPTION_OVERLOAD) {
288 cookie.file_has_options = data[0] & 1;
289 cookie.server_name_has_options = data[0] & 2;
290 continue;
293 return true;
298 const uint8*
299 dhcp_message::FindOption(message_option which) const
301 dhcp_option_cookie cookie;
302 message_option option;
303 const uint8* data;
304 size_t size;
305 while (NextOption(cookie, option, data, size)) {
306 // iterate through all options
307 if (option == which)
308 return data;
311 return NULL;
315 const uint8*
316 dhcp_message::LastOption() const
318 dhcp_option_cookie cookie;
319 message_option option;
320 const uint8* data;
321 size_t size;
322 while (NextOption(cookie, option, data, size)) {
323 // iterate through all options
326 return cookie.next;
330 message_type
331 dhcp_message::Type() const
333 const uint8* data = FindOption(OPTION_MESSAGE_TYPE);
334 if (data)
335 return (message_type)data[0];
337 return DHCP_NONE;
341 size_t
342 dhcp_message::Size() const
344 const uint8* last = LastOption();
346 if (last < options) {
347 // if last option is stored above "options" field, it means
348 // the whole options field and message is already filled...
349 return sizeof(dhcp_message);
352 return sizeof(dhcp_message) - sizeof(options) + last + 1 - options;
356 uint8*
357 dhcp_message::PrepareMessage(uint8 type)
359 uint8* next = options;
360 next = PutOption(next, OPTION_MESSAGE_TYPE, type);
361 next = PutOption(next, OPTION_MAX_MESSAGE_SIZE,
362 (uint16)htons(sizeof(dhcp_message)));
363 return next;
367 uint8*
368 dhcp_message::PutOption(uint8* options, message_option option)
370 options[0] = option;
371 return options + 1;
375 uint8*
376 dhcp_message::PutOption(uint8* options, message_option option, uint8 data)
378 return PutOption(options, option, &data, 1);
382 uint8*
383 dhcp_message::PutOption(uint8* options, message_option option, uint16 data)
385 return PutOption(options, option, (uint8*)&data, sizeof(data));
389 uint8*
390 dhcp_message::PutOption(uint8* options, message_option option, uint32 data)
392 return PutOption(options, option, (uint8*)&data, sizeof(data));
396 uint8*
397 dhcp_message::PutOption(uint8* options, message_option option,
398 const uint8* data, uint32 size)
400 // TODO: check enough space is available
402 options[0] = option;
403 options[1] = size;
404 memcpy(&options[2], data, size);
406 return options + 2 + size;
410 uint8*
411 dhcp_message::FinishOptions(uint8* options)
413 return PutOption(options, OPTION_END);
417 /*static*/ const char*
418 dhcp_message::TypeToString(message_type type)
420 switch (type) {
421 #define CASE(x) case x: return #x;
422 CASE(DHCP_NONE)
423 CASE(DHCP_DISCOVER)
424 CASE(DHCP_OFFER)
425 CASE(DHCP_REQUEST)
426 CASE(DHCP_DECLINE)
427 CASE(DHCP_ACK)
428 CASE(DHCP_NACK)
429 CASE(DHCP_RELEASE)
430 CASE(DHCP_INFORM)
431 #undef CASE
434 return "<unknown>";
438 void
439 socket_timeout::UpdateSocket(int socket) const
441 struct timeval value;
442 value.tv_sec = timeout / 1000000;
443 value.tv_usec = timeout % 1000000;
444 setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &value, sizeof(value));
448 bool
449 socket_timeout::Shift(int socket, bigtime_t stateMaxTime, const char* device)
451 tries++;
452 timeout += timeout;
453 if (timeout > AS_USECS(MAX_TIMEOUT))
454 timeout = AS_USECS(MAX_TIMEOUT);
456 if (tries > MAX_RETRIES) {
457 if (stateMaxTime == -1)
458 return false;
459 bigtime_t remaining = (stateMaxTime - system_time()) / 2 + 1;
460 timeout = std::max(remaining, bigtime_t(60));
463 syslog(LOG_DEBUG, "%s: Timeout shift: %lu secs (try %lu)\n",
464 device, timeout, tries);
466 UpdateSocket(socket);
467 return true;
471 // #pragma mark -
474 DHCPClient::DHCPClient(BMessenger target, const char* device)
476 AutoconfigClient("dhcp", target, device),
477 fConfiguration(kMsgConfigureInterface),
478 fResolverConfiguration(kMsgConfigureResolver),
479 fRunner(NULL),
480 fAssignedAddress(0),
481 fServer(AF_INET, NULL, DHCP_SERVER_PORT, B_UNCONFIGURED_ADDRESS_FAMILIES),
482 fLeaseTime(0)
484 fTransactionID = (uint32)system_time() ^ rand();
486 BNetworkAddress link;
487 BNetworkInterface interface(device);
488 fStatus = interface.GetHardwareAddress(link);
489 if (fStatus != B_OK)
490 return;
492 memcpy(fMAC, link.LinkLevelAddress(), sizeof(fMAC));
494 if ((interface.Flags() & IFF_AUTO_CONFIGURED) != 0) {
495 // Check for interface previous auto-configured address, if any.
496 BNetworkInterfaceAddress interfaceAddress;
497 int index = interface.FindFirstAddress(AF_INET);
498 if (index >= 0
499 && interface.GetAddressAt(index, interfaceAddress) == B_OK) {
500 BNetworkAddress address = interfaceAddress.Address();
501 const sockaddr_in& addr = (sockaddr_in&)address.SockAddr();
502 fAssignedAddress = addr.sin_addr.s_addr;
504 if ((ntohl(fAssignedAddress) & IN_CLASSB_NET) == 0xa9fe0000) {
505 // previous auto-configured address is a link-local one:
506 // there is no point asking a DHCP server to renew such
507 // server-less assigned address...
508 fAssignedAddress = 0;
513 openlog_thread("DHCP", 0, LOG_DAEMON);
517 DHCPClient::~DHCPClient()
519 if (fStatus != B_OK)
520 return;
522 delete fRunner;
524 int socket = ::socket(AF_INET, SOCK_DGRAM, 0);
525 if (socket < 0)
526 return;
528 // release lease
530 dhcp_message release(DHCP_RELEASE);
531 _PrepareMessage(release, BOUND);
533 _SendMessage(socket, release, fServer);
534 close(socket);
536 closelog_thread();
540 status_t
541 DHCPClient::Initialize()
543 fStatus = _Negotiate(fAssignedAddress == 0 ? INIT : INIT_REBOOT);
544 syslog(LOG_DEBUG, "%s: DHCP status = %s\n", Device(), strerror(fStatus));
545 return fStatus;
549 status_t
550 DHCPClient::_Negotiate(dhcp_state state)
552 if (state == BOUND)
553 return B_OK;
555 fStartTime = system_time();
556 fTransactionID++;
558 char hostName[MAXHOSTNAMELEN];
559 if (gethostname(hostName, MAXHOSTNAMELEN) == 0)
560 fHostName.SetTo(hostName, MAXHOSTNAMELEN);
561 else
562 fHostName.Truncate(0);
564 int socket = ::socket(AF_INET, SOCK_DGRAM, 0);
565 if (socket < 0)
566 return errno;
568 // Enable reusing the port. This is needed in case there is more
569 // than 1 interface that needs to be configured. Note that the only reason
570 // this works is because there is code below to bind to a specific
571 // interface.
572 int option = 1;
573 setsockopt(socket, SOL_SOCKET, SO_REUSEPORT, &option, sizeof(option));
575 BNetworkAddress local;
576 local.SetToWildcard(AF_INET, DHCP_CLIENT_PORT);
578 option = 1;
579 setsockopt(socket, SOL_SOCKET, SO_BROADCAST, &option, sizeof(option));
581 if (bind(socket, local, local.Length()) < 0) {
582 close(socket);
583 return errno;
586 bigtime_t previousLeaseTime = fLeaseTime;
588 status_t status = B_OK;
589 while (state != BOUND) {
590 status = _StateTransition(socket, state);
591 if (status != B_OK && (state == SELECTING || state == REBOOTING))
592 break;
595 close(socket);
597 if (fLeaseTime == 0)
598 fLeaseTime = previousLeaseTime;
599 if (fLeaseTime == 0)
600 fLeaseTime = 60;
602 if (fRenewalTime == 0)
603 fRenewalTime = fLeaseTime / 2;
604 if (fRebindingTime == 0)
605 fRebindingTime = fLeaseTime * 7 / 8;
606 fLeaseTime += fRequestTime;
607 fRenewalTime += fRequestTime;
608 fRebindingTime += fRequestTime;
609 _RestartLease(fRenewalTime);
611 fStatus = status;
612 if (status)
613 return status;
615 // configure interface
616 BMessage reply;
617 status = Target().SendMessage(&fConfiguration, &reply);
618 if (status == B_OK)
619 status = reply.FindInt32("status", &fStatus);
621 // configure resolver
622 reply.MakeEmpty();
623 fResolverConfiguration.AddString("device", Device());
624 status = Target().SendMessage(&fResolverConfiguration, &reply);
625 if (status == B_OK)
626 status = reply.FindInt32("status", &fStatus);
627 return status;
631 status_t
632 DHCPClient::_GotMessage(dhcp_state& state, dhcp_message* message)
634 switch (state) {
635 case SELECTING:
636 if (message->Type() == DHCP_OFFER) {
637 state = REQUESTING;
639 fAssignedAddress = message->your_address;
640 syslog(LOG_INFO, " your_address: %s\n",
641 _AddressToString(fAssignedAddress).String());
643 fConfiguration.MakeEmpty();
644 fConfiguration.AddString("device", Device());
645 fConfiguration.AddBool("auto_configured", true);
647 BMessage address;
648 address.AddString("family", "inet");
649 address.AddString("address", _AddressToString(fAssignedAddress));
650 fResolverConfiguration.MakeEmpty();
651 _ParseOptions(*message, address, fResolverConfiguration);
653 fConfiguration.AddMessage("address", &address);
654 return B_OK;
657 return B_BAD_VALUE;
659 case REBOOTING:
660 case REBINDING:
661 case RENEWING:
662 case REQUESTING:
663 if (message->Type() == DHCP_ACK) {
664 // TODO: we might want to configure the stuff, don't we?
665 BMessage address;
666 fResolverConfiguration.MakeEmpty();
667 _ParseOptions(*message, address, fResolverConfiguration);
668 // TODO: currently, only lease time and DNS is updated this
669 // way
671 // our address request has been acknowledged
672 state = BOUND;
674 return B_OK;
677 if (message->Type() == DHCP_NACK) {
678 // server reject our request on previous assigned address
679 // back to square one...
680 fAssignedAddress = 0;
681 state = INIT;
682 return B_OK;
685 default:
686 return B_BAD_VALUE;
691 status_t
692 DHCPClient::_StateTransition(int socket, dhcp_state& state)
694 if (state == INIT) {
695 // The local interface does not have an address yet, bind the socket
696 // to the device directly.
697 BNetworkDevice device(Device());
698 int index = device.Index();
700 setsockopt(socket, SOL_SOCKET, SO_BINDTODEVICE, &index, sizeof(int));
703 BNetworkAddress broadcast;
704 broadcast.SetToBroadcast(AF_INET, DHCP_SERVER_PORT);
706 socket_timeout timeout(socket);
708 dhcp_message discover(DHCP_DISCOVER);
709 _PrepareMessage(discover, state);
711 dhcp_message request(DHCP_REQUEST);
712 _PrepareMessage(request, state);
714 bool skipRequest = false;
715 dhcp_state originalState = state;
716 fRequestTime = system_time();
717 while (true) {
718 if (!skipRequest) {
719 _SendMessage(socket, originalState == INIT ? discover : request,
720 originalState == RENEWING ? fServer : broadcast);
722 if (originalState == INIT)
723 state = SELECTING;
724 else if (originalState == INIT_REBOOT)
725 state = REBOOTING;
728 char buffer[2048];
729 struct sockaddr_in from;
730 socklen_t fromLength = sizeof(from);
731 ssize_t bytesReceived = recvfrom(socket, buffer, sizeof(buffer),
732 0, (struct sockaddr*)&from, &fromLength);
733 if (bytesReceived < 0 && errno == B_TIMED_OUT) {
734 // depending on the state, we'll just try again
735 if (!_TimeoutShift(socket, state, timeout))
736 return B_TIMED_OUT;
737 skipRequest = false;
738 continue;
739 } else if (bytesReceived < 0)
740 return errno;
742 skipRequest = true;
743 dhcp_message* message = (dhcp_message*)buffer;
744 if (message->transaction_id != htonl(fTransactionID)
745 || !message->HasOptions()
746 || memcmp(message->mac_address, discover.mac_address,
747 discover.hardware_address_length)) {
748 // this message is not for us
749 continue;
752 syslog(LOG_DEBUG, "%s: Received %s from %s\n",
753 Device(), dhcp_message::TypeToString(message->Type()),
754 _AddressToString(from.sin_addr.s_addr).String());
756 if (_GotMessage(state, message) == B_OK)
757 break;
760 return B_OK;
764 void
765 DHCPClient::_RestartLease(bigtime_t leaseTime)
767 if (leaseTime == 0)
768 return;
770 BMessage lease(kMsgLeaseTime);
771 fRunner = new BMessageRunner(this, &lease, leaseTime - system_time(), 1);
775 void
776 DHCPClient::_ParseOptions(dhcp_message& message, BMessage& address,
777 BMessage& resolverConfiguration)
779 dhcp_option_cookie cookie;
780 message_option option;
781 const uint8* data;
782 size_t size;
783 while (message.NextOption(cookie, option, data, size)) {
784 // iterate through all options
785 switch (option) {
786 case OPTION_ROUTER_ADDRESS:
787 syslog(LOG_DEBUG, " gateway: %s\n",
788 _AddressToString(data).String());
789 address.AddString("gateway", _AddressToString(data));
790 break;
791 case OPTION_SUBNET_MASK:
792 syslog(LOG_DEBUG, " subnet: %s\n",
793 _AddressToString(data).String());
794 address.AddString("mask", _AddressToString(data));
795 break;
796 case OPTION_BROADCAST_ADDRESS:
797 syslog(LOG_DEBUG, " broadcast: %s\n",
798 _AddressToString(data).String());
799 address.AddString("broadcast", _AddressToString(data));
800 break;
801 case OPTION_DOMAIN_NAME_SERVER:
803 for (uint32 i = 0; i < size / 4; i++) {
804 syslog(LOG_DEBUG, " nameserver[%d]: %s\n", i,
805 _AddressToString(&data[i * 4]).String());
806 resolverConfiguration.AddString("nameserver",
807 _AddressToString(&data[i * 4]).String());
809 resolverConfiguration.AddInt32("nameserver_count",
810 size / 4);
811 break;
813 case OPTION_SERVER_ADDRESS:
815 syslog(LOG_DEBUG, " server: %s\n",
816 _AddressToString(data).String());
817 status_t status = fServer.SetAddress(*(in_addr_t*)data);
818 if (status != B_OK) {
819 syslog(LOG_ERR, " BNetworkAddress::SetAddress failed with %s!\n",
820 strerror(status));
821 fServer.Unset();
823 break;
826 case OPTION_ADDRESS_LEASE_TIME:
827 syslog(LOG_DEBUG, " lease time: %lu seconds\n",
828 ntohl(*(uint32*)data));
829 fLeaseTime = ntohl(*(uint32*)data) * 1000000LL;
830 break;
831 case OPTION_RENEWAL_TIME:
832 syslog(LOG_DEBUG, " renewal time: %lu seconds\n",
833 ntohl(*(uint32*)data));
834 fRenewalTime = ntohl(*(uint32*)data) * 1000000LL;
835 break;
836 case OPTION_REBINDING_TIME:
837 syslog(LOG_DEBUG, " rebinding time: %lu seconds\n",
838 ntohl(*(uint32*)data));
839 fRebindingTime = ntohl(*(uint32*)data) * 1000000LL;
840 break;
842 case OPTION_HOST_NAME:
843 syslog(LOG_DEBUG, " host name: \"%.*s\"\n", (int)size,
844 (const char*)data);
845 break;
847 case OPTION_DOMAIN_NAME:
849 char domain[256];
850 strlcpy(domain, (const char*)data,
851 min_c(size + 1, sizeof(domain)));
853 syslog(LOG_DEBUG, " domain name: \"%s\"\n", domain);
855 resolverConfiguration.AddString("domain", domain);
856 break;
859 case OPTION_MESSAGE_TYPE:
860 break;
862 case OPTION_ERROR_MESSAGE:
863 syslog(LOG_INFO, " error message: \"%.*s\"\n", (int)size,
864 (const char*)data);
865 break;
867 default:
868 syslog(LOG_DEBUG, " UNKNOWN OPTION %lu (0x%x)\n",
869 (uint32)option, (uint32)option);
870 break;
876 void
877 DHCPClient::_PrepareMessage(dhcp_message& message, dhcp_state state)
879 message.opcode = BOOT_REQUEST;
880 message.hardware_type = ARP_HARDWARE_TYPE_ETHER;
881 message.hardware_address_length = 6;
882 message.transaction_id = htonl(fTransactionID);
883 message.seconds_since_start = htons(min_c((system_time() - fStartTime)
884 / 1000000LL, 65535));
885 memcpy(message.mac_address, fMAC, 6);
887 message_type type = message.Type();
889 uint8 *next = message.PrepareMessage(type);
891 switch (type) {
892 case DHCP_DISCOVER:
893 next = message.PutOption(next, OPTION_REQUEST_PARAMETERS,
894 kRequestParameters, sizeof(kRequestParameters));
896 if (fHostName.Length() > 0) {
897 next = message.PutOption(next, OPTION_HOST_NAME,
898 reinterpret_cast<const uint8*>(fHostName.String()),
899 fHostName.Length());
901 break;
903 case DHCP_REQUEST:
904 next = message.PutOption(next, OPTION_REQUEST_PARAMETERS,
905 kRequestParameters, sizeof(kRequestParameters));
907 if (fHostName.Length() > 0) {
908 next = message.PutOption(next, OPTION_HOST_NAME,
909 reinterpret_cast<const uint8*>(fHostName.String()),
910 fHostName.Length());
913 if (state == REQUESTING) {
914 const sockaddr_in& server = (sockaddr_in&)fServer.SockAddr();
915 next = message.PutOption(next, OPTION_SERVER_ADDRESS,
916 (uint32)server.sin_addr.s_addr);
919 if (state == INIT || state == INIT_REBOOT
920 || state == REQUESTING) {
921 next = message.PutOption(next, OPTION_REQUEST_IP_ADDRESS,
922 (uint32)fAssignedAddress);
923 } else
924 message.client_address = fAssignedAddress;
925 break;
927 case DHCP_RELEASE: {
928 const sockaddr_in& server = (sockaddr_in&)fServer.SockAddr();
929 next = message.PutOption(next, OPTION_SERVER_ADDRESS,
930 (uint32)server.sin_addr.s_addr);
932 message.client_address = fAssignedAddress;
933 break;
936 default:
937 break;
940 message.FinishOptions(next);
944 bool
945 DHCPClient::_TimeoutShift(int socket, dhcp_state& state,
946 socket_timeout& timeout)
948 bigtime_t stateMaxTime = -1;
949 if (state == RENEWING)
950 stateMaxTime = fRebindingTime;
951 else if (state == REBINDING)
952 stateMaxTime = fLeaseTime;
954 if (system_time() > stateMaxTime) {
955 state = state == REBINDING ? INIT : REBINDING;
956 return false;
959 return timeout.Shift(socket, stateMaxTime, Device());
963 /*static*/ BString
964 DHCPClient::_AddressToString(const uint8* data)
966 BString target = inet_ntoa(*(in_addr*)data);
967 return target;
971 /*static*/ BString
972 DHCPClient::_AddressToString(in_addr_t address)
974 BString target = inet_ntoa(*(in_addr*)&address);
975 return target;
979 status_t
980 DHCPClient::_SendMessage(int socket, dhcp_message& message,
981 const BNetworkAddress& address) const
983 message_type type = message.Type();
984 BString text;
985 text << dhcp_message::TypeToString(type);
987 const uint8* requestAddress = message.FindOption(OPTION_REQUEST_IP_ADDRESS);
988 if (type == DHCP_REQUEST && requestAddress != NULL)
989 text << " for " << _AddressToString(requestAddress).String();
991 syslog(LOG_DEBUG, "%s: Send %s to %s\n", Device(), text.String(),
992 address.ToString().String());
994 ssize_t bytesSent = sendto(socket, &message, message.Size(),
995 address.IsBroadcast() ? MSG_BCAST : 0, address, address.Length());
996 if (bytesSent < 0)
997 return errno;
999 return B_OK;
1003 dhcp_state
1004 DHCPClient::_CurrentState() const
1006 bigtime_t now = system_time();
1008 if (now > fLeaseTime || fStatus != B_OK)
1009 return INIT;
1010 if (now >= fRebindingTime)
1011 return REBINDING;
1012 if (now >= fRenewalTime)
1013 return RENEWING;
1014 return BOUND;
1018 void
1019 DHCPClient::MessageReceived(BMessage* message)
1021 switch (message->what) {
1022 case kMsgLeaseTime:
1023 _Negotiate(_CurrentState());
1024 break;
1026 default:
1027 BHandler::MessageReceived(message);
1028 break;