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