1 /*
2 * Copyright 2006-2018, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 * Axel Dörfler, axeld@pinc-software.de
7 * Vegard Wærp, vegarwa@online.no
8 * Philippe Houdoin, <phoudoin at haiku-os dot org>
9 */
10
11
12 #include "DHCPClient.h"
13
14 #include <Message.h>
15 #include <MessageRunner.h>
16 #include <NetworkDevice.h>
17 #include <NetworkInterface.h>
18
19 #include <algorithm>
20 #include <arpa/inet.h>
21 #include <errno.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include <syslog.h>
26 #include <sys/sockio.h>
27 #include <sys/time.h>
28 #include <unistd.h>
29
30 #include <Debug.h>
31 #include <Message.h>
32 #include <MessageRunner.h>
33
34 #include "NetServer.h"
35
36
37 // See RFC 2131 for DHCP, see RFC 1533 for BOOTP/DHCP options
38
39 #define DHCP_CLIENT_PORT 68
40 #define DHCP_SERVER_PORT 67
41
42 #define DEFAULT_TIMEOUT 0.25 // secs
43 #define MAX_TIMEOUT 64 // secs
44
45 #define AS_USECS(t) (1000000 * t)
46
47 #define MAX_RETRIES 5
48
49 enum message_opcode {
50 BOOT_REQUEST = 1,
51 BOOT_REPLY
52 };
53
54 enum message_option {
55 OPTION_MAGIC = 0x63825363,
56
57 // generic options
58 OPTION_PAD = 0,
59 OPTION_END = 255,
60 OPTION_SUBNET_MASK = 1,
61 OPTION_TIME_OFFSET = 2,
62 OPTION_ROUTER_ADDRESS = 3,
63 OPTION_DOMAIN_NAME_SERVER = 6,
64 OPTION_HOST_NAME = 12,
65 OPTION_DOMAIN_NAME = 15,
66 OPTION_MAX_DATAGRAM_SIZE = 22,
67 OPTION_INTERFACE_MTU = 26,
68 OPTION_BROADCAST_ADDRESS = 28,
69 OPTION_NETWORK_TIME_PROTOCOL_SERVERS = 42,
70 OPTION_NETBIOS_NAME_SERVER = 44,
71 OPTION_NETBIOS_SCOPE = 47,
72
73 // DHCP specific options
74 OPTION_REQUEST_IP_ADDRESS = 50,
75 OPTION_ADDRESS_LEASE_TIME = 51,
76 OPTION_OVERLOAD = 52,
77 OPTION_MESSAGE_TYPE = 53,
78 OPTION_SERVER_ADDRESS = 54,
79 OPTION_REQUEST_PARAMETERS = 55,
80 OPTION_ERROR_MESSAGE = 56,
81 OPTION_MAX_MESSAGE_SIZE = 57,
82 OPTION_RENEWAL_TIME = 58,
83 OPTION_REBINDING_TIME = 59,
84 OPTION_CLASS_IDENTIFIER = 60,
85 OPTION_CLIENT_IDENTIFIER = 61,
86 };
87
88 enum message_type {
89 DHCP_NONE = 0,
90 DHCP_DISCOVER = 1,
91 DHCP_OFFER = 2,
92 DHCP_REQUEST = 3,
93 DHCP_DECLINE = 4,
94 DHCP_ACK = 5,
95 DHCP_NACK = 6,
96 DHCP_RELEASE = 7,
97 DHCP_INFORM = 8
98 };
99
100 struct dhcp_option_cookie {
dhcp_option_cookiedhcp_option_cookie101 dhcp_option_cookie()
102 :
103 state(0),
104 file_has_options(false),
105 server_name_has_options(false)
106 {
107 }
108
109 const uint8* next;
110 uint8 state;
111 bool file_has_options;
112 bool server_name_has_options;
113 };
114
115 struct dhcp_message {
116 dhcp_message(message_type type);
117
118 uint8 opcode;
119 uint8 hardware_type;
120 uint8 hardware_address_length;
121 uint8 hop_count;
122 uint32 transaction_id;
123 uint16 seconds_since_start;
124 uint16 flags;
125 in_addr_t client_address;
126 in_addr_t your_address;
127 in_addr_t server_address;
128 in_addr_t gateway_address;
129 uint8 mac_address[16];
130 uint8 server_name[64];
131 uint8 file[128];
132 uint32 options_magic;
133 uint8 options[1260];
134
MinSizedhcp_message135 size_t MinSize() const { return 576; }
136 size_t Size() const;
137
138 bool HasOptions() const;
139 bool NextOption(dhcp_option_cookie& cookie, message_option& option,
140 const uint8*& data, size_t& size) const;
141 const uint8* FindOption(message_option which) const;
142 const uint8* LastOption() const;
143 message_type Type() const;
144
145 uint8* PrepareMessage(uint8 type);
146 uint8* PutOption(uint8* options, message_option option);
147 uint8* PutOption(uint8* options, message_option option, uint8 data);
148 uint8* PutOption(uint8* options, message_option option, uint16 data);
149 uint8* PutOption(uint8* options, message_option option, uint32 data);
150 uint8* PutOption(uint8* options, message_option option, const uint8* data,
151 uint32 size);
152 uint8* FinishOptions(uint8* options);
153
154 static const char* TypeToString(message_type type);
155 } _PACKED;
156
157 struct socket_timeout {
socket_timeoutsocket_timeout158 socket_timeout(int socket)
159 :
160 timeout(time_t(AS_USECS(DEFAULT_TIMEOUT))),
161 tries(0)
162 {
163 UpdateSocket(socket);
164 }
165
166 bigtime_t timeout; // in micro secs
167 uint8 tries;
168
169 bool Shift(int socket, bigtime_t stateMaxTime, const char* device);
170 void UpdateSocket(int socket) const;
171 };
172
173 #define DHCP_FLAG_BROADCAST 0x8000
174
175 #define ARP_HARDWARE_TYPE_ETHER 1
176
177 const uint32 kMsgLeaseTime = 'lstm';
178
179 static const uint8 kRequestParameters[] = {
180 OPTION_SUBNET_MASK, OPTION_ROUTER_ADDRESS,
181 OPTION_DOMAIN_NAME_SERVER, OPTION_BROADCAST_ADDRESS,
182 OPTION_DOMAIN_NAME
183 };
184
185
dhcp_message(message_type type)186 dhcp_message::dhcp_message(message_type type)
187 {
188 // ASSERT(this == offsetof(this, opcode));
189 memset(this, 0, sizeof(*this));
190 options_magic = htonl(OPTION_MAGIC);
191
192 uint8* next = PrepareMessage(type);
193 FinishOptions(next);
194 }
195
196
197 bool
HasOptions() const198 dhcp_message::HasOptions() const
199 {
200 return options_magic == htonl(OPTION_MAGIC);
201 }
202
203
204 bool
NextOption(dhcp_option_cookie & cookie,message_option & option,const uint8 * & data,size_t & size) const205 dhcp_message::NextOption(dhcp_option_cookie& cookie,
206 message_option& option, const uint8*& data, size_t& size) const
207 {
208 if (!HasOptions())
209 return false;
210
211 if (cookie.state == 0) {
212 cookie.state++;
213 cookie.next = options;
214 }
215
216 uint32 bytesLeft = 0;
217
218 switch (cookie.state) {
219 case 1:
220 // options from "options"
221 bytesLeft = sizeof(options) - (cookie.next - options);
222 break;
223
224 case 2:
225 // options from "file"
226 bytesLeft = sizeof(file) - (cookie.next - file);
227 break;
228
229 case 3:
230 // options from "server_name"
231 bytesLeft = sizeof(server_name) - (cookie.next - server_name);
232 break;
233 }
234
235 while (true) {
236 if (bytesLeft == 0) {
237 cookie.state++;
238
239 // handle option overload in file and/or server_name fields.
240 switch (cookie.state) {
241 case 2:
242 // options from "file" field
243 if (cookie.file_has_options) {
244 bytesLeft = sizeof(file);
245 cookie.next = file;
246 }
247 break;
248
249 case 3:
250 // options from "server_name" field
251 if (cookie.server_name_has_options) {
252 bytesLeft = sizeof(server_name);
253 cookie.next = server_name;
254 }
255 break;
256
257 default:
258 // no more place to look for options
259 // if last option is not OPTION_END,
260 // there is no space left for other option!
261 if (option != OPTION_END)
262 cookie.next = NULL;
263 return false;
264 }
265
266 if (bytesLeft == 0) {
267 // no options in this state, try next one
268 continue;
269 }
270 }
271
272 option = (message_option)cookie.next[0];
273 if (option == OPTION_END) {
274 bytesLeft = 0;
275 continue;
276 } else if (option == OPTION_PAD) {
277 bytesLeft--;
278 cookie.next++;
279 continue;
280 }
281
282 size = cookie.next[1];
283 data = &cookie.next[2];
284 cookie.next += 2 + size;
285 bytesLeft -= 2 + size;
286
287 if (option == OPTION_OVERLOAD) {
288 cookie.file_has_options = data[0] & 1;
289 cookie.server_name_has_options = data[0] & 2;
290 continue;
291 }
292
293 return true;
294 }
295 }
296
297
298 const uint8*
FindOption(message_option which) const299 dhcp_message::FindOption(message_option which) const
300 {
301 dhcp_option_cookie cookie;
302 message_option option;
303 const uint8* data;
304 size_t size;
305 while (NextOption(cookie, option, data, size)) {
306 // iterate through all options
307 if (option == which)
308 return data;
309 }
310
311 return NULL;
312 }
313
314
315 const uint8*
LastOption() const316 dhcp_message::LastOption() const
317 {
318 dhcp_option_cookie cookie;
319 message_option option;
320 const uint8* data;
321 size_t size;
322 while (NextOption(cookie, option, data, size)) {
323 // iterate through all options
324 }
325
326 return cookie.next;
327 }
328
329
330 message_type
Type() const331 dhcp_message::Type() const
332 {
333 const uint8* data = FindOption(OPTION_MESSAGE_TYPE);
334 if (data)
335 return (message_type)data[0];
336
337 return DHCP_NONE;
338 }
339
340
341 size_t
Size() const342 dhcp_message::Size() const
343 {
344 const uint8* last = LastOption();
345
346 if (last < options) {
347 // if last option is stored above "options" field, it means
348 // the whole options field and message is already filled...
349 return sizeof(dhcp_message);
350 }
351
352 return sizeof(dhcp_message) - sizeof(options) + last + 1 - options;
353 }
354
355
356 uint8*
PrepareMessage(uint8 type)357 dhcp_message::PrepareMessage(uint8 type)
358 {
359 uint8* next = options;
360 next = PutOption(next, OPTION_MESSAGE_TYPE, type);
361 next = PutOption(next, OPTION_MAX_MESSAGE_SIZE,
362 (uint16)htons(sizeof(dhcp_message)));
363 return next;
364 }
365
366
367 uint8*
PutOption(uint8 * options,message_option option)368 dhcp_message::PutOption(uint8* options, message_option option)
369 {
370 options[0] = option;
371 return options + 1;
372 }
373
374
375 uint8*
PutOption(uint8 * options,message_option option,uint8 data)376 dhcp_message::PutOption(uint8* options, message_option option, uint8 data)
377 {
378 return PutOption(options, option, &data, 1);
379 }
380
381
382 uint8*
PutOption(uint8 * options,message_option option,uint16 data)383 dhcp_message::PutOption(uint8* options, message_option option, uint16 data)
384 {
385 return PutOption(options, option, (uint8*)&data, sizeof(data));
386 }
387
388
389 uint8*
PutOption(uint8 * options,message_option option,uint32 data)390 dhcp_message::PutOption(uint8* options, message_option option, uint32 data)
391 {
392 return PutOption(options, option, (uint8*)&data, sizeof(data));
393 }
394
395
396 uint8*
PutOption(uint8 * options,message_option option,const uint8 * data,uint32 size)397 dhcp_message::PutOption(uint8* options, message_option option,
398 const uint8* data, uint32 size)
399 {
400 // TODO: check enough space is available
401
402 options[0] = option;
403 options[1] = size;
404 memcpy(&options[2], data, size);
405
406 return options + 2 + size;
407 }
408
409
410 uint8*
FinishOptions(uint8 * options)411 dhcp_message::FinishOptions(uint8* options)
412 {
413 return PutOption(options, OPTION_END);
414 }
415
416
417 /*static*/ const char*
TypeToString(message_type type)418 dhcp_message::TypeToString(message_type type)
419 {
420 switch (type) {
421 #define CASE(x) case x: return #x;
422 CASE(DHCP_NONE)
423 CASE(DHCP_DISCOVER)
424 CASE(DHCP_OFFER)
425 CASE(DHCP_REQUEST)
426 CASE(DHCP_DECLINE)
427 CASE(DHCP_ACK)
428 CASE(DHCP_NACK)
429 CASE(DHCP_RELEASE)
430 CASE(DHCP_INFORM)
431 #undef CASE
432 }
433
434 return "<unknown>";
435 }
436
437
438 void
UpdateSocket(int socket) const439 socket_timeout::UpdateSocket(int socket) const
440 {
441 struct timeval value;
442 value.tv_sec = timeout / 1000000;
443 value.tv_usec = timeout % 1000000;
444 setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &value, sizeof(value));
445 }
446
447
448 bool
Shift(int socket,bigtime_t stateMaxTime,const char * device)449 socket_timeout::Shift(int socket, bigtime_t stateMaxTime, const char* device)
450 {
451 if (tries == UINT8_MAX)
452 return false;
453
454 tries++;
455
456 if (tries > MAX_RETRIES) {
457 bigtime_t now = system_time();
458 if (stateMaxTime == -1 || stateMaxTime < now)
459 return false;
460 bigtime_t remaining = (stateMaxTime - now) / 2 + 1;
461 timeout = std::max(remaining, bigtime_t(AS_USECS(60)));
462 } else
463 timeout += timeout;
464
465 if (timeout > AS_USECS(MAX_TIMEOUT))
466 timeout = AS_USECS(MAX_TIMEOUT);
467
468 syslog(LOG_DEBUG, "%s: Timeout shift: %" B_PRIdTIME " msecs (try %" B_PRIu8 ")\n",
469 device, timeout / 1000, tries);
470
471 UpdateSocket(socket);
472 return true;
473 }
474
475
476 // #pragma mark -
477
478
DHCPClient(BMessenger target,const char * device)479 DHCPClient::DHCPClient(BMessenger target, const char* device)
480 :
481 AutoconfigClient("dhcp", target, device),
482 fConfiguration(kMsgConfigureInterface),
483 fResolverConfiguration(kMsgConfigureResolver),
484 fRunner(NULL),
485 fAssignedAddress(0),
486 fServer(AF_INET, NULL, DHCP_SERVER_PORT, B_UNCONFIGURED_ADDRESS_FAMILIES),
487 fStartTime(0),
488 fRequestTime(0),
489 fRenewalTime(0),
490 fRebindingTime(0),
491 fLeaseTime(0)
492 {
493 fTransactionID = (uint32)system_time() ^ rand();
494
495 BNetworkAddress link;
496 BNetworkInterface interface(device);
497 fStatus = interface.GetHardwareAddress(link);
498 if (fStatus != B_OK)
499 return;
500
501 memcpy(fMAC, link.LinkLevelAddress(), sizeof(fMAC));
502
503 if ((interface.Flags() & IFF_AUTO_CONFIGURED) != 0) {
504 // Check for interface previous auto-configured address, if any.
505 BNetworkInterfaceAddress interfaceAddress;
506 int index = interface.FindFirstAddress(AF_INET);
507 if (index >= 0
508 && interface.GetAddressAt(index, interfaceAddress) == B_OK) {
509 BNetworkAddress address = interfaceAddress.Address();
510 const sockaddr_in& addr = (sockaddr_in&)address.SockAddr();
511 fAssignedAddress = addr.sin_addr.s_addr;
512
513 if ((ntohl(fAssignedAddress) & IN_CLASSB_NET) == 0xa9fe0000) {
514 // previous auto-configured address is a link-local one:
515 // there is no point asking a DHCP server to renew such
516 // server-less assigned address...
517 fAssignedAddress = 0;
518 }
519 }
520 }
521
522 openlog_thread("DHCP", 0, LOG_DAEMON);
523 }
524
525
~DHCPClient()526 DHCPClient::~DHCPClient()
527 {
528 if (fStatus != B_OK)
529 return;
530
531 delete fRunner;
532
533 int socket = ::socket(AF_INET, SOCK_DGRAM, 0);
534 if (socket < 0)
535 return;
536
537 // release lease
538
539 dhcp_message release(DHCP_RELEASE);
540 _PrepareMessage(release, BOUND);
541
542 _SendMessage(socket, release, fServer);
543 close(socket);
544
545 closelog_thread();
546 }
547
548
549 status_t
Initialize()550 DHCPClient::Initialize()
551 {
552 fStatus = _Negotiate(fAssignedAddress == 0 ? INIT : INIT_REBOOT);
553 syslog(LOG_DEBUG, "%s: DHCP status = %s\n", Device(), strerror(fStatus));
554 return fStatus;
555 }
556
557
558 status_t
_Negotiate(dhcp_state state)559 DHCPClient::_Negotiate(dhcp_state state)
560 {
561 if (state == BOUND)
562 return B_OK;
563
564 fStartTime = system_time();
565 fTransactionID++;
566
567 char hostName[MAXHOSTNAMELEN];
568 if (gethostname(hostName, MAXHOSTNAMELEN) == 0)
569 fHostName.SetTo(hostName, MAXHOSTNAMELEN);
570 else
571 fHostName.Truncate(0);
572
573 int socket = ::socket(AF_INET, SOCK_DGRAM, 0);
574 if (socket < 0)
575 return errno;
576
577 // Enable reusing the port. This is needed in case there is more
578 // than 1 interface that needs to be configured. Note that the only reason
579 // this works is because there is code below to bind to a specific
580 // interface.
581 int option = 1;
582 setsockopt(socket, SOL_SOCKET, SO_REUSEPORT, &option, sizeof(option));
583
584 BNetworkAddress local;
585 local.SetToWildcard(AF_INET, DHCP_CLIENT_PORT);
586
587 option = 1;
588 setsockopt(socket, SOL_SOCKET, SO_BROADCAST, &option, sizeof(option));
589
590 if (bind(socket, local, local.Length()) < 0) {
591 close(socket);
592 return errno;
593 }
594
595 bigtime_t previousLeaseTime = fLeaseTime;
596
597 status_t status = B_OK;
598 while (state != BOUND) {
599 status = _StateTransition(socket, state);
600 if (status != B_OK && (state == SELECTING || state == REBOOTING))
601 break;
602 }
603
604 close(socket);
605
606 if (fLeaseTime == 0)
607 fLeaseTime = previousLeaseTime;
608 if (fLeaseTime == 0)
609 fLeaseTime = 60;
610
611 if (fRenewalTime == 0)
612 fRenewalTime = fLeaseTime / 2;
613 if (fRebindingTime == 0)
614 fRebindingTime = fLeaseTime * 7 / 8;
615 fLeaseTime += fRequestTime;
616 fRenewalTime += fRequestTime;
617 fRebindingTime += fRequestTime;
618 _RestartLease(fRenewalTime);
619
620 fStatus = status;
621 if (status)
622 return status;
623
624 // configure interface
625 BMessage reply;
626 status = Target().SendMessage(&fConfiguration, &reply);
627 if (status == B_OK)
628 status = reply.FindInt32("status", &fStatus);
629
630 // configure resolver
631 reply.MakeEmpty();
632 fResolverConfiguration.AddString("device", Device());
633 status = Target().SendMessage(&fResolverConfiguration, &reply);
634 if (status == B_OK)
635 status = reply.FindInt32("status", &fStatus);
636 return status;
637 }
638
639
640 status_t
_GotMessage(dhcp_state & state,dhcp_message * message)641 DHCPClient::_GotMessage(dhcp_state& state, dhcp_message* message)
642 {
643 switch (state) {
644 case SELECTING:
645 if (message->Type() == DHCP_OFFER) {
646 state = REQUESTING;
647
648 fAssignedAddress = message->your_address;
649 syslog(LOG_INFO, " your_address: %s\n",
650 _AddressToString(fAssignedAddress).String());
651
652 fConfiguration.MakeEmpty();
653 fConfiguration.AddString("device", Device());
654 fConfiguration.AddBool("auto_configured", true);
655
656 BMessage address;
657 address.AddString("family", "inet");
658 address.AddString("address", _AddressToString(fAssignedAddress));
659 fResolverConfiguration.MakeEmpty();
660 _ParseOptions(*message, address, fResolverConfiguration);
661
662 fConfiguration.AddMessage("address", &address);
663 return B_OK;
664 }
665
666 return B_BAD_VALUE;
667
668 case REBOOTING:
669 case REBINDING:
670 case RENEWING:
671 case REQUESTING:
672 if (message->Type() == DHCP_ACK) {
673 // TODO: we might want to configure the stuff, don't we?
674 BMessage address;
675 fResolverConfiguration.MakeEmpty();
676 _ParseOptions(*message, address, fResolverConfiguration);
677 // TODO: currently, only lease time and DNS is updated this
678 // way
679
680 // our address request has been acknowledged
681 state = BOUND;
682
683 return B_OK;
684 }
685
686 if (message->Type() == DHCP_NACK) {
687 // server reject our request on previous assigned address
688 // back to square one...
689 fAssignedAddress = 0;
690 state = INIT;
691 return B_OK;
692 }
693
694 default:
695 return B_BAD_VALUE;
696 }
697 }
698
699
700 status_t
_StateTransition(int socket,dhcp_state & state)701 DHCPClient::_StateTransition(int socket, dhcp_state& state)
702 {
703 if (state == INIT) {
704 // The local interface does not have an address yet, bind the socket
705 // to the device directly.
706 BNetworkDevice device(Device());
707 int index = device.Index();
708
709 setsockopt(socket, SOL_SOCKET, SO_BINDTODEVICE, &index, sizeof(int));
710 }
711
712 BNetworkAddress broadcast;
713 broadcast.SetToBroadcast(AF_INET, DHCP_SERVER_PORT);
714
715 socket_timeout timeout(socket);
716
717 dhcp_message discover(DHCP_DISCOVER);
718 _PrepareMessage(discover, state);
719
720 dhcp_message request(DHCP_REQUEST);
721 _PrepareMessage(request, state);
722
723 bool skipRequest = false;
724 dhcp_state originalState = state;
725 fRequestTime = system_time();
726 while (true) {
727 if (!skipRequest) {
728 _SendMessage(socket, originalState == INIT ? discover : request,
729 originalState == RENEWING ? fServer : broadcast);
730
731 if (originalState == INIT)
732 state = SELECTING;
733 else if (originalState == INIT_REBOOT)
734 state = REBOOTING;
735 }
736
737 char buffer[2048];
738 struct sockaddr_in from;
739 socklen_t fromLength = sizeof(from);
740 ssize_t bytesReceived = recvfrom(socket, buffer, sizeof(buffer),
741 0, (struct sockaddr*)&from, &fromLength);
742 if (bytesReceived < 0 && errno == B_TIMED_OUT) {
743 // depending on the state, we'll just try again
744 if (!_TimeoutShift(socket, state, timeout))
745 return B_TIMED_OUT;
746 skipRequest = false;
747 continue;
748 } else if (bytesReceived < 0)
749 return errno;
750
751 skipRequest = true;
752 dhcp_message* message = (dhcp_message*)buffer;
753 if (message->transaction_id != htonl(fTransactionID)
754 || !message->HasOptions()
755 || memcmp(message->mac_address, discover.mac_address,
756 discover.hardware_address_length)) {
757 // this message is not for us
758 syslog(LOG_DEBUG, "%s: Ignoring %s not for us from %s\n",
759 Device(), dhcp_message::TypeToString(message->Type()),
760 _AddressToString(from.sin_addr.s_addr).String());
761 continue;
762 }
763
764 syslog(LOG_DEBUG, "%s: Received %s from %s\n",
765 Device(), dhcp_message::TypeToString(message->Type()),
766 _AddressToString(from.sin_addr.s_addr).String());
767
768 if (_GotMessage(state, message) == B_OK)
769 break;
770 }
771
772 return B_OK;
773 }
774
775
776 void
_RestartLease(bigtime_t leaseTime)777 DHCPClient::_RestartLease(bigtime_t leaseTime)
778 {
779 if (leaseTime == 0)
780 return;
781
782 BMessage lease(kMsgLeaseTime);
783 fRunner = new BMessageRunner(this, &lease, leaseTime - system_time(), 1);
784 }
785
786
787 void
_ParseOptions(dhcp_message & message,BMessage & address,BMessage & resolverConfiguration)788 DHCPClient::_ParseOptions(dhcp_message& message, BMessage& address,
789 BMessage& resolverConfiguration)
790 {
791 dhcp_option_cookie cookie;
792 message_option option;
793 const uint8* data;
794 size_t size;
795 while (message.NextOption(cookie, option, data, size)) {
796 // iterate through all options
797 switch (option) {
798 case OPTION_ROUTER_ADDRESS:
799 syslog(LOG_DEBUG, " gateway: %s\n",
800 _AddressToString(data).String());
801 address.AddString("gateway", _AddressToString(data));
802 break;
803 case OPTION_SUBNET_MASK:
804 syslog(LOG_DEBUG, " subnet: %s\n",
805 _AddressToString(data).String());
806 address.AddString("mask", _AddressToString(data));
807 break;
808 case OPTION_BROADCAST_ADDRESS:
809 syslog(LOG_DEBUG, " broadcast: %s\n",
810 _AddressToString(data).String());
811 address.AddString("broadcast", _AddressToString(data));
812 break;
813 case OPTION_DOMAIN_NAME_SERVER:
814 {
815 for (uint32 i = 0; i < size / 4; i++) {
816 syslog(LOG_DEBUG, " nameserver[%d]: %s\n", i,
817 _AddressToString(&data[i * 4]).String());
818 resolverConfiguration.AddString("nameserver",
819 _AddressToString(&data[i * 4]).String());
820 }
821 resolverConfiguration.AddInt32("nameserver_count",
822 size / 4);
823 break;
824 }
825 case OPTION_SERVER_ADDRESS:
826 {
827 syslog(LOG_DEBUG, " server: %s\n",
828 _AddressToString(data).String());
829 status_t status = fServer.SetAddress(*(in_addr_t*)data);
830 if (status != B_OK) {
831 syslog(LOG_ERR, " BNetworkAddress::SetAddress failed with %s!\n",
832 strerror(status));
833 fServer.Unset();
834 }
835 break;
836 }
837
838 case OPTION_ADDRESS_LEASE_TIME:
839 syslog(LOG_DEBUG, " lease time: %lu seconds\n",
840 ntohl(*(uint32*)data));
841 fLeaseTime = ntohl(*(uint32*)data) * 1000000LL;
842 break;
843 case OPTION_RENEWAL_TIME:
844 syslog(LOG_DEBUG, " renewal time: %lu seconds\n",
845 ntohl(*(uint32*)data));
846 fRenewalTime = ntohl(*(uint32*)data) * 1000000LL;
847 break;
848 case OPTION_REBINDING_TIME:
849 syslog(LOG_DEBUG, " rebinding time: %lu seconds\n",
850 ntohl(*(uint32*)data));
851 fRebindingTime = ntohl(*(uint32*)data) * 1000000LL;
852 break;
853
854 case OPTION_HOST_NAME:
855 syslog(LOG_DEBUG, " host name: \"%.*s\"\n", (int)size,
856 (const char*)data);
857 break;
858
859 case OPTION_DOMAIN_NAME:
860 {
861 char domain[256];
862 strlcpy(domain, (const char*)data,
863 min_c(size + 1, sizeof(domain)));
864
865 syslog(LOG_DEBUG, " domain name: \"%s\"\n", domain);
866
867 resolverConfiguration.AddString("domain", domain);
868 break;
869 }
870
871 case OPTION_MESSAGE_TYPE:
872 break;
873
874 case OPTION_ERROR_MESSAGE:
875 syslog(LOG_INFO, " error message: \"%.*s\"\n", (int)size,
876 (const char*)data);
877 break;
878
879 default:
880 syslog(LOG_DEBUG, " UNKNOWN OPTION %" B_PRIu32 " (0x%" B_PRIx32 ")\n",
881 (uint32)option, (uint32)option);
882 break;
883 }
884 }
885 }
886
887
888 void
_PrepareMessage(dhcp_message & message,dhcp_state state)889 DHCPClient::_PrepareMessage(dhcp_message& message, dhcp_state state)
890 {
891 message.opcode = BOOT_REQUEST;
892 message.hardware_type = ARP_HARDWARE_TYPE_ETHER;
893 message.hardware_address_length = 6;
894 message.transaction_id = htonl(fTransactionID);
895 message.seconds_since_start = htons(min_c((system_time() - fStartTime)
896 / 1000000LL, 65535));
897 memcpy(message.mac_address, fMAC, 6);
898
899 message_type type = message.Type();
900
901 uint8 *next = message.PrepareMessage(type);
902
903 switch (type) {
904 case DHCP_DISCOVER:
905 next = message.PutOption(next, OPTION_REQUEST_PARAMETERS,
906 kRequestParameters, sizeof(kRequestParameters));
907
908 if (fHostName.Length() > 0) {
909 next = message.PutOption(next, OPTION_HOST_NAME,
910 reinterpret_cast<const uint8*>(fHostName.String()),
911 fHostName.Length());
912 }
913 break;
914
915 case DHCP_REQUEST:
916 next = message.PutOption(next, OPTION_REQUEST_PARAMETERS,
917 kRequestParameters, sizeof(kRequestParameters));
918
919 if (fHostName.Length() > 0) {
920 next = message.PutOption(next, OPTION_HOST_NAME,
921 reinterpret_cast<const uint8*>(fHostName.String()),
922 fHostName.Length());
923 }
924
925 if (state == REQUESTING) {
926 const sockaddr_in& server = (sockaddr_in&)fServer.SockAddr();
927 next = message.PutOption(next, OPTION_SERVER_ADDRESS,
928 (uint32)server.sin_addr.s_addr);
929 }
930
931 if (state == INIT || state == INIT_REBOOT
932 || state == REQUESTING) {
933 next = message.PutOption(next, OPTION_REQUEST_IP_ADDRESS,
934 (uint32)fAssignedAddress);
935 } else
936 message.client_address = fAssignedAddress;
937 break;
938
939 case DHCP_RELEASE: {
940 const sockaddr_in& server = (sockaddr_in&)fServer.SockAddr();
941 next = message.PutOption(next, OPTION_SERVER_ADDRESS,
942 (uint32)server.sin_addr.s_addr);
943
944 message.client_address = fAssignedAddress;
945 break;
946 }
947
948 default:
949 break;
950 }
951
952 message.FinishOptions(next);
953 }
954
955
956 bool
_TimeoutShift(int socket,dhcp_state & state,socket_timeout & timeout)957 DHCPClient::_TimeoutShift(int socket, dhcp_state& state,
958 socket_timeout& timeout)
959 {
960 bigtime_t stateMaxTime = -1;
961
962 // Compute the date at which we must consider the DHCP negociation failed.
963 // This varies depending on the current state. In renewing and rebinding
964 // states, it is based on the lease expiration.
965 // We can stay for up to 1 minute in the selecting and requesting states
966 // (these correspond to sending DHCP_DISCOVER and DHCP_REQUEST,
967 // respectively).
968 // All other states time out immediately after a single try.
969 // If this timeout expires, the DHCP negociation is aborted and starts
970 // over. As long as the timeout is not expired, we repeat the message with
971 // increasing delays (the delay is computed in timeout.shift below, and is
972 // at most equal to the timeout, but usually much shorter).
973 if (state == RENEWING)
974 stateMaxTime = fRebindingTime;
975 else if (state == REBINDING)
976 stateMaxTime = fLeaseTime;
977 else if (state == SELECTING || state == REQUESTING)
978 stateMaxTime = fRequestTime + AS_USECS(MAX_TIMEOUT);
979
980 if (system_time() > stateMaxTime) {
981 state = state == REBINDING ? INIT : REBINDING;
982 return false;
983 }
984
985 return timeout.Shift(socket, stateMaxTime, Device());
986 }
987
988
989 /*static*/ BString
_AddressToString(const uint8 * data)990 DHCPClient::_AddressToString(const uint8* data)
991 {
992 BString target = inet_ntoa(*(in_addr*)data);
993 return target;
994 }
995
996
997 /*static*/ BString
_AddressToString(in_addr_t address)998 DHCPClient::_AddressToString(in_addr_t address)
999 {
1000 BString target = inet_ntoa(*(in_addr*)&address);
1001 return target;
1002 }
1003
1004
1005 status_t
_SendMessage(int socket,dhcp_message & message,const BNetworkAddress & address) const1006 DHCPClient::_SendMessage(int socket, dhcp_message& message,
1007 const BNetworkAddress& address) const
1008 {
1009 message_type type = message.Type();
1010 BString text;
1011 text << dhcp_message::TypeToString(type);
1012
1013 const uint8* requestAddress = message.FindOption(OPTION_REQUEST_IP_ADDRESS);
1014 if (type == DHCP_REQUEST && requestAddress != NULL)
1015 text << " for " << _AddressToString(requestAddress).String();
1016
1017 syslog(LOG_DEBUG, "%s: Send %s to %s\n", Device(), text.String(),
1018 address.ToString().String());
1019
1020 ssize_t bytesSent = sendto(socket, &message, message.Size(),
1021 address.IsBroadcast() ? MSG_BCAST : 0, address, address.Length());
1022 if (bytesSent < 0)
1023 return errno;
1024
1025 return B_OK;
1026 }
1027
1028
1029 dhcp_state
_CurrentState() const1030 DHCPClient::_CurrentState() const
1031 {
1032 bigtime_t now = system_time();
1033
1034 if (now > fLeaseTime || fStatus != B_OK)
1035 return INIT;
1036 if (now >= fRebindingTime)
1037 return REBINDING;
1038 if (now >= fRenewalTime)
1039 return RENEWING;
1040 return BOUND;
1041 }
1042
1043
1044 void
MessageReceived(BMessage * message)1045 DHCPClient::MessageReceived(BMessage* message)
1046 {
1047 switch (message->what) {
1048 case kMsgLeaseTime:
1049 _Negotiate(_CurrentState());
1050 break;
1051
1052 default:
1053 BHandler::MessageReceived(message);
1054 break;
1055 }
1056 }
1057