From fb81684f819c432b2cdc30db597e08ccd88b5a25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Wed, 6 Dec 2006 19:00:00 +0000 Subject: [PATCH] The beginnings of automatic configuration of network devices using DHCP; this is currently only triggered when there is no configuration file - it can't be configured this way yet. All DHCP currently does is to send a UDP broadcast DHCP discover message. More to come. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@19437 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/servers/net/AutoconfigLooper.cpp | 96 ++++++++ src/servers/net/AutoconfigLooper.h | 33 +++ src/servers/net/DHCPClient.cpp | 342 +++++++++++++++++++++++++++ src/servers/net/DHCPClient.h | 34 +++ src/servers/net/Jamfile | 3 + src/servers/net/NetServer.cpp | 149 ++++++++++-- src/servers/net/NetServer.h | 22 ++ 7 files changed, 658 insertions(+), 21 deletions(-) create mode 100644 src/servers/net/AutoconfigLooper.cpp create mode 100644 src/servers/net/AutoconfigLooper.h create mode 100644 src/servers/net/DHCPClient.cpp create mode 100644 src/servers/net/DHCPClient.h create mode 100644 src/servers/net/NetServer.h diff --git a/src/servers/net/AutoconfigLooper.cpp b/src/servers/net/AutoconfigLooper.cpp new file mode 100644 index 0000000000..8af0f03c25 --- /dev/null +++ b/src/servers/net/AutoconfigLooper.cpp @@ -0,0 +1,96 @@ +/* + * Copyright 2006, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Axel Dörfler, axeld@pinc-software.de + */ + + +#include "AutoconfigLooper.h" +#include "DHCPClient.h" +#include "NetServer.h" + +#include +#include +#include +#include +#include +#include + + +static const uint32 kMsgReadyToRun = 'rdyr'; + + +AutoconfigLooper::AutoconfigLooper(BMessenger target, const char* device) + : BLooper(device), + fTarget(target), + fDevice(device) +{ + BMessage ready(kMsgReadyToRun); + PostMessage(&ready); +} + + +AutoconfigLooper::~AutoconfigLooper() +{ +} + + +void +AutoconfigLooper::_ReadyToRun() +{ + // start with DHCP + + DHCPClient* client = new DHCPClient(fDevice.String()); + if (client->InitCheck() == B_OK) { + AddHandler(client); + return; + } +puts("DHCP failed miserably!"); + + // DHCP obviously didn't work out, take some default values for now + // TODO: have a look at zeroconf + // TODO: this could also be done add-on based + + BMessage interface; + interface.AddString("device", fDevice.String()); + + uint8 mac[6]; + uint8 last = 56; + if (get_mac_address(fDevice.String(), mac) == B_OK) { + // choose IP address depending on the MAC address, if available + last = mac[0] ^ mac[1] ^ mac[2] ^ mac[3] ^ mac[4] ^ mac[5]; + if (last > 253) + last = 253; + else if (last == 0) + last = 1; + } + + char string[64]; + snprintf(string, sizeof(string), "192.168.0.%u", last); + + BMessage address; + address.AddString("family", "inet"); + address.AddString("address", string); + address.AddString("gateway", "192.168.0.254"); + interface.AddMessage("address", &address); + + fTarget.SendMessage(kMsgConfigureInterface, &interface); +} + + +void +AutoconfigLooper::MessageReceived(BMessage* message) +{ + switch (message->what) { + case kMsgReadyToRun: + _ReadyToRun(); + break; + + default: + BLooper::MessageReceived(message); + break; + } +} + diff --git a/src/servers/net/AutoconfigLooper.h b/src/servers/net/AutoconfigLooper.h new file mode 100644 index 0000000000..b4a322d998 --- /dev/null +++ b/src/servers/net/AutoconfigLooper.h @@ -0,0 +1,33 @@ +/* + * Copyright 2006, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Axel Dörfler, axeld@pinc-software.de + */ +#ifndef AUTOCONFIG_LOOPER_H +#define AUTOCONFIG_LOOPER_H + + +#include +#include +#include + + +class AutoconfigLooper : public BLooper { + public: + AutoconfigLooper(BMessenger target, const char* device); + virtual ~AutoconfigLooper(); + + virtual void MessageReceived(BMessage* message); + + BMessenger Target() const { return fTarget; } + + private: + void _ReadyToRun(); + + BMessenger fTarget; + BString fDevice; +}; + +#endif // AUTOCONFIG_LOOPER_H diff --git a/src/servers/net/DHCPClient.cpp b/src/servers/net/DHCPClient.cpp new file mode 100644 index 0000000000..2a59fb9f23 --- /dev/null +++ b/src/servers/net/DHCPClient.cpp @@ -0,0 +1,342 @@ +/* + * Copyright 2006, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Axel Dörfler, axeld@pinc-software.de + */ + + +#include "DHCPClient.h" +#include "NetServer.h" + +#include +#include +#include +#include + + +// See RFC 2131 for DHCP, see RFC 1533 for BOOTP/DHCP options + +#define DHCP_CLIENT_PORT 68 +#define DHCP_SERVER_PORT 67 + +enum message_opcode { + BOOT_REQUEST = 1, + BOOT_REPLY +}; + +enum message_option { + OPTION_MAGIC = 0x63825363, + + // generic options + OPTION_PAD = 0, + OPTION_END = 1, + OPTION_DATAGRAM_SIZE = 22, + OPTION_MTU = 26, + OPTION_BROADCAST_ADDRESS = 28, + OPTION_NETWORK_TIME_SERVERS = 42, + + // DHCP specific options + OPTION_REQUEST_IP_ADDRESS = 50, + OPTION_ADDRESS_LEASE_TIME = 51, + OPTION_OVERLOAD = 52, + OPTION_MESSAGE_TYPE = 53, + OPTION_SERVER_ADDRESS = 54, + OPTION_REQUEST_PARAMETERS = 55, + OPTION_ERROR_MESSAGE = 56, + OPTION_MESSAGE_SIZE = 57, + OPTION_RENEWAL_TIME = 58, + OPTION_REBINDING_TIME = 59, + OPTION_CLASS_IDENTIFIER = 60, + OPTION_CLIENT_IDENTIFIER = 61, +}; + +enum message_type { + DHCP_DISCOVER = 1, + DHCP_OFFER, + DHCP_REQUEST, + DHCP_DECLINE, + DHCP_ACK, + DHCP_NAK, + DHCP_RELEASE, + DHCP_INFORM +}; + +struct dhcp_option_cookie { + dhcp_option_cookie() : state(0), file_has_options(false), server_name_has_options(false) {} + + const uint8* next; + uint8 state; + bool file_has_options; + bool server_name_has_options; +}; + +struct dhcp_message { + dhcp_message(message_type type); + + uint8 opcode; + uint8 hardware_type; + uint8 hardware_address_length; + uint8 hop_count; + uint32 transaction_id; + uint16 seconds_since_boot; + uint16 flags; + in_addr_t client_address; + in_addr_t your_address; + in_addr_t server_address; + in_addr_t gateway_address; + uint8 mac_address[16]; + uint8 server_name[64]; + uint8 file[128]; + uint32 options_magic; + uint8 options[1260]; + + size_t MinSize() const { return 576; } + size_t Size() const; + + bool HasOptions() const; + bool NextOption(dhcp_option_cookie& cookie, message_option& option, + const uint8*& data, size_t& size) const; + const uint8* LastOption() const; + + uint8* PutOption(uint8* options, message_option option); + uint8* PutOption(uint8* options, message_option option, uint8 data); + uint8* PutOption(uint8* options, message_option option, uint16 data); + uint8* PutOption(uint8* options, message_option option, uint32 data); + uint8* PutOption(uint8* options, message_option option, uint8* data, uint32 size); +} _PACKED; + + +#define ARP_HARDWARE_TYPE_ETHER 1 + + +dhcp_message::dhcp_message(message_type type) +{ + memset(this, 0, sizeof(*this)); + options_magic = htonl(OPTION_MAGIC); + + uint8* next = options; + next = PutOption(next, OPTION_MESSAGE_TYPE, (uint8)type); + next = PutOption(next, OPTION_MESSAGE_SIZE, (uint16)sizeof(dhcp_message)); + next = PutOption(next, OPTION_END); +} + + +bool +dhcp_message::HasOptions() const +{ + return options_magic == htonl(OPTION_MAGIC); +} + + +bool +dhcp_message::NextOption(dhcp_option_cookie& cookie, + message_option& option, const uint8*& data, size_t& size) const +{ + if (cookie.state == 0) { + if (!HasOptions()) + return false; + + cookie.state++; + cookie.next = options; + } + + uint32 bytesLeft = 0; + + switch (cookie.state) { + case 1: + // options from "options" + bytesLeft = sizeof(options) + cookie.next - options; + break; + + case 2: + // options from "file" + bytesLeft = sizeof(options) + cookie.next - options; + break; + + case 3: + // options from "server_name" + bytesLeft = sizeof(options) + cookie.next - options; + break; + } + + while (true) { + if (bytesLeft == 0) { + // TODO: suppport OPTION_OVERLOAD! + cookie.state = 4; + return false; + } + + option = (message_option)cookie.next[0]; + if (option == OPTION_END) { + cookie.state = 4; + return false; + } else if (option == OPTION_PAD) { + bytesLeft--; + cookie.next++; + continue; + } + + size = cookie.next[1]; + data = &cookie.next[2]; + cookie.next += 2 + size; + + if (option == OPTION_OVERLOAD) { + cookie.file_has_options = data[0] & 1; + cookie.server_name_has_options = data[0] & 2; + continue; + } + + return true; + } +} + + +const uint8* +dhcp_message::LastOption() const +{ + dhcp_option_cookie cookie; + message_option option; + const uint8 *data; + size_t size; + while (NextOption(cookie, option, data, size)) { + // iterate through all options + } + + return cookie.next; +} + + +size_t +dhcp_message::Size() const +{ + const uint8* last = LastOption(); + return sizeof(dhcp_message) - sizeof(options) + last + 1 - options; +} + + +uint8* +dhcp_message::PutOption(uint8* options, message_option option) +{ + options[0] = option; + return options + 1; +} + + +uint8* +dhcp_message::PutOption(uint8* options, message_option option, uint8 data) +{ + return PutOption(options, option, &data, 1); +} + + +uint8* +dhcp_message::PutOption(uint8* options, message_option option, uint16 data) +{ + data = htons(data); + return PutOption(options, option, (uint8*)&data, sizeof(data)); +} + + +uint8* +dhcp_message::PutOption(uint8* options, message_option option, uint32 data) +{ + data = htonl(data); + return PutOption(options, option, (uint8*)&data, sizeof(data)); +} + + +uint8* +dhcp_message::PutOption(uint8* options, message_option option, uint8* data, uint32 size) +{ + options[0] = option; + options[1] = size; + memcpy(&options[2], data, size); + + return options + 2 + size; +} + + +// #pragma mark - + + +DHCPClient::DHCPClient(const char* device) + : BHandler("dhcp"), + fDevice(device) +{ + fTransactionID = system_time(); + + dhcp_message message(DHCP_DISCOVER); + message.opcode = BOOT_REQUEST; + message.hardware_type = ARP_HARDWARE_TYPE_ETHER; + message.hardware_address_length = 6; + message.transaction_id = htonl(fTransactionID); + message.seconds_since_boot = htons(max_c(system_time() / 1000000LL, 65535)); + fStatus = get_mac_address(device, message.mac_address); + if (fStatus < B_OK) + return; + + int socket = ::socket(AF_INET, SOCK_DGRAM, 0); + if (socket < 0) { + fStatus = errno; + return; + } + + sockaddr_in local; + memset(&local, 0, sizeof(struct sockaddr_in)); + local.sin_family = AF_INET; + local.sin_len = sizeof(struct sockaddr_in); + local.sin_port = htons(DHCP_CLIENT_PORT); + local.sin_addr.s_addr = INADDR_ANY; + + if (bind(socket, (struct sockaddr *)&local, sizeof(local)) < 0) { + fStatus = errno; + close(socket); + return; + } + + sockaddr_in broadcast; + memset(&broadcast, 0, sizeof(struct sockaddr_in)); + broadcast.sin_family = AF_INET; + broadcast.sin_len = sizeof(struct sockaddr_in); + broadcast.sin_port = htons(DHCP_SERVER_PORT); + broadcast.sin_addr.s_addr = INADDR_BROADCAST; + + int option = 1; + setsockopt(socket, SOL_SOCKET, SO_BROADCAST, &option, sizeof(option)); + +printf("DHCP message size %lu\n", message.Size()); + + ssize_t bytesSent = sendto(socket, &message, message.MinSize(), 0, + (struct sockaddr*)&broadcast, sizeof(broadcast)); + if (bytesSent < 0) { + fStatus = errno; + close(socket); + return; + } + + close(socket); + fStatus = B_ERROR; +} + + +DHCPClient::~DHCPClient() +{ +} + + +status_t +DHCPClient::InitCheck() +{ +printf("DHCP for %s, status: %s\n", fDevice.String(), strerror(fStatus)); + return fStatus; +} + + +void +DHCPClient::MessageReceived(BMessage* message) +{ + BHandler::MessageReceived(message); +} + diff --git a/src/servers/net/DHCPClient.h b/src/servers/net/DHCPClient.h new file mode 100644 index 0000000000..bfd61769f0 --- /dev/null +++ b/src/servers/net/DHCPClient.h @@ -0,0 +1,34 @@ +/* + * Copyright 2006, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Axel Dörfler, axeld@pinc-software.de + */ +#ifndef DHCP_CLIENT_H +#define DHCP_CLIENT_H + + +#include +#include + + +class BMessageRunner; + +class DHCPClient : public BHandler { + public: + DHCPClient(const char* device); + virtual ~DHCPClient(); + + status_t InitCheck(); + + virtual void MessageReceived(BMessage* message); + + private: + BString fDevice; + BMessageRunner* fRunner; + uint32 fTransactionID; + status_t fStatus; +}; + +#endif // DHCP_CLIENT_H diff --git a/src/servers/net/Jamfile b/src/servers/net/Jamfile index 8a7717bfc3..8ddbffec96 100644 --- a/src/servers/net/Jamfile +++ b/src/servers/net/Jamfile @@ -11,11 +11,14 @@ AddResources net_server : net_server.rdef ; Server net_server : NetServer.cpp Settings.cpp + AutoconfigLooper.cpp + DHCPClient.cpp ; LinkAgainst net_server : be libnetwork.so + $(TARGET_LIBSTDC++) # for PPP #libppp.a diff --git a/src/servers/net/NetServer.cpp b/src/servers/net/NetServer.cpp index cdc5cb3d2a..0cbe5c5d9d 100644 --- a/src/servers/net/NetServer.cpp +++ b/src/servers/net/NetServer.cpp @@ -7,6 +7,8 @@ */ +#include "AutoconfigLooper.h" +#include "NetServer.h" #include "Settings.h" #include @@ -18,20 +20,24 @@ #include #include -#include #include #include #include #include #include +#include #include #include #include +#include #include #include +typedef std::map LooperMap; + + class NetServer : public BApplication { public: NetServer(); @@ -41,15 +47,17 @@ class NetServer : public BApplication { virtual void MessageReceived(BMessage* message); private: - bool _PrepareRequest(ifreq& request, const char* name); bool _TestForInterface(int socket, const char* name); status_t _ConfigureInterface(int socket, BMessage& interface); + bool _QuitLooperForDevice(const char* device); + BLooper* _LooperForDevice(const char* device); status_t _ConfigureDevice(int socket, const char* path); void _ConfigureDevices(int socket, const char* path); void _ConfigureInterfaces(int socket); void _BringUpInterfaces(); Settings fSettings; + LooperMap fDeviceMap; }; @@ -125,6 +133,51 @@ parse_address(int32 familyIndex, const char* argument, struct sockaddr& address) } +bool +prepare_request(ifreq& request, const char* name) +{ + if (strlen(name) > IF_NAMESIZE) + return false; + + strcpy(request.ifr_name, name); + return true; +} + + +status_t +get_mac_address(const char* device, uint8* address) +{ + int socket = ::socket(AF_LINK, SOCK_DGRAM, 0); + if (socket < 0) + return errno; + + ifreq request; + if (!prepare_request(request, device)) { + close(socket); + return B_ERROR; + } + + if (ioctl(socket, SIOCGIFADDR, &request, sizeof(struct ifreq)) < 0) { + close(socket); + return errno; + } + + close(socket); + + sockaddr_dl &link = *(sockaddr_dl *)&request.ifr_addr; + if (link.sdl_type != IFT_ETHER) + return B_BAD_TYPE; + + if (link.sdl_alen == 0) + return B_ENTRY_NOT_FOUND; + + uint8 *mac = (uint8 *)LLADDR(&link); + memcpy(address, mac, 6); + + return B_OK; +} + + // #pragma mark - @@ -181,6 +234,23 @@ NetServer::MessageReceived(BMessage* message) break; } + case kMsgConfigureInterface: + { + if (!message->ReturnAddress().IsTargetLocal()) { + // for now, we only accept this message from add-ons + break; + } + + // we need a socket to talk to the networking stack + int socket = ::socket(AF_INET, SOCK_DGRAM, 0); + if (socket < 0) + break; + + _ConfigureInterface(socket, *message); + close(socket); + break; + } + default: BApplication::MessageReceived(message); return; @@ -188,19 +258,6 @@ NetServer::MessageReceived(BMessage* message) } -bool -NetServer::_PrepareRequest(ifreq& request, const char* name) -{ - if (strlen(name) > IF_NAMESIZE) { - fprintf(stderr, "%s: interface name \"%s\" is too long.\n", Name(), name); - return false; - } - - strcpy(request.ifr_name, name); - return true; -} - - bool NetServer::_TestForInterface(int socket, const char* name) { @@ -254,7 +311,7 @@ NetServer::_ConfigureInterface(int socket, BMessage& interface) return B_BAD_VALUE; ifreq request; - if (!_PrepareRequest(request, device)) + if (!prepare_request(request, device)) return B_ERROR; int32 flags; @@ -458,20 +515,70 @@ NetServer::_ConfigureInterface(int socket, BMessage& interface) } +bool +NetServer::_QuitLooperForDevice(const char* device) +{ + LooperMap::iterator iterator = fDeviceMap.find(device); + if (iterator == fDeviceMap.end()) + return false; + + // there is a looper for this device - quit it + iterator->second->Lock(); + iterator->second->Quit(); + + fDeviceMap.erase(iterator); + return true; +} + + +BLooper* +NetServer::_LooperForDevice(const char* device) +{ + LooperMap::const_iterator iterator = fDeviceMap.find(device); + if (iterator == fDeviceMap.end()) + return NULL; + + return iterator->second; +} + + status_t NetServer::_ConfigureDevice(int socket, const char* path) { + _QuitLooperForDevice(path); + + // bring interface up, but don't configure it just yet BMessage interface; interface.AddString("device", path); - - // TODO: enable DHCP instead BMessage address; address.AddString("family", "inet"); - address.AddString("address", "192.168.0.56"); - address.AddString("gateway", "192.168.0.254"); interface.AddMessage("address", &address); - return _ConfigureInterface(socket, interface); + status_t status = _ConfigureInterface(socket, interface); + if (status < B_OK) + return status; + + // add a default route to make the interface accessible, even without an address + route_entry route; + memset(&route, 0, sizeof(route_entry)); + route.flags = RTF_STATIC | RTF_DEFAULT; + + ifreq request; + if (!prepare_request(request, path)) + return B_ERROR; + + request.ifr_route = route; + if (ioctl(socket, SIOCADDRT, &request, sizeof(request)) < 0) { + fprintf(stderr, "%s: Could not add route for %s: %s\n", + Name(), path, strerror(errno)); + } + + AutoconfigLooper* looper = new AutoconfigLooper(this, path); + looper->Run(); + + fDeviceMap[path] = looper; + + return B_OK; } diff --git a/src/servers/net/NetServer.h b/src/servers/net/NetServer.h new file mode 100644 index 0000000000..fd3f11d355 --- /dev/null +++ b/src/servers/net/NetServer.h @@ -0,0 +1,22 @@ +/* + * Copyright 2006, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Axel Dörfler, axeld@pinc-software.de + */ +#ifndef NET_SERVER_H +#define NET_SERVER_H + + +#include + +#include + + +static const uint32 kMsgConfigureInterface = 'COif'; + +extern bool prepare_request(ifreq& request, const char* name); +extern status_t get_mac_address(const char* device, uint8* address); + +#endif // NET_SERVER_H