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