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