diff options
Diffstat (limited to 'osdep')
| -rw-r--r-- | osdep/Binder.hpp | 321 | ||||
| -rw-r--r-- | osdep/Http.cpp | 2 | ||||
| -rw-r--r-- | osdep/LinuxEthernetTap.cpp | 7 | ||||
| -rw-r--r-- | osdep/Phy.hpp | 4 | ||||
| -rw-r--r-- | osdep/RoutingTable.cpp | 608 | ||||
| -rw-r--r-- | osdep/RoutingTable.hpp | 92 | ||||
| -rw-r--r-- | osdep/WindowsEthernetTap.cpp | 142 |
7 files changed, 1105 insertions, 71 deletions
diff --git a/osdep/Binder.hpp b/osdep/Binder.hpp new file mode 100644 index 00000000..b68e6dac --- /dev/null +++ b/osdep/Binder.hpp @@ -0,0 +1,321 @@ +/* + * ZeroTier One - Network Virtualization Everywhere + * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef ZT_BINDER_HPP +#define ZT_BINDER_HPP + +#include "../node/Constants.hpp" + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifdef __WINDOWS__ +#include <WinSock2.h> +#include <Windows.h> +#include <ShlObj.h> +#include <netioapi.h> +#include <iphlpapi.h> +#else +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/wait.h> +#include <unistd.h> +#include <ifaddrs.h> +#endif + +#include <string> +#include <vector> +#include <algorithm> +#include <utility> + +#include "../node/NonCopyable.hpp" +#include "../node/InetAddress.hpp" +#include "../node/Mutex.hpp" + +#include "Phy.hpp" + +/** + * Period between binder rescans/refreshes + * + * OneService also does this on detected restarts. + */ +#define ZT_BINDER_REFRESH_PERIOD 30000 + +namespace ZeroTier { + +/** + * Enumerates local devices and binds to all potential ZeroTier path endpoints + * + * This replaces binding to wildcard (0.0.0.0 and ::0) with explicit binding + * as part of the path to default gateway support. Under the hood it uses + * different queries on different OSes to enumerate devices, and also exposes + * device enumeration and endpoint IP data for use elsewhere. + * + * On OSes that do not support local port enumeration or where this is not + * meaningful, this degrades to binding to wildcard. + */ +class Binder : NonCopyable +{ +private: + struct _Binding + { + _Binding() : + udpSock((PhySocket *)0), + tcpListenSock((PhySocket *)0), + address() {} + + PhySocket *udpSock; + PhySocket *tcpListenSock; + InetAddress address; + }; + +public: + Binder() {} + + /** + * Close all bound ports + * + * This should be called on shutdown. It closes listen sockets and UDP ports + * but not TCP connections from any TCP listen sockets. + * + * @param phy Physical interface + */ + template<typename PHY_HANDLER_TYPE> + void closeAll(Phy<PHY_HANDLER_TYPE> &phy) + { + Mutex::Lock _l(_lock); + for(typename std::vector<_Binding>::const_iterator i(_bindings.begin());i!=_bindings.end();++i) { + phy.close(i->udpSock,false); + phy.close(i->tcpListenSock,false); + } + } + + /** + * Scan local devices and addresses and rebind TCP and UDP + * + * This should be called after wake from sleep, on detected network device + * changes, on startup, or periodically (e.g. every 30-60s). + * + * @param phy Physical interface + * @param port Port to bind to on all interfaces (TCP and UDP) + * @param ignoreInterfacesByName Ignore these interfaces by name + * @param ignoreInterfacesByNamePrefix Ignore these interfaces by name-prefix (starts-with, e.g. zt ignores zt*) + * @param ignoreInterfacesByAddress Ignore these interfaces by address + * @tparam PHY_HANDLER_TYPE Type for Phy<> template + * @tparam INTERFACE_CHECKER Type for class containing shouldBindInterface() method + */ + template<typename PHY_HANDLER_TYPE,typename INTERFACE_CHECKER> + void refresh(Phy<PHY_HANDLER_TYPE> &phy,unsigned int port,INTERFACE_CHECKER &ifChecker) + { + std::vector<InetAddress> localIfAddrs; + std::vector<_Binding> newBindings; + std::vector<std::string>::const_iterator si; + std::vector<InetAddress>::const_iterator ii; + typename std::vector<_Binding>::const_iterator bi; + PhySocket *udps; + //PhySocket *tcps; + InetAddress ip; + Mutex::Lock _l(_lock); + +#ifdef __WINDOWS__ + + char aabuf[32768]; + ULONG aalen = sizeof(aabuf); + if (GetAdaptersAddresses(AF_UNSPEC,GAA_FLAG_SKIP_ANYCAST|GAA_FLAG_SKIP_MULTICAST|GAA_FLAG_SKIP_DNS_SERVER,(void *)0,reinterpret_cast<PIP_ADAPTER_ADDRESSES>(aabuf),&aalen) == NO_ERROR) { + PIP_ADAPTER_ADDRESSES a = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(aabuf); + while (a) { + PIP_ADAPTER_UNICAST_ADDRESS ua = a->FirstUnicastAddress; + while (ua) { + InetAddress ip(ua->Address.lpSockaddr); + if (ifChecker.shouldBindInterface("",ip)) { + switch(ip.ipScope()) { + default: break; + case InetAddress::IP_SCOPE_PSEUDOPRIVATE: + case InetAddress::IP_SCOPE_GLOBAL: + //case InetAddress::IP_SCOPE_LINK_LOCAL: + case InetAddress::IP_SCOPE_SHARED: + case InetAddress::IP_SCOPE_PRIVATE: + ip.setPort(port); + localIfAddrs.push_back(ip); + break; + } + } + ua = ua->Next; + } + a = a->Next; + } + } + +#else // not __WINDOWS__ + + struct ifaddrs *ifatbl = (struct ifaddrs *)0; + struct ifaddrs *ifa; + if ((getifaddrs(&ifatbl) == 0)&&(ifatbl)) { + ifa = ifatbl; + while (ifa) { + if ((ifa->ifa_name)&&(ifa->ifa_addr)) { + ip = *(ifa->ifa_addr); + if (ifChecker.shouldBindInterface(ifa->ifa_name,ip)) { + switch(ip.ipScope()) { + default: break; + case InetAddress::IP_SCOPE_PSEUDOPRIVATE: + case InetAddress::IP_SCOPE_GLOBAL: + //case InetAddress::IP_SCOPE_LINK_LOCAL: + case InetAddress::IP_SCOPE_SHARED: + case InetAddress::IP_SCOPE_PRIVATE: + ip.setPort(port); + localIfAddrs.push_back(ip); + break; + } + } + } + ifa = ifa->ifa_next; + } + freeifaddrs(ifatbl); + } + +#endif + + // Default to binding to wildcard if we can't enumerate addresses + if (localIfAddrs.size() == 0) { + localIfAddrs.push_back(InetAddress((uint32_t)0,port)); + localIfAddrs.push_back(InetAddress((const void *)"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",16,port)); + } + + // Close any old bindings to anything that doesn't exist anymore + for(bi=_bindings.begin();bi!=_bindings.end();++bi) { + if (std::find(localIfAddrs.begin(),localIfAddrs.end(),bi->address) == localIfAddrs.end()) { + phy.close(bi->udpSock,false); + phy.close(bi->tcpListenSock,false); + } + } + + for(ii=localIfAddrs.begin();ii!=localIfAddrs.end();++ii) { + // Copy over bindings that still are valid + for(bi=_bindings.begin();bi!=_bindings.end();++bi) { + if (bi->address == *ii) { + newBindings.push_back(*bi); + break; + } + } + + // Add new bindings + if (bi == _bindings.end()) { + udps = phy.udpBind(reinterpret_cast<const struct sockaddr *>(&(*ii)),(void *)0,ZT_UDP_DESIRED_BUF_SIZE); + if (udps) { + //tcps = phy.tcpListen(reinterpret_cast<const struct sockaddr *>(&ii),(void *)0); + //if (tcps) { + newBindings.push_back(_Binding()); + newBindings.back().udpSock = udps; + //newBindings.back().tcpListenSock = tcps; + newBindings.back().address = *ii; + //} else { + // phy.close(udps,false); + //} + } + } + } + + /* + for(bi=newBindings.begin();bi!=newBindings.end();++bi) { + printf("Binder: bound to %s\n",bi->address.toString().c_str()); + } + */ + + // Swapping pointers and then letting the old one fall out of scope is faster than copying again + _bindings.swap(newBindings); + } + + /** + * Send a UDP packet from the specified local interface, or all + * + * Unfortunately even by examining the routing table there is no ultimately + * robust way to tell where we might reach another host that works in all + * environments. As a result, we send packets with null (wildcard) local + * addresses from *every* bound interface. + * + * These are typically initial HELLOs, path probes, etc., since normal + * conversations will have a local endpoint address. So the cost is low and + * if the peer is not reachable via that route then the packet will go + * nowhere and nothing will happen. + * + * It will of course only send via interface bindings of the same socket + * family. No point in sending V4 via V6 or vice versa. + * + * In any case on most hosts there's only one or two interfaces that we + * will use, so none of this is particularly costly. + * + * @param local Local interface address or null address for 'all' + * @param remote Remote address + * @param data Data to send + * @param len Length of data + * @param v4ttl If non-zero, send this packet with the specified IP TTL (IPv4 only) + */ + template<typename PHY_HANDLER_TYPE> + inline bool udpSend(Phy<PHY_HANDLER_TYPE> &phy,const InetAddress &local,const InetAddress &remote,const void *data,unsigned int len,unsigned int v4ttl = 0) const + { + Mutex::Lock _l(_lock); + if (local) { + for(typename std::vector<_Binding>::const_iterator i(_bindings.begin());i!=_bindings.end();++i) { + if (i->address == local) { + if ((v4ttl)&&(local.ss_family == AF_INET)) + phy.setIp4UdpTtl(i->udpSock,v4ttl); + const bool result = phy.udpSend(i->udpSock,reinterpret_cast<const struct sockaddr *>(&remote),data,len); + if ((v4ttl)&&(local.ss_family == AF_INET)) + phy.setIp4UdpTtl(i->udpSock,255); + return result; + } + } + return false; + } else { + bool result = false; + for(typename std::vector<_Binding>::const_iterator i(_bindings.begin());i!=_bindings.end();++i) { + if (i->address.ss_family == remote.ss_family) { + if ((v4ttl)&&(remote.ss_family == AF_INET)) + phy.setIp4UdpTtl(i->udpSock,v4ttl); + result |= phy.udpSend(i->udpSock,reinterpret_cast<const struct sockaddr *>(&remote),data,len); + if ((v4ttl)&&(remote.ss_family == AF_INET)) + phy.setIp4UdpTtl(i->udpSock,255); + } + } + return result; + } + } + + /** + * @return All currently bound local interface addresses + */ + inline std::vector<InetAddress> allBoundLocalInterfaceAddresses() + { + Mutex::Lock _l(_lock); + std::vector<InetAddress> aa; + for(std::vector<_Binding>::const_iterator i(_bindings.begin());i!=_bindings.end();++i) + aa.push_back(i->address); + return aa; + } + +private: + std::vector<_Binding> _bindings; + Mutex _lock; +}; + +} // namespace ZeroTier + +#endif diff --git a/osdep/Http.cpp b/osdep/Http.cpp index 2e13dd3a..6e3135de 100644 --- a/osdep/Http.cpp +++ b/osdep/Http.cpp @@ -53,7 +53,7 @@ static const struct http_parser_settings HTTP_PARSER_SETTINGS = { struct HttpPhyHandler { // not used - inline void phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *from,void *data,unsigned long len) {} + inline void phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *localAddr,const struct sockaddr *from,void *data,unsigned long len) {} inline void phyOnTcpAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN,const struct sockaddr *from) {} inline void phyOnTcpConnect(PhySocket *sock,void **uptr,bool success) diff --git a/osdep/LinuxEthernetTap.cpp b/osdep/LinuxEthernetTap.cpp index 3e225b36..c66b7a38 100644 --- a/osdep/LinuxEthernetTap.cpp +++ b/osdep/LinuxEthernetTap.cpp @@ -83,8 +83,11 @@ LinuxEthernetTap::LinuxEthernetTap( throw std::runtime_error("max tap MTU is 2800"); _fd = ::open("/dev/net/tun",O_RDWR); - if (_fd <= 0) - throw std::runtime_error(std::string("could not open TUN/TAP device: ") + strerror(errno)); + if (_fd <= 0) { + _fd = ::open("/dev/tun",O_RDWR); + if (_fd <= 0) + throw std::runtime_error(std::string("could not open TUN/TAP device: ") + strerror(errno)); + } struct ifreq ifr; memset(&ifr,0,sizeof(ifr)); diff --git a/osdep/Phy.hpp b/osdep/Phy.hpp index 0f993c9f..c32a36fd 100644 --- a/osdep/Phy.hpp +++ b/osdep/Phy.hpp @@ -89,7 +89,7 @@ typedef void PhySocket; * * For all platforms: * - * phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *from,void *data,unsigned long len) + * phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *localAddr,const struct sockaddr *from,void *data,unsigned long len) * phyOnTcpConnect(PhySocket *sock,void **uptr,bool success) * phyOnTcpAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN,const struct sockaddr *from) * phyOnTcpClose(PhySocket *sock,void **uptr) @@ -963,7 +963,7 @@ public: long n = (long)::recvfrom(s->sock,buf,sizeof(buf),0,(struct sockaddr *)&ss,&slen); if (n > 0) { try { - _handler->phyOnDatagram((PhySocket *)&(*s),&(s->uptr),(const struct sockaddr *)&ss,(void *)buf,(unsigned long)n); + _handler->phyOnDatagram((PhySocket *)&(*s),&(s->uptr),(const struct sockaddr *)&(s->saddr),(const struct sockaddr *)&ss,(void *)buf,(unsigned long)n); } catch ( ... ) {} } else if (n < 0) break; diff --git a/osdep/RoutingTable.cpp b/osdep/RoutingTable.cpp new file mode 100644 index 00000000..40523898 --- /dev/null +++ b/osdep/RoutingTable.cpp @@ -0,0 +1,608 @@ +/* + * ZeroTier One - Network Virtualization Everywhere + * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "../node/Constants.hpp" + +#ifdef __WINDOWS__ +#include <WinSock2.h> +#include <Windows.h> +#include <netioapi.h> +#include <IPHlpApi.h> +#endif + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifdef __UNIX_LIKE__ +#include <unistd.h> +#include <sys/param.h> +#include <sys/sysctl.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <net/route.h> +#include <net/if.h> +#include <net/if_dl.h> +#include <ifaddrs.h> +#endif + +#include <vector> +#include <algorithm> +#include <utility> + +#include "RoutingTable.hpp" + +#define ZT_BSD_ROUTE_CMD "/sbin/route" +#define ZT_LINUX_IP_COMMAND "/sbin/ip" + +namespace ZeroTier { + +// --------------------------------------------------------------------------- + +#ifdef __LINUX__ + +std::vector<RoutingTable::Entry> RoutingTable::get(bool includeLinkLocal,bool includeLoopback) +{ + char buf[131072]; + char *stmp,*stmp2; + std::vector<RoutingTable::Entry> entries; + + { + int fd = ::open("/proc/net/route",O_RDONLY); + if (fd <= 0) + buf[0] = (char)0; + else { + int n = (int)::read(fd,buf,sizeof(buf) - 1); + ::close(fd); + if (n < 0) n = 0; + buf[n] = (char)0; + } + } + + int lineno = 0; + for(char *line=Utils::stok(buf,"\r\n",&stmp);(line);line=Utils::stok((char *)0,"\r\n",&stmp)) { + if (lineno == 0) { + ++lineno; + continue; // skip header + } + + char *iface = (char *)0; + uint32_t destination = 0; + uint32_t gateway = 0; + int metric = 0; + uint32_t mask = 0; + + int fno = 0; + for(char *f=Utils::stok(line,"\t \r\n",&stmp2);(f);f=Utils::stok((char *)0,"\t \r\n",&stmp2)) { + switch(fno) { + case 0: iface = f; break; + case 1: destination = (uint32_t)Utils::hexStrToULong(f); break; + case 2: gateway = (uint32_t)Utils::hexStrToULong(f); break; + case 6: metric = (int)Utils::strToInt(f); break; + case 7: mask = (uint32_t)Utils::hexStrToULong(f); break; + } + ++fno; + } + + if ((iface)&&(destination)) { + RoutingTable::Entry e; + if (destination) + e.destination.set(&destination,4,Utils::countBits(mask)); + e.gateway.set(&gateway,4,0); + e.deviceIndex = 0; // not used on Linux + e.metric = metric; + Utils::scopy(e.device,sizeof(e.device),iface); + if ((e.destination)&&((includeLinkLocal)||(!e.destination.isLinkLocal()))&&((includeLoopback)||((!e.destination.isLoopback())&&(!e.gateway.isLoopback())&&(strcmp(iface,"lo"))))) + entries.push_back(e); + } + + ++lineno; + } + + { + int fd = ::open("/proc/net/ipv6_route",O_RDONLY); + if (fd <= 0) + buf[0] = (char)0; + else { + int n = (int)::read(fd,buf,sizeof(buf) - 1); + ::close(fd); + if (n < 0) n = 0; + buf[n] = (char)0; + } + } + + for(char *line=Utils::stok(buf,"\r\n",&stmp);(line);line=Utils::stok((char *)0,"\r\n",&stmp)) { + char *destination = (char *)0; + unsigned int destPrefixLen = 0; + char *gateway = (char *)0; // next hop in ipv6 terminology + int metric = 0; + char *device = (char *)0; + + int fno = 0; + for(char *f=Utils::stok(line,"\t \r\n",&stmp2);(f);f=Utils::stok((char *)0,"\t \r\n",&stmp2)) { + switch(fno) { + case 0: destination = f; break; + case 1: destPrefixLen = (unsigned int)Utils::hexStrToULong(f); break; + case 4: gateway = f; break; + case 5: metric = (int)Utils::hexStrToLong(f); break; + case 9: device = f; break; + } + ++fno; + } + + if ((device)&&(destination)) { + unsigned char tmp[16]; + RoutingTable::Entry e; + Utils::unhex(destination,tmp,16); + if ((!Utils::isZero(tmp,16))&&(tmp[0] != 0xff)) + e.destination.set(tmp,16,destPrefixLen); + Utils::unhex(gateway,tmp,16); + e.gateway.set(tmp,16,0); + e.deviceIndex = 0; // not used on Linux + e.metric = metric; + Utils::scopy(e.device,sizeof(e.device),device); + if ((e.destination)&&((includeLinkLocal)||(!e.destination.isLinkLocal()))&&((includeLoopback)||((!e.destination.isLoopback())&&(!e.gateway.isLoopback())&&(strcmp(device,"lo"))))) + entries.push_back(e); + } + } + + std::sort(entries.begin(),entries.end()); + return entries; +} + +RoutingTable::Entry RoutingTable::set(const InetAddress &destination,const InetAddress &gateway,const char *device,int metric,bool ifscope) +{ + char metstr[128]; + + if ((!gateway)&&((!device)||(!device[0]))) + return RoutingTable::Entry(); + + Utils::snprintf(metstr,sizeof(metstr),"%d",metric); + + if (metric < 0) { + long pid = (long)vfork(); + if (pid == 0) { + if (gateway) { + if ((device)&&(device[0])) { + ::execl(ZT_LINUX_IP_COMMAND,ZT_LINUX_IP_COMMAND,"route","del",destination.toString().c_str(),"via",gateway.toIpString().c_str(),"dev",device,(const char *)0); + } else { + ::execl(ZT_LINUX_IP_COMMAND,ZT_LINUX_IP_COMMAND,"route","del",destination.toString().c_str(),"via",gateway.toIpString().c_str(),(const char *)0); + } + } else { + ::execl(ZT_LINUX_IP_COMMAND,ZT_LINUX_IP_COMMAND,"route","del",destination.toString().c_str(),"dev",device,(const char *)0); + } + ::_exit(-1); + } else if (pid > 0) { + int exitcode = -1; + ::waitpid(pid,&exitcode,0); + } + } else { + long pid = (long)vfork(); + if (pid == 0) { + if (gateway) { + if ((device)&&(device[0])) { + ::execl(ZT_LINUX_IP_COMMAND,ZT_LINUX_IP_COMMAND,"route","replace",destination.toString().c_str(),"metric",metstr,"via",gateway.toIpString().c_str(),"dev",device,(const char *)0); + } else { + ::execl(ZT_LINUX_IP_COMMAND,ZT_LINUX_IP_COMMAND,"route","replace",destination.toString().c_str(),"metric",metstr,"via",gateway.toIpString().c_str(),(const char *)0); + } + } else { + ::execl(ZT_LINUX_IP_COMMAND,ZT_LINUX_IP_COMMAND,"route","replace",destination.toString().c_str(),"metric",metstr,"dev",device,(const char *)0); + } + ::_exit(-1); + } else if (pid > 0) { + int exitcode = -1; + ::waitpid(pid,&exitcode,0); + } + } + + std::vector<RoutingTable::Entry> rtab(get(true,true)); + std::vector<RoutingTable::Entry>::iterator bestEntry(rtab.end()); + for(std::vector<RoutingTable::Entry>::iterator e(rtab.begin());e!=rtab.end();++e) { + if ((e->destination == destination)&&(e->gateway.ipsEqual(gateway))) { + if ((device)&&(device[0])) { + if (!strcmp(device,e->device)) { + if (metric == e->metric) + bestEntry = e; + } + } + if (bestEntry == rtab.end()) + bestEntry = e; + } + } + if (bestEntry != rtab.end()) + return *bestEntry; + + return RoutingTable::Entry(); +} + +#endif // __LINUX__ + +// --------------------------------------------------------------------------- + +#ifdef __BSD__ + +std::vector<RoutingTable::Entry> RoutingTable::get(bool includeLinkLocal,bool includeLoopback) +{ + std::vector<RoutingTable::Entry> entries; + int mib[6]; + size_t needed; + + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[2] = 0; + mib[3] = 0; + mib[4] = NET_RT_DUMP; + mib[5] = 0; + if (!sysctl(mib,6,NULL,&needed,NULL,0)) { + if (needed <= 0) + return entries; + + char *buf = (char *)::malloc(needed); + if (buf) { + if (!sysctl(mib,6,buf,&needed,NULL,0)) { + struct rt_msghdr *rtm; + for(char *next=buf,*end=buf+needed;next<end;) { + rtm = (struct rt_msghdr *)next; + char *saptr = (char *)(rtm + 1); + char *saend = next + rtm->rtm_msglen; + + if (((rtm->rtm_flags & RTF_LLINFO) == 0)&&((rtm->rtm_flags & RTF_HOST) == 0)&&((rtm->rtm_flags & RTF_UP) != 0)&&((rtm->rtm_flags & RTF_MULTICAST) == 0)) { + RoutingTable::Entry e; + e.deviceIndex = -9999; // unset + + int which = 0; + while (saptr < saend) { + struct sockaddr *sa = (struct sockaddr *)saptr; + unsigned int salen = sa->sa_len; + if (!salen) + break; + + // Skip missing fields in rtm_addrs bit field + while ((rtm->rtm_addrs & 1) == 0) { + rtm->rtm_addrs >>= 1; + ++which; + if (which > 6) + break; + } + if (which > 6) + break; + + rtm->rtm_addrs >>= 1; + switch(which++) { + case 0: + //printf("RTA_DST\n"); + if (sa->sa_family == AF_INET6) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; + // Nobody expects the Spanish inquisition! + if ((sin6->sin6_addr.s6_addr[0] == 0xfe)&&((sin6->sin6_addr.s6_addr[1] & 0xc0) == 0x80)) { + // Our chief weapon is... in-band signaling! + // Seriously who in the living fuck thought this was a good idea and + // then had the sadistic idea to not document it anywhere? Of course it's + // not like there is any documentation on BSD sysctls anyway. + unsigned int interfaceIndex = ((((unsigned int)sin6->sin6_addr.s6_addr[2]) << 8) & 0xff) | (((unsigned int)sin6->sin6_addr.s6_addr[3]) & 0xff); + sin6->sin6_addr.s6_addr[2] = 0; + sin6->sin6_addr.s6_addr[3] = 0; + if (!sin6->sin6_scope_id) + sin6->sin6_scope_id = interfaceIndex; + } + } + e.destination = *sa; + break; + case 1: + //printf("RTA_GATEWAY\n"); + switch(sa->sa_family) { + case AF_LINK: + e.deviceIndex = (int)((const struct sockaddr_dl *)sa)->sdl_index; + break; + case AF_INET: + case AF_INET6: + e.gateway = *sa; + break; + } + break; + case 2: { + if (e.destination.isV6()) { + salen = sizeof(struct sockaddr_in6); // Confess! + unsigned int bits = 0; + for(int i=0;i<16;++i) { + unsigned char c = (unsigned char)((const struct sockaddr_in6 *)sa)->sin6_addr.s6_addr[i]; + if (c == 0xff) + bits += 8; + else break; + /* must they be multiples of 8? Most of the BSD source I can find says yes..? + else { + while ((c & 0x80) == 0x80) { + ++bits; + c <<= 1; + } + break; + } + */ + } + e.destination.setPort(bits); + } else { + salen = sizeof(struct sockaddr_in); // Confess! + e.destination.setPort((unsigned int)Utils::countBits((uint32_t)((const struct sockaddr_in *)sa)->sin_addr.s_addr)); + } + //printf("RTA_NETMASK\n"); + } break; + /* + case 3: + //printf("RTA_GENMASK\n"); + break; + case 4: + //printf("RTA_IFP\n"); + break; + case 5: + //printf("RTA_IFA\n"); + break; + case 6: + //printf("RTA_AUTHOR\n"); + break; + */ + } + + saptr += salen; + } + + e.metric = (int)rtm->rtm_rmx.rmx_hopcount; + if (e.metric < 0) + e.metric = 0; + + InetAddress::IpScope dscope = e.destination.ipScope(); + if ( ((includeLinkLocal)||(dscope != InetAddress::IP_SCOPE_LINK_LOCAL)) && ((includeLoopback)||((dscope != InetAddress::IP_SCOPE_LOOPBACK) && (e.gateway.ipScope() != InetAddress::IP_SCOPE_LOOPBACK) ))) + entries.push_back(e); + } + + next = saend; + } + } + + ::free(buf); + } + } + + for(std::vector<ZeroTier::RoutingTable::Entry>::iterator e1(entries.begin());e1!=entries.end();++e1) { + if ((!e1->device[0])&&(e1->deviceIndex >= 0)) + if_indextoname(e1->deviceIndex,e1->device); + } + for(std::vector<ZeroTier::RoutingTable::Entry>::iterator e1(entries.begin());e1!=entries.end();++e1) { + if ((!e1->device[0])&&(e1->gateway)) { + int bestMetric = 9999999; + for(std::vector<ZeroTier::RoutingTable::Entry>::iterator e2(entries.begin());e2!=entries.end();++e2) { + if ((e2->destination.containsAddress(e1->gateway))&&(e2->metric <= bestMetric)) { + bestMetric = e2->metric; + Utils::scopy(e1->device,sizeof(e1->device),e2->device); + } + } + } + } + + std::sort(entries.begin(),entries.end()); + + return entries; +} + +RoutingTable::Entry RoutingTable::set(const InetAddress &destination,const InetAddress &gateway,const char *device,int metric,bool ifscope) +{ + if ((!gateway)&&((!device)||(!device[0]))) + return RoutingTable::Entry(); + + std::vector<RoutingTable::Entry> rtab(get(true,true)); + + for(std::vector<RoutingTable::Entry>::iterator e(rtab.begin());e!=rtab.end();++e) { + if (e->destination == destination) { + if (((!device)||(!device[0]))||(!strcmp(device,e->device))) { + long p = (long)fork(); + if (p > 0) { + int exitcode = -1; + ::waitpid(p,&exitcode,0); + } else if (p == 0) { + ::close(STDOUT_FILENO); + ::close(STDERR_FILENO); + ::execl(ZT_BSD_ROUTE_CMD,ZT_BSD_ROUTE_CMD,"delete",(destination.isV6() ? "-inet6" : "-inet"),destination.toString().c_str(),(const char *)0); + ::_exit(-1); + } + } + } + } + + if (metric < 0) + return RoutingTable::Entry(); + + { + char hcstr[64]; + Utils::snprintf(hcstr,sizeof(hcstr),"%d",metric); + long p = (long)fork(); + if (p > 0) { + int exitcode = -1; + ::waitpid(p,&exitcode,0); + } else if (p == 0) { + ::close(STDOUT_FILENO); + ::close(STDERR_FILENO); + if (gateway) { + ::execl(ZT_BSD_ROUTE_CMD,ZT_BSD_ROUTE_CMD,"add",(destination.isV6() ? "-inet6" : "-inet"),destination.toString().c_str(),gateway.toIpString().c_str(),"-hopcount",hcstr,(const char *)0); + } else if ((device)&&(device[0])) { + ::execl(ZT_BSD_ROUTE_CMD,ZT_BSD_ROUTE_CMD,"add",(destination.isV6() ? "-inet6" : "-inet"),destination.toString().c_str(),"-interface",device,"-hopcount",hcstr,(const char *)0); + } + ::_exit(-1); + } + } + + rtab = get(true,true); + std::vector<RoutingTable::Entry>::iterator bestEntry(rtab.end()); + for(std::vector<RoutingTable::Entry>::iterator e(rtab.begin());e!=rtab.end();++e) { + if ((e->destination == destination)&&(e->gateway.ipsEqual(gateway))) { + if ((device)&&(device[0])) { + if (!strcmp(device,e->device)) { + if (metric == e->metric) + bestEntry = e; + } + } + if (bestEntry == rtab.end()) + bestEntry = e; + } + } + if (bestEntry != rtab.end()) + return *bestEntry; + + return RoutingTable::Entry(); +} + +#endif // __BSD__ + +// --------------------------------------------------------------------------- + +#ifdef __WINDOWS__ + +static void _copyInetAddressToSockaddrInet(const InetAddress &a,SOCKADDR_INET &sinet) +{ + memset(&sinet,0,sizeof(sinet)); + if (a.isV4()) { + sinet.Ipv4.sin_addr.S_un.S_addr = *((const uint32_t *)a.rawIpData()); + sinet.Ipv4.sin_family = AF_INET; + sinet.Ipv4.sin_port = htons(a.port()); + } else if (a.isV6()) { + memcpy(sinet.Ipv6.sin6_addr.u.Byte,a.rawIpData(),16); + sinet.Ipv6.sin6_family = AF_INET6; + sinet.Ipv6.sin6_port = htons(a.port()); + } +} + +std::vector<RoutingTable::Entry> RoutingTable::get(bool includeLinkLocal,bool includeLoopback) const +{ + std::vector<RoutingTable::Entry> entries; + PMIB_IPFORWARD_TABLE2 rtbl = NULL; + + if (GetIpForwardTable2(AF_UNSPEC,&rtbl) != NO_ERROR) + return entries; + if (!rtbl) + return entries; + + for(ULONG r=0;r<rtbl->NumEntries;++r) { + RoutingTable::Entry e; + switch(rtbl->Table[r].DestinationPrefix.Prefix.si_family) { + case AF_INET: + e.destination.set(&(rtbl->Table[r].DestinationPrefix.Prefix.Ipv4.sin_addr.S_un.S_addr),4,rtbl->Table[r].DestinationPrefix.PrefixLength); + break; + case AF_INET6: + e.destination.set(rtbl->Table[r].DestinationPrefix.Prefix.Ipv6.sin6_addr.u.Byte,16,rtbl->Table[r].DestinationPrefix.PrefixLength); + break; + } + switch(rtbl->Table[r].NextHop.si_family) { + case AF_INET: + e.gateway.set(&(rtbl->Table[r].NextHop.Ipv4.sin_addr.S_un.S_addr),4,0); + break; + case AF_INET6: + e.gateway.set(rtbl->Table[r].NextHop.Ipv6.sin6_addr.u.Byte,16,0); + break; + } + e.deviceIndex = (int)rtbl->Table[r].InterfaceIndex; + e.metric = (int)rtbl->Table[r].Metric; + ConvertInterfaceLuidToNameA(&(rtbl->Table[r].InterfaceLuid),e.device,sizeof(e.device)); + if ((e.destination)&&((includeLinkLocal)||(!e.destination.isLinkLocal()))&&((includeLoopback)||((!e.destination.isLoopback())&&(!e.gateway.isLoopback())))) + entries.push_back(e); + } + + FreeMibTable(rtbl); + std::sort(entries.begin(),entries.end()); + return entries; +} + +RoutingTable::Entry RoutingTable::set(const InetAddress &destination,const InetAddress &gateway,const char *device,int metric,bool ifscope) +{ + NET_LUID luid; + luid.Value = 0; + if (ConvertInterfaceNameToLuidA(device,&luid) != NO_ERROR) + return RoutingTable::Entry(); + + bool needCreate = true; + PMIB_IPFORWARD_TABLE2 rtbl = NULL; + if (GetIpForwardTable2(AF_UNSPEC,&rtbl) != NO_ERROR) + return RoutingTable::Entry(); + if (!rtbl) + return RoutingTable::Entry(); + for(ULONG r=0;r<rtbl->NumEntries;++r) { + if (rtbl->Table[r].InterfaceLuid.Value == luid.Value) { + InetAddress rdest; + switch(rtbl->Table[r].DestinationPrefix.Prefix.si_family) { + case AF_INET: + rdest.set(&(rtbl->Table[r].DestinationPrefix.Prefix.Ipv4.sin_addr.S_un.S_addr),4,rtbl->Table[r].DestinationPrefix.PrefixLength); + break; + case AF_INET6: + rdest.set(rtbl->Table[r].DestinationPrefix.Prefix.Ipv6.sin6_addr.u.Byte,16,rtbl->Table[r].DestinationPrefix.PrefixLength); + break; + } + if (rdest == destination) { + if (metric >= 0) { + _copyInetAddressToSockaddrInet(gateway,rtbl->Table[r].NextHop); + rtbl->Table[r].Metric = metric; + SetIpForwardEntry2(&(rtbl->Table[r])); + needCreate = false; + } else { + DeleteIpForwardEntry2(&(rtbl->Table[r])); + FreeMibTable(rtbl); + return RoutingTable::Entry(); + } + } + } + } + FreeMibTable(rtbl); + + if ((metric >= 0)&&(needCreate)) { + MIB_IPFORWARD_ROW2 nr; + InitializeIpForwardEntry(&nr); + nr.InterfaceLuid.Value = luid.Value; + _copyInetAddressToSockaddrInet(destination,nr.DestinationPrefix.Prefix); + nr.DestinationPrefix.PrefixLength = destination.netmaskBits(); + _copyInetAddressToSockaddrInet(gateway,nr.NextHop); + nr.Metric = metric; + nr.Protocol = MIB_IPPROTO_NETMGMT; + DWORD result = CreateIpForwardEntry2(&nr); + if (result != NO_ERROR) + return RoutingTable::Entry(); + } + + std::vector<RoutingTable::Entry> rtab(get(true,true)); + std::vector<RoutingTable::Entry>::iterator bestEntry(rtab.end()); + for(std::vector<RoutingTable::Entry>::iterator e(rtab.begin());e!=rtab.end();++e) { + if ((e->destination == destination)&&(e->gateway.ipsEqual(gateway))) { + if ((device)&&(device[0])) { + if (!strcmp(device,e->device)) { + if (metric == e->metric) + bestEntry = e; + } + } + if (bestEntry == rtab.end()) + bestEntry = e; + } + } + if (bestEntry != rtab.end()) + return *bestEntry; + return RoutingTable::Entry(); +} + +#endif // __WINDOWS__ + +// --------------------------------------------------------------------------- + +} // namespace ZeroTier diff --git a/osdep/RoutingTable.hpp b/osdep/RoutingTable.hpp new file mode 100644 index 00000000..6f430136 --- /dev/null +++ b/osdep/RoutingTable.hpp @@ -0,0 +1,92 @@ +/* + * ZeroTier One - Network Virtualization Everywhere + * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef ZT_ROUTINGTABLE_HPP +#define ZT_ROUTINGTABLE_HPP + +#include <vector> + +#include "../node/Constants.hpp" +#include "../node/InetAddress.hpp" + +namespace ZeroTier { + +class RoutingTable +{ +public: + struct Entry + { + /** + * Destination IP and netmask bits (CIDR format) + */ + InetAddress destination; + + /** + * Gateway or null address if direct link-level route, netmask/port part of InetAddress not used + */ + InetAddress gateway; + + /** + * System device index or ID (not included in comparison operators, may not be set on all platforms) + */ + int deviceIndex; + + /** + * Metric or hop count -- higher = lower routing priority + */ + int metric; + + /** + * Interface scoped route? (always false if not meaningful on this OS) + */ + bool ifscope; + + /** + * System device name (may be empty if it doesn't exist or isn't important on this OS) + */ + char device[128]; + + /** + * @return True if at least one required field is present (object is not null) + */ + inline operator bool() const { return ((destination)||(gateway)); } + }; + + /** + * Get routing table + * + * @param includeLinkLocal Include link-local IPs? + * @param includeLoopback Include loopback routes? + */ + static std::vector<RoutingTable::Entry> get(bool includeLinkLocal,bool includeLoopback); + + /** + * Add or replace a routing table entry + * + * @param destination Route destination + * @param gateway Gateway or null if local + * @param device Device name (if applicable) + * @param metric Route metric (if applicable) + * @param ifscope Interface bound route? If so, device must be set. (only applicable on some OSes) + */ + static RoutingTable::Entry set(const InetAddress &destination,const InetAddress &gateway,const char *device,int metric,bool ifscope); +}; + +} // namespace ZeroTier + +#endif diff --git a/osdep/WindowsEthernetTap.cpp b/osdep/WindowsEthernetTap.cpp index 5cf74aa6..d60c0d54 100644 --- a/osdep/WindowsEthernetTap.cpp +++ b/osdep/WindowsEthernetTap.cpp @@ -68,7 +68,6 @@ typedef BOOL (WINAPI *SetupDiSetClassInstallParamsA_t)(_In_ HDEVINFO DeviceInfoS typedef CONFIGRET (WINAPI *CM_Get_Device_ID_ExA_t)(_In_ DEVINST dnDevInst,_Out_writes_(BufferLen) PSTR Buffer,_In_ ULONG BufferLen,_In_ ULONG ulFlags,_In_opt_ HMACHINE hMachine); typedef BOOL (WINAPI *SetupDiGetDeviceInstanceIdA_t)(_In_ HDEVINFO DeviceInfoSet,_In_ PSP_DEVINFO_DATA DeviceInfoData,_Out_writes_opt_(DeviceInstanceIdSize) PSTR DeviceInstanceId,_In_ DWORD DeviceInstanceIdSize,_Out_opt_ PDWORD RequiredSize); - namespace ZeroTier { namespace { @@ -477,7 +476,7 @@ WindowsEthernetTap::WindowsEthernetTap( std::string mySubkeyName; if (mtu > 2800) - throw std::runtime_error("MTU too large for Windows tap"); + throw std::runtime_error("MTU too large."); // We "tag" registry entries with the network ID to identify persistent devices Utils::snprintf(tag,sizeof(tag),"%.16llx",(unsigned long long)nwid); @@ -696,37 +695,39 @@ bool WindowsEthernetTap::removeIp(const InetAddress &ip) try { MIB_UNICASTIPADDRESS_TABLE *ipt = (MIB_UNICASTIPADDRESS_TABLE *)0; if (GetUnicastIpAddressTable(AF_UNSPEC,&ipt) == NO_ERROR) { - for(DWORD i=0;i<ipt->NumEntries;++i) { - if (ipt->Table[i].InterfaceLuid.Value == _deviceLuid.Value) { - InetAddress addr; - switch(ipt->Table[i].Address.si_family) { - case AF_INET: - addr.set(&(ipt->Table[i].Address.Ipv4.sin_addr.S_un.S_addr),4,ipt->Table[i].OnLinkPrefixLength); - break; - case AF_INET6: - addr.set(ipt->Table[i].Address.Ipv6.sin6_addr.u.Byte,16,ipt->Table[i].OnLinkPrefixLength); - if (addr.ipScope() == InetAddress::IP_SCOPE_LINK_LOCAL) - continue; // can't remove link-local IPv6 addresses - break; - } - if (addr == ip) { - DeleteUnicastIpAddressEntry(&(ipt->Table[i])); - FreeMibTable(ipt); - - std::vector<std::string> regIps(_getRegistryIPv4Value("IPAddress")); - std::vector<std::string> regSubnetMasks(_getRegistryIPv4Value("SubnetMask")); - std::string ipstr(ip.toIpString()); - for(std::vector<std::string>::iterator rip(regIps.begin()),rm(regSubnetMasks.begin());((rip!=regIps.end())&&(rm!=regSubnetMasks.end()));++rip,++rm) { - if (*rip == ipstr) { - regIps.erase(rip); - regSubnetMasks.erase(rm); - _setRegistryIPv4Value("IPAddress",regIps); - _setRegistryIPv4Value("SubnetMask",regSubnetMasks); + if ((ipt)&&(ipt->NumEntries > 0)) { + for(DWORD i=0;i<(DWORD)ipt->NumEntries;++i) { + if (ipt->Table[i].InterfaceLuid.Value == _deviceLuid.Value) { + InetAddress addr; + switch(ipt->Table[i].Address.si_family) { + case AF_INET: + addr.set(&(ipt->Table[i].Address.Ipv4.sin_addr.S_un.S_addr),4,ipt->Table[i].OnLinkPrefixLength); + break; + case AF_INET6: + addr.set(ipt->Table[i].Address.Ipv6.sin6_addr.u.Byte,16,ipt->Table[i].OnLinkPrefixLength); + if (addr.ipScope() == InetAddress::IP_SCOPE_LINK_LOCAL) + continue; // can't remove link-local IPv6 addresses break; - } } + if (addr == ip) { + DeleteUnicastIpAddressEntry(&(ipt->Table[i])); + FreeMibTable(ipt); + + std::vector<std::string> regIps(_getRegistryIPv4Value("IPAddress")); + std::vector<std::string> regSubnetMasks(_getRegistryIPv4Value("SubnetMask")); + std::string ipstr(ip.toIpString()); + for(std::vector<std::string>::iterator rip(regIps.begin()),rm(regSubnetMasks.begin());((rip!=regIps.end())&&(rm!=regSubnetMasks.end()));++rip,++rm) { + if (*rip == ipstr) { + regIps.erase(rip); + regSubnetMasks.erase(rm); + _setRegistryIPv4Value("IPAddress",regIps); + _setRegistryIPv4Value("SubnetMask",regSubnetMasks); + break; + } + } - return true; + return true; + } } } } @@ -747,19 +748,21 @@ std::vector<InetAddress> WindowsEthernetTap::ips() const try { MIB_UNICASTIPADDRESS_TABLE *ipt = (MIB_UNICASTIPADDRESS_TABLE *)0; if (GetUnicastIpAddressTable(AF_UNSPEC,&ipt) == NO_ERROR) { - for(DWORD i=0;i<ipt->NumEntries;++i) { - if (ipt->Table[i].InterfaceLuid.Value == _deviceLuid.Value) { - switch(ipt->Table[i].Address.si_family) { - 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.push_back(ip); - } break; - case AF_INET6: { - InetAddress ip(ipt->Table[i].Address.Ipv6.sin6_addr.u.Byte,16,ipt->Table[i].OnLinkPrefixLength); - if ((ip != linkLocalLoopback)&&(ip != InetAddress::LO6)) - addrs.push_back(ip); - } break; + if ((ipt)&&(ipt->NumEntries > 0)) { + for(DWORD i=0;i<(DWORD)ipt->NumEntries;++i) { + if (ipt->Table[i].InterfaceLuid.Value == _deviceLuid.Value) { + switch(ipt->Table[i].Address.si_family) { + 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.push_back(ip); + } break; + case AF_INET6: { + InetAddress ip(ipt->Table[i].Address.Ipv6.sin6_addr.u.Byte,16,ipt->Table[i].OnLinkPrefixLength); + if ((ip != linkLocalLoopback)&&(ip != InetAddress::LO6)) + addrs.push_back(ip); + } break; + } } } } @@ -768,7 +771,7 @@ std::vector<InetAddress> WindowsEthernetTap::ips() const } catch ( ... ) {} // sanity check, shouldn't happen unless out of memory std::sort(addrs.begin(),addrs.end()); - std::unique(addrs.begin(),addrs.end()); + addrs.erase(std::unique(addrs.begin(),addrs.end()),addrs.end()); return addrs; } @@ -825,14 +828,16 @@ void WindowsEthernetTap::scanMulticastGroups(std::vector<MulticastGroup> &added, unsigned char mcastbuf[TAP_WIN_IOCTL_GET_MULTICAST_MEMBERSHIPS_OUTPUT_BUF_SIZE]; DWORD bytesReturned = 0; if (DeviceIoControl(t,TAP_WIN_IOCTL_GET_MULTICAST_MEMBERSHIPS,(LPVOID)0,0,(LPVOID)mcastbuf,sizeof(mcastbuf),&bytesReturned,NULL)) { - MAC mac; - DWORD i = 0; - while ((i + 6) <= bytesReturned) { - mac.setTo(mcastbuf + i,6); - i += 6; - if ((mac.isMulticast())&&(!mac.isBroadcast())) { - // exclude the nulls that may be returned or any other junk Windows puts in there - newGroups.push_back(MulticastGroup(mac,0)); + if ((bytesReturned > 0)&&(bytesReturned <= TAP_WIN_IOCTL_GET_MULTICAST_MEMBERSHIPS_OUTPUT_BUF_SIZE)) { // sanity check + MAC mac; + DWORD i = 0; + while ((i + 6) <= bytesReturned) { + mac.setTo(mcastbuf + i,6); + i += 6; + if ((mac.isMulticast())&&(!mac.isBroadcast())) { + // exclude the nulls that may be returned or any other junk Windows puts in there + newGroups.push_back(MulticastGroup(mac,0)); + } } } } @@ -842,7 +847,7 @@ void WindowsEthernetTap::scanMulticastGroups(std::vector<MulticastGroup> &added, newGroups.push_back(MulticastGroup::deriveMulticastGroupForAddressResolution(*ip)); std::sort(newGroups.begin(),newGroups.end()); - std::unique(newGroups.begin(),newGroups.end()); + newGroups.erase(std::unique(newGroups.begin(),newGroups.end()),newGroups.end()); for(std::vector<MulticastGroup>::iterator m(newGroups.begin());m!=newGroups.end();++m) { if (!std::binary_search(_multicastGroups.begin(),_multicastGroups.end(),*m)) @@ -869,18 +874,19 @@ void WindowsEthernetTap::threadMain() try { while (_run) { // Because Windows + Sleep(250); setPersistentTapDeviceState(_deviceInstanceId.c_str(),false); - Sleep(500); + Sleep(250); setPersistentTapDeviceState(_deviceInstanceId.c_str(),true); - Sleep(500); + Sleep(250); setPersistentTapDeviceState(_deviceInstanceId.c_str(),false); - Sleep(500); + Sleep(250); setPersistentTapDeviceState(_deviceInstanceId.c_str(),true); - Sleep(500); + Sleep(250); _tap = CreateFileA(tapPath,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_SYSTEM|FILE_FLAG_OVERLAPPED,NULL); if (_tap == INVALID_HANDLE_VALUE) { - Sleep(1000); + Sleep(250); continue; } @@ -948,7 +954,7 @@ void WindowsEthernetTap::threadMain() ipnr.ReachabilityTime.LastUnreachable = 1; DWORD result = CreateIpNetEntry2(&ipnr); if (result != NO_ERROR) - Sleep(500); + Sleep(250); else break; } for(int i=0;i<8;++i) { @@ -963,7 +969,7 @@ void WindowsEthernetTap::threadMain() nr.Protocol = MIB_IPPROTO_NETMGMT; DWORD result = CreateIpForwardEntry2(&nr); if (result != NO_ERROR) - Sleep(500); + Sleep(250); else break; } } @@ -1021,8 +1027,10 @@ void WindowsEthernetTap::threadMain() } } - if ((waitResult == WAIT_TIMEOUT)||(waitResult == WAIT_FAILED)) + if ((waitResult == WAIT_TIMEOUT)||(waitResult == WAIT_FAILED)) { + Sleep(250); // guard against spinning under some conditions continue; + } if (HasOverlappedIoCompleted(&tapOvlRead)) { DWORD bytesRead = 0; @@ -1075,11 +1083,13 @@ NET_IFINDEX WindowsEthernetTap::_getDeviceIndex() if (GetIfTable2Ex(MibIfTableRaw,&ift) != NO_ERROR) throw std::runtime_error("GetIfTable2Ex() failed"); - for(ULONG i=0;i<ift->NumEntries;++i) { - if (ift->Table[i].InterfaceLuid.Value == _deviceLuid.Value) { - NET_IFINDEX idx = ift->Table[i].InterfaceIndex; - FreeMibTable(ift); - return idx; + if (ift->NumEntries > 0) { + for(ULONG i=0;i<ift->NumEntries;++i) { + if (ift->Table[i].InterfaceLuid.Value == _deviceLuid.Value) { + NET_IFINDEX idx = ift->Table[i].InterfaceIndex; + FreeMibTable(ift); + return idx; + } } } |
