diff --git a/node/EthernetTap.cpp b/node/EthernetTap.cpp index 5ae537a93..47355cbcb 100644 --- a/node/EthernetTap.cpp +++ b/node/EthernetTap.cpp @@ -1089,6 +1089,15 @@ EthernetTap::EthernetTap( if (RegGetValueA(nwAdapters,subkeyName,"DeviceInstanceID",RRF_RT_ANY,&type,(PVOID)data,&dataLen) == ERROR_SUCCESS) _myDeviceInstanceIdPath.assign(data,dataLen); mySubkeyName = subkeyName; + + // Disable DHCP by default on newly created devices + HKEY tcpIpInterfaces; + if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,"SYSTEM\\CurrentControlSet\\services\\Tcpip\\Parameters\\Interfaces",0,KEY_READ|KEY_WRITE,&tcpIpInterfaces) == ERROR_SUCCESS) { + DWORD enable = 0; + RegSetKeyValueA(tcpIpInterfaces,_myDeviceInstanceId.c_str(),"EnableDHCP",REG_DWORD,&enable,sizeof(enable)); + RegCloseKey(tcpIpInterfaces); + } + subkeyIndex = -1; // break outer loop } } @@ -1133,14 +1142,6 @@ EthernetTap::EthernetTap( throw std::runtime_error("unable to convert instance ID GUID to native GUID (invalid NetCfgInstanceId in registry?)"); } - // Disable DHCP -- this might get changed if/when DHCP is supported - HKEY tcpIpInterfaces; - if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,"SYSTEM\\CurrentControlSet\\services\\Tcpip\\Parameters\\Interfaces",0,KEY_READ|KEY_WRITE,&tcpIpInterfaces) == ERROR_SUCCESS) { - DWORD enable = 0; - RegSetKeyValueA(tcpIpInterfaces,_myDeviceInstanceId.c_str(),"EnableDHCP",REG_DWORD,&enable,sizeof(enable)); - RegCloseKey(tcpIpInterfaces); - } - // Disable and enable interface to ensure registry settings take effect { STARTUPINFOA startupInfo; @@ -1241,40 +1242,70 @@ void EthernetTap::setDisplayName(const char *dn) bool EthernetTap::addIP(const InetAddress &ip) { - Mutex::Lock _l(_ips_m); - - if (_ips.count(ip)) - return true; - - if (!ip.port()) + if (!ip.netmaskBits()) // sanity check... netmask of 0.0.0.0 is WUT? return false; + std::set haveIps(ips()); + try { - std::pair ifidx = _findAdapterByGuid(_deviceGuid); - MIB_UNICASTIPADDRESS_ROW ipr; + // Add IP to interface at the netlink level if not already assigned. + if (!haveIps.count(ip)) { + std::pair ifidx = _findAdapterByGuid(_deviceGuid); + MIB_UNICASTIPADDRESS_ROW ipr; - InitializeUnicastIpAddressEntry(&ipr); - if (ip.isV4()) { - ipr.Address.Ipv4.sin_family = AF_INET; - ipr.Address.Ipv4.sin_addr.S_un.S_addr = *((const uint32_t *)ip.rawIpData()); - ipr.OnLinkPrefixLength = ip.port(); - } else if (ip.isV6()) { - } else return false; + InitializeUnicastIpAddressEntry(&ipr); + if (ip.isV4()) { + ipr.Address.Ipv4.sin_family = AF_INET; + ipr.Address.Ipv4.sin_addr.S_un.S_addr = *((const uint32_t *)ip.rawIpData()); + ipr.OnLinkPrefixLength = ip.port(); + } else if (ip.isV6()) { + // TODO + } else return false; - ipr.PrefixOrigin = IpPrefixOriginManual; - ipr.SuffixOrigin = IpSuffixOriginManual; - ipr.ValidLifetime = 0xffffffff; - ipr.PreferredLifetime = 0xffffffff; + ipr.PrefixOrigin = IpPrefixOriginManual; + ipr.SuffixOrigin = IpSuffixOriginManual; + ipr.ValidLifetime = 0xffffffff; + ipr.PreferredLifetime = 0xffffffff; - ipr.InterfaceLuid = ifidx.first; - ipr.InterfaceIndex = ifidx.second; + ipr.InterfaceLuid = ifidx.first; + ipr.InterfaceIndex = ifidx.second; - if (CreateUnicastIpAddressEntry(&ipr) == NO_ERROR) { - _ips.insert(ip); - return true; + if (CreateUnicastIpAddressEntry(&ipr) == NO_ERROR) { + haveIps.insert(ip); + } else { + LOG("unable to add IP address %s to interface %s: %d",ip.toString().c_str(),deviceName().c_str(),(int)GetLastError()); + return false; + } } - } catch ( ... ) {} + // Update registry to contain all non-link-local IPs for this interface + std::string regMultiIps,regMultiNetmasks; + for(std::set::const_iterator i(haveIps.begin());i!=haveIps.end();++i) { + if (!i->isLinkLocal()) { + regMultiIps.append(i->toIpString()); + regMultiIps.push_back((char)0); + regMultiNetmasks.append(i->netmask().toIpString()); + regMultiNetmasks.push_back((char)0); + } + } + HKEY tcpIpInterfaces; + if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,"SYSTEM\\CurrentControlSet\\services\\Tcpip\\Parameters\\Interfaces",0,KEY_READ|KEY_WRITE,&tcpIpInterfaces) == ERROR_SUCCESS) { + if (regMultiIps.length()) { + regMultiIps.push_back((char)0); + regMultiNetmasks.push_back((char)0); + RegSetKeyValueA(tcpIpInterfaces,_myDeviceInstanceId.c_str(),"IPAddress",REG_MULTI_SZ,regMultiIps.data(),(DWORD)regMultiIps.length()); + RegSetKeyValueA(tcpIpInterfaces,_myDeviceInstanceId.c_str(),"SubnetMask",REG_MULTI_SZ,regMultiNetmasks.data(),(DWORD)regMultiNetmasks.length()); + } else { + RegDeleteKeyValueA(tcpIpInterfaces,_myDeviceInstanceId.c_str(),"IPAddress"); + RegDeleteKeyValueA(tcpIpInterfaces,_myDeviceInstanceId.c_str(),"SubnetMask"); + } + } + RegCloseKey(tcpIpInterfaces); + } catch (std::exception &exc) { + LOG("unexpected exception adding IP address to %s: %s",ip.toString().c_str(),deviceName().c_str(),exc.what()); + } catch ( ... ) { + LOG("unexpected exception adding IP address %s to %s: unknown exception",ip.toString().c_str(),deviceName().c_str()); + } return false; } @@ -1283,7 +1314,6 @@ bool EthernetTap::removeIP(const InetAddress &ip) try { MIB_UNICASTIPADDRESS_TABLE *ipt = (MIB_UNICASTIPADDRESS_TABLE *)0; std::pair ifidx = _findAdapterByGuid(_deviceGuid); - if (GetUnicastIpAddressTable(AF_UNSPEC,&ipt) == NO_ERROR) { for(DWORD i=0;iNumEntries;++i) { if ((ipt->Table[i].InterfaceLuid.Value == ifidx.first.Value)&&(ipt->Table[i].InterfaceIndex == ifidx.second)) { @@ -1294,26 +1324,26 @@ bool EthernetTap::removeIP(const InetAddress &ip) break; case AF_INET6: addr.set(ipt->Table[i].Address.Ipv6.sin6_addr.u.Byte,16,ipt->Table[i].OnLinkPrefixLength); + if (addr.isLinkLocal()) + continue; // can't remove link-local IPv6 addresses break; } if (addr == ip) { DeleteUnicastIpAddressEntry(&(ipt->Table[i])); FreeMibTable(ipt); - Mutex::Lock _l(_ips_m); - _ips.erase(ip); return true; } } } - FreeMibTable(&ipt); + FreeMibTable((PVOID)ipt); } } catch ( ... ) {} return false; } -std::set EthernetTap::allIps() const +std::set EthernetTap::ips() const { - static const InetAddress ifLoopback("fe80::1",64); + static const InetAddress linkLocalLoopback("fe80::1",64); // what is this and why does Windows assign it? std::set addrs; try { @@ -1324,12 +1354,14 @@ std::set EthernetTap::allIps() const for(DWORD i=0;iNumEntries;++i) { if ((ipt->Table[i].InterfaceLuid.Value == ifidx.first.Value)&&(ipt->Table[i].InterfaceIndex == ifidx.second)) { switch(ipt->Table[i].Address.si_family) { - case AF_INET: - addrs.insert(InetAddress(&(ipt->Table[i].Address.Ipv4.sin_addr.S_un.S_addr),4,ipt->Table[i].OnLinkPrefixLength)); - break; + case AF_INET: { + InetAddress ip(&(ipt->Table[i].Address.Ipv4.sin_addr.S_un.S_addr),4,ipt->Table[i].OnLinkPrefixLength); + if (ip != InetAddress::LO4) + addrs.insert(ip); + } break; case AF_INET6: { InetAddress ip(ipt->Table[i].Address.Ipv6.sin6_addr.u.Byte,16,ipt->Table[i].OnLinkPrefixLength); - if (ip != ifLoopback) // don't include fe80::1 + if ((ip != linkLocalLoopback)&&(ip != InetAddress::LO6)) addrs.insert(ip); } break; } @@ -1372,8 +1404,8 @@ bool EthernetTap::updateMulticastGroups(std::set &groups) // Ensure that groups are added for each IP... this handles the MAC:ADI // groups that are created from IPv4 addresses. Some of these may end // up being duplicates of what the IOCTL returns but that's okay since - // the set will filter these. - std::set ipaddrs(allIps()); + // the set<> will filter that. + std::set ipaddrs(ips()); for(std::set::const_iterator i(ipaddrs.begin());i!=ipaddrs.end();++i) newGroups.insert(MulticastGroup::deriveMulticastGroupForAddressResolution(*i)); diff --git a/node/EthernetTap.hpp b/node/EthernetTap.hpp index 3d91261bb..dd3654d48 100644 --- a/node/EthernetTap.hpp +++ b/node/EthernetTap.hpp @@ -129,24 +129,17 @@ public: /** * Remove an IP from this interface * + * Link-local IP addresses may not be able to be removed, depending on platform and type. + * * @param ip IP and netmask (netmask stored in port field) * @return True if IP removed successfully */ bool removeIP(const InetAddress &ip); /** - * @return Set of IP addresses / netmasks + * @return All IP addresses (V4 and V6) assigned to this interface (including link-local) */ - inline std::set ips() const - { - Mutex::Lock _l(_ips_m); - return _ips; - } - - /** - * @return Set of IP addresses / netmasks included any we did not assign, link-local, etc. - */ - std::set allIps() const; + std::set ips() const; /** * Set this tap's IP addresses to exactly this set of IPs @@ -208,15 +201,14 @@ private: const RuntimeEnvironment *_r; - std::set _ips; - Mutex _ips_m; - void (*_handler)(void *,const MAC &,const MAC &,unsigned int,const Buffer<4096> &); void *_arg; Thread _thread; #ifdef __UNIX_LIKE__ + std::set _ips; + Mutex _ips_m; char _dev[16]; int _fd; int _shutdownSignalPipe[2]; diff --git a/node/InetAddress.hpp b/node/InetAddress.hpp index d90574e5d..49729017c 100644 --- a/node/InetAddress.hpp +++ b/node/InetAddress.hpp @@ -35,6 +35,7 @@ #include #include "Constants.hpp" +#include "Utils.hpp" #ifdef __WINDOWS__ #include @@ -188,6 +189,28 @@ public: _sa.sin6.sin6_port = htons((uint16_t)port); } + /** + * @return True if this is a link-local IP address + */ + inline bool isLinkLocal() const + throw() + { + if (_sa.saddr.sa_family == AF_INET) + return ((Utils::ntoh((uint32_t)_sa.sin.sin_addr.s_addr) & 0xffff0000) == 0xa9fe0000); + else if (_sa.saddr.sa_family == AF_INET6) { + if (_sa.sin6.sin6_addr.s6_addr[0] != 0xfe) return false; + if (_sa.sin6.sin6_addr.s6_addr[1] != 0x80) return false; + if (_sa.sin6.sin6_addr.s6_addr[2] != 0x00) return false; + if (_sa.sin6.sin6_addr.s6_addr[3] != 0x00) return false; + if (_sa.sin6.sin6_addr.s6_addr[4] != 0x00) return false; + if (_sa.sin6.sin6_addr.s6_addr[5] != 0x00) return false; + if (_sa.sin6.sin6_addr.s6_addr[6] != 0x00) return false; + if (_sa.sin6.sin6_addr.s6_addr[7] != 0x00) return false; + return true; + } + return false; + } + /** * @return ASCII IP/port format representation */ @@ -233,6 +256,31 @@ public: return port(); } + /** + * Construct a full netmask as an InetAddress + */ + inline InetAddress netmask() const + throw() + { + InetAddress r(*this); + switch(_sa.saddr.sa_family) { + case AF_INET: + r._sa.sin.sin_addr.s_addr = Utils::hton((uint32_t)(0xffffffff << (32 - netmaskBits()))); + break; + case AF_INET6: { + unsigned char *bf = (unsigned char *)r._sa.sin6.sin6_addr.s6_addr; + signed int bitsLeft = (signed int)netmaskBits(); + for(unsigned int i=0;i<16;++i) { + if (bitsLeft > 0) { + bf[i] = (unsigned char)((bitsLeft >= 8) ? 0xff : (0xff << (8 - bitsLeft))); + bitsLeft -= 8; + } else bf[i] = (unsigned char)0; + } + } break; + } + return r; + } + /** * @return True if this is an IPv4 address */