1fb81684fSAxel Dörfler /*
242080aceSAugustin Cavalier * Copyright 2006-2018, Haiku, Inc. All Rights Reserved.
3fb81684fSAxel Dörfler * Distributed under the terms of the MIT License.
4fb81684fSAxel Dörfler *
5fb81684fSAxel Dörfler * Authors:
6fb81684fSAxel Dörfler * Axel Dörfler, axeld@pinc-software.de
70f87f52fSStephan Aßmus * Vegard Wærp, vegarwa@online.no
8d5d2fdf0SPhilippe Houdoin * Philippe Houdoin, <phoudoin at haiku-os dot org>
9fb81684fSAxel Dörfler */
10fb81684fSAxel Dörfler
11fb81684fSAxel Dörfler
12fb81684fSAxel Dörfler #include "DHCPClient.h"
13fb81684fSAxel Dörfler
14f9af6566SAxel Dörfler #include <Message.h>
156cc7630fSAxel Dörfler #include <MessageRunner.h>
16af074561SAxel Dörfler #include <NetworkDevice.h>
17af074561SAxel Dörfler #include <NetworkInterface.h>
18f9af6566SAxel Dörfler
196972b91eSPawel Dziepak #include <algorithm>
20f9af6566SAxel Dörfler #include <arpa/inet.h>
21fb81684fSAxel Dörfler #include <errno.h>
22fb81684fSAxel Dörfler #include <stdio.h>
23fb81684fSAxel Dörfler #include <string.h>
24d09c1f8eSPhilippe Houdoin #include <stdlib.h>
25293ed4feSAxel Dörfler #include <syslog.h>
2636bde12dSAxel Dörfler #include <sys/sockio.h>
27f9af6566SAxel Dörfler #include <sys/time.h>
285f25cf08SSiarzhuk Zharski #include <unistd.h>
29fb81684fSAxel Dörfler
304ac66051SAxel Dörfler #include <Debug.h>
314ac66051SAxel Dörfler #include <Message.h>
324ac66051SAxel Dörfler #include <MessageRunner.h>
334ac66051SAxel Dörfler
344ac66051SAxel Dörfler #include "NetServer.h"
354ac66051SAxel Dörfler
36fb81684fSAxel Dörfler
37fb81684fSAxel Dörfler // See RFC 2131 for DHCP, see RFC 1533 for BOOTP/DHCP options
38fb81684fSAxel Dörfler
39fb81684fSAxel Dörfler #define DHCP_CLIENT_PORT 68
40fb81684fSAxel Dörfler #define DHCP_SERVER_PORT 67
41fb81684fSAxel Dörfler
423bbff30dSFredrik Holmqvist #define DEFAULT_TIMEOUT 0.25 // secs
436972b91eSPawel Dziepak #define MAX_TIMEOUT 64 // secs
446972b91eSPawel Dziepak
453bbff30dSFredrik Holmqvist #define AS_USECS(t) (1000000 * t)
463bbff30dSFredrik Holmqvist
476972b91eSPawel Dziepak #define MAX_RETRIES 5
48f9af6566SAxel Dörfler
49fb81684fSAxel Dörfler enum message_opcode {
50fb81684fSAxel Dörfler BOOT_REQUEST = 1,
51fb81684fSAxel Dörfler BOOT_REPLY
52fb81684fSAxel Dörfler };
53fb81684fSAxel Dörfler
54fb81684fSAxel Dörfler enum message_option {
55fb81684fSAxel Dörfler OPTION_MAGIC = 0x63825363,
56fb81684fSAxel Dörfler
57fb81684fSAxel Dörfler // generic options
58fb81684fSAxel Dörfler OPTION_PAD = 0,
59f9af6566SAxel Dörfler OPTION_END = 255,
60f9af6566SAxel Dörfler OPTION_SUBNET_MASK = 1,
6165186fecSAxel Dörfler OPTION_TIME_OFFSET = 2,
62f9af6566SAxel Dörfler OPTION_ROUTER_ADDRESS = 3,
63f9af6566SAxel Dörfler OPTION_DOMAIN_NAME_SERVER = 6,
64f9af6566SAxel Dörfler OPTION_HOST_NAME = 12,
655782c5a3SAxel Dörfler OPTION_DOMAIN_NAME = 15,
66d5d2fdf0SPhilippe Houdoin OPTION_MAX_DATAGRAM_SIZE = 22,
67d5d2fdf0SPhilippe Houdoin OPTION_INTERFACE_MTU = 26,
68fb81684fSAxel Dörfler OPTION_BROADCAST_ADDRESS = 28,
69d5d2fdf0SPhilippe Houdoin OPTION_NETWORK_TIME_PROTOCOL_SERVERS = 42,
7065186fecSAxel Dörfler OPTION_NETBIOS_NAME_SERVER = 44,
7165186fecSAxel Dörfler OPTION_NETBIOS_SCOPE = 47,
72fb81684fSAxel Dörfler
73fb81684fSAxel Dörfler // DHCP specific options
74fb81684fSAxel Dörfler OPTION_REQUEST_IP_ADDRESS = 50,
75fb81684fSAxel Dörfler OPTION_ADDRESS_LEASE_TIME = 51,
76fb81684fSAxel Dörfler OPTION_OVERLOAD = 52,
77fb81684fSAxel Dörfler OPTION_MESSAGE_TYPE = 53,
78cefe2a40SPhilippe Houdoin OPTION_SERVER_ADDRESS = 54,
79fb81684fSAxel Dörfler OPTION_REQUEST_PARAMETERS = 55,
80fb81684fSAxel Dörfler OPTION_ERROR_MESSAGE = 56,
81d5d2fdf0SPhilippe Houdoin OPTION_MAX_MESSAGE_SIZE = 57,
82fb81684fSAxel Dörfler OPTION_RENEWAL_TIME = 58,
83fb81684fSAxel Dörfler OPTION_REBINDING_TIME = 59,
84fb81684fSAxel Dörfler OPTION_CLASS_IDENTIFIER = 60,
85fb81684fSAxel Dörfler OPTION_CLIENT_IDENTIFIER = 61,
86fb81684fSAxel Dörfler };
87fb81684fSAxel Dörfler
88fb81684fSAxel Dörfler enum message_type {
89f9af6566SAxel Dörfler DHCP_NONE = 0,
90d6f83df4SAugustin Cavalier DHCP_DISCOVER = 1,
91d6f83df4SAugustin Cavalier DHCP_OFFER = 2,
92d6f83df4SAugustin Cavalier DHCP_REQUEST = 3,
93d6f83df4SAugustin Cavalier DHCP_DECLINE = 4,
94d6f83df4SAugustin Cavalier DHCP_ACK = 5,
95d6f83df4SAugustin Cavalier DHCP_NACK = 6,
96d6f83df4SAugustin Cavalier DHCP_RELEASE = 7,
97d6f83df4SAugustin Cavalier DHCP_INFORM = 8
98fb81684fSAxel Dörfler };
99fb81684fSAxel Dörfler
100fb81684fSAxel Dörfler struct dhcp_option_cookie {
dhcp_option_cookiedhcp_option_cookie10115ab0bcfSAxel Dörfler dhcp_option_cookie()
10215ab0bcfSAxel Dörfler :
10315ab0bcfSAxel Dörfler state(0),
10415ab0bcfSAxel Dörfler file_has_options(false),
10515ab0bcfSAxel Dörfler server_name_has_options(false)
10615ab0bcfSAxel Dörfler {
10715ab0bcfSAxel Dörfler }
108fb81684fSAxel Dörfler
109fb81684fSAxel Dörfler const uint8* next;
110fb81684fSAxel Dörfler uint8 state;
111fb81684fSAxel Dörfler bool file_has_options;
112fb81684fSAxel Dörfler bool server_name_has_options;
113fb81684fSAxel Dörfler };
114fb81684fSAxel Dörfler
115fb81684fSAxel Dörfler struct dhcp_message {
116fb81684fSAxel Dörfler dhcp_message(message_type type);
117fb81684fSAxel Dörfler
118fb81684fSAxel Dörfler uint8 opcode;
119fb81684fSAxel Dörfler uint8 hardware_type;
120fb81684fSAxel Dörfler uint8 hardware_address_length;
121fb81684fSAxel Dörfler uint8 hop_count;
122fb81684fSAxel Dörfler uint32 transaction_id;
12346ff5400SAxel Dörfler uint16 seconds_since_start;
124fb81684fSAxel Dörfler uint16 flags;
125fb81684fSAxel Dörfler in_addr_t client_address;
126fb81684fSAxel Dörfler in_addr_t your_address;
127fb81684fSAxel Dörfler in_addr_t server_address;
128fb81684fSAxel Dörfler in_addr_t gateway_address;
129fb81684fSAxel Dörfler uint8 mac_address[16];
130fb81684fSAxel Dörfler uint8 server_name[64];
131fb81684fSAxel Dörfler uint8 file[128];
132fb81684fSAxel Dörfler uint32 options_magic;
133fb81684fSAxel Dörfler uint8 options[1260];
134fb81684fSAxel Dörfler
MinSizedhcp_message135fb81684fSAxel Dörfler size_t MinSize() const { return 576; }
136fb81684fSAxel Dörfler size_t Size() const;
137fb81684fSAxel Dörfler
138fb81684fSAxel Dörfler bool HasOptions() const;
139fb81684fSAxel Dörfler bool NextOption(dhcp_option_cookie& cookie, message_option& option,
140fb81684fSAxel Dörfler const uint8*& data, size_t& size) const;
14103f6ab7fSPhilippe Houdoin const uint8* FindOption(message_option which) const;
142fb81684fSAxel Dörfler const uint8* LastOption() const;
14303f6ab7fSPhilippe Houdoin message_type Type() const;
144fb81684fSAxel Dörfler
145a073ba1aSHugo Santos uint8* PrepareMessage(uint8 type);
146fb81684fSAxel Dörfler uint8* PutOption(uint8* options, message_option option);
147fb81684fSAxel Dörfler uint8* PutOption(uint8* options, message_option option, uint8 data);
148fb81684fSAxel Dörfler uint8* PutOption(uint8* options, message_option option, uint16 data);
149fb81684fSAxel Dörfler uint8* PutOption(uint8* options, message_option option, uint32 data);
15015ab0bcfSAxel Dörfler uint8* PutOption(uint8* options, message_option option, const uint8* data,
15115ab0bcfSAxel Dörfler uint32 size);
15237a68cb1SAxel Dörfler uint8* FinishOptions(uint8* options);
1534ac66051SAxel Dörfler
1544ac66051SAxel Dörfler static const char* TypeToString(message_type type);
155fb81684fSAxel Dörfler } _PACKED;
156fb81684fSAxel Dörfler
1573bbff30dSFredrik Holmqvist struct socket_timeout {
socket_timeoutsocket_timeout1583bbff30dSFredrik Holmqvist socket_timeout(int socket)
1593bbff30dSFredrik Holmqvist :
160f1361497SFredrik Holmqvist timeout(time_t(AS_USECS(DEFAULT_TIMEOUT))),
1613bbff30dSFredrik Holmqvist tries(0)
1623bbff30dSFredrik Holmqvist {
1633bbff30dSFredrik Holmqvist UpdateSocket(socket);
1643bbff30dSFredrik Holmqvist }
1653bbff30dSFredrik Holmqvist
166a8b90daaSAdrien Destugues bigtime_t timeout; // in micro secs
1673bbff30dSFredrik Holmqvist uint8 tries;
1683bbff30dSFredrik Holmqvist
1693bbff30dSFredrik Holmqvist bool Shift(int socket, bigtime_t stateMaxTime, const char* device);
1703bbff30dSFredrik Holmqvist void UpdateSocket(int socket) const;
1713bbff30dSFredrik Holmqvist };
1723bbff30dSFredrik Holmqvist
1730cf5e6acSAxel Dörfler #define DHCP_FLAG_BROADCAST 0x8000
174fb81684fSAxel Dörfler
175fb81684fSAxel Dörfler #define ARP_HARDWARE_TYPE_ETHER 1
176fb81684fSAxel Dörfler
1776cc7630fSAxel Dörfler const uint32 kMsgLeaseTime = 'lstm';
1786cc7630fSAxel Dörfler
17937a68cb1SAxel Dörfler static const uint8 kRequestParameters[] = {
18065186fecSAxel Dörfler OPTION_SUBNET_MASK, OPTION_ROUTER_ADDRESS,
181bfd479b3SAxel Dörfler OPTION_DOMAIN_NAME_SERVER, OPTION_BROADCAST_ADDRESS,
182bfd479b3SAxel Dörfler OPTION_DOMAIN_NAME
1835d4d5313SHugo Santos };
1845d4d5313SHugo Santos
185fb81684fSAxel Dörfler
dhcp_message(message_type type)186fb81684fSAxel Dörfler dhcp_message::dhcp_message(message_type type)
187fb81684fSAxel Dörfler {
188fb4527d2SPhilippe Houdoin // ASSERT(this == offsetof(this, opcode));
189fb81684fSAxel Dörfler memset(this, 0, sizeof(*this));
190fb81684fSAxel Dörfler options_magic = htonl(OPTION_MAGIC);
191fb81684fSAxel Dörfler
192a073ba1aSHugo Santos uint8* next = PrepareMessage(type);
193a073ba1aSHugo Santos FinishOptions(next);
194fb81684fSAxel Dörfler }
195fb81684fSAxel Dörfler
196fb81684fSAxel Dörfler
197fb81684fSAxel Dörfler bool
HasOptions() const198fb81684fSAxel Dörfler dhcp_message::HasOptions() const
199fb81684fSAxel Dörfler {
200fb81684fSAxel Dörfler return options_magic == htonl(OPTION_MAGIC);
201fb81684fSAxel Dörfler }
202fb81684fSAxel Dörfler
203fb81684fSAxel Dörfler
204fb81684fSAxel Dörfler bool
NextOption(dhcp_option_cookie & cookie,message_option & option,const uint8 * & data,size_t & size) const205fb81684fSAxel Dörfler dhcp_message::NextOption(dhcp_option_cookie& cookie,
206fb81684fSAxel Dörfler message_option& option, const uint8*& data, size_t& size) const
207fb81684fSAxel Dörfler {
208fb81684fSAxel Dörfler if (!HasOptions())
209fb81684fSAxel Dörfler return false;
210fb81684fSAxel Dörfler
211fb4527d2SPhilippe Houdoin if (cookie.state == 0) {
212fb81684fSAxel Dörfler cookie.state++;
213fb81684fSAxel Dörfler cookie.next = options;
214fb81684fSAxel Dörfler }
215fb81684fSAxel Dörfler
216fb81684fSAxel Dörfler uint32 bytesLeft = 0;
217fb81684fSAxel Dörfler
218fb81684fSAxel Dörfler switch (cookie.state) {
219fb81684fSAxel Dörfler case 1:
220ce36f054SPhilippe Houdoin // options from "options"
221fb4527d2SPhilippe Houdoin bytesLeft = sizeof(options) - (cookie.next - options);
222fb81684fSAxel Dörfler break;
223fb81684fSAxel Dörfler
224fb81684fSAxel Dörfler case 2:
225fb81684fSAxel Dörfler // options from "file"
226fb4527d2SPhilippe Houdoin bytesLeft = sizeof(file) - (cookie.next - file);
227fb81684fSAxel Dörfler break;
228fb81684fSAxel Dörfler
229fb81684fSAxel Dörfler case 3:
230fb81684fSAxel Dörfler // options from "server_name"
231fb4527d2SPhilippe Houdoin bytesLeft = sizeof(server_name) - (cookie.next - server_name);
232fb81684fSAxel Dörfler break;
233fb81684fSAxel Dörfler }
234fb81684fSAxel Dörfler
235fb81684fSAxel Dörfler while (true) {
236fb81684fSAxel Dörfler if (bytesLeft == 0) {
237fb4527d2SPhilippe Houdoin cookie.state++;
238fb4527d2SPhilippe Houdoin
239ce36f054SPhilippe Houdoin // handle option overload in file and/or server_name fields.
240fb4527d2SPhilippe Houdoin switch (cookie.state) {
241fb4527d2SPhilippe Houdoin case 2:
242d5d2fdf0SPhilippe Houdoin // options from "file" field
243fb4527d2SPhilippe Houdoin if (cookie.file_has_options) {
244fb4527d2SPhilippe Houdoin bytesLeft = sizeof(file);
245fb4527d2SPhilippe Houdoin cookie.next = file;
246fb4527d2SPhilippe Houdoin }
247fb4527d2SPhilippe Houdoin break;
248fb4527d2SPhilippe Houdoin
249fb4527d2SPhilippe Houdoin case 3:
250d5d2fdf0SPhilippe Houdoin // options from "server_name" field
251fb4527d2SPhilippe Houdoin if (cookie.server_name_has_options) {
252fb4527d2SPhilippe Houdoin bytesLeft = sizeof(server_name);
253fb4527d2SPhilippe Houdoin cookie.next = server_name;
254fb4527d2SPhilippe Houdoin }
255fb4527d2SPhilippe Houdoin break;
256fb4527d2SPhilippe Houdoin
257d5d2fdf0SPhilippe Houdoin default:
258ce36f054SPhilippe Houdoin // no more place to look for options
259d5d2fdf0SPhilippe Houdoin // if last option is not OPTION_END,
260d5d2fdf0SPhilippe Houdoin // there is no space left for other option!
261d5d2fdf0SPhilippe Houdoin if (option != OPTION_END)
262d5d2fdf0SPhilippe Houdoin cookie.next = NULL;
263fb81684fSAxel Dörfler return false;
264fb81684fSAxel Dörfler }
265fb81684fSAxel Dörfler
266fb4527d2SPhilippe Houdoin if (bytesLeft == 0) {
267d5d2fdf0SPhilippe Houdoin // no options in this state, try next one
268fb4527d2SPhilippe Houdoin continue;
269fb4527d2SPhilippe Houdoin }
270fb4527d2SPhilippe Houdoin }
271fb4527d2SPhilippe Houdoin
272fb81684fSAxel Dörfler option = (message_option)cookie.next[0];
273fb81684fSAxel Dörfler if (option == OPTION_END) {
274fb4527d2SPhilippe Houdoin bytesLeft = 0;
275fb4527d2SPhilippe Houdoin continue;
276fb81684fSAxel Dörfler } else if (option == OPTION_PAD) {
277fb81684fSAxel Dörfler bytesLeft--;
278fb81684fSAxel Dörfler cookie.next++;
279fb81684fSAxel Dörfler continue;
280fb81684fSAxel Dörfler }
281fb81684fSAxel Dörfler
282fb81684fSAxel Dörfler size = cookie.next[1];
283fb81684fSAxel Dörfler data = &cookie.next[2];
284fb81684fSAxel Dörfler cookie.next += 2 + size;
285fb4527d2SPhilippe Houdoin bytesLeft -= 2 + size;
286fb81684fSAxel Dörfler
287fb81684fSAxel Dörfler if (option == OPTION_OVERLOAD) {
288fb81684fSAxel Dörfler cookie.file_has_options = data[0] & 1;
289fb81684fSAxel Dörfler cookie.server_name_has_options = data[0] & 2;
290fb81684fSAxel Dörfler continue;
291fb81684fSAxel Dörfler }
292fb81684fSAxel Dörfler
293fb81684fSAxel Dörfler return true;
294fb81684fSAxel Dörfler }
295fb81684fSAxel Dörfler }
296fb81684fSAxel Dörfler
297fb81684fSAxel Dörfler
29803f6ab7fSPhilippe Houdoin const uint8*
FindOption(message_option which) const29903f6ab7fSPhilippe Houdoin dhcp_message::FindOption(message_option which) const
300f9af6566SAxel Dörfler {
301f9af6566SAxel Dörfler dhcp_option_cookie cookie;
302f9af6566SAxel Dörfler message_option option;
303f9af6566SAxel Dörfler const uint8* data;
304f9af6566SAxel Dörfler size_t size;
305f9af6566SAxel Dörfler while (NextOption(cookie, option, data, size)) {
306f9af6566SAxel Dörfler // iterate through all options
30703f6ab7fSPhilippe Houdoin if (option == which)
30803f6ab7fSPhilippe Houdoin return data;
309f9af6566SAxel Dörfler }
310f9af6566SAxel Dörfler
31103f6ab7fSPhilippe Houdoin return NULL;
312f9af6566SAxel Dörfler }
313f9af6566SAxel Dörfler
314f9af6566SAxel Dörfler
315fb81684fSAxel Dörfler const uint8*
LastOption() const316fb81684fSAxel Dörfler dhcp_message::LastOption() const
317fb81684fSAxel Dörfler {
318fb81684fSAxel Dörfler dhcp_option_cookie cookie;
319fb81684fSAxel Dörfler message_option option;
320fb81684fSAxel Dörfler const uint8* data;
321fb81684fSAxel Dörfler size_t size;
322fb81684fSAxel Dörfler while (NextOption(cookie, option, data, size)) {
323fb81684fSAxel Dörfler // iterate through all options
324fb81684fSAxel Dörfler }
325fb81684fSAxel Dörfler
326fb81684fSAxel Dörfler return cookie.next;
327fb81684fSAxel Dörfler }
328fb81684fSAxel Dörfler
329fb81684fSAxel Dörfler
33003f6ab7fSPhilippe Houdoin message_type
Type() const33103f6ab7fSPhilippe Houdoin dhcp_message::Type() const
33203f6ab7fSPhilippe Houdoin {
33303f6ab7fSPhilippe Houdoin const uint8* data = FindOption(OPTION_MESSAGE_TYPE);
33403f6ab7fSPhilippe Houdoin if (data)
33503f6ab7fSPhilippe Houdoin return (message_type)data[0];
33603f6ab7fSPhilippe Houdoin
33703f6ab7fSPhilippe Houdoin return DHCP_NONE;
33803f6ab7fSPhilippe Houdoin }
33903f6ab7fSPhilippe Houdoin
34003f6ab7fSPhilippe Houdoin
341fb81684fSAxel Dörfler size_t
Size() const342fb81684fSAxel Dörfler dhcp_message::Size() const
343fb81684fSAxel Dörfler {
344fb81684fSAxel Dörfler const uint8* last = LastOption();
345d5d2fdf0SPhilippe Houdoin
346d5d2fdf0SPhilippe Houdoin if (last < options) {
347d5d2fdf0SPhilippe Houdoin // if last option is stored above "options" field, it means
348d5d2fdf0SPhilippe Houdoin // the whole options field and message is already filled...
349d5d2fdf0SPhilippe Houdoin return sizeof(dhcp_message);
350d5d2fdf0SPhilippe Houdoin }
351d5d2fdf0SPhilippe Houdoin
352fb81684fSAxel Dörfler return sizeof(dhcp_message) - sizeof(options) + last + 1 - options;
353fb81684fSAxel Dörfler }
354fb81684fSAxel Dörfler
355fb81684fSAxel Dörfler
356fb81684fSAxel Dörfler uint8*
PrepareMessage(uint8 type)357a073ba1aSHugo Santos dhcp_message::PrepareMessage(uint8 type)
358a073ba1aSHugo Santos {
359a073ba1aSHugo Santos uint8* next = options;
360a073ba1aSHugo Santos next = PutOption(next, OPTION_MESSAGE_TYPE, type);
361d5d2fdf0SPhilippe Houdoin next = PutOption(next, OPTION_MAX_MESSAGE_SIZE,
36215ab0bcfSAxel Dörfler (uint16)htons(sizeof(dhcp_message)));
363a073ba1aSHugo Santos return next;
364a073ba1aSHugo Santos }
365a073ba1aSHugo Santos
36665186fecSAxel Dörfler
367a073ba1aSHugo Santos uint8*
PutOption(uint8 * options,message_option option)368fb81684fSAxel Dörfler dhcp_message::PutOption(uint8* options, message_option option)
369fb81684fSAxel Dörfler {
370fb81684fSAxel Dörfler options[0] = option;
371fb81684fSAxel Dörfler return options + 1;
372fb81684fSAxel Dörfler }
373fb81684fSAxel Dörfler
374fb81684fSAxel Dörfler
375fb81684fSAxel Dörfler uint8*
PutOption(uint8 * options,message_option option,uint8 data)376fb81684fSAxel Dörfler dhcp_message::PutOption(uint8* options, message_option option, uint8 data)
377fb81684fSAxel Dörfler {
378fb81684fSAxel Dörfler return PutOption(options, option, &data, 1);
379fb81684fSAxel Dörfler }
380fb81684fSAxel Dörfler
381fb81684fSAxel Dörfler
382fb81684fSAxel Dörfler uint8*
PutOption(uint8 * options,message_option option,uint16 data)383fb81684fSAxel Dörfler dhcp_message::PutOption(uint8* options, message_option option, uint16 data)
384fb81684fSAxel Dörfler {
385fb81684fSAxel Dörfler return PutOption(options, option, (uint8*)&data, sizeof(data));
386fb81684fSAxel Dörfler }
387fb81684fSAxel Dörfler
388fb81684fSAxel Dörfler
389fb81684fSAxel Dörfler uint8*
PutOption(uint8 * options,message_option option,uint32 data)390fb81684fSAxel Dörfler dhcp_message::PutOption(uint8* options, message_option option, uint32 data)
391fb81684fSAxel Dörfler {
392fb81684fSAxel Dörfler return PutOption(options, option, (uint8*)&data, sizeof(data));
393fb81684fSAxel Dörfler }
394fb81684fSAxel Dörfler
395fb81684fSAxel Dörfler
396fb81684fSAxel Dörfler uint8*
PutOption(uint8 * options,message_option option,const uint8 * data,uint32 size)39715ab0bcfSAxel Dörfler dhcp_message::PutOption(uint8* options, message_option option,
39815ab0bcfSAxel Dörfler const uint8* data, uint32 size)
399fb81684fSAxel Dörfler {
400d5d2fdf0SPhilippe Houdoin // TODO: check enough space is available
401d5d2fdf0SPhilippe Houdoin
402fb81684fSAxel Dörfler options[0] = option;
403fb81684fSAxel Dörfler options[1] = size;
404fb81684fSAxel Dörfler memcpy(&options[2], data, size);
405fb81684fSAxel Dörfler
406fb81684fSAxel Dörfler return options + 2 + size;
407fb81684fSAxel Dörfler }
408fb81684fSAxel Dörfler
409fb81684fSAxel Dörfler
410a073ba1aSHugo Santos uint8*
FinishOptions(uint8 * options)41137a68cb1SAxel Dörfler dhcp_message::FinishOptions(uint8* options)
412a073ba1aSHugo Santos {
41337a68cb1SAxel Dörfler return PutOption(options, OPTION_END);
414a073ba1aSHugo Santos }
415a073ba1aSHugo Santos
416a073ba1aSHugo Santos
4174ac66051SAxel Dörfler /*static*/ const char*
TypeToString(message_type type)4184ac66051SAxel Dörfler dhcp_message::TypeToString(message_type type)
4194ac66051SAxel Dörfler {
4204ac66051SAxel Dörfler switch (type) {
4214ac66051SAxel Dörfler #define CASE(x) case x: return #x;
4224ac66051SAxel Dörfler CASE(DHCP_NONE)
4234ac66051SAxel Dörfler CASE(DHCP_DISCOVER)
4244ac66051SAxel Dörfler CASE(DHCP_OFFER)
4254ac66051SAxel Dörfler CASE(DHCP_REQUEST)
4264ac66051SAxel Dörfler CASE(DHCP_DECLINE)
4274ac66051SAxel Dörfler CASE(DHCP_ACK)
4284ac66051SAxel Dörfler CASE(DHCP_NACK)
4294ac66051SAxel Dörfler CASE(DHCP_RELEASE)
4304ac66051SAxel Dörfler CASE(DHCP_INFORM)
4314ac66051SAxel Dörfler #undef CASE
4324ac66051SAxel Dörfler }
4334ac66051SAxel Dörfler
4344ac66051SAxel Dörfler return "<unknown>";
4354ac66051SAxel Dörfler }
4364ac66051SAxel Dörfler
4374ac66051SAxel Dörfler
4383bbff30dSFredrik Holmqvist void
UpdateSocket(int socket) const4393bbff30dSFredrik Holmqvist socket_timeout::UpdateSocket(int socket) const
4403bbff30dSFredrik Holmqvist {
4413bbff30dSFredrik Holmqvist struct timeval value;
4423bbff30dSFredrik Holmqvist value.tv_sec = timeout / 1000000;
4433bbff30dSFredrik Holmqvist value.tv_usec = timeout % 1000000;
4443bbff30dSFredrik Holmqvist setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &value, sizeof(value));
4453bbff30dSFredrik Holmqvist }
4463bbff30dSFredrik Holmqvist
4473bbff30dSFredrik Holmqvist
4483bbff30dSFredrik Holmqvist bool
Shift(int socket,bigtime_t stateMaxTime,const char * device)4493bbff30dSFredrik Holmqvist socket_timeout::Shift(int socket, bigtime_t stateMaxTime, const char* device)
4503bbff30dSFredrik Holmqvist {
451a8b90daaSAdrien Destugues if (tries == UINT8_MAX)
452a8b90daaSAdrien Destugues return false;
453a8b90daaSAdrien Destugues
4543bbff30dSFredrik Holmqvist tries++;
4553bbff30dSFredrik Holmqvist
4563bbff30dSFredrik Holmqvist if (tries > MAX_RETRIES) {
457a8b90daaSAdrien Destugues bigtime_t now = system_time();
458a8b90daaSAdrien Destugues if (stateMaxTime == -1 || stateMaxTime < now)
4593bbff30dSFredrik Holmqvist return false;
460a8b90daaSAdrien Destugues bigtime_t remaining = (stateMaxTime - now) / 2 + 1;
461a8b90daaSAdrien Destugues timeout = std::max(remaining, bigtime_t(AS_USECS(60)));
462a8b90daaSAdrien Destugues } else
463a8b90daaSAdrien Destugues timeout += timeout;
464a8b90daaSAdrien Destugues
465a8b90daaSAdrien Destugues if (timeout > AS_USECS(MAX_TIMEOUT))
466a8b90daaSAdrien Destugues timeout = AS_USECS(MAX_TIMEOUT);
4673bbff30dSFredrik Holmqvist
46877c1611fSMurai Takashi syslog(LOG_DEBUG, "%s: Timeout shift: %" B_PRIdTIME " msecs (try %" B_PRIu8 ")\n",
46942080aceSAugustin Cavalier device, timeout / 1000, tries);
4703bbff30dSFredrik Holmqvist
4713bbff30dSFredrik Holmqvist UpdateSocket(socket);
4723bbff30dSFredrik Holmqvist return true;
4733bbff30dSFredrik Holmqvist }
4743bbff30dSFredrik Holmqvist
4753bbff30dSFredrik Holmqvist
476fb81684fSAxel Dörfler // #pragma mark -
477fb81684fSAxel Dörfler
478fb81684fSAxel Dörfler
DHCPClient(BMessenger target,const char * device)479f9af6566SAxel Dörfler DHCPClient::DHCPClient(BMessenger target, const char* device)
480af074561SAxel Dörfler :
481af074561SAxel Dörfler AutoconfigClient("dhcp", target, device),
4826cc7630fSAxel Dörfler fConfiguration(kMsgConfigureInterface),
48316e8f13aSAxel Dörfler fResolverConfiguration(kMsgConfigureResolver),
4846cc7630fSAxel Dörfler fRunner(NULL),
4850892800fSPhilippe Houdoin fAssignedAddress(0),
4862df71589SAdrien Destugues fServer(AF_INET, NULL, DHCP_SERVER_PORT, B_UNCONFIGURED_ADDRESS_FAMILIES),
487*d8df5637SMichael Lotz fStartTime(0),
488*d8df5637SMichael Lotz fRequestTime(0),
489*d8df5637SMichael Lotz fRenewalTime(0),
490*d8df5637SMichael Lotz fRebindingTime(0),
491c1264353SStephan Aßmus fLeaseTime(0)
492fb81684fSAxel Dörfler {
4936972b91eSPawel Dziepak fTransactionID = (uint32)system_time() ^ rand();
4947ca2da76SFredrik Holmqvist
495af074561SAxel Dörfler BNetworkAddress link;
496af074561SAxel Dörfler BNetworkInterface interface(device);
497af074561SAxel Dörfler fStatus = interface.GetHardwareAddress(link);
498af074561SAxel Dörfler if (fStatus != B_OK)
499fb81684fSAxel Dörfler return;
500fb81684fSAxel Dörfler
501af074561SAxel Dörfler memcpy(fMAC, link.LinkLevelAddress(), sizeof(fMAC));
502293ed4feSAxel Dörfler
5030892800fSPhilippe Houdoin if ((interface.Flags() & IFF_AUTO_CONFIGURED) != 0) {
5040892800fSPhilippe Houdoin // Check for interface previous auto-configured address, if any.
5050892800fSPhilippe Houdoin BNetworkInterfaceAddress interfaceAddress;
5060892800fSPhilippe Houdoin int index = interface.FindFirstAddress(AF_INET);
5070892800fSPhilippe Houdoin if (index >= 0
5080892800fSPhilippe Houdoin && interface.GetAddressAt(index, interfaceAddress) == B_OK) {
5090892800fSPhilippe Houdoin BNetworkAddress address = interfaceAddress.Address();
5100892800fSPhilippe Houdoin const sockaddr_in& addr = (sockaddr_in&)address.SockAddr();
5110892800fSPhilippe Houdoin fAssignedAddress = addr.sin_addr.s_addr;
512b6ba1daaSPhilippe Houdoin
513b6ba1daaSPhilippe Houdoin if ((ntohl(fAssignedAddress) & IN_CLASSB_NET) == 0xa9fe0000) {
514b6ba1daaSPhilippe Houdoin // previous auto-configured address is a link-local one:
515b6ba1daaSPhilippe Houdoin // there is no point asking a DHCP server to renew such
516b6ba1daaSPhilippe Houdoin // server-less assigned address...
517b6ba1daaSPhilippe Houdoin fAssignedAddress = 0;
518b6ba1daaSPhilippe Houdoin }
5190892800fSPhilippe Houdoin }
5200892800fSPhilippe Houdoin }
5210892800fSPhilippe Houdoin
522293ed4feSAxel Dörfler openlog_thread("DHCP", 0, LOG_DAEMON);
5236cc7630fSAxel Dörfler }
5246cc7630fSAxel Dörfler
5256cc7630fSAxel Dörfler
~DHCPClient()5266cc7630fSAxel Dörfler DHCPClient::~DHCPClient()
5276cc7630fSAxel Dörfler {
5286cc7630fSAxel Dörfler if (fStatus != B_OK)
5296cc7630fSAxel Dörfler return;
5306cc7630fSAxel Dörfler
5316cc7630fSAxel Dörfler delete fRunner;
5320ce7725eSAxel Dörfler
533fb81684fSAxel Dörfler int socket = ::socket(AF_INET, SOCK_DGRAM, 0);
5346cc7630fSAxel Dörfler if (socket < 0)
535fb81684fSAxel Dörfler return;
5366cc7630fSAxel Dörfler
5376cc7630fSAxel Dörfler // release lease
5386cc7630fSAxel Dörfler
5396cc7630fSAxel Dörfler dhcp_message release(DHCP_RELEASE);
54046ff5400SAxel Dörfler _PrepareMessage(release, BOUND);
5416cc7630fSAxel Dörfler
5426cc7630fSAxel Dörfler _SendMessage(socket, release, fServer);
5436cc7630fSAxel Dörfler close(socket);
544293ed4feSAxel Dörfler
545293ed4feSAxel Dörfler closelog_thread();
546fb81684fSAxel Dörfler }
547fb81684fSAxel Dörfler
5486cc7630fSAxel Dörfler
5496cc7630fSAxel Dörfler status_t
Initialize()5506cc7630fSAxel Dörfler DHCPClient::Initialize()
5516cc7630fSAxel Dörfler {
5520892800fSPhilippe Houdoin fStatus = _Negotiate(fAssignedAddress == 0 ? INIT : INIT_REBOOT);
5530892800fSPhilippe Houdoin syslog(LOG_DEBUG, "%s: DHCP status = %s\n", Device(), strerror(fStatus));
5546cc7630fSAxel Dörfler return fStatus;
5556cc7630fSAxel Dörfler }
5566cc7630fSAxel Dörfler
5576cc7630fSAxel Dörfler
5586cc7630fSAxel Dörfler status_t
_Negotiate(dhcp_state state)5596cc7630fSAxel Dörfler DHCPClient::_Negotiate(dhcp_state state)
5606cc7630fSAxel Dörfler {
5616972b91eSPawel Dziepak if (state == BOUND)
5626972b91eSPawel Dziepak return B_OK;
5636972b91eSPawel Dziepak
5646972b91eSPawel Dziepak fStartTime = system_time();
5656972b91eSPawel Dziepak fTransactionID++;
5666972b91eSPawel Dziepak
5676972b91eSPawel Dziepak char hostName[MAXHOSTNAMELEN];
5686972b91eSPawel Dziepak if (gethostname(hostName, MAXHOSTNAMELEN) == 0)
5696972b91eSPawel Dziepak fHostName.SetTo(hostName, MAXHOSTNAMELEN);
5706972b91eSPawel Dziepak else
5716972b91eSPawel Dziepak fHostName.Truncate(0);
5726972b91eSPawel Dziepak
5736cc7630fSAxel Dörfler int socket = ::socket(AF_INET, SOCK_DGRAM, 0);
5746cc7630fSAxel Dörfler if (socket < 0)
5756cc7630fSAxel Dörfler return errno;
5766cc7630fSAxel Dörfler
5772b829b04SBruno G. Albuquerque // Enable reusing the port. This is needed in case there is more
5782b829b04SBruno G. Albuquerque // than 1 interface that needs to be configured. Note that the only reason
5792b829b04SBruno G. Albuquerque // this works is because there is code below to bind to a specific
5802b829b04SBruno G. Albuquerque // interface.
5812b829b04SBruno G. Albuquerque int option = 1;
5822b829b04SBruno G. Albuquerque setsockopt(socket, SOL_SOCKET, SO_REUSEPORT, &option, sizeof(option));
5832b829b04SBruno G. Albuquerque
5846972b91eSPawel Dziepak BNetworkAddress local;
5856972b91eSPawel Dziepak local.SetToWildcard(AF_INET, DHCP_CLIENT_PORT);
5866972b91eSPawel Dziepak
5876972b91eSPawel Dziepak option = 1;
5886972b91eSPawel Dziepak setsockopt(socket, SOL_SOCKET, SO_BROADCAST, &option, sizeof(option));
5896972b91eSPawel Dziepak
59077206143SAxel Dörfler if (bind(socket, local, local.Length()) < 0) {
591fb81684fSAxel Dörfler close(socket);
5926cc7630fSAxel Dörfler return errno;
593fb81684fSAxel Dörfler }
594fb81684fSAxel Dörfler
5956cc7630fSAxel Dörfler bigtime_t previousLeaseTime = fLeaseTime;
596fb81684fSAxel Dörfler
5976972b91eSPawel Dziepak status_t status = B_OK;
5980892800fSPhilippe Houdoin while (state != BOUND) {
5996972b91eSPawel Dziepak status = _StateTransition(socket, state);
6006972b91eSPawel Dziepak if (status != B_OK && (state == SELECTING || state == REBOOTING))
6016972b91eSPawel Dziepak break;
6026972b91eSPawel Dziepak }
6036972b91eSPawel Dziepak
6046cc7630fSAxel Dörfler close(socket);
6056972b91eSPawel Dziepak
6066972b91eSPawel Dziepak if (fLeaseTime == 0)
6076972b91eSPawel Dziepak fLeaseTime = previousLeaseTime;
6086972b91eSPawel Dziepak if (fLeaseTime == 0)
6096972b91eSPawel Dziepak fLeaseTime = 60;
6106972b91eSPawel Dziepak
6116972b91eSPawel Dziepak if (fRenewalTime == 0)
6126972b91eSPawel Dziepak fRenewalTime = fLeaseTime / 2;
6136972b91eSPawel Dziepak if (fRebindingTime == 0)
6146972b91eSPawel Dziepak fRebindingTime = fLeaseTime * 7 / 8;
6156972b91eSPawel Dziepak fLeaseTime += fRequestTime;
6166972b91eSPawel Dziepak fRenewalTime += fRequestTime;
6176972b91eSPawel Dziepak fRebindingTime += fRequestTime;
6186972b91eSPawel Dziepak _RestartLease(fRenewalTime);
6196972b91eSPawel Dziepak
6206972b91eSPawel Dziepak fStatus = status;
6216972b91eSPawel Dziepak if (status)
6226972b91eSPawel Dziepak return status;
6236972b91eSPawel Dziepak
6246972b91eSPawel Dziepak // configure interface
6256972b91eSPawel Dziepak BMessage reply;
6266972b91eSPawel Dziepak status = Target().SendMessage(&fConfiguration, &reply);
6276972b91eSPawel Dziepak if (status == B_OK)
6286972b91eSPawel Dziepak status = reply.FindInt32("status", &fStatus);
6296972b91eSPawel Dziepak
6306972b91eSPawel Dziepak // configure resolver
6316972b91eSPawel Dziepak reply.MakeEmpty();
6326972b91eSPawel Dziepak fResolverConfiguration.AddString("device", Device());
6336972b91eSPawel Dziepak status = Target().SendMessage(&fResolverConfiguration, &reply);
6346972b91eSPawel Dziepak if (status == B_OK)
6356972b91eSPawel Dziepak status = reply.FindInt32("status", &fStatus);
6366972b91eSPawel Dziepak return status;
637f9af6566SAxel Dörfler }
638f9af6566SAxel Dörfler
639b6ba1daaSPhilippe Houdoin
6406972b91eSPawel Dziepak status_t
_GotMessage(dhcp_state & state,dhcp_message * message)6416972b91eSPawel Dziepak DHCPClient::_GotMessage(dhcp_state& state, dhcp_message* message)
642f9af6566SAxel Dörfler {
6436972b91eSPawel Dziepak switch (state) {
6446972b91eSPawel Dziepak case SELECTING:
6456972b91eSPawel Dziepak if (message->Type() == DHCP_OFFER) {
6466972b91eSPawel Dziepak state = REQUESTING;
647f9af6566SAxel Dörfler
648f9af6566SAxel Dörfler fAssignedAddress = message->your_address;
64969b5cacbSPhilippe Houdoin syslog(LOG_INFO, " your_address: %s\n",
65069b5cacbSPhilippe Houdoin _AddressToString(fAssignedAddress).String());
651f9af6566SAxel Dörfler
65210cc12daSAxel Dörfler fConfiguration.MakeEmpty();
653293ed4feSAxel Dörfler fConfiguration.AddString("device", Device());
6540892800fSPhilippe Houdoin fConfiguration.AddBool("auto_configured", true);
655f9af6566SAxel Dörfler
656f9af6566SAxel Dörfler BMessage address;
657f9af6566SAxel Dörfler address.AddString("family", "inet");
658d5d2fdf0SPhilippe Houdoin address.AddString("address", _AddressToString(fAssignedAddress));
65916e8f13aSAxel Dörfler fResolverConfiguration.MakeEmpty();
66016e8f13aSAxel Dörfler _ParseOptions(*message, address, fResolverConfiguration);
661f9af6566SAxel Dörfler
66210cc12daSAxel Dörfler fConfiguration.AddMessage("address", &address);
6636972b91eSPawel Dziepak return B_OK;
664f9af6566SAxel Dörfler }
665f9af6566SAxel Dörfler
6666972b91eSPawel Dziepak return B_BAD_VALUE;
667f9af6566SAxel Dörfler
6686972b91eSPawel Dziepak case REBOOTING:
6696972b91eSPawel Dziepak case REBINDING:
6706972b91eSPawel Dziepak case RENEWING:
6716972b91eSPawel Dziepak case REQUESTING:
6726972b91eSPawel Dziepak if (message->Type() == DHCP_ACK) {
6736cc7630fSAxel Dörfler // TODO: we might want to configure the stuff, don't we?
6746cc7630fSAxel Dörfler BMessage address;
67516e8f13aSAxel Dörfler fResolverConfiguration.MakeEmpty();
67616e8f13aSAxel Dörfler _ParseOptions(*message, address, fResolverConfiguration);
6774ac66051SAxel Dörfler // TODO: currently, only lease time and DNS is updated this
6784ac66051SAxel Dörfler // way
6796cc7630fSAxel Dörfler
680f9af6566SAxel Dörfler // our address request has been acknowledged
6810892800fSPhilippe Houdoin state = BOUND;
68210cc12daSAxel Dörfler
6836972b91eSPawel Dziepak return B_OK;
68410cc12daSAxel Dörfler }
685f9af6566SAxel Dörfler
6866972b91eSPawel Dziepak if (message->Type() == DHCP_NACK) {
6870892800fSPhilippe Houdoin // server reject our request on previous assigned address
6880892800fSPhilippe Houdoin // back to square one...
6890892800fSPhilippe Houdoin fAssignedAddress = 0;
6906972b91eSPawel Dziepak state = INIT;
6916972b91eSPawel Dziepak return B_OK;
6920892800fSPhilippe Houdoin }
6930892800fSPhilippe Houdoin
6946972b91eSPawel Dziepak default:
6956972b91eSPawel Dziepak return B_BAD_VALUE;
6966972b91eSPawel Dziepak }
6976972b91eSPawel Dziepak }
6986972b91eSPawel Dziepak
6996972b91eSPawel Dziepak
7006972b91eSPawel Dziepak status_t
_StateTransition(int socket,dhcp_state & state)7016972b91eSPawel Dziepak DHCPClient::_StateTransition(int socket, dhcp_state& state)
7026972b91eSPawel Dziepak {
7036972b91eSPawel Dziepak if (state == INIT) {
7046972b91eSPawel Dziepak // The local interface does not have an address yet, bind the socket
7056972b91eSPawel Dziepak // to the device directly.
7066972b91eSPawel Dziepak BNetworkDevice device(Device());
7076972b91eSPawel Dziepak int index = device.Index();
7086972b91eSPawel Dziepak
7096972b91eSPawel Dziepak setsockopt(socket, SOL_SOCKET, SO_BINDTODEVICE, &index, sizeof(int));
7106972b91eSPawel Dziepak }
7116972b91eSPawel Dziepak
7126972b91eSPawel Dziepak BNetworkAddress broadcast;
7136972b91eSPawel Dziepak broadcast.SetToBroadcast(AF_INET, DHCP_SERVER_PORT);
7146972b91eSPawel Dziepak
7153bbff30dSFredrik Holmqvist socket_timeout timeout(socket);
7166972b91eSPawel Dziepak
7176972b91eSPawel Dziepak dhcp_message discover(DHCP_DISCOVER);
7186972b91eSPawel Dziepak _PrepareMessage(discover, state);
7196972b91eSPawel Dziepak
7206972b91eSPawel Dziepak dhcp_message request(DHCP_REQUEST);
7216972b91eSPawel Dziepak _PrepareMessage(request, state);
7226972b91eSPawel Dziepak
7236972b91eSPawel Dziepak bool skipRequest = false;
7246972b91eSPawel Dziepak dhcp_state originalState = state;
7256972b91eSPawel Dziepak fRequestTime = system_time();
7266972b91eSPawel Dziepak while (true) {
7276972b91eSPawel Dziepak if (!skipRequest) {
7286972b91eSPawel Dziepak _SendMessage(socket, originalState == INIT ? discover : request,
7296972b91eSPawel Dziepak originalState == RENEWING ? fServer : broadcast);
7306972b91eSPawel Dziepak
7316972b91eSPawel Dziepak if (originalState == INIT)
7326972b91eSPawel Dziepak state = SELECTING;
7336972b91eSPawel Dziepak else if (originalState == INIT_REBOOT)
7346972b91eSPawel Dziepak state = REBOOTING;
7356972b91eSPawel Dziepak }
7366972b91eSPawel Dziepak
7376972b91eSPawel Dziepak char buffer[2048];
7386972b91eSPawel Dziepak struct sockaddr_in from;
7396972b91eSPawel Dziepak socklen_t fromLength = sizeof(from);
7406972b91eSPawel Dziepak ssize_t bytesReceived = recvfrom(socket, buffer, sizeof(buffer),
7416972b91eSPawel Dziepak 0, (struct sockaddr*)&from, &fromLength);
7426972b91eSPawel Dziepak if (bytesReceived < 0 && errno == B_TIMED_OUT) {
7436972b91eSPawel Dziepak // depending on the state, we'll just try again
7443bbff30dSFredrik Holmqvist if (!_TimeoutShift(socket, state, timeout))
7456972b91eSPawel Dziepak return B_TIMED_OUT;
7466972b91eSPawel Dziepak skipRequest = false;
7476972b91eSPawel Dziepak continue;
7486972b91eSPawel Dziepak } else if (bytesReceived < 0)
7496972b91eSPawel Dziepak return errno;
7506972b91eSPawel Dziepak
7516972b91eSPawel Dziepak skipRequest = true;
7526972b91eSPawel Dziepak dhcp_message* message = (dhcp_message*)buffer;
7536972b91eSPawel Dziepak if (message->transaction_id != htonl(fTransactionID)
7546972b91eSPawel Dziepak || !message->HasOptions()
7556972b91eSPawel Dziepak || memcmp(message->mac_address, discover.mac_address,
7566972b91eSPawel Dziepak discover.hardware_address_length)) {
7576972b91eSPawel Dziepak // this message is not for us
75842080aceSAugustin Cavalier syslog(LOG_DEBUG, "%s: Ignoring %s not for us from %s\n",
75942080aceSAugustin Cavalier Device(), dhcp_message::TypeToString(message->Type()),
76042080aceSAugustin Cavalier _AddressToString(from.sin_addr.s_addr).String());
7616972b91eSPawel Dziepak continue;
7626972b91eSPawel Dziepak }
7636972b91eSPawel Dziepak
7646972b91eSPawel Dziepak syslog(LOG_DEBUG, "%s: Received %s from %s\n",
7656972b91eSPawel Dziepak Device(), dhcp_message::TypeToString(message->Type()),
7666972b91eSPawel Dziepak _AddressToString(from.sin_addr.s_addr).String());
7676972b91eSPawel Dziepak
7686972b91eSPawel Dziepak if (_GotMessage(state, message) == B_OK)
769f9af6566SAxel Dörfler break;
770f9af6566SAxel Dörfler }
771f9af6566SAxel Dörfler
7726972b91eSPawel Dziepak return B_OK;
773fb81684fSAxel Dörfler }
774fb81684fSAxel Dörfler
775fb81684fSAxel Dörfler
7766cc7630fSAxel Dörfler void
_RestartLease(bigtime_t leaseTime)7776cc7630fSAxel Dörfler DHCPClient::_RestartLease(bigtime_t leaseTime)
778fb81684fSAxel Dörfler {
7796cc7630fSAxel Dörfler if (leaseTime == 0)
780f9af6566SAxel Dörfler return;
781f9af6566SAxel Dörfler
7826cc7630fSAxel Dörfler BMessage lease(kMsgLeaseTime);
7836972b91eSPawel Dziepak fRunner = new BMessageRunner(this, &lease, leaseTime - system_time(), 1);
784f9af6566SAxel Dörfler }
785f9af6566SAxel Dörfler
786f9af6566SAxel Dörfler
787f9af6566SAxel Dörfler void
_ParseOptions(dhcp_message & message,BMessage & address,BMessage & resolverConfiguration)78816e8f13aSAxel Dörfler DHCPClient::_ParseOptions(dhcp_message& message, BMessage& address,
78916e8f13aSAxel Dörfler BMessage& resolverConfiguration)
7900ce7725eSAxel Dörfler {
7910ce7725eSAxel Dörfler dhcp_option_cookie cookie;
7920ce7725eSAxel Dörfler message_option option;
7930ce7725eSAxel Dörfler const uint8* data;
7940ce7725eSAxel Dörfler size_t size;
7950ce7725eSAxel Dörfler while (message.NextOption(cookie, option, data, size)) {
7960ce7725eSAxel Dörfler // iterate through all options
7970ce7725eSAxel Dörfler switch (option) {
7980ce7725eSAxel Dörfler case OPTION_ROUTER_ADDRESS:
799cefe2a40SPhilippe Houdoin syslog(LOG_DEBUG, " gateway: %s\n",
80069b5cacbSPhilippe Houdoin _AddressToString(data).String());
801d5d2fdf0SPhilippe Houdoin address.AddString("gateway", _AddressToString(data));
8020ce7725eSAxel Dörfler break;
8030ce7725eSAxel Dörfler case OPTION_SUBNET_MASK:
804cefe2a40SPhilippe Houdoin syslog(LOG_DEBUG, " subnet: %s\n",
80569b5cacbSPhilippe Houdoin _AddressToString(data).String());
806d5d2fdf0SPhilippe Houdoin address.AddString("mask", _AddressToString(data));
8070ce7725eSAxel Dörfler break;
8085782c5a3SAxel Dörfler case OPTION_BROADCAST_ADDRESS:
809cefe2a40SPhilippe Houdoin syslog(LOG_DEBUG, " broadcast: %s\n",
81069b5cacbSPhilippe Houdoin _AddressToString(data).String());
811d5d2fdf0SPhilippe Houdoin address.AddString("broadcast", _AddressToString(data));
8125782c5a3SAxel Dörfler break;
8130ce7725eSAxel Dörfler case OPTION_DOMAIN_NAME_SERVER:
814a552ec13SAxel Dörfler {
8150ce7725eSAxel Dörfler for (uint32 i = 0; i < size / 4; i++) {
816cefe2a40SPhilippe Houdoin syslog(LOG_DEBUG, " nameserver[%d]: %s\n", i,
817d5d2fdf0SPhilippe Houdoin _AddressToString(&data[i * 4]).String());
81816e8f13aSAxel Dörfler resolverConfiguration.AddString("nameserver",
819d5d2fdf0SPhilippe Houdoin _AddressToString(&data[i * 4]).String());
82015ab0bcfSAxel Dörfler }
82116e8f13aSAxel Dörfler resolverConfiguration.AddInt32("nameserver_count",
82216e8f13aSAxel Dörfler size / 4);
8230ce7725eSAxel Dörfler break;
824a552ec13SAxel Dörfler }
825cefe2a40SPhilippe Houdoin case OPTION_SERVER_ADDRESS:
826f9ab315eSAugustin Cavalier {
827cefe2a40SPhilippe Houdoin syslog(LOG_DEBUG, " server: %s\n",
82869b5cacbSPhilippe Houdoin _AddressToString(data).String());
829f9ab315eSAugustin Cavalier status_t status = fServer.SetAddress(*(in_addr_t*)data);
830f9ab315eSAugustin Cavalier if (status != B_OK) {
831f9ab315eSAugustin Cavalier syslog(LOG_ERR, " BNetworkAddress::SetAddress failed with %s!\n",
832f9ab315eSAugustin Cavalier strerror(status));
833f9ab315eSAugustin Cavalier fServer.Unset();
834f9ab315eSAugustin Cavalier }
8350ce7725eSAxel Dörfler break;
836f9ab315eSAugustin Cavalier }
83746ff5400SAxel Dörfler
8380ce7725eSAxel Dörfler case OPTION_ADDRESS_LEASE_TIME:
839cefe2a40SPhilippe Houdoin syslog(LOG_DEBUG, " lease time: %lu seconds\n",
84069b5cacbSPhilippe Houdoin ntohl(*(uint32*)data));
84169b5cacbSPhilippe Houdoin fLeaseTime = ntohl(*(uint32*)data) * 1000000LL;
8420ce7725eSAxel Dörfler break;
8431a4e8e7bSAxel Dörfler case OPTION_RENEWAL_TIME:
844cefe2a40SPhilippe Houdoin syslog(LOG_DEBUG, " renewal time: %lu seconds\n",
84569b5cacbSPhilippe Houdoin ntohl(*(uint32*)data));
84669b5cacbSPhilippe Houdoin fRenewalTime = ntohl(*(uint32*)data) * 1000000LL;
84746ff5400SAxel Dörfler break;
8481a4e8e7bSAxel Dörfler case OPTION_REBINDING_TIME:
849cefe2a40SPhilippe Houdoin syslog(LOG_DEBUG, " rebinding time: %lu seconds\n",
85069b5cacbSPhilippe Houdoin ntohl(*(uint32*)data));
85169b5cacbSPhilippe Houdoin fRebindingTime = ntohl(*(uint32*)data) * 1000000LL;
8521a4e8e7bSAxel Dörfler break;
8531a4e8e7bSAxel Dörfler
8540ce7725eSAxel Dörfler case OPTION_HOST_NAME:
855cefe2a40SPhilippe Houdoin syslog(LOG_DEBUG, " host name: \"%.*s\"\n", (int)size,
85616e8f13aSAxel Dörfler (const char*)data);
8570ce7725eSAxel Dörfler break;
8585782c5a3SAxel Dörfler
8595782c5a3SAxel Dörfler case OPTION_DOMAIN_NAME:
8605782c5a3SAxel Dörfler {
86116e8f13aSAxel Dörfler char domain[256];
86216e8f13aSAxel Dörfler strlcpy(domain, (const char*)data,
86316e8f13aSAxel Dörfler min_c(size + 1, sizeof(domain)));
8640f87f52fSStephan Aßmus
865cefe2a40SPhilippe Houdoin syslog(LOG_DEBUG, " domain name: \"%s\"\n", domain);
8660f87f52fSStephan Aßmus
86716e8f13aSAxel Dörfler resolverConfiguration.AddString("domain", domain);
8685782c5a3SAxel Dörfler break;
8695782c5a3SAxel Dörfler }
8700ce7725eSAxel Dörfler
8710ce7725eSAxel Dörfler case OPTION_MESSAGE_TYPE:
8720ce7725eSAxel Dörfler break;
8730ce7725eSAxel Dörfler
874d5d2fdf0SPhilippe Houdoin case OPTION_ERROR_MESSAGE:
87569b5cacbSPhilippe Houdoin syslog(LOG_INFO, " error message: \"%.*s\"\n", (int)size,
876d5d2fdf0SPhilippe Houdoin (const char*)data);
877d5d2fdf0SPhilippe Houdoin break;
878d5d2fdf0SPhilippe Houdoin
8790ce7725eSAxel Dörfler default:
88077c1611fSMurai Takashi syslog(LOG_DEBUG, " UNKNOWN OPTION %" B_PRIu32 " (0x%" B_PRIx32 ")\n",
88169b5cacbSPhilippe Houdoin (uint32)option, (uint32)option);
8820ce7725eSAxel Dörfler break;
8830ce7725eSAxel Dörfler }
8840ce7725eSAxel Dörfler }
8850ce7725eSAxel Dörfler }
8860ce7725eSAxel Dörfler
8870ce7725eSAxel Dörfler
8880ce7725eSAxel Dörfler void
_PrepareMessage(dhcp_message & message,dhcp_state state)88946ff5400SAxel Dörfler DHCPClient::_PrepareMessage(dhcp_message& message, dhcp_state state)
8900ce7725eSAxel Dörfler {
8910ce7725eSAxel Dörfler message.opcode = BOOT_REQUEST;
8920ce7725eSAxel Dörfler message.hardware_type = ARP_HARDWARE_TYPE_ETHER;
8930ce7725eSAxel Dörfler message.hardware_address_length = 6;
8940ce7725eSAxel Dörfler message.transaction_id = htonl(fTransactionID);
895bcc8dadaSAxel Dörfler message.seconds_since_start = htons(min_c((system_time() - fStartTime)
89615ab0bcfSAxel Dörfler / 1000000LL, 65535));
8970ce7725eSAxel Dörfler memcpy(message.mac_address, fMAC, 6);
8980ce7725eSAxel Dörfler
89946ff5400SAxel Dörfler message_type type = message.Type();
90046ff5400SAxel Dörfler
901a073ba1aSHugo Santos uint8 *next = message.PrepareMessage(type);
902d5d2fdf0SPhilippe Houdoin
903d5d2fdf0SPhilippe Houdoin switch (type) {
904d5d2fdf0SPhilippe Houdoin case DHCP_DISCOVER:
905d5d2fdf0SPhilippe Houdoin next = message.PutOption(next, OPTION_REQUEST_PARAMETERS,
906d5d2fdf0SPhilippe Houdoin kRequestParameters, sizeof(kRequestParameters));
9075f25cf08SSiarzhuk Zharski
9085f25cf08SSiarzhuk Zharski if (fHostName.Length() > 0) {
9095f25cf08SSiarzhuk Zharski next = message.PutOption(next, OPTION_HOST_NAME,
9105f25cf08SSiarzhuk Zharski reinterpret_cast<const uint8*>(fHostName.String()),
9115f25cf08SSiarzhuk Zharski fHostName.Length());
9125f25cf08SSiarzhuk Zharski }
913d5d2fdf0SPhilippe Houdoin break;
914d5d2fdf0SPhilippe Houdoin
915d5d2fdf0SPhilippe Houdoin case DHCP_REQUEST:
916d5d2fdf0SPhilippe Houdoin next = message.PutOption(next, OPTION_REQUEST_PARAMETERS,
917d5d2fdf0SPhilippe Houdoin kRequestParameters, sizeof(kRequestParameters));
918d5d2fdf0SPhilippe Houdoin
9195f25cf08SSiarzhuk Zharski if (fHostName.Length() > 0) {
9205f25cf08SSiarzhuk Zharski next = message.PutOption(next, OPTION_HOST_NAME,
9215f25cf08SSiarzhuk Zharski reinterpret_cast<const uint8*>(fHostName.String()),
9225f25cf08SSiarzhuk Zharski fHostName.Length());
9235f25cf08SSiarzhuk Zharski }
9245f25cf08SSiarzhuk Zharski
925d5d2fdf0SPhilippe Houdoin if (state == REQUESTING) {
926d5d2fdf0SPhilippe Houdoin const sockaddr_in& server = (sockaddr_in&)fServer.SockAddr();
927cefe2a40SPhilippe Houdoin next = message.PutOption(next, OPTION_SERVER_ADDRESS,
928d5d2fdf0SPhilippe Houdoin (uint32)server.sin_addr.s_addr);
929d5d2fdf0SPhilippe Houdoin }
930d5d2fdf0SPhilippe Houdoin
9310892800fSPhilippe Houdoin if (state == INIT || state == INIT_REBOOT
9320892800fSPhilippe Houdoin || state == REQUESTING) {
933d5d2fdf0SPhilippe Houdoin next = message.PutOption(next, OPTION_REQUEST_IP_ADDRESS,
934d5d2fdf0SPhilippe Houdoin (uint32)fAssignedAddress);
935d5d2fdf0SPhilippe Houdoin } else
936d5d2fdf0SPhilippe Houdoin message.client_address = fAssignedAddress;
937d5d2fdf0SPhilippe Houdoin break;
938d5d2fdf0SPhilippe Houdoin
939d5d2fdf0SPhilippe Houdoin case DHCP_RELEASE: {
940d5d2fdf0SPhilippe Houdoin const sockaddr_in& server = (sockaddr_in&)fServer.SockAddr();
941cefe2a40SPhilippe Houdoin next = message.PutOption(next, OPTION_SERVER_ADDRESS,
942af074561SAxel Dörfler (uint32)server.sin_addr.s_addr);
94346ff5400SAxel Dörfler
94446ff5400SAxel Dörfler message.client_address = fAssignedAddress;
9450ce7725eSAxel Dörfler break;
9460ce7725eSAxel Dörfler }
9470ce7725eSAxel Dörfler
9480ce7725eSAxel Dörfler default:
9490ce7725eSAxel Dörfler break;
9500ce7725eSAxel Dörfler }
951d5d2fdf0SPhilippe Houdoin
952d5d2fdf0SPhilippe Houdoin message.FinishOptions(next);
9530ce7725eSAxel Dörfler }
9540ce7725eSAxel Dörfler
9550ce7725eSAxel Dörfler
956f9af6566SAxel Dörfler bool
_TimeoutShift(int socket,dhcp_state & state,socket_timeout & timeout)9573bbff30dSFredrik Holmqvist DHCPClient::_TimeoutShift(int socket, dhcp_state& state,
9583bbff30dSFredrik Holmqvist socket_timeout& timeout)
959f9af6566SAxel Dörfler {
9603bbff30dSFredrik Holmqvist bigtime_t stateMaxTime = -1;
9619fef538bSAdrien Destugues
9629fef538bSAdrien Destugues // Compute the date at which we must consider the DHCP negociation failed.
9639fef538bSAdrien Destugues // This varies depending on the current state. In renewing and rebinding
9649fef538bSAdrien Destugues // states, it is based on the lease expiration.
9659fef538bSAdrien Destugues // We can stay for up to 1 minute in the selecting and requesting states
9669fef538bSAdrien Destugues // (these correspond to sending DHCP_DISCOVER and DHCP_REQUEST,
9679fef538bSAdrien Destugues // respectively).
9689fef538bSAdrien Destugues // All other states time out immediately after a single try.
9699fef538bSAdrien Destugues // If this timeout expires, the DHCP negociation is aborted and starts
9709fef538bSAdrien Destugues // over. As long as the timeout is not expired, we repeat the message with
9719fef538bSAdrien Destugues // increasing delays (the delay is computed in timeout.shift below, and is
9729fef538bSAdrien Destugues // at most equal to the timeout, but usually much shorter).
9736972b91eSPawel Dziepak if (state == RENEWING)
9743bbff30dSFredrik Holmqvist stateMaxTime = fRebindingTime;
9756972b91eSPawel Dziepak else if (state == REBINDING)
9763bbff30dSFredrik Holmqvist stateMaxTime = fLeaseTime;
9779fef538bSAdrien Destugues else if (state == SELECTING || state == REQUESTING)
9789fef538bSAdrien Destugues stateMaxTime = fRequestTime + AS_USECS(MAX_TIMEOUT);
9796972b91eSPawel Dziepak
9803bbff30dSFredrik Holmqvist if (system_time() > stateMaxTime) {
9813bbff30dSFredrik Holmqvist state = state == REBINDING ? INIT : REBINDING;
9823bbff30dSFredrik Holmqvist return false;
9836972b91eSPawel Dziepak }
9846972b91eSPawel Dziepak
9853bbff30dSFredrik Holmqvist return timeout.Shift(socket, stateMaxTime, Device());
986f9af6566SAxel Dörfler }
987f9af6566SAxel Dörfler
988f9af6566SAxel Dörfler
98984fdf2c3SPhilippe Houdoin /*static*/ BString
_AddressToString(const uint8 * data)99084fdf2c3SPhilippe Houdoin DHCPClient::_AddressToString(const uint8* data)
991f9af6566SAxel Dörfler {
992f9af6566SAxel Dörfler BString target = inet_ntoa(*(in_addr*)data);
993f9af6566SAxel Dörfler return target;
994f9af6566SAxel Dörfler }
995f9af6566SAxel Dörfler
996f9af6566SAxel Dörfler
99784fdf2c3SPhilippe Houdoin /*static*/ BString
_AddressToString(in_addr_t address)99884fdf2c3SPhilippe Houdoin DHCPClient::_AddressToString(in_addr_t address)
999f9af6566SAxel Dörfler {
1000f9af6566SAxel Dörfler BString target = inet_ntoa(*(in_addr*)&address);
1001f9af6566SAxel Dörfler return target;
1002f9af6566SAxel Dörfler }
1003f9af6566SAxel Dörfler
1004f9af6566SAxel Dörfler
1005f9af6566SAxel Dörfler status_t
_SendMessage(int socket,dhcp_message & message,const BNetworkAddress & address) const10061a905a76SAxel Dörfler DHCPClient::_SendMessage(int socket, dhcp_message& message,
1007af074561SAxel Dörfler const BNetworkAddress& address) const
1008f9af6566SAxel Dörfler {
10090892800fSPhilippe Houdoin message_type type = message.Type();
10100892800fSPhilippe Houdoin BString text;
10110892800fSPhilippe Houdoin text << dhcp_message::TypeToString(type);
10120892800fSPhilippe Houdoin
10130892800fSPhilippe Houdoin const uint8* requestAddress = message.FindOption(OPTION_REQUEST_IP_ADDRESS);
10140892800fSPhilippe Houdoin if (type == DHCP_REQUEST && requestAddress != NULL)
10150892800fSPhilippe Houdoin text << " for " << _AddressToString(requestAddress).String();
10160892800fSPhilippe Houdoin
10170892800fSPhilippe Houdoin syslog(LOG_DEBUG, "%s: Send %s to %s\n", Device(), text.String(),
10180892800fSPhilippe Houdoin address.ToString().String());
101961729d93SAxel Dörfler
1020f9af6566SAxel Dörfler ssize_t bytesSent = sendto(socket, &message, message.Size(),
1021af074561SAxel Dörfler address.IsBroadcast() ? MSG_BCAST : 0, address, address.Length());
1022f9af6566SAxel Dörfler if (bytesSent < 0)
1023f9af6566SAxel Dörfler return errno;
1024f9af6566SAxel Dörfler
1025f9af6566SAxel Dörfler return B_OK;
1026fb81684fSAxel Dörfler }
1027fb81684fSAxel Dörfler
1028fb81684fSAxel Dörfler
102946ff5400SAxel Dörfler dhcp_state
_CurrentState() const103046ff5400SAxel Dörfler DHCPClient::_CurrentState() const
103146ff5400SAxel Dörfler {
103246ff5400SAxel Dörfler bigtime_t now = system_time();
103346ff5400SAxel Dörfler
10346972b91eSPawel Dziepak if (now > fLeaseTime || fStatus != B_OK)
103546ff5400SAxel Dörfler return INIT;
103646ff5400SAxel Dörfler if (now >= fRebindingTime)
103746ff5400SAxel Dörfler return REBINDING;
103846ff5400SAxel Dörfler if (now >= fRenewalTime)
10390892800fSPhilippe Houdoin return RENEWING;
104046ff5400SAxel Dörfler return BOUND;
104146ff5400SAxel Dörfler }
104246ff5400SAxel Dörfler
104346ff5400SAxel Dörfler
1044fb81684fSAxel Dörfler void
MessageReceived(BMessage * message)1045fb81684fSAxel Dörfler DHCPClient::MessageReceived(BMessage* message)
1046fb81684fSAxel Dörfler {
10476cc7630fSAxel Dörfler switch (message->what) {
10486cc7630fSAxel Dörfler case kMsgLeaseTime:
10496972b91eSPawel Dziepak _Negotiate(_CurrentState());
10506cc7630fSAxel Dörfler break;
10516cc7630fSAxel Dörfler
10526cc7630fSAxel Dörfler default:
1053fb81684fSAxel Dörfler BHandler::MessageReceived(message);
10546cc7630fSAxel Dörfler break;
10556cc7630fSAxel Dörfler }
1056fb81684fSAxel Dörfler }
1057