From dd7f08b519348fc0fb96ec52f841cd09e40da8b8 Mon Sep 17 00:00:00 2001 From: Alexander von Gluck IV Date: Tue, 7 Feb 2012 15:45:36 -0600 Subject: [PATCH] NetServer: Add IPv6 link local address to each nic on boot * Remove old ioctl code, cleanup AutoLooper. * Move link local code into NetServer AutoLooper should only be used for things that count as "auto-configuration" such as DHCP, router advertisements, and DHCPv6 * Properly form IPv6 link local address from MAC address * I think some IPv6 routes are needed still for proper local link connectivity. * Duplicate Address Detection is still a TODO * Style cleanup --- src/servers/net/AutoconfigLooper.cpp | 159 +-------------------------- src/servers/net/AutoconfigLooper.h | 4 - src/servers/net/Jamfile | 2 - src/servers/net/NetServer.cpp | 139 ++++++++++++++--------- 4 files changed, 96 insertions(+), 208 deletions(-) diff --git a/src/servers/net/AutoconfigLooper.cpp b/src/servers/net/AutoconfigLooper.cpp index 5e6eb03c46..18b7d6a583 100644 --- a/src/servers/net/AutoconfigLooper.cpp +++ b/src/servers/net/AutoconfigLooper.cpp @@ -1,9 +1,10 @@ /* - * Copyright 2006-2010, Haiku, Inc. All Rights Reserved. + * Copyright 2006-2012, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. * * Authors: * Axel Dörfler, axeld@pinc-software.de + * Alexander von Gluck, kallisti5@unixzen.com */ @@ -33,7 +34,6 @@ AutoconfigLooper::AutoconfigLooper(BMessenger target, const char* device) fDevice(device), fCurrentClient(NULL) { - memset(fCurrentMac, 0, sizeof(fCurrentMac)); BMessage ready(kMsgReadyToRun); PostMessage(&ready); } @@ -60,12 +60,12 @@ void AutoconfigLooper::_ConfigureIPv4() { // start with DHCP - + if (fCurrentClient == NULL) { fCurrentClient = new DHCPClient(fTarget, fDevice.String()); AddHandler(fCurrentClient); } - + // set IFF_CONFIGURING flag on interface BNetworkInterface interface(fDevice.String()); @@ -120,157 +120,12 @@ AutoconfigLooper::_ConfigureIPv4() } -#ifdef INET6 -static in6_addr -BuildIPv6LinkLocalAddress(uint8 mac[6]) -{ - in6_addr result; - - result.s6_addr[0] = 0xfe; - result.s6_addr[1] = 0x80; - result.s6_addr[2] = 0; - result.s6_addr[3] = 0; - result.s6_addr[4] = 0; - result.s6_addr[5] = 0; - result.s6_addr[6] = 0; - result.s6_addr[7] = 0; - - result.s6_addr[8] = mac[0] ^ 0x02; - result.s6_addr[9] = mac[1]; - result.s6_addr[10] = mac[2]; - result.s6_addr[11] = 0xff; - result.s6_addr[12] = 0xfe; - result.s6_addr[13] = mac[3]; - result.s6_addr[14] = mac[4]; - result.s6_addr[15] = mac[5]; - - return result; -} - - -void -AutoconfigLooper::_ConfigureIPv6LinkLocal(bool add) -{ - // do not touch the loopback device - if (!strncmp(fDevice.String(), "loop", 4)) - return; - - ifreq request; - if (!prepare_request(request, fDevice.String())) - return; - - int socket = ::socket(AF_INET6, SOCK_DGRAM, 0); - if (socket < 0) - return; - - // set IFF_CONFIGURING flag on interface - if (ioctl(socket, SIOCGIFFLAGS, &request, sizeof(struct ifreq)) == 0) { - request.ifr_flags |= IFF_CONFIGURING; - ioctl(socket, SIOCSIFFLAGS, &request, sizeof(struct ifreq)); - } - - uint8 mac[6]; - memcpy(mac, fCurrentMac, 6); - if (add == true) { - if (get_mac_address(fDevice.String(), mac) != B_OK) - add = false; - } - - if (add == true) { - in6_addr inetAddress = BuildIPv6LinkLocalAddress(mac); - if (_AddIPv6LinkLocal(socket, inetAddress) != true) - add = false; - - // save the MAC address for later usage - memcpy(fCurrentMac, mac, 6); - } - - if (add == false) { - static const uint8 zeroMac[6] = {0}; - if (memcmp(fCurrentMac, zeroMac, 6)) { - in6_addr inetAddress = BuildIPv6LinkLocalAddress(fCurrentMac); - _RemoveIPv6LinkLocal(socket, inetAddress); - // reset the stored MAC address - memcpy(fCurrentMac, zeroMac, 6); - } - } - - if (ioctl(socket, SIOCGIFFLAGS, &request, sizeof(struct ifreq)) == 0 - && (request.ifr_flags & IFF_CONFIGURING) == 0) { - // Someone else configured the interface in the mean time - close(socket); - return; - } - - close(socket); -} - - -bool -AutoconfigLooper::_AddIPv6LinkLocal(int socket, const in6_addr &address) -{ - struct ifreq request; - if (!prepare_request(request, fDevice.String())) - return false; - - ifaliasreq aliasRequest; - memset(&aliasRequest, 0, sizeof(ifaliasreq)); - strlcpy(aliasRequest.ifra_name, fDevice.String(), IF_NAMESIZE); - aliasRequest.ifra_addr.ss_len = sizeof(sockaddr_in6); - aliasRequest.ifra_addr.ss_family = AF_INET6; - - if (ioctl(socket, SIOCAIFADDR, &aliasRequest, sizeof(ifaliasreq)) < 0) { - if (errno != B_NAME_IN_USE) - return false; - } - - sockaddr_in6* socketAddress = (sockaddr_in6*)&request.ifr_addr; - socketAddress->sin6_len = sizeof(sockaddr_in6); - socketAddress->sin6_family = AF_INET6; - - // address - memcpy(&socketAddress->sin6_addr, &address, sizeof(in6_addr)); - if (ioctl(socket, SIOCSIFADDR, &request, sizeof(struct ifreq)) < 0) - return false; - - // mask (/64) - memset(socketAddress->sin6_addr.s6_addr, 0xff, 8); - memset(socketAddress->sin6_addr.s6_addr + 8, 0, 8); - - if (ioctl(socket, SIOCSIFNETMASK, &request, sizeof(struct ifreq)) < 0) - return false; - - return true; -} - - -void -AutoconfigLooper::_RemoveIPv6LinkLocal(int socket, const in6_addr &address) -{ - struct ifreq request; - if (!prepare_request(request, fDevice.String())) - return; - - sockaddr_in6* socketAddress = (sockaddr_in6*)&request.ifr_addr; - socketAddress->sin6_len = sizeof(sockaddr_in6); - socketAddress->sin6_family = AF_INET6; - - // address - memcpy(&socketAddress->sin6_addr, &address, sizeof(in6_addr)); - if (ioctl(socket, SIOCDIFADDR, &request, sizeof(struct ifreq)) < 0) - return; -} -#endif // INET6 - - void AutoconfigLooper::_ReadyToRun() { start_watching_network(B_WATCH_NETWORK_LINK_CHANGES, this); _ConfigureIPv4(); -#ifdef INET6 - _ConfigureIPv6LinkLocal(true); -#endif + //_ConfigureIPv6(); // TODO: router advertisement and dhcpv6 } @@ -296,10 +151,8 @@ AutoconfigLooper::MessageReceived(BMessage* message) if ((media & IFM_ACTIVE) != 0) { // Reconfigure the interface when we have a link again _ConfigureIPv4(); + //_ConfigureIPv6(); // TODO: router advertisement and dhcpv6 } -#ifdef INET6 - _ConfigureIPv6LinkLocal((media & IFM_ACTIVE) != 0); -#endif break; default: diff --git a/src/servers/net/AutoconfigLooper.h b/src/servers/net/AutoconfigLooper.h index 669e502250..d08a8ebd52 100644 --- a/src/servers/net/AutoconfigLooper.h +++ b/src/servers/net/AutoconfigLooper.h @@ -29,15 +29,11 @@ public: private: void _RemoveClient(); void _ConfigureIPv4(); - void _ConfigureIPv6LinkLocal(bool add); - bool _AddIPv6LinkLocal(int socket, const in6_addr &); - void _RemoveIPv6LinkLocal(int socket, const in6_addr &); void _ReadyToRun(); BMessenger fTarget; BString fDevice; AutoconfigClient* fCurrentClient; - uint8 fCurrentMac[6]; }; #endif // AUTOCONFIG_LOOPER_H diff --git a/src/servers/net/Jamfile b/src/servers/net/Jamfile index da53e5b6e3..edf2b1ed71 100644 --- a/src/servers/net/Jamfile +++ b/src/servers/net/Jamfile @@ -10,8 +10,6 @@ UseHeaders [ FDirName $(HAIKU_TOP) src libs compat freebsd_wlan ] : true ; #UseHeaders [ FDirName $(HAIKU_TOP) src add-ons kernel network ppp shared libkernelppp headers ] ; #UseHeaders [ FDirName $(HAIKU_TOP) src tests kits net DialUpPreflet ] ; -#local defines = [ FDefines INET6=1 ] ; - SubDirCcFlags $(defines) ; SubDirC++Flags $(defines) ; diff --git a/src/servers/net/NetServer.cpp b/src/servers/net/NetServer.cpp index 4714b675fd..7b59ec42f4 100644 --- a/src/servers/net/NetServer.cpp +++ b/src/servers/net/NetServer.cpp @@ -1,10 +1,11 @@ /* - * Copyright 2006-2010, Haiku, Inc. All Rights Reserved. + * Copyright 2006-2012, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. * * Authors: * Axel Dörfler, axeld@pinc-software.de * Vegard Wærp, vegarwa@online.no + * Alexander von Gluck, kallisti5@unixzen.com */ @@ -16,6 +17,7 @@ #include #include #include +#include #include #include @@ -79,6 +81,7 @@ private: BMessage* suggestedInterface = NULL); void _ConfigureInterfaces( BMessage* _missingDevice = NULL); + void _ConfigureIPv6LinkLocal(const char* name); void _BringUpInterfaces(); void _StartServices(); status_t _HandleDeviceMonitor(BMessage* message); @@ -105,13 +108,6 @@ struct address_family { }; -// AF_INET6 family -#if INET6 -static bool inet6_parse_address(const char* string, sockaddr* address); -static void inet6_set_any_address(sockaddr* address); -static void inet6_set_port(sockaddr* address, int32 port); -#endif - static const address_family kFamilies[] = { { AF_INET, @@ -209,44 +205,6 @@ parse_address(int32& family, const char* argument, BNetworkAddress& address) } -#if INET6 -static bool -inet6_parse_address(const char* string, sockaddr* _address) -{ - sockaddr_in6& address = *(sockaddr_in6*)_address; - - if (inet_pton(AF_INET6, string, &address.sin6_addr) != 1) - return false; - - address.sin6_family = AF_INET6; - address.sin6_len = sizeof(sockaddr_in6); - address.sin6_port = 0; - address.sin6_flowinfo = 0; - address.sin6_scope_id = 0; - - return true; -} - - -void -inet6_set_any_address(sockaddr* _address) -{ - sockaddr_in6& address = *(sockaddr_in6*)_address; - memset(&address, 0, sizeof(sockaddr_in6)); - address.sin6_family = AF_INET6; - address.sin6_len = sizeof(struct sockaddr_in6); -} - - -void -inet6_set_port(sockaddr* _address, int32 port) -{ - sockaddr_in6& address = *(sockaddr_in6*)_address; - address.sin6_port = port; -} -#endif - - // #pragma mark - @@ -574,6 +532,9 @@ NetServer::_ConfigureInterface(BMessage& message) } } + // Set up IPv6 Link Local + _ConfigureIPv6LinkLocal(name); + BMessage addressMessage; for (int32 index = 0; message.FindMessage("address", index, &addressMessage) == B_OK; index++) { @@ -619,7 +580,7 @@ NetServer::_ConfigureInterface(BMessage& message) if (addressMessage.FindString("broadcast", &string) == B_OK) parse_address(family, string, broadcast); } - + if (autoConfig) { _QuitLooperForDevice(name); startAutoConfig = true; @@ -885,6 +846,86 @@ NetServer::_BringUpInterfaces() } +void +NetServer::_ConfigureIPv6LinkLocal(const char* name) +{ + // Don't touch the loopback device + if (!strncmp(name, "loop", 4)) + return; + + int socket = ::socket(AF_INET6, SOCK_DGRAM, 0); + if (socket < 0) { + // No ipv6 support, skip + return; + } + close(socket); + + BNetworkInterface interface(name); + BNetworkAddress link; + status_t result = interface.GetHardwareAddress(link); + if (result != B_OK) + return; + + BString macString = link.ToString(); + + int32 macLength = macString.Length(); + if (macLength != 17) { + syslog(LOG_DEBUG, "%s: MacAddress length (%" B_PRIu32 ") for interface" + " '%s' is invalid\n", __func__, macLength, name); + return; + } + + uint8 mac[6]; + char* macBuffer = macString.LockBuffer(macLength); + sscanf(macBuffer, "%2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx", + &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]); + macString.UnlockBuffer(macLength); + + // Check for a few failure situations + static const char zeroMac[6] = {0, 0, 0, 0, 0, 0}; + static const char fullMac[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + if (memcmp(macBuffer, zeroMac, 6) == 0 + || memcmp(macBuffer, fullMac, 6) == 0) { + // Mac address is all 0 or all FF's + syslog(LOG_DEBUG, "%s: MacAddress for interface '%s' is invalid.", + __func__, name); + return; + } + + // Generate a Link Local Scope address + // (IPv6 address based on Mac address) + in6_addr addressRaw; + memset(addressRaw.s6_addr, 0, sizeof(addressRaw.s6_addr)); + addressRaw.s6_addr[0] = 0xfe; + addressRaw.s6_addr[1] = 0x80; + addressRaw.s6_addr[8] = mac[0] ^ 0x02; + addressRaw.s6_addr[9] = mac[1]; + addressRaw.s6_addr[10] = mac[2]; + addressRaw.s6_addr[11] = 0xff; + addressRaw.s6_addr[12] = 0xfe; + addressRaw.s6_addr[13] = mac[3]; + addressRaw.s6_addr[14] = mac[4]; + addressRaw.s6_addr[15] = mac[5]; + + BNetworkAddress localLinkAddress(addressRaw, 0); + BNetworkAddress localLinkMask("ffff:ffff:ffff:ffff::"); // 64 + BNetworkAddress localLinkBroadcast("fe80::ffff:ffff:ffff:ffff"); + + BNetworkInterfaceAddress interfaceAddress; + interfaceAddress.SetAddress(localLinkAddress); + interfaceAddress.SetMask(localLinkMask); + interfaceAddress.SetBroadcast(localLinkMask); + + /* TODO: Duplicate Address Detection. (DAD) + Need to blast an icmp packet over the IPv6 network from :: to ensure + there aren't duplicate MAC addresses on the network. (definitely an + edge case, but a possible issue) + */ + + interface.AddAddress(interfaceAddress); +} + + void NetServer::_StartServices() { @@ -910,12 +951,12 @@ NetServer::_HandleDeviceMonitor(BMessage* message) // not a device entry, ignore return B_NAME_NOT_FOUND; } - + if (opcode == B_ENTRY_CREATED) _ConfigureDevice(path); else _RemoveInterface(path); - + return B_OK; }