xref: /haiku/src/servers/net/DHCPClient.cpp (revision a5a3b2d9a3d95cbae71eaf371708c73a1780ac0d)
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 = 1,
89 	DHCP_OFFER = 2,
90 	DHCP_REQUEST = 3,
91 	DHCP_DECLINE = 4,
92 	DHCP_ACK = 5,
93 	DHCP_NACK = 6,
94 	DHCP_RELEASE = 7,
95 	DHCP_INFORM = 8
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, B_UNCONFIGURED_ADDRESS_FAMILIES),
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 			{
767 				syslog(LOG_DEBUG, "  server: %s\n",
768 					_AddressToString(data).String());
769 				status_t status = fServer.SetAddress(*(in_addr_t*)data);
770 				if (status != B_OK) {
771 					syslog(LOG_ERR, "   BNetworkAddress::SetAddress failed with %s!\n",
772 						strerror(status));
773 					fServer.Unset();
774 				}
775 				break;
776 			}
777 
778 			case OPTION_ADDRESS_LEASE_TIME:
779 				syslog(LOG_DEBUG, "  lease time: %lu seconds\n",
780 					ntohl(*(uint32*)data));
781 				fLeaseTime = ntohl(*(uint32*)data) * 1000000LL;
782 				break;
783 			case OPTION_RENEWAL_TIME:
784 				syslog(LOG_DEBUG, "  renewal time: %lu seconds\n",
785 					ntohl(*(uint32*)data));
786 				fRenewalTime = ntohl(*(uint32*)data) * 1000000LL;
787 				break;
788 			case OPTION_REBINDING_TIME:
789 				syslog(LOG_DEBUG, "  rebinding time: %lu seconds\n",
790 					ntohl(*(uint32*)data));
791 				fRebindingTime = ntohl(*(uint32*)data) * 1000000LL;
792 				break;
793 
794 			case OPTION_HOST_NAME:
795 				syslog(LOG_DEBUG, "  host name: \"%.*s\"\n", (int)size,
796 					(const char*)data);
797 				break;
798 
799 			case OPTION_DOMAIN_NAME:
800 			{
801 				char domain[256];
802 				strlcpy(domain, (const char*)data,
803 					min_c(size + 1, sizeof(domain)));
804 
805 				syslog(LOG_DEBUG, "  domain name: \"%s\"\n", domain);
806 
807 				resolverConfiguration.AddString("domain", domain);
808 				break;
809 			}
810 
811 			case OPTION_MESSAGE_TYPE:
812 				break;
813 
814 			case OPTION_ERROR_MESSAGE:
815 				syslog(LOG_INFO, "  error message: \"%.*s\"\n", (int)size,
816 					(const char*)data);
817 				break;
818 
819 			default:
820 				syslog(LOG_DEBUG, "  UNKNOWN OPTION %lu (0x%x)\n",
821 					(uint32)option, (uint32)option);
822 				break;
823 		}
824 	}
825 }
826 
827 
828 void
829 DHCPClient::_PrepareMessage(dhcp_message& message, dhcp_state state)
830 {
831 	message.opcode = BOOT_REQUEST;
832 	message.hardware_type = ARP_HARDWARE_TYPE_ETHER;
833 	message.hardware_address_length = 6;
834 	message.transaction_id = htonl(fTransactionID);
835 	message.seconds_since_start = htons(min_c((system_time() - fStartTime)
836 		/ 1000000LL, 65535));
837 	memcpy(message.mac_address, fMAC, 6);
838 
839 	message_type type = message.Type();
840 
841 	uint8 *next = message.PrepareMessage(type);
842 
843 	switch (type) {
844 		case DHCP_DISCOVER:
845 			next = message.PutOption(next, OPTION_REQUEST_PARAMETERS,
846 				kRequestParameters, sizeof(kRequestParameters));
847 
848 			if (fHostName.Length() > 0) {
849 				next = message.PutOption(next, OPTION_HOST_NAME,
850 					reinterpret_cast<const uint8*>(fHostName.String()),
851 					fHostName.Length());
852 			}
853 			break;
854 
855 		case DHCP_REQUEST:
856 			next = message.PutOption(next, OPTION_REQUEST_PARAMETERS,
857 				kRequestParameters, sizeof(kRequestParameters));
858 
859 			if (fHostName.Length() > 0) {
860 				next = message.PutOption(next, OPTION_HOST_NAME,
861 					reinterpret_cast<const uint8*>(fHostName.String()),
862 					fHostName.Length());
863 			}
864 
865 			if (state == REQUESTING) {
866 				const sockaddr_in& server = (sockaddr_in&)fServer.SockAddr();
867 				next = message.PutOption(next, OPTION_SERVER_ADDRESS,
868 					(uint32)server.sin_addr.s_addr);
869 			}
870 
871 			if (state == INIT || state == INIT_REBOOT
872 				|| state == REQUESTING) {
873 				next = message.PutOption(next, OPTION_REQUEST_IP_ADDRESS,
874 					(uint32)fAssignedAddress);
875 			} else
876 				message.client_address = fAssignedAddress;
877 			break;
878 
879 		case DHCP_RELEASE: {
880 			const sockaddr_in& server = (sockaddr_in&)fServer.SockAddr();
881 			next = message.PutOption(next, OPTION_SERVER_ADDRESS,
882 				(uint32)server.sin_addr.s_addr);
883 
884 			message.client_address = fAssignedAddress;
885 			break;
886 		}
887 
888 		default:
889 			break;
890 	}
891 
892 	message.FinishOptions(next);
893 }
894 
895 
896 void
897 DHCPClient::_ResetTimeout(int socket, dhcp_state& state, time_t& timeout,
898 	uint32& tries)
899 {
900 	timeout = DEFAULT_TIMEOUT;
901 	tries = 0;
902 
903 	struct timeval value;
904 	value.tv_sec = timeout;
905 	value.tv_usec = rand() % 1000000;
906 	setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &value, sizeof(value));
907 }
908 
909 
910 bool
911 DHCPClient::_TimeoutShift(int socket, dhcp_state& state, time_t& timeout,
912 	uint32& tries)
913 {
914 	if (state == RENEWING && system_time() > fRebindingTime) {
915 		state = REBINDING;
916 		return false;
917 	}
918 
919 	if (state == REBINDING && system_time() > fLeaseTime) {
920 		state = INIT;
921 		return false;
922 	}
923 
924 	tries++;
925 	timeout += timeout;
926 	if (timeout > MAX_TIMEOUT)
927 		timeout = MAX_TIMEOUT;
928 
929 	if (tries > MAX_RETRIES) {
930 		bigtime_t remaining = 0;
931 		if (state == RENEWING)
932 			remaining = (fRebindingTime - system_time()) / 2 + 1;
933 		else if (state == REBINDING)
934 			remaining = (fLeaseTime - system_time()) / 2 + 1;
935 		else
936 			return false;
937 
938 		timeout = std::max(remaining / 1000000, bigtime_t(60));
939 	}
940 
941 	syslog(LOG_DEBUG, "%s: Timeout shift: %lu secs (try %lu)\n",
942 		Device(), timeout, tries);
943 
944 	struct timeval value;
945 	value.tv_sec = timeout;
946 	value.tv_usec = rand() % 1000000;
947 	setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &value, sizeof(value));
948 
949 	return true;
950 }
951 
952 
953 /*static*/ BString
954 DHCPClient::_AddressToString(const uint8* data)
955 {
956 	BString target = inet_ntoa(*(in_addr*)data);
957 	return target;
958 }
959 
960 
961 /*static*/ BString
962 DHCPClient::_AddressToString(in_addr_t address)
963 {
964 	BString target = inet_ntoa(*(in_addr*)&address);
965 	return target;
966 }
967 
968 
969 status_t
970 DHCPClient::_SendMessage(int socket, dhcp_message& message,
971 	const BNetworkAddress& address) const
972 {
973 	message_type type = message.Type();
974 	BString text;
975 	text << dhcp_message::TypeToString(type);
976 
977 	const uint8* requestAddress = message.FindOption(OPTION_REQUEST_IP_ADDRESS);
978 	if (type == DHCP_REQUEST && requestAddress != NULL)
979 		text << " for " << _AddressToString(requestAddress).String();
980 
981 	syslog(LOG_DEBUG, "%s: Send %s to %s\n", Device(), text.String(),
982 		address.ToString().String());
983 
984 	ssize_t bytesSent = sendto(socket, &message, message.Size(),
985 		address.IsBroadcast() ? MSG_BCAST : 0, address, address.Length());
986 	if (bytesSent < 0)
987 		return errno;
988 
989 	return B_OK;
990 }
991 
992 
993 dhcp_state
994 DHCPClient::_CurrentState() const
995 {
996 	bigtime_t now = system_time();
997 
998 	if (now > fLeaseTime || fStatus != B_OK)
999 		return INIT;
1000 	if (now >= fRebindingTime)
1001 		return REBINDING;
1002 	if (now >= fRenewalTime)
1003 		return RENEWING;
1004 	return BOUND;
1005 }
1006 
1007 
1008 void
1009 DHCPClient::MessageReceived(BMessage* message)
1010 {
1011 	switch (message->what) {
1012 		case kMsgLeaseTime:
1013 			_Negotiate(_CurrentState());
1014 			break;
1015 
1016 		default:
1017 			BHandler::MessageReceived(message);
1018 			break;
1019 	}
1020 }
1021