xref: /haiku/src/servers/net/DHCPClient.cpp (revision 7ca2da76f00eba4af4f50f8cbf3f03872d36c92f)
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 
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>
23d09c1f8eSPhilippe Houdoin #include <stdlib.h>
24293ed4feSAxel Dörfler #include <syslog.h>
2536bde12dSAxel Dörfler #include <sys/sockio.h>
26f9af6566SAxel Dörfler #include <sys/time.h>
27fb81684fSAxel Dörfler 
284ac66051SAxel Dörfler #include <Debug.h>
294ac66051SAxel Dörfler #include <Message.h>
304ac66051SAxel Dörfler #include <MessageRunner.h>
314ac66051SAxel Dörfler 
324ac66051SAxel Dörfler #include "NetServer.h"
334ac66051SAxel Dörfler 
34fb81684fSAxel Dörfler 
35fb81684fSAxel Dörfler // See RFC 2131 for DHCP, see RFC 1533 for BOOTP/DHCP options
36fb81684fSAxel Dörfler 
37fb81684fSAxel Dörfler #define DHCP_CLIENT_PORT	68
38fb81684fSAxel Dörfler #define DHCP_SERVER_PORT	67
39fb81684fSAxel Dörfler 
40f9af6566SAxel Dörfler #define DEFAULT_TIMEOUT		2	// secs
41f9af6566SAxel Dörfler #define MAX_TIMEOUT			15	// secs
42f9af6566SAxel Dörfler 
43fb81684fSAxel Dörfler enum message_opcode {
44fb81684fSAxel Dörfler 	BOOT_REQUEST = 1,
45fb81684fSAxel Dörfler 	BOOT_REPLY
46fb81684fSAxel Dörfler };
47fb81684fSAxel Dörfler 
48fb81684fSAxel Dörfler enum message_option {
49fb81684fSAxel Dörfler 	OPTION_MAGIC = 0x63825363,
50fb81684fSAxel Dörfler 
51fb81684fSAxel Dörfler 	// generic options
52fb81684fSAxel Dörfler 	OPTION_PAD = 0,
53f9af6566SAxel Dörfler 	OPTION_END = 255,
54f9af6566SAxel Dörfler 	OPTION_SUBNET_MASK = 1,
5565186fecSAxel Dörfler 	OPTION_TIME_OFFSET = 2,
56f9af6566SAxel Dörfler 	OPTION_ROUTER_ADDRESS = 3,
57f9af6566SAxel Dörfler 	OPTION_DOMAIN_NAME_SERVER = 6,
58f9af6566SAxel Dörfler 	OPTION_HOST_NAME = 12,
595782c5a3SAxel Dörfler 	OPTION_DOMAIN_NAME = 15,
60d5d2fdf0SPhilippe Houdoin 	OPTION_MAX_DATAGRAM_SIZE = 22,
61d5d2fdf0SPhilippe Houdoin 	OPTION_INTERFACE_MTU = 26,
62fb81684fSAxel Dörfler 	OPTION_BROADCAST_ADDRESS = 28,
63d5d2fdf0SPhilippe Houdoin 	OPTION_NETWORK_TIME_PROTOCOL_SERVERS = 42,
6465186fecSAxel Dörfler 	OPTION_NETBIOS_NAME_SERVER = 44,
6565186fecSAxel Dörfler 	OPTION_NETBIOS_SCOPE = 47,
66fb81684fSAxel Dörfler 
67fb81684fSAxel Dörfler 	// DHCP specific options
68fb81684fSAxel Dörfler 	OPTION_REQUEST_IP_ADDRESS = 50,
69fb81684fSAxel Dörfler 	OPTION_ADDRESS_LEASE_TIME = 51,
70fb81684fSAxel Dörfler 	OPTION_OVERLOAD = 52,
71fb81684fSAxel Dörfler 	OPTION_MESSAGE_TYPE = 53,
72cefe2a40SPhilippe Houdoin 	OPTION_SERVER_ADDRESS = 54,
73fb81684fSAxel Dörfler 	OPTION_REQUEST_PARAMETERS = 55,
74fb81684fSAxel Dörfler 	OPTION_ERROR_MESSAGE = 56,
75d5d2fdf0SPhilippe Houdoin 	OPTION_MAX_MESSAGE_SIZE = 57,
76fb81684fSAxel Dörfler 	OPTION_RENEWAL_TIME = 58,
77fb81684fSAxel Dörfler 	OPTION_REBINDING_TIME = 59,
78fb81684fSAxel Dörfler 	OPTION_CLASS_IDENTIFIER = 60,
79fb81684fSAxel Dörfler 	OPTION_CLIENT_IDENTIFIER = 61,
80fb81684fSAxel Dörfler };
81fb81684fSAxel Dörfler 
82fb81684fSAxel Dörfler enum message_type {
83f9af6566SAxel Dörfler 	DHCP_NONE = 0,
84f9af6566SAxel Dörfler 	DHCP_DISCOVER,
85fb81684fSAxel Dörfler 	DHCP_OFFER,
86fb81684fSAxel Dörfler 	DHCP_REQUEST,
87fb81684fSAxel Dörfler 	DHCP_DECLINE,
88fb81684fSAxel Dörfler 	DHCP_ACK,
89f9af6566SAxel Dörfler 	DHCP_NACK,
90fb81684fSAxel Dörfler 	DHCP_RELEASE,
91fb81684fSAxel Dörfler 	DHCP_INFORM
92fb81684fSAxel Dörfler };
93fb81684fSAxel Dörfler 
94fb81684fSAxel Dörfler struct dhcp_option_cookie {
9515ab0bcfSAxel Dörfler 	dhcp_option_cookie()
9615ab0bcfSAxel Dörfler 		:
9715ab0bcfSAxel Dörfler 		state(0),
9815ab0bcfSAxel Dörfler 		file_has_options(false),
9915ab0bcfSAxel Dörfler 		server_name_has_options(false)
10015ab0bcfSAxel Dörfler 	{
10115ab0bcfSAxel Dörfler 	}
102fb81684fSAxel Dörfler 
103fb81684fSAxel Dörfler 	const uint8* next;
104fb81684fSAxel Dörfler 	uint8	state;
105fb81684fSAxel Dörfler 	bool	file_has_options;
106fb81684fSAxel Dörfler 	bool	server_name_has_options;
107fb81684fSAxel Dörfler };
108fb81684fSAxel Dörfler 
109fb81684fSAxel Dörfler struct dhcp_message {
110fb81684fSAxel Dörfler 	dhcp_message(message_type type);
111fb81684fSAxel Dörfler 
112fb81684fSAxel Dörfler 	uint8		opcode;
113fb81684fSAxel Dörfler 	uint8		hardware_type;
114fb81684fSAxel Dörfler 	uint8		hardware_address_length;
115fb81684fSAxel Dörfler 	uint8		hop_count;
116fb81684fSAxel Dörfler 	uint32		transaction_id;
11746ff5400SAxel Dörfler 	uint16		seconds_since_start;
118fb81684fSAxel Dörfler 	uint16		flags;
119fb81684fSAxel Dörfler 	in_addr_t	client_address;
120fb81684fSAxel Dörfler 	in_addr_t	your_address;
121fb81684fSAxel Dörfler 	in_addr_t	server_address;
122fb81684fSAxel Dörfler 	in_addr_t	gateway_address;
123fb81684fSAxel Dörfler 	uint8		mac_address[16];
124fb81684fSAxel Dörfler 	uint8		server_name[64];
125fb81684fSAxel Dörfler 	uint8		file[128];
126fb81684fSAxel Dörfler 	uint32		options_magic;
127fb81684fSAxel Dörfler 	uint8		options[1260];
128fb81684fSAxel Dörfler 
129fb81684fSAxel Dörfler 	size_t MinSize() const { return 576; }
130fb81684fSAxel Dörfler 	size_t Size() const;
131fb81684fSAxel Dörfler 
132fb81684fSAxel Dörfler 	bool HasOptions() const;
133fb81684fSAxel Dörfler 	bool NextOption(dhcp_option_cookie& cookie, message_option& option,
134fb81684fSAxel Dörfler 		const uint8*& data, size_t& size) const;
13503f6ab7fSPhilippe Houdoin 	const uint8* FindOption(message_option which) const;
136fb81684fSAxel Dörfler 	const uint8* LastOption() const;
13703f6ab7fSPhilippe Houdoin 	message_type Type() const;
138fb81684fSAxel Dörfler 
139a073ba1aSHugo Santos 	uint8* PrepareMessage(uint8 type);
140fb81684fSAxel Dörfler 	uint8* PutOption(uint8* options, message_option option);
141fb81684fSAxel Dörfler 	uint8* PutOption(uint8* options, message_option option, uint8 data);
142fb81684fSAxel Dörfler 	uint8* PutOption(uint8* options, message_option option, uint16 data);
143fb81684fSAxel Dörfler 	uint8* PutOption(uint8* options, message_option option, uint32 data);
14415ab0bcfSAxel Dörfler 	uint8* PutOption(uint8* options, message_option option, const uint8* data,
14515ab0bcfSAxel Dörfler 		uint32 size);
14637a68cb1SAxel Dörfler 	uint8* FinishOptions(uint8* options);
1474ac66051SAxel Dörfler 
1484ac66051SAxel Dörfler 	static const char* TypeToString(message_type type);
149fb81684fSAxel Dörfler } _PACKED;
150fb81684fSAxel Dörfler 
1510cf5e6acSAxel Dörfler #define DHCP_FLAG_BROADCAST		0x8000
152fb81684fSAxel Dörfler 
153fb81684fSAxel Dörfler #define ARP_HARDWARE_TYPE_ETHER	1
154fb81684fSAxel Dörfler 
1556cc7630fSAxel Dörfler const uint32 kMsgLeaseTime = 'lstm';
1566cc7630fSAxel Dörfler 
15737a68cb1SAxel Dörfler static const uint8 kRequestParameters[] = {
15865186fecSAxel Dörfler 	OPTION_SUBNET_MASK, OPTION_ROUTER_ADDRESS,
159bfd479b3SAxel Dörfler 	OPTION_DOMAIN_NAME_SERVER, OPTION_BROADCAST_ADDRESS,
160bfd479b3SAxel Dörfler 	OPTION_DOMAIN_NAME
1615d4d5313SHugo Santos };
1625d4d5313SHugo Santos 
163fb81684fSAxel Dörfler 
164fb81684fSAxel Dörfler dhcp_message::dhcp_message(message_type type)
165fb81684fSAxel Dörfler {
166fb4527d2SPhilippe Houdoin 	// ASSERT(this == offsetof(this, opcode));
167fb81684fSAxel Dörfler 	memset(this, 0, sizeof(*this));
168fb81684fSAxel Dörfler 	options_magic = htonl(OPTION_MAGIC);
169fb81684fSAxel Dörfler 
170a073ba1aSHugo Santos 	uint8* next = PrepareMessage(type);
171a073ba1aSHugo Santos 	FinishOptions(next);
172fb81684fSAxel Dörfler }
173fb81684fSAxel Dörfler 
174fb81684fSAxel Dörfler 
175fb81684fSAxel Dörfler bool
176fb81684fSAxel Dörfler dhcp_message::HasOptions() const
177fb81684fSAxel Dörfler {
178fb81684fSAxel Dörfler 	return options_magic == htonl(OPTION_MAGIC);
179fb81684fSAxel Dörfler }
180fb81684fSAxel Dörfler 
181fb81684fSAxel Dörfler 
182fb81684fSAxel Dörfler bool
183fb81684fSAxel Dörfler dhcp_message::NextOption(dhcp_option_cookie& cookie,
184fb81684fSAxel Dörfler 	message_option& option, const uint8*& data, size_t& size) const
185fb81684fSAxel Dörfler {
186fb81684fSAxel Dörfler 	if (!HasOptions())
187fb81684fSAxel Dörfler 		return false;
188fb81684fSAxel Dörfler 
189fb4527d2SPhilippe Houdoin 	if (cookie.state == 0) {
190fb81684fSAxel Dörfler 		cookie.state++;
191fb81684fSAxel Dörfler 		cookie.next = options;
192fb81684fSAxel Dörfler 	}
193fb81684fSAxel Dörfler 
194fb81684fSAxel Dörfler 	uint32 bytesLeft = 0;
195fb81684fSAxel Dörfler 
196fb81684fSAxel Dörfler 	switch (cookie.state) {
197fb81684fSAxel Dörfler 		case 1:
198ce36f054SPhilippe Houdoin 			// options from "options"
199fb4527d2SPhilippe Houdoin 			bytesLeft = sizeof(options) - (cookie.next - options);
200fb81684fSAxel Dörfler 			break;
201fb81684fSAxel Dörfler 
202fb81684fSAxel Dörfler 		case 2:
203fb81684fSAxel Dörfler 			// options from "file"
204fb4527d2SPhilippe Houdoin 			bytesLeft = sizeof(file) - (cookie.next - file);
205fb81684fSAxel Dörfler 			break;
206fb81684fSAxel Dörfler 
207fb81684fSAxel Dörfler 		case 3:
208fb81684fSAxel Dörfler 			// options from "server_name"
209fb4527d2SPhilippe Houdoin 			bytesLeft = sizeof(server_name) - (cookie.next - server_name);
210fb81684fSAxel Dörfler 			break;
211fb81684fSAxel Dörfler 	}
212fb81684fSAxel Dörfler 
213fb81684fSAxel Dörfler 	while (true) {
214fb81684fSAxel Dörfler 		if (bytesLeft == 0) {
215fb4527d2SPhilippe Houdoin 			cookie.state++;
216fb4527d2SPhilippe Houdoin 
217ce36f054SPhilippe Houdoin 			// handle option overload in file and/or server_name fields.
218fb4527d2SPhilippe Houdoin 			switch (cookie.state) {
219fb4527d2SPhilippe Houdoin 				case 2:
220d5d2fdf0SPhilippe Houdoin 					// options from "file" field
221fb4527d2SPhilippe Houdoin 					if (cookie.file_has_options) {
222fb4527d2SPhilippe Houdoin 						bytesLeft = sizeof(file);
223fb4527d2SPhilippe Houdoin 						cookie.next = file;
224fb4527d2SPhilippe Houdoin 					}
225fb4527d2SPhilippe Houdoin 					break;
226fb4527d2SPhilippe Houdoin 
227fb4527d2SPhilippe Houdoin 				case 3:
228d5d2fdf0SPhilippe Houdoin 					// options from "server_name" field
229fb4527d2SPhilippe Houdoin 					if (cookie.server_name_has_options) {
230fb4527d2SPhilippe Houdoin 						bytesLeft = sizeof(server_name);
231fb4527d2SPhilippe Houdoin 						cookie.next = server_name;
232fb4527d2SPhilippe Houdoin 					}
233fb4527d2SPhilippe Houdoin 					break;
234fb4527d2SPhilippe Houdoin 
235d5d2fdf0SPhilippe Houdoin 				default:
236ce36f054SPhilippe Houdoin 					// no more place to look for options
237d5d2fdf0SPhilippe Houdoin 					// if last option is not OPTION_END,
238d5d2fdf0SPhilippe Houdoin 					// there is no space left for other option!
239d5d2fdf0SPhilippe Houdoin 					if (option != OPTION_END)
240d5d2fdf0SPhilippe Houdoin 						cookie.next = NULL;
241fb81684fSAxel Dörfler 					return false;
242fb81684fSAxel Dörfler 			}
243fb81684fSAxel Dörfler 
244fb4527d2SPhilippe Houdoin 			if (bytesLeft == 0) {
245d5d2fdf0SPhilippe Houdoin 				// no options in this state, try next one
246fb4527d2SPhilippe Houdoin 				continue;
247fb4527d2SPhilippe Houdoin 			}
248fb4527d2SPhilippe Houdoin 		}
249fb4527d2SPhilippe Houdoin 
250fb81684fSAxel Dörfler 		option = (message_option)cookie.next[0];
251fb81684fSAxel Dörfler 		if (option == OPTION_END) {
252fb4527d2SPhilippe Houdoin 			bytesLeft = 0;
253fb4527d2SPhilippe Houdoin 			continue;
254fb81684fSAxel Dörfler 		} else if (option == OPTION_PAD) {
255fb81684fSAxel Dörfler 			bytesLeft--;
256fb81684fSAxel Dörfler 			cookie.next++;
257fb81684fSAxel Dörfler 			continue;
258fb81684fSAxel Dörfler 		}
259fb81684fSAxel Dörfler 
260fb81684fSAxel Dörfler 		size = cookie.next[1];
261fb81684fSAxel Dörfler 		data = &cookie.next[2];
262fb81684fSAxel Dörfler 		cookie.next += 2 + size;
263fb4527d2SPhilippe Houdoin 		bytesLeft -= 2 + size;
264fb81684fSAxel Dörfler 
265fb81684fSAxel Dörfler 		if (option == OPTION_OVERLOAD) {
266fb81684fSAxel Dörfler 			cookie.file_has_options = data[0] & 1;
267fb81684fSAxel Dörfler 			cookie.server_name_has_options = data[0] & 2;
268fb81684fSAxel Dörfler 			continue;
269fb81684fSAxel Dörfler 		}
270fb81684fSAxel Dörfler 
271fb81684fSAxel Dörfler 		return true;
272fb81684fSAxel Dörfler 	}
273fb81684fSAxel Dörfler }
274fb81684fSAxel Dörfler 
275fb81684fSAxel Dörfler 
27603f6ab7fSPhilippe Houdoin const uint8*
27703f6ab7fSPhilippe Houdoin dhcp_message::FindOption(message_option which) const
278f9af6566SAxel Dörfler {
279f9af6566SAxel Dörfler 	dhcp_option_cookie cookie;
280f9af6566SAxel Dörfler 	message_option option;
281f9af6566SAxel Dörfler 	const uint8* data;
282f9af6566SAxel Dörfler 	size_t size;
283f9af6566SAxel Dörfler 	while (NextOption(cookie, option, data, size)) {
284f9af6566SAxel Dörfler 		// iterate through all options
28503f6ab7fSPhilippe Houdoin 		if (option == which)
28603f6ab7fSPhilippe Houdoin 			return data;
287f9af6566SAxel Dörfler 	}
288f9af6566SAxel Dörfler 
28903f6ab7fSPhilippe Houdoin 	return NULL;
290f9af6566SAxel Dörfler }
291f9af6566SAxel Dörfler 
292f9af6566SAxel Dörfler 
293fb81684fSAxel Dörfler const uint8*
294fb81684fSAxel Dörfler dhcp_message::LastOption() const
295fb81684fSAxel Dörfler {
296fb81684fSAxel Dörfler 	dhcp_option_cookie cookie;
297fb81684fSAxel Dörfler 	message_option option;
298fb81684fSAxel Dörfler 	const uint8* data;
299fb81684fSAxel Dörfler 	size_t size;
300fb81684fSAxel Dörfler 	while (NextOption(cookie, option, data, size)) {
301fb81684fSAxel Dörfler 		// iterate through all options
302fb81684fSAxel Dörfler 	}
303fb81684fSAxel Dörfler 
304fb81684fSAxel Dörfler 	return cookie.next;
305fb81684fSAxel Dörfler }
306fb81684fSAxel Dörfler 
307fb81684fSAxel Dörfler 
30803f6ab7fSPhilippe Houdoin message_type
30903f6ab7fSPhilippe Houdoin dhcp_message::Type() const
31003f6ab7fSPhilippe Houdoin {
31103f6ab7fSPhilippe Houdoin 	const uint8* data = FindOption(OPTION_MESSAGE_TYPE);
31203f6ab7fSPhilippe Houdoin 	if (data)
31303f6ab7fSPhilippe Houdoin 		return (message_type)data[0];
31403f6ab7fSPhilippe Houdoin 
31503f6ab7fSPhilippe Houdoin 	return DHCP_NONE;
31603f6ab7fSPhilippe Houdoin }
31703f6ab7fSPhilippe Houdoin 
31803f6ab7fSPhilippe Houdoin 
319fb81684fSAxel Dörfler size_t
320fb81684fSAxel Dörfler dhcp_message::Size() const
321fb81684fSAxel Dörfler {
322fb81684fSAxel Dörfler 	const uint8* last = LastOption();
323d5d2fdf0SPhilippe Houdoin 
324d5d2fdf0SPhilippe Houdoin 	if (last < options) {
325d5d2fdf0SPhilippe Houdoin 		// if last option is stored above "options" field, it means
326d5d2fdf0SPhilippe Houdoin 		// the whole options field and message is already filled...
327d5d2fdf0SPhilippe Houdoin 		return sizeof(dhcp_message);
328d5d2fdf0SPhilippe Houdoin 	}
329d5d2fdf0SPhilippe Houdoin 
330fb81684fSAxel Dörfler 	return sizeof(dhcp_message) - sizeof(options) + last + 1 - options;
331fb81684fSAxel Dörfler }
332fb81684fSAxel Dörfler 
333fb81684fSAxel Dörfler 
334fb81684fSAxel Dörfler uint8*
335a073ba1aSHugo Santos dhcp_message::PrepareMessage(uint8 type)
336a073ba1aSHugo Santos {
337a073ba1aSHugo Santos 	uint8* next = options;
338a073ba1aSHugo Santos 	next = PutOption(next, OPTION_MESSAGE_TYPE, type);
339d5d2fdf0SPhilippe Houdoin 	next = PutOption(next, OPTION_MAX_MESSAGE_SIZE,
34015ab0bcfSAxel Dörfler 		(uint16)htons(sizeof(dhcp_message)));
341a073ba1aSHugo Santos 	return next;
342a073ba1aSHugo Santos }
343a073ba1aSHugo Santos 
34465186fecSAxel Dörfler 
345a073ba1aSHugo Santos uint8*
346fb81684fSAxel Dörfler dhcp_message::PutOption(uint8* options, message_option option)
347fb81684fSAxel Dörfler {
348fb81684fSAxel Dörfler 	options[0] = option;
349fb81684fSAxel Dörfler 	return options + 1;
350fb81684fSAxel Dörfler }
351fb81684fSAxel Dörfler 
352fb81684fSAxel Dörfler 
353fb81684fSAxel Dörfler uint8*
354fb81684fSAxel Dörfler dhcp_message::PutOption(uint8* options, message_option option, uint8 data)
355fb81684fSAxel Dörfler {
356fb81684fSAxel Dörfler 	return PutOption(options, option, &data, 1);
357fb81684fSAxel Dörfler }
358fb81684fSAxel Dörfler 
359fb81684fSAxel Dörfler 
360fb81684fSAxel Dörfler uint8*
361fb81684fSAxel Dörfler dhcp_message::PutOption(uint8* options, message_option option, uint16 data)
362fb81684fSAxel Dörfler {
363fb81684fSAxel Dörfler 	return PutOption(options, option, (uint8*)&data, sizeof(data));
364fb81684fSAxel Dörfler }
365fb81684fSAxel Dörfler 
366fb81684fSAxel Dörfler 
367fb81684fSAxel Dörfler uint8*
368fb81684fSAxel Dörfler dhcp_message::PutOption(uint8* options, message_option option, uint32 data)
369fb81684fSAxel Dörfler {
370fb81684fSAxel Dörfler 	return PutOption(options, option, (uint8*)&data, sizeof(data));
371fb81684fSAxel Dörfler }
372fb81684fSAxel Dörfler 
373fb81684fSAxel Dörfler 
374fb81684fSAxel Dörfler uint8*
37515ab0bcfSAxel Dörfler dhcp_message::PutOption(uint8* options, message_option option,
37615ab0bcfSAxel Dörfler 	const uint8* data, uint32 size)
377fb81684fSAxel Dörfler {
378d5d2fdf0SPhilippe Houdoin 	// TODO: check enough space is available
379d5d2fdf0SPhilippe Houdoin 
380fb81684fSAxel Dörfler 	options[0] = option;
381fb81684fSAxel Dörfler 	options[1] = size;
382fb81684fSAxel Dörfler 	memcpy(&options[2], data, size);
383fb81684fSAxel Dörfler 
384fb81684fSAxel Dörfler 	return options + 2 + size;
385fb81684fSAxel Dörfler }
386fb81684fSAxel Dörfler 
387fb81684fSAxel Dörfler 
388a073ba1aSHugo Santos uint8*
38937a68cb1SAxel Dörfler dhcp_message::FinishOptions(uint8* options)
390a073ba1aSHugo Santos {
39137a68cb1SAxel Dörfler 	return PutOption(options, OPTION_END);
392a073ba1aSHugo Santos }
393a073ba1aSHugo Santos 
394a073ba1aSHugo Santos 
3954ac66051SAxel Dörfler /*static*/ const char*
3964ac66051SAxel Dörfler dhcp_message::TypeToString(message_type type)
3974ac66051SAxel Dörfler {
3984ac66051SAxel Dörfler 	switch (type) {
3994ac66051SAxel Dörfler #define CASE(x) case x: return #x;
4004ac66051SAxel Dörfler 		CASE(DHCP_NONE)
4014ac66051SAxel Dörfler 		CASE(DHCP_DISCOVER)
4024ac66051SAxel Dörfler 		CASE(DHCP_OFFER)
4034ac66051SAxel Dörfler 		CASE(DHCP_REQUEST)
4044ac66051SAxel Dörfler 		CASE(DHCP_DECLINE)
4054ac66051SAxel Dörfler 		CASE(DHCP_ACK)
4064ac66051SAxel Dörfler 		CASE(DHCP_NACK)
4074ac66051SAxel Dörfler 		CASE(DHCP_RELEASE)
4084ac66051SAxel Dörfler 		CASE(DHCP_INFORM)
4094ac66051SAxel Dörfler #undef CASE
4104ac66051SAxel Dörfler 	}
4114ac66051SAxel Dörfler 
4124ac66051SAxel Dörfler 	return "<unknown>";
4134ac66051SAxel Dörfler }
4144ac66051SAxel Dörfler 
4154ac66051SAxel Dörfler 
416fb81684fSAxel Dörfler //	#pragma mark -
417fb81684fSAxel Dörfler 
418fb81684fSAxel Dörfler 
419f9af6566SAxel Dörfler DHCPClient::DHCPClient(BMessenger target, const char* device)
420af074561SAxel Dörfler 	:
421af074561SAxel Dörfler 	AutoconfigClient("dhcp", target, device),
4226cc7630fSAxel Dörfler 	fConfiguration(kMsgConfigureInterface),
42316e8f13aSAxel Dörfler 	fResolverConfiguration(kMsgConfigureResolver),
4246cc7630fSAxel Dörfler 	fRunner(NULL),
4250892800fSPhilippe Houdoin 	fAssignedAddress(0),
426af074561SAxel Dörfler 	fServer(AF_INET, NULL, DHCP_SERVER_PORT),
427c1264353SStephan Aßmus 	fLeaseTime(0)
428fb81684fSAxel Dörfler {
42946ff5400SAxel Dörfler 	fStartTime = system_time();
43046ff5400SAxel Dörfler 	fTransactionID = (uint32)fStartTime;
431fb81684fSAxel Dörfler 
432*7ca2da76SFredrik Holmqvist 	srand(fTransactionID);
433*7ca2da76SFredrik Holmqvist 
434af074561SAxel Dörfler 	BNetworkAddress link;
435af074561SAxel Dörfler 	BNetworkInterface interface(device);
436af074561SAxel Dörfler 	fStatus = interface.GetHardwareAddress(link);
437af074561SAxel Dörfler 	if (fStatus != B_OK)
438fb81684fSAxel Dörfler 		return;
439fb81684fSAxel Dörfler 
440af074561SAxel Dörfler 	memcpy(fMAC, link.LinkLevelAddress(), sizeof(fMAC));
441293ed4feSAxel Dörfler 
4420892800fSPhilippe Houdoin 	if ((interface.Flags() & IFF_AUTO_CONFIGURED) != 0) {
4430892800fSPhilippe Houdoin 		// Check for interface previous auto-configured address, if any.
4440892800fSPhilippe Houdoin 		BNetworkInterfaceAddress interfaceAddress;
4450892800fSPhilippe Houdoin 		int index = interface.FindFirstAddress(AF_INET);
4460892800fSPhilippe Houdoin 		if (index >= 0
4470892800fSPhilippe Houdoin 			&& interface.GetAddressAt(index, interfaceAddress) == B_OK) {
4480892800fSPhilippe Houdoin 			BNetworkAddress address = interfaceAddress.Address();
4490892800fSPhilippe Houdoin 			const sockaddr_in& addr = (sockaddr_in&)address.SockAddr();
4500892800fSPhilippe Houdoin 			fAssignedAddress = addr.sin_addr.s_addr;
451b6ba1daaSPhilippe Houdoin 
452b6ba1daaSPhilippe Houdoin 			if ((ntohl(fAssignedAddress) & IN_CLASSB_NET) == 0xa9fe0000) {
453b6ba1daaSPhilippe Houdoin 				// previous auto-configured address is a link-local one:
454b6ba1daaSPhilippe Houdoin 				// there is no point asking a DHCP server to renew such
455b6ba1daaSPhilippe Houdoin 				// server-less assigned address...
456b6ba1daaSPhilippe Houdoin 				fAssignedAddress = 0;
457b6ba1daaSPhilippe Houdoin 			}
4580892800fSPhilippe Houdoin 		}
4590892800fSPhilippe Houdoin 	}
4600892800fSPhilippe Houdoin 
461293ed4feSAxel Dörfler 	openlog_thread("DHCP", 0, LOG_DAEMON);
4626cc7630fSAxel Dörfler }
4636cc7630fSAxel Dörfler 
4646cc7630fSAxel Dörfler 
4656cc7630fSAxel Dörfler DHCPClient::~DHCPClient()
4666cc7630fSAxel Dörfler {
4676cc7630fSAxel Dörfler 	if (fStatus != B_OK)
4686cc7630fSAxel Dörfler 		return;
4696cc7630fSAxel Dörfler 
4706cc7630fSAxel Dörfler 	delete fRunner;
4710ce7725eSAxel Dörfler 
472fb81684fSAxel Dörfler 	int socket = ::socket(AF_INET, SOCK_DGRAM, 0);
4736cc7630fSAxel Dörfler 	if (socket < 0)
474fb81684fSAxel Dörfler 		return;
4756cc7630fSAxel Dörfler 
4766cc7630fSAxel Dörfler 	// release lease
4776cc7630fSAxel Dörfler 
4786cc7630fSAxel Dörfler 	dhcp_message release(DHCP_RELEASE);
47946ff5400SAxel Dörfler 	_PrepareMessage(release, BOUND);
4806cc7630fSAxel Dörfler 
4816cc7630fSAxel Dörfler 	_SendMessage(socket, release, fServer);
4826cc7630fSAxel Dörfler 	close(socket);
483293ed4feSAxel Dörfler 
484293ed4feSAxel Dörfler 	closelog_thread();
485fb81684fSAxel Dörfler }
486fb81684fSAxel Dörfler 
4876cc7630fSAxel Dörfler 
4886cc7630fSAxel Dörfler status_t
4896cc7630fSAxel Dörfler DHCPClient::Initialize()
4906cc7630fSAxel Dörfler {
4910892800fSPhilippe Houdoin 	fStatus = _Negotiate(fAssignedAddress == 0 ? INIT : INIT_REBOOT);
4920892800fSPhilippe Houdoin 	syslog(LOG_DEBUG, "%s: DHCP status = %s\n", Device(), strerror(fStatus));
4936cc7630fSAxel Dörfler 	return fStatus;
4946cc7630fSAxel Dörfler }
4956cc7630fSAxel Dörfler 
4966cc7630fSAxel Dörfler 
4976cc7630fSAxel Dörfler status_t
4986cc7630fSAxel Dörfler DHCPClient::_Negotiate(dhcp_state state)
4996cc7630fSAxel Dörfler {
5006cc7630fSAxel Dörfler 	int socket = ::socket(AF_INET, SOCK_DGRAM, 0);
5016cc7630fSAxel Dörfler 	if (socket < 0)
5026cc7630fSAxel Dörfler 		return errno;
5036cc7630fSAxel Dörfler 
504fb4527d2SPhilippe Houdoin 	BNetworkAddress local;
505fb4527d2SPhilippe Houdoin 	local.SetToWildcard(AF_INET, DHCP_CLIENT_PORT);
506fb81684fSAxel Dörfler 
5072b829b04SBruno G. Albuquerque 	// Enable reusing the port. This is needed in case there is more
5082b829b04SBruno G. Albuquerque 	// than 1 interface that needs to be configured. Note that the only reason
5092b829b04SBruno G. Albuquerque 	// this works is because there is code below to bind to a specific
5102b829b04SBruno G. Albuquerque 	// interface.
5112b829b04SBruno G. Albuquerque 	int option = 1;
5122b829b04SBruno G. Albuquerque 	setsockopt(socket, SOL_SOCKET, SO_REUSEPORT, &option, sizeof(option));
5132b829b04SBruno G. Albuquerque 
514af074561SAxel Dörfler 	if (bind(socket, local, local.Length()) < 0) {
515fb81684fSAxel Dörfler 		close(socket);
5166cc7630fSAxel Dörfler 		return errno;
517fb81684fSAxel Dörfler 	}
518fb81684fSAxel Dörfler 
519af074561SAxel Dörfler 	BNetworkAddress broadcast;
520af074561SAxel Dörfler 	broadcast.SetToBroadcast(AF_INET, DHCP_SERVER_PORT);
521fb81684fSAxel Dörfler 
5222b829b04SBruno G. Albuquerque 	option = 1;
523fb81684fSAxel Dörfler 	setsockopt(socket, SOL_SOCKET, SO_BROADCAST, &option, sizeof(option));
524fb81684fSAxel Dörfler 
52536bde12dSAxel Dörfler 	if (state == INIT) {
52636bde12dSAxel Dörfler 		// The local interface does not have an address yet, bind the socket
52736bde12dSAxel Dörfler 		// to the device directly.
528af074561SAxel Dörfler 		BNetworkDevice device(Device());
529af074561SAxel Dörfler 		int index = device.Index();
53036bde12dSAxel Dörfler 
531af074561SAxel Dörfler 		setsockopt(socket, SOL_SOCKET, SO_BINDTODEVICE, &index, sizeof(int));
53236bde12dSAxel Dörfler 	}
53336bde12dSAxel Dörfler 
5346cc7630fSAxel Dörfler 	bigtime_t previousLeaseTime = fLeaseTime;
5356cc7630fSAxel Dörfler 	fLeaseTime = 0;
53646ff5400SAxel Dörfler 	fRenewalTime = 0;
53746ff5400SAxel Dörfler 	fRebindingTime = 0;
538fb81684fSAxel Dörfler 
5396cc7630fSAxel Dörfler 	status_t status = B_ERROR;
5406cc7630fSAxel Dörfler 	time_t timeout;
5416cc7630fSAxel Dörfler 	uint32 tries;
5426cc7630fSAxel Dörfler 	_ResetTimeout(socket, timeout, tries);
5436cc7630fSAxel Dörfler 
5446cc7630fSAxel Dörfler 	dhcp_message discover(DHCP_DISCOVER);
54546ff5400SAxel Dörfler 	_PrepareMessage(discover, state);
5466cc7630fSAxel Dörfler 
5476cc7630fSAxel Dörfler 	dhcp_message request(DHCP_REQUEST);
54846ff5400SAxel Dörfler 	_PrepareMessage(request, state);
5496cc7630fSAxel Dörfler 
55046ff5400SAxel Dörfler 	// send discover/request message
551593a0d2dSAxel Dörfler 	_SendMessage(socket, state == INIT ? discover : request,
5520892800fSPhilippe Houdoin 		state != RENEWING ? broadcast : fServer);
553593a0d2dSAxel Dörfler 		// no need to check the status; in case of an error we'll just send
554593a0d2dSAxel Dörfler 		// the message again
555fb81684fSAxel Dörfler 
556f9af6566SAxel Dörfler 	// receive loop until we've got an offer and acknowledged it
557f9af6566SAxel Dörfler 
5580892800fSPhilippe Houdoin 	while (state != BOUND) {
559f9af6566SAxel Dörfler 		char buffer[2048];
56069b5cacbSPhilippe Houdoin 		struct sockaddr_in from;
56169b5cacbSPhilippe Houdoin 		socklen_t fromLength = sizeof(from);
562f9af6566SAxel Dörfler 		ssize_t bytesReceived = recvfrom(socket, buffer, sizeof(buffer),
56369b5cacbSPhilippe Houdoin 			0, (struct sockaddr*)&from, &fromLength);
5644b661a95SAxel Dörfler 		if (bytesReceived < 0 && errno == B_TIMED_OUT) {
565f9af6566SAxel Dörfler 			// depending on the state, we'll just try again
5666cc7630fSAxel Dörfler 			if (!_TimeoutShift(socket, timeout, tries)) {
5676cc7630fSAxel Dörfler 				close(socket);
5686cc7630fSAxel Dörfler 				return B_TIMED_OUT;
569f9af6566SAxel Dörfler 			}
570f9af6566SAxel Dörfler 
5710892800fSPhilippe Houdoin 			_SendMessage(socket, state == INIT ? discover : request,
5720892800fSPhilippe Houdoin 				state != RENEWING ? broadcast : fServer);
573d77fe260SAxel Dörfler 			continue;
574b6ba1daaSPhilippe Houdoin 
575593a0d2dSAxel Dörfler 		} else if (bytesReceived < 0)
576f9af6566SAxel Dörfler 			break;
577f9af6566SAxel Dörfler 
578f9af6566SAxel Dörfler 		dhcp_message* message = (dhcp_message*)buffer;
579f9af6566SAxel Dörfler 		if (message->transaction_id != htonl(fTransactionID)
580f9af6566SAxel Dörfler 			|| !message->HasOptions()
581f9af6566SAxel Dörfler 			|| memcmp(message->mac_address, discover.mac_address,
582f9af6566SAxel Dörfler 				discover.hardware_address_length)) {
583f9af6566SAxel Dörfler 			// this message is not for us
584f9af6566SAxel Dörfler 			continue;
585f9af6566SAxel Dörfler 		}
586f9af6566SAxel Dörfler 
5870892800fSPhilippe Houdoin 		// advance from startup state
5880892800fSPhilippe Houdoin 		if (state == INIT)
5890892800fSPhilippe Houdoin 			state = SELECTING;
5900892800fSPhilippe Houdoin 		else if (state == INIT_REBOOT)
5910892800fSPhilippe Houdoin 			state = REBOOTING;
5920892800fSPhilippe Houdoin 
5930892800fSPhilippe Houdoin 		syslog(LOG_DEBUG, "%s: Received %s from %s\n",
5940892800fSPhilippe Houdoin 			Device(), dhcp_message::TypeToString(message->Type()),
5950892800fSPhilippe Houdoin 			_AddressToString(from.sin_addr.s_addr).String());
5964ac66051SAxel Dörfler 
597f9af6566SAxel Dörfler 		switch (message->Type()) {
598f9af6566SAxel Dörfler 			case DHCP_NONE:
599f9af6566SAxel Dörfler 			default:
600f9af6566SAxel Dörfler 				// ignore this message
601f9af6566SAxel Dörfler 				break;
602f9af6566SAxel Dörfler 
603f9af6566SAxel Dörfler 			case DHCP_OFFER:
604f9af6566SAxel Dörfler 			{
605f9af6566SAxel Dörfler 				// first offer wins
6060892800fSPhilippe Houdoin 				if (state != SELECTING)
607f9af6566SAxel Dörfler 					break;
608f9af6566SAxel Dörfler 
609f9af6566SAxel Dörfler 				// collect interface options
610f9af6566SAxel Dörfler 
611f9af6566SAxel Dörfler 				fAssignedAddress = message->your_address;
61269b5cacbSPhilippe Houdoin 				syslog(LOG_INFO, "  your_address: %s\n",
61369b5cacbSPhilippe Houdoin 						_AddressToString(fAssignedAddress).String());
614f9af6566SAxel Dörfler 
61510cc12daSAxel Dörfler 				fConfiguration.MakeEmpty();
616293ed4feSAxel Dörfler 				fConfiguration.AddString("device", Device());
6170892800fSPhilippe Houdoin 				fConfiguration.AddBool("auto_configured", true);
618f9af6566SAxel Dörfler 
619f9af6566SAxel Dörfler 				BMessage address;
620f9af6566SAxel Dörfler 				address.AddString("family", "inet");
621d5d2fdf0SPhilippe Houdoin 				address.AddString("address", _AddressToString(fAssignedAddress));
62216e8f13aSAxel Dörfler 				fResolverConfiguration.MakeEmpty();
62316e8f13aSAxel Dörfler 				_ParseOptions(*message, address, fResolverConfiguration);
624f9af6566SAxel Dörfler 
62510cc12daSAxel Dörfler 				fConfiguration.AddMessage("address", &address);
626f9af6566SAxel Dörfler 
62710cc12daSAxel Dörfler 				// request configuration from the server
628f9af6566SAxel Dörfler 
6296cc7630fSAxel Dörfler 				_ResetTimeout(socket, timeout, tries);
630f9af6566SAxel Dörfler 				state = REQUESTING;
63146ff5400SAxel Dörfler 				_PrepareMessage(request, state);
632f9af6566SAxel Dörfler 
6336cc7630fSAxel Dörfler 				status = _SendMessage(socket, request, broadcast);
63415ab0bcfSAxel Dörfler 					// we're sending a broadcast so that all potential offers
63515ab0bcfSAxel Dörfler 					// get an answer
63610cc12daSAxel Dörfler 				break;
637f9af6566SAxel Dörfler 			}
638f9af6566SAxel Dörfler 
639f9af6566SAxel Dörfler 			case DHCP_ACK:
64010cc12daSAxel Dörfler 			{
6410892800fSPhilippe Houdoin 				if (state != REQUESTING
6420892800fSPhilippe Houdoin 					&& state != REBOOTING
6430892800fSPhilippe Houdoin 					&& state != REBINDING
6440892800fSPhilippe Houdoin 					&& state != RENEWING)
645f9af6566SAxel Dörfler 					continue;
646f9af6566SAxel Dörfler 
6476cc7630fSAxel Dörfler 				// TODO: we might want to configure the stuff, don't we?
6486cc7630fSAxel Dörfler 				BMessage address;
64916e8f13aSAxel Dörfler 				fResolverConfiguration.MakeEmpty();
65016e8f13aSAxel Dörfler 				_ParseOptions(*message, address, fResolverConfiguration);
6514ac66051SAxel Dörfler 					// TODO: currently, only lease time and DNS is updated this
6524ac66051SAxel Dörfler 					// way
6536cc7630fSAxel Dörfler 
654f9af6566SAxel Dörfler 				// our address request has been acknowledged
6550892800fSPhilippe Houdoin 				state = BOUND;
65610cc12daSAxel Dörfler 
65710cc12daSAxel Dörfler 				// configure interface
65810cc12daSAxel Dörfler 				BMessage reply;
659d23fdd96SOliver Tappe 				status = Target().SendMessage(&fConfiguration, &reply);
660d23fdd96SOliver Tappe 				if (status == B_OK)
661d23fdd96SOliver Tappe 					status = reply.FindInt32("status", &fStatus);
66216e8f13aSAxel Dörfler 
66316e8f13aSAxel Dörfler 				// configure resolver
66416e8f13aSAxel Dörfler 				reply.MakeEmpty();
665d5d2fdf0SPhilippe Houdoin 				fResolverConfiguration.AddString("device", Device());
66616e8f13aSAxel Dörfler 				status = Target().SendMessage(&fResolverConfiguration, &reply);
66716e8f13aSAxel Dörfler 				if (status == B_OK)
66816e8f13aSAxel Dörfler 					status = reply.FindInt32("status", &fStatus);
669f9af6566SAxel Dörfler 				break;
67010cc12daSAxel Dörfler 			}
671f9af6566SAxel Dörfler 
672f9af6566SAxel Dörfler 			case DHCP_NACK:
6730892800fSPhilippe Houdoin 				if (state != REQUESTING
6740892800fSPhilippe Houdoin 					&& state != REBOOTING
6750892800fSPhilippe Houdoin 					&& state != REBINDING
6760892800fSPhilippe Houdoin 					&& state != RENEWING)
677f9af6566SAxel Dörfler 					continue;
678f9af6566SAxel Dörfler 
6790892800fSPhilippe Houdoin 				if (state == REBOOTING) {
6800892800fSPhilippe Houdoin 					// server reject our request on previous assigned address
6810892800fSPhilippe Houdoin 					// back to square one...
6820892800fSPhilippe Houdoin 					fAssignedAddress = 0;
6830892800fSPhilippe Houdoin 				}
6840892800fSPhilippe Houdoin 
68515ab0bcfSAxel Dörfler 				// try again (maybe we should prefer other servers if this
68615ab0bcfSAxel Dörfler 				// happens more than once)
6876cc7630fSAxel Dörfler 				status = _SendMessage(socket, discover, broadcast);
6886cc7630fSAxel Dörfler 				if (status == B_OK)
689f9af6566SAxel Dörfler 					state = INIT;
690f9af6566SAxel Dörfler 				break;
691f9af6566SAxel Dörfler 		}
692f9af6566SAxel Dörfler 	}
693f9af6566SAxel Dörfler 
694fb81684fSAxel Dörfler 	close(socket);
6956cc7630fSAxel Dörfler 
6966cc7630fSAxel Dörfler 	if (status == B_OK && fLeaseTime > 0) {
6976cc7630fSAxel Dörfler 		// notify early enough when the lease is
69846ff5400SAxel Dörfler 		if (fRenewalTime == 0)
69946ff5400SAxel Dörfler 			fRenewalTime = fLeaseTime * 2/3;
70046ff5400SAxel Dörfler 		if (fRebindingTime == 0)
70146ff5400SAxel Dörfler 			fRebindingTime = fLeaseTime * 5/6;
70246ff5400SAxel Dörfler 
703168cc3dfSRene Gollent 		bigtime_t now = system_time();
70446ff5400SAxel Dörfler 		_RestartLease(fRenewalTime);
70546ff5400SAxel Dörfler 
70646ff5400SAxel Dörfler 		fLeaseTime += now;
70746ff5400SAxel Dörfler 		fRenewalTime += now;
70846ff5400SAxel Dörfler 		fRebindingTime += now;
70946ff5400SAxel Dörfler 			// make lease times absolute
71046ff5400SAxel Dörfler 	} else {
7116cc7630fSAxel Dörfler 		fLeaseTime = previousLeaseTime;
71246ff5400SAxel Dörfler 		bigtime_t now = system_time();
71346ff5400SAxel Dörfler 		fRenewalTime = (fLeaseTime - now) * 2/3 + now;
71446ff5400SAxel Dörfler 		fRebindingTime = (fLeaseTime - now) * 5/6 + now;
71546ff5400SAxel Dörfler 	}
7166cc7630fSAxel Dörfler 
7176cc7630fSAxel Dörfler 	return status;
718fb81684fSAxel Dörfler }
719fb81684fSAxel Dörfler 
720fb81684fSAxel Dörfler 
7216cc7630fSAxel Dörfler void
7226cc7630fSAxel Dörfler DHCPClient::_RestartLease(bigtime_t leaseTime)
723fb81684fSAxel Dörfler {
7246cc7630fSAxel Dörfler 	if (leaseTime == 0)
725f9af6566SAxel Dörfler 		return;
726f9af6566SAxel Dörfler 
7276cc7630fSAxel Dörfler 	BMessage lease(kMsgLeaseTime);
72846ff5400SAxel Dörfler 	fRunner = new BMessageRunner(this, &lease, leaseTime, 1);
729f9af6566SAxel Dörfler }
730f9af6566SAxel Dörfler 
731f9af6566SAxel Dörfler 
732f9af6566SAxel Dörfler void
73316e8f13aSAxel Dörfler DHCPClient::_ParseOptions(dhcp_message& message, BMessage& address,
73416e8f13aSAxel Dörfler 	BMessage& resolverConfiguration)
7350ce7725eSAxel Dörfler {
7360ce7725eSAxel Dörfler 	dhcp_option_cookie cookie;
7370ce7725eSAxel Dörfler 	message_option option;
7380ce7725eSAxel Dörfler 	const uint8* data;
7390ce7725eSAxel Dörfler 	size_t size;
7400ce7725eSAxel Dörfler 	while (message.NextOption(cookie, option, data, size)) {
7410ce7725eSAxel Dörfler 		// iterate through all options
7420ce7725eSAxel Dörfler 		switch (option) {
7430ce7725eSAxel Dörfler 			case OPTION_ROUTER_ADDRESS:
744cefe2a40SPhilippe Houdoin 				syslog(LOG_DEBUG, "  gateway: %s\n",
74569b5cacbSPhilippe Houdoin 					_AddressToString(data).String());
746d5d2fdf0SPhilippe Houdoin 				address.AddString("gateway", _AddressToString(data));
7470ce7725eSAxel Dörfler 				break;
7480ce7725eSAxel Dörfler 			case OPTION_SUBNET_MASK:
749cefe2a40SPhilippe Houdoin 				syslog(LOG_DEBUG, "  subnet: %s\n",
75069b5cacbSPhilippe Houdoin 					_AddressToString(data).String());
751d5d2fdf0SPhilippe Houdoin 				address.AddString("mask", _AddressToString(data));
7520ce7725eSAxel Dörfler 				break;
7535782c5a3SAxel Dörfler 			case OPTION_BROADCAST_ADDRESS:
754cefe2a40SPhilippe Houdoin 				syslog(LOG_DEBUG, "  broadcast: %s\n",
75569b5cacbSPhilippe Houdoin 					_AddressToString(data).String());
756d5d2fdf0SPhilippe Houdoin 				address.AddString("broadcast", _AddressToString(data));
7575782c5a3SAxel Dörfler 				break;
7580ce7725eSAxel Dörfler 			case OPTION_DOMAIN_NAME_SERVER:
759a552ec13SAxel Dörfler 			{
7600ce7725eSAxel Dörfler 				for (uint32 i = 0; i < size / 4; i++) {
761cefe2a40SPhilippe Houdoin 					syslog(LOG_DEBUG, "  nameserver[%d]: %s\n", i,
762d5d2fdf0SPhilippe Houdoin 						_AddressToString(&data[i * 4]).String());
76316e8f13aSAxel Dörfler 					resolverConfiguration.AddString("nameserver",
764d5d2fdf0SPhilippe Houdoin 						_AddressToString(&data[i * 4]).String());
76515ab0bcfSAxel Dörfler 				}
76616e8f13aSAxel Dörfler 				resolverConfiguration.AddInt32("nameserver_count",
76716e8f13aSAxel Dörfler 					size / 4);
7680ce7725eSAxel Dörfler 				break;
769a552ec13SAxel Dörfler 			}
770cefe2a40SPhilippe Houdoin 			case OPTION_SERVER_ADDRESS:
771cefe2a40SPhilippe Houdoin 				syslog(LOG_DEBUG, "  server: %s\n",
77269b5cacbSPhilippe Houdoin 					_AddressToString(data).String());
773ddf57b6cSAxel Dörfler 				fServer.SetAddress(*(in_addr_t*)data);
7740ce7725eSAxel Dörfler 				break;
77546ff5400SAxel Dörfler 
7760ce7725eSAxel Dörfler 			case OPTION_ADDRESS_LEASE_TIME:
777cefe2a40SPhilippe Houdoin 				syslog(LOG_DEBUG, "  lease time: %lu seconds\n",
77869b5cacbSPhilippe Houdoin 					ntohl(*(uint32*)data));
77969b5cacbSPhilippe Houdoin 				fLeaseTime = ntohl(*(uint32*)data) * 1000000LL;
7800ce7725eSAxel Dörfler 				break;
7811a4e8e7bSAxel Dörfler 			case OPTION_RENEWAL_TIME:
782cefe2a40SPhilippe Houdoin 				syslog(LOG_DEBUG, "  renewal time: %lu seconds\n",
78369b5cacbSPhilippe Houdoin 					ntohl(*(uint32*)data));
78469b5cacbSPhilippe Houdoin 				fRenewalTime = ntohl(*(uint32*)data) * 1000000LL;
78546ff5400SAxel Dörfler 				break;
7861a4e8e7bSAxel Dörfler 			case OPTION_REBINDING_TIME:
787cefe2a40SPhilippe Houdoin 				syslog(LOG_DEBUG, "  rebinding time: %lu seconds\n",
78869b5cacbSPhilippe Houdoin 					ntohl(*(uint32*)data));
78969b5cacbSPhilippe Houdoin 				fRebindingTime = ntohl(*(uint32*)data) * 1000000LL;
7901a4e8e7bSAxel Dörfler 				break;
7911a4e8e7bSAxel Dörfler 
7920ce7725eSAxel Dörfler 			case OPTION_HOST_NAME:
793cefe2a40SPhilippe Houdoin 				syslog(LOG_DEBUG, "  host name: \"%.*s\"\n", (int)size,
79416e8f13aSAxel Dörfler 					(const char*)data);
7950ce7725eSAxel Dörfler 				break;
7965782c5a3SAxel Dörfler 
7975782c5a3SAxel Dörfler 			case OPTION_DOMAIN_NAME:
7985782c5a3SAxel Dörfler 			{
79916e8f13aSAxel Dörfler 				char domain[256];
80016e8f13aSAxel Dörfler 				strlcpy(domain, (const char*)data,
80116e8f13aSAxel Dörfler 					min_c(size + 1, sizeof(domain)));
8020f87f52fSStephan Aßmus 
803cefe2a40SPhilippe Houdoin 				syslog(LOG_DEBUG, "  domain name: \"%s\"\n", domain);
8040f87f52fSStephan Aßmus 
80516e8f13aSAxel Dörfler 				resolverConfiguration.AddString("domain", domain);
8065782c5a3SAxel Dörfler 				break;
8075782c5a3SAxel Dörfler 			}
8080ce7725eSAxel Dörfler 
8090ce7725eSAxel Dörfler 			case OPTION_MESSAGE_TYPE:
8100ce7725eSAxel Dörfler 				break;
8110ce7725eSAxel Dörfler 
812d5d2fdf0SPhilippe Houdoin 			case OPTION_ERROR_MESSAGE:
81369b5cacbSPhilippe Houdoin 				syslog(LOG_INFO, "  error message: \"%.*s\"\n", (int)size,
814d5d2fdf0SPhilippe Houdoin 					(const char*)data);
815d5d2fdf0SPhilippe Houdoin 				break;
816d5d2fdf0SPhilippe Houdoin 
8170ce7725eSAxel Dörfler 			default:
818cefe2a40SPhilippe Houdoin 				syslog(LOG_DEBUG, "  UNKNOWN OPTION %lu (0x%x)\n",
81969b5cacbSPhilippe Houdoin 					(uint32)option, (uint32)option);
8200ce7725eSAxel Dörfler 				break;
8210ce7725eSAxel Dörfler 		}
8220ce7725eSAxel Dörfler 	}
8230ce7725eSAxel Dörfler }
8240ce7725eSAxel Dörfler 
8250ce7725eSAxel Dörfler 
8260ce7725eSAxel Dörfler void
82746ff5400SAxel Dörfler DHCPClient::_PrepareMessage(dhcp_message& message, dhcp_state state)
8280ce7725eSAxel Dörfler {
8290ce7725eSAxel Dörfler 	message.opcode = BOOT_REQUEST;
8300ce7725eSAxel Dörfler 	message.hardware_type = ARP_HARDWARE_TYPE_ETHER;
8310ce7725eSAxel Dörfler 	message.hardware_address_length = 6;
8320ce7725eSAxel Dörfler 	message.transaction_id = htonl(fTransactionID);
833bcc8dadaSAxel Dörfler 	message.seconds_since_start = htons(min_c((system_time() - fStartTime)
83415ab0bcfSAxel Dörfler 		/ 1000000LL, 65535));
8350ce7725eSAxel Dörfler 	memcpy(message.mac_address, fMAC, 6);
8360ce7725eSAxel Dörfler 
83746ff5400SAxel Dörfler 	message_type type = message.Type();
83846ff5400SAxel Dörfler 
839a073ba1aSHugo Santos 	uint8 *next = message.PrepareMessage(type);
840d5d2fdf0SPhilippe Houdoin 
841d5d2fdf0SPhilippe Houdoin 	switch (type) {
842d5d2fdf0SPhilippe Houdoin 		case DHCP_DISCOVER:
843d5d2fdf0SPhilippe Houdoin 			next = message.PutOption(next, OPTION_REQUEST_PARAMETERS,
844d5d2fdf0SPhilippe Houdoin 				kRequestParameters, sizeof(kRequestParameters));
845d5d2fdf0SPhilippe Houdoin 			break;
846d5d2fdf0SPhilippe Houdoin 
847d5d2fdf0SPhilippe Houdoin 		case DHCP_REQUEST:
848d5d2fdf0SPhilippe Houdoin 			next = message.PutOption(next, OPTION_REQUEST_PARAMETERS,
849d5d2fdf0SPhilippe Houdoin 				kRequestParameters, sizeof(kRequestParameters));
850d5d2fdf0SPhilippe Houdoin 
851d5d2fdf0SPhilippe Houdoin 			if (state == REQUESTING) {
852d5d2fdf0SPhilippe Houdoin 				const sockaddr_in& server = (sockaddr_in&)fServer.SockAddr();
853cefe2a40SPhilippe Houdoin 				next = message.PutOption(next, OPTION_SERVER_ADDRESS,
854d5d2fdf0SPhilippe Houdoin 					(uint32)server.sin_addr.s_addr);
855d5d2fdf0SPhilippe Houdoin 			}
856d5d2fdf0SPhilippe Houdoin 
8570892800fSPhilippe Houdoin 			if (state == INIT || state == INIT_REBOOT
8580892800fSPhilippe Houdoin 				|| state == REQUESTING) {
859d5d2fdf0SPhilippe Houdoin 				next = message.PutOption(next, OPTION_REQUEST_IP_ADDRESS,
860d5d2fdf0SPhilippe Houdoin 					(uint32)fAssignedAddress);
861d5d2fdf0SPhilippe Houdoin 			} else
862d5d2fdf0SPhilippe Houdoin 				message.client_address = fAssignedAddress;
863d5d2fdf0SPhilippe Houdoin 			break;
864d5d2fdf0SPhilippe Houdoin 
865d5d2fdf0SPhilippe Houdoin 		case DHCP_RELEASE: {
866d5d2fdf0SPhilippe Houdoin 			const sockaddr_in& server = (sockaddr_in&)fServer.SockAddr();
867cefe2a40SPhilippe Houdoin 			next = message.PutOption(next, OPTION_SERVER_ADDRESS,
868af074561SAxel Dörfler 				(uint32)server.sin_addr.s_addr);
86946ff5400SAxel Dörfler 
87046ff5400SAxel Dörfler 			message.client_address = fAssignedAddress;
8710ce7725eSAxel Dörfler 			break;
8720ce7725eSAxel Dörfler 		}
8730ce7725eSAxel Dörfler 
8740ce7725eSAxel Dörfler 		default:
8750ce7725eSAxel Dörfler 			break;
8760ce7725eSAxel Dörfler 	}
877d5d2fdf0SPhilippe Houdoin 
878d5d2fdf0SPhilippe Houdoin 	message.FinishOptions(next);
8790ce7725eSAxel Dörfler }
8800ce7725eSAxel Dörfler 
8810ce7725eSAxel Dörfler 
8820ce7725eSAxel Dörfler void
8836cc7630fSAxel Dörfler DHCPClient::_ResetTimeout(int socket, time_t& timeout, uint32& tries)
884f9af6566SAxel Dörfler {
8856cc7630fSAxel Dörfler 	timeout = DEFAULT_TIMEOUT;
8866cc7630fSAxel Dörfler 	tries = 0;
887f9af6566SAxel Dörfler 
888f9af6566SAxel Dörfler 	struct timeval value;
8896cc7630fSAxel Dörfler 	value.tv_sec = timeout;
890d09c1f8eSPhilippe Houdoin 	value.tv_usec = rand() % 1000000;
891f9af6566SAxel Dörfler 	setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &value, sizeof(value));
892f9af6566SAxel Dörfler }
893f9af6566SAxel Dörfler 
894f9af6566SAxel Dörfler 
895f9af6566SAxel Dörfler bool
8966cc7630fSAxel Dörfler DHCPClient::_TimeoutShift(int socket, time_t& timeout, uint32& tries)
897f9af6566SAxel Dörfler {
8986cc7630fSAxel Dörfler 	timeout += timeout;
8996cc7630fSAxel Dörfler 	if (timeout > MAX_TIMEOUT) {
9006cc7630fSAxel Dörfler 		timeout = DEFAULT_TIMEOUT;
901f9af6566SAxel Dörfler 
9026cc7630fSAxel Dörfler 		if (++tries > 2)
903f9af6566SAxel Dörfler 			return false;
904f9af6566SAxel Dörfler 	}
9050892800fSPhilippe Houdoin 	syslog(LOG_DEBUG, "%s: Timeout shift: %lu secs (try %lu)\n",
90661729d93SAxel Dörfler 		Device(), timeout, tries);
907f9af6566SAxel Dörfler 
908f9af6566SAxel Dörfler 	struct timeval value;
9096cc7630fSAxel Dörfler 	value.tv_sec = timeout;
910d09c1f8eSPhilippe Houdoin 	value.tv_usec = rand() % 1000000;
911f9af6566SAxel Dörfler 	setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &value, sizeof(value));
912f9af6566SAxel Dörfler 
913f9af6566SAxel Dörfler 	return true;
914f9af6566SAxel Dörfler }
915f9af6566SAxel Dörfler 
916f9af6566SAxel Dörfler 
91784fdf2c3SPhilippe Houdoin /*static*/ BString
91884fdf2c3SPhilippe Houdoin DHCPClient::_AddressToString(const uint8* data)
919f9af6566SAxel Dörfler {
920f9af6566SAxel Dörfler 	BString target = inet_ntoa(*(in_addr*)data);
921f9af6566SAxel Dörfler 	return target;
922f9af6566SAxel Dörfler }
923f9af6566SAxel Dörfler 
924f9af6566SAxel Dörfler 
92584fdf2c3SPhilippe Houdoin /*static*/ BString
92684fdf2c3SPhilippe Houdoin DHCPClient::_AddressToString(in_addr_t address)
927f9af6566SAxel Dörfler {
928f9af6566SAxel Dörfler 	BString target = inet_ntoa(*(in_addr*)&address);
929f9af6566SAxel Dörfler 	return target;
930f9af6566SAxel Dörfler }
931f9af6566SAxel Dörfler 
932f9af6566SAxel Dörfler 
933f9af6566SAxel Dörfler status_t
9341a905a76SAxel Dörfler DHCPClient::_SendMessage(int socket, dhcp_message& message,
935af074561SAxel Dörfler 	const BNetworkAddress& address) const
936f9af6566SAxel Dörfler {
9370892800fSPhilippe Houdoin 	message_type type = message.Type();
9380892800fSPhilippe Houdoin 	BString text;
9390892800fSPhilippe Houdoin 	text << dhcp_message::TypeToString(type);
9400892800fSPhilippe Houdoin 
9410892800fSPhilippe Houdoin 	const uint8* requestAddress = message.FindOption(OPTION_REQUEST_IP_ADDRESS);
9420892800fSPhilippe Houdoin 	if (type == DHCP_REQUEST && requestAddress != NULL)
9430892800fSPhilippe Houdoin 		text << " for " << _AddressToString(requestAddress).String();
9440892800fSPhilippe Houdoin 
9450892800fSPhilippe Houdoin 	syslog(LOG_DEBUG, "%s: Send %s to %s\n", Device(), text.String(),
9460892800fSPhilippe Houdoin 		address.ToString().String());
94761729d93SAxel Dörfler 
948f9af6566SAxel Dörfler 	ssize_t bytesSent = sendto(socket, &message, message.Size(),
949af074561SAxel Dörfler 		address.IsBroadcast() ? MSG_BCAST : 0, address, address.Length());
950f9af6566SAxel Dörfler 	if (bytesSent < 0)
951f9af6566SAxel Dörfler 		return errno;
952f9af6566SAxel Dörfler 
953f9af6566SAxel Dörfler 	return B_OK;
954fb81684fSAxel Dörfler }
955fb81684fSAxel Dörfler 
956fb81684fSAxel Dörfler 
95746ff5400SAxel Dörfler dhcp_state
95846ff5400SAxel Dörfler DHCPClient::_CurrentState() const
95946ff5400SAxel Dörfler {
96046ff5400SAxel Dörfler 	bigtime_t now = system_time();
96146ff5400SAxel Dörfler 
96246ff5400SAxel Dörfler 	if (now > fLeaseTime || fStatus < B_OK)
96346ff5400SAxel Dörfler 		return INIT;
96446ff5400SAxel Dörfler 	if (now >= fRebindingTime)
96546ff5400SAxel Dörfler 		return REBINDING;
96646ff5400SAxel Dörfler 	if (now >= fRenewalTime)
9670892800fSPhilippe Houdoin 		return RENEWING;
96846ff5400SAxel Dörfler 
96946ff5400SAxel Dörfler 	return BOUND;
97046ff5400SAxel Dörfler }
97146ff5400SAxel Dörfler 
97246ff5400SAxel Dörfler 
973fb81684fSAxel Dörfler void
974fb81684fSAxel Dörfler DHCPClient::MessageReceived(BMessage* message)
975fb81684fSAxel Dörfler {
9766cc7630fSAxel Dörfler 	switch (message->what) {
9776cc7630fSAxel Dörfler 		case kMsgLeaseTime:
97846ff5400SAxel Dörfler 		{
97946ff5400SAxel Dörfler 			dhcp_state state = _CurrentState();
98046ff5400SAxel Dörfler 
98146ff5400SAxel Dörfler 			bigtime_t next;
98246ff5400SAxel Dörfler 			if (_Negotiate(state) == B_OK) {
98346ff5400SAxel Dörfler 				switch (state) {
9840892800fSPhilippe Houdoin 					case RENEWING:
98546ff5400SAxel Dörfler 						next = fRebindingTime;
9866cc7630fSAxel Dörfler 						break;
98746ff5400SAxel Dörfler 					case REBINDING:
98846ff5400SAxel Dörfler 					default:
98946ff5400SAxel Dörfler 						next = fRenewalTime;
99046ff5400SAxel Dörfler 						break;
99146ff5400SAxel Dörfler 				}
99246ff5400SAxel Dörfler 			} else {
99346ff5400SAxel Dörfler 				switch (state) {
9940892800fSPhilippe Houdoin 					case RENEWING:
99546ff5400SAxel Dörfler 						next = (fLeaseTime - fRebindingTime) / 4 + system_time();
99646ff5400SAxel Dörfler 						break;
99746ff5400SAxel Dörfler 					case REBINDING:
99846ff5400SAxel Dörfler 					default:
99946ff5400SAxel Dörfler 						next = (fLeaseTime - fRenewalTime) / 4 + system_time();
100046ff5400SAxel Dörfler 						break;
100146ff5400SAxel Dörfler 				}
100246ff5400SAxel Dörfler 			}
100346ff5400SAxel Dörfler 
100446ff5400SAxel Dörfler 			_RestartLease(next - system_time());
100546ff5400SAxel Dörfler 			break;
100646ff5400SAxel Dörfler 		}
10076cc7630fSAxel Dörfler 
10086cc7630fSAxel Dörfler 		default:
1009fb81684fSAxel Dörfler 			BHandler::MessageReceived(message);
10106cc7630fSAxel Dörfler 			break;
10116cc7630fSAxel Dörfler 	}
1012fb81684fSAxel Dörfler }
1013