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