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