xref: /haiku/src/servers/net/DHCPClient.cpp (revision ac078a5b110045de12089052a685a6a080545db1)
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 	}
451 
452 	openlog_thread("DHCP", 0, LOG_DAEMON);
453 }
454 
455 
456 DHCPClient::~DHCPClient()
457 {
458 	if (fStatus != B_OK)
459 		return;
460 
461 	delete fRunner;
462 
463 	int socket = ::socket(AF_INET, SOCK_DGRAM, 0);
464 	if (socket < 0)
465 		return;
466 
467 	// release lease
468 
469 	dhcp_message release(DHCP_RELEASE);
470 	_PrepareMessage(release, BOUND);
471 
472 	_SendMessage(socket, release, fServer);
473 	close(socket);
474 
475 	closelog_thread();
476 }
477 
478 
479 status_t
480 DHCPClient::Initialize()
481 {
482 	fStatus = _Negotiate(fAssignedAddress == 0 ? INIT : INIT_REBOOT);
483 	syslog(LOG_DEBUG, "%s: DHCP status = %s\n", Device(), strerror(fStatus));
484 	return fStatus;
485 }
486 
487 
488 status_t
489 DHCPClient::_Negotiate(dhcp_state state)
490 {
491 	int socket = ::socket(AF_INET, SOCK_DGRAM, 0);
492 	if (socket < 0)
493 		return errno;
494 
495 	BNetworkAddress local;
496 	local.SetToWildcard(AF_INET, DHCP_CLIENT_PORT);
497 
498 	// Enable reusing the port. This is needed in case there is more
499 	// than 1 interface that needs to be configured. Note that the only reason
500 	// this works is because there is code below to bind to a specific
501 	// interface.
502 	int option = 1;
503 	setsockopt(socket, SOL_SOCKET, SO_REUSEPORT, &option, sizeof(option));
504 
505 	if (bind(socket, local, local.Length()) < 0) {
506 		close(socket);
507 		return errno;
508 	}
509 
510 	BNetworkAddress broadcast;
511 	broadcast.SetToBroadcast(AF_INET, DHCP_SERVER_PORT);
512 
513 	option = 1;
514 	setsockopt(socket, SOL_SOCKET, SO_BROADCAST, &option, sizeof(option));
515 
516 	if (state == INIT) {
517 		// The local interface does not have an address yet, bind the socket
518 		// to the device directly.
519 		BNetworkDevice device(Device());
520 		int index = device.Index();
521 
522 		setsockopt(socket, SOL_SOCKET, SO_BINDTODEVICE, &index, sizeof(int));
523 	}
524 
525 	bigtime_t previousLeaseTime = fLeaseTime;
526 	fLeaseTime = 0;
527 	fRenewalTime = 0;
528 	fRebindingTime = 0;
529 
530 	status_t status = B_ERROR;
531 	time_t timeout;
532 	uint32 tries;
533 	_ResetTimeout(socket, timeout, tries);
534 
535 	dhcp_message discover(DHCP_DISCOVER);
536 	_PrepareMessage(discover, state);
537 
538 	dhcp_message request(DHCP_REQUEST);
539 	_PrepareMessage(request, state);
540 
541 	// send discover/request message
542 	_SendMessage(socket, state == INIT ? discover : request,
543 		state != RENEWING ? broadcast : fServer);
544 		// no need to check the status; in case of an error we'll just send
545 		// the message again
546 
547 	// receive loop until we've got an offer and acknowledged it
548 
549 	while (state != BOUND) {
550 		char buffer[2048];
551 		struct sockaddr_in from;
552 		socklen_t fromLength = sizeof(from);
553 		ssize_t bytesReceived = recvfrom(socket, buffer, sizeof(buffer),
554 			0, (struct sockaddr*)&from, &fromLength);
555 		if (bytesReceived < 0 && errno == B_TIMED_OUT) {
556 			// depending on the state, we'll just try again
557 			if (!_TimeoutShift(socket, timeout, tries)) {
558 				close(socket);
559 				return B_TIMED_OUT;
560 			}
561 
562 			_SendMessage(socket, state == INIT ? discover : request,
563 				state != RENEWING ? broadcast : fServer);
564 
565 			continue;
566 		} else if (bytesReceived < 0)
567 			break;
568 
569 		dhcp_message* message = (dhcp_message*)buffer;
570 		if (message->transaction_id != htonl(fTransactionID)
571 			|| !message->HasOptions()
572 			|| memcmp(message->mac_address, discover.mac_address,
573 				discover.hardware_address_length)) {
574 			// this message is not for us
575 			continue;
576 		}
577 
578 		// advance from startup state
579 		if (state == INIT)
580 			state = SELECTING;
581 		else if (state == INIT_REBOOT)
582 			state = REBOOTING;
583 
584 		syslog(LOG_DEBUG, "%s: Received %s from %s\n",
585 			Device(), dhcp_message::TypeToString(message->Type()),
586 			_AddressToString(from.sin_addr.s_addr).String());
587 
588 		switch (message->Type()) {
589 			case DHCP_NONE:
590 			default:
591 				// ignore this message
592 				break;
593 
594 			case DHCP_OFFER:
595 			{
596 				// first offer wins
597 				if (state != SELECTING)
598 					break;
599 
600 				// collect interface options
601 
602 				fAssignedAddress = message->your_address;
603 				syslog(LOG_INFO, "  your_address: %s\n",
604 						_AddressToString(fAssignedAddress).String());
605 
606 				fConfiguration.MakeEmpty();
607 				fConfiguration.AddString("device", Device());
608 				fConfiguration.AddBool("auto_configured", true);
609 
610 				BMessage address;
611 				address.AddString("family", "inet");
612 				address.AddString("address", _AddressToString(fAssignedAddress));
613 				fResolverConfiguration.MakeEmpty();
614 				_ParseOptions(*message, address, fResolverConfiguration);
615 
616 				fConfiguration.AddMessage("address", &address);
617 
618 				// request configuration from the server
619 
620 				_ResetTimeout(socket, timeout, tries);
621 				state = REQUESTING;
622 				_PrepareMessage(request, state);
623 
624 				status = _SendMessage(socket, request, broadcast);
625 					// we're sending a broadcast so that all potential offers
626 					// get an answer
627 				break;
628 			}
629 
630 			case DHCP_ACK:
631 			{
632 				if (state != REQUESTING
633 					&& state != REBOOTING
634 					&& state != REBINDING
635 					&& state != RENEWING)
636 					continue;
637 
638 				// TODO: we might want to configure the stuff, don't we?
639 				BMessage address;
640 				fResolverConfiguration.MakeEmpty();
641 				_ParseOptions(*message, address, fResolverConfiguration);
642 					// TODO: currently, only lease time and DNS is updated this
643 					// way
644 
645 				// our address request has been acknowledged
646 				state = BOUND;
647 
648 				// configure interface
649 				BMessage reply;
650 				status = Target().SendMessage(&fConfiguration, &reply);
651 				if (status == B_OK)
652 					status = reply.FindInt32("status", &fStatus);
653 
654 				// configure resolver
655 				reply.MakeEmpty();
656 				fResolverConfiguration.AddString("device", Device());
657 				status = Target().SendMessage(&fResolverConfiguration, &reply);
658 				if (status == B_OK)
659 					status = reply.FindInt32("status", &fStatus);
660 				break;
661 			}
662 
663 			case DHCP_NACK:
664 				if (state != REQUESTING
665 					&& state != REBOOTING
666 					&& state != REBINDING
667 					&& state != RENEWING)
668 					continue;
669 
670 				if (state == REBOOTING) {
671 					// server reject our request on previous assigned address
672 					// back to square one...
673 					fAssignedAddress = 0;
674 				}
675 
676 				// try again (maybe we should prefer other servers if this
677 				// happens more than once)
678 				status = _SendMessage(socket, discover, broadcast);
679 				if (status == B_OK)
680 					state = INIT;
681 				break;
682 		}
683 	}
684 
685 	close(socket);
686 
687 	if (status == B_OK && fLeaseTime > 0) {
688 		// notify early enough when the lease is
689 		if (fRenewalTime == 0)
690 			fRenewalTime = fLeaseTime * 2/3;
691 		if (fRebindingTime == 0)
692 			fRebindingTime = fLeaseTime * 5/6;
693 
694 		bigtime_t now = system_time();
695 		_RestartLease(fRenewalTime);
696 
697 		fLeaseTime += now;
698 		fRenewalTime += now;
699 		fRebindingTime += now;
700 			// make lease times absolute
701 	} else {
702 		fLeaseTime = previousLeaseTime;
703 		bigtime_t now = system_time();
704 		fRenewalTime = (fLeaseTime - now) * 2/3 + now;
705 		fRebindingTime = (fLeaseTime - now) * 5/6 + now;
706 	}
707 
708 	return status;
709 }
710 
711 
712 void
713 DHCPClient::_RestartLease(bigtime_t leaseTime)
714 {
715 	if (leaseTime == 0)
716 		return;
717 
718 	BMessage lease(kMsgLeaseTime);
719 	fRunner = new BMessageRunner(this, &lease, leaseTime, 1);
720 }
721 
722 
723 void
724 DHCPClient::_ParseOptions(dhcp_message& message, BMessage& address,
725 	BMessage& resolverConfiguration)
726 {
727 	dhcp_option_cookie cookie;
728 	message_option option;
729 	const uint8* data;
730 	size_t size;
731 	while (message.NextOption(cookie, option, data, size)) {
732 		// iterate through all options
733 		switch (option) {
734 			case OPTION_ROUTER_ADDRESS:
735 				syslog(LOG_DEBUG, "  gateway: %s\n",
736 					_AddressToString(data).String());
737 				address.AddString("gateway", _AddressToString(data));
738 				break;
739 			case OPTION_SUBNET_MASK:
740 				syslog(LOG_DEBUG, "  subnet: %s\n",
741 					_AddressToString(data).String());
742 				address.AddString("mask", _AddressToString(data));
743 				break;
744 			case OPTION_BROADCAST_ADDRESS:
745 				syslog(LOG_DEBUG, "  broadcast: %s\n",
746 					_AddressToString(data).String());
747 				address.AddString("broadcast", _AddressToString(data));
748 				break;
749 			case OPTION_DOMAIN_NAME_SERVER:
750 			{
751 				for (uint32 i = 0; i < size / 4; i++) {
752 					syslog(LOG_DEBUG, "  nameserver[%d]: %s\n", i,
753 						_AddressToString(&data[i * 4]).String());
754 					resolverConfiguration.AddString("nameserver",
755 						_AddressToString(&data[i * 4]).String());
756 				}
757 				resolverConfiguration.AddInt32("nameserver_count",
758 					size / 4);
759 				break;
760 			}
761 			case OPTION_SERVER_ADDRESS:
762 				syslog(LOG_DEBUG, "  server: %s\n",
763 					_AddressToString(data).String());
764 				fServer.SetAddress(*(in_addr_t*)data);
765 				break;
766 
767 			case OPTION_ADDRESS_LEASE_TIME:
768 				syslog(LOG_DEBUG, "  lease time: %lu seconds\n",
769 					ntohl(*(uint32*)data));
770 				fLeaseTime = ntohl(*(uint32*)data) * 1000000LL;
771 				break;
772 			case OPTION_RENEWAL_TIME:
773 				syslog(LOG_DEBUG, "  renewal time: %lu seconds\n",
774 					ntohl(*(uint32*)data));
775 				fRenewalTime = ntohl(*(uint32*)data) * 1000000LL;
776 				break;
777 			case OPTION_REBINDING_TIME:
778 				syslog(LOG_DEBUG, "  rebinding time: %lu seconds\n",
779 					ntohl(*(uint32*)data));
780 				fRebindingTime = ntohl(*(uint32*)data) * 1000000LL;
781 				break;
782 
783 			case OPTION_HOST_NAME:
784 				syslog(LOG_DEBUG, "  host name: \"%.*s\"\n", (int)size,
785 					(const char*)data);
786 				break;
787 
788 			case OPTION_DOMAIN_NAME:
789 			{
790 				char domain[256];
791 				strlcpy(domain, (const char*)data,
792 					min_c(size + 1, sizeof(domain)));
793 
794 				syslog(LOG_DEBUG, "  domain name: \"%s\"\n", domain);
795 
796 				resolverConfiguration.AddString("domain", domain);
797 				break;
798 			}
799 
800 			case OPTION_MESSAGE_TYPE:
801 				break;
802 
803 			case OPTION_ERROR_MESSAGE:
804 				syslog(LOG_INFO, "  error message: \"%.*s\"\n", (int)size,
805 					(const char*)data);
806 				break;
807 
808 			default:
809 				syslog(LOG_DEBUG, "  UNKNOWN OPTION %lu (0x%x)\n",
810 					(uint32)option, (uint32)option);
811 				break;
812 		}
813 	}
814 }
815 
816 
817 void
818 DHCPClient::_PrepareMessage(dhcp_message& message, dhcp_state state)
819 {
820 	message.opcode = BOOT_REQUEST;
821 	message.hardware_type = ARP_HARDWARE_TYPE_ETHER;
822 	message.hardware_address_length = 6;
823 	message.transaction_id = htonl(fTransactionID);
824 	message.seconds_since_start = htons(min_c((system_time() - fStartTime)
825 		/ 1000000LL, 65535));
826 	memcpy(message.mac_address, fMAC, 6);
827 
828 	message_type type = message.Type();
829 
830 	uint8 *next = message.PrepareMessage(type);
831 
832 	switch (type) {
833 		case DHCP_DISCOVER:
834 			next = message.PutOption(next, OPTION_REQUEST_PARAMETERS,
835 				kRequestParameters, sizeof(kRequestParameters));
836 			break;
837 
838 		case DHCP_REQUEST:
839 			next = message.PutOption(next, OPTION_REQUEST_PARAMETERS,
840 				kRequestParameters, sizeof(kRequestParameters));
841 
842 			if (state == REQUESTING) {
843 				const sockaddr_in& server = (sockaddr_in&)fServer.SockAddr();
844 				next = message.PutOption(next, OPTION_SERVER_ADDRESS,
845 					(uint32)server.sin_addr.s_addr);
846 			}
847 
848 			if (state == INIT || state == INIT_REBOOT
849 				|| state == REQUESTING) {
850 				next = message.PutOption(next, OPTION_REQUEST_IP_ADDRESS,
851 					(uint32)fAssignedAddress);
852 			} else
853 				message.client_address = fAssignedAddress;
854 			break;
855 
856 		case DHCP_RELEASE: {
857 			const sockaddr_in& server = (sockaddr_in&)fServer.SockAddr();
858 			next = message.PutOption(next, OPTION_SERVER_ADDRESS,
859 				(uint32)server.sin_addr.s_addr);
860 
861 			message.client_address = fAssignedAddress;
862 			break;
863 		}
864 
865 		default:
866 			break;
867 	}
868 
869 	message.FinishOptions(next);
870 }
871 
872 
873 void
874 DHCPClient::_ResetTimeout(int socket, time_t& timeout, uint32& tries)
875 {
876 	timeout = DEFAULT_TIMEOUT;
877 	tries = 0;
878 
879 	struct timeval value;
880 	value.tv_sec = timeout;
881 	value.tv_usec = rand() % 1000000;
882 	setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &value, sizeof(value));
883 }
884 
885 
886 bool
887 DHCPClient::_TimeoutShift(int socket, time_t& timeout, uint32& tries)
888 {
889 	timeout += timeout;
890 	if (timeout > MAX_TIMEOUT) {
891 		timeout = DEFAULT_TIMEOUT;
892 
893 		if (++tries > 2)
894 			return false;
895 	}
896 	syslog(LOG_DEBUG, "%s: Timeout shift: %lu secs (try %lu)\n",
897 		Device(), timeout, tries);
898 
899 	struct timeval value;
900 	value.tv_sec = timeout;
901 	value.tv_usec = rand() % 1000000;
902 	setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &value, sizeof(value));
903 
904 	return true;
905 }
906 
907 
908 /*static*/ BString
909 DHCPClient::_AddressToString(const uint8* data)
910 {
911 	BString target = inet_ntoa(*(in_addr*)data);
912 	return target;
913 }
914 
915 
916 /*static*/ BString
917 DHCPClient::_AddressToString(in_addr_t address)
918 {
919 	BString target = inet_ntoa(*(in_addr*)&address);
920 	return target;
921 }
922 
923 
924 status_t
925 DHCPClient::_SendMessage(int socket, dhcp_message& message,
926 	const BNetworkAddress& address) const
927 {
928 	message_type type = message.Type();
929 	BString text;
930 	text << dhcp_message::TypeToString(type);
931 
932 	const uint8* requestAddress = message.FindOption(OPTION_REQUEST_IP_ADDRESS);
933 	if (type == DHCP_REQUEST && requestAddress != NULL)
934 		text << " for " << _AddressToString(requestAddress).String();
935 
936 	syslog(LOG_DEBUG, "%s: Send %s to %s\n", Device(), text.String(),
937 		address.ToString().String());
938 
939 	ssize_t bytesSent = sendto(socket, &message, message.Size(),
940 		address.IsBroadcast() ? MSG_BCAST : 0, address, address.Length());
941 	if (bytesSent < 0)
942 		return errno;
943 
944 	return B_OK;
945 }
946 
947 
948 dhcp_state
949 DHCPClient::_CurrentState() const
950 {
951 	bigtime_t now = system_time();
952 
953 	if (now > fLeaseTime || fStatus < B_OK)
954 		return INIT;
955 	if (now >= fRebindingTime)
956 		return REBINDING;
957 	if (now >= fRenewalTime)
958 		return RENEWING;
959 
960 	return BOUND;
961 }
962 
963 
964 void
965 DHCPClient::MessageReceived(BMessage* message)
966 {
967 	switch (message->what) {
968 		case kMsgLeaseTime:
969 		{
970 			dhcp_state state = _CurrentState();
971 
972 			bigtime_t next;
973 			if (_Negotiate(state) == B_OK) {
974 				switch (state) {
975 					case RENEWING:
976 						next = fRebindingTime;
977 						break;
978 					case REBINDING:
979 					default:
980 						next = fRenewalTime;
981 						break;
982 				}
983 			} else {
984 				switch (state) {
985 					case RENEWING:
986 						next = (fLeaseTime - fRebindingTime) / 4 + system_time();
987 						break;
988 					case REBINDING:
989 					default:
990 						next = (fLeaseTime - fRenewalTime) / 4 + system_time();
991 						break;
992 				}
993 			}
994 
995 			_RestartLease(next - system_time());
996 			break;
997 		}
998 
999 		default:
1000 			BHandler::MessageReceived(message);
1001 			break;
1002 	}
1003 }
1004