xref: /haiku/src/servers/net/DHCPClient.cpp (revision fb81684f819c432b2cdc30db597e08ccd88b5a25)
1*fb81684fSAxel Dörfler /*
2*fb81684fSAxel Dörfler  * Copyright 2006, Haiku, Inc. All Rights Reserved.
3*fb81684fSAxel Dörfler  * Distributed under the terms of the MIT License.
4*fb81684fSAxel Dörfler  *
5*fb81684fSAxel Dörfler  * Authors:
6*fb81684fSAxel Dörfler  *		Axel Dörfler, axeld@pinc-software.de
7*fb81684fSAxel Dörfler  */
8*fb81684fSAxel Dörfler 
9*fb81684fSAxel Dörfler 
10*fb81684fSAxel Dörfler #include "DHCPClient.h"
11*fb81684fSAxel Dörfler #include "NetServer.h"
12*fb81684fSAxel Dörfler 
13*fb81684fSAxel Dörfler #include <errno.h>
14*fb81684fSAxel Dörfler #include <netinet/in.h>
15*fb81684fSAxel Dörfler #include <stdio.h>
16*fb81684fSAxel Dörfler #include <string.h>
17*fb81684fSAxel Dörfler 
18*fb81684fSAxel Dörfler 
19*fb81684fSAxel Dörfler // See RFC 2131 for DHCP, see RFC 1533 for BOOTP/DHCP options
20*fb81684fSAxel Dörfler 
21*fb81684fSAxel Dörfler #define DHCP_CLIENT_PORT	68
22*fb81684fSAxel Dörfler #define DHCP_SERVER_PORT	67
23*fb81684fSAxel Dörfler 
24*fb81684fSAxel Dörfler enum message_opcode {
25*fb81684fSAxel Dörfler 	BOOT_REQUEST = 1,
26*fb81684fSAxel Dörfler 	BOOT_REPLY
27*fb81684fSAxel Dörfler };
28*fb81684fSAxel Dörfler 
29*fb81684fSAxel Dörfler enum message_option {
30*fb81684fSAxel Dörfler 	OPTION_MAGIC = 0x63825363,
31*fb81684fSAxel Dörfler 
32*fb81684fSAxel Dörfler 	// generic options
33*fb81684fSAxel Dörfler 	OPTION_PAD = 0,
34*fb81684fSAxel Dörfler 	OPTION_END = 1,
35*fb81684fSAxel Dörfler 	OPTION_DATAGRAM_SIZE = 22,
36*fb81684fSAxel Dörfler 	OPTION_MTU = 26,
37*fb81684fSAxel Dörfler 	OPTION_BROADCAST_ADDRESS = 28,
38*fb81684fSAxel Dörfler 	OPTION_NETWORK_TIME_SERVERS = 42,
39*fb81684fSAxel Dörfler 
40*fb81684fSAxel Dörfler 	// DHCP specific options
41*fb81684fSAxel Dörfler 	OPTION_REQUEST_IP_ADDRESS = 50,
42*fb81684fSAxel Dörfler 	OPTION_ADDRESS_LEASE_TIME = 51,
43*fb81684fSAxel Dörfler 	OPTION_OVERLOAD = 52,
44*fb81684fSAxel Dörfler 	OPTION_MESSAGE_TYPE = 53,
45*fb81684fSAxel Dörfler 	OPTION_SERVER_ADDRESS = 54,
46*fb81684fSAxel Dörfler 	OPTION_REQUEST_PARAMETERS = 55,
47*fb81684fSAxel Dörfler 	OPTION_ERROR_MESSAGE = 56,
48*fb81684fSAxel Dörfler 	OPTION_MESSAGE_SIZE = 57,
49*fb81684fSAxel Dörfler 	OPTION_RENEWAL_TIME = 58,
50*fb81684fSAxel Dörfler 	OPTION_REBINDING_TIME = 59,
51*fb81684fSAxel Dörfler 	OPTION_CLASS_IDENTIFIER = 60,
52*fb81684fSAxel Dörfler 	OPTION_CLIENT_IDENTIFIER = 61,
53*fb81684fSAxel Dörfler };
54*fb81684fSAxel Dörfler 
55*fb81684fSAxel Dörfler enum message_type {
56*fb81684fSAxel Dörfler 	DHCP_DISCOVER = 1,
57*fb81684fSAxel Dörfler 	DHCP_OFFER,
58*fb81684fSAxel Dörfler 	DHCP_REQUEST,
59*fb81684fSAxel Dörfler 	DHCP_DECLINE,
60*fb81684fSAxel Dörfler 	DHCP_ACK,
61*fb81684fSAxel Dörfler 	DHCP_NAK,
62*fb81684fSAxel Dörfler 	DHCP_RELEASE,
63*fb81684fSAxel Dörfler 	DHCP_INFORM
64*fb81684fSAxel Dörfler };
65*fb81684fSAxel Dörfler 
66*fb81684fSAxel Dörfler struct dhcp_option_cookie {
67*fb81684fSAxel Dörfler 	dhcp_option_cookie() : state(0), file_has_options(false), server_name_has_options(false) {}
68*fb81684fSAxel Dörfler 
69*fb81684fSAxel Dörfler 	const uint8* next;
70*fb81684fSAxel Dörfler 	uint8	state;
71*fb81684fSAxel Dörfler 	bool	file_has_options;
72*fb81684fSAxel Dörfler 	bool	server_name_has_options;
73*fb81684fSAxel Dörfler };
74*fb81684fSAxel Dörfler 
75*fb81684fSAxel Dörfler struct dhcp_message {
76*fb81684fSAxel Dörfler 	dhcp_message(message_type type);
77*fb81684fSAxel Dörfler 
78*fb81684fSAxel Dörfler 	uint8		opcode;
79*fb81684fSAxel Dörfler 	uint8		hardware_type;
80*fb81684fSAxel Dörfler 	uint8		hardware_address_length;
81*fb81684fSAxel Dörfler 	uint8		hop_count;
82*fb81684fSAxel Dörfler 	uint32		transaction_id;
83*fb81684fSAxel Dörfler 	uint16		seconds_since_boot;
84*fb81684fSAxel Dörfler 	uint16		flags;
85*fb81684fSAxel Dörfler 	in_addr_t	client_address;
86*fb81684fSAxel Dörfler 	in_addr_t	your_address;
87*fb81684fSAxel Dörfler 	in_addr_t	server_address;
88*fb81684fSAxel Dörfler 	in_addr_t	gateway_address;
89*fb81684fSAxel Dörfler 	uint8		mac_address[16];
90*fb81684fSAxel Dörfler 	uint8		server_name[64];
91*fb81684fSAxel Dörfler 	uint8		file[128];
92*fb81684fSAxel Dörfler 	uint32		options_magic;
93*fb81684fSAxel Dörfler 	uint8		options[1260];
94*fb81684fSAxel Dörfler 
95*fb81684fSAxel Dörfler 	size_t MinSize() const { return 576; }
96*fb81684fSAxel Dörfler 	size_t Size() const;
97*fb81684fSAxel Dörfler 
98*fb81684fSAxel Dörfler 	bool HasOptions() const;
99*fb81684fSAxel Dörfler 	bool NextOption(dhcp_option_cookie& cookie, message_option& option,
100*fb81684fSAxel Dörfler 		const uint8*& data, size_t& size) const;
101*fb81684fSAxel Dörfler 	const uint8* LastOption() const;
102*fb81684fSAxel Dörfler 
103*fb81684fSAxel Dörfler 	uint8* PutOption(uint8* options, message_option option);
104*fb81684fSAxel Dörfler 	uint8* PutOption(uint8* options, message_option option, uint8 data);
105*fb81684fSAxel Dörfler 	uint8* PutOption(uint8* options, message_option option, uint16 data);
106*fb81684fSAxel Dörfler 	uint8* PutOption(uint8* options, message_option option, uint32 data);
107*fb81684fSAxel Dörfler 	uint8* PutOption(uint8* options, message_option option, uint8* data, uint32 size);
108*fb81684fSAxel Dörfler } _PACKED;
109*fb81684fSAxel Dörfler 
110*fb81684fSAxel Dörfler 
111*fb81684fSAxel Dörfler #define ARP_HARDWARE_TYPE_ETHER	1
112*fb81684fSAxel Dörfler 
113*fb81684fSAxel Dörfler 
114*fb81684fSAxel Dörfler dhcp_message::dhcp_message(message_type type)
115*fb81684fSAxel Dörfler {
116*fb81684fSAxel Dörfler 	memset(this, 0, sizeof(*this));
117*fb81684fSAxel Dörfler 	options_magic = htonl(OPTION_MAGIC);
118*fb81684fSAxel Dörfler 
119*fb81684fSAxel Dörfler 	uint8* next = options;
120*fb81684fSAxel Dörfler 	next = PutOption(next, OPTION_MESSAGE_TYPE, (uint8)type);
121*fb81684fSAxel Dörfler 	next = PutOption(next, OPTION_MESSAGE_SIZE, (uint16)sizeof(dhcp_message));
122*fb81684fSAxel Dörfler 	next = PutOption(next, OPTION_END);
123*fb81684fSAxel Dörfler }
124*fb81684fSAxel Dörfler 
125*fb81684fSAxel Dörfler 
126*fb81684fSAxel Dörfler bool
127*fb81684fSAxel Dörfler dhcp_message::HasOptions() const
128*fb81684fSAxel Dörfler {
129*fb81684fSAxel Dörfler 	return options_magic == htonl(OPTION_MAGIC);
130*fb81684fSAxel Dörfler }
131*fb81684fSAxel Dörfler 
132*fb81684fSAxel Dörfler 
133*fb81684fSAxel Dörfler bool
134*fb81684fSAxel Dörfler dhcp_message::NextOption(dhcp_option_cookie& cookie,
135*fb81684fSAxel Dörfler 	message_option& option, const uint8*& data, size_t& size) const
136*fb81684fSAxel Dörfler {
137*fb81684fSAxel Dörfler 	if (cookie.state == 0) {
138*fb81684fSAxel Dörfler 		if (!HasOptions())
139*fb81684fSAxel Dörfler 			return false;
140*fb81684fSAxel Dörfler 
141*fb81684fSAxel Dörfler 		cookie.state++;
142*fb81684fSAxel Dörfler 		cookie.next = options;
143*fb81684fSAxel Dörfler 	}
144*fb81684fSAxel Dörfler 
145*fb81684fSAxel Dörfler 	uint32 bytesLeft = 0;
146*fb81684fSAxel Dörfler 
147*fb81684fSAxel Dörfler 	switch (cookie.state) {
148*fb81684fSAxel Dörfler 		case 1:
149*fb81684fSAxel Dörfler 			// options from "options"
150*fb81684fSAxel Dörfler 			bytesLeft = sizeof(options) + cookie.next - options;
151*fb81684fSAxel Dörfler 			break;
152*fb81684fSAxel Dörfler 
153*fb81684fSAxel Dörfler 		case 2:
154*fb81684fSAxel Dörfler 			// options from "file"
155*fb81684fSAxel Dörfler 			bytesLeft = sizeof(options) + cookie.next - options;
156*fb81684fSAxel Dörfler 			break;
157*fb81684fSAxel Dörfler 
158*fb81684fSAxel Dörfler 		case 3:
159*fb81684fSAxel Dörfler 			// options from "server_name"
160*fb81684fSAxel Dörfler 			bytesLeft = sizeof(options) + cookie.next - options;
161*fb81684fSAxel Dörfler 			break;
162*fb81684fSAxel Dörfler 	}
163*fb81684fSAxel Dörfler 
164*fb81684fSAxel Dörfler 	while (true) {
165*fb81684fSAxel Dörfler 		if (bytesLeft == 0) {
166*fb81684fSAxel Dörfler 			// TODO: suppport OPTION_OVERLOAD!
167*fb81684fSAxel Dörfler 			cookie.state = 4;
168*fb81684fSAxel Dörfler 			return false;
169*fb81684fSAxel Dörfler 		}
170*fb81684fSAxel Dörfler 
171*fb81684fSAxel Dörfler 		option = (message_option)cookie.next[0];
172*fb81684fSAxel Dörfler 		if (option == OPTION_END) {
173*fb81684fSAxel Dörfler 			cookie.state = 4;
174*fb81684fSAxel Dörfler 			return false;
175*fb81684fSAxel Dörfler 		} else if (option == OPTION_PAD) {
176*fb81684fSAxel Dörfler 			bytesLeft--;
177*fb81684fSAxel Dörfler 			cookie.next++;
178*fb81684fSAxel Dörfler 			continue;
179*fb81684fSAxel Dörfler 		}
180*fb81684fSAxel Dörfler 
181*fb81684fSAxel Dörfler 		size = cookie.next[1];
182*fb81684fSAxel Dörfler 		data = &cookie.next[2];
183*fb81684fSAxel Dörfler 		cookie.next += 2 + size;
184*fb81684fSAxel Dörfler 
185*fb81684fSAxel Dörfler 		if (option == OPTION_OVERLOAD) {
186*fb81684fSAxel Dörfler 			cookie.file_has_options = data[0] & 1;
187*fb81684fSAxel Dörfler 			cookie.server_name_has_options = data[0] & 2;
188*fb81684fSAxel Dörfler 			continue;
189*fb81684fSAxel Dörfler 		}
190*fb81684fSAxel Dörfler 
191*fb81684fSAxel Dörfler 		return true;
192*fb81684fSAxel Dörfler 	}
193*fb81684fSAxel Dörfler }
194*fb81684fSAxel Dörfler 
195*fb81684fSAxel Dörfler 
196*fb81684fSAxel Dörfler const uint8*
197*fb81684fSAxel Dörfler dhcp_message::LastOption() const
198*fb81684fSAxel Dörfler {
199*fb81684fSAxel Dörfler 	dhcp_option_cookie cookie;
200*fb81684fSAxel Dörfler 	message_option option;
201*fb81684fSAxel Dörfler 	const uint8 *data;
202*fb81684fSAxel Dörfler 	size_t size;
203*fb81684fSAxel Dörfler 	while (NextOption(cookie, option, data, size)) {
204*fb81684fSAxel Dörfler 		// iterate through all options
205*fb81684fSAxel Dörfler 	}
206*fb81684fSAxel Dörfler 
207*fb81684fSAxel Dörfler 	return cookie.next;
208*fb81684fSAxel Dörfler }
209*fb81684fSAxel Dörfler 
210*fb81684fSAxel Dörfler 
211*fb81684fSAxel Dörfler size_t
212*fb81684fSAxel Dörfler dhcp_message::Size() const
213*fb81684fSAxel Dörfler {
214*fb81684fSAxel Dörfler 	const uint8* last = LastOption();
215*fb81684fSAxel Dörfler 	return sizeof(dhcp_message) - sizeof(options) + last + 1 - options;
216*fb81684fSAxel Dörfler }
217*fb81684fSAxel Dörfler 
218*fb81684fSAxel Dörfler 
219*fb81684fSAxel Dörfler uint8*
220*fb81684fSAxel Dörfler dhcp_message::PutOption(uint8* options, message_option option)
221*fb81684fSAxel Dörfler {
222*fb81684fSAxel Dörfler 	options[0] = option;
223*fb81684fSAxel Dörfler 	return options + 1;
224*fb81684fSAxel Dörfler }
225*fb81684fSAxel Dörfler 
226*fb81684fSAxel Dörfler 
227*fb81684fSAxel Dörfler uint8*
228*fb81684fSAxel Dörfler dhcp_message::PutOption(uint8* options, message_option option, uint8 data)
229*fb81684fSAxel Dörfler {
230*fb81684fSAxel Dörfler 	return PutOption(options, option, &data, 1);
231*fb81684fSAxel Dörfler }
232*fb81684fSAxel Dörfler 
233*fb81684fSAxel Dörfler 
234*fb81684fSAxel Dörfler uint8*
235*fb81684fSAxel Dörfler dhcp_message::PutOption(uint8* options, message_option option, uint16 data)
236*fb81684fSAxel Dörfler {
237*fb81684fSAxel Dörfler 	data = htons(data);
238*fb81684fSAxel Dörfler 	return PutOption(options, option, (uint8*)&data, sizeof(data));
239*fb81684fSAxel Dörfler }
240*fb81684fSAxel Dörfler 
241*fb81684fSAxel Dörfler 
242*fb81684fSAxel Dörfler uint8*
243*fb81684fSAxel Dörfler dhcp_message::PutOption(uint8* options, message_option option, uint32 data)
244*fb81684fSAxel Dörfler {
245*fb81684fSAxel Dörfler 	data = htonl(data);
246*fb81684fSAxel Dörfler 	return PutOption(options, option, (uint8*)&data, sizeof(data));
247*fb81684fSAxel Dörfler }
248*fb81684fSAxel Dörfler 
249*fb81684fSAxel Dörfler 
250*fb81684fSAxel Dörfler uint8*
251*fb81684fSAxel Dörfler dhcp_message::PutOption(uint8* options, message_option option, uint8* data, uint32 size)
252*fb81684fSAxel Dörfler {
253*fb81684fSAxel Dörfler 	options[0] = option;
254*fb81684fSAxel Dörfler 	options[1] = size;
255*fb81684fSAxel Dörfler 	memcpy(&options[2], data, size);
256*fb81684fSAxel Dörfler 
257*fb81684fSAxel Dörfler 	return options + 2 + size;
258*fb81684fSAxel Dörfler }
259*fb81684fSAxel Dörfler 
260*fb81684fSAxel Dörfler 
261*fb81684fSAxel Dörfler //	#pragma mark -
262*fb81684fSAxel Dörfler 
263*fb81684fSAxel Dörfler 
264*fb81684fSAxel Dörfler DHCPClient::DHCPClient(const char* device)
265*fb81684fSAxel Dörfler 	: BHandler("dhcp"),
266*fb81684fSAxel Dörfler 	fDevice(device)
267*fb81684fSAxel Dörfler {
268*fb81684fSAxel Dörfler 	fTransactionID = system_time();
269*fb81684fSAxel Dörfler 
270*fb81684fSAxel Dörfler 	dhcp_message message(DHCP_DISCOVER);
271*fb81684fSAxel Dörfler 	message.opcode = BOOT_REQUEST;
272*fb81684fSAxel Dörfler 	message.hardware_type = ARP_HARDWARE_TYPE_ETHER;
273*fb81684fSAxel Dörfler 	message.hardware_address_length = 6;
274*fb81684fSAxel Dörfler 	message.transaction_id = htonl(fTransactionID);
275*fb81684fSAxel Dörfler 	message.seconds_since_boot = htons(max_c(system_time() / 1000000LL, 65535));
276*fb81684fSAxel Dörfler 	fStatus = get_mac_address(device, message.mac_address);
277*fb81684fSAxel Dörfler 	if (fStatus < B_OK)
278*fb81684fSAxel Dörfler 		return;
279*fb81684fSAxel Dörfler 
280*fb81684fSAxel Dörfler 	int socket = ::socket(AF_INET, SOCK_DGRAM, 0);
281*fb81684fSAxel Dörfler 	if (socket < 0) {
282*fb81684fSAxel Dörfler 		fStatus = errno;
283*fb81684fSAxel Dörfler 		return;
284*fb81684fSAxel Dörfler 	}
285*fb81684fSAxel Dörfler 
286*fb81684fSAxel Dörfler 	sockaddr_in local;
287*fb81684fSAxel Dörfler 	memset(&local, 0, sizeof(struct sockaddr_in));
288*fb81684fSAxel Dörfler 	local.sin_family = AF_INET;
289*fb81684fSAxel Dörfler 	local.sin_len = sizeof(struct sockaddr_in);
290*fb81684fSAxel Dörfler 	local.sin_port = htons(DHCP_CLIENT_PORT);
291*fb81684fSAxel Dörfler 	local.sin_addr.s_addr = INADDR_ANY;
292*fb81684fSAxel Dörfler 
293*fb81684fSAxel Dörfler 	if (bind(socket, (struct sockaddr *)&local, sizeof(local)) < 0) {
294*fb81684fSAxel Dörfler 		fStatus = errno;
295*fb81684fSAxel Dörfler 		close(socket);
296*fb81684fSAxel Dörfler 		return;
297*fb81684fSAxel Dörfler 	}
298*fb81684fSAxel Dörfler 
299*fb81684fSAxel Dörfler 	sockaddr_in broadcast;
300*fb81684fSAxel Dörfler 	memset(&broadcast, 0, sizeof(struct sockaddr_in));
301*fb81684fSAxel Dörfler 	broadcast.sin_family = AF_INET;
302*fb81684fSAxel Dörfler 	broadcast.sin_len = sizeof(struct sockaddr_in);
303*fb81684fSAxel Dörfler 	broadcast.sin_port = htons(DHCP_SERVER_PORT);
304*fb81684fSAxel Dörfler 	broadcast.sin_addr.s_addr = INADDR_BROADCAST;
305*fb81684fSAxel Dörfler 
306*fb81684fSAxel Dörfler 	int option = 1;
307*fb81684fSAxel Dörfler 	setsockopt(socket, SOL_SOCKET, SO_BROADCAST, &option, sizeof(option));
308*fb81684fSAxel Dörfler 
309*fb81684fSAxel Dörfler printf("DHCP message size %lu\n", message.Size());
310*fb81684fSAxel Dörfler 
311*fb81684fSAxel Dörfler 	ssize_t bytesSent = sendto(socket, &message, message.MinSize(), 0,
312*fb81684fSAxel Dörfler 		(struct sockaddr*)&broadcast, sizeof(broadcast));
313*fb81684fSAxel Dörfler 	if (bytesSent < 0) {
314*fb81684fSAxel Dörfler 		fStatus = errno;
315*fb81684fSAxel Dörfler 		close(socket);
316*fb81684fSAxel Dörfler 		return;
317*fb81684fSAxel Dörfler 	}
318*fb81684fSAxel Dörfler 
319*fb81684fSAxel Dörfler 	close(socket);
320*fb81684fSAxel Dörfler 	fStatus = B_ERROR;
321*fb81684fSAxel Dörfler }
322*fb81684fSAxel Dörfler 
323*fb81684fSAxel Dörfler 
324*fb81684fSAxel Dörfler DHCPClient::~DHCPClient()
325*fb81684fSAxel Dörfler {
326*fb81684fSAxel Dörfler }
327*fb81684fSAxel Dörfler 
328*fb81684fSAxel Dörfler 
329*fb81684fSAxel Dörfler status_t
330*fb81684fSAxel Dörfler DHCPClient::InitCheck()
331*fb81684fSAxel Dörfler {
332*fb81684fSAxel Dörfler printf("DHCP for %s, status: %s\n", fDevice.String(), strerror(fStatus));
333*fb81684fSAxel Dörfler 	return fStatus;
334*fb81684fSAxel Dörfler }
335*fb81684fSAxel Dörfler 
336*fb81684fSAxel Dörfler 
337*fb81684fSAxel Dörfler void
338*fb81684fSAxel Dörfler DHCPClient::MessageReceived(BMessage* message)
339*fb81684fSAxel Dörfler {
340*fb81684fSAxel Dörfler 	BHandler::MessageReceived(message);
341*fb81684fSAxel Dörfler }
342*fb81684fSAxel Dörfler 
343