xref: /haiku/src/servers/net/DHCPClient.cpp (revision f9ab315e0243a142c9728e357dd82f048e7a4f0c)
1fb81684fSAxel Dörfler /*
2669afb50SAxel Dörfler  * Copyright 2006-2011, 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
8d5d2fdf0SPhilippe Houdoin  *		Philippe Houdoin, <phoudoin at haiku-os dot org>
9fb81684fSAxel Dörfler  */
10fb81684fSAxel Dörfler 
11fb81684fSAxel Dörfler 
12fb81684fSAxel Dörfler #include "DHCPClient.h"
13fb81684fSAxel Dörfler 
14f9af6566SAxel Dörfler #include <Message.h>
156cc7630fSAxel Dörfler #include <MessageRunner.h>
16af074561SAxel Dörfler #include <NetworkDevice.h>
17af074561SAxel Dörfler #include <NetworkInterface.h>
18f9af6566SAxel Dörfler 
196972b91eSPawel Dziepak #include <algorithm>
20f9af6566SAxel Dörfler #include <arpa/inet.h>
21fb81684fSAxel Dörfler #include <errno.h>
22fb81684fSAxel Dörfler #include <stdio.h>
23fb81684fSAxel Dörfler #include <string.h>
24d09c1f8eSPhilippe Houdoin #include <stdlib.h>
25293ed4feSAxel Dörfler #include <syslog.h>
2636bde12dSAxel Dörfler #include <sys/sockio.h>
27f9af6566SAxel Dörfler #include <sys/time.h>
285f25cf08SSiarzhuk Zharski #include <unistd.h>
29fb81684fSAxel Dörfler 
304ac66051SAxel Dörfler #include <Debug.h>
314ac66051SAxel Dörfler #include <Message.h>
324ac66051SAxel Dörfler #include <MessageRunner.h>
334ac66051SAxel Dörfler 
344ac66051SAxel Dörfler #include "NetServer.h"
354ac66051SAxel Dörfler 
36fb81684fSAxel Dörfler 
37fb81684fSAxel Dörfler // See RFC 2131 for DHCP, see RFC 1533 for BOOTP/DHCP options
38fb81684fSAxel Dörfler 
39fb81684fSAxel Dörfler #define DHCP_CLIENT_PORT	68
40fb81684fSAxel Dörfler #define DHCP_SERVER_PORT	67
41fb81684fSAxel Dörfler 
426972b91eSPawel Dziepak #define DEFAULT_TIMEOUT		4	// secs
436972b91eSPawel Dziepak #define MAX_TIMEOUT			64	// secs
446972b91eSPawel Dziepak 
456972b91eSPawel Dziepak #define MAX_RETRIES			5
46f9af6566SAxel Dörfler 
47fb81684fSAxel Dörfler enum message_opcode {
48fb81684fSAxel Dörfler 	BOOT_REQUEST = 1,
49fb81684fSAxel Dörfler 	BOOT_REPLY
50fb81684fSAxel Dörfler };
51fb81684fSAxel Dörfler 
52fb81684fSAxel Dörfler enum message_option {
53fb81684fSAxel Dörfler 	OPTION_MAGIC = 0x63825363,
54fb81684fSAxel Dörfler 
55fb81684fSAxel Dörfler 	// generic options
56fb81684fSAxel Dörfler 	OPTION_PAD = 0,
57f9af6566SAxel Dörfler 	OPTION_END = 255,
58f9af6566SAxel Dörfler 	OPTION_SUBNET_MASK = 1,
5965186fecSAxel Dörfler 	OPTION_TIME_OFFSET = 2,
60f9af6566SAxel Dörfler 	OPTION_ROUTER_ADDRESS = 3,
61f9af6566SAxel Dörfler 	OPTION_DOMAIN_NAME_SERVER = 6,
62f9af6566SAxel Dörfler 	OPTION_HOST_NAME = 12,
635782c5a3SAxel Dörfler 	OPTION_DOMAIN_NAME = 15,
64d5d2fdf0SPhilippe Houdoin 	OPTION_MAX_DATAGRAM_SIZE = 22,
65d5d2fdf0SPhilippe Houdoin 	OPTION_INTERFACE_MTU = 26,
66fb81684fSAxel Dörfler 	OPTION_BROADCAST_ADDRESS = 28,
67d5d2fdf0SPhilippe Houdoin 	OPTION_NETWORK_TIME_PROTOCOL_SERVERS = 42,
6865186fecSAxel Dörfler 	OPTION_NETBIOS_NAME_SERVER = 44,
6965186fecSAxel Dörfler 	OPTION_NETBIOS_SCOPE = 47,
70fb81684fSAxel Dörfler 
71fb81684fSAxel Dörfler 	// DHCP specific options
72fb81684fSAxel Dörfler 	OPTION_REQUEST_IP_ADDRESS = 50,
73fb81684fSAxel Dörfler 	OPTION_ADDRESS_LEASE_TIME = 51,
74fb81684fSAxel Dörfler 	OPTION_OVERLOAD = 52,
75fb81684fSAxel Dörfler 	OPTION_MESSAGE_TYPE = 53,
76cefe2a40SPhilippe Houdoin 	OPTION_SERVER_ADDRESS = 54,
77fb81684fSAxel Dörfler 	OPTION_REQUEST_PARAMETERS = 55,
78fb81684fSAxel Dörfler 	OPTION_ERROR_MESSAGE = 56,
79d5d2fdf0SPhilippe Houdoin 	OPTION_MAX_MESSAGE_SIZE = 57,
80fb81684fSAxel Dörfler 	OPTION_RENEWAL_TIME = 58,
81fb81684fSAxel Dörfler 	OPTION_REBINDING_TIME = 59,
82fb81684fSAxel Dörfler 	OPTION_CLASS_IDENTIFIER = 60,
83fb81684fSAxel Dörfler 	OPTION_CLIENT_IDENTIFIER = 61,
84fb81684fSAxel Dörfler };
85fb81684fSAxel Dörfler 
86fb81684fSAxel Dörfler enum message_type {
87f9af6566SAxel Dörfler 	DHCP_NONE = 0,
88d6f83df4SAugustin Cavalier 	DHCP_DISCOVER = 1,
89d6f83df4SAugustin Cavalier 	DHCP_OFFER = 2,
90d6f83df4SAugustin Cavalier 	DHCP_REQUEST = 3,
91d6f83df4SAugustin Cavalier 	DHCP_DECLINE = 4,
92d6f83df4SAugustin Cavalier 	DHCP_ACK = 5,
93d6f83df4SAugustin Cavalier 	DHCP_NACK = 6,
94d6f83df4SAugustin Cavalier 	DHCP_RELEASE = 7,
95d6f83df4SAugustin Cavalier 	DHCP_INFORM = 8
96fb81684fSAxel Dörfler };
97fb81684fSAxel Dörfler 
98fb81684fSAxel Dörfler struct dhcp_option_cookie {
9915ab0bcfSAxel Dörfler 	dhcp_option_cookie()
10015ab0bcfSAxel Dörfler 		:
10115ab0bcfSAxel Dörfler 		state(0),
10215ab0bcfSAxel Dörfler 		file_has_options(false),
10315ab0bcfSAxel Dörfler 		server_name_has_options(false)
10415ab0bcfSAxel Dörfler 	{
10515ab0bcfSAxel Dörfler 	}
106fb81684fSAxel Dörfler 
107fb81684fSAxel Dörfler 	const uint8* next;
108fb81684fSAxel Dörfler 	uint8	state;
109fb81684fSAxel Dörfler 	bool	file_has_options;
110fb81684fSAxel Dörfler 	bool	server_name_has_options;
111fb81684fSAxel Dörfler };
112fb81684fSAxel Dörfler 
113fb81684fSAxel Dörfler struct dhcp_message {
114fb81684fSAxel Dörfler 	dhcp_message(message_type type);
115fb81684fSAxel Dörfler 
116fb81684fSAxel Dörfler 	uint8		opcode;
117fb81684fSAxel Dörfler 	uint8		hardware_type;
118fb81684fSAxel Dörfler 	uint8		hardware_address_length;
119fb81684fSAxel Dörfler 	uint8		hop_count;
120fb81684fSAxel Dörfler 	uint32		transaction_id;
12146ff5400SAxel Dörfler 	uint16		seconds_since_start;
122fb81684fSAxel Dörfler 	uint16		flags;
123fb81684fSAxel Dörfler 	in_addr_t	client_address;
124fb81684fSAxel Dörfler 	in_addr_t	your_address;
125fb81684fSAxel Dörfler 	in_addr_t	server_address;
126fb81684fSAxel Dörfler 	in_addr_t	gateway_address;
127fb81684fSAxel Dörfler 	uint8		mac_address[16];
128fb81684fSAxel Dörfler 	uint8		server_name[64];
129fb81684fSAxel Dörfler 	uint8		file[128];
130fb81684fSAxel Dörfler 	uint32		options_magic;
131fb81684fSAxel Dörfler 	uint8		options[1260];
132fb81684fSAxel Dörfler 
133fb81684fSAxel Dörfler 	size_t MinSize() const { return 576; }
134fb81684fSAxel Dörfler 	size_t Size() const;
135fb81684fSAxel Dörfler 
136fb81684fSAxel Dörfler 	bool HasOptions() const;
137fb81684fSAxel Dörfler 	bool NextOption(dhcp_option_cookie& cookie, message_option& option,
138fb81684fSAxel Dörfler 		const uint8*& data, size_t& size) const;
13903f6ab7fSPhilippe Houdoin 	const uint8* FindOption(message_option which) const;
140fb81684fSAxel Dörfler 	const uint8* LastOption() const;
14103f6ab7fSPhilippe Houdoin 	message_type Type() const;
142fb81684fSAxel Dörfler 
143a073ba1aSHugo Santos 	uint8* PrepareMessage(uint8 type);
144fb81684fSAxel Dörfler 	uint8* PutOption(uint8* options, message_option option);
145fb81684fSAxel Dörfler 	uint8* PutOption(uint8* options, message_option option, uint8 data);
146fb81684fSAxel Dörfler 	uint8* PutOption(uint8* options, message_option option, uint16 data);
147fb81684fSAxel Dörfler 	uint8* PutOption(uint8* options, message_option option, uint32 data);
14815ab0bcfSAxel Dörfler 	uint8* PutOption(uint8* options, message_option option, const uint8* data,
14915ab0bcfSAxel Dörfler 		uint32 size);
15037a68cb1SAxel Dörfler 	uint8* FinishOptions(uint8* options);
1514ac66051SAxel Dörfler 
1524ac66051SAxel Dörfler 	static const char* TypeToString(message_type type);
153fb81684fSAxel Dörfler } _PACKED;
154fb81684fSAxel Dörfler 
1550cf5e6acSAxel Dörfler #define DHCP_FLAG_BROADCAST		0x8000
156fb81684fSAxel Dörfler 
157fb81684fSAxel Dörfler #define ARP_HARDWARE_TYPE_ETHER	1
158fb81684fSAxel Dörfler 
1596cc7630fSAxel Dörfler const uint32 kMsgLeaseTime = 'lstm';
1606cc7630fSAxel Dörfler 
16137a68cb1SAxel Dörfler static const uint8 kRequestParameters[] = {
16265186fecSAxel Dörfler 	OPTION_SUBNET_MASK, OPTION_ROUTER_ADDRESS,
163bfd479b3SAxel Dörfler 	OPTION_DOMAIN_NAME_SERVER, OPTION_BROADCAST_ADDRESS,
164bfd479b3SAxel Dörfler 	OPTION_DOMAIN_NAME
1655d4d5313SHugo Santos };
1665d4d5313SHugo Santos 
167fb81684fSAxel Dörfler 
168fb81684fSAxel Dörfler dhcp_message::dhcp_message(message_type type)
169fb81684fSAxel Dörfler {
170fb4527d2SPhilippe Houdoin 	// ASSERT(this == offsetof(this, opcode));
171fb81684fSAxel Dörfler 	memset(this, 0, sizeof(*this));
172fb81684fSAxel Dörfler 	options_magic = htonl(OPTION_MAGIC);
173fb81684fSAxel Dörfler 
174a073ba1aSHugo Santos 	uint8* next = PrepareMessage(type);
175a073ba1aSHugo Santos 	FinishOptions(next);
176fb81684fSAxel Dörfler }
177fb81684fSAxel Dörfler 
178fb81684fSAxel Dörfler 
179fb81684fSAxel Dörfler bool
180fb81684fSAxel Dörfler dhcp_message::HasOptions() const
181fb81684fSAxel Dörfler {
182fb81684fSAxel Dörfler 	return options_magic == htonl(OPTION_MAGIC);
183fb81684fSAxel Dörfler }
184fb81684fSAxel Dörfler 
185fb81684fSAxel Dörfler 
186fb81684fSAxel Dörfler bool
187fb81684fSAxel Dörfler dhcp_message::NextOption(dhcp_option_cookie& cookie,
188fb81684fSAxel Dörfler 	message_option& option, const uint8*& data, size_t& size) const
189fb81684fSAxel Dörfler {
190fb81684fSAxel Dörfler 	if (!HasOptions())
191fb81684fSAxel Dörfler 		return false;
192fb81684fSAxel Dörfler 
193fb4527d2SPhilippe Houdoin 	if (cookie.state == 0) {
194fb81684fSAxel Dörfler 		cookie.state++;
195fb81684fSAxel Dörfler 		cookie.next = options;
196fb81684fSAxel Dörfler 	}
197fb81684fSAxel Dörfler 
198fb81684fSAxel Dörfler 	uint32 bytesLeft = 0;
199fb81684fSAxel Dörfler 
200fb81684fSAxel Dörfler 	switch (cookie.state) {
201fb81684fSAxel Dörfler 		case 1:
202ce36f054SPhilippe Houdoin 			// options from "options"
203fb4527d2SPhilippe Houdoin 			bytesLeft = sizeof(options) - (cookie.next - options);
204fb81684fSAxel Dörfler 			break;
205fb81684fSAxel Dörfler 
206fb81684fSAxel Dörfler 		case 2:
207fb81684fSAxel Dörfler 			// options from "file"
208fb4527d2SPhilippe Houdoin 			bytesLeft = sizeof(file) - (cookie.next - file);
209fb81684fSAxel Dörfler 			break;
210fb81684fSAxel Dörfler 
211fb81684fSAxel Dörfler 		case 3:
212fb81684fSAxel Dörfler 			// options from "server_name"
213fb4527d2SPhilippe Houdoin 			bytesLeft = sizeof(server_name) - (cookie.next - server_name);
214fb81684fSAxel Dörfler 			break;
215fb81684fSAxel Dörfler 	}
216fb81684fSAxel Dörfler 
217fb81684fSAxel Dörfler 	while (true) {
218fb81684fSAxel Dörfler 		if (bytesLeft == 0) {
219fb4527d2SPhilippe Houdoin 			cookie.state++;
220fb4527d2SPhilippe Houdoin 
221ce36f054SPhilippe Houdoin 			// handle option overload in file and/or server_name fields.
222fb4527d2SPhilippe Houdoin 			switch (cookie.state) {
223fb4527d2SPhilippe Houdoin 				case 2:
224d5d2fdf0SPhilippe Houdoin 					// options from "file" field
225fb4527d2SPhilippe Houdoin 					if (cookie.file_has_options) {
226fb4527d2SPhilippe Houdoin 						bytesLeft = sizeof(file);
227fb4527d2SPhilippe Houdoin 						cookie.next = file;
228fb4527d2SPhilippe Houdoin 					}
229fb4527d2SPhilippe Houdoin 					break;
230fb4527d2SPhilippe Houdoin 
231fb4527d2SPhilippe Houdoin 				case 3:
232d5d2fdf0SPhilippe Houdoin 					// options from "server_name" field
233fb4527d2SPhilippe Houdoin 					if (cookie.server_name_has_options) {
234fb4527d2SPhilippe Houdoin 						bytesLeft = sizeof(server_name);
235fb4527d2SPhilippe Houdoin 						cookie.next = server_name;
236fb4527d2SPhilippe Houdoin 					}
237fb4527d2SPhilippe Houdoin 					break;
238fb4527d2SPhilippe Houdoin 
239d5d2fdf0SPhilippe Houdoin 				default:
240ce36f054SPhilippe Houdoin 					// no more place to look for options
241d5d2fdf0SPhilippe Houdoin 					// if last option is not OPTION_END,
242d5d2fdf0SPhilippe Houdoin 					// there is no space left for other option!
243d5d2fdf0SPhilippe Houdoin 					if (option != OPTION_END)
244d5d2fdf0SPhilippe Houdoin 						cookie.next = NULL;
245fb81684fSAxel Dörfler 					return false;
246fb81684fSAxel Dörfler 			}
247fb81684fSAxel Dörfler 
248fb4527d2SPhilippe Houdoin 			if (bytesLeft == 0) {
249d5d2fdf0SPhilippe Houdoin 				// no options in this state, try next one
250fb4527d2SPhilippe Houdoin 				continue;
251fb4527d2SPhilippe Houdoin 			}
252fb4527d2SPhilippe Houdoin 		}
253fb4527d2SPhilippe Houdoin 
254fb81684fSAxel Dörfler 		option = (message_option)cookie.next[0];
255fb81684fSAxel Dörfler 		if (option == OPTION_END) {
256fb4527d2SPhilippe Houdoin 			bytesLeft = 0;
257fb4527d2SPhilippe Houdoin 			continue;
258fb81684fSAxel Dörfler 		} else if (option == OPTION_PAD) {
259fb81684fSAxel Dörfler 			bytesLeft--;
260fb81684fSAxel Dörfler 			cookie.next++;
261fb81684fSAxel Dörfler 			continue;
262fb81684fSAxel Dörfler 		}
263fb81684fSAxel Dörfler 
264fb81684fSAxel Dörfler 		size = cookie.next[1];
265fb81684fSAxel Dörfler 		data = &cookie.next[2];
266fb81684fSAxel Dörfler 		cookie.next += 2 + size;
267fb4527d2SPhilippe Houdoin 		bytesLeft -= 2 + size;
268fb81684fSAxel Dörfler 
269fb81684fSAxel Dörfler 		if (option == OPTION_OVERLOAD) {
270fb81684fSAxel Dörfler 			cookie.file_has_options = data[0] & 1;
271fb81684fSAxel Dörfler 			cookie.server_name_has_options = data[0] & 2;
272fb81684fSAxel Dörfler 			continue;
273fb81684fSAxel Dörfler 		}
274fb81684fSAxel Dörfler 
275fb81684fSAxel Dörfler 		return true;
276fb81684fSAxel Dörfler 	}
277fb81684fSAxel Dörfler }
278fb81684fSAxel Dörfler 
279fb81684fSAxel Dörfler 
28003f6ab7fSPhilippe Houdoin const uint8*
28103f6ab7fSPhilippe Houdoin dhcp_message::FindOption(message_option which) const
282f9af6566SAxel Dörfler {
283f9af6566SAxel Dörfler 	dhcp_option_cookie cookie;
284f9af6566SAxel Dörfler 	message_option option;
285f9af6566SAxel Dörfler 	const uint8* data;
286f9af6566SAxel Dörfler 	size_t size;
287f9af6566SAxel Dörfler 	while (NextOption(cookie, option, data, size)) {
288f9af6566SAxel Dörfler 		// iterate through all options
28903f6ab7fSPhilippe Houdoin 		if (option == which)
29003f6ab7fSPhilippe Houdoin 			return data;
291f9af6566SAxel Dörfler 	}
292f9af6566SAxel Dörfler 
29303f6ab7fSPhilippe Houdoin 	return NULL;
294f9af6566SAxel Dörfler }
295f9af6566SAxel Dörfler 
296f9af6566SAxel Dörfler 
297fb81684fSAxel Dörfler const uint8*
298fb81684fSAxel Dörfler dhcp_message::LastOption() const
299fb81684fSAxel Dörfler {
300fb81684fSAxel Dörfler 	dhcp_option_cookie cookie;
301fb81684fSAxel Dörfler 	message_option option;
302fb81684fSAxel Dörfler 	const uint8* data;
303fb81684fSAxel Dörfler 	size_t size;
304fb81684fSAxel Dörfler 	while (NextOption(cookie, option, data, size)) {
305fb81684fSAxel Dörfler 		// iterate through all options
306fb81684fSAxel Dörfler 	}
307fb81684fSAxel Dörfler 
308fb81684fSAxel Dörfler 	return cookie.next;
309fb81684fSAxel Dörfler }
310fb81684fSAxel Dörfler 
311fb81684fSAxel Dörfler 
31203f6ab7fSPhilippe Houdoin message_type
31303f6ab7fSPhilippe Houdoin dhcp_message::Type() const
31403f6ab7fSPhilippe Houdoin {
31503f6ab7fSPhilippe Houdoin 	const uint8* data = FindOption(OPTION_MESSAGE_TYPE);
31603f6ab7fSPhilippe Houdoin 	if (data)
31703f6ab7fSPhilippe Houdoin 		return (message_type)data[0];
31803f6ab7fSPhilippe Houdoin 
31903f6ab7fSPhilippe Houdoin 	return DHCP_NONE;
32003f6ab7fSPhilippe Houdoin }
32103f6ab7fSPhilippe Houdoin 
32203f6ab7fSPhilippe Houdoin 
323fb81684fSAxel Dörfler size_t
324fb81684fSAxel Dörfler dhcp_message::Size() const
325fb81684fSAxel Dörfler {
326fb81684fSAxel Dörfler 	const uint8* last = LastOption();
327d5d2fdf0SPhilippe Houdoin 
328d5d2fdf0SPhilippe Houdoin 	if (last < options) {
329d5d2fdf0SPhilippe Houdoin 		// if last option is stored above "options" field, it means
330d5d2fdf0SPhilippe Houdoin 		// the whole options field and message is already filled...
331d5d2fdf0SPhilippe Houdoin 		return sizeof(dhcp_message);
332d5d2fdf0SPhilippe Houdoin 	}
333d5d2fdf0SPhilippe Houdoin 
334fb81684fSAxel Dörfler 	return sizeof(dhcp_message) - sizeof(options) + last + 1 - options;
335fb81684fSAxel Dörfler }
336fb81684fSAxel Dörfler 
337fb81684fSAxel Dörfler 
338fb81684fSAxel Dörfler uint8*
339a073ba1aSHugo Santos dhcp_message::PrepareMessage(uint8 type)
340a073ba1aSHugo Santos {
341a073ba1aSHugo Santos 	uint8* next = options;
342a073ba1aSHugo Santos 	next = PutOption(next, OPTION_MESSAGE_TYPE, type);
343d5d2fdf0SPhilippe Houdoin 	next = PutOption(next, OPTION_MAX_MESSAGE_SIZE,
34415ab0bcfSAxel Dörfler 		(uint16)htons(sizeof(dhcp_message)));
345a073ba1aSHugo Santos 	return next;
346a073ba1aSHugo Santos }
347a073ba1aSHugo Santos 
34865186fecSAxel Dörfler 
349a073ba1aSHugo Santos uint8*
350fb81684fSAxel Dörfler dhcp_message::PutOption(uint8* options, message_option option)
351fb81684fSAxel Dörfler {
352fb81684fSAxel Dörfler 	options[0] = option;
353fb81684fSAxel Dörfler 	return options + 1;
354fb81684fSAxel Dörfler }
355fb81684fSAxel Dörfler 
356fb81684fSAxel Dörfler 
357fb81684fSAxel Dörfler uint8*
358fb81684fSAxel Dörfler dhcp_message::PutOption(uint8* options, message_option option, uint8 data)
359fb81684fSAxel Dörfler {
360fb81684fSAxel Dörfler 	return PutOption(options, option, &data, 1);
361fb81684fSAxel Dörfler }
362fb81684fSAxel Dörfler 
363fb81684fSAxel Dörfler 
364fb81684fSAxel Dörfler uint8*
365fb81684fSAxel Dörfler dhcp_message::PutOption(uint8* options, message_option option, uint16 data)
366fb81684fSAxel Dörfler {
367fb81684fSAxel Dörfler 	return PutOption(options, option, (uint8*)&data, sizeof(data));
368fb81684fSAxel Dörfler }
369fb81684fSAxel Dörfler 
370fb81684fSAxel Dörfler 
371fb81684fSAxel Dörfler uint8*
372fb81684fSAxel Dörfler dhcp_message::PutOption(uint8* options, message_option option, uint32 data)
373fb81684fSAxel Dörfler {
374fb81684fSAxel Dörfler 	return PutOption(options, option, (uint8*)&data, sizeof(data));
375fb81684fSAxel Dörfler }
376fb81684fSAxel Dörfler 
377fb81684fSAxel Dörfler 
378fb81684fSAxel Dörfler uint8*
37915ab0bcfSAxel Dörfler dhcp_message::PutOption(uint8* options, message_option option,
38015ab0bcfSAxel Dörfler 	const uint8* data, uint32 size)
381fb81684fSAxel Dörfler {
382d5d2fdf0SPhilippe Houdoin 	// TODO: check enough space is available
383d5d2fdf0SPhilippe Houdoin 
384fb81684fSAxel Dörfler 	options[0] = option;
385fb81684fSAxel Dörfler 	options[1] = size;
386fb81684fSAxel Dörfler 	memcpy(&options[2], data, size);
387fb81684fSAxel Dörfler 
388fb81684fSAxel Dörfler 	return options + 2 + size;
389fb81684fSAxel Dörfler }
390fb81684fSAxel Dörfler 
391fb81684fSAxel Dörfler 
392a073ba1aSHugo Santos uint8*
39337a68cb1SAxel Dörfler dhcp_message::FinishOptions(uint8* options)
394a073ba1aSHugo Santos {
39537a68cb1SAxel Dörfler 	return PutOption(options, OPTION_END);
396a073ba1aSHugo Santos }
397a073ba1aSHugo Santos 
398a073ba1aSHugo Santos 
3994ac66051SAxel Dörfler /*static*/ const char*
4004ac66051SAxel Dörfler dhcp_message::TypeToString(message_type type)
4014ac66051SAxel Dörfler {
4024ac66051SAxel Dörfler 	switch (type) {
4034ac66051SAxel Dörfler #define CASE(x) case x: return #x;
4044ac66051SAxel Dörfler 		CASE(DHCP_NONE)
4054ac66051SAxel Dörfler 		CASE(DHCP_DISCOVER)
4064ac66051SAxel Dörfler 		CASE(DHCP_OFFER)
4074ac66051SAxel Dörfler 		CASE(DHCP_REQUEST)
4084ac66051SAxel Dörfler 		CASE(DHCP_DECLINE)
4094ac66051SAxel Dörfler 		CASE(DHCP_ACK)
4104ac66051SAxel Dörfler 		CASE(DHCP_NACK)
4114ac66051SAxel Dörfler 		CASE(DHCP_RELEASE)
4124ac66051SAxel Dörfler 		CASE(DHCP_INFORM)
4134ac66051SAxel Dörfler #undef CASE
4144ac66051SAxel Dörfler 	}
4154ac66051SAxel Dörfler 
4164ac66051SAxel Dörfler 	return "<unknown>";
4174ac66051SAxel Dörfler }
4184ac66051SAxel Dörfler 
4194ac66051SAxel Dörfler 
420fb81684fSAxel Dörfler //	#pragma mark -
421fb81684fSAxel Dörfler 
422fb81684fSAxel Dörfler 
423f9af6566SAxel Dörfler DHCPClient::DHCPClient(BMessenger target, const char* device)
424af074561SAxel Dörfler 	:
425af074561SAxel Dörfler 	AutoconfigClient("dhcp", target, device),
4266cc7630fSAxel Dörfler 	fConfiguration(kMsgConfigureInterface),
42716e8f13aSAxel Dörfler 	fResolverConfiguration(kMsgConfigureResolver),
4286cc7630fSAxel Dörfler 	fRunner(NULL),
4290892800fSPhilippe Houdoin 	fAssignedAddress(0),
430af074561SAxel Dörfler 	fServer(AF_INET, NULL, DHCP_SERVER_PORT),
431c1264353SStephan Aßmus 	fLeaseTime(0)
432fb81684fSAxel Dörfler {
4336972b91eSPawel Dziepak 	fTransactionID = (uint32)system_time() ^ rand();
4347ca2da76SFredrik Holmqvist 
435af074561SAxel Dörfler 	BNetworkAddress link;
436af074561SAxel Dörfler 	BNetworkInterface interface(device);
437af074561SAxel Dörfler 	fStatus = interface.GetHardwareAddress(link);
438af074561SAxel Dörfler 	if (fStatus != B_OK)
439fb81684fSAxel Dörfler 		return;
440fb81684fSAxel Dörfler 
441af074561SAxel Dörfler 	memcpy(fMAC, link.LinkLevelAddress(), sizeof(fMAC));
442293ed4feSAxel Dörfler 
4430892800fSPhilippe Houdoin 	if ((interface.Flags() & IFF_AUTO_CONFIGURED) != 0) {
4440892800fSPhilippe Houdoin 		// Check for interface previous auto-configured address, if any.
4450892800fSPhilippe Houdoin 		BNetworkInterfaceAddress interfaceAddress;
4460892800fSPhilippe Houdoin 		int index = interface.FindFirstAddress(AF_INET);
4470892800fSPhilippe Houdoin 		if (index >= 0
4480892800fSPhilippe Houdoin 			&& interface.GetAddressAt(index, interfaceAddress) == B_OK) {
4490892800fSPhilippe Houdoin 			BNetworkAddress address = interfaceAddress.Address();
4500892800fSPhilippe Houdoin 			const sockaddr_in& addr = (sockaddr_in&)address.SockAddr();
4510892800fSPhilippe Houdoin 			fAssignedAddress = addr.sin_addr.s_addr;
452b6ba1daaSPhilippe Houdoin 
453b6ba1daaSPhilippe Houdoin 			if ((ntohl(fAssignedAddress) & IN_CLASSB_NET) == 0xa9fe0000) {
454b6ba1daaSPhilippe Houdoin 				// previous auto-configured address is a link-local one:
455b6ba1daaSPhilippe Houdoin 				// there is no point asking a DHCP server to renew such
456b6ba1daaSPhilippe Houdoin 				// server-less assigned address...
457b6ba1daaSPhilippe Houdoin 				fAssignedAddress = 0;
458b6ba1daaSPhilippe Houdoin 			}
4590892800fSPhilippe Houdoin 		}
4600892800fSPhilippe Houdoin 	}
4610892800fSPhilippe Houdoin 
462293ed4feSAxel Dörfler 	openlog_thread("DHCP", 0, LOG_DAEMON);
4636cc7630fSAxel Dörfler }
4646cc7630fSAxel Dörfler 
4656cc7630fSAxel Dörfler 
4666cc7630fSAxel Dörfler DHCPClient::~DHCPClient()
4676cc7630fSAxel Dörfler {
4686cc7630fSAxel Dörfler 	if (fStatus != B_OK)
4696cc7630fSAxel Dörfler 		return;
4706cc7630fSAxel Dörfler 
4716cc7630fSAxel Dörfler 	delete fRunner;
4720ce7725eSAxel Dörfler 
473fb81684fSAxel Dörfler 	int socket = ::socket(AF_INET, SOCK_DGRAM, 0);
4746cc7630fSAxel Dörfler 	if (socket < 0)
475fb81684fSAxel Dörfler 		return;
4766cc7630fSAxel Dörfler 
4776cc7630fSAxel Dörfler 	// release lease
4786cc7630fSAxel Dörfler 
4796cc7630fSAxel Dörfler 	dhcp_message release(DHCP_RELEASE);
48046ff5400SAxel Dörfler 	_PrepareMessage(release, BOUND);
4816cc7630fSAxel Dörfler 
4826cc7630fSAxel Dörfler 	_SendMessage(socket, release, fServer);
4836cc7630fSAxel Dörfler 	close(socket);
484293ed4feSAxel Dörfler 
485293ed4feSAxel Dörfler 	closelog_thread();
486fb81684fSAxel Dörfler }
487fb81684fSAxel Dörfler 
4886cc7630fSAxel Dörfler 
4896cc7630fSAxel Dörfler status_t
4906cc7630fSAxel Dörfler DHCPClient::Initialize()
4916cc7630fSAxel Dörfler {
4920892800fSPhilippe Houdoin 	fStatus = _Negotiate(fAssignedAddress == 0 ? INIT : INIT_REBOOT);
4930892800fSPhilippe Houdoin 	syslog(LOG_DEBUG, "%s: DHCP status = %s\n", Device(), strerror(fStatus));
4946cc7630fSAxel Dörfler 	return fStatus;
4956cc7630fSAxel Dörfler }
4966cc7630fSAxel Dörfler 
4976cc7630fSAxel Dörfler 
4986cc7630fSAxel Dörfler status_t
4996cc7630fSAxel Dörfler DHCPClient::_Negotiate(dhcp_state state)
5006cc7630fSAxel Dörfler {
5016972b91eSPawel Dziepak 	if (state == BOUND)
5026972b91eSPawel Dziepak 		return B_OK;
5036972b91eSPawel Dziepak 
5046972b91eSPawel Dziepak 	fStartTime = system_time();
5056972b91eSPawel Dziepak 	fTransactionID++;
5066972b91eSPawel Dziepak 
5076972b91eSPawel Dziepak 	char hostName[MAXHOSTNAMELEN];
5086972b91eSPawel Dziepak 	if (gethostname(hostName, MAXHOSTNAMELEN) == 0)
5096972b91eSPawel Dziepak 		fHostName.SetTo(hostName, MAXHOSTNAMELEN);
5106972b91eSPawel Dziepak 	else
5116972b91eSPawel Dziepak 		fHostName.Truncate(0);
5126972b91eSPawel Dziepak 
5136cc7630fSAxel Dörfler 	int socket = ::socket(AF_INET, SOCK_DGRAM, 0);
5146cc7630fSAxel Dörfler 	if (socket < 0)
5156cc7630fSAxel Dörfler 		return errno;
5166cc7630fSAxel Dörfler 
5172b829b04SBruno G. Albuquerque 	// Enable reusing the port. This is needed in case there is more
5182b829b04SBruno G. Albuquerque 	// than 1 interface that needs to be configured. Note that the only reason
5192b829b04SBruno G. Albuquerque 	// this works is because there is code below to bind to a specific
5202b829b04SBruno G. Albuquerque 	// interface.
5212b829b04SBruno G. Albuquerque 	int option = 1;
5222b829b04SBruno G. Albuquerque 	setsockopt(socket, SOL_SOCKET, SO_REUSEPORT, &option, sizeof(option));
5232b829b04SBruno G. Albuquerque 
5246972b91eSPawel Dziepak 	BNetworkAddress local;
5256972b91eSPawel Dziepak 	local.SetToWildcard(AF_INET, DHCP_CLIENT_PORT);
5266972b91eSPawel Dziepak 
5276972b91eSPawel Dziepak 	option = 1;
5286972b91eSPawel Dziepak 	setsockopt(socket, SOL_SOCKET, SO_BROADCAST, &option, sizeof(option));
5296972b91eSPawel Dziepak 
53077206143SAxel Dörfler 	if (bind(socket, local, local.Length()) < 0) {
531fb81684fSAxel Dörfler 		close(socket);
5326cc7630fSAxel Dörfler 		return errno;
533fb81684fSAxel Dörfler 	}
534fb81684fSAxel Dörfler 
5356cc7630fSAxel Dörfler 	bigtime_t previousLeaseTime = fLeaseTime;
536fb81684fSAxel Dörfler 
5376972b91eSPawel Dziepak 	status_t status = B_OK;
5380892800fSPhilippe Houdoin 	while (state != BOUND) {
5396972b91eSPawel Dziepak 		status = _StateTransition(socket, state);
5406972b91eSPawel Dziepak 		if (status != B_OK && (state == SELECTING || state == REBOOTING))
5416972b91eSPawel Dziepak 			break;
5426972b91eSPawel Dziepak 	}
5436972b91eSPawel Dziepak 
5446cc7630fSAxel Dörfler 	close(socket);
5456972b91eSPawel Dziepak 
5466972b91eSPawel Dziepak 	if (fLeaseTime == 0)
5476972b91eSPawel Dziepak 		fLeaseTime = previousLeaseTime;
5486972b91eSPawel Dziepak 	if (fLeaseTime == 0)
5496972b91eSPawel Dziepak 		fLeaseTime = 60;
5506972b91eSPawel Dziepak 
5516972b91eSPawel Dziepak 	if (fRenewalTime == 0)
5526972b91eSPawel Dziepak 		fRenewalTime = fLeaseTime / 2;
5536972b91eSPawel Dziepak 	if (fRebindingTime == 0)
5546972b91eSPawel Dziepak 		fRebindingTime = fLeaseTime * 7 / 8;
5556972b91eSPawel Dziepak 	fLeaseTime += fRequestTime;
5566972b91eSPawel Dziepak 	fRenewalTime += fRequestTime;
5576972b91eSPawel Dziepak 	fRebindingTime += fRequestTime;
5586972b91eSPawel Dziepak 	_RestartLease(fRenewalTime);
5596972b91eSPawel Dziepak 
5606972b91eSPawel Dziepak 	fStatus = status;
5616972b91eSPawel Dziepak 	if (status)
5626972b91eSPawel Dziepak 		return status;
5636972b91eSPawel Dziepak 
5646972b91eSPawel Dziepak 	// configure interface
5656972b91eSPawel Dziepak 	BMessage reply;
5666972b91eSPawel Dziepak 	status = Target().SendMessage(&fConfiguration, &reply);
5676972b91eSPawel Dziepak 	if (status == B_OK)
5686972b91eSPawel Dziepak 		status = reply.FindInt32("status", &fStatus);
5696972b91eSPawel Dziepak 
5706972b91eSPawel Dziepak 	// configure resolver
5716972b91eSPawel Dziepak 	reply.MakeEmpty();
5726972b91eSPawel Dziepak 	fResolverConfiguration.AddString("device", Device());
5736972b91eSPawel Dziepak 	status = Target().SendMessage(&fResolverConfiguration, &reply);
5746972b91eSPawel Dziepak 	if (status == B_OK)
5756972b91eSPawel Dziepak 		status = reply.FindInt32("status", &fStatus);
5766972b91eSPawel Dziepak 	return status;
577f9af6566SAxel Dörfler }
578f9af6566SAxel Dörfler 
579b6ba1daaSPhilippe Houdoin 
5806972b91eSPawel Dziepak status_t
5816972b91eSPawel Dziepak DHCPClient::_GotMessage(dhcp_state& state, dhcp_message* message)
582f9af6566SAxel Dörfler {
5836972b91eSPawel Dziepak 	switch (state) {
5846972b91eSPawel Dziepak 		case SELECTING:
5856972b91eSPawel Dziepak 			if (message->Type() == DHCP_OFFER) {
5866972b91eSPawel Dziepak 				state = REQUESTING;
587f9af6566SAxel Dörfler 
588f9af6566SAxel Dörfler 				fAssignedAddress = message->your_address;
58969b5cacbSPhilippe Houdoin 				syslog(LOG_INFO, "  your_address: %s\n",
59069b5cacbSPhilippe Houdoin 						_AddressToString(fAssignedAddress).String());
591f9af6566SAxel Dörfler 
59210cc12daSAxel Dörfler 				fConfiguration.MakeEmpty();
593293ed4feSAxel Dörfler 				fConfiguration.AddString("device", Device());
5940892800fSPhilippe Houdoin 				fConfiguration.AddBool("auto_configured", true);
595f9af6566SAxel Dörfler 
596f9af6566SAxel Dörfler 				BMessage address;
597f9af6566SAxel Dörfler 				address.AddString("family", "inet");
598d5d2fdf0SPhilippe Houdoin 				address.AddString("address", _AddressToString(fAssignedAddress));
59916e8f13aSAxel Dörfler 				fResolverConfiguration.MakeEmpty();
60016e8f13aSAxel Dörfler 				_ParseOptions(*message, address, fResolverConfiguration);
601f9af6566SAxel Dörfler 
60210cc12daSAxel Dörfler 				fConfiguration.AddMessage("address", &address);
6036972b91eSPawel Dziepak 				return B_OK;
604f9af6566SAxel Dörfler 			}
605f9af6566SAxel Dörfler 
6066972b91eSPawel Dziepak 			return B_BAD_VALUE;
607f9af6566SAxel Dörfler 
6086972b91eSPawel Dziepak 		case REBOOTING:
6096972b91eSPawel Dziepak 		case REBINDING:
6106972b91eSPawel Dziepak 		case RENEWING:
6116972b91eSPawel Dziepak 		case REQUESTING:
6126972b91eSPawel Dziepak 			if (message->Type() == DHCP_ACK) {
6136cc7630fSAxel Dörfler 				// TODO: we might want to configure the stuff, don't we?
6146cc7630fSAxel Dörfler 				BMessage address;
61516e8f13aSAxel Dörfler 				fResolverConfiguration.MakeEmpty();
61616e8f13aSAxel Dörfler 				_ParseOptions(*message, address, fResolverConfiguration);
6174ac66051SAxel Dörfler 					// TODO: currently, only lease time and DNS is updated this
6184ac66051SAxel Dörfler 					// way
6196cc7630fSAxel Dörfler 
620f9af6566SAxel Dörfler 				// our address request has been acknowledged
6210892800fSPhilippe Houdoin 				state = BOUND;
62210cc12daSAxel Dörfler 
6236972b91eSPawel Dziepak 				return B_OK;
62410cc12daSAxel Dörfler 			}
625f9af6566SAxel Dörfler 
6266972b91eSPawel Dziepak 			if (message->Type() == DHCP_NACK) {
6270892800fSPhilippe Houdoin 				// server reject our request on previous assigned address
6280892800fSPhilippe Houdoin 				// back to square one...
6290892800fSPhilippe Houdoin 				fAssignedAddress = 0;
6306972b91eSPawel Dziepak 				state = INIT;
6316972b91eSPawel Dziepak 				return B_OK;
6320892800fSPhilippe Houdoin 			}
6330892800fSPhilippe Houdoin 
6346972b91eSPawel Dziepak 		default:
6356972b91eSPawel Dziepak 			return B_BAD_VALUE;
6366972b91eSPawel Dziepak 	}
6376972b91eSPawel Dziepak }
6386972b91eSPawel Dziepak 
6396972b91eSPawel Dziepak 
6406972b91eSPawel Dziepak status_t
6416972b91eSPawel Dziepak DHCPClient::_StateTransition(int socket, dhcp_state& state)
6426972b91eSPawel Dziepak {
6436972b91eSPawel Dziepak 	if (state == INIT) {
6446972b91eSPawel Dziepak 		// The local interface does not have an address yet, bind the socket
6456972b91eSPawel Dziepak 		// to the device directly.
6466972b91eSPawel Dziepak 		BNetworkDevice device(Device());
6476972b91eSPawel Dziepak 		int index = device.Index();
6486972b91eSPawel Dziepak 
6496972b91eSPawel Dziepak 		setsockopt(socket, SOL_SOCKET, SO_BINDTODEVICE, &index, sizeof(int));
6506972b91eSPawel Dziepak 	}
6516972b91eSPawel Dziepak 
6526972b91eSPawel Dziepak 	BNetworkAddress broadcast;
6536972b91eSPawel Dziepak 	broadcast.SetToBroadcast(AF_INET, DHCP_SERVER_PORT);
6546972b91eSPawel Dziepak 
6556972b91eSPawel Dziepak 	time_t timeout;
6566972b91eSPawel Dziepak 	uint32 tries;
6576972b91eSPawel Dziepak 	_ResetTimeout(socket, state, timeout, tries);
6586972b91eSPawel Dziepak 
6596972b91eSPawel Dziepak 	dhcp_message discover(DHCP_DISCOVER);
6606972b91eSPawel Dziepak 	_PrepareMessage(discover, state);
6616972b91eSPawel Dziepak 
6626972b91eSPawel Dziepak 	dhcp_message request(DHCP_REQUEST);
6636972b91eSPawel Dziepak 	_PrepareMessage(request, state);
6646972b91eSPawel Dziepak 
6656972b91eSPawel Dziepak 	bool skipRequest = false;
6666972b91eSPawel Dziepak 	dhcp_state originalState = state;
6676972b91eSPawel Dziepak 	fRequestTime = system_time();
6686972b91eSPawel Dziepak 	while (true) {
6696972b91eSPawel Dziepak 		if (!skipRequest) {
6706972b91eSPawel Dziepak 			_SendMessage(socket, originalState == INIT ? discover : request,
6716972b91eSPawel Dziepak 				originalState == RENEWING ? fServer : broadcast);
6726972b91eSPawel Dziepak 
6736972b91eSPawel Dziepak 			if (originalState == INIT)
6746972b91eSPawel Dziepak 				state = SELECTING;
6756972b91eSPawel Dziepak 			else if (originalState == INIT_REBOOT)
6766972b91eSPawel Dziepak 				state = REBOOTING;
6776972b91eSPawel Dziepak 		}
6786972b91eSPawel Dziepak 
6796972b91eSPawel Dziepak 		char buffer[2048];
6806972b91eSPawel Dziepak 		struct sockaddr_in from;
6816972b91eSPawel Dziepak 		socklen_t fromLength = sizeof(from);
6826972b91eSPawel Dziepak 
6836972b91eSPawel Dziepak 		ssize_t bytesReceived = recvfrom(socket, buffer, sizeof(buffer),
6846972b91eSPawel Dziepak 			0, (struct sockaddr*)&from, &fromLength);
6856972b91eSPawel Dziepak 		if (bytesReceived < 0 && errno == B_TIMED_OUT) {
6866972b91eSPawel Dziepak 			// depending on the state, we'll just try again
6876972b91eSPawel Dziepak 			if (!_TimeoutShift(socket, state, timeout, tries))
6886972b91eSPawel Dziepak 				return B_TIMED_OUT;
6896972b91eSPawel Dziepak 			skipRequest = false;
6906972b91eSPawel Dziepak 			continue;
6916972b91eSPawel Dziepak 		} else if (bytesReceived < 0)
6926972b91eSPawel Dziepak 			return errno;
6936972b91eSPawel Dziepak 
6946972b91eSPawel Dziepak 		skipRequest = true;
6956972b91eSPawel Dziepak 		dhcp_message* message = (dhcp_message*)buffer;
6966972b91eSPawel Dziepak 		if (message->transaction_id != htonl(fTransactionID)
6976972b91eSPawel Dziepak 			|| !message->HasOptions()
6986972b91eSPawel Dziepak 			|| memcmp(message->mac_address, discover.mac_address,
6996972b91eSPawel Dziepak 				discover.hardware_address_length)) {
7006972b91eSPawel Dziepak 			// this message is not for us
7016972b91eSPawel Dziepak 			continue;
7026972b91eSPawel Dziepak 		}
7036972b91eSPawel Dziepak 
7046972b91eSPawel Dziepak 		syslog(LOG_DEBUG, "%s: Received %s from %s\n",
7056972b91eSPawel Dziepak 			Device(), dhcp_message::TypeToString(message->Type()),
7066972b91eSPawel Dziepak 			_AddressToString(from.sin_addr.s_addr).String());
7076972b91eSPawel Dziepak 
7086972b91eSPawel Dziepak 		if (_GotMessage(state, message) == B_OK)
709f9af6566SAxel Dörfler 			break;
710f9af6566SAxel Dörfler 	}
711f9af6566SAxel Dörfler 
7126972b91eSPawel Dziepak 	return B_OK;
713fb81684fSAxel Dörfler }
714fb81684fSAxel Dörfler 
715fb81684fSAxel Dörfler 
7166cc7630fSAxel Dörfler void
7176cc7630fSAxel Dörfler DHCPClient::_RestartLease(bigtime_t leaseTime)
718fb81684fSAxel Dörfler {
7196cc7630fSAxel Dörfler 	if (leaseTime == 0)
720f9af6566SAxel Dörfler 		return;
721f9af6566SAxel Dörfler 
7226cc7630fSAxel Dörfler 	BMessage lease(kMsgLeaseTime);
7236972b91eSPawel Dziepak 	fRunner = new BMessageRunner(this, &lease, leaseTime - system_time(), 1);
724f9af6566SAxel Dörfler }
725f9af6566SAxel Dörfler 
726f9af6566SAxel Dörfler 
727f9af6566SAxel Dörfler void
72816e8f13aSAxel Dörfler DHCPClient::_ParseOptions(dhcp_message& message, BMessage& address,
72916e8f13aSAxel Dörfler 	BMessage& resolverConfiguration)
7300ce7725eSAxel Dörfler {
7310ce7725eSAxel Dörfler 	dhcp_option_cookie cookie;
7320ce7725eSAxel Dörfler 	message_option option;
7330ce7725eSAxel Dörfler 	const uint8* data;
7340ce7725eSAxel Dörfler 	size_t size;
7350ce7725eSAxel Dörfler 	while (message.NextOption(cookie, option, data, size)) {
7360ce7725eSAxel Dörfler 		// iterate through all options
7370ce7725eSAxel Dörfler 		switch (option) {
7380ce7725eSAxel Dörfler 			case OPTION_ROUTER_ADDRESS:
739cefe2a40SPhilippe Houdoin 				syslog(LOG_DEBUG, "  gateway: %s\n",
74069b5cacbSPhilippe Houdoin 					_AddressToString(data).String());
741d5d2fdf0SPhilippe Houdoin 				address.AddString("gateway", _AddressToString(data));
7420ce7725eSAxel Dörfler 				break;
7430ce7725eSAxel Dörfler 			case OPTION_SUBNET_MASK:
744cefe2a40SPhilippe Houdoin 				syslog(LOG_DEBUG, "  subnet: %s\n",
74569b5cacbSPhilippe Houdoin 					_AddressToString(data).String());
746d5d2fdf0SPhilippe Houdoin 				address.AddString("mask", _AddressToString(data));
7470ce7725eSAxel Dörfler 				break;
7485782c5a3SAxel Dörfler 			case OPTION_BROADCAST_ADDRESS:
749cefe2a40SPhilippe Houdoin 				syslog(LOG_DEBUG, "  broadcast: %s\n",
75069b5cacbSPhilippe Houdoin 					_AddressToString(data).String());
751d5d2fdf0SPhilippe Houdoin 				address.AddString("broadcast", _AddressToString(data));
7525782c5a3SAxel Dörfler 				break;
7530ce7725eSAxel Dörfler 			case OPTION_DOMAIN_NAME_SERVER:
754a552ec13SAxel Dörfler 			{
7550ce7725eSAxel Dörfler 				for (uint32 i = 0; i < size / 4; i++) {
756cefe2a40SPhilippe Houdoin 					syslog(LOG_DEBUG, "  nameserver[%d]: %s\n", i,
757d5d2fdf0SPhilippe Houdoin 						_AddressToString(&data[i * 4]).String());
75816e8f13aSAxel Dörfler 					resolverConfiguration.AddString("nameserver",
759d5d2fdf0SPhilippe Houdoin 						_AddressToString(&data[i * 4]).String());
76015ab0bcfSAxel Dörfler 				}
76116e8f13aSAxel Dörfler 				resolverConfiguration.AddInt32("nameserver_count",
76216e8f13aSAxel Dörfler 					size / 4);
7630ce7725eSAxel Dörfler 				break;
764a552ec13SAxel Dörfler 			}
765cefe2a40SPhilippe Houdoin 			case OPTION_SERVER_ADDRESS:
766*f9ab315eSAugustin Cavalier 			{
767cefe2a40SPhilippe Houdoin 				syslog(LOG_DEBUG, "  server: %s\n",
76869b5cacbSPhilippe Houdoin 					_AddressToString(data).String());
769*f9ab315eSAugustin Cavalier 				status_t status = fServer.SetAddress(*(in_addr_t*)data);
770*f9ab315eSAugustin Cavalier 				if (status != B_OK) {
771*f9ab315eSAugustin Cavalier 					syslog(LOG_ERR, "   BNetworkAddress::SetAddress failed with %s!\n",
772*f9ab315eSAugustin Cavalier 						strerror(status));
773*f9ab315eSAugustin Cavalier 					fServer.Unset();
774*f9ab315eSAugustin Cavalier 				}
7750ce7725eSAxel Dörfler 				break;
776*f9ab315eSAugustin Cavalier 			}
77746ff5400SAxel Dörfler 
7780ce7725eSAxel Dörfler 			case OPTION_ADDRESS_LEASE_TIME:
779cefe2a40SPhilippe Houdoin 				syslog(LOG_DEBUG, "  lease time: %lu seconds\n",
78069b5cacbSPhilippe Houdoin 					ntohl(*(uint32*)data));
78169b5cacbSPhilippe Houdoin 				fLeaseTime = ntohl(*(uint32*)data) * 1000000LL;
7820ce7725eSAxel Dörfler 				break;
7831a4e8e7bSAxel Dörfler 			case OPTION_RENEWAL_TIME:
784cefe2a40SPhilippe Houdoin 				syslog(LOG_DEBUG, "  renewal time: %lu seconds\n",
78569b5cacbSPhilippe Houdoin 					ntohl(*(uint32*)data));
78669b5cacbSPhilippe Houdoin 				fRenewalTime = ntohl(*(uint32*)data) * 1000000LL;
78746ff5400SAxel Dörfler 				break;
7881a4e8e7bSAxel Dörfler 			case OPTION_REBINDING_TIME:
789cefe2a40SPhilippe Houdoin 				syslog(LOG_DEBUG, "  rebinding time: %lu seconds\n",
79069b5cacbSPhilippe Houdoin 					ntohl(*(uint32*)data));
79169b5cacbSPhilippe Houdoin 				fRebindingTime = ntohl(*(uint32*)data) * 1000000LL;
7921a4e8e7bSAxel Dörfler 				break;
7931a4e8e7bSAxel Dörfler 
7940ce7725eSAxel Dörfler 			case OPTION_HOST_NAME:
795cefe2a40SPhilippe Houdoin 				syslog(LOG_DEBUG, "  host name: \"%.*s\"\n", (int)size,
79616e8f13aSAxel Dörfler 					(const char*)data);
7970ce7725eSAxel Dörfler 				break;
7985782c5a3SAxel Dörfler 
7995782c5a3SAxel Dörfler 			case OPTION_DOMAIN_NAME:
8005782c5a3SAxel Dörfler 			{
80116e8f13aSAxel Dörfler 				char domain[256];
80216e8f13aSAxel Dörfler 				strlcpy(domain, (const char*)data,
80316e8f13aSAxel Dörfler 					min_c(size + 1, sizeof(domain)));
8040f87f52fSStephan Aßmus 
805cefe2a40SPhilippe Houdoin 				syslog(LOG_DEBUG, "  domain name: \"%s\"\n", domain);
8060f87f52fSStephan Aßmus 
80716e8f13aSAxel Dörfler 				resolverConfiguration.AddString("domain", domain);
8085782c5a3SAxel Dörfler 				break;
8095782c5a3SAxel Dörfler 			}
8100ce7725eSAxel Dörfler 
8110ce7725eSAxel Dörfler 			case OPTION_MESSAGE_TYPE:
8120ce7725eSAxel Dörfler 				break;
8130ce7725eSAxel Dörfler 
814d5d2fdf0SPhilippe Houdoin 			case OPTION_ERROR_MESSAGE:
81569b5cacbSPhilippe Houdoin 				syslog(LOG_INFO, "  error message: \"%.*s\"\n", (int)size,
816d5d2fdf0SPhilippe Houdoin 					(const char*)data);
817d5d2fdf0SPhilippe Houdoin 				break;
818d5d2fdf0SPhilippe Houdoin 
8190ce7725eSAxel Dörfler 			default:
820cefe2a40SPhilippe Houdoin 				syslog(LOG_DEBUG, "  UNKNOWN OPTION %lu (0x%x)\n",
82169b5cacbSPhilippe Houdoin 					(uint32)option, (uint32)option);
8220ce7725eSAxel Dörfler 				break;
8230ce7725eSAxel Dörfler 		}
8240ce7725eSAxel Dörfler 	}
8250ce7725eSAxel Dörfler }
8260ce7725eSAxel Dörfler 
8270ce7725eSAxel Dörfler 
8280ce7725eSAxel Dörfler void
82946ff5400SAxel Dörfler DHCPClient::_PrepareMessage(dhcp_message& message, dhcp_state state)
8300ce7725eSAxel Dörfler {
8310ce7725eSAxel Dörfler 	message.opcode = BOOT_REQUEST;
8320ce7725eSAxel Dörfler 	message.hardware_type = ARP_HARDWARE_TYPE_ETHER;
8330ce7725eSAxel Dörfler 	message.hardware_address_length = 6;
8340ce7725eSAxel Dörfler 	message.transaction_id = htonl(fTransactionID);
835bcc8dadaSAxel Dörfler 	message.seconds_since_start = htons(min_c((system_time() - fStartTime)
83615ab0bcfSAxel Dörfler 		/ 1000000LL, 65535));
8370ce7725eSAxel Dörfler 	memcpy(message.mac_address, fMAC, 6);
8380ce7725eSAxel Dörfler 
83946ff5400SAxel Dörfler 	message_type type = message.Type();
84046ff5400SAxel Dörfler 
841a073ba1aSHugo Santos 	uint8 *next = message.PrepareMessage(type);
842d5d2fdf0SPhilippe Houdoin 
843d5d2fdf0SPhilippe Houdoin 	switch (type) {
844d5d2fdf0SPhilippe Houdoin 		case DHCP_DISCOVER:
845d5d2fdf0SPhilippe Houdoin 			next = message.PutOption(next, OPTION_REQUEST_PARAMETERS,
846d5d2fdf0SPhilippe Houdoin 				kRequestParameters, sizeof(kRequestParameters));
8475f25cf08SSiarzhuk Zharski 
8485f25cf08SSiarzhuk Zharski 			if (fHostName.Length() > 0) {
8495f25cf08SSiarzhuk Zharski 				next = message.PutOption(next, OPTION_HOST_NAME,
8505f25cf08SSiarzhuk Zharski 					reinterpret_cast<const uint8*>(fHostName.String()),
8515f25cf08SSiarzhuk Zharski 					fHostName.Length());
8525f25cf08SSiarzhuk Zharski 			}
853d5d2fdf0SPhilippe Houdoin 			break;
854d5d2fdf0SPhilippe Houdoin 
855d5d2fdf0SPhilippe Houdoin 		case DHCP_REQUEST:
856d5d2fdf0SPhilippe Houdoin 			next = message.PutOption(next, OPTION_REQUEST_PARAMETERS,
857d5d2fdf0SPhilippe Houdoin 				kRequestParameters, sizeof(kRequestParameters));
858d5d2fdf0SPhilippe Houdoin 
8595f25cf08SSiarzhuk Zharski 			if (fHostName.Length() > 0) {
8605f25cf08SSiarzhuk Zharski 				next = message.PutOption(next, OPTION_HOST_NAME,
8615f25cf08SSiarzhuk Zharski 					reinterpret_cast<const uint8*>(fHostName.String()),
8625f25cf08SSiarzhuk Zharski 					fHostName.Length());
8635f25cf08SSiarzhuk Zharski 			}
8645f25cf08SSiarzhuk Zharski 
865d5d2fdf0SPhilippe Houdoin 			if (state == REQUESTING) {
866d5d2fdf0SPhilippe Houdoin 				const sockaddr_in& server = (sockaddr_in&)fServer.SockAddr();
867cefe2a40SPhilippe Houdoin 				next = message.PutOption(next, OPTION_SERVER_ADDRESS,
868d5d2fdf0SPhilippe Houdoin 					(uint32)server.sin_addr.s_addr);
869d5d2fdf0SPhilippe Houdoin 			}
870d5d2fdf0SPhilippe Houdoin 
8710892800fSPhilippe Houdoin 			if (state == INIT || state == INIT_REBOOT
8720892800fSPhilippe Houdoin 				|| state == REQUESTING) {
873d5d2fdf0SPhilippe Houdoin 				next = message.PutOption(next, OPTION_REQUEST_IP_ADDRESS,
874d5d2fdf0SPhilippe Houdoin 					(uint32)fAssignedAddress);
875d5d2fdf0SPhilippe Houdoin 			} else
876d5d2fdf0SPhilippe Houdoin 				message.client_address = fAssignedAddress;
877d5d2fdf0SPhilippe Houdoin 			break;
878d5d2fdf0SPhilippe Houdoin 
879d5d2fdf0SPhilippe Houdoin 		case DHCP_RELEASE: {
880d5d2fdf0SPhilippe Houdoin 			const sockaddr_in& server = (sockaddr_in&)fServer.SockAddr();
881cefe2a40SPhilippe Houdoin 			next = message.PutOption(next, OPTION_SERVER_ADDRESS,
882af074561SAxel Dörfler 				(uint32)server.sin_addr.s_addr);
88346ff5400SAxel Dörfler 
88446ff5400SAxel Dörfler 			message.client_address = fAssignedAddress;
8850ce7725eSAxel Dörfler 			break;
8860ce7725eSAxel Dörfler 		}
8870ce7725eSAxel Dörfler 
8880ce7725eSAxel Dörfler 		default:
8890ce7725eSAxel Dörfler 			break;
8900ce7725eSAxel Dörfler 	}
891d5d2fdf0SPhilippe Houdoin 
892d5d2fdf0SPhilippe Houdoin 	message.FinishOptions(next);
8930ce7725eSAxel Dörfler }
8940ce7725eSAxel Dörfler 
8950ce7725eSAxel Dörfler 
8960ce7725eSAxel Dörfler void
8976972b91eSPawel Dziepak DHCPClient::_ResetTimeout(int socket, dhcp_state& state, time_t& timeout,
8986972b91eSPawel Dziepak 	uint32& tries)
899f9af6566SAxel Dörfler {
9006cc7630fSAxel Dörfler 	timeout = DEFAULT_TIMEOUT;
9016cc7630fSAxel Dörfler 	tries = 0;
902f9af6566SAxel Dörfler 
903f9af6566SAxel Dörfler 	struct timeval value;
9046cc7630fSAxel Dörfler 	value.tv_sec = timeout;
905d09c1f8eSPhilippe Houdoin 	value.tv_usec = rand() % 1000000;
906f9af6566SAxel Dörfler 	setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &value, sizeof(value));
907f9af6566SAxel Dörfler }
908f9af6566SAxel Dörfler 
909f9af6566SAxel Dörfler 
910f9af6566SAxel Dörfler bool
9116972b91eSPawel Dziepak DHCPClient::_TimeoutShift(int socket, dhcp_state& state, time_t& timeout,
9126972b91eSPawel Dziepak 	uint32& tries)
913f9af6566SAxel Dörfler {
9146972b91eSPawel Dziepak 	if (state == RENEWING && system_time() > fRebindingTime) {
9156972b91eSPawel Dziepak 		state = REBINDING;
916f9af6566SAxel Dörfler 		return false;
917f9af6566SAxel Dörfler 	}
9186972b91eSPawel Dziepak 
9196972b91eSPawel Dziepak 	if (state == REBINDING && system_time() > fLeaseTime) {
9206972b91eSPawel Dziepak 		state = INIT;
9216972b91eSPawel Dziepak 		return false;
9226972b91eSPawel Dziepak 	}
9236972b91eSPawel Dziepak 
9246972b91eSPawel Dziepak 	tries++;
9256972b91eSPawel Dziepak 	timeout += timeout;
9266972b91eSPawel Dziepak 	if (timeout > MAX_TIMEOUT)
9276972b91eSPawel Dziepak 		timeout = MAX_TIMEOUT;
9286972b91eSPawel Dziepak 
9296972b91eSPawel Dziepak 	if (tries > MAX_RETRIES) {
9306972b91eSPawel Dziepak 		bigtime_t remaining = 0;
9316972b91eSPawel Dziepak 		if (state == RENEWING)
9326972b91eSPawel Dziepak 			remaining = (fRebindingTime - system_time()) / 2 + 1;
9336972b91eSPawel Dziepak 		else if (state == REBINDING)
9346972b91eSPawel Dziepak 			remaining = (fLeaseTime - system_time()) / 2 + 1;
9356972b91eSPawel Dziepak 		else
9366972b91eSPawel Dziepak 			return false;
9376972b91eSPawel Dziepak 
9386972b91eSPawel Dziepak 		timeout = std::max(remaining / 1000000, bigtime_t(60));
9396972b91eSPawel Dziepak 	}
9406972b91eSPawel Dziepak 
9410892800fSPhilippe Houdoin 	syslog(LOG_DEBUG, "%s: Timeout shift: %lu secs (try %lu)\n",
94261729d93SAxel Dörfler 		Device(), timeout, tries);
943f9af6566SAxel Dörfler 
944f9af6566SAxel Dörfler 	struct timeval value;
9456cc7630fSAxel Dörfler 	value.tv_sec = timeout;
946d09c1f8eSPhilippe Houdoin 	value.tv_usec = rand() % 1000000;
947f9af6566SAxel Dörfler 	setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &value, sizeof(value));
948f9af6566SAxel Dörfler 
949f9af6566SAxel Dörfler 	return true;
950f9af6566SAxel Dörfler }
951f9af6566SAxel Dörfler 
952f9af6566SAxel Dörfler 
95384fdf2c3SPhilippe Houdoin /*static*/ BString
95484fdf2c3SPhilippe Houdoin DHCPClient::_AddressToString(const uint8* data)
955f9af6566SAxel Dörfler {
956f9af6566SAxel Dörfler 	BString target = inet_ntoa(*(in_addr*)data);
957f9af6566SAxel Dörfler 	return target;
958f9af6566SAxel Dörfler }
959f9af6566SAxel Dörfler 
960f9af6566SAxel Dörfler 
96184fdf2c3SPhilippe Houdoin /*static*/ BString
96284fdf2c3SPhilippe Houdoin DHCPClient::_AddressToString(in_addr_t address)
963f9af6566SAxel Dörfler {
964f9af6566SAxel Dörfler 	BString target = inet_ntoa(*(in_addr*)&address);
965f9af6566SAxel Dörfler 	return target;
966f9af6566SAxel Dörfler }
967f9af6566SAxel Dörfler 
968f9af6566SAxel Dörfler 
969f9af6566SAxel Dörfler status_t
9701a905a76SAxel Dörfler DHCPClient::_SendMessage(int socket, dhcp_message& message,
971af074561SAxel Dörfler 	const BNetworkAddress& address) const
972f9af6566SAxel Dörfler {
9730892800fSPhilippe Houdoin 	message_type type = message.Type();
9740892800fSPhilippe Houdoin 	BString text;
9750892800fSPhilippe Houdoin 	text << dhcp_message::TypeToString(type);
9760892800fSPhilippe Houdoin 
9770892800fSPhilippe Houdoin 	const uint8* requestAddress = message.FindOption(OPTION_REQUEST_IP_ADDRESS);
9780892800fSPhilippe Houdoin 	if (type == DHCP_REQUEST && requestAddress != NULL)
9790892800fSPhilippe Houdoin 		text << " for " << _AddressToString(requestAddress).String();
9800892800fSPhilippe Houdoin 
9810892800fSPhilippe Houdoin 	syslog(LOG_DEBUG, "%s: Send %s to %s\n", Device(), text.String(),
9820892800fSPhilippe Houdoin 		address.ToString().String());
98361729d93SAxel Dörfler 
984f9af6566SAxel Dörfler 	ssize_t bytesSent = sendto(socket, &message, message.Size(),
985af074561SAxel Dörfler 		address.IsBroadcast() ? MSG_BCAST : 0, address, address.Length());
986f9af6566SAxel Dörfler 	if (bytesSent < 0)
987f9af6566SAxel Dörfler 		return errno;
988f9af6566SAxel Dörfler 
989f9af6566SAxel Dörfler 	return B_OK;
990fb81684fSAxel Dörfler }
991fb81684fSAxel Dörfler 
992fb81684fSAxel Dörfler 
99346ff5400SAxel Dörfler dhcp_state
99446ff5400SAxel Dörfler DHCPClient::_CurrentState() const
99546ff5400SAxel Dörfler {
99646ff5400SAxel Dörfler 	bigtime_t now = system_time();
99746ff5400SAxel Dörfler 
9986972b91eSPawel Dziepak 	if (now > fLeaseTime || fStatus != B_OK)
99946ff5400SAxel Dörfler 		return INIT;
100046ff5400SAxel Dörfler 	if (now >= fRebindingTime)
100146ff5400SAxel Dörfler 		return REBINDING;
100246ff5400SAxel Dörfler 	if (now >= fRenewalTime)
10030892800fSPhilippe Houdoin 		return RENEWING;
100446ff5400SAxel Dörfler 	return BOUND;
100546ff5400SAxel Dörfler }
100646ff5400SAxel Dörfler 
100746ff5400SAxel Dörfler 
1008fb81684fSAxel Dörfler void
1009fb81684fSAxel Dörfler DHCPClient::MessageReceived(BMessage* message)
1010fb81684fSAxel Dörfler {
10116cc7630fSAxel Dörfler 	switch (message->what) {
10126cc7630fSAxel Dörfler 		case kMsgLeaseTime:
10136972b91eSPawel Dziepak 			_Negotiate(_CurrentState());
10146cc7630fSAxel Dörfler 			break;
10156cc7630fSAxel Dörfler 
10166cc7630fSAxel Dörfler 		default:
1017fb81684fSAxel Dörfler 			BHandler::MessageReceived(message);
10186cc7630fSAxel Dörfler 			break;
10196cc7630fSAxel Dörfler 	}
1020fb81684fSAxel Dörfler }
1021