1/*
2 * Copyright 2006-2018, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Axel D��rfler, axeld@pinc-software.de
7 *		Vegard W��rp, vegarwa@online.no
8 *		Philippe Houdoin, <phoudoin at haiku-os dot org>
9 */
10
11
12#include "DHCPClient.h"
13
14#include <Message.h>
15#include <MessageRunner.h>
16#include <NetworkDevice.h>
17#include <NetworkInterface.h>
18
19#include <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>
29
30#include <Debug.h>
31#include <Message.h>
32#include <MessageRunner.h>
33
34#include "NetServer.h"
35
36
37// See RFC 2131 for DHCP, see RFC 1533 for BOOTP/DHCP options
38
39#define DHCP_CLIENT_PORT	68
40#define DHCP_SERVER_PORT	67
41
42#define DEFAULT_TIMEOUT		0.25	// secs
43#define MAX_TIMEOUT			64		// secs
44
45#define AS_USECS(t) (1000000 * t)
46
47#define MAX_RETRIES			5
48
49enum message_opcode {
50	BOOT_REQUEST = 1,
51	BOOT_REPLY
52};
53
54enum message_option {
55	OPTION_MAGIC = 0x63825363,
56
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,
72
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,
86};
87
88enum 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
98};
99
100struct dhcp_option_cookie {
101	dhcp_option_cookie()
102		:
103		state(0),
104		file_has_options(false),
105		server_name_has_options(false)
106	{
107	}
108
109	const uint8* next;
110	uint8	state;
111	bool	file_has_options;
112	bool	server_name_has_options;
113};
114
115struct dhcp_message {
116	dhcp_message(message_type type);
117
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];
134
135	size_t MinSize() const { return 576; }
136	size_t Size() const;
137
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;
144
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);
153
154	static const char* TypeToString(message_type type);
155} _PACKED;
156
157struct socket_timeout {
158	socket_timeout(int socket)
159		:
160		timeout(time_t(AS_USECS(DEFAULT_TIMEOUT))),
161		tries(0)
162	{
163		UpdateSocket(socket);
164	}
165
166	bigtime_t timeout; // in micro secs
167	uint8 tries;
168
169	bool Shift(int socket, bigtime_t stateMaxTime, const char* device);
170	void UpdateSocket(int socket) const;
171};
172
173#define DHCP_FLAG_BROADCAST		0x8000
174
175#define ARP_HARDWARE_TYPE_ETHER	1
176
177const uint32 kMsgLeaseTime = 'lstm';
178
179static const uint8 kRequestParameters[] = {
180	OPTION_SUBNET_MASK, OPTION_ROUTER_ADDRESS,
181	OPTION_DOMAIN_NAME_SERVER, OPTION_BROADCAST_ADDRESS,
182	OPTION_DOMAIN_NAME
183};
184
185
186dhcp_message::dhcp_message(message_type type)
187{
188	// ASSERT(this == offsetof(this, opcode));
189	memset(this, 0, sizeof(*this));
190	options_magic = htonl(OPTION_MAGIC);
191
192	uint8* next = PrepareMessage(type);
193	FinishOptions(next);
194}
195
196
197bool
198dhcp_message::HasOptions() const
199{
200	return options_magic == htonl(OPTION_MAGIC);
201}
202
203
204bool
205dhcp_message::NextOption(dhcp_option_cookie& cookie,
206	message_option& option, const uint8*& data, size_t& size) const
207{
208	if (!HasOptions())
209		return false;
210
211	if (cookie.state == 0) {
212		cookie.state++;
213		cookie.next = options;
214	}
215
216	uint32 bytesLeft = 0;
217
218	switch (cookie.state) {
219		case 1:
220			// options from "options"
221			bytesLeft = sizeof(options) - (cookie.next - options);
222			break;
223
224		case 2:
225			// options from "file"
226			bytesLeft = sizeof(file) - (cookie.next - file);
227			break;
228
229		case 3:
230			// options from "server_name"
231			bytesLeft = sizeof(server_name) - (cookie.next - server_name);
232			break;
233	}
234
235	while (true) {
236		if (bytesLeft == 0) {
237			cookie.state++;
238
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;
246					}
247					break;
248
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;
254					}
255					break;
256
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;
264			}
265
266			if (bytesLeft == 0) {
267				// no options in this state, try next one
268				continue;
269			}
270		}
271
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;
280		}
281
282		size = cookie.next[1];
283		data = &cookie.next[2];
284		cookie.next += 2 + size;
285		bytesLeft -= 2 + size;
286
287		if (option == OPTION_OVERLOAD) {
288			cookie.file_has_options = data[0] & 1;
289			cookie.server_name_has_options = data[0] & 2;
290			continue;
291		}
292
293		return true;
294	}
295}
296
297
298const uint8*
299dhcp_message::FindOption(message_option which) const
300{
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;
309	}
310
311	return NULL;
312}
313
314
315const uint8*
316dhcp_message::LastOption() const
317{
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
324	}
325
326	return cookie.next;
327}
328
329
330message_type
331dhcp_message::Type() const
332{
333	const uint8* data = FindOption(OPTION_MESSAGE_TYPE);
334	if (data)
335		return (message_type)data[0];
336
337	return DHCP_NONE;
338}
339
340
341size_t
342dhcp_message::Size() const
343{
344	const uint8* last = LastOption();
345
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);
350	}
351
352	return sizeof(dhcp_message) - sizeof(options) + last + 1 - options;
353}
354
355
356uint8*
357dhcp_message::PrepareMessage(uint8 type)
358{
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;
364}
365
366
367uint8*
368dhcp_message::PutOption(uint8* options, message_option option)
369{
370	options[0] = option;
371	return options + 1;
372}
373
374
375uint8*
376dhcp_message::PutOption(uint8* options, message_option option, uint8 data)
377{
378	return PutOption(options, option, &data, 1);
379}
380
381
382uint8*
383dhcp_message::PutOption(uint8* options, message_option option, uint16 data)
384{
385	return PutOption(options, option, (uint8*)&data, sizeof(data));
386}
387
388
389uint8*
390dhcp_message::PutOption(uint8* options, message_option option, uint32 data)
391{
392	return PutOption(options, option, (uint8*)&data, sizeof(data));
393}
394
395
396uint8*
397dhcp_message::PutOption(uint8* options, message_option option,
398	const uint8* data, uint32 size)
399{
400	// TODO: check enough space is available
401
402	options[0] = option;
403	options[1] = size;
404	memcpy(&options[2], data, size);
405
406	return options + 2 + size;
407}
408
409
410uint8*
411dhcp_message::FinishOptions(uint8* options)
412{
413	return PutOption(options, OPTION_END);
414}
415
416
417/*static*/ const char*
418dhcp_message::TypeToString(message_type type)
419{
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
432	}
433
434	return "<unknown>";
435}
436
437
438void
439socket_timeout::UpdateSocket(int socket) const
440{
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));
445}
446
447
448bool
449socket_timeout::Shift(int socket, bigtime_t stateMaxTime, const char* device)
450{
451	if (tries == UINT8_MAX)
452		return false;
453
454	tries++;
455
456	if (tries > MAX_RETRIES) {
457		bigtime_t now = system_time();
458		if (stateMaxTime == -1 || stateMaxTime < now)
459			return false;
460		bigtime_t remaining = (stateMaxTime - now) / 2 + 1;
461		timeout = std::max(remaining, bigtime_t(AS_USECS(60)));
462	} else
463		timeout += timeout;
464
465	if (timeout > AS_USECS(MAX_TIMEOUT))
466		timeout = AS_USECS(MAX_TIMEOUT);
467
468	syslog(LOG_DEBUG, "%s: Timeout shift: %" B_PRIdTIME " msecs (try %" B_PRIu8 ")\n",
469		device, timeout / 1000, tries);
470
471	UpdateSocket(socket);
472	return true;
473}
474
475
476//	#pragma mark -
477
478
479DHCPClient::DHCPClient(BMessenger target, const char* device)
480	:
481	AutoconfigClient("dhcp", target, device),
482	fConfiguration(kMsgConfigureInterface),
483	fResolverConfiguration(kMsgConfigureResolver),
484	fRunner(NULL),
485	fAssignedAddress(0),
486	fServer(AF_INET, NULL, DHCP_SERVER_PORT, B_UNCONFIGURED_ADDRESS_FAMILIES),
487	fLeaseTime(0)
488{
489	fTransactionID = (uint32)system_time() ^ rand();
490
491	BNetworkAddress link;
492	BNetworkInterface interface(device);
493	fStatus = interface.GetHardwareAddress(link);
494	if (fStatus != B_OK)
495		return;
496
497	memcpy(fMAC, link.LinkLevelAddress(), sizeof(fMAC));
498
499	if ((interface.Flags() & IFF_AUTO_CONFIGURED) != 0) {
500		// Check for interface previous auto-configured address, if any.
501		BNetworkInterfaceAddress interfaceAddress;
502		int index = interface.FindFirstAddress(AF_INET);
503		if (index >= 0
504			&& interface.GetAddressAt(index, interfaceAddress) == B_OK) {
505			BNetworkAddress address = interfaceAddress.Address();
506			const sockaddr_in& addr = (sockaddr_in&)address.SockAddr();
507			fAssignedAddress = addr.sin_addr.s_addr;
508
509			if ((ntohl(fAssignedAddress) & IN_CLASSB_NET) == 0xa9fe0000) {
510				// previous auto-configured address is a link-local one:
511				// there is no point asking a DHCP server to renew such
512				// server-less assigned address...
513				fAssignedAddress = 0;
514			}
515		}
516	}
517
518	openlog_thread("DHCP", 0, LOG_DAEMON);
519}
520
521
522DHCPClient::~DHCPClient()
523{
524	if (fStatus != B_OK)
525		return;
526
527	delete fRunner;
528
529	int socket = ::socket(AF_INET, SOCK_DGRAM, 0);
530	if (socket < 0)
531		return;
532
533	// release lease
534
535	dhcp_message release(DHCP_RELEASE);
536	_PrepareMessage(release, BOUND);
537
538	_SendMessage(socket, release, fServer);
539	close(socket);
540
541	closelog_thread();
542}
543
544
545status_t
546DHCPClient::Initialize()
547{
548	fStatus = _Negotiate(fAssignedAddress == 0 ? INIT : INIT_REBOOT);
549	syslog(LOG_DEBUG, "%s: DHCP status = %s\n", Device(), strerror(fStatus));
550	return fStatus;
551}
552
553
554status_t
555DHCPClient::_Negotiate(dhcp_state state)
556{
557	if (state == BOUND)
558		return B_OK;
559
560	fStartTime = system_time();
561	fTransactionID++;
562
563	char hostName[MAXHOSTNAMELEN];
564	if (gethostname(hostName, MAXHOSTNAMELEN) == 0)
565		fHostName.SetTo(hostName, MAXHOSTNAMELEN);
566	else
567		fHostName.Truncate(0);
568
569	int socket = ::socket(AF_INET, SOCK_DGRAM, 0);
570	if (socket < 0)
571		return errno;
572
573	// Enable reusing the port. This is needed in case there is more
574	// than 1 interface that needs to be configured. Note that the only reason
575	// this works is because there is code below to bind to a specific
576	// interface.
577	int option = 1;
578	setsockopt(socket, SOL_SOCKET, SO_REUSEPORT, &option, sizeof(option));
579
580	BNetworkAddress local;
581	local.SetToWildcard(AF_INET, DHCP_CLIENT_PORT);
582
583	option = 1;
584	setsockopt(socket, SOL_SOCKET, SO_BROADCAST, &option, sizeof(option));
585
586	if (bind(socket, local, local.Length()) < 0) {
587		close(socket);
588		return errno;
589	}
590
591	bigtime_t previousLeaseTime = fLeaseTime;
592
593	status_t status = B_OK;
594	while (state != BOUND) {
595		status = _StateTransition(socket, state);
596		if (status != B_OK && (state == SELECTING || state == REBOOTING))
597			break;
598	}
599
600	close(socket);
601
602	if (fLeaseTime == 0)
603		fLeaseTime = previousLeaseTime;
604	if (fLeaseTime == 0)
605		fLeaseTime = 60;
606
607	if (fRenewalTime == 0)
608		fRenewalTime = fLeaseTime / 2;
609	if (fRebindingTime == 0)
610		fRebindingTime = fLeaseTime * 7 / 8;
611	fLeaseTime += fRequestTime;
612	fRenewalTime += fRequestTime;
613	fRebindingTime += fRequestTime;
614	_RestartLease(fRenewalTime);
615
616	fStatus = status;
617	if (status)
618		return status;
619
620	// configure interface
621	BMessage reply;
622	status = Target().SendMessage(&fConfiguration, &reply);
623	if (status == B_OK)
624		status = reply.FindInt32("status", &fStatus);
625
626	// configure resolver
627	reply.MakeEmpty();
628	fResolverConfiguration.AddString("device", Device());
629	status = Target().SendMessage(&fResolverConfiguration, &reply);
630	if (status == B_OK)
631		status = reply.FindInt32("status", &fStatus);
632	return status;
633}
634
635
636status_t
637DHCPClient::_GotMessage(dhcp_state& state, dhcp_message* message)
638{
639	switch (state) {
640		case SELECTING:
641			if (message->Type() == DHCP_OFFER) {
642				state = REQUESTING;
643
644				fAssignedAddress = message->your_address;
645				syslog(LOG_INFO, "  your_address: %s\n",
646						_AddressToString(fAssignedAddress).String());
647
648				fConfiguration.MakeEmpty();
649				fConfiguration.AddString("device", Device());
650				fConfiguration.AddBool("auto_configured", true);
651
652				BMessage address;
653				address.AddString("family", "inet");
654				address.AddString("address", _AddressToString(fAssignedAddress));
655				fResolverConfiguration.MakeEmpty();
656				_ParseOptions(*message, address, fResolverConfiguration);
657
658				fConfiguration.AddMessage("address", &address);
659				return B_OK;
660			}
661
662			return B_BAD_VALUE;
663
664		case REBOOTING:
665		case REBINDING:
666		case RENEWING:
667		case REQUESTING:
668			if (message->Type() == DHCP_ACK) {
669				// TODO: we might want to configure the stuff, don't we?
670				BMessage address;
671				fResolverConfiguration.MakeEmpty();
672				_ParseOptions(*message, address, fResolverConfiguration);
673					// TODO: currently, only lease time and DNS is updated this
674					// way
675
676				// our address request has been acknowledged
677				state = BOUND;
678
679				return B_OK;
680			}
681
682			if (message->Type() == DHCP_NACK) {
683				// server reject our request on previous assigned address
684				// back to square one...
685				fAssignedAddress = 0;
686				state = INIT;
687				return B_OK;
688			}
689
690		default:
691			return B_BAD_VALUE;
692	}
693}
694
695
696status_t
697DHCPClient::_StateTransition(int socket, dhcp_state& state)
698{
699	if (state == INIT) {
700		// The local interface does not have an address yet, bind the socket
701		// to the device directly.
702		BNetworkDevice device(Device());
703		int index = device.Index();
704
705		setsockopt(socket, SOL_SOCKET, SO_BINDTODEVICE, &index, sizeof(int));
706	}
707
708	BNetworkAddress broadcast;
709	broadcast.SetToBroadcast(AF_INET, DHCP_SERVER_PORT);
710
711	socket_timeout timeout(socket);
712
713	dhcp_message discover(DHCP_DISCOVER);
714	_PrepareMessage(discover, state);
715
716	dhcp_message request(DHCP_REQUEST);
717	_PrepareMessage(request, state);
718
719	bool skipRequest = false;
720	dhcp_state originalState = state;
721	fRequestTime = system_time();
722	while (true) {
723		if (!skipRequest) {
724			_SendMessage(socket, originalState == INIT ? discover : request,
725				originalState == RENEWING ? fServer : broadcast);
726
727			if (originalState == INIT)
728				state = SELECTING;
729			else if (originalState == INIT_REBOOT)
730				state = REBOOTING;
731		}
732
733		char buffer[2048];
734		struct sockaddr_in from;
735		socklen_t fromLength = sizeof(from);
736		ssize_t bytesReceived = recvfrom(socket, buffer, sizeof(buffer),
737			0, (struct sockaddr*)&from, &fromLength);
738		if (bytesReceived < 0 && errno == B_TIMED_OUT) {
739			// depending on the state, we'll just try again
740			if (!_TimeoutShift(socket, state, timeout))
741				return B_TIMED_OUT;
742			skipRequest = false;
743			continue;
744		} else if (bytesReceived < 0)
745			return errno;
746
747		skipRequest = true;
748		dhcp_message* message = (dhcp_message*)buffer;
749		if (message->transaction_id != htonl(fTransactionID)
750			|| !message->HasOptions()
751			|| memcmp(message->mac_address, discover.mac_address,
752				discover.hardware_address_length)) {
753			// this message is not for us
754			syslog(LOG_DEBUG, "%s: Ignoring %s not for us from %s\n",
755				Device(), dhcp_message::TypeToString(message->Type()),
756				_AddressToString(from.sin_addr.s_addr).String());
757			continue;
758		}
759
760		syslog(LOG_DEBUG, "%s: Received %s from %s\n",
761			Device(), dhcp_message::TypeToString(message->Type()),
762			_AddressToString(from.sin_addr.s_addr).String());
763
764		if (_GotMessage(state, message) == B_OK)
765			break;
766	}
767
768	return B_OK;
769}
770
771
772void
773DHCPClient::_RestartLease(bigtime_t leaseTime)
774{
775	if (leaseTime == 0)
776		return;
777
778	BMessage lease(kMsgLeaseTime);
779	fRunner = new BMessageRunner(this, &lease, leaseTime - system_time(), 1);
780}
781
782
783void
784DHCPClient::_ParseOptions(dhcp_message& message, BMessage& address,
785	BMessage& resolverConfiguration)
786{
787	dhcp_option_cookie cookie;
788	message_option option;
789	const uint8* data;
790	size_t size;
791	while (message.NextOption(cookie, option, data, size)) {
792		// iterate through all options
793		switch (option) {
794			case OPTION_ROUTER_ADDRESS:
795				syslog(LOG_DEBUG, "  gateway: %s\n",
796					_AddressToString(data).String());
797				address.AddString("gateway", _AddressToString(data));
798				break;
799			case OPTION_SUBNET_MASK:
800				syslog(LOG_DEBUG, "  subnet: %s\n",
801					_AddressToString(data).String());
802				address.AddString("mask", _AddressToString(data));
803				break;
804			case OPTION_BROADCAST_ADDRESS:
805				syslog(LOG_DEBUG, "  broadcast: %s\n",
806					_AddressToString(data).String());
807				address.AddString("broadcast", _AddressToString(data));
808				break;
809			case OPTION_DOMAIN_NAME_SERVER:
810			{
811				for (uint32 i = 0; i < size / 4; i++) {
812					syslog(LOG_DEBUG, "  nameserver[%d]: %s\n", i,
813						_AddressToString(&data[i * 4]).String());
814					resolverConfiguration.AddString("nameserver",
815						_AddressToString(&data[i * 4]).String());
816				}
817				resolverConfiguration.AddInt32("nameserver_count",
818					size / 4);
819				break;
820			}
821			case OPTION_SERVER_ADDRESS:
822			{
823				syslog(LOG_DEBUG, "  server: %s\n",
824					_AddressToString(data).String());
825				status_t status = fServer.SetAddress(*(in_addr_t*)data);
826				if (status != B_OK) {
827					syslog(LOG_ERR, "   BNetworkAddress::SetAddress failed with %s!\n",
828						strerror(status));
829					fServer.Unset();
830				}
831				break;
832			}
833
834			case OPTION_ADDRESS_LEASE_TIME:
835				syslog(LOG_DEBUG, "  lease time: %lu seconds\n",
836					ntohl(*(uint32*)data));
837				fLeaseTime = ntohl(*(uint32*)data) * 1000000LL;
838				break;
839			case OPTION_RENEWAL_TIME:
840				syslog(LOG_DEBUG, "  renewal time: %lu seconds\n",
841					ntohl(*(uint32*)data));
842				fRenewalTime = ntohl(*(uint32*)data) * 1000000LL;
843				break;
844			case OPTION_REBINDING_TIME:
845				syslog(LOG_DEBUG, "  rebinding time: %lu seconds\n",
846					ntohl(*(uint32*)data));
847				fRebindingTime = ntohl(*(uint32*)data) * 1000000LL;
848				break;
849
850			case OPTION_HOST_NAME:
851				syslog(LOG_DEBUG, "  host name: \"%.*s\"\n", (int)size,
852					(const char*)data);
853				break;
854
855			case OPTION_DOMAIN_NAME:
856			{
857				char domain[256];
858				strlcpy(domain, (const char*)data,
859					min_c(size + 1, sizeof(domain)));
860
861				syslog(LOG_DEBUG, "  domain name: \"%s\"\n", domain);
862
863				resolverConfiguration.AddString("domain", domain);
864				break;
865			}
866
867			case OPTION_MESSAGE_TYPE:
868				break;
869
870			case OPTION_ERROR_MESSAGE:
871				syslog(LOG_INFO, "  error message: \"%.*s\"\n", (int)size,
872					(const char*)data);
873				break;
874
875			default:
876				syslog(LOG_DEBUG, "  UNKNOWN OPTION %" B_PRIu32 " (0x%" B_PRIx32 ")\n",
877					(uint32)option, (uint32)option);
878				break;
879		}
880	}
881}
882
883
884void
885DHCPClient::_PrepareMessage(dhcp_message& message, dhcp_state state)
886{
887	message.opcode = BOOT_REQUEST;
888	message.hardware_type = ARP_HARDWARE_TYPE_ETHER;
889	message.hardware_address_length = 6;
890	message.transaction_id = htonl(fTransactionID);
891	message.seconds_since_start = htons(min_c((system_time() - fStartTime)
892		/ 1000000LL, 65535));
893	memcpy(message.mac_address, fMAC, 6);
894
895	message_type type = message.Type();
896
897	uint8 *next = message.PrepareMessage(type);
898
899	switch (type) {
900		case DHCP_DISCOVER:
901			next = message.PutOption(next, OPTION_REQUEST_PARAMETERS,
902				kRequestParameters, sizeof(kRequestParameters));
903
904			if (fHostName.Length() > 0) {
905				next = message.PutOption(next, OPTION_HOST_NAME,
906					reinterpret_cast<const uint8*>(fHostName.String()),
907					fHostName.Length());
908			}
909			break;
910
911		case DHCP_REQUEST:
912			next = message.PutOption(next, OPTION_REQUEST_PARAMETERS,
913				kRequestParameters, sizeof(kRequestParameters));
914
915			if (fHostName.Length() > 0) {
916				next = message.PutOption(next, OPTION_HOST_NAME,
917					reinterpret_cast<const uint8*>(fHostName.String()),
918					fHostName.Length());
919			}
920
921			if (state == REQUESTING) {
922				const sockaddr_in& server = (sockaddr_in&)fServer.SockAddr();
923				next = message.PutOption(next, OPTION_SERVER_ADDRESS,
924					(uint32)server.sin_addr.s_addr);
925			}
926
927			if (state == INIT || state == INIT_REBOOT
928				|| state == REQUESTING) {
929				next = message.PutOption(next, OPTION_REQUEST_IP_ADDRESS,
930					(uint32)fAssignedAddress);
931			} else
932				message.client_address = fAssignedAddress;
933			break;
934
935		case DHCP_RELEASE: {
936			const sockaddr_in& server = (sockaddr_in&)fServer.SockAddr();
937			next = message.PutOption(next, OPTION_SERVER_ADDRESS,
938				(uint32)server.sin_addr.s_addr);
939
940			message.client_address = fAssignedAddress;
941			break;
942		}
943
944		default:
945			break;
946	}
947
948	message.FinishOptions(next);
949}
950
951
952bool
953DHCPClient::_TimeoutShift(int socket, dhcp_state& state,
954	socket_timeout& timeout)
955{
956	bigtime_t stateMaxTime = -1;
957
958	// Compute the date at which we must consider the DHCP negociation failed.
959	// This varies depending on the current state. In renewing and rebinding
960	// states, it is based on the lease expiration.
961	// We can stay for up to 1 minute in the selecting and requesting states
962	// (these correspond to sending DHCP_DISCOVER and DHCP_REQUEST,
963	// respectively).
964	// All other states time out immediately after a single try.
965	// If this timeout expires, the DHCP negociation is aborted and starts
966	// over. As long as the timeout is not expired, we repeat the message with
967	// increasing delays (the delay is computed in timeout.shift below, and is
968	// at most equal to the timeout, but usually much shorter).
969	if (state == RENEWING)
970		stateMaxTime = fRebindingTime;
971	else if (state == REBINDING)
972		stateMaxTime = fLeaseTime;
973	else if (state == SELECTING || state == REQUESTING)
974		stateMaxTime = fRequestTime + AS_USECS(MAX_TIMEOUT);
975
976	if (system_time() > stateMaxTime) {
977		state = state == REBINDING ? INIT : REBINDING;
978		return false;
979	}
980
981	return timeout.Shift(socket, stateMaxTime, Device());
982}
983
984
985/*static*/ BString
986DHCPClient::_AddressToString(const uint8* data)
987{
988	BString target = inet_ntoa(*(in_addr*)data);
989	return target;
990}
991
992
993/*static*/ BString
994DHCPClient::_AddressToString(in_addr_t address)
995{
996	BString target = inet_ntoa(*(in_addr*)&address);
997	return target;
998}
999
1000
1001status_t
1002DHCPClient::_SendMessage(int socket, dhcp_message& message,
1003	const BNetworkAddress& address) const
1004{
1005	message_type type = message.Type();
1006	BString text;
1007	text << dhcp_message::TypeToString(type);
1008
1009	const uint8* requestAddress = message.FindOption(OPTION_REQUEST_IP_ADDRESS);
1010	if (type == DHCP_REQUEST && requestAddress != NULL)
1011		text << " for " << _AddressToString(requestAddress).String();
1012
1013	syslog(LOG_DEBUG, "%s: Send %s to %s\n", Device(), text.String(),
1014		address.ToString().String());
1015
1016	ssize_t bytesSent = sendto(socket, &message, message.Size(),
1017		address.IsBroadcast() ? MSG_BCAST : 0, address, address.Length());
1018	if (bytesSent < 0)
1019		return errno;
1020
1021	return B_OK;
1022}
1023
1024
1025dhcp_state
1026DHCPClient::_CurrentState() const
1027{
1028	bigtime_t now = system_time();
1029
1030	if (now > fLeaseTime || fStatus != B_OK)
1031		return INIT;
1032	if (now >= fRebindingTime)
1033		return REBINDING;
1034	if (now >= fRenewalTime)
1035		return RENEWING;
1036	return BOUND;
1037}
1038
1039
1040void
1041DHCPClient::MessageReceived(BMessage* message)
1042{
1043	switch (message->what) {
1044		case kMsgLeaseTime:
1045			_Negotiate(_CurrentState());
1046			break;
1047
1048		default:
1049			BHandler::MessageReceived(message);
1050			break;
1051	}
1052}
1053