xref: /haiku/src/servers/net/DHCPClient.cpp (revision 50b3e74489a1a46fec88df793e4f6780e4de933c)
1 /*
2  * Copyright 2006-2010, 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  */
9 
10 
11 #include "DHCPClient.h"
12 #include "NetServer.h"
13 
14 #include <Message.h>
15 #include <MessageRunner.h>
16 
17 #include <arpa/inet.h>
18 #include <errno.h>
19 #include <stdio.h>
20 #include <string.h>
21 #include <syslog.h>
22 #include <sys/sockio.h>
23 #include <sys/time.h>
24 
25 
26 // See RFC 2131 for DHCP, see RFC 1533 for BOOTP/DHCP options
27 
28 #define DHCP_CLIENT_PORT	68
29 #define DHCP_SERVER_PORT	67
30 
31 #define DEFAULT_TIMEOUT		2	// secs
32 #define MAX_TIMEOUT			15	// secs
33 
34 enum message_opcode {
35 	BOOT_REQUEST = 1,
36 	BOOT_REPLY
37 };
38 
39 enum message_option {
40 	OPTION_MAGIC = 0x63825363,
41 
42 	// generic options
43 	OPTION_PAD = 0,
44 	OPTION_END = 255,
45 	OPTION_SUBNET_MASK = 1,
46 	OPTION_TIME_OFFSET = 2,
47 	OPTION_ROUTER_ADDRESS = 3,
48 	OPTION_DOMAIN_NAME_SERVER = 6,
49 	OPTION_HOST_NAME = 12,
50 	OPTION_DOMAIN_NAME = 15,
51 	OPTION_DATAGRAM_SIZE = 22,
52 	OPTION_MTU = 26,
53 	OPTION_BROADCAST_ADDRESS = 28,
54 	OPTION_NETWORK_TIME_SERVERS = 42,
55 	OPTION_NETBIOS_NAME_SERVER = 44,
56 	OPTION_NETBIOS_SCOPE = 47,
57 
58 	// DHCP specific options
59 	OPTION_REQUEST_IP_ADDRESS = 50,
60 	OPTION_ADDRESS_LEASE_TIME = 51,
61 	OPTION_OVERLOAD = 52,
62 	OPTION_MESSAGE_TYPE = 53,
63 	OPTION_SERVER_ADDRESS = 54,
64 	OPTION_REQUEST_PARAMETERS = 55,
65 	OPTION_ERROR_MESSAGE = 56,
66 	OPTION_MESSAGE_SIZE = 57,
67 	OPTION_RENEWAL_TIME = 58,
68 	OPTION_REBINDING_TIME = 59,
69 	OPTION_CLASS_IDENTIFIER = 60,
70 	OPTION_CLIENT_IDENTIFIER = 61,
71 };
72 
73 enum message_type {
74 	DHCP_NONE = 0,
75 	DHCP_DISCOVER,
76 	DHCP_OFFER,
77 	DHCP_REQUEST,
78 	DHCP_DECLINE,
79 	DHCP_ACK,
80 	DHCP_NACK,
81 	DHCP_RELEASE,
82 	DHCP_INFORM
83 };
84 
85 struct dhcp_option_cookie {
86 	dhcp_option_cookie()
87 		:
88 		state(0),
89 		file_has_options(false),
90 		server_name_has_options(false)
91 	{
92 	}
93 
94 	const uint8* next;
95 	uint8	state;
96 	bool	file_has_options;
97 	bool	server_name_has_options;
98 };
99 
100 struct dhcp_message {
101 	dhcp_message(message_type type);
102 
103 	uint8		opcode;
104 	uint8		hardware_type;
105 	uint8		hardware_address_length;
106 	uint8		hop_count;
107 	uint32		transaction_id;
108 	uint16		seconds_since_start;
109 	uint16		flags;
110 	in_addr_t	client_address;
111 	in_addr_t	your_address;
112 	in_addr_t	server_address;
113 	in_addr_t	gateway_address;
114 	uint8		mac_address[16];
115 	uint8		server_name[64];
116 	uint8		file[128];
117 	uint32		options_magic;
118 	uint8		options[1260];
119 
120 	size_t MinSize() const { return 576; }
121 	size_t Size() const;
122 
123 	bool HasOptions() const;
124 	bool NextOption(dhcp_option_cookie& cookie, message_option& option,
125 		const uint8*& data, size_t& size) const;
126 	message_type Type() const;
127 	const uint8* LastOption() const;
128 
129 	uint8* PrepareMessage(uint8 type);
130 	uint8* PutOption(uint8* options, message_option option);
131 	uint8* PutOption(uint8* options, message_option option, uint8 data);
132 	uint8* PutOption(uint8* options, message_option option, uint16 data);
133 	uint8* PutOption(uint8* options, message_option option, uint32 data);
134 	uint8* PutOption(uint8* options, message_option option, const uint8* data,
135 		uint32 size);
136 	uint8* FinishOptions(uint8* options);
137 } _PACKED;
138 
139 #define DHCP_FLAG_BROADCAST		0x8000
140 
141 #define ARP_HARDWARE_TYPE_ETHER	1
142 
143 const uint32 kMsgLeaseTime = 'lstm';
144 
145 static const uint8 kRequestParameters[] = {
146 	OPTION_SUBNET_MASK, OPTION_ROUTER_ADDRESS,
147 	OPTION_DOMAIN_NAME_SERVER, OPTION_BROADCAST_ADDRESS,
148 	OPTION_DOMAIN_NAME
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* options)
326 {
327 	return PutOption(options, 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 	fResolverConfiguration(kMsgConfigureResolver),
338 	fRunner(NULL),
339 	fLeaseTime(0)
340 {
341 	fStartTime = system_time();
342 	fTransactionID = (uint32)fStartTime;
343 
344 	fStatus = get_mac_address(device, fMAC);
345 	if (fStatus < B_OK)
346 		return;
347 
348 	memset(&fServer, 0, sizeof(struct sockaddr_in));
349 	fServer.sin_family = AF_INET;
350 	fServer.sin_len = sizeof(struct sockaddr_in);
351 	fServer.sin_port = htons(DHCP_SERVER_PORT);
352 
353 	openlog_thread("DHCP", 0, LOG_DAEMON);
354 }
355 
356 
357 DHCPClient::~DHCPClient()
358 {
359 	if (fStatus != B_OK)
360 		return;
361 
362 	delete fRunner;
363 
364 	int socket = ::socket(AF_INET, SOCK_DGRAM, 0);
365 	if (socket < 0)
366 		return;
367 
368 	// release lease
369 
370 	dhcp_message release(DHCP_RELEASE);
371 	_PrepareMessage(release, BOUND);
372 
373 	_SendMessage(socket, release, fServer);
374 	close(socket);
375 
376 	closelog_thread();
377 }
378 
379 
380 status_t
381 DHCPClient::Initialize()
382 {
383 	fStatus = _Negotiate(INIT);
384 	syslog(LOG_DEBUG, "DHCP for %s, status: %s\n", Device(), strerror(fStatus));
385 	return fStatus;
386 }
387 
388 
389 status_t
390 DHCPClient::_Negotiate(dhcp_state state)
391 {
392 	int socket = ::socket(AF_INET, SOCK_DGRAM, 0);
393 	if (socket < 0)
394 		return errno;
395 
396 	sockaddr_in local;
397 	memset(&local, 0, sizeof(struct sockaddr_in));
398 	local.sin_family = AF_INET;
399 	local.sin_len = sizeof(struct sockaddr_in);
400 	local.sin_port = htons(DHCP_CLIENT_PORT);
401 	local.sin_addr.s_addr = INADDR_ANY;
402 
403 	// Enable reusing the port . This is needed in case there is more
404 	// than 1 interface that needs to be configured. Note that the only reason
405 	// this works is because there is code below to bind to a specific
406 	// interface.
407 	int option = 1;
408 	setsockopt(socket, SOL_SOCKET, SO_REUSEPORT, &option, sizeof(option));
409 
410 	if (bind(socket, (struct sockaddr*)&local, sizeof(local)) < 0) {
411 		close(socket);
412 		return errno;
413 	}
414 
415 	sockaddr_in broadcast;
416 	memset(&broadcast, 0, sizeof(struct sockaddr_in));
417 	broadcast.sin_family = AF_INET;
418 	broadcast.sin_len = sizeof(struct sockaddr_in);
419 	broadcast.sin_port = htons(DHCP_SERVER_PORT);
420 	broadcast.sin_addr.s_addr = INADDR_BROADCAST;
421 
422 	option = 1;
423 	setsockopt(socket, SOL_SOCKET, SO_BROADCAST, &option, sizeof(option));
424 
425 	if (state == INIT) {
426 		// The local interface does not have an address yet, bind the socket
427 		// to the device directly.
428 		int linkSocket = ::socket(AF_LINK, SOCK_DGRAM, 0);
429 		if (linkSocket >= 0) {
430 			// we need to know the index of the device to be able to bind to it
431 			ifreq request;
432 			prepare_request(request, Device());
433 			if (ioctl(linkSocket, SIOCGIFINDEX, &request, sizeof(struct ifreq))
434 					== 0) {
435 				setsockopt(socket, SOL_SOCKET, SO_BINDTODEVICE,
436 					&request.ifr_index, sizeof(int));
437 			}
438 
439 			close(linkSocket);
440 		}
441 	}
442 
443 	bigtime_t previousLeaseTime = fLeaseTime;
444 	fLeaseTime = 0;
445 	fRenewalTime = 0;
446 	fRebindingTime = 0;
447 
448 	status_t status = B_ERROR;
449 	time_t timeout;
450 	uint32 tries;
451 	_ResetTimeout(socket, timeout, tries);
452 
453 	dhcp_message discover(DHCP_DISCOVER);
454 	_PrepareMessage(discover, state);
455 
456 	dhcp_message request(DHCP_REQUEST);
457 	_PrepareMessage(request, state);
458 
459 	// send discover/request message
460 	_SendMessage(socket, state == INIT ? discover : request,
461 		state != RENEWAL ? broadcast : fServer);
462 		// no need to check the status; in case of an error we'll just send
463 		// the message again
464 
465 	// receive loop until we've got an offer and acknowledged it
466 
467 	while (state != ACKNOWLEDGED) {
468 		char buffer[2048];
469 		ssize_t bytesReceived = recvfrom(socket, buffer, sizeof(buffer),
470 			0, NULL, NULL);
471 		if (bytesReceived < 0 && errno == B_TIMED_OUT) {
472 			// depending on the state, we'll just try again
473 			if (!_TimeoutShift(socket, timeout, tries)) {
474 				close(socket);
475 				return B_TIMED_OUT;
476 			}
477 
478 			if (state == INIT)
479 				_SendMessage(socket, discover, broadcast);
480 			else {
481 				_SendMessage(socket, request,
482 					state != RENEWAL ? broadcast : fServer);
483 			}
484 
485 			continue;
486 		} else if (bytesReceived < 0)
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 				fResolverConfiguration.MakeEmpty();
522 				_ParseOptions(*message, address, fResolverConfiguration);
523 
524 				fConfiguration.AddMessage("address", &address);
525 
526 				// request configuration from the server
527 
528 				_ResetTimeout(socket, timeout, tries);
529 				state = REQUESTING;
530 				_PrepareMessage(request, state);
531 
532 				status = _SendMessage(socket, request, broadcast);
533 					// we're sending a broadcast so that all potential offers
534 					// get an answer
535 				break;
536 			}
537 
538 			case DHCP_ACK:
539 			{
540 				if (state != REQUESTING && state != REBINDING
541 					&& state != RENEWAL)
542 					continue;
543 
544 				// TODO: we might want to configure the stuff, don't we?
545 				BMessage address;
546 				fResolverConfiguration.MakeEmpty();
547 				_ParseOptions(*message, address, fResolverConfiguration);
548 					// TODO: currently, only lease time and DNS is updated this way
549 
550 				// our address request has been acknowledged
551 				state = ACKNOWLEDGED;
552 
553 				// configure interface
554 				BMessage reply;
555 				status = Target().SendMessage(&fConfiguration, &reply);
556 				if (status == B_OK)
557 					status = reply.FindInt32("status", &fStatus);
558 
559 				// configure resolver
560 				reply.MakeEmpty();
561 				status = Target().SendMessage(&fResolverConfiguration, &reply);
562 				if (status == B_OK)
563 					status = reply.FindInt32("status", &fStatus);
564 				break;
565 			}
566 
567 			case DHCP_NACK:
568 				if (state != REQUESTING)
569 					continue;
570 
571 				// try again (maybe we should prefer other servers if this
572 				// happens more than once)
573 				status = _SendMessage(socket, discover, broadcast);
574 				if (status == B_OK)
575 					state = INIT;
576 				break;
577 		}
578 	}
579 
580 	close(socket);
581 
582 	if (status == B_OK && fLeaseTime > 0) {
583 		// notify early enough when the lease is
584 		if (fRenewalTime == 0)
585 			fRenewalTime = fLeaseTime * 2/3;
586 		if (fRebindingTime == 0)
587 			fRebindingTime = fLeaseTime * 5/6;
588 
589 		bigtime_t now = system_time();
590 		_RestartLease(fRenewalTime);
591 
592 		fLeaseTime += now;
593 		fRenewalTime += now;
594 		fRebindingTime += now;
595 			// make lease times absolute
596 	} else {
597 		fLeaseTime = previousLeaseTime;
598 		bigtime_t now = system_time();
599 		fRenewalTime = (fLeaseTime - now) * 2/3 + now;
600 		fRebindingTime = (fLeaseTime - now) * 5/6 + now;
601 	}
602 
603 	return status;
604 }
605 
606 
607 void
608 DHCPClient::_RestartLease(bigtime_t leaseTime)
609 {
610 	if (leaseTime == 0)
611 		return;
612 
613 	BMessage lease(kMsgLeaseTime);
614 	fRunner = new BMessageRunner(this, &lease, leaseTime, 1);
615 }
616 
617 
618 void
619 DHCPClient::_ParseOptions(dhcp_message& message, BMessage& address,
620 	BMessage& resolverConfiguration)
621 {
622 	dhcp_option_cookie cookie;
623 	message_option option;
624 	const uint8* data;
625 	size_t size;
626 	while (message.NextOption(cookie, option, data, size)) {
627 		// iterate through all options
628 		switch (option) {
629 			case OPTION_ROUTER_ADDRESS:
630 				address.AddString("gateway", _ToString(data));
631 				break;
632 			case OPTION_SUBNET_MASK:
633 				address.AddString("mask", _ToString(data));
634 				break;
635 			case OPTION_BROADCAST_ADDRESS:
636 				address.AddString("broadcast", _ToString(data));
637 				break;
638 			case OPTION_DOMAIN_NAME_SERVER:
639 			{
640 				for (uint32 i = 0; i < size / 4; i++) {
641 					syslog(LOG_INFO, "DNS: %s\n",
642 						_ToString(&data[i * 4]).String());
643 					resolverConfiguration.AddString("nameserver",
644 						_ToString(&data[i * 4]).String());
645 				}
646 				resolverConfiguration.AddInt32("nameserver_count",
647 					size / 4);
648 				break;
649 			}
650 			case OPTION_SERVER_ADDRESS:
651 				fServer.sin_addr.s_addr = *(in_addr_t*)data;
652 				break;
653 
654 			case OPTION_ADDRESS_LEASE_TIME:
655 				syslog(LOG_INFO, "lease time of %lu seconds\n",
656 					htonl(*(uint32*)data));
657 				fLeaseTime = htonl(*(uint32*)data) * 1000000LL;
658 				break;
659 			case OPTION_RENEWAL_TIME:
660 				syslog(LOG_INFO, "renewal time of %lu seconds\n",
661 					htonl(*(uint32*)data));
662 				fRenewalTime = htonl(*(uint32*)data) * 1000000LL;
663 				break;
664 			case OPTION_REBINDING_TIME:
665 				syslog(LOG_INFO, "rebinding time of %lu seconds\n",
666 					htonl(*(uint32*)data));
667 				fRebindingTime = htonl(*(uint32*)data) * 1000000LL;
668 				break;
669 
670 			case OPTION_HOST_NAME:
671 				syslog(LOG_INFO, "DHCP host name: \"%.*s\"\n", (int)size,
672 					(const char*)data);
673 				break;
674 
675 			case OPTION_DOMAIN_NAME:
676 			{
677 				char domain[256];
678 				strlcpy(domain, (const char*)data,
679 					min_c(size + 1, sizeof(domain)));
680 
681 				syslog(LOG_INFO, "DHCP domain name: \"%s\"\n", domain);
682 
683 				resolverConfiguration.AddString("domain", domain);
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 					kRequestParameters, sizeof(kRequestParameters));
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 				kRequestParameters, sizeof(kRequestParameters));
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