xref: /haiku/src/servers/net/DHCPClient.cpp (revision af0745618fc4fca86016d858e5ba9dc64dab16ea)
1fb81684fSAxel Dörfler /*
237a68cb1SAxel Dörfler  * Copyright 2006-2010, Haiku, Inc. All Rights Reserved.
3fb81684fSAxel Dörfler  * Distributed under the terms of the MIT License.
4fb81684fSAxel Dörfler  *
5fb81684fSAxel Dörfler  * Authors:
6fb81684fSAxel Dörfler  *		Axel Dörfler, axeld@pinc-software.de
70f87f52fSStephan Aßmus  *		Vegard Wærp, vegarwa@online.no
8fb81684fSAxel Dörfler  */
9fb81684fSAxel Dörfler 
10fb81684fSAxel Dörfler 
11fb81684fSAxel Dörfler #include "DHCPClient.h"
12fb81684fSAxel Dörfler #include "NetServer.h"
13fb81684fSAxel Dörfler 
14f9af6566SAxel Dörfler #include <Message.h>
156cc7630fSAxel Dörfler #include <MessageRunner.h>
16*af074561SAxel Dörfler #include <NetworkDevice.h>
17*af074561SAxel Dörfler #include <NetworkInterface.h>
18f9af6566SAxel Dörfler 
19f9af6566SAxel Dörfler #include <arpa/inet.h>
20fb81684fSAxel Dörfler #include <errno.h>
21fb81684fSAxel Dörfler #include <stdio.h>
22fb81684fSAxel Dörfler #include <string.h>
23293ed4feSAxel Dörfler #include <syslog.h>
2436bde12dSAxel Dörfler #include <sys/sockio.h>
25f9af6566SAxel Dörfler #include <sys/time.h>
26fb81684fSAxel Dörfler 
27fb81684fSAxel Dörfler 
28fb81684fSAxel Dörfler // See RFC 2131 for DHCP, see RFC 1533 for BOOTP/DHCP options
29fb81684fSAxel Dörfler 
30fb81684fSAxel Dörfler #define DHCP_CLIENT_PORT	68
31fb81684fSAxel Dörfler #define DHCP_SERVER_PORT	67
32fb81684fSAxel Dörfler 
33f9af6566SAxel Dörfler #define DEFAULT_TIMEOUT		2	// secs
34f9af6566SAxel Dörfler #define MAX_TIMEOUT			15	// secs
35f9af6566SAxel Dörfler 
36fb81684fSAxel Dörfler enum message_opcode {
37fb81684fSAxel Dörfler 	BOOT_REQUEST = 1,
38fb81684fSAxel Dörfler 	BOOT_REPLY
39fb81684fSAxel Dörfler };
40fb81684fSAxel Dörfler 
41fb81684fSAxel Dörfler enum message_option {
42fb81684fSAxel Dörfler 	OPTION_MAGIC = 0x63825363,
43fb81684fSAxel Dörfler 
44fb81684fSAxel Dörfler 	// generic options
45fb81684fSAxel Dörfler 	OPTION_PAD = 0,
46f9af6566SAxel Dörfler 	OPTION_END = 255,
47f9af6566SAxel Dörfler 	OPTION_SUBNET_MASK = 1,
4865186fecSAxel Dörfler 	OPTION_TIME_OFFSET = 2,
49f9af6566SAxel Dörfler 	OPTION_ROUTER_ADDRESS = 3,
50f9af6566SAxel Dörfler 	OPTION_DOMAIN_NAME_SERVER = 6,
51f9af6566SAxel Dörfler 	OPTION_HOST_NAME = 12,
525782c5a3SAxel Dörfler 	OPTION_DOMAIN_NAME = 15,
53fb81684fSAxel Dörfler 	OPTION_DATAGRAM_SIZE = 22,
54fb81684fSAxel Dörfler 	OPTION_MTU = 26,
55fb81684fSAxel Dörfler 	OPTION_BROADCAST_ADDRESS = 28,
56fb81684fSAxel Dörfler 	OPTION_NETWORK_TIME_SERVERS = 42,
5765186fecSAxel Dörfler 	OPTION_NETBIOS_NAME_SERVER = 44,
5865186fecSAxel Dörfler 	OPTION_NETBIOS_SCOPE = 47,
59fb81684fSAxel Dörfler 
60fb81684fSAxel Dörfler 	// DHCP specific options
61fb81684fSAxel Dörfler 	OPTION_REQUEST_IP_ADDRESS = 50,
62fb81684fSAxel Dörfler 	OPTION_ADDRESS_LEASE_TIME = 51,
63fb81684fSAxel Dörfler 	OPTION_OVERLOAD = 52,
64fb81684fSAxel Dörfler 	OPTION_MESSAGE_TYPE = 53,
65fb81684fSAxel Dörfler 	OPTION_SERVER_ADDRESS = 54,
66fb81684fSAxel Dörfler 	OPTION_REQUEST_PARAMETERS = 55,
67fb81684fSAxel Dörfler 	OPTION_ERROR_MESSAGE = 56,
68fb81684fSAxel Dörfler 	OPTION_MESSAGE_SIZE = 57,
69fb81684fSAxel Dörfler 	OPTION_RENEWAL_TIME = 58,
70fb81684fSAxel Dörfler 	OPTION_REBINDING_TIME = 59,
71fb81684fSAxel Dörfler 	OPTION_CLASS_IDENTIFIER = 60,
72fb81684fSAxel Dörfler 	OPTION_CLIENT_IDENTIFIER = 61,
73fb81684fSAxel Dörfler };
74fb81684fSAxel Dörfler 
75fb81684fSAxel Dörfler enum message_type {
76f9af6566SAxel Dörfler 	DHCP_NONE = 0,
77f9af6566SAxel Dörfler 	DHCP_DISCOVER,
78fb81684fSAxel Dörfler 	DHCP_OFFER,
79fb81684fSAxel Dörfler 	DHCP_REQUEST,
80fb81684fSAxel Dörfler 	DHCP_DECLINE,
81fb81684fSAxel Dörfler 	DHCP_ACK,
82f9af6566SAxel Dörfler 	DHCP_NACK,
83fb81684fSAxel Dörfler 	DHCP_RELEASE,
84fb81684fSAxel Dörfler 	DHCP_INFORM
85fb81684fSAxel Dörfler };
86fb81684fSAxel Dörfler 
87fb81684fSAxel Dörfler struct dhcp_option_cookie {
8815ab0bcfSAxel Dörfler 	dhcp_option_cookie()
8915ab0bcfSAxel Dörfler 		:
9015ab0bcfSAxel Dörfler 		state(0),
9115ab0bcfSAxel Dörfler 		file_has_options(false),
9215ab0bcfSAxel Dörfler 		server_name_has_options(false)
9315ab0bcfSAxel Dörfler 	{
9415ab0bcfSAxel Dörfler 	}
95fb81684fSAxel Dörfler 
96fb81684fSAxel Dörfler 	const uint8* next;
97fb81684fSAxel Dörfler 	uint8	state;
98fb81684fSAxel Dörfler 	bool	file_has_options;
99fb81684fSAxel Dörfler 	bool	server_name_has_options;
100fb81684fSAxel Dörfler };
101fb81684fSAxel Dörfler 
102fb81684fSAxel Dörfler struct dhcp_message {
103fb81684fSAxel Dörfler 	dhcp_message(message_type type);
104fb81684fSAxel Dörfler 
105fb81684fSAxel Dörfler 	uint8		opcode;
106fb81684fSAxel Dörfler 	uint8		hardware_type;
107fb81684fSAxel Dörfler 	uint8		hardware_address_length;
108fb81684fSAxel Dörfler 	uint8		hop_count;
109fb81684fSAxel Dörfler 	uint32		transaction_id;
11046ff5400SAxel Dörfler 	uint16		seconds_since_start;
111fb81684fSAxel Dörfler 	uint16		flags;
112fb81684fSAxel Dörfler 	in_addr_t	client_address;
113fb81684fSAxel Dörfler 	in_addr_t	your_address;
114fb81684fSAxel Dörfler 	in_addr_t	server_address;
115fb81684fSAxel Dörfler 	in_addr_t	gateway_address;
116fb81684fSAxel Dörfler 	uint8		mac_address[16];
117fb81684fSAxel Dörfler 	uint8		server_name[64];
118fb81684fSAxel Dörfler 	uint8		file[128];
119fb81684fSAxel Dörfler 	uint32		options_magic;
120fb81684fSAxel Dörfler 	uint8		options[1260];
121fb81684fSAxel Dörfler 
122fb81684fSAxel Dörfler 	size_t MinSize() const { return 576; }
123fb81684fSAxel Dörfler 	size_t Size() const;
124fb81684fSAxel Dörfler 
125fb81684fSAxel Dörfler 	bool HasOptions() const;
126fb81684fSAxel Dörfler 	bool NextOption(dhcp_option_cookie& cookie, message_option& option,
127fb81684fSAxel Dörfler 		const uint8*& data, size_t& size) const;
128f9af6566SAxel Dörfler 	message_type Type() const;
129fb81684fSAxel Dörfler 	const uint8* LastOption() const;
130fb81684fSAxel Dörfler 
131a073ba1aSHugo Santos 	uint8* PrepareMessage(uint8 type);
132fb81684fSAxel Dörfler 	uint8* PutOption(uint8* options, message_option option);
133fb81684fSAxel Dörfler 	uint8* PutOption(uint8* options, message_option option, uint8 data);
134fb81684fSAxel Dörfler 	uint8* PutOption(uint8* options, message_option option, uint16 data);
135fb81684fSAxel Dörfler 	uint8* PutOption(uint8* options, message_option option, uint32 data);
13615ab0bcfSAxel Dörfler 	uint8* PutOption(uint8* options, message_option option, const uint8* data,
13715ab0bcfSAxel Dörfler 		uint32 size);
13837a68cb1SAxel Dörfler 	uint8* FinishOptions(uint8* options);
139fb81684fSAxel Dörfler } _PACKED;
140fb81684fSAxel Dörfler 
1410cf5e6acSAxel Dörfler #define DHCP_FLAG_BROADCAST		0x8000
142fb81684fSAxel Dörfler 
143fb81684fSAxel Dörfler #define ARP_HARDWARE_TYPE_ETHER	1
144fb81684fSAxel Dörfler 
1456cc7630fSAxel Dörfler const uint32 kMsgLeaseTime = 'lstm';
1466cc7630fSAxel Dörfler 
14737a68cb1SAxel Dörfler static const uint8 kRequestParameters[] = {
14865186fecSAxel Dörfler 	OPTION_SUBNET_MASK, OPTION_ROUTER_ADDRESS,
149bfd479b3SAxel Dörfler 	OPTION_DOMAIN_NAME_SERVER, OPTION_BROADCAST_ADDRESS,
150bfd479b3SAxel Dörfler 	OPTION_DOMAIN_NAME
1515d4d5313SHugo Santos };
1525d4d5313SHugo Santos 
153fb81684fSAxel Dörfler 
154fb81684fSAxel Dörfler dhcp_message::dhcp_message(message_type type)
155fb81684fSAxel Dörfler {
156fb81684fSAxel Dörfler 	memset(this, 0, sizeof(*this));
157fb81684fSAxel Dörfler 	options_magic = htonl(OPTION_MAGIC);
158fb81684fSAxel Dörfler 
159a073ba1aSHugo Santos 	uint8* next = PrepareMessage(type);
160a073ba1aSHugo Santos 	FinishOptions(next);
161fb81684fSAxel Dörfler }
162fb81684fSAxel Dörfler 
163fb81684fSAxel Dörfler 
164fb81684fSAxel Dörfler bool
165fb81684fSAxel Dörfler dhcp_message::HasOptions() const
166fb81684fSAxel Dörfler {
167fb81684fSAxel Dörfler 	return options_magic == htonl(OPTION_MAGIC);
168fb81684fSAxel Dörfler }
169fb81684fSAxel Dörfler 
170fb81684fSAxel Dörfler 
171fb81684fSAxel Dörfler bool
172fb81684fSAxel Dörfler dhcp_message::NextOption(dhcp_option_cookie& cookie,
173fb81684fSAxel Dörfler 	message_option& option, const uint8*& data, size_t& size) const
174fb81684fSAxel Dörfler {
175fb81684fSAxel Dörfler 	if (cookie.state == 0) {
176fb81684fSAxel Dörfler 		if (!HasOptions())
177fb81684fSAxel Dörfler 			return false;
178fb81684fSAxel Dörfler 
179fb81684fSAxel Dörfler 		cookie.state++;
180fb81684fSAxel Dörfler 		cookie.next = options;
181fb81684fSAxel Dörfler 	}
182fb81684fSAxel Dörfler 
183fb81684fSAxel Dörfler 	uint32 bytesLeft = 0;
184fb81684fSAxel Dörfler 
185fb81684fSAxel Dörfler 	switch (cookie.state) {
186fb81684fSAxel Dörfler 		case 1:
187fb81684fSAxel Dörfler 			// options from "options"
188fb81684fSAxel Dörfler 			bytesLeft = sizeof(options) + cookie.next - options;
189fb81684fSAxel Dörfler 			break;
190fb81684fSAxel Dörfler 
191fb81684fSAxel Dörfler 		case 2:
192fb81684fSAxel Dörfler 			// options from "file"
193fb81684fSAxel Dörfler 			bytesLeft = sizeof(options) + cookie.next - options;
194fb81684fSAxel Dörfler 			break;
195fb81684fSAxel Dörfler 
196fb81684fSAxel Dörfler 		case 3:
197fb81684fSAxel Dörfler 			// options from "server_name"
198fb81684fSAxel Dörfler 			bytesLeft = sizeof(options) + cookie.next - options;
199fb81684fSAxel Dörfler 			break;
200fb81684fSAxel Dörfler 	}
201fb81684fSAxel Dörfler 
202fb81684fSAxel Dörfler 	while (true) {
203fb81684fSAxel Dörfler 		if (bytesLeft == 0) {
204fb81684fSAxel Dörfler 			// TODO: suppport OPTION_OVERLOAD!
205fb81684fSAxel Dörfler 			cookie.state = 4;
206fb81684fSAxel Dörfler 			return false;
207fb81684fSAxel Dörfler 		}
208fb81684fSAxel Dörfler 
209fb81684fSAxel Dörfler 		option = (message_option)cookie.next[0];
210fb81684fSAxel Dörfler 		if (option == OPTION_END) {
211fb81684fSAxel Dörfler 			cookie.state = 4;
212fb81684fSAxel Dörfler 			return false;
213fb81684fSAxel Dörfler 		} else if (option == OPTION_PAD) {
214fb81684fSAxel Dörfler 			bytesLeft--;
215fb81684fSAxel Dörfler 			cookie.next++;
216fb81684fSAxel Dörfler 			continue;
217fb81684fSAxel Dörfler 		}
218fb81684fSAxel Dörfler 
219fb81684fSAxel Dörfler 		size = cookie.next[1];
220fb81684fSAxel Dörfler 		data = &cookie.next[2];
221fb81684fSAxel Dörfler 		cookie.next += 2 + size;
222fb81684fSAxel Dörfler 
223fb81684fSAxel Dörfler 		if (option == OPTION_OVERLOAD) {
224fb81684fSAxel Dörfler 			cookie.file_has_options = data[0] & 1;
225fb81684fSAxel Dörfler 			cookie.server_name_has_options = data[0] & 2;
226fb81684fSAxel Dörfler 			continue;
227fb81684fSAxel Dörfler 		}
228fb81684fSAxel Dörfler 
229fb81684fSAxel Dörfler 		return true;
230fb81684fSAxel Dörfler 	}
231fb81684fSAxel Dörfler }
232fb81684fSAxel Dörfler 
233fb81684fSAxel Dörfler 
234f9af6566SAxel Dörfler message_type
235f9af6566SAxel Dörfler dhcp_message::Type() const
236f9af6566SAxel Dörfler {
237f9af6566SAxel Dörfler 	dhcp_option_cookie cookie;
238f9af6566SAxel Dörfler 	message_option option;
239f9af6566SAxel Dörfler 	const uint8* data;
240f9af6566SAxel Dörfler 	size_t size;
241f9af6566SAxel Dörfler 	while (NextOption(cookie, option, data, size)) {
242f9af6566SAxel Dörfler 		// iterate through all options
243f9af6566SAxel Dörfler 		if (option == OPTION_MESSAGE_TYPE)
244f9af6566SAxel Dörfler 			return (message_type)data[0];
245f9af6566SAxel Dörfler 	}
246f9af6566SAxel Dörfler 
247f9af6566SAxel Dörfler 	return DHCP_NONE;
248f9af6566SAxel Dörfler }
249f9af6566SAxel Dörfler 
250f9af6566SAxel Dörfler 
251fb81684fSAxel Dörfler const uint8*
252fb81684fSAxel Dörfler dhcp_message::LastOption() const
253fb81684fSAxel Dörfler {
254fb81684fSAxel Dörfler 	dhcp_option_cookie cookie;
255fb81684fSAxel Dörfler 	message_option option;
256fb81684fSAxel Dörfler 	const uint8* data;
257fb81684fSAxel Dörfler 	size_t size;
258fb81684fSAxel Dörfler 	while (NextOption(cookie, option, data, size)) {
259fb81684fSAxel Dörfler 		// iterate through all options
260fb81684fSAxel Dörfler 	}
261fb81684fSAxel Dörfler 
262fb81684fSAxel Dörfler 	return cookie.next;
263fb81684fSAxel Dörfler }
264fb81684fSAxel Dörfler 
265fb81684fSAxel Dörfler 
266fb81684fSAxel Dörfler size_t
267fb81684fSAxel Dörfler dhcp_message::Size() const
268fb81684fSAxel Dörfler {
269fb81684fSAxel Dörfler 	const uint8* last = LastOption();
270fb81684fSAxel Dörfler 	return sizeof(dhcp_message) - sizeof(options) + last + 1 - options;
271fb81684fSAxel Dörfler }
272fb81684fSAxel Dörfler 
273fb81684fSAxel Dörfler 
274fb81684fSAxel Dörfler uint8*
275a073ba1aSHugo Santos dhcp_message::PrepareMessage(uint8 type)
276a073ba1aSHugo Santos {
277a073ba1aSHugo Santos 	uint8* next = options;
278a073ba1aSHugo Santos 	next = PutOption(next, OPTION_MESSAGE_TYPE, type);
27915ab0bcfSAxel Dörfler 	next = PutOption(next, OPTION_MESSAGE_SIZE,
28015ab0bcfSAxel Dörfler 		(uint16)htons(sizeof(dhcp_message)));
281a073ba1aSHugo Santos 	return next;
282a073ba1aSHugo Santos }
283a073ba1aSHugo Santos 
28465186fecSAxel Dörfler 
285a073ba1aSHugo Santos uint8*
286fb81684fSAxel Dörfler dhcp_message::PutOption(uint8* options, message_option option)
287fb81684fSAxel Dörfler {
288fb81684fSAxel Dörfler 	options[0] = option;
289fb81684fSAxel Dörfler 	return options + 1;
290fb81684fSAxel Dörfler }
291fb81684fSAxel Dörfler 
292fb81684fSAxel Dörfler 
293fb81684fSAxel Dörfler uint8*
294fb81684fSAxel Dörfler dhcp_message::PutOption(uint8* options, message_option option, uint8 data)
295fb81684fSAxel Dörfler {
296fb81684fSAxel Dörfler 	return PutOption(options, option, &data, 1);
297fb81684fSAxel Dörfler }
298fb81684fSAxel Dörfler 
299fb81684fSAxel Dörfler 
300fb81684fSAxel Dörfler uint8*
301fb81684fSAxel Dörfler dhcp_message::PutOption(uint8* options, message_option option, uint16 data)
302fb81684fSAxel Dörfler {
303fb81684fSAxel Dörfler 	return PutOption(options, option, (uint8*)&data, sizeof(data));
304fb81684fSAxel Dörfler }
305fb81684fSAxel Dörfler 
306fb81684fSAxel Dörfler 
307fb81684fSAxel Dörfler uint8*
308fb81684fSAxel Dörfler dhcp_message::PutOption(uint8* options, message_option option, uint32 data)
309fb81684fSAxel Dörfler {
310fb81684fSAxel Dörfler 	return PutOption(options, option, (uint8*)&data, sizeof(data));
311fb81684fSAxel Dörfler }
312fb81684fSAxel Dörfler 
313fb81684fSAxel Dörfler 
314fb81684fSAxel Dörfler uint8*
31515ab0bcfSAxel Dörfler dhcp_message::PutOption(uint8* options, message_option option,
31615ab0bcfSAxel Dörfler 	const uint8* data, uint32 size)
317fb81684fSAxel Dörfler {
318fb81684fSAxel Dörfler 	options[0] = option;
319fb81684fSAxel Dörfler 	options[1] = size;
320fb81684fSAxel Dörfler 	memcpy(&options[2], data, size);
321fb81684fSAxel Dörfler 
322fb81684fSAxel Dörfler 	return options + 2 + size;
323fb81684fSAxel Dörfler }
324fb81684fSAxel Dörfler 
325fb81684fSAxel Dörfler 
326a073ba1aSHugo Santos uint8*
32737a68cb1SAxel Dörfler dhcp_message::FinishOptions(uint8* options)
328a073ba1aSHugo Santos {
32937a68cb1SAxel Dörfler 	return PutOption(options, OPTION_END);
330a073ba1aSHugo Santos }
331a073ba1aSHugo Santos 
332a073ba1aSHugo Santos 
333fb81684fSAxel Dörfler //	#pragma mark -
334fb81684fSAxel Dörfler 
335fb81684fSAxel Dörfler 
336f9af6566SAxel Dörfler DHCPClient::DHCPClient(BMessenger target, const char* device)
337*af074561SAxel Dörfler 	:
338*af074561SAxel Dörfler 	AutoconfigClient("dhcp", target, device),
3396cc7630fSAxel Dörfler 	fConfiguration(kMsgConfigureInterface),
34016e8f13aSAxel Dörfler 	fResolverConfiguration(kMsgConfigureResolver),
3416cc7630fSAxel Dörfler 	fRunner(NULL),
342*af074561SAxel Dörfler 	fServer(AF_INET, NULL, DHCP_SERVER_PORT),
343c1264353SStephan Aßmus 	fLeaseTime(0)
344fb81684fSAxel Dörfler {
34546ff5400SAxel Dörfler 	fStartTime = system_time();
34646ff5400SAxel Dörfler 	fTransactionID = (uint32)fStartTime;
347fb81684fSAxel Dörfler 
348*af074561SAxel Dörfler 	BNetworkAddress link;
349*af074561SAxel Dörfler 	BNetworkInterface interface(device);
350*af074561SAxel Dörfler 	fStatus = interface.GetHardwareAddress(link);
351*af074561SAxel Dörfler 	if (fStatus != B_OK)
352fb81684fSAxel Dörfler 		return;
353fb81684fSAxel Dörfler 
354*af074561SAxel Dörfler 	memcpy(fMAC, link.LinkLevelAddress(), sizeof(fMAC));
355293ed4feSAxel Dörfler 
356293ed4feSAxel Dörfler 	openlog_thread("DHCP", 0, LOG_DAEMON);
3576cc7630fSAxel Dörfler }
3586cc7630fSAxel Dörfler 
3596cc7630fSAxel Dörfler 
3606cc7630fSAxel Dörfler DHCPClient::~DHCPClient()
3616cc7630fSAxel Dörfler {
3626cc7630fSAxel Dörfler 	if (fStatus != B_OK)
3636cc7630fSAxel Dörfler 		return;
3646cc7630fSAxel Dörfler 
3656cc7630fSAxel Dörfler 	delete fRunner;
3660ce7725eSAxel Dörfler 
367fb81684fSAxel Dörfler 	int socket = ::socket(AF_INET, SOCK_DGRAM, 0);
3686cc7630fSAxel Dörfler 	if (socket < 0)
369fb81684fSAxel Dörfler 		return;
3706cc7630fSAxel Dörfler 
3716cc7630fSAxel Dörfler 	// release lease
3726cc7630fSAxel Dörfler 
3736cc7630fSAxel Dörfler 	dhcp_message release(DHCP_RELEASE);
37446ff5400SAxel Dörfler 	_PrepareMessage(release, BOUND);
3756cc7630fSAxel Dörfler 
3766cc7630fSAxel Dörfler 	_SendMessage(socket, release, fServer);
3776cc7630fSAxel Dörfler 	close(socket);
378293ed4feSAxel Dörfler 
379293ed4feSAxel Dörfler 	closelog_thread();
380fb81684fSAxel Dörfler }
381fb81684fSAxel Dörfler 
3826cc7630fSAxel Dörfler 
3836cc7630fSAxel Dörfler status_t
3846cc7630fSAxel Dörfler DHCPClient::Initialize()
3856cc7630fSAxel Dörfler {
3866cc7630fSAxel Dörfler 	fStatus = _Negotiate(INIT);
387293ed4feSAxel Dörfler 	syslog(LOG_DEBUG, "DHCP for %s, status: %s\n", Device(), strerror(fStatus));
3886cc7630fSAxel Dörfler 	return fStatus;
3896cc7630fSAxel Dörfler }
3906cc7630fSAxel Dörfler 
3916cc7630fSAxel Dörfler 
3926cc7630fSAxel Dörfler status_t
3936cc7630fSAxel Dörfler DHCPClient::_Negotiate(dhcp_state state)
3946cc7630fSAxel Dörfler {
3956cc7630fSAxel Dörfler 	int socket = ::socket(AF_INET, SOCK_DGRAM, 0);
3966cc7630fSAxel Dörfler 	if (socket < 0)
3976cc7630fSAxel Dörfler 		return errno;
3986cc7630fSAxel Dörfler 
399*af074561SAxel Dörfler 	BNetworkAddress local(AF_INET, NULL, DHCP_CLIENT_PORT);
400fb81684fSAxel Dörfler 
4012b829b04SBruno G. Albuquerque 	// Enable reusing the port . This is needed in case there is more
4022b829b04SBruno G. Albuquerque 	// than 1 interface that needs to be configured. Note that the only reason
4032b829b04SBruno G. Albuquerque 	// this works is because there is code below to bind to a specific
4042b829b04SBruno G. Albuquerque 	// interface.
4052b829b04SBruno G. Albuquerque 	int option = 1;
4062b829b04SBruno G. Albuquerque 	setsockopt(socket, SOL_SOCKET, SO_REUSEPORT, &option, sizeof(option));
4072b829b04SBruno G. Albuquerque 
408*af074561SAxel Dörfler 	if (bind(socket, local, local.Length()) < 0) {
409fb81684fSAxel Dörfler 		close(socket);
4106cc7630fSAxel Dörfler 		return errno;
411fb81684fSAxel Dörfler 	}
412fb81684fSAxel Dörfler 
413*af074561SAxel Dörfler 	BNetworkAddress broadcast;
414*af074561SAxel Dörfler 	broadcast.SetToBroadcast(AF_INET, DHCP_SERVER_PORT);
415fb81684fSAxel Dörfler 
4162b829b04SBruno G. Albuquerque 	option = 1;
417fb81684fSAxel Dörfler 	setsockopt(socket, SOL_SOCKET, SO_BROADCAST, &option, sizeof(option));
418fb81684fSAxel Dörfler 
41936bde12dSAxel Dörfler 	if (state == INIT) {
42036bde12dSAxel Dörfler 		// The local interface does not have an address yet, bind the socket
42136bde12dSAxel Dörfler 		// to the device directly.
422*af074561SAxel Dörfler 		BNetworkDevice device(Device());
423*af074561SAxel Dörfler 		int index = device.Index();
42436bde12dSAxel Dörfler 
425*af074561SAxel Dörfler 		setsockopt(socket, SOL_SOCKET, SO_BINDTODEVICE, &index, sizeof(int));
42636bde12dSAxel Dörfler 	}
42736bde12dSAxel Dörfler 
4286cc7630fSAxel Dörfler 	bigtime_t previousLeaseTime = fLeaseTime;
4296cc7630fSAxel Dörfler 	fLeaseTime = 0;
43046ff5400SAxel Dörfler 	fRenewalTime = 0;
43146ff5400SAxel Dörfler 	fRebindingTime = 0;
432fb81684fSAxel Dörfler 
4336cc7630fSAxel Dörfler 	status_t status = B_ERROR;
4346cc7630fSAxel Dörfler 	time_t timeout;
4356cc7630fSAxel Dörfler 	uint32 tries;
4366cc7630fSAxel Dörfler 	_ResetTimeout(socket, timeout, tries);
4376cc7630fSAxel Dörfler 
4386cc7630fSAxel Dörfler 	dhcp_message discover(DHCP_DISCOVER);
43946ff5400SAxel Dörfler 	_PrepareMessage(discover, state);
4406cc7630fSAxel Dörfler 
4416cc7630fSAxel Dörfler 	dhcp_message request(DHCP_REQUEST);
44246ff5400SAxel Dörfler 	_PrepareMessage(request, state);
4436cc7630fSAxel Dörfler 
44446ff5400SAxel Dörfler 	// send discover/request message
445593a0d2dSAxel Dörfler 	_SendMessage(socket, state == INIT ? discover : request,
44646ff5400SAxel Dörfler 		state != RENEWAL ? broadcast : fServer);
447593a0d2dSAxel Dörfler 		// no need to check the status; in case of an error we'll just send
448593a0d2dSAxel Dörfler 		// the message again
449fb81684fSAxel Dörfler 
450f9af6566SAxel Dörfler 	// receive loop until we've got an offer and acknowledged it
451f9af6566SAxel Dörfler 
452f9af6566SAxel Dörfler 	while (state != ACKNOWLEDGED) {
453f9af6566SAxel Dörfler 		char buffer[2048];
454f9af6566SAxel Dörfler 		ssize_t bytesReceived = recvfrom(socket, buffer, sizeof(buffer),
455f9af6566SAxel Dörfler 			0, NULL, NULL);
4564b661a95SAxel Dörfler 		if (bytesReceived < 0 && errno == B_TIMED_OUT) {
457f9af6566SAxel Dörfler 			// depending on the state, we'll just try again
4586cc7630fSAxel Dörfler 			if (!_TimeoutShift(socket, timeout, tries)) {
4596cc7630fSAxel Dörfler 				close(socket);
4606cc7630fSAxel Dörfler 				return B_TIMED_OUT;
461f9af6566SAxel Dörfler 			}
462f9af6566SAxel Dörfler 
463f9af6566SAxel Dörfler 			if (state == INIT)
464593a0d2dSAxel Dörfler 				_SendMessage(socket, discover, broadcast);
46515ab0bcfSAxel Dörfler 			else {
466593a0d2dSAxel Dörfler 				_SendMessage(socket, request,
467593a0d2dSAxel Dörfler 					state != RENEWAL ? broadcast : fServer);
46815ab0bcfSAxel Dörfler 			}
469f9af6566SAxel Dörfler 
470d77fe260SAxel Dörfler 			continue;
471593a0d2dSAxel Dörfler 		} else if (bytesReceived < 0)
472f9af6566SAxel Dörfler 			break;
473f9af6566SAxel Dörfler 
474f9af6566SAxel Dörfler 		dhcp_message* message = (dhcp_message*)buffer;
475f9af6566SAxel Dörfler 		if (message->transaction_id != htonl(fTransactionID)
476f9af6566SAxel Dörfler 			|| !message->HasOptions()
477f9af6566SAxel Dörfler 			|| memcmp(message->mac_address, discover.mac_address,
478f9af6566SAxel Dörfler 				discover.hardware_address_length)) {
479f9af6566SAxel Dörfler 			// this message is not for us
480f9af6566SAxel Dörfler 			continue;
481f9af6566SAxel Dörfler 		}
482f9af6566SAxel Dörfler 
483f9af6566SAxel Dörfler 		switch (message->Type()) {
484f9af6566SAxel Dörfler 			case DHCP_NONE:
485f9af6566SAxel Dörfler 			default:
486f9af6566SAxel Dörfler 				// ignore this message
487f9af6566SAxel Dörfler 				break;
488f9af6566SAxel Dörfler 
489f9af6566SAxel Dörfler 			case DHCP_OFFER:
490f9af6566SAxel Dörfler 			{
49161729d93SAxel Dörfler 				syslog(LOG_DEBUG, "DHCP received offer for %s\n", Device());
49261729d93SAxel Dörfler 
493f9af6566SAxel Dörfler 				// first offer wins
494f9af6566SAxel Dörfler 				if (state != INIT)
495f9af6566SAxel Dörfler 					break;
496f9af6566SAxel Dörfler 
497f9af6566SAxel Dörfler 				// collect interface options
498f9af6566SAxel Dörfler 
499f9af6566SAxel Dörfler 				fAssignedAddress = message->your_address;
500f9af6566SAxel Dörfler 
50110cc12daSAxel Dörfler 				fConfiguration.MakeEmpty();
502293ed4feSAxel Dörfler 				fConfiguration.AddString("device", Device());
503f01106c3SAxel Dörfler 				fConfiguration.AddBool("auto", true);
504f9af6566SAxel Dörfler 
505f9af6566SAxel Dörfler 				BMessage address;
506f9af6566SAxel Dörfler 				address.AddString("family", "inet");
507f9af6566SAxel Dörfler 				address.AddString("address", _ToString(fAssignedAddress));
50816e8f13aSAxel Dörfler 				fResolverConfiguration.MakeEmpty();
50916e8f13aSAxel Dörfler 				_ParseOptions(*message, address, fResolverConfiguration);
510f9af6566SAxel Dörfler 
51110cc12daSAxel Dörfler 				fConfiguration.AddMessage("address", &address);
512f9af6566SAxel Dörfler 
51310cc12daSAxel Dörfler 				// request configuration from the server
514f9af6566SAxel Dörfler 
5156cc7630fSAxel Dörfler 				_ResetTimeout(socket, timeout, tries);
516f9af6566SAxel Dörfler 				state = REQUESTING;
51746ff5400SAxel Dörfler 				_PrepareMessage(request, state);
518f9af6566SAxel Dörfler 
5196cc7630fSAxel Dörfler 				status = _SendMessage(socket, request, broadcast);
52015ab0bcfSAxel Dörfler 					// we're sending a broadcast so that all potential offers
52115ab0bcfSAxel Dörfler 					// get an answer
52210cc12daSAxel Dörfler 				break;
523f9af6566SAxel Dörfler 			}
524f9af6566SAxel Dörfler 
525f9af6566SAxel Dörfler 			case DHCP_ACK:
52610cc12daSAxel Dörfler 			{
52761729d93SAxel Dörfler 				syslog(LOG_DEBUG, "DHCP received ack for %s\n", Device());
52815ab0bcfSAxel Dörfler 				if (state != REQUESTING && state != REBINDING
52915ab0bcfSAxel Dörfler 					&& state != RENEWAL)
530f9af6566SAxel Dörfler 					continue;
531f9af6566SAxel Dörfler 
5326cc7630fSAxel Dörfler 				// TODO: we might want to configure the stuff, don't we?
5336cc7630fSAxel Dörfler 				BMessage address;
53416e8f13aSAxel Dörfler 				fResolverConfiguration.MakeEmpty();
53516e8f13aSAxel Dörfler 				_ParseOptions(*message, address, fResolverConfiguration);
5366cc7630fSAxel Dörfler 					// TODO: currently, only lease time and DNS is updated this way
5376cc7630fSAxel Dörfler 
538f9af6566SAxel Dörfler 				// our address request has been acknowledged
539f9af6566SAxel Dörfler 				state = ACKNOWLEDGED;
54010cc12daSAxel Dörfler 
54110cc12daSAxel Dörfler 				// configure interface
54210cc12daSAxel Dörfler 				BMessage reply;
543d23fdd96SOliver Tappe 				status = Target().SendMessage(&fConfiguration, &reply);
544d23fdd96SOliver Tappe 				if (status == B_OK)
545d23fdd96SOliver Tappe 					status = reply.FindInt32("status", &fStatus);
54616e8f13aSAxel Dörfler 
54716e8f13aSAxel Dörfler 				// configure resolver
54816e8f13aSAxel Dörfler 				reply.MakeEmpty();
54916e8f13aSAxel Dörfler 				status = Target().SendMessage(&fResolverConfiguration, &reply);
55016e8f13aSAxel Dörfler 				if (status == B_OK)
55116e8f13aSAxel Dörfler 					status = reply.FindInt32("status", &fStatus);
552f9af6566SAxel Dörfler 				break;
55310cc12daSAxel Dörfler 			}
554f9af6566SAxel Dörfler 
555f9af6566SAxel Dörfler 			case DHCP_NACK:
55661729d93SAxel Dörfler 				syslog(LOG_DEBUG, "DHCP received nack for %s\n", Device());
55761729d93SAxel Dörfler 
558f9af6566SAxel Dörfler 				if (state != REQUESTING)
559f9af6566SAxel Dörfler 					continue;
560f9af6566SAxel Dörfler 
56115ab0bcfSAxel Dörfler 				// try again (maybe we should prefer other servers if this
56215ab0bcfSAxel Dörfler 				// happens more than once)
5636cc7630fSAxel Dörfler 				status = _SendMessage(socket, discover, broadcast);
5646cc7630fSAxel Dörfler 				if (status == B_OK)
565f9af6566SAxel Dörfler 					state = INIT;
566f9af6566SAxel Dörfler 				break;
567f9af6566SAxel Dörfler 		}
568f9af6566SAxel Dörfler 	}
569f9af6566SAxel Dörfler 
570fb81684fSAxel Dörfler 	close(socket);
5716cc7630fSAxel Dörfler 
5726cc7630fSAxel Dörfler 	if (status == B_OK && fLeaseTime > 0) {
5736cc7630fSAxel Dörfler 		// notify early enough when the lease is
57446ff5400SAxel Dörfler 		if (fRenewalTime == 0)
57546ff5400SAxel Dörfler 			fRenewalTime = fLeaseTime * 2/3;
57646ff5400SAxel Dörfler 		if (fRebindingTime == 0)
57746ff5400SAxel Dörfler 			fRebindingTime = fLeaseTime * 5/6;
57846ff5400SAxel Dörfler 
579168cc3dfSRene Gollent 		bigtime_t now = system_time();
58046ff5400SAxel Dörfler 		_RestartLease(fRenewalTime);
58146ff5400SAxel Dörfler 
58246ff5400SAxel Dörfler 		fLeaseTime += now;
58346ff5400SAxel Dörfler 		fRenewalTime += now;
58446ff5400SAxel Dörfler 		fRebindingTime += now;
58546ff5400SAxel Dörfler 			// make lease times absolute
58646ff5400SAxel Dörfler 	} else {
5876cc7630fSAxel Dörfler 		fLeaseTime = previousLeaseTime;
58846ff5400SAxel Dörfler 		bigtime_t now = system_time();
58946ff5400SAxel Dörfler 		fRenewalTime = (fLeaseTime - now) * 2/3 + now;
59046ff5400SAxel Dörfler 		fRebindingTime = (fLeaseTime - now) * 5/6 + now;
59146ff5400SAxel Dörfler 	}
5926cc7630fSAxel Dörfler 
5936cc7630fSAxel Dörfler 	return status;
594fb81684fSAxel Dörfler }
595fb81684fSAxel Dörfler 
596fb81684fSAxel Dörfler 
5976cc7630fSAxel Dörfler void
5986cc7630fSAxel Dörfler DHCPClient::_RestartLease(bigtime_t leaseTime)
599fb81684fSAxel Dörfler {
6006cc7630fSAxel Dörfler 	if (leaseTime == 0)
601f9af6566SAxel Dörfler 		return;
602f9af6566SAxel Dörfler 
6036cc7630fSAxel Dörfler 	BMessage lease(kMsgLeaseTime);
60446ff5400SAxel Dörfler 	fRunner = new BMessageRunner(this, &lease, leaseTime, 1);
605f9af6566SAxel Dörfler }
606f9af6566SAxel Dörfler 
607f9af6566SAxel Dörfler 
608f9af6566SAxel Dörfler void
60916e8f13aSAxel Dörfler DHCPClient::_ParseOptions(dhcp_message& message, BMessage& address,
61016e8f13aSAxel Dörfler 	BMessage& resolverConfiguration)
6110ce7725eSAxel Dörfler {
6120ce7725eSAxel Dörfler 	dhcp_option_cookie cookie;
6130ce7725eSAxel Dörfler 	message_option option;
6140ce7725eSAxel Dörfler 	const uint8* data;
6150ce7725eSAxel Dörfler 	size_t size;
6160ce7725eSAxel Dörfler 	while (message.NextOption(cookie, option, data, size)) {
6170ce7725eSAxel Dörfler 		// iterate through all options
6180ce7725eSAxel Dörfler 		switch (option) {
6190ce7725eSAxel Dörfler 			case OPTION_ROUTER_ADDRESS:
6200ce7725eSAxel Dörfler 				address.AddString("gateway", _ToString(data));
6210ce7725eSAxel Dörfler 				break;
6220ce7725eSAxel Dörfler 			case OPTION_SUBNET_MASK:
6230ce7725eSAxel Dörfler 				address.AddString("mask", _ToString(data));
6240ce7725eSAxel Dörfler 				break;
6255782c5a3SAxel Dörfler 			case OPTION_BROADCAST_ADDRESS:
6265782c5a3SAxel Dörfler 				address.AddString("broadcast", _ToString(data));
6275782c5a3SAxel Dörfler 				break;
6280ce7725eSAxel Dörfler 			case OPTION_DOMAIN_NAME_SERVER:
629a552ec13SAxel Dörfler 			{
6300ce7725eSAxel Dörfler 				for (uint32 i = 0; i < size / 4; i++) {
63161729d93SAxel Dörfler 					syslog(LOG_INFO, "DHCP for %s got DNS: %s\n", Device(),
632293ed4feSAxel Dörfler 						_ToString(&data[i * 4]).String());
63316e8f13aSAxel Dörfler 					resolverConfiguration.AddString("nameserver",
63415ab0bcfSAxel Dörfler 						_ToString(&data[i * 4]).String());
63515ab0bcfSAxel Dörfler 				}
63616e8f13aSAxel Dörfler 				resolverConfiguration.AddInt32("nameserver_count",
63716e8f13aSAxel Dörfler 					size / 4);
6380ce7725eSAxel Dörfler 				break;
639a552ec13SAxel Dörfler 			}
6400ce7725eSAxel Dörfler 			case OPTION_SERVER_ADDRESS:
641*af074561SAxel Dörfler 				fServer.SetAddress(*(in_addr_t*)data);
6420ce7725eSAxel Dörfler 				break;
64346ff5400SAxel Dörfler 
6440ce7725eSAxel Dörfler 			case OPTION_ADDRESS_LEASE_TIME:
645293ed4feSAxel Dörfler 				syslog(LOG_INFO, "lease time of %lu seconds\n",
646293ed4feSAxel Dörfler 					htonl(*(uint32*)data));
6476cc7630fSAxel Dörfler 				fLeaseTime = htonl(*(uint32*)data) * 1000000LL;
6480ce7725eSAxel Dörfler 				break;
6491a4e8e7bSAxel Dörfler 			case OPTION_RENEWAL_TIME:
650293ed4feSAxel Dörfler 				syslog(LOG_INFO, "renewal time of %lu seconds\n",
65146ff5400SAxel Dörfler 					htonl(*(uint32*)data));
65246ff5400SAxel Dörfler 				fRenewalTime = htonl(*(uint32*)data) * 1000000LL;
65346ff5400SAxel Dörfler 				break;
6541a4e8e7bSAxel Dörfler 			case OPTION_REBINDING_TIME:
655293ed4feSAxel Dörfler 				syslog(LOG_INFO, "rebinding time of %lu seconds\n",
65646ff5400SAxel Dörfler 					htonl(*(uint32*)data));
65746ff5400SAxel Dörfler 				fRebindingTime = htonl(*(uint32*)data) * 1000000LL;
6581a4e8e7bSAxel Dörfler 				break;
6591a4e8e7bSAxel Dörfler 
6600ce7725eSAxel Dörfler 			case OPTION_HOST_NAME:
66116e8f13aSAxel Dörfler 				syslog(LOG_INFO, "DHCP host name: \"%.*s\"\n", (int)size,
66216e8f13aSAxel Dörfler 					(const char*)data);
6630ce7725eSAxel Dörfler 				break;
6645782c5a3SAxel Dörfler 
6655782c5a3SAxel Dörfler 			case OPTION_DOMAIN_NAME:
6665782c5a3SAxel Dörfler 			{
66716e8f13aSAxel Dörfler 				char domain[256];
66816e8f13aSAxel Dörfler 				strlcpy(domain, (const char*)data,
66916e8f13aSAxel Dörfler 					min_c(size + 1, sizeof(domain)));
6700f87f52fSStephan Aßmus 
67116e8f13aSAxel Dörfler 				syslog(LOG_INFO, "DHCP domain name: \"%s\"\n", domain);
6720f87f52fSStephan Aßmus 
67316e8f13aSAxel Dörfler 				resolverConfiguration.AddString("domain", domain);
6745782c5a3SAxel Dörfler 				break;
6755782c5a3SAxel Dörfler 			}
6760ce7725eSAxel Dörfler 
6770ce7725eSAxel Dörfler 			case OPTION_MESSAGE_TYPE:
6780ce7725eSAxel Dörfler 				break;
6790ce7725eSAxel Dörfler 
6800ce7725eSAxel Dörfler 			default:
681293ed4feSAxel Dörfler 				syslog(LOG_INFO, "unknown option %lu\n", (uint32)option);
6820ce7725eSAxel Dörfler 				break;
6830ce7725eSAxel Dörfler 		}
6840ce7725eSAxel Dörfler 	}
6850ce7725eSAxel Dörfler }
6860ce7725eSAxel Dörfler 
6870ce7725eSAxel Dörfler 
6880ce7725eSAxel Dörfler void
68946ff5400SAxel Dörfler DHCPClient::_PrepareMessage(dhcp_message& message, dhcp_state state)
6900ce7725eSAxel Dörfler {
6910ce7725eSAxel Dörfler 	message.opcode = BOOT_REQUEST;
6920ce7725eSAxel Dörfler 	message.hardware_type = ARP_HARDWARE_TYPE_ETHER;
6930ce7725eSAxel Dörfler 	message.hardware_address_length = 6;
6940ce7725eSAxel Dörfler 	message.transaction_id = htonl(fTransactionID);
695bcc8dadaSAxel Dörfler 	message.seconds_since_start = htons(min_c((system_time() - fStartTime)
69615ab0bcfSAxel Dörfler 		/ 1000000LL, 65535));
6970ce7725eSAxel Dörfler 	memcpy(message.mac_address, fMAC, 6);
6980ce7725eSAxel Dörfler 
69946ff5400SAxel Dörfler 	message_type type = message.Type();
70046ff5400SAxel Dörfler 
70146ff5400SAxel Dörfler 	switch (type) {
7020ce7725eSAxel Dörfler 		case DHCP_REQUEST:
7030ce7725eSAxel Dörfler 		case DHCP_RELEASE:
7040ce7725eSAxel Dörfler 		{
7050ce7725eSAxel Dörfler 			// add server identifier option
706*af074561SAxel Dörfler 			const sockaddr_in& server = (sockaddr_in&)fServer.SockAddr();
707a073ba1aSHugo Santos 			uint8* next = message.PrepareMessage(type);
7080ce7725eSAxel Dörfler 			next = message.PutOption(next, OPTION_SERVER_ADDRESS,
709*af074561SAxel Dörfler 				(uint32)server.sin_addr.s_addr);
71046ff5400SAxel Dörfler 
71146ff5400SAxel Dörfler 			// In RENEWAL or REBINDING state, we must set the client_address field, and not
71246ff5400SAxel Dörfler 			// use OPTION_REQUEST_IP_ADDRESS for DHCP_REQUEST messages
7135d4d5313SHugo Santos 			if (type == DHCP_REQUEST && (state == INIT || state == REQUESTING)) {
71415ab0bcfSAxel Dörfler 				next = message.PutOption(next, OPTION_REQUEST_IP_ADDRESS,
71515ab0bcfSAxel Dörfler 					(uint32)fAssignedAddress);
7165d4d5313SHugo Santos 				next = message.PutOption(next, OPTION_REQUEST_PARAMETERS,
71737a68cb1SAxel Dörfler 					kRequestParameters, sizeof(kRequestParameters));
7185d4d5313SHugo Santos 			} else
71946ff5400SAxel Dörfler 				message.client_address = fAssignedAddress;
72046ff5400SAxel Dörfler 
721a073ba1aSHugo Santos 			message.FinishOptions(next);
722a073ba1aSHugo Santos 			break;
723a073ba1aSHugo Santos 		}
724a073ba1aSHugo Santos 
725a073ba1aSHugo Santos 		case DHCP_DISCOVER:
726a073ba1aSHugo Santos 		{
727a073ba1aSHugo Santos 			uint8* next = message.PrepareMessage(type);
728a073ba1aSHugo Santos 			next = message.PutOption(next, OPTION_REQUEST_PARAMETERS,
72937a68cb1SAxel Dörfler 				kRequestParameters, sizeof(kRequestParameters));
730a073ba1aSHugo Santos 			message.FinishOptions(next);
7310ce7725eSAxel Dörfler 			break;
7320ce7725eSAxel Dörfler 		}
7330ce7725eSAxel Dörfler 
7340ce7725eSAxel Dörfler 		default:
7350ce7725eSAxel Dörfler 			// the default options are fine
7360ce7725eSAxel Dörfler 			break;
7370ce7725eSAxel Dörfler 	}
7380ce7725eSAxel Dörfler }
7390ce7725eSAxel Dörfler 
7400ce7725eSAxel Dörfler 
7410ce7725eSAxel Dörfler void
7426cc7630fSAxel Dörfler DHCPClient::_ResetTimeout(int socket, time_t& timeout, uint32& tries)
743f9af6566SAxel Dörfler {
7446cc7630fSAxel Dörfler 	timeout = DEFAULT_TIMEOUT;
7456cc7630fSAxel Dörfler 	tries = 0;
746f9af6566SAxel Dörfler 
747f9af6566SAxel Dörfler 	struct timeval value;
7486cc7630fSAxel Dörfler 	value.tv_sec = timeout;
749f9af6566SAxel Dörfler 	value.tv_usec = 0;
750f9af6566SAxel Dörfler 	setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &value, sizeof(value));
751f9af6566SAxel Dörfler }
752f9af6566SAxel Dörfler 
753f9af6566SAxel Dörfler 
754f9af6566SAxel Dörfler bool
7556cc7630fSAxel Dörfler DHCPClient::_TimeoutShift(int socket, time_t& timeout, uint32& tries)
756f9af6566SAxel Dörfler {
7576cc7630fSAxel Dörfler 	timeout += timeout;
7586cc7630fSAxel Dörfler 	if (timeout > MAX_TIMEOUT) {
7596cc7630fSAxel Dörfler 		timeout = DEFAULT_TIMEOUT;
760f9af6566SAxel Dörfler 
7616cc7630fSAxel Dörfler 		if (++tries > 2)
762f9af6566SAxel Dörfler 			return false;
763f9af6566SAxel Dörfler 	}
76461729d93SAxel Dörfler 	syslog(LOG_DEBUG, "DHCP timeout shift for %s: %lu secs (try %lu)\n",
76561729d93SAxel Dörfler 		Device(), timeout, tries);
766f9af6566SAxel Dörfler 
767f9af6566SAxel Dörfler 	struct timeval value;
7686cc7630fSAxel Dörfler 	value.tv_sec = timeout;
769f9af6566SAxel Dörfler 	value.tv_usec = 0;
770f9af6566SAxel Dörfler 	setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &value, sizeof(value));
771f9af6566SAxel Dörfler 
772f9af6566SAxel Dörfler 	return true;
773f9af6566SAxel Dörfler }
774f9af6566SAxel Dörfler 
775f9af6566SAxel Dörfler 
776f9af6566SAxel Dörfler BString
777f9af6566SAxel Dörfler DHCPClient::_ToString(const uint8* data) const
778f9af6566SAxel Dörfler {
779f9af6566SAxel Dörfler 	BString target = inet_ntoa(*(in_addr*)data);
780f9af6566SAxel Dörfler 	return target;
781f9af6566SAxel Dörfler }
782f9af6566SAxel Dörfler 
783f9af6566SAxel Dörfler 
784f9af6566SAxel Dörfler BString
785f9af6566SAxel Dörfler DHCPClient::_ToString(in_addr_t address) const
786f9af6566SAxel Dörfler {
787f9af6566SAxel Dörfler 	BString target = inet_ntoa(*(in_addr*)&address);
788f9af6566SAxel Dörfler 	return target;
789f9af6566SAxel Dörfler }
790f9af6566SAxel Dörfler 
791f9af6566SAxel Dörfler 
792f9af6566SAxel Dörfler status_t
7931a905a76SAxel Dörfler DHCPClient::_SendMessage(int socket, dhcp_message& message,
794*af074561SAxel Dörfler 	const BNetworkAddress& address) const
795f9af6566SAxel Dörfler {
79661729d93SAxel Dörfler 	syslog(LOG_DEBUG, "DHCP send message %u for %s\n", message.Type(),
79761729d93SAxel Dörfler 		Device());
79861729d93SAxel Dörfler 
799f9af6566SAxel Dörfler 	ssize_t bytesSent = sendto(socket, &message, message.Size(),
800*af074561SAxel Dörfler 		address.IsBroadcast() ? MSG_BCAST : 0, address, address.Length());
801f9af6566SAxel Dörfler 	if (bytesSent < 0)
802f9af6566SAxel Dörfler 		return errno;
803f9af6566SAxel Dörfler 
804f9af6566SAxel Dörfler 	return B_OK;
805fb81684fSAxel Dörfler }
806fb81684fSAxel Dörfler 
807fb81684fSAxel Dörfler 
80846ff5400SAxel Dörfler dhcp_state
80946ff5400SAxel Dörfler DHCPClient::_CurrentState() const
81046ff5400SAxel Dörfler {
81146ff5400SAxel Dörfler 	bigtime_t now = system_time();
81246ff5400SAxel Dörfler 
81346ff5400SAxel Dörfler 	if (now > fLeaseTime || fStatus < B_OK)
81446ff5400SAxel Dörfler 		return INIT;
81546ff5400SAxel Dörfler 	if (now >= fRebindingTime)
81646ff5400SAxel Dörfler 		return REBINDING;
81746ff5400SAxel Dörfler 	if (now >= fRenewalTime)
81846ff5400SAxel Dörfler 		return RENEWAL;
81946ff5400SAxel Dörfler 
82046ff5400SAxel Dörfler 	return BOUND;
82146ff5400SAxel Dörfler }
82246ff5400SAxel Dörfler 
82346ff5400SAxel Dörfler 
824fb81684fSAxel Dörfler void
825fb81684fSAxel Dörfler DHCPClient::MessageReceived(BMessage* message)
826fb81684fSAxel Dörfler {
8276cc7630fSAxel Dörfler 	switch (message->what) {
8286cc7630fSAxel Dörfler 		case kMsgLeaseTime:
82946ff5400SAxel Dörfler 		{
83046ff5400SAxel Dörfler 			dhcp_state state = _CurrentState();
83146ff5400SAxel Dörfler 
83246ff5400SAxel Dörfler 			bigtime_t next;
83346ff5400SAxel Dörfler 			if (_Negotiate(state) == B_OK) {
83446ff5400SAxel Dörfler 				switch (state) {
83546ff5400SAxel Dörfler 					case RENEWAL:
83646ff5400SAxel Dörfler 						next = fRebindingTime;
8376cc7630fSAxel Dörfler 						break;
83846ff5400SAxel Dörfler 					case REBINDING:
83946ff5400SAxel Dörfler 					default:
84046ff5400SAxel Dörfler 						next = fRenewalTime;
84146ff5400SAxel Dörfler 						break;
84246ff5400SAxel Dörfler 				}
84346ff5400SAxel Dörfler 			} else {
84446ff5400SAxel Dörfler 				switch (state) {
84546ff5400SAxel Dörfler 					case RENEWAL:
84646ff5400SAxel Dörfler 						next = (fLeaseTime - fRebindingTime) / 4 + system_time();
84746ff5400SAxel Dörfler 						break;
84846ff5400SAxel Dörfler 					case REBINDING:
84946ff5400SAxel Dörfler 					default:
85046ff5400SAxel Dörfler 						next = (fLeaseTime - fRenewalTime) / 4 + system_time();
85146ff5400SAxel Dörfler 						break;
85246ff5400SAxel Dörfler 				}
85346ff5400SAxel Dörfler 			}
85446ff5400SAxel Dörfler 
85546ff5400SAxel Dörfler 			_RestartLease(next - system_time());
85646ff5400SAxel Dörfler 			break;
85746ff5400SAxel Dörfler 		}
8586cc7630fSAxel Dörfler 
8596cc7630fSAxel Dörfler 		default:
860fb81684fSAxel Dörfler 			BHandler::MessageReceived(message);
8616cc7630fSAxel Dörfler 			break;
8626cc7630fSAxel Dörfler 	}
863fb81684fSAxel Dörfler }
864