xref: /haiku/src/servers/net/DHCPClient.cpp (revision 9a6a20d4689307142a7ed26a1437ba47e244e73f)
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 
49 enum message_opcode {
50 	BOOT_REQUEST = 1,
51 	BOOT_REPLY
52 };
53 
54 enum 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 
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
98 };
99 
100 struct 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 
115 struct 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 
157 struct 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 
177 const uint32 kMsgLeaseTime = 'lstm';
178 
179 static 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 
186 dhcp_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 
197 bool
198 dhcp_message::HasOptions() const
199 {
200 	return options_magic == htonl(OPTION_MAGIC);
201 }
202 
203 
204 bool
205 dhcp_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 
298 const uint8*
299 dhcp_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 
315 const uint8*
316 dhcp_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 
330 message_type
331 dhcp_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 
341 size_t
342 dhcp_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 
356 uint8*
357 dhcp_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 
367 uint8*
368 dhcp_message::PutOption(uint8* options, message_option option)
369 {
370 	options[0] = option;
371 	return options + 1;
372 }
373 
374 
375 uint8*
376 dhcp_message::PutOption(uint8* options, message_option option, uint8 data)
377 {
378 	return PutOption(options, option, &data, 1);
379 }
380 
381 
382 uint8*
383 dhcp_message::PutOption(uint8* options, message_option option, uint16 data)
384 {
385 	return PutOption(options, option, (uint8*)&data, sizeof(data));
386 }
387 
388 
389 uint8*
390 dhcp_message::PutOption(uint8* options, message_option option, uint32 data)
391 {
392 	return PutOption(options, option, (uint8*)&data, sizeof(data));
393 }
394 
395 
396 uint8*
397 dhcp_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 
410 uint8*
411 dhcp_message::FinishOptions(uint8* options)
412 {
413 	return PutOption(options, OPTION_END);
414 }
415 
416 
417 /*static*/ const char*
418 dhcp_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 
438 void
439 socket_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 
448 bool
449 socket_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 
479 DHCPClient::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 	fStartTime(0),
488 	fRequestTime(0),
489 	fRenewalTime(0),
490 	fRebindingTime(0),
491 	fLeaseTime(0)
492 {
493 	fTransactionID = (uint32)system_time() ^ rand();
494 
495 	BNetworkAddress link;
496 	BNetworkInterface interface(device);
497 	fStatus = interface.GetHardwareAddress(link);
498 	if (fStatus != B_OK)
499 		return;
500 
501 	memcpy(fMAC, link.LinkLevelAddress(), sizeof(fMAC));
502 
503 	if ((interface.Flags() & IFF_AUTO_CONFIGURED) != 0) {
504 		// Check for interface previous auto-configured address, if any.
505 		BNetworkInterfaceAddress interfaceAddress;
506 		int index = interface.FindFirstAddress(AF_INET);
507 		if (index >= 0
508 			&& interface.GetAddressAt(index, interfaceAddress) == B_OK) {
509 			BNetworkAddress address = interfaceAddress.Address();
510 			const sockaddr_in& addr = (sockaddr_in&)address.SockAddr();
511 			fAssignedAddress = addr.sin_addr.s_addr;
512 
513 			if ((ntohl(fAssignedAddress) & IN_CLASSB_NET) == 0xa9fe0000) {
514 				// previous auto-configured address is a link-local one:
515 				// there is no point asking a DHCP server to renew such
516 				// server-less assigned address...
517 				fAssignedAddress = 0;
518 			}
519 		}
520 	}
521 
522 	openlog_thread("DHCP", 0, LOG_DAEMON);
523 }
524 
525 
526 DHCPClient::~DHCPClient()
527 {
528 	if (fStatus != B_OK)
529 		return;
530 
531 	delete fRunner;
532 
533 	int socket = ::socket(AF_INET, SOCK_DGRAM, 0);
534 	if (socket < 0)
535 		return;
536 
537 	// release lease
538 
539 	dhcp_message release(DHCP_RELEASE);
540 	_PrepareMessage(release, BOUND);
541 
542 	_SendMessage(socket, release, fServer);
543 	close(socket);
544 
545 	closelog_thread();
546 }
547 
548 
549 status_t
550 DHCPClient::Initialize()
551 {
552 	fStatus = _Negotiate(fAssignedAddress == 0 ? INIT : INIT_REBOOT);
553 	syslog(LOG_DEBUG, "%s: DHCP status = %s\n", Device(), strerror(fStatus));
554 	return fStatus;
555 }
556 
557 
558 status_t
559 DHCPClient::_Negotiate(dhcp_state state)
560 {
561 	if (state == BOUND)
562 		return B_OK;
563 
564 	fStartTime = system_time();
565 	fTransactionID++;
566 
567 	char hostName[MAXHOSTNAMELEN];
568 	if (gethostname(hostName, MAXHOSTNAMELEN) == 0)
569 		fHostName.SetTo(hostName, MAXHOSTNAMELEN);
570 	else
571 		fHostName.Truncate(0);
572 
573 	int socket = ::socket(AF_INET, SOCK_DGRAM, 0);
574 	if (socket < 0)
575 		return errno;
576 
577 	// Enable reusing the port. This is needed in case there is more
578 	// than 1 interface that needs to be configured. Note that the only reason
579 	// this works is because there is code below to bind to a specific
580 	// interface.
581 	int option = 1;
582 	setsockopt(socket, SOL_SOCKET, SO_REUSEPORT, &option, sizeof(option));
583 
584 	BNetworkAddress local;
585 	local.SetToWildcard(AF_INET, DHCP_CLIENT_PORT);
586 
587 	option = 1;
588 	setsockopt(socket, SOL_SOCKET, SO_BROADCAST, &option, sizeof(option));
589 
590 	if (bind(socket, local, local.Length()) < 0) {
591 		close(socket);
592 		return errno;
593 	}
594 
595 	bigtime_t previousLeaseTime = fLeaseTime;
596 
597 	status_t status = B_OK;
598 	while (state != BOUND) {
599 		status = _StateTransition(socket, state);
600 		if (status != B_OK && (state == SELECTING || state == REBOOTING))
601 			break;
602 	}
603 
604 	close(socket);
605 
606 	if (fLeaseTime == 0)
607 		fLeaseTime = previousLeaseTime;
608 	if (fLeaseTime == 0)
609 		fLeaseTime = 60;
610 
611 	if (fRenewalTime == 0)
612 		fRenewalTime = fLeaseTime / 2;
613 	if (fRebindingTime == 0)
614 		fRebindingTime = fLeaseTime * 7 / 8;
615 	fLeaseTime += fRequestTime;
616 	fRenewalTime += fRequestTime;
617 	fRebindingTime += fRequestTime;
618 	_RestartLease(fRenewalTime);
619 
620 	fStatus = status;
621 	if (status)
622 		return status;
623 
624 	// configure interface
625 	BMessage reply;
626 	status = Target().SendMessage(&fConfiguration, &reply);
627 	if (status == B_OK)
628 		status = reply.FindInt32("status", &fStatus);
629 
630 	// configure resolver
631 	reply.MakeEmpty();
632 	fResolverConfiguration.AddString("device", Device());
633 	status = Target().SendMessage(&fResolverConfiguration, &reply);
634 	if (status == B_OK)
635 		status = reply.FindInt32("status", &fStatus);
636 	return status;
637 }
638 
639 
640 status_t
641 DHCPClient::_GotMessage(dhcp_state& state, dhcp_message* message)
642 {
643 	switch (state) {
644 		case SELECTING:
645 			if (message->Type() == DHCP_OFFER) {
646 				state = REQUESTING;
647 
648 				fAssignedAddress = message->your_address;
649 				syslog(LOG_INFO, "  your_address: %s\n",
650 						_AddressToString(fAssignedAddress).String());
651 
652 				fConfiguration.MakeEmpty();
653 				fConfiguration.AddString("device", Device());
654 				fConfiguration.AddBool("auto_configured", true);
655 
656 				BMessage address;
657 				address.AddString("family", "inet");
658 				address.AddString("address", _AddressToString(fAssignedAddress));
659 				fResolverConfiguration.MakeEmpty();
660 				_ParseOptions(*message, address, fResolverConfiguration);
661 
662 				fConfiguration.AddMessage("address", &address);
663 				return B_OK;
664 			}
665 
666 			return B_BAD_VALUE;
667 
668 		case REBOOTING:
669 		case REBINDING:
670 		case RENEWING:
671 		case REQUESTING:
672 			if (message->Type() == DHCP_ACK) {
673 				// TODO: we might want to configure the stuff, don't we?
674 				BMessage address;
675 				fResolverConfiguration.MakeEmpty();
676 				_ParseOptions(*message, address, fResolverConfiguration);
677 					// TODO: currently, only lease time and DNS is updated this
678 					// way
679 
680 				// our address request has been acknowledged
681 				state = BOUND;
682 
683 				return B_OK;
684 			}
685 
686 			if (message->Type() == DHCP_NACK) {
687 				// server reject our request on previous assigned address
688 				// back to square one...
689 				fAssignedAddress = 0;
690 				state = INIT;
691 				return B_OK;
692 			}
693 
694 		default:
695 			return B_BAD_VALUE;
696 	}
697 }
698 
699 
700 status_t
701 DHCPClient::_StateTransition(int socket, dhcp_state& state)
702 {
703 	if (state == INIT) {
704 		// The local interface does not have an address yet, bind the socket
705 		// to the device directly.
706 		BNetworkDevice device(Device());
707 		int index = device.Index();
708 
709 		setsockopt(socket, SOL_SOCKET, SO_BINDTODEVICE, &index, sizeof(int));
710 	}
711 
712 	BNetworkAddress broadcast;
713 	broadcast.SetToBroadcast(AF_INET, DHCP_SERVER_PORT);
714 
715 	socket_timeout timeout(socket);
716 
717 	dhcp_message discover(DHCP_DISCOVER);
718 	_PrepareMessage(discover, state);
719 
720 	dhcp_message request(DHCP_REQUEST);
721 	_PrepareMessage(request, state);
722 
723 	bool skipRequest = false;
724 	dhcp_state originalState = state;
725 	fRequestTime = system_time();
726 	while (true) {
727 		if (!skipRequest) {
728 			_SendMessage(socket, originalState == INIT ? discover : request,
729 				originalState == RENEWING ? fServer : broadcast);
730 
731 			if (originalState == INIT)
732 				state = SELECTING;
733 			else if (originalState == INIT_REBOOT)
734 				state = REBOOTING;
735 		}
736 
737 		char buffer[2048];
738 		struct sockaddr_in from;
739 		socklen_t fromLength = sizeof(from);
740 		ssize_t bytesReceived = recvfrom(socket, buffer, sizeof(buffer),
741 			0, (struct sockaddr*)&from, &fromLength);
742 		if (bytesReceived < 0 && errno == B_TIMED_OUT) {
743 			// depending on the state, we'll just try again
744 			if (!_TimeoutShift(socket, state, timeout))
745 				return B_TIMED_OUT;
746 			skipRequest = false;
747 			continue;
748 		} else if (bytesReceived < 0)
749 			return errno;
750 
751 		skipRequest = true;
752 		dhcp_message* message = (dhcp_message*)buffer;
753 		if (message->transaction_id != htonl(fTransactionID)
754 			|| !message->HasOptions()
755 			|| memcmp(message->mac_address, discover.mac_address,
756 				discover.hardware_address_length)) {
757 			// this message is not for us
758 			syslog(LOG_DEBUG, "%s: Ignoring %s not for us from %s\n",
759 				Device(), dhcp_message::TypeToString(message->Type()),
760 				_AddressToString(from.sin_addr.s_addr).String());
761 			continue;
762 		}
763 
764 		syslog(LOG_DEBUG, "%s: Received %s from %s\n",
765 			Device(), dhcp_message::TypeToString(message->Type()),
766 			_AddressToString(from.sin_addr.s_addr).String());
767 
768 		if (_GotMessage(state, message) == B_OK)
769 			break;
770 	}
771 
772 	return B_OK;
773 }
774 
775 
776 void
777 DHCPClient::_RestartLease(bigtime_t leaseTime)
778 {
779 	if (leaseTime == 0)
780 		return;
781 
782 	BMessage lease(kMsgLeaseTime);
783 	fRunner = new BMessageRunner(this, &lease, leaseTime - system_time(), 1);
784 }
785 
786 
787 void
788 DHCPClient::_ParseOptions(dhcp_message& message, BMessage& address,
789 	BMessage& resolverConfiguration)
790 {
791 	dhcp_option_cookie cookie;
792 	message_option option;
793 	const uint8* data;
794 	size_t size;
795 	while (message.NextOption(cookie, option, data, size)) {
796 		// iterate through all options
797 		switch (option) {
798 			case OPTION_ROUTER_ADDRESS:
799 				syslog(LOG_DEBUG, "  gateway: %s\n",
800 					_AddressToString(data).String());
801 				address.AddString("gateway", _AddressToString(data));
802 				break;
803 			case OPTION_SUBNET_MASK:
804 				syslog(LOG_DEBUG, "  subnet: %s\n",
805 					_AddressToString(data).String());
806 				address.AddString("mask", _AddressToString(data));
807 				break;
808 			case OPTION_BROADCAST_ADDRESS:
809 				syslog(LOG_DEBUG, "  broadcast: %s\n",
810 					_AddressToString(data).String());
811 				address.AddString("broadcast", _AddressToString(data));
812 				break;
813 			case OPTION_DOMAIN_NAME_SERVER:
814 			{
815 				for (uint32 i = 0; i < size / 4; i++) {
816 					syslog(LOG_DEBUG, "  nameserver[%d]: %s\n", i,
817 						_AddressToString(&data[i * 4]).String());
818 					resolverConfiguration.AddString("nameserver",
819 						_AddressToString(&data[i * 4]).String());
820 				}
821 				resolverConfiguration.AddInt32("nameserver_count",
822 					size / 4);
823 				break;
824 			}
825 			case OPTION_SERVER_ADDRESS:
826 			{
827 				syslog(LOG_DEBUG, "  server: %s\n",
828 					_AddressToString(data).String());
829 				status_t status = fServer.SetAddress(*(in_addr_t*)data);
830 				if (status != B_OK) {
831 					syslog(LOG_ERR, "   BNetworkAddress::SetAddress failed with %s!\n",
832 						strerror(status));
833 					fServer.Unset();
834 				}
835 				break;
836 			}
837 
838 			case OPTION_ADDRESS_LEASE_TIME:
839 				syslog(LOG_DEBUG, "  lease time: %lu seconds\n",
840 					ntohl(*(uint32*)data));
841 				fLeaseTime = ntohl(*(uint32*)data) * 1000000LL;
842 				break;
843 			case OPTION_RENEWAL_TIME:
844 				syslog(LOG_DEBUG, "  renewal time: %lu seconds\n",
845 					ntohl(*(uint32*)data));
846 				fRenewalTime = ntohl(*(uint32*)data) * 1000000LL;
847 				break;
848 			case OPTION_REBINDING_TIME:
849 				syslog(LOG_DEBUG, "  rebinding time: %lu seconds\n",
850 					ntohl(*(uint32*)data));
851 				fRebindingTime = ntohl(*(uint32*)data) * 1000000LL;
852 				break;
853 
854 			case OPTION_HOST_NAME:
855 				syslog(LOG_DEBUG, "  host name: \"%.*s\"\n", (int)size,
856 					(const char*)data);
857 				break;
858 
859 			case OPTION_DOMAIN_NAME:
860 			{
861 				char domain[256];
862 				strlcpy(domain, (const char*)data,
863 					min_c(size + 1, sizeof(domain)));
864 
865 				syslog(LOG_DEBUG, "  domain name: \"%s\"\n", domain);
866 
867 				resolverConfiguration.AddString("domain", domain);
868 				break;
869 			}
870 
871 			case OPTION_MESSAGE_TYPE:
872 				break;
873 
874 			case OPTION_ERROR_MESSAGE:
875 				syslog(LOG_INFO, "  error message: \"%.*s\"\n", (int)size,
876 					(const char*)data);
877 				break;
878 
879 			default:
880 				syslog(LOG_DEBUG, "  UNKNOWN OPTION %" B_PRIu32 " (0x%" B_PRIx32 ")\n",
881 					(uint32)option, (uint32)option);
882 				break;
883 		}
884 	}
885 }
886 
887 
888 void
889 DHCPClient::_PrepareMessage(dhcp_message& message, dhcp_state state)
890 {
891 	message.opcode = BOOT_REQUEST;
892 	message.hardware_type = ARP_HARDWARE_TYPE_ETHER;
893 	message.hardware_address_length = 6;
894 	message.transaction_id = htonl(fTransactionID);
895 	message.seconds_since_start = htons(min_c((system_time() - fStartTime)
896 		/ 1000000LL, 65535));
897 	memcpy(message.mac_address, fMAC, 6);
898 
899 	message_type type = message.Type();
900 
901 	uint8 *next = message.PrepareMessage(type);
902 
903 	switch (type) {
904 		case DHCP_DISCOVER:
905 			next = message.PutOption(next, OPTION_REQUEST_PARAMETERS,
906 				kRequestParameters, sizeof(kRequestParameters));
907 
908 			if (fHostName.Length() > 0) {
909 				next = message.PutOption(next, OPTION_HOST_NAME,
910 					reinterpret_cast<const uint8*>(fHostName.String()),
911 					fHostName.Length());
912 			}
913 			break;
914 
915 		case DHCP_REQUEST:
916 			next = message.PutOption(next, OPTION_REQUEST_PARAMETERS,
917 				kRequestParameters, sizeof(kRequestParameters));
918 
919 			if (fHostName.Length() > 0) {
920 				next = message.PutOption(next, OPTION_HOST_NAME,
921 					reinterpret_cast<const uint8*>(fHostName.String()),
922 					fHostName.Length());
923 			}
924 
925 			if (state == REQUESTING) {
926 				const sockaddr_in& server = (sockaddr_in&)fServer.SockAddr();
927 				next = message.PutOption(next, OPTION_SERVER_ADDRESS,
928 					(uint32)server.sin_addr.s_addr);
929 			}
930 
931 			if (state == INIT || state == INIT_REBOOT
932 				|| state == REQUESTING) {
933 				next = message.PutOption(next, OPTION_REQUEST_IP_ADDRESS,
934 					(uint32)fAssignedAddress);
935 			} else
936 				message.client_address = fAssignedAddress;
937 			break;
938 
939 		case DHCP_RELEASE: {
940 			const sockaddr_in& server = (sockaddr_in&)fServer.SockAddr();
941 			next = message.PutOption(next, OPTION_SERVER_ADDRESS,
942 				(uint32)server.sin_addr.s_addr);
943 
944 			message.client_address = fAssignedAddress;
945 			break;
946 		}
947 
948 		default:
949 			break;
950 	}
951 
952 	message.FinishOptions(next);
953 }
954 
955 
956 bool
957 DHCPClient::_TimeoutShift(int socket, dhcp_state& state,
958 	socket_timeout& timeout)
959 {
960 	bigtime_t stateMaxTime = -1;
961 
962 	// Compute the date at which we must consider the DHCP negociation failed.
963 	// This varies depending on the current state. In renewing and rebinding
964 	// states, it is based on the lease expiration.
965 	// We can stay for up to 1 minute in the selecting and requesting states
966 	// (these correspond to sending DHCP_DISCOVER and DHCP_REQUEST,
967 	// respectively).
968 	// All other states time out immediately after a single try.
969 	// If this timeout expires, the DHCP negociation is aborted and starts
970 	// over. As long as the timeout is not expired, we repeat the message with
971 	// increasing delays (the delay is computed in timeout.shift below, and is
972 	// at most equal to the timeout, but usually much shorter).
973 	if (state == RENEWING)
974 		stateMaxTime = fRebindingTime;
975 	else if (state == REBINDING)
976 		stateMaxTime = fLeaseTime;
977 	else if (state == SELECTING || state == REQUESTING)
978 		stateMaxTime = fRequestTime + AS_USECS(MAX_TIMEOUT);
979 
980 	if (system_time() > stateMaxTime) {
981 		state = state == REBINDING ? INIT : REBINDING;
982 		return false;
983 	}
984 
985 	return timeout.Shift(socket, stateMaxTime, Device());
986 }
987 
988 
989 /*static*/ BString
990 DHCPClient::_AddressToString(const uint8* data)
991 {
992 	BString target = inet_ntoa(*(in_addr*)data);
993 	return target;
994 }
995 
996 
997 /*static*/ BString
998 DHCPClient::_AddressToString(in_addr_t address)
999 {
1000 	BString target = inet_ntoa(*(in_addr*)&address);
1001 	return target;
1002 }
1003 
1004 
1005 status_t
1006 DHCPClient::_SendMessage(int socket, dhcp_message& message,
1007 	const BNetworkAddress& address) const
1008 {
1009 	message_type type = message.Type();
1010 	BString text;
1011 	text << dhcp_message::TypeToString(type);
1012 
1013 	const uint8* requestAddress = message.FindOption(OPTION_REQUEST_IP_ADDRESS);
1014 	if (type == DHCP_REQUEST && requestAddress != NULL)
1015 		text << " for " << _AddressToString(requestAddress).String();
1016 
1017 	syslog(LOG_DEBUG, "%s: Send %s to %s\n", Device(), text.String(),
1018 		address.ToString().String());
1019 
1020 	ssize_t bytesSent = sendto(socket, &message, message.Size(),
1021 		address.IsBroadcast() ? MSG_BCAST : 0, address, address.Length());
1022 	if (bytesSent < 0)
1023 		return errno;
1024 
1025 	return B_OK;
1026 }
1027 
1028 
1029 dhcp_state
1030 DHCPClient::_CurrentState() const
1031 {
1032 	bigtime_t now = system_time();
1033 
1034 	if (now > fLeaseTime || fStatus != B_OK)
1035 		return INIT;
1036 	if (now >= fRebindingTime)
1037 		return REBINDING;
1038 	if (now >= fRenewalTime)
1039 		return RENEWING;
1040 	return BOUND;
1041 }
1042 
1043 
1044 void
1045 DHCPClient::MessageReceived(BMessage* message)
1046 {
1047 	switch (message->what) {
1048 		case kMsgLeaseTime:
1049 			_Negotiate(_CurrentState());
1050 			break;
1051 
1052 		default:
1053 			BHandler::MessageReceived(message);
1054 			break;
1055 	}
1056 }
1057