xref: /haiku/src/servers/net/DHCPClient.cpp (revision ce36f054505bb0b4fb3fa97ca8d8f0d36dc35ef7)
1fb81684fSAxel Dörfler /*
237a68cb1SAxel Dörfler  * Copyright 2006-2010, Haiku, Inc. All Rights Reserved.
3fb81684fSAxel Dörfler  * Distributed under the terms of the MIT License.
4fb81684fSAxel Dörfler  *
5fb81684fSAxel Dörfler  * Authors:
6fb81684fSAxel Dörfler  *		Axel Dörfler, axeld@pinc-software.de
70f87f52fSStephan Aßmus  *		Vegard Wærp, vegarwa@online.no
8fb81684fSAxel Dörfler  */
9fb81684fSAxel Dörfler 
10fb81684fSAxel Dörfler 
11fb81684fSAxel Dörfler #include "DHCPClient.h"
12fb81684fSAxel Dörfler 
13f9af6566SAxel Dörfler #include <Message.h>
146cc7630fSAxel Dörfler #include <MessageRunner.h>
15af074561SAxel Dörfler #include <NetworkDevice.h>
16af074561SAxel Dörfler #include <NetworkInterface.h>
17f9af6566SAxel Dörfler 
18f9af6566SAxel Dörfler #include <arpa/inet.h>
19fb81684fSAxel Dörfler #include <errno.h>
20fb81684fSAxel Dörfler #include <stdio.h>
21fb81684fSAxel Dörfler #include <string.h>
22293ed4feSAxel Dörfler #include <syslog.h>
2336bde12dSAxel Dörfler #include <sys/sockio.h>
24f9af6566SAxel Dörfler #include <sys/time.h>
25fb81684fSAxel Dörfler 
264ac66051SAxel Dörfler #include <Debug.h>
274ac66051SAxel Dörfler #include <Message.h>
284ac66051SAxel Dörfler #include <MessageRunner.h>
294ac66051SAxel Dörfler 
304ac66051SAxel Dörfler #include "NetServer.h"
314ac66051SAxel Dörfler 
32fb81684fSAxel Dörfler 
33fb81684fSAxel Dörfler // See RFC 2131 for DHCP, see RFC 1533 for BOOTP/DHCP options
34fb81684fSAxel Dörfler 
35fb81684fSAxel Dörfler #define DHCP_CLIENT_PORT	68
36fb81684fSAxel Dörfler #define DHCP_SERVER_PORT	67
37fb81684fSAxel Dörfler 
38f9af6566SAxel Dörfler #define DEFAULT_TIMEOUT		2	// secs
39f9af6566SAxel Dörfler #define MAX_TIMEOUT			15	// secs
40f9af6566SAxel Dörfler 
41fb81684fSAxel Dörfler enum message_opcode {
42fb81684fSAxel Dörfler 	BOOT_REQUEST = 1,
43fb81684fSAxel Dörfler 	BOOT_REPLY
44fb81684fSAxel Dörfler };
45fb81684fSAxel Dörfler 
46fb81684fSAxel Dörfler enum message_option {
47fb81684fSAxel Dörfler 	OPTION_MAGIC = 0x63825363,
48fb81684fSAxel Dörfler 
49fb81684fSAxel Dörfler 	// generic options
50fb81684fSAxel Dörfler 	OPTION_PAD = 0,
51f9af6566SAxel Dörfler 	OPTION_END = 255,
52f9af6566SAxel Dörfler 	OPTION_SUBNET_MASK = 1,
5365186fecSAxel Dörfler 	OPTION_TIME_OFFSET = 2,
54f9af6566SAxel Dörfler 	OPTION_ROUTER_ADDRESS = 3,
55f9af6566SAxel Dörfler 	OPTION_DOMAIN_NAME_SERVER = 6,
56f9af6566SAxel Dörfler 	OPTION_HOST_NAME = 12,
575782c5a3SAxel Dörfler 	OPTION_DOMAIN_NAME = 15,
58fb81684fSAxel Dörfler 	OPTION_DATAGRAM_SIZE = 22,
59fb81684fSAxel Dörfler 	OPTION_MTU = 26,
60fb81684fSAxel Dörfler 	OPTION_BROADCAST_ADDRESS = 28,
61fb81684fSAxel Dörfler 	OPTION_NETWORK_TIME_SERVERS = 42,
6265186fecSAxel Dörfler 	OPTION_NETBIOS_NAME_SERVER = 44,
6365186fecSAxel Dörfler 	OPTION_NETBIOS_SCOPE = 47,
64fb81684fSAxel Dörfler 
65fb81684fSAxel Dörfler 	// DHCP specific options
66fb81684fSAxel Dörfler 	OPTION_REQUEST_IP_ADDRESS = 50,
67fb81684fSAxel Dörfler 	OPTION_ADDRESS_LEASE_TIME = 51,
68fb81684fSAxel Dörfler 	OPTION_OVERLOAD = 52,
69fb81684fSAxel Dörfler 	OPTION_MESSAGE_TYPE = 53,
70fb81684fSAxel Dörfler 	OPTION_SERVER_ADDRESS = 54,
71fb81684fSAxel Dörfler 	OPTION_REQUEST_PARAMETERS = 55,
72fb81684fSAxel Dörfler 	OPTION_ERROR_MESSAGE = 56,
73fb81684fSAxel Dörfler 	OPTION_MESSAGE_SIZE = 57,
74fb81684fSAxel Dörfler 	OPTION_RENEWAL_TIME = 58,
75fb81684fSAxel Dörfler 	OPTION_REBINDING_TIME = 59,
76fb81684fSAxel Dörfler 	OPTION_CLASS_IDENTIFIER = 60,
77fb81684fSAxel Dörfler 	OPTION_CLIENT_IDENTIFIER = 61,
78fb81684fSAxel Dörfler };
79fb81684fSAxel Dörfler 
80fb81684fSAxel Dörfler enum message_type {
81f9af6566SAxel Dörfler 	DHCP_NONE = 0,
82f9af6566SAxel Dörfler 	DHCP_DISCOVER,
83fb81684fSAxel Dörfler 	DHCP_OFFER,
84fb81684fSAxel Dörfler 	DHCP_REQUEST,
85fb81684fSAxel Dörfler 	DHCP_DECLINE,
86fb81684fSAxel Dörfler 	DHCP_ACK,
87f9af6566SAxel Dörfler 	DHCP_NACK,
88fb81684fSAxel Dörfler 	DHCP_RELEASE,
89fb81684fSAxel Dörfler 	DHCP_INFORM
90fb81684fSAxel Dörfler };
91fb81684fSAxel Dörfler 
92fb81684fSAxel Dörfler struct dhcp_option_cookie {
9315ab0bcfSAxel Dörfler 	dhcp_option_cookie()
9415ab0bcfSAxel Dörfler 		:
9515ab0bcfSAxel Dörfler 		state(0),
9615ab0bcfSAxel Dörfler 		file_has_options(false),
9715ab0bcfSAxel Dörfler 		server_name_has_options(false)
9815ab0bcfSAxel Dörfler 	{
9915ab0bcfSAxel Dörfler 	}
100fb81684fSAxel Dörfler 
101fb81684fSAxel Dörfler 	const uint8* next;
102fb81684fSAxel Dörfler 	uint8	state;
103fb81684fSAxel Dörfler 	bool	file_has_options;
104fb81684fSAxel Dörfler 	bool	server_name_has_options;
105fb81684fSAxel Dörfler };
106fb81684fSAxel Dörfler 
107fb81684fSAxel Dörfler struct dhcp_message {
108fb81684fSAxel Dörfler 	dhcp_message(message_type type);
109fb81684fSAxel Dörfler 
110fb81684fSAxel Dörfler 	uint8		opcode;
111fb81684fSAxel Dörfler 	uint8		hardware_type;
112fb81684fSAxel Dörfler 	uint8		hardware_address_length;
113fb81684fSAxel Dörfler 	uint8		hop_count;
114fb81684fSAxel Dörfler 	uint32		transaction_id;
11546ff5400SAxel Dörfler 	uint16		seconds_since_start;
116fb81684fSAxel Dörfler 	uint16		flags;
117fb81684fSAxel Dörfler 	in_addr_t	client_address;
118fb81684fSAxel Dörfler 	in_addr_t	your_address;
119fb81684fSAxel Dörfler 	in_addr_t	server_address;
120fb81684fSAxel Dörfler 	in_addr_t	gateway_address;
121fb81684fSAxel Dörfler 	uint8		mac_address[16];
122fb81684fSAxel Dörfler 	uint8		server_name[64];
123fb81684fSAxel Dörfler 	uint8		file[128];
124fb81684fSAxel Dörfler 	uint32		options_magic;
125fb81684fSAxel Dörfler 	uint8		options[1260];
126fb81684fSAxel Dörfler 
127fb81684fSAxel Dörfler 	size_t MinSize() const { return 576; }
128fb81684fSAxel Dörfler 	size_t Size() const;
129fb81684fSAxel Dörfler 
130fb81684fSAxel Dörfler 	bool HasOptions() const;
131fb81684fSAxel Dörfler 	bool NextOption(dhcp_option_cookie& cookie, message_option& option,
132fb81684fSAxel Dörfler 		const uint8*& data, size_t& size) const;
133f9af6566SAxel Dörfler 	message_type Type() const;
134fb81684fSAxel Dörfler 	const uint8* LastOption() const;
135fb81684fSAxel Dörfler 
136a073ba1aSHugo Santos 	uint8* PrepareMessage(uint8 type);
137fb81684fSAxel Dörfler 	uint8* PutOption(uint8* options, message_option option);
138fb81684fSAxel Dörfler 	uint8* PutOption(uint8* options, message_option option, uint8 data);
139fb81684fSAxel Dörfler 	uint8* PutOption(uint8* options, message_option option, uint16 data);
140fb81684fSAxel Dörfler 	uint8* PutOption(uint8* options, message_option option, uint32 data);
14115ab0bcfSAxel Dörfler 	uint8* PutOption(uint8* options, message_option option, const uint8* data,
14215ab0bcfSAxel Dörfler 		uint32 size);
14337a68cb1SAxel Dörfler 	uint8* FinishOptions(uint8* options);
1444ac66051SAxel Dörfler 
1454ac66051SAxel Dörfler 	static const char* TypeToString(message_type type);
146fb81684fSAxel Dörfler } _PACKED;
147fb81684fSAxel Dörfler 
1480cf5e6acSAxel Dörfler #define DHCP_FLAG_BROADCAST		0x8000
149fb81684fSAxel Dörfler 
150fb81684fSAxel Dörfler #define ARP_HARDWARE_TYPE_ETHER	1
151fb81684fSAxel Dörfler 
1526cc7630fSAxel Dörfler const uint32 kMsgLeaseTime = 'lstm';
1536cc7630fSAxel Dörfler 
15437a68cb1SAxel Dörfler static const uint8 kRequestParameters[] = {
15565186fecSAxel Dörfler 	OPTION_SUBNET_MASK, OPTION_ROUTER_ADDRESS,
156bfd479b3SAxel Dörfler 	OPTION_DOMAIN_NAME_SERVER, OPTION_BROADCAST_ADDRESS,
157bfd479b3SAxel Dörfler 	OPTION_DOMAIN_NAME
1585d4d5313SHugo Santos };
1595d4d5313SHugo Santos 
160fb81684fSAxel Dörfler 
161fb81684fSAxel Dörfler dhcp_message::dhcp_message(message_type type)
162fb81684fSAxel Dörfler {
163fb4527d2SPhilippe Houdoin 	// ASSERT(this == offsetof(this, opcode));
164fb81684fSAxel Dörfler 	memset(this, 0, sizeof(*this));
165fb81684fSAxel Dörfler 	options_magic = htonl(OPTION_MAGIC);
166fb81684fSAxel Dörfler 
167a073ba1aSHugo Santos 	uint8* next = PrepareMessage(type);
168a073ba1aSHugo Santos 	FinishOptions(next);
169fb81684fSAxel Dörfler }
170fb81684fSAxel Dörfler 
171fb81684fSAxel Dörfler 
172fb81684fSAxel Dörfler bool
173fb81684fSAxel Dörfler dhcp_message::HasOptions() const
174fb81684fSAxel Dörfler {
175fb81684fSAxel Dörfler 	return options_magic == htonl(OPTION_MAGIC);
176fb81684fSAxel Dörfler }
177fb81684fSAxel Dörfler 
178fb81684fSAxel Dörfler 
179fb81684fSAxel Dörfler bool
180fb81684fSAxel Dörfler dhcp_message::NextOption(dhcp_option_cookie& cookie,
181fb81684fSAxel Dörfler 	message_option& option, const uint8*& data, size_t& size) const
182fb81684fSAxel Dörfler {
183fb81684fSAxel Dörfler 	if (!HasOptions())
184fb81684fSAxel Dörfler 		return false;
185fb81684fSAxel Dörfler 
186fb4527d2SPhilippe Houdoin 	if (cookie.state == 0) {
187fb81684fSAxel Dörfler 		cookie.state++;
188fb81684fSAxel Dörfler 		cookie.next = options;
189fb81684fSAxel Dörfler 	}
190fb81684fSAxel Dörfler 
191fb81684fSAxel Dörfler 	uint32 bytesLeft = 0;
192fb81684fSAxel Dörfler 
193fb81684fSAxel Dörfler 	switch (cookie.state) {
194fb81684fSAxel Dörfler 		case 1:
195*ce36f054SPhilippe Houdoin 			// options from "options"
196fb4527d2SPhilippe Houdoin 			bytesLeft = sizeof(options) - (cookie.next - options);
197fb81684fSAxel Dörfler 			break;
198fb81684fSAxel Dörfler 
199fb81684fSAxel Dörfler 		case 2:
200fb81684fSAxel Dörfler 			// options from "file"
201fb4527d2SPhilippe Houdoin 			bytesLeft = sizeof(file) - (cookie.next - file);
202fb81684fSAxel Dörfler 			break;
203fb81684fSAxel Dörfler 
204fb81684fSAxel Dörfler 		case 3:
205fb81684fSAxel Dörfler 			// options from "server_name"
206fb4527d2SPhilippe Houdoin 			bytesLeft = sizeof(server_name) - (cookie.next - server_name);
207fb81684fSAxel Dörfler 			break;
208fb81684fSAxel Dörfler 	}
209fb81684fSAxel Dörfler 
210fb81684fSAxel Dörfler 	while (true) {
211fb81684fSAxel Dörfler 		if (bytesLeft == 0) {
212fb4527d2SPhilippe Houdoin 			cookie.state++;
213fb4527d2SPhilippe Houdoin 
214*ce36f054SPhilippe Houdoin 			// handle option overload in file and/or server_name fields.
215fb4527d2SPhilippe Houdoin 			switch (cookie.state) {
216fb4527d2SPhilippe Houdoin 				case 2:
217fb4527d2SPhilippe Houdoin 					// options from "file"
218fb4527d2SPhilippe Houdoin 					if (cookie.file_has_options) {
219fb4527d2SPhilippe Houdoin 						bytesLeft = sizeof(file);
220fb4527d2SPhilippe Houdoin 						cookie.next = file;
221fb4527d2SPhilippe Houdoin 					}
222fb4527d2SPhilippe Houdoin 					break;
223fb4527d2SPhilippe Houdoin 
224fb4527d2SPhilippe Houdoin 				case 3:
225fb4527d2SPhilippe Houdoin 					// options from "server_name"
226fb4527d2SPhilippe Houdoin 					if (cookie.server_name_has_options) {
227fb4527d2SPhilippe Houdoin 						bytesLeft = sizeof(server_name);
228fb4527d2SPhilippe Houdoin 						cookie.next = server_name;
229fb4527d2SPhilippe Houdoin 					}
230fb4527d2SPhilippe Houdoin 					break;
231fb4527d2SPhilippe Houdoin 
232fb4527d2SPhilippe Houdoin 				case 4:
233*ce36f054SPhilippe Houdoin 					// no more place to look for options
234fb81684fSAxel Dörfler 					return false;
235fb81684fSAxel Dörfler 			}
236fb81684fSAxel Dörfler 
237fb4527d2SPhilippe Houdoin 			if (bytesLeft == 0) {
238fb4527d2SPhilippe Houdoin 				// no options for this state, try next one
239fb4527d2SPhilippe Houdoin 				continue;
240fb4527d2SPhilippe Houdoin 			}
241fb4527d2SPhilippe Houdoin 		}
242fb4527d2SPhilippe Houdoin 
243fb81684fSAxel Dörfler 		option = (message_option)cookie.next[0];
244fb81684fSAxel Dörfler 		if (option == OPTION_END) {
245fb4527d2SPhilippe Houdoin 			bytesLeft = 0;
246fb4527d2SPhilippe Houdoin 			continue;
247fb81684fSAxel Dörfler 		} else if (option == OPTION_PAD) {
248fb81684fSAxel Dörfler 			bytesLeft--;
249fb81684fSAxel Dörfler 			cookie.next++;
250fb81684fSAxel Dörfler 			continue;
251fb81684fSAxel Dörfler 		}
252fb81684fSAxel Dörfler 
253fb81684fSAxel Dörfler 		size = cookie.next[1];
254fb81684fSAxel Dörfler 		data = &cookie.next[2];
255fb81684fSAxel Dörfler 		cookie.next += 2 + size;
256fb4527d2SPhilippe Houdoin 		bytesLeft -= 2 + size;
257fb81684fSAxel Dörfler 
258fb81684fSAxel Dörfler 		if (option == OPTION_OVERLOAD) {
259fb81684fSAxel Dörfler 			cookie.file_has_options = data[0] & 1;
260fb81684fSAxel Dörfler 			cookie.server_name_has_options = data[0] & 2;
261fb81684fSAxel Dörfler 			continue;
262fb81684fSAxel Dörfler 		}
263fb81684fSAxel Dörfler 
264fb81684fSAxel Dörfler 		return true;
265fb81684fSAxel Dörfler 	}
266fb81684fSAxel Dörfler }
267fb81684fSAxel Dörfler 
268fb81684fSAxel Dörfler 
269f9af6566SAxel Dörfler message_type
270f9af6566SAxel Dörfler dhcp_message::Type() const
271f9af6566SAxel Dörfler {
272f9af6566SAxel Dörfler 	dhcp_option_cookie cookie;
273f9af6566SAxel Dörfler 	message_option option;
274f9af6566SAxel Dörfler 	const uint8* data;
275f9af6566SAxel Dörfler 	size_t size;
276f9af6566SAxel Dörfler 	while (NextOption(cookie, option, data, size)) {
277f9af6566SAxel Dörfler 		// iterate through all options
278f9af6566SAxel Dörfler 		if (option == OPTION_MESSAGE_TYPE)
279f9af6566SAxel Dörfler 			return (message_type)data[0];
280f9af6566SAxel Dörfler 	}
281f9af6566SAxel Dörfler 
282f9af6566SAxel Dörfler 	return DHCP_NONE;
283f9af6566SAxel Dörfler }
284f9af6566SAxel Dörfler 
285f9af6566SAxel Dörfler 
286fb81684fSAxel Dörfler const uint8*
287fb81684fSAxel Dörfler dhcp_message::LastOption() const
288fb81684fSAxel Dörfler {
289fb81684fSAxel Dörfler 	dhcp_option_cookie cookie;
290fb81684fSAxel Dörfler 	message_option option;
291fb81684fSAxel Dörfler 	const uint8* data;
292fb81684fSAxel Dörfler 	size_t size;
293fb81684fSAxel Dörfler 	while (NextOption(cookie, option, data, size)) {
294fb81684fSAxel Dörfler 		// iterate through all options
295fb81684fSAxel Dörfler 	}
296fb81684fSAxel Dörfler 
297fb81684fSAxel Dörfler 	return cookie.next;
298fb81684fSAxel Dörfler }
299fb81684fSAxel Dörfler 
300fb81684fSAxel Dörfler 
301fb81684fSAxel Dörfler size_t
302fb81684fSAxel Dörfler dhcp_message::Size() const
303fb81684fSAxel Dörfler {
304fb81684fSAxel Dörfler 	const uint8* last = LastOption();
305fb81684fSAxel Dörfler 	return sizeof(dhcp_message) - sizeof(options) + last + 1 - options;
306fb81684fSAxel Dörfler }
307fb81684fSAxel Dörfler 
308fb81684fSAxel Dörfler 
309fb81684fSAxel Dörfler uint8*
310a073ba1aSHugo Santos dhcp_message::PrepareMessage(uint8 type)
311a073ba1aSHugo Santos {
312a073ba1aSHugo Santos 	uint8* next = options;
313a073ba1aSHugo Santos 	next = PutOption(next, OPTION_MESSAGE_TYPE, type);
31415ab0bcfSAxel Dörfler 	next = PutOption(next, OPTION_MESSAGE_SIZE,
31515ab0bcfSAxel Dörfler 		(uint16)htons(sizeof(dhcp_message)));
316a073ba1aSHugo Santos 	return next;
317a073ba1aSHugo Santos }
318a073ba1aSHugo Santos 
31965186fecSAxel Dörfler 
320a073ba1aSHugo Santos uint8*
321fb81684fSAxel Dörfler dhcp_message::PutOption(uint8* options, message_option option)
322fb81684fSAxel Dörfler {
323fb81684fSAxel Dörfler 	options[0] = option;
324fb81684fSAxel Dörfler 	return options + 1;
325fb81684fSAxel Dörfler }
326fb81684fSAxel Dörfler 
327fb81684fSAxel Dörfler 
328fb81684fSAxel Dörfler uint8*
329fb81684fSAxel Dörfler dhcp_message::PutOption(uint8* options, message_option option, uint8 data)
330fb81684fSAxel Dörfler {
331fb81684fSAxel Dörfler 	return PutOption(options, option, &data, 1);
332fb81684fSAxel Dörfler }
333fb81684fSAxel Dörfler 
334fb81684fSAxel Dörfler 
335fb81684fSAxel Dörfler uint8*
336fb81684fSAxel Dörfler dhcp_message::PutOption(uint8* options, message_option option, uint16 data)
337fb81684fSAxel Dörfler {
338fb81684fSAxel Dörfler 	return PutOption(options, option, (uint8*)&data, sizeof(data));
339fb81684fSAxel Dörfler }
340fb81684fSAxel Dörfler 
341fb81684fSAxel Dörfler 
342fb81684fSAxel Dörfler uint8*
343fb81684fSAxel Dörfler dhcp_message::PutOption(uint8* options, message_option option, uint32 data)
344fb81684fSAxel Dörfler {
345fb81684fSAxel Dörfler 	return PutOption(options, option, (uint8*)&data, sizeof(data));
346fb81684fSAxel Dörfler }
347fb81684fSAxel Dörfler 
348fb81684fSAxel Dörfler 
349fb81684fSAxel Dörfler uint8*
35015ab0bcfSAxel Dörfler dhcp_message::PutOption(uint8* options, message_option option,
35115ab0bcfSAxel Dörfler 	const uint8* data, uint32 size)
352fb81684fSAxel Dörfler {
353fb81684fSAxel Dörfler 	options[0] = option;
354fb81684fSAxel Dörfler 	options[1] = size;
355fb81684fSAxel Dörfler 	memcpy(&options[2], data, size);
356fb81684fSAxel Dörfler 
357fb81684fSAxel Dörfler 	return options + 2 + size;
358fb81684fSAxel Dörfler }
359fb81684fSAxel Dörfler 
360fb81684fSAxel Dörfler 
361a073ba1aSHugo Santos uint8*
36237a68cb1SAxel Dörfler dhcp_message::FinishOptions(uint8* options)
363a073ba1aSHugo Santos {
36437a68cb1SAxel Dörfler 	return PutOption(options, OPTION_END);
365a073ba1aSHugo Santos }
366a073ba1aSHugo Santos 
367a073ba1aSHugo Santos 
3684ac66051SAxel Dörfler /*static*/ const char*
3694ac66051SAxel Dörfler dhcp_message::TypeToString(message_type type)
3704ac66051SAxel Dörfler {
3714ac66051SAxel Dörfler 	switch (type) {
3724ac66051SAxel Dörfler #define CASE(x) case x: return #x;
3734ac66051SAxel Dörfler 		CASE(DHCP_NONE)
3744ac66051SAxel Dörfler 		CASE(DHCP_DISCOVER)
3754ac66051SAxel Dörfler 		CASE(DHCP_OFFER)
3764ac66051SAxel Dörfler 		CASE(DHCP_REQUEST)
3774ac66051SAxel Dörfler 		CASE(DHCP_DECLINE)
3784ac66051SAxel Dörfler 		CASE(DHCP_ACK)
3794ac66051SAxel Dörfler 		CASE(DHCP_NACK)
3804ac66051SAxel Dörfler 		CASE(DHCP_RELEASE)
3814ac66051SAxel Dörfler 		CASE(DHCP_INFORM)
3824ac66051SAxel Dörfler #undef CASE
3834ac66051SAxel Dörfler 	}
3844ac66051SAxel Dörfler 
3854ac66051SAxel Dörfler 	return "<unknown>";
3864ac66051SAxel Dörfler }
3874ac66051SAxel Dörfler 
3884ac66051SAxel Dörfler 
389fb81684fSAxel Dörfler //	#pragma mark -
390fb81684fSAxel Dörfler 
391fb81684fSAxel Dörfler 
392f9af6566SAxel Dörfler DHCPClient::DHCPClient(BMessenger target, const char* device)
393af074561SAxel Dörfler 	:
394af074561SAxel Dörfler 	AutoconfigClient("dhcp", target, device),
3956cc7630fSAxel Dörfler 	fConfiguration(kMsgConfigureInterface),
39616e8f13aSAxel Dörfler 	fResolverConfiguration(kMsgConfigureResolver),
3976cc7630fSAxel Dörfler 	fRunner(NULL),
398af074561SAxel Dörfler 	fServer(AF_INET, NULL, DHCP_SERVER_PORT),
399c1264353SStephan Aßmus 	fLeaseTime(0)
400fb81684fSAxel Dörfler {
40146ff5400SAxel Dörfler 	fStartTime = system_time();
40246ff5400SAxel Dörfler 	fTransactionID = (uint32)fStartTime;
403fb81684fSAxel Dörfler 
404af074561SAxel Dörfler 	BNetworkAddress link;
405af074561SAxel Dörfler 	BNetworkInterface interface(device);
406af074561SAxel Dörfler 	fStatus = interface.GetHardwareAddress(link);
407af074561SAxel Dörfler 	if (fStatus != B_OK)
408fb81684fSAxel Dörfler 		return;
409fb81684fSAxel Dörfler 
410af074561SAxel Dörfler 	memcpy(fMAC, link.LinkLevelAddress(), sizeof(fMAC));
411293ed4feSAxel Dörfler 
412293ed4feSAxel Dörfler 	openlog_thread("DHCP", 0, LOG_DAEMON);
4136cc7630fSAxel Dörfler }
4146cc7630fSAxel Dörfler 
4156cc7630fSAxel Dörfler 
4166cc7630fSAxel Dörfler DHCPClient::~DHCPClient()
4176cc7630fSAxel Dörfler {
4186cc7630fSAxel Dörfler 	if (fStatus != B_OK)
4196cc7630fSAxel Dörfler 		return;
4206cc7630fSAxel Dörfler 
4216cc7630fSAxel Dörfler 	delete fRunner;
4220ce7725eSAxel Dörfler 
423fb81684fSAxel Dörfler 	int socket = ::socket(AF_INET, SOCK_DGRAM, 0);
4246cc7630fSAxel Dörfler 	if (socket < 0)
425fb81684fSAxel Dörfler 		return;
4266cc7630fSAxel Dörfler 
4276cc7630fSAxel Dörfler 	// release lease
4286cc7630fSAxel Dörfler 
4296cc7630fSAxel Dörfler 	dhcp_message release(DHCP_RELEASE);
43046ff5400SAxel Dörfler 	_PrepareMessage(release, BOUND);
4316cc7630fSAxel Dörfler 
4326cc7630fSAxel Dörfler 	_SendMessage(socket, release, fServer);
4336cc7630fSAxel Dörfler 	close(socket);
434293ed4feSAxel Dörfler 
435293ed4feSAxel Dörfler 	closelog_thread();
436fb81684fSAxel Dörfler }
437fb81684fSAxel Dörfler 
4386cc7630fSAxel Dörfler 
4396cc7630fSAxel Dörfler status_t
4406cc7630fSAxel Dörfler DHCPClient::Initialize()
4416cc7630fSAxel Dörfler {
4426cc7630fSAxel Dörfler 	fStatus = _Negotiate(INIT);
443293ed4feSAxel Dörfler 	syslog(LOG_DEBUG, "DHCP for %s, status: %s\n", Device(), strerror(fStatus));
4446cc7630fSAxel Dörfler 	return fStatus;
4456cc7630fSAxel Dörfler }
4466cc7630fSAxel Dörfler 
4476cc7630fSAxel Dörfler 
4486cc7630fSAxel Dörfler status_t
4496cc7630fSAxel Dörfler DHCPClient::_Negotiate(dhcp_state state)
4506cc7630fSAxel Dörfler {
4516cc7630fSAxel Dörfler 	int socket = ::socket(AF_INET, SOCK_DGRAM, 0);
4526cc7630fSAxel Dörfler 	if (socket < 0)
4536cc7630fSAxel Dörfler 		return errno;
4546cc7630fSAxel Dörfler 
455fb4527d2SPhilippe Houdoin 	BNetworkAddress local;
456fb4527d2SPhilippe Houdoin 	local.SetToWildcard(AF_INET, DHCP_CLIENT_PORT);
457fb81684fSAxel Dörfler 
4582b829b04SBruno G. Albuquerque 	// Enable reusing the port . This is needed in case there is more
4592b829b04SBruno G. Albuquerque 	// than 1 interface that needs to be configured. Note that the only reason
4602b829b04SBruno G. Albuquerque 	// this works is because there is code below to bind to a specific
4612b829b04SBruno G. Albuquerque 	// interface.
4622b829b04SBruno G. Albuquerque 	int option = 1;
4632b829b04SBruno G. Albuquerque 	setsockopt(socket, SOL_SOCKET, SO_REUSEPORT, &option, sizeof(option));
4642b829b04SBruno G. Albuquerque 
465af074561SAxel Dörfler 	if (bind(socket, local, local.Length()) < 0) {
466fb81684fSAxel Dörfler 		close(socket);
4676cc7630fSAxel Dörfler 		return errno;
468fb81684fSAxel Dörfler 	}
469fb81684fSAxel Dörfler 
470af074561SAxel Dörfler 	BNetworkAddress broadcast;
471af074561SAxel Dörfler 	broadcast.SetToBroadcast(AF_INET, DHCP_SERVER_PORT);
472fb81684fSAxel Dörfler 
4732b829b04SBruno G. Albuquerque 	option = 1;
474fb81684fSAxel Dörfler 	setsockopt(socket, SOL_SOCKET, SO_BROADCAST, &option, sizeof(option));
475fb81684fSAxel Dörfler 
47636bde12dSAxel Dörfler 	if (state == INIT) {
47736bde12dSAxel Dörfler 		// The local interface does not have an address yet, bind the socket
47836bde12dSAxel Dörfler 		// to the device directly.
479af074561SAxel Dörfler 		BNetworkDevice device(Device());
480af074561SAxel Dörfler 		int index = device.Index();
48136bde12dSAxel Dörfler 
482af074561SAxel Dörfler 		setsockopt(socket, SOL_SOCKET, SO_BINDTODEVICE, &index, sizeof(int));
48336bde12dSAxel Dörfler 	}
48436bde12dSAxel Dörfler 
4856cc7630fSAxel Dörfler 	bigtime_t previousLeaseTime = fLeaseTime;
4866cc7630fSAxel Dörfler 	fLeaseTime = 0;
48746ff5400SAxel Dörfler 	fRenewalTime = 0;
48846ff5400SAxel Dörfler 	fRebindingTime = 0;
489fb81684fSAxel Dörfler 
4906cc7630fSAxel Dörfler 	status_t status = B_ERROR;
4916cc7630fSAxel Dörfler 	time_t timeout;
4926cc7630fSAxel Dörfler 	uint32 tries;
4936cc7630fSAxel Dörfler 	_ResetTimeout(socket, timeout, tries);
4946cc7630fSAxel Dörfler 
4956cc7630fSAxel Dörfler 	dhcp_message discover(DHCP_DISCOVER);
49646ff5400SAxel Dörfler 	_PrepareMessage(discover, state);
4976cc7630fSAxel Dörfler 
4986cc7630fSAxel Dörfler 	dhcp_message request(DHCP_REQUEST);
49946ff5400SAxel Dörfler 	_PrepareMessage(request, state);
5006cc7630fSAxel Dörfler 
50146ff5400SAxel Dörfler 	// send discover/request message
502593a0d2dSAxel Dörfler 	_SendMessage(socket, state == INIT ? discover : request,
50346ff5400SAxel Dörfler 		state != RENEWAL ? broadcast : fServer);
504593a0d2dSAxel Dörfler 		// no need to check the status; in case of an error we'll just send
505593a0d2dSAxel Dörfler 		// the message again
506fb81684fSAxel Dörfler 
507f9af6566SAxel Dörfler 	// receive loop until we've got an offer and acknowledged it
508f9af6566SAxel Dörfler 
509f9af6566SAxel Dörfler 	while (state != ACKNOWLEDGED) {
510f9af6566SAxel Dörfler 		char buffer[2048];
511f9af6566SAxel Dörfler 		ssize_t bytesReceived = recvfrom(socket, buffer, sizeof(buffer),
512f9af6566SAxel Dörfler 			0, NULL, NULL);
5134b661a95SAxel Dörfler 		if (bytesReceived < 0 && errno == B_TIMED_OUT) {
514f9af6566SAxel Dörfler 			// depending on the state, we'll just try again
5156cc7630fSAxel Dörfler 			if (!_TimeoutShift(socket, timeout, tries)) {
5166cc7630fSAxel Dörfler 				close(socket);
5176cc7630fSAxel Dörfler 				return B_TIMED_OUT;
518f9af6566SAxel Dörfler 			}
519f9af6566SAxel Dörfler 
520f9af6566SAxel Dörfler 			if (state == INIT)
521593a0d2dSAxel Dörfler 				_SendMessage(socket, discover, broadcast);
52215ab0bcfSAxel Dörfler 			else {
523593a0d2dSAxel Dörfler 				_SendMessage(socket, request,
524593a0d2dSAxel Dörfler 					state != RENEWAL ? broadcast : fServer);
52515ab0bcfSAxel Dörfler 			}
526f9af6566SAxel Dörfler 
527d77fe260SAxel Dörfler 			continue;
528593a0d2dSAxel Dörfler 		} else if (bytesReceived < 0)
529f9af6566SAxel Dörfler 			break;
530f9af6566SAxel Dörfler 
531f9af6566SAxel Dörfler 		dhcp_message* message = (dhcp_message*)buffer;
532f9af6566SAxel Dörfler 		if (message->transaction_id != htonl(fTransactionID)
533f9af6566SAxel Dörfler 			|| !message->HasOptions()
534f9af6566SAxel Dörfler 			|| memcmp(message->mac_address, discover.mac_address,
535f9af6566SAxel Dörfler 				discover.hardware_address_length)) {
536f9af6566SAxel Dörfler 			// this message is not for us
537f9af6566SAxel Dörfler 			continue;
538f9af6566SAxel Dörfler 		}
539f9af6566SAxel Dörfler 
5404ac66051SAxel Dörfler 		syslog(LOG_DEBUG, "DHCP received %s for %s\n",
5414ac66051SAxel Dörfler 			dhcp_message::TypeToString(message->Type()), Device());
5424ac66051SAxel Dörfler 
543f9af6566SAxel Dörfler 		switch (message->Type()) {
544f9af6566SAxel Dörfler 			case DHCP_NONE:
545f9af6566SAxel Dörfler 			default:
546f9af6566SAxel Dörfler 				// ignore this message
547f9af6566SAxel Dörfler 				break;
548f9af6566SAxel Dörfler 
549f9af6566SAxel Dörfler 			case DHCP_OFFER:
550f9af6566SAxel Dörfler 			{
551f9af6566SAxel Dörfler 				// first offer wins
552f9af6566SAxel Dörfler 				if (state != INIT)
553f9af6566SAxel Dörfler 					break;
554f9af6566SAxel Dörfler 
555f9af6566SAxel Dörfler 				// collect interface options
556f9af6566SAxel Dörfler 
557f9af6566SAxel Dörfler 				fAssignedAddress = message->your_address;
558f9af6566SAxel Dörfler 
55910cc12daSAxel Dörfler 				fConfiguration.MakeEmpty();
560293ed4feSAxel Dörfler 				fConfiguration.AddString("device", Device());
561f01106c3SAxel Dörfler 				fConfiguration.AddBool("auto", true);
562f9af6566SAxel Dörfler 
563f9af6566SAxel Dörfler 				BMessage address;
564f9af6566SAxel Dörfler 				address.AddString("family", "inet");
565f9af6566SAxel Dörfler 				address.AddString("address", _ToString(fAssignedAddress));
56616e8f13aSAxel Dörfler 				fResolverConfiguration.MakeEmpty();
56716e8f13aSAxel Dörfler 				_ParseOptions(*message, address, fResolverConfiguration);
568f9af6566SAxel Dörfler 
56910cc12daSAxel Dörfler 				fConfiguration.AddMessage("address", &address);
570f9af6566SAxel Dörfler 
57110cc12daSAxel Dörfler 				// request configuration from the server
572f9af6566SAxel Dörfler 
5736cc7630fSAxel Dörfler 				_ResetTimeout(socket, timeout, tries);
574f9af6566SAxel Dörfler 				state = REQUESTING;
57546ff5400SAxel Dörfler 				_PrepareMessage(request, state);
576f9af6566SAxel Dörfler 
5776cc7630fSAxel Dörfler 				status = _SendMessage(socket, request, broadcast);
57815ab0bcfSAxel Dörfler 					// we're sending a broadcast so that all potential offers
57915ab0bcfSAxel Dörfler 					// get an answer
58010cc12daSAxel Dörfler 				break;
581f9af6566SAxel Dörfler 			}
582f9af6566SAxel Dörfler 
583f9af6566SAxel Dörfler 			case DHCP_ACK:
58410cc12daSAxel Dörfler 			{
58515ab0bcfSAxel Dörfler 				if (state != REQUESTING && state != REBINDING
58615ab0bcfSAxel Dörfler 					&& state != RENEWAL)
587f9af6566SAxel Dörfler 					continue;
588f9af6566SAxel Dörfler 
5896cc7630fSAxel Dörfler 				// TODO: we might want to configure the stuff, don't we?
5906cc7630fSAxel Dörfler 				BMessage address;
59116e8f13aSAxel Dörfler 				fResolverConfiguration.MakeEmpty();
59216e8f13aSAxel Dörfler 				_ParseOptions(*message, address, fResolverConfiguration);
5934ac66051SAxel Dörfler 					// TODO: currently, only lease time and DNS is updated this
5944ac66051SAxel Dörfler 					// way
5956cc7630fSAxel Dörfler 
596f9af6566SAxel Dörfler 				// our address request has been acknowledged
597f9af6566SAxel Dörfler 				state = ACKNOWLEDGED;
59810cc12daSAxel Dörfler 
59910cc12daSAxel Dörfler 				// configure interface
60010cc12daSAxel Dörfler 				BMessage reply;
601d23fdd96SOliver Tappe 				status = Target().SendMessage(&fConfiguration, &reply);
602d23fdd96SOliver Tappe 				if (status == B_OK)
603d23fdd96SOliver Tappe 					status = reply.FindInt32("status", &fStatus);
60416e8f13aSAxel Dörfler 
60516e8f13aSAxel Dörfler 				// configure resolver
60616e8f13aSAxel Dörfler 				reply.MakeEmpty();
60716e8f13aSAxel Dörfler 				status = Target().SendMessage(&fResolverConfiguration, &reply);
60816e8f13aSAxel Dörfler 				if (status == B_OK)
60916e8f13aSAxel Dörfler 					status = reply.FindInt32("status", &fStatus);
610f9af6566SAxel Dörfler 				break;
61110cc12daSAxel Dörfler 			}
612f9af6566SAxel Dörfler 
613f9af6566SAxel Dörfler 			case DHCP_NACK:
614f9af6566SAxel Dörfler 				if (state != REQUESTING)
615f9af6566SAxel Dörfler 					continue;
616f9af6566SAxel Dörfler 
61715ab0bcfSAxel Dörfler 				// try again (maybe we should prefer other servers if this
61815ab0bcfSAxel Dörfler 				// happens more than once)
6196cc7630fSAxel Dörfler 				status = _SendMessage(socket, discover, broadcast);
6206cc7630fSAxel Dörfler 				if (status == B_OK)
621f9af6566SAxel Dörfler 					state = INIT;
622f9af6566SAxel Dörfler 				break;
623f9af6566SAxel Dörfler 		}
624f9af6566SAxel Dörfler 	}
625f9af6566SAxel Dörfler 
626fb81684fSAxel Dörfler 	close(socket);
6276cc7630fSAxel Dörfler 
6286cc7630fSAxel Dörfler 	if (status == B_OK && fLeaseTime > 0) {
6296cc7630fSAxel Dörfler 		// notify early enough when the lease is
63046ff5400SAxel Dörfler 		if (fRenewalTime == 0)
63146ff5400SAxel Dörfler 			fRenewalTime = fLeaseTime * 2/3;
63246ff5400SAxel Dörfler 		if (fRebindingTime == 0)
63346ff5400SAxel Dörfler 			fRebindingTime = fLeaseTime * 5/6;
63446ff5400SAxel Dörfler 
635168cc3dfSRene Gollent 		bigtime_t now = system_time();
63646ff5400SAxel Dörfler 		_RestartLease(fRenewalTime);
63746ff5400SAxel Dörfler 
63846ff5400SAxel Dörfler 		fLeaseTime += now;
63946ff5400SAxel Dörfler 		fRenewalTime += now;
64046ff5400SAxel Dörfler 		fRebindingTime += now;
64146ff5400SAxel Dörfler 			// make lease times absolute
64246ff5400SAxel Dörfler 	} else {
6436cc7630fSAxel Dörfler 		fLeaseTime = previousLeaseTime;
64446ff5400SAxel Dörfler 		bigtime_t now = system_time();
64546ff5400SAxel Dörfler 		fRenewalTime = (fLeaseTime - now) * 2/3 + now;
64646ff5400SAxel Dörfler 		fRebindingTime = (fLeaseTime - now) * 5/6 + now;
64746ff5400SAxel Dörfler 	}
6486cc7630fSAxel Dörfler 
6496cc7630fSAxel Dörfler 	return status;
650fb81684fSAxel Dörfler }
651fb81684fSAxel Dörfler 
652fb81684fSAxel Dörfler 
6536cc7630fSAxel Dörfler void
6546cc7630fSAxel Dörfler DHCPClient::_RestartLease(bigtime_t leaseTime)
655fb81684fSAxel Dörfler {
6566cc7630fSAxel Dörfler 	if (leaseTime == 0)
657f9af6566SAxel Dörfler 		return;
658f9af6566SAxel Dörfler 
6596cc7630fSAxel Dörfler 	BMessage lease(kMsgLeaseTime);
66046ff5400SAxel Dörfler 	fRunner = new BMessageRunner(this, &lease, leaseTime, 1);
661f9af6566SAxel Dörfler }
662f9af6566SAxel Dörfler 
663f9af6566SAxel Dörfler 
664f9af6566SAxel Dörfler void
66516e8f13aSAxel Dörfler DHCPClient::_ParseOptions(dhcp_message& message, BMessage& address,
66616e8f13aSAxel Dörfler 	BMessage& resolverConfiguration)
6670ce7725eSAxel Dörfler {
6680ce7725eSAxel Dörfler 	dhcp_option_cookie cookie;
6690ce7725eSAxel Dörfler 	message_option option;
6700ce7725eSAxel Dörfler 	const uint8* data;
6710ce7725eSAxel Dörfler 	size_t size;
6720ce7725eSAxel Dörfler 	while (message.NextOption(cookie, option, data, size)) {
6730ce7725eSAxel Dörfler 		// iterate through all options
6740ce7725eSAxel Dörfler 		switch (option) {
6750ce7725eSAxel Dörfler 			case OPTION_ROUTER_ADDRESS:
6760ce7725eSAxel Dörfler 				address.AddString("gateway", _ToString(data));
6770ce7725eSAxel Dörfler 				break;
6780ce7725eSAxel Dörfler 			case OPTION_SUBNET_MASK:
6790ce7725eSAxel Dörfler 				address.AddString("mask", _ToString(data));
6800ce7725eSAxel Dörfler 				break;
6815782c5a3SAxel Dörfler 			case OPTION_BROADCAST_ADDRESS:
6825782c5a3SAxel Dörfler 				address.AddString("broadcast", _ToString(data));
6835782c5a3SAxel Dörfler 				break;
6840ce7725eSAxel Dörfler 			case OPTION_DOMAIN_NAME_SERVER:
685a552ec13SAxel Dörfler 			{
6860ce7725eSAxel Dörfler 				for (uint32 i = 0; i < size / 4; i++) {
68761729d93SAxel Dörfler 					syslog(LOG_INFO, "DHCP for %s got DNS: %s\n", Device(),
688293ed4feSAxel Dörfler 						_ToString(&data[i * 4]).String());
68916e8f13aSAxel Dörfler 					resolverConfiguration.AddString("nameserver",
69015ab0bcfSAxel Dörfler 						_ToString(&data[i * 4]).String());
69115ab0bcfSAxel Dörfler 				}
69216e8f13aSAxel Dörfler 				resolverConfiguration.AddInt32("nameserver_count",
69316e8f13aSAxel Dörfler 					size / 4);
6940ce7725eSAxel Dörfler 				break;
695a552ec13SAxel Dörfler 			}
6960ce7725eSAxel Dörfler 			case OPTION_SERVER_ADDRESS:
697af074561SAxel Dörfler 				fServer.SetAddress(*(in_addr_t*)data);
6980ce7725eSAxel Dörfler 				break;
69946ff5400SAxel Dörfler 
7000ce7725eSAxel Dörfler 			case OPTION_ADDRESS_LEASE_TIME:
701293ed4feSAxel Dörfler 				syslog(LOG_INFO, "lease time of %lu seconds\n",
702293ed4feSAxel Dörfler 					htonl(*(uint32*)data));
7036cc7630fSAxel Dörfler 				fLeaseTime = htonl(*(uint32*)data) * 1000000LL;
7040ce7725eSAxel Dörfler 				break;
7051a4e8e7bSAxel Dörfler 			case OPTION_RENEWAL_TIME:
706293ed4feSAxel Dörfler 				syslog(LOG_INFO, "renewal time of %lu seconds\n",
70746ff5400SAxel Dörfler 					htonl(*(uint32*)data));
70846ff5400SAxel Dörfler 				fRenewalTime = htonl(*(uint32*)data) * 1000000LL;
70946ff5400SAxel Dörfler 				break;
7101a4e8e7bSAxel Dörfler 			case OPTION_REBINDING_TIME:
711293ed4feSAxel Dörfler 				syslog(LOG_INFO, "rebinding time of %lu seconds\n",
71246ff5400SAxel Dörfler 					htonl(*(uint32*)data));
71346ff5400SAxel Dörfler 				fRebindingTime = htonl(*(uint32*)data) * 1000000LL;
7141a4e8e7bSAxel Dörfler 				break;
7151a4e8e7bSAxel Dörfler 
7160ce7725eSAxel Dörfler 			case OPTION_HOST_NAME:
71716e8f13aSAxel Dörfler 				syslog(LOG_INFO, "DHCP host name: \"%.*s\"\n", (int)size,
71816e8f13aSAxel Dörfler 					(const char*)data);
7190ce7725eSAxel Dörfler 				break;
7205782c5a3SAxel Dörfler 
7215782c5a3SAxel Dörfler 			case OPTION_DOMAIN_NAME:
7225782c5a3SAxel Dörfler 			{
72316e8f13aSAxel Dörfler 				char domain[256];
72416e8f13aSAxel Dörfler 				strlcpy(domain, (const char*)data,
72516e8f13aSAxel Dörfler 					min_c(size + 1, sizeof(domain)));
7260f87f52fSStephan Aßmus 
72716e8f13aSAxel Dörfler 				syslog(LOG_INFO, "DHCP domain name: \"%s\"\n", domain);
7280f87f52fSStephan Aßmus 
72916e8f13aSAxel Dörfler 				resolverConfiguration.AddString("domain", domain);
7305782c5a3SAxel Dörfler 				break;
7315782c5a3SAxel Dörfler 			}
7320ce7725eSAxel Dörfler 
7330ce7725eSAxel Dörfler 			case OPTION_MESSAGE_TYPE:
7340ce7725eSAxel Dörfler 				break;
7350ce7725eSAxel Dörfler 
7360ce7725eSAxel Dörfler 			default:
737293ed4feSAxel Dörfler 				syslog(LOG_INFO, "unknown option %lu\n", (uint32)option);
7380ce7725eSAxel Dörfler 				break;
7390ce7725eSAxel Dörfler 		}
7400ce7725eSAxel Dörfler 	}
7410ce7725eSAxel Dörfler }
7420ce7725eSAxel Dörfler 
7430ce7725eSAxel Dörfler 
7440ce7725eSAxel Dörfler void
74546ff5400SAxel Dörfler DHCPClient::_PrepareMessage(dhcp_message& message, dhcp_state state)
7460ce7725eSAxel Dörfler {
7470ce7725eSAxel Dörfler 	message.opcode = BOOT_REQUEST;
7480ce7725eSAxel Dörfler 	message.hardware_type = ARP_HARDWARE_TYPE_ETHER;
7490ce7725eSAxel Dörfler 	message.hardware_address_length = 6;
7500ce7725eSAxel Dörfler 	message.transaction_id = htonl(fTransactionID);
751bcc8dadaSAxel Dörfler 	message.seconds_since_start = htons(min_c((system_time() - fStartTime)
75215ab0bcfSAxel Dörfler 		/ 1000000LL, 65535));
7530ce7725eSAxel Dörfler 	memcpy(message.mac_address, fMAC, 6);
7540ce7725eSAxel Dörfler 
75546ff5400SAxel Dörfler 	message_type type = message.Type();
75646ff5400SAxel Dörfler 
75746ff5400SAxel Dörfler 	switch (type) {
7580ce7725eSAxel Dörfler 		case DHCP_REQUEST:
7590ce7725eSAxel Dörfler 		case DHCP_RELEASE:
7600ce7725eSAxel Dörfler 		{
7610ce7725eSAxel Dörfler 			// add server identifier option
762af074561SAxel Dörfler 			const sockaddr_in& server = (sockaddr_in&)fServer.SockAddr();
763a073ba1aSHugo Santos 			uint8* next = message.PrepareMessage(type);
7640ce7725eSAxel Dörfler 			next = message.PutOption(next, OPTION_SERVER_ADDRESS,
765af074561SAxel Dörfler 				(uint32)server.sin_addr.s_addr);
76646ff5400SAxel Dörfler 
76746ff5400SAxel Dörfler 			// In RENEWAL or REBINDING state, we must set the client_address field, and not
76846ff5400SAxel Dörfler 			// use OPTION_REQUEST_IP_ADDRESS for DHCP_REQUEST messages
7695d4d5313SHugo Santos 			if (type == DHCP_REQUEST && (state == INIT || state == REQUESTING)) {
77015ab0bcfSAxel Dörfler 				next = message.PutOption(next, OPTION_REQUEST_IP_ADDRESS,
77115ab0bcfSAxel Dörfler 					(uint32)fAssignedAddress);
7725d4d5313SHugo Santos 				next = message.PutOption(next, OPTION_REQUEST_PARAMETERS,
77337a68cb1SAxel Dörfler 					kRequestParameters, sizeof(kRequestParameters));
7745d4d5313SHugo Santos 			} else
77546ff5400SAxel Dörfler 				message.client_address = fAssignedAddress;
77646ff5400SAxel Dörfler 
777a073ba1aSHugo Santos 			message.FinishOptions(next);
778a073ba1aSHugo Santos 			break;
779a073ba1aSHugo Santos 		}
780a073ba1aSHugo Santos 
781a073ba1aSHugo Santos 		case DHCP_DISCOVER:
782a073ba1aSHugo Santos 		{
783a073ba1aSHugo Santos 			uint8* next = message.PrepareMessage(type);
784a073ba1aSHugo Santos 			next = message.PutOption(next, OPTION_REQUEST_PARAMETERS,
78537a68cb1SAxel Dörfler 				kRequestParameters, sizeof(kRequestParameters));
786a073ba1aSHugo Santos 			message.FinishOptions(next);
7870ce7725eSAxel Dörfler 			break;
7880ce7725eSAxel Dörfler 		}
7890ce7725eSAxel Dörfler 
7900ce7725eSAxel Dörfler 		default:
7910ce7725eSAxel Dörfler 			// the default options are fine
7920ce7725eSAxel Dörfler 			break;
7930ce7725eSAxel Dörfler 	}
7940ce7725eSAxel Dörfler }
7950ce7725eSAxel Dörfler 
7960ce7725eSAxel Dörfler 
7970ce7725eSAxel Dörfler void
7986cc7630fSAxel Dörfler DHCPClient::_ResetTimeout(int socket, time_t& timeout, uint32& tries)
799f9af6566SAxel Dörfler {
8006cc7630fSAxel Dörfler 	timeout = DEFAULT_TIMEOUT;
8016cc7630fSAxel Dörfler 	tries = 0;
802f9af6566SAxel Dörfler 
803f9af6566SAxel Dörfler 	struct timeval value;
8046cc7630fSAxel Dörfler 	value.tv_sec = timeout;
805f9af6566SAxel Dörfler 	value.tv_usec = 0;
806f9af6566SAxel Dörfler 	setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &value, sizeof(value));
807f9af6566SAxel Dörfler }
808f9af6566SAxel Dörfler 
809f9af6566SAxel Dörfler 
810f9af6566SAxel Dörfler bool
8116cc7630fSAxel Dörfler DHCPClient::_TimeoutShift(int socket, time_t& timeout, uint32& tries)
812f9af6566SAxel Dörfler {
8136cc7630fSAxel Dörfler 	timeout += timeout;
8146cc7630fSAxel Dörfler 	if (timeout > MAX_TIMEOUT) {
8156cc7630fSAxel Dörfler 		timeout = DEFAULT_TIMEOUT;
816f9af6566SAxel Dörfler 
8176cc7630fSAxel Dörfler 		if (++tries > 2)
818f9af6566SAxel Dörfler 			return false;
819f9af6566SAxel Dörfler 	}
82061729d93SAxel Dörfler 	syslog(LOG_DEBUG, "DHCP timeout shift for %s: %lu secs (try %lu)\n",
82161729d93SAxel Dörfler 		Device(), timeout, tries);
822f9af6566SAxel Dörfler 
823f9af6566SAxel Dörfler 	struct timeval value;
8246cc7630fSAxel Dörfler 	value.tv_sec = timeout;
825f9af6566SAxel Dörfler 	value.tv_usec = 0;
826f9af6566SAxel Dörfler 	setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &value, sizeof(value));
827f9af6566SAxel Dörfler 
828f9af6566SAxel Dörfler 	return true;
829f9af6566SAxel Dörfler }
830f9af6566SAxel Dörfler 
831f9af6566SAxel Dörfler 
832f9af6566SAxel Dörfler BString
833f9af6566SAxel Dörfler DHCPClient::_ToString(const uint8* data) const
834f9af6566SAxel Dörfler {
835f9af6566SAxel Dörfler 	BString target = inet_ntoa(*(in_addr*)data);
836f9af6566SAxel Dörfler 	return target;
837f9af6566SAxel Dörfler }
838f9af6566SAxel Dörfler 
839f9af6566SAxel Dörfler 
840f9af6566SAxel Dörfler BString
841f9af6566SAxel Dörfler DHCPClient::_ToString(in_addr_t address) const
842f9af6566SAxel Dörfler {
843f9af6566SAxel Dörfler 	BString target = inet_ntoa(*(in_addr*)&address);
844f9af6566SAxel Dörfler 	return target;
845f9af6566SAxel Dörfler }
846f9af6566SAxel Dörfler 
847f9af6566SAxel Dörfler 
848f9af6566SAxel Dörfler status_t
8491a905a76SAxel Dörfler DHCPClient::_SendMessage(int socket, dhcp_message& message,
850af074561SAxel Dörfler 	const BNetworkAddress& address) const
851f9af6566SAxel Dörfler {
8524ac66051SAxel Dörfler 	syslog(LOG_DEBUG, "DHCP send message %s for %s\n",
8534ac66051SAxel Dörfler 		dhcp_message::TypeToString(message.Type()), Device());
85461729d93SAxel Dörfler 
855f9af6566SAxel Dörfler 	ssize_t bytesSent = sendto(socket, &message, message.Size(),
856af074561SAxel Dörfler 		address.IsBroadcast() ? MSG_BCAST : 0, address, address.Length());
857f9af6566SAxel Dörfler 	if (bytesSent < 0)
858f9af6566SAxel Dörfler 		return errno;
859f9af6566SAxel Dörfler 
860f9af6566SAxel Dörfler 	return B_OK;
861fb81684fSAxel Dörfler }
862fb81684fSAxel Dörfler 
863fb81684fSAxel Dörfler 
86446ff5400SAxel Dörfler dhcp_state
86546ff5400SAxel Dörfler DHCPClient::_CurrentState() const
86646ff5400SAxel Dörfler {
86746ff5400SAxel Dörfler 	bigtime_t now = system_time();
86846ff5400SAxel Dörfler 
86946ff5400SAxel Dörfler 	if (now > fLeaseTime || fStatus < B_OK)
87046ff5400SAxel Dörfler 		return INIT;
87146ff5400SAxel Dörfler 	if (now >= fRebindingTime)
87246ff5400SAxel Dörfler 		return REBINDING;
87346ff5400SAxel Dörfler 	if (now >= fRenewalTime)
87446ff5400SAxel Dörfler 		return RENEWAL;
87546ff5400SAxel Dörfler 
87646ff5400SAxel Dörfler 	return BOUND;
87746ff5400SAxel Dörfler }
87846ff5400SAxel Dörfler 
87946ff5400SAxel Dörfler 
880fb81684fSAxel Dörfler void
881fb81684fSAxel Dörfler DHCPClient::MessageReceived(BMessage* message)
882fb81684fSAxel Dörfler {
8836cc7630fSAxel Dörfler 	switch (message->what) {
8846cc7630fSAxel Dörfler 		case kMsgLeaseTime:
88546ff5400SAxel Dörfler 		{
88646ff5400SAxel Dörfler 			dhcp_state state = _CurrentState();
88746ff5400SAxel Dörfler 
88846ff5400SAxel Dörfler 			bigtime_t next;
88946ff5400SAxel Dörfler 			if (_Negotiate(state) == B_OK) {
89046ff5400SAxel Dörfler 				switch (state) {
89146ff5400SAxel Dörfler 					case RENEWAL:
89246ff5400SAxel Dörfler 						next = fRebindingTime;
8936cc7630fSAxel Dörfler 						break;
89446ff5400SAxel Dörfler 					case REBINDING:
89546ff5400SAxel Dörfler 					default:
89646ff5400SAxel Dörfler 						next = fRenewalTime;
89746ff5400SAxel Dörfler 						break;
89846ff5400SAxel Dörfler 				}
89946ff5400SAxel Dörfler 			} else {
90046ff5400SAxel Dörfler 				switch (state) {
90146ff5400SAxel Dörfler 					case RENEWAL:
90246ff5400SAxel Dörfler 						next = (fLeaseTime - fRebindingTime) / 4 + system_time();
90346ff5400SAxel Dörfler 						break;
90446ff5400SAxel Dörfler 					case REBINDING:
90546ff5400SAxel Dörfler 					default:
90646ff5400SAxel Dörfler 						next = (fLeaseTime - fRenewalTime) / 4 + system_time();
90746ff5400SAxel Dörfler 						break;
90846ff5400SAxel Dörfler 				}
90946ff5400SAxel Dörfler 			}
91046ff5400SAxel Dörfler 
91146ff5400SAxel Dörfler 			_RestartLease(next - system_time());
91246ff5400SAxel Dörfler 			break;
91346ff5400SAxel Dörfler 		}
9146cc7630fSAxel Dörfler 
9156cc7630fSAxel Dörfler 		default:
916fb81684fSAxel Dörfler 			BHandler::MessageReceived(message);
9176cc7630fSAxel Dörfler 			break;
9186cc7630fSAxel Dörfler 	}
919fb81684fSAxel Dörfler }
920