xref: /haiku/src/servers/net/DHCPClient.cpp (revision b46615c55ad2c8fe6de54412055a0713da3d610a)
1 /*
2  * Copyright 2006-2011, 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 <arpa/inet.h>
20 #include <errno.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #include <syslog.h>
25 #include <sys/sockio.h>
26 #include <sys/time.h>
27 
28 #include <Debug.h>
29 #include <Message.h>
30 #include <MessageRunner.h>
31 
32 #include "NetServer.h"
33 
34 
35 // See RFC 2131 for DHCP, see RFC 1533 for BOOTP/DHCP options
36 
37 #define DHCP_CLIENT_PORT	68
38 #define DHCP_SERVER_PORT	67
39 
40 #define DEFAULT_TIMEOUT		2	// secs
41 #define MAX_TIMEOUT			15	// secs
42 
43 enum message_opcode {
44 	BOOT_REQUEST = 1,
45 	BOOT_REPLY
46 };
47 
48 enum message_option {
49 	OPTION_MAGIC = 0x63825363,
50 
51 	// generic options
52 	OPTION_PAD = 0,
53 	OPTION_END = 255,
54 	OPTION_SUBNET_MASK = 1,
55 	OPTION_TIME_OFFSET = 2,
56 	OPTION_ROUTER_ADDRESS = 3,
57 	OPTION_DOMAIN_NAME_SERVER = 6,
58 	OPTION_HOST_NAME = 12,
59 	OPTION_DOMAIN_NAME = 15,
60 	OPTION_MAX_DATAGRAM_SIZE = 22,
61 	OPTION_INTERFACE_MTU = 26,
62 	OPTION_BROADCAST_ADDRESS = 28,
63 	OPTION_NETWORK_TIME_PROTOCOL_SERVERS = 42,
64 	OPTION_NETBIOS_NAME_SERVER = 44,
65 	OPTION_NETBIOS_SCOPE = 47,
66 
67 	// DHCP specific options
68 	OPTION_REQUEST_IP_ADDRESS = 50,
69 	OPTION_ADDRESS_LEASE_TIME = 51,
70 	OPTION_OVERLOAD = 52,
71 	OPTION_MESSAGE_TYPE = 53,
72 	OPTION_SERVER_ADDRESS = 54,
73 	OPTION_REQUEST_PARAMETERS = 55,
74 	OPTION_ERROR_MESSAGE = 56,
75 	OPTION_MAX_MESSAGE_SIZE = 57,
76 	OPTION_RENEWAL_TIME = 58,
77 	OPTION_REBINDING_TIME = 59,
78 	OPTION_CLASS_IDENTIFIER = 60,
79 	OPTION_CLIENT_IDENTIFIER = 61,
80 };
81 
82 enum message_type {
83 	DHCP_NONE = 0,
84 	DHCP_DISCOVER,
85 	DHCP_OFFER,
86 	DHCP_REQUEST,
87 	DHCP_DECLINE,
88 	DHCP_ACK,
89 	DHCP_NACK,
90 	DHCP_RELEASE,
91 	DHCP_INFORM
92 };
93 
94 struct dhcp_option_cookie {
95 	dhcp_option_cookie()
96 		:
97 		state(0),
98 		file_has_options(false),
99 		server_name_has_options(false)
100 	{
101 	}
102 
103 	const uint8* next;
104 	uint8	state;
105 	bool	file_has_options;
106 	bool	server_name_has_options;
107 };
108 
109 struct dhcp_message {
110 	dhcp_message(message_type type);
111 
112 	uint8		opcode;
113 	uint8		hardware_type;
114 	uint8		hardware_address_length;
115 	uint8		hop_count;
116 	uint32		transaction_id;
117 	uint16		seconds_since_start;
118 	uint16		flags;
119 	in_addr_t	client_address;
120 	in_addr_t	your_address;
121 	in_addr_t	server_address;
122 	in_addr_t	gateway_address;
123 	uint8		mac_address[16];
124 	uint8		server_name[64];
125 	uint8		file[128];
126 	uint32		options_magic;
127 	uint8		options[1260];
128 
129 	size_t MinSize() const { return 576; }
130 	size_t Size() const;
131 
132 	bool HasOptions() const;
133 	bool NextOption(dhcp_option_cookie& cookie, message_option& option,
134 		const uint8*& data, size_t& size) const;
135 	const uint8* FindOption(message_option which) const;
136 	const uint8* LastOption() const;
137 	message_type Type() const;
138 
139 	uint8* PrepareMessage(uint8 type);
140 	uint8* PutOption(uint8* options, message_option option);
141 	uint8* PutOption(uint8* options, message_option option, uint8 data);
142 	uint8* PutOption(uint8* options, message_option option, uint16 data);
143 	uint8* PutOption(uint8* options, message_option option, uint32 data);
144 	uint8* PutOption(uint8* options, message_option option, const uint8* data,
145 		uint32 size);
146 	uint8* FinishOptions(uint8* options);
147 
148 	static const char* TypeToString(message_type type);
149 } _PACKED;
150 
151 #define DHCP_FLAG_BROADCAST		0x8000
152 
153 #define ARP_HARDWARE_TYPE_ETHER	1
154 
155 const uint32 kMsgLeaseTime = 'lstm';
156 
157 static const uint8 kRequestParameters[] = {
158 	OPTION_SUBNET_MASK, OPTION_ROUTER_ADDRESS,
159 	OPTION_DOMAIN_NAME_SERVER, OPTION_BROADCAST_ADDRESS,
160 	OPTION_DOMAIN_NAME
161 };
162 
163 
164 dhcp_message::dhcp_message(message_type type)
165 {
166 	// ASSERT(this == offsetof(this, opcode));
167 	memset(this, 0, sizeof(*this));
168 	options_magic = htonl(OPTION_MAGIC);
169 
170 	uint8* next = PrepareMessage(type);
171 	FinishOptions(next);
172 }
173 
174 
175 bool
176 dhcp_message::HasOptions() const
177 {
178 	return options_magic == htonl(OPTION_MAGIC);
179 }
180 
181 
182 bool
183 dhcp_message::NextOption(dhcp_option_cookie& cookie,
184 	message_option& option, const uint8*& data, size_t& size) const
185 {
186 	if (!HasOptions())
187 		return false;
188 
189 	if (cookie.state == 0) {
190 		cookie.state++;
191 		cookie.next = options;
192 	}
193 
194 	uint32 bytesLeft = 0;
195 
196 	switch (cookie.state) {
197 		case 1:
198 			// options from "options"
199 			bytesLeft = sizeof(options) - (cookie.next - options);
200 			break;
201 
202 		case 2:
203 			// options from "file"
204 			bytesLeft = sizeof(file) - (cookie.next - file);
205 			break;
206 
207 		case 3:
208 			// options from "server_name"
209 			bytesLeft = sizeof(server_name) - (cookie.next - server_name);
210 			break;
211 	}
212 
213 	while (true) {
214 		if (bytesLeft == 0) {
215 			cookie.state++;
216 
217 			// handle option overload in file and/or server_name fields.
218 			switch (cookie.state) {
219 				case 2:
220 					// options from "file" field
221 					if (cookie.file_has_options) {
222 						bytesLeft = sizeof(file);
223 						cookie.next = file;
224 					}
225 					break;
226 
227 				case 3:
228 					// options from "server_name" field
229 					if (cookie.server_name_has_options) {
230 						bytesLeft = sizeof(server_name);
231 						cookie.next = server_name;
232 					}
233 					break;
234 
235 				default:
236 					// no more place to look for options
237 					// if last option is not OPTION_END,
238 					// there is no space left for other option!
239 					if (option != OPTION_END)
240 						cookie.next = NULL;
241 					return false;
242 			}
243 
244 			if (bytesLeft == 0) {
245 				// no options in this state, try next one
246 				continue;
247 			}
248 		}
249 
250 		option = (message_option)cookie.next[0];
251 		if (option == OPTION_END) {
252 			bytesLeft = 0;
253 			continue;
254 		} else if (option == OPTION_PAD) {
255 			bytesLeft--;
256 			cookie.next++;
257 			continue;
258 		}
259 
260 		size = cookie.next[1];
261 		data = &cookie.next[2];
262 		cookie.next += 2 + size;
263 		bytesLeft -= 2 + size;
264 
265 		if (option == OPTION_OVERLOAD) {
266 			cookie.file_has_options = data[0] & 1;
267 			cookie.server_name_has_options = data[0] & 2;
268 			continue;
269 		}
270 
271 		return true;
272 	}
273 }
274 
275 
276 const uint8*
277 dhcp_message::FindOption(message_option which) const
278 {
279 	dhcp_option_cookie cookie;
280 	message_option option;
281 	const uint8* data;
282 	size_t size;
283 	while (NextOption(cookie, option, data, size)) {
284 		// iterate through all options
285 		if (option == which)
286 			return data;
287 	}
288 
289 	return NULL;
290 }
291 
292 
293 const uint8*
294 dhcp_message::LastOption() const
295 {
296 	dhcp_option_cookie cookie;
297 	message_option option;
298 	const uint8* data;
299 	size_t size;
300 	while (NextOption(cookie, option, data, size)) {
301 		// iterate through all options
302 	}
303 
304 	return cookie.next;
305 }
306 
307 
308 message_type
309 dhcp_message::Type() const
310 {
311 	const uint8* data = FindOption(OPTION_MESSAGE_TYPE);
312 	if (data)
313 		return (message_type)data[0];
314 
315 	return DHCP_NONE;
316 }
317 
318 
319 size_t
320 dhcp_message::Size() const
321 {
322 	const uint8* last = LastOption();
323 
324 	if (last < options) {
325 		// if last option is stored above "options" field, it means
326 		// the whole options field and message is already filled...
327 		return sizeof(dhcp_message);
328 	}
329 
330 	return sizeof(dhcp_message) - sizeof(options) + last + 1 - options;
331 }
332 
333 
334 uint8*
335 dhcp_message::PrepareMessage(uint8 type)
336 {
337 	uint8* next = options;
338 	next = PutOption(next, OPTION_MESSAGE_TYPE, type);
339 	next = PutOption(next, OPTION_MAX_MESSAGE_SIZE,
340 		(uint16)htons(sizeof(dhcp_message)));
341 	return next;
342 }
343 
344 
345 uint8*
346 dhcp_message::PutOption(uint8* options, message_option option)
347 {
348 	options[0] = option;
349 	return options + 1;
350 }
351 
352 
353 uint8*
354 dhcp_message::PutOption(uint8* options, message_option option, uint8 data)
355 {
356 	return PutOption(options, option, &data, 1);
357 }
358 
359 
360 uint8*
361 dhcp_message::PutOption(uint8* options, message_option option, uint16 data)
362 {
363 	return PutOption(options, option, (uint8*)&data, sizeof(data));
364 }
365 
366 
367 uint8*
368 dhcp_message::PutOption(uint8* options, message_option option, uint32 data)
369 {
370 	return PutOption(options, option, (uint8*)&data, sizeof(data));
371 }
372 
373 
374 uint8*
375 dhcp_message::PutOption(uint8* options, message_option option,
376 	const uint8* data, uint32 size)
377 {
378 	// TODO: check enough space is available
379 
380 	options[0] = option;
381 	options[1] = size;
382 	memcpy(&options[2], data, size);
383 
384 	return options + 2 + size;
385 }
386 
387 
388 uint8*
389 dhcp_message::FinishOptions(uint8* options)
390 {
391 	return PutOption(options, OPTION_END);
392 }
393 
394 
395 /*static*/ const char*
396 dhcp_message::TypeToString(message_type type)
397 {
398 	switch (type) {
399 #define CASE(x) case x: return #x;
400 		CASE(DHCP_NONE)
401 		CASE(DHCP_DISCOVER)
402 		CASE(DHCP_OFFER)
403 		CASE(DHCP_REQUEST)
404 		CASE(DHCP_DECLINE)
405 		CASE(DHCP_ACK)
406 		CASE(DHCP_NACK)
407 		CASE(DHCP_RELEASE)
408 		CASE(DHCP_INFORM)
409 #undef CASE
410 	}
411 
412 	return "<unknown>";
413 }
414 
415 
416 //	#pragma mark -
417 
418 
419 DHCPClient::DHCPClient(BMessenger target, const char* device)
420 	:
421 	AutoconfigClient("dhcp", target, device),
422 	fConfiguration(kMsgConfigureInterface),
423 	fResolverConfiguration(kMsgConfigureResolver),
424 	fRunner(NULL),
425 	fAssignedAddress(0),
426 	fServer(AF_INET, NULL, DHCP_SERVER_PORT),
427 	fLeaseTime(0)
428 {
429 	fStartTime = system_time();
430 	fTransactionID = (uint32)fStartTime;
431 
432 	BNetworkAddress link;
433 	BNetworkInterface interface(device);
434 	fStatus = interface.GetHardwareAddress(link);
435 	if (fStatus != B_OK)
436 		return;
437 
438 	memcpy(fMAC, link.LinkLevelAddress(), sizeof(fMAC));
439 
440 	if ((interface.Flags() & IFF_AUTO_CONFIGURED) != 0) {
441 		// Check for interface previous auto-configured address, if any.
442 		BNetworkInterfaceAddress interfaceAddress;
443 		int index = interface.FindFirstAddress(AF_INET);
444 		if (index >= 0
445 			&& interface.GetAddressAt(index, interfaceAddress) == B_OK) {
446 			BNetworkAddress address = interfaceAddress.Address();
447 			const sockaddr_in& addr = (sockaddr_in&)address.SockAddr();
448 			fAssignedAddress = addr.sin_addr.s_addr;
449 
450 			if ((ntohl(fAssignedAddress) & IN_CLASSB_NET) == 0xa9fe0000) {
451 				// previous auto-configured address is a link-local one:
452 				// there is no point asking a DHCP server to renew such
453 				// server-less assigned address...
454 				fAssignedAddress = 0;
455 			}
456 		}
457 	}
458 
459 	openlog_thread("DHCP", 0, LOG_DAEMON);
460 }
461 
462 
463 DHCPClient::~DHCPClient()
464 {
465 	if (fStatus != B_OK)
466 		return;
467 
468 	delete fRunner;
469 
470 	int socket = ::socket(AF_INET, SOCK_DGRAM, 0);
471 	if (socket < 0)
472 		return;
473 
474 	// release lease
475 
476 	dhcp_message release(DHCP_RELEASE);
477 	_PrepareMessage(release, BOUND);
478 
479 	_SendMessage(socket, release, fServer);
480 	close(socket);
481 
482 	closelog_thread();
483 }
484 
485 
486 status_t
487 DHCPClient::Initialize()
488 {
489 	fStatus = _Negotiate(fAssignedAddress == 0 ? INIT : INIT_REBOOT);
490 	syslog(LOG_DEBUG, "%s: DHCP status = %s\n", Device(), strerror(fStatus));
491 	return fStatus;
492 }
493 
494 
495 status_t
496 DHCPClient::_Negotiate(dhcp_state state)
497 {
498 	int socket = ::socket(AF_INET, SOCK_DGRAM, 0);
499 	if (socket < 0)
500 		return errno;
501 
502 	BNetworkAddress local;
503 	local.SetToWildcard(AF_INET, DHCP_CLIENT_PORT);
504 
505 	// Enable reusing the port. This is needed in case there is more
506 	// than 1 interface that needs to be configured. Note that the only reason
507 	// this works is because there is code below to bind to a specific
508 	// interface.
509 	int option = 1;
510 	setsockopt(socket, SOL_SOCKET, SO_REUSEPORT, &option, sizeof(option));
511 
512 	if (bind(socket, local, local.Length()) < 0) {
513 		close(socket);
514 		return errno;
515 	}
516 
517 	BNetworkAddress broadcast;
518 	broadcast.SetToBroadcast(AF_INET, DHCP_SERVER_PORT);
519 
520 	option = 1;
521 	setsockopt(socket, SOL_SOCKET, SO_BROADCAST, &option, sizeof(option));
522 
523 	if (state == INIT) {
524 		// The local interface does not have an address yet, bind the socket
525 		// to the device directly.
526 		BNetworkDevice device(Device());
527 		int index = device.Index();
528 
529 		setsockopt(socket, SOL_SOCKET, SO_BINDTODEVICE, &index, sizeof(int));
530 	}
531 
532 	bigtime_t previousLeaseTime = fLeaseTime;
533 	fLeaseTime = 0;
534 	fRenewalTime = 0;
535 	fRebindingTime = 0;
536 
537 	status_t status = B_ERROR;
538 	time_t timeout;
539 	uint32 tries;
540 	_ResetTimeout(socket, timeout, tries);
541 
542 	dhcp_message discover(DHCP_DISCOVER);
543 	_PrepareMessage(discover, state);
544 
545 	dhcp_message request(DHCP_REQUEST);
546 	_PrepareMessage(request, state);
547 
548 	// send discover/request message
549 	_SendMessage(socket, state == INIT ? discover : request,
550 		state != RENEWING ? broadcast : fServer);
551 		// no need to check the status; in case of an error we'll just send
552 		// the message again
553 
554 	// receive loop until we've got an offer and acknowledged it
555 
556 	while (state != BOUND) {
557 		char buffer[2048];
558 		struct sockaddr_in from;
559 		socklen_t fromLength = sizeof(from);
560 		ssize_t bytesReceived = recvfrom(socket, buffer, sizeof(buffer),
561 			0, (struct sockaddr*)&from, &fromLength);
562 		if (bytesReceived < 0 && errno == B_TIMED_OUT) {
563 			// depending on the state, we'll just try again
564 			if (!_TimeoutShift(socket, timeout, tries)) {
565 				close(socket);
566 				return B_TIMED_OUT;
567 			}
568 
569 			_SendMessage(socket, state == INIT ? discover : request,
570 				state != RENEWING ? broadcast : fServer);
571 			continue;
572 
573 		} else if (bytesReceived < 0)
574 			break;
575 
576 		dhcp_message* message = (dhcp_message*)buffer;
577 		if (message->transaction_id != htonl(fTransactionID)
578 			|| !message->HasOptions()
579 			|| memcmp(message->mac_address, discover.mac_address,
580 				discover.hardware_address_length)) {
581 			// this message is not for us
582 			continue;
583 		}
584 
585 		// advance from startup state
586 		if (state == INIT)
587 			state = SELECTING;
588 		else if (state == INIT_REBOOT)
589 			state = REBOOTING;
590 
591 		syslog(LOG_DEBUG, "%s: Received %s from %s\n",
592 			Device(), dhcp_message::TypeToString(message->Type()),
593 			_AddressToString(from.sin_addr.s_addr).String());
594 
595 		switch (message->Type()) {
596 			case DHCP_NONE:
597 			default:
598 				// ignore this message
599 				break;
600 
601 			case DHCP_OFFER:
602 			{
603 				// first offer wins
604 				if (state != SELECTING)
605 					break;
606 
607 				// collect interface options
608 
609 				fAssignedAddress = message->your_address;
610 				syslog(LOG_INFO, "  your_address: %s\n",
611 						_AddressToString(fAssignedAddress).String());
612 
613 				fConfiguration.MakeEmpty();
614 				fConfiguration.AddString("device", Device());
615 				fConfiguration.AddBool("auto_configured", true);
616 
617 				BMessage address;
618 				address.AddString("family", "inet");
619 				address.AddString("address", _AddressToString(fAssignedAddress));
620 				fResolverConfiguration.MakeEmpty();
621 				_ParseOptions(*message, address, fResolverConfiguration);
622 
623 				fConfiguration.AddMessage("address", &address);
624 
625 				// request configuration from the server
626 
627 				_ResetTimeout(socket, timeout, tries);
628 				state = REQUESTING;
629 				_PrepareMessage(request, state);
630 
631 				status = _SendMessage(socket, request, broadcast);
632 					// we're sending a broadcast so that all potential offers
633 					// get an answer
634 				break;
635 			}
636 
637 			case DHCP_ACK:
638 			{
639 				if (state != REQUESTING
640 					&& state != REBOOTING
641 					&& state != REBINDING
642 					&& state != RENEWING)
643 					continue;
644 
645 				// TODO: we might want to configure the stuff, don't we?
646 				BMessage address;
647 				fResolverConfiguration.MakeEmpty();
648 				_ParseOptions(*message, address, fResolverConfiguration);
649 					// TODO: currently, only lease time and DNS is updated this
650 					// way
651 
652 				// our address request has been acknowledged
653 				state = BOUND;
654 
655 				// configure interface
656 				BMessage reply;
657 				status = Target().SendMessage(&fConfiguration, &reply);
658 				if (status == B_OK)
659 					status = reply.FindInt32("status", &fStatus);
660 
661 				// configure resolver
662 				reply.MakeEmpty();
663 				fResolverConfiguration.AddString("device", Device());
664 				status = Target().SendMessage(&fResolverConfiguration, &reply);
665 				if (status == B_OK)
666 					status = reply.FindInt32("status", &fStatus);
667 				break;
668 			}
669 
670 			case DHCP_NACK:
671 				if (state != REQUESTING
672 					&& state != REBOOTING
673 					&& state != REBINDING
674 					&& state != RENEWING)
675 					continue;
676 
677 				if (state == REBOOTING) {
678 					// server reject our request on previous assigned address
679 					// back to square one...
680 					fAssignedAddress = 0;
681 				}
682 
683 				// try again (maybe we should prefer other servers if this
684 				// happens more than once)
685 				status = _SendMessage(socket, discover, broadcast);
686 				if (status == B_OK)
687 					state = INIT;
688 				break;
689 		}
690 	}
691 
692 	close(socket);
693 
694 	if (status == B_OK && fLeaseTime > 0) {
695 		// notify early enough when the lease is
696 		if (fRenewalTime == 0)
697 			fRenewalTime = fLeaseTime * 2/3;
698 		if (fRebindingTime == 0)
699 			fRebindingTime = fLeaseTime * 5/6;
700 
701 		bigtime_t now = system_time();
702 		_RestartLease(fRenewalTime);
703 
704 		fLeaseTime += now;
705 		fRenewalTime += now;
706 		fRebindingTime += now;
707 			// make lease times absolute
708 	} else {
709 		fLeaseTime = previousLeaseTime;
710 		bigtime_t now = system_time();
711 		fRenewalTime = (fLeaseTime - now) * 2/3 + now;
712 		fRebindingTime = (fLeaseTime - now) * 5/6 + now;
713 	}
714 
715 	return status;
716 }
717 
718 
719 void
720 DHCPClient::_RestartLease(bigtime_t leaseTime)
721 {
722 	if (leaseTime == 0)
723 		return;
724 
725 	BMessage lease(kMsgLeaseTime);
726 	fRunner = new BMessageRunner(this, &lease, leaseTime, 1);
727 }
728 
729 
730 void
731 DHCPClient::_ParseOptions(dhcp_message& message, BMessage& address,
732 	BMessage& resolverConfiguration)
733 {
734 	dhcp_option_cookie cookie;
735 	message_option option;
736 	const uint8* data;
737 	size_t size;
738 	while (message.NextOption(cookie, option, data, size)) {
739 		// iterate through all options
740 		switch (option) {
741 			case OPTION_ROUTER_ADDRESS:
742 				syslog(LOG_DEBUG, "  gateway: %s\n",
743 					_AddressToString(data).String());
744 				address.AddString("gateway", _AddressToString(data));
745 				break;
746 			case OPTION_SUBNET_MASK:
747 				syslog(LOG_DEBUG, "  subnet: %s\n",
748 					_AddressToString(data).String());
749 				address.AddString("mask", _AddressToString(data));
750 				break;
751 			case OPTION_BROADCAST_ADDRESS:
752 				syslog(LOG_DEBUG, "  broadcast: %s\n",
753 					_AddressToString(data).String());
754 				address.AddString("broadcast", _AddressToString(data));
755 				break;
756 			case OPTION_DOMAIN_NAME_SERVER:
757 			{
758 				for (uint32 i = 0; i < size / 4; i++) {
759 					syslog(LOG_DEBUG, "  nameserver[%d]: %s\n", i,
760 						_AddressToString(&data[i * 4]).String());
761 					resolverConfiguration.AddString("nameserver",
762 						_AddressToString(&data[i * 4]).String());
763 				}
764 				resolverConfiguration.AddInt32("nameserver_count",
765 					size / 4);
766 				break;
767 			}
768 			case OPTION_SERVER_ADDRESS:
769 				syslog(LOG_DEBUG, "  server: %s\n",
770 					_AddressToString(data).String());
771 				fServer.SetAddress(*(in_addr_t*)data);
772 				break;
773 
774 			case OPTION_ADDRESS_LEASE_TIME:
775 				syslog(LOG_DEBUG, "  lease time: %lu seconds\n",
776 					ntohl(*(uint32*)data));
777 				fLeaseTime = ntohl(*(uint32*)data) * 1000000LL;
778 				break;
779 			case OPTION_RENEWAL_TIME:
780 				syslog(LOG_DEBUG, "  renewal time: %lu seconds\n",
781 					ntohl(*(uint32*)data));
782 				fRenewalTime = ntohl(*(uint32*)data) * 1000000LL;
783 				break;
784 			case OPTION_REBINDING_TIME:
785 				syslog(LOG_DEBUG, "  rebinding time: %lu seconds\n",
786 					ntohl(*(uint32*)data));
787 				fRebindingTime = ntohl(*(uint32*)data) * 1000000LL;
788 				break;
789 
790 			case OPTION_HOST_NAME:
791 				syslog(LOG_DEBUG, "  host name: \"%.*s\"\n", (int)size,
792 					(const char*)data);
793 				break;
794 
795 			case OPTION_DOMAIN_NAME:
796 			{
797 				char domain[256];
798 				strlcpy(domain, (const char*)data,
799 					min_c(size + 1, sizeof(domain)));
800 
801 				syslog(LOG_DEBUG, "  domain name: \"%s\"\n", domain);
802 
803 				resolverConfiguration.AddString("domain", domain);
804 				break;
805 			}
806 
807 			case OPTION_MESSAGE_TYPE:
808 				break;
809 
810 			case OPTION_ERROR_MESSAGE:
811 				syslog(LOG_INFO, "  error message: \"%.*s\"\n", (int)size,
812 					(const char*)data);
813 				break;
814 
815 			default:
816 				syslog(LOG_DEBUG, "  UNKNOWN OPTION %lu (0x%x)\n",
817 					(uint32)option, (uint32)option);
818 				break;
819 		}
820 	}
821 }
822 
823 
824 void
825 DHCPClient::_PrepareMessage(dhcp_message& message, dhcp_state state)
826 {
827 	message.opcode = BOOT_REQUEST;
828 	message.hardware_type = ARP_HARDWARE_TYPE_ETHER;
829 	message.hardware_address_length = 6;
830 	message.transaction_id = htonl(fTransactionID);
831 	message.seconds_since_start = htons(min_c((system_time() - fStartTime)
832 		/ 1000000LL, 65535));
833 	memcpy(message.mac_address, fMAC, 6);
834 
835 	message_type type = message.Type();
836 
837 	uint8 *next = message.PrepareMessage(type);
838 
839 	switch (type) {
840 		case DHCP_DISCOVER:
841 			next = message.PutOption(next, OPTION_REQUEST_PARAMETERS,
842 				kRequestParameters, sizeof(kRequestParameters));
843 			break;
844 
845 		case DHCP_REQUEST:
846 			next = message.PutOption(next, OPTION_REQUEST_PARAMETERS,
847 				kRequestParameters, sizeof(kRequestParameters));
848 
849 			if (state == REQUESTING) {
850 				const sockaddr_in& server = (sockaddr_in&)fServer.SockAddr();
851 				next = message.PutOption(next, OPTION_SERVER_ADDRESS,
852 					(uint32)server.sin_addr.s_addr);
853 			}
854 
855 			if (state == INIT || state == INIT_REBOOT
856 				|| state == REQUESTING) {
857 				next = message.PutOption(next, OPTION_REQUEST_IP_ADDRESS,
858 					(uint32)fAssignedAddress);
859 			} else
860 				message.client_address = fAssignedAddress;
861 			break;
862 
863 		case DHCP_RELEASE: {
864 			const sockaddr_in& server = (sockaddr_in&)fServer.SockAddr();
865 			next = message.PutOption(next, OPTION_SERVER_ADDRESS,
866 				(uint32)server.sin_addr.s_addr);
867 
868 			message.client_address = fAssignedAddress;
869 			break;
870 		}
871 
872 		default:
873 			break;
874 	}
875 
876 	message.FinishOptions(next);
877 }
878 
879 
880 void
881 DHCPClient::_ResetTimeout(int socket, time_t& timeout, uint32& tries)
882 {
883 	timeout = DEFAULT_TIMEOUT;
884 	tries = 0;
885 
886 	struct timeval value;
887 	value.tv_sec = timeout;
888 	value.tv_usec = rand() % 1000000;
889 	setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &value, sizeof(value));
890 }
891 
892 
893 bool
894 DHCPClient::_TimeoutShift(int socket, time_t& timeout, uint32& tries)
895 {
896 	timeout += timeout;
897 	if (timeout > MAX_TIMEOUT) {
898 		timeout = DEFAULT_TIMEOUT;
899 
900 		if (++tries > 2)
901 			return false;
902 	}
903 	syslog(LOG_DEBUG, "%s: Timeout shift: %lu secs (try %lu)\n",
904 		Device(), timeout, tries);
905 
906 	struct timeval value;
907 	value.tv_sec = timeout;
908 	value.tv_usec = rand() % 1000000;
909 	setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &value, sizeof(value));
910 
911 	return true;
912 }
913 
914 
915 /*static*/ BString
916 DHCPClient::_AddressToString(const uint8* data)
917 {
918 	BString target = inet_ntoa(*(in_addr*)data);
919 	return target;
920 }
921 
922 
923 /*static*/ BString
924 DHCPClient::_AddressToString(in_addr_t address)
925 {
926 	BString target = inet_ntoa(*(in_addr*)&address);
927 	return target;
928 }
929 
930 
931 status_t
932 DHCPClient::_SendMessage(int socket, dhcp_message& message,
933 	const BNetworkAddress& address) const
934 {
935 	message_type type = message.Type();
936 	BString text;
937 	text << dhcp_message::TypeToString(type);
938 
939 	const uint8* requestAddress = message.FindOption(OPTION_REQUEST_IP_ADDRESS);
940 	if (type == DHCP_REQUEST && requestAddress != NULL)
941 		text << " for " << _AddressToString(requestAddress).String();
942 
943 	syslog(LOG_DEBUG, "%s: Send %s to %s\n", Device(), text.String(),
944 		address.ToString().String());
945 
946 	ssize_t bytesSent = sendto(socket, &message, message.Size(),
947 		address.IsBroadcast() ? MSG_BCAST : 0, address, address.Length());
948 	if (bytesSent < 0)
949 		return errno;
950 
951 	return B_OK;
952 }
953 
954 
955 dhcp_state
956 DHCPClient::_CurrentState() const
957 {
958 	bigtime_t now = system_time();
959 
960 	if (now > fLeaseTime || fStatus < B_OK)
961 		return INIT;
962 	if (now >= fRebindingTime)
963 		return REBINDING;
964 	if (now >= fRenewalTime)
965 		return RENEWING;
966 
967 	return BOUND;
968 }
969 
970 
971 void
972 DHCPClient::MessageReceived(BMessage* message)
973 {
974 	switch (message->what) {
975 		case kMsgLeaseTime:
976 		{
977 			dhcp_state state = _CurrentState();
978 
979 			bigtime_t next;
980 			if (_Negotiate(state) == B_OK) {
981 				switch (state) {
982 					case RENEWING:
983 						next = fRebindingTime;
984 						break;
985 					case REBINDING:
986 					default:
987 						next = fRenewalTime;
988 						break;
989 				}
990 			} else {
991 				switch (state) {
992 					case RENEWING:
993 						next = (fLeaseTime - fRebindingTime) / 4 + system_time();
994 						break;
995 					case REBINDING:
996 					default:
997 						next = (fLeaseTime - fRenewalTime) / 4 + system_time();
998 						break;
999 				}
1000 			}
1001 
1002 			_RestartLease(next - system_time());
1003 			break;
1004 		}
1005 
1006 		default:
1007 			BHandler::MessageReceived(message);
1008 			break;
1009 	}
1010 }
1011