diff options
| author | Adam Ierymenko <adam.ierymenko@zerotier.com> | 2014-04-08 12:00:21 -0700 |
|---|---|---|
| committer | Adam Ierymenko <adam.ierymenko@zerotier.com> | 2014-04-08 12:00:21 -0700 |
| commit | 5abfb11813d0db9a85903712db2b3389c7c34d6e (patch) | |
| tree | 8bd6c240dd69f519a22147d54f308fefabe0f67c /node/WindowsEthernetTap.cpp | |
| parent | 0b8d6c7f4a9c7753e5da255bd213ef8fde59bb1e (diff) | |
| download | infinitytier-5abfb11813d0db9a85903712db2b3389c7c34d6e.tar.gz infinitytier-5abfb11813d0db9a85903712db2b3389c7c34d6e.zip | |
Some installer stuff, complete refactoring of Windows side of newly split tap driver. Seems to work. Now to see if the cleanup we did here gets rid of the zombie tap device issue on Windows.
Diffstat (limited to 'node/WindowsEthernetTap.cpp')
| -rw-r--r-- | node/WindowsEthernetTap.cpp | 1350 |
1 files changed, 192 insertions, 1158 deletions
diff --git a/node/WindowsEthernetTap.cpp b/node/WindowsEthernetTap.cpp index 5b7ea575..8a9bc035 100644 --- a/node/WindowsEthernetTap.cpp +++ b/node/WindowsEthernetTap.cpp @@ -25,1045 +25,174 @@ * LLC. Start here: http://www.zerotier.com/ */ -#include <string> -#include <map> -#include <set> -#include <algorithm> - #include "Constants.hpp" + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <WinSock2.h> +#include <Windows.h> +#include <tchar.h> +#include <winreg.h> +#include <wchar.h> +#include <ws2ipdef.h> +#include <WS2tcpip.h> +#include <IPHlpApi.h> +#include <nldef.h> +#include <netioapi.h> + #include "EthernetTap.hpp" +#include "WindowsEthernetTap.hpp" #include "Logger.hpp" #include "RuntimeEnvironment.hpp" #include "Utils.hpp" #include "Mutex.hpp" -#include "Utils.hpp" + +#include "..\windows\TapDriver\tap-windows.h" // ff:ff:ff:ff:ff:ff with no ADI static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC(0xff),0); -// -// TAP implementation for *nix OSes, with some specialization for different flavors -// - -#ifdef __UNIX_LIKE__ ///////////////////////////////////////////////////////// - -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <signal.h> -#include <fcntl.h> -#include <errno.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/ioctl.h> -#include <sys/wait.h> -#include <sys/select.h> -#include <netinet/in.h> -#include <net/if_arp.h> -#include <arpa/inet.h> - -// Command identifiers used with command finder static (on various *nixes) -#define ZT_UNIX_IP_COMMAND 1 -#define ZT_UNIX_IFCONFIG_COMMAND 2 -#define ZT_MAC_KEXTLOAD_COMMAND 3 -#define ZT_MAC_KEXTUNLOAD_COMMAND 4 - -// Finds external commands on startup -class _CommandFinder -{ -public: - _CommandFinder() - { - _findCmd(ZT_UNIX_IFCONFIG_COMMAND,"ifconfig"); -#ifdef __LINUX__ - _findCmd(ZT_UNIX_IP_COMMAND,"ip"); -#endif -#ifdef __APPLE__ - _findCmd(ZT_MAC_KEXTLOAD_COMMAND,"kextload"); - _findCmd(ZT_MAC_KEXTUNLOAD_COMMAND,"kextunload"); -#endif - } - - // returns NULL if command was not found - inline const char *operator[](int id) const - throw() - { - std::map<int,std::string>::const_iterator c(_paths.find(id)); - if (c == _paths.end()) - return (const char *)0; - return c->second.c_str(); - } - -private: - inline void _findCmd(int id,const char *name) - { - char tmp[4096]; - ZeroTier::Utils::snprintf(tmp,sizeof(tmp),"/sbin/%s",name); - if (ZeroTier::Utils::fileExists(tmp)) { - _paths[id] = tmp; - return; - } - ZeroTier::Utils::snprintf(tmp,sizeof(tmp),"/usr/sbin/%s",name); - if (ZeroTier::Utils::fileExists(tmp)) { - _paths[id] = tmp; - return; - } - ZeroTier::Utils::snprintf(tmp,sizeof(tmp),"/bin/%s",name); - if (ZeroTier::Utils::fileExists(tmp)) { - _paths[id] = tmp; - return; - } - ZeroTier::Utils::snprintf(tmp,sizeof(tmp),"/usr/bin/%s",name); - if (ZeroTier::Utils::fileExists(tmp)) { - _paths[id] = tmp; - return; - } - } - std::map<int,std::string> _paths; -}; -static const _CommandFinder UNIX_COMMANDS; - -#ifdef __LINUX__ -#include <linux/if.h> -#include <linux/if_tun.h> -#include <linux/if_addr.h> -#include <linux/if_ether.h> -#include <ifaddrs.h> -#endif // __LINUX__ - -#ifdef __APPLE__ - -#include <sys/cdefs.h> -#include <sys/uio.h> -#include <sys/param.h> -#include <sys/sysctl.h> -#include <sys/ioctl.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <net/route.h> -#include <net/if.h> -#include <net/if_dl.h> -#include <net/if_media.h> -struct prf_ra { // stupid OSX compile fix... in6_var defines this in a struct which namespaces it for C++ - u_char onlink : 1; - u_char autonomous : 1; - u_char reserved : 6; -} prf_ra; -#include <netinet6/in6_var.h> -#include <netinet/in.h> -#include <netinet/in_var.h> -#include <netinet/icmp6.h> -#include <netinet6/nd6.h> -#include <ifaddrs.h> - -// These are KERNEL_PRIVATE... why? -#ifndef SIOCAUTOCONF_START -#define SIOCAUTOCONF_START _IOWR('i', 132, struct in6_ifreq) /* accept rtadvd on this interface */ -#endif -#ifndef SIOCAUTOCONF_STOP -#define SIOCAUTOCONF_STOP _IOWR('i', 133, struct in6_ifreq) /* stop accepting rtadv for this interface */ -#endif - -static volatile int EthernetTap_instances = 0; -static ZeroTier::Mutex EthernetTap_instances_m; - -static inline bool _setIpv6Stuff(const char *ifname,bool performNUD,bool acceptRouterAdverts) -{ - struct in6_ndireq nd; - struct in6_ifreq ifr; - - int s = socket(AF_INET6,SOCK_DGRAM,0); - if (s <= 0) - return false; - - memset(&nd,0,sizeof(nd)); - strncpy(nd.ifname,ifname,sizeof(nd.ifname)); - - if (ioctl(s,SIOCGIFINFO_IN6,&nd)) { - close(s); - return false; - } - - unsigned long oldFlags = (unsigned long)nd.ndi.flags; - - if (performNUD) - nd.ndi.flags |= ND6_IFF_PERFORMNUD; - else nd.ndi.flags &= ~ND6_IFF_PERFORMNUD; - - if (oldFlags != (unsigned long)nd.ndi.flags) { - if (ioctl(s,SIOCSIFINFO_FLAGS,&nd)) { - close(s); - return false; - } - } - - memset(&ifr,0,sizeof(ifr)); - strncpy(ifr.ifr_name,ifname,sizeof(ifr.ifr_name)); - if (ioctl(s,acceptRouterAdverts ? SIOCAUTOCONF_START : SIOCAUTOCONF_STOP,&ifr)) { - close(s); - return false; - } - - close(s); - return true; -} - -#endif // __APPLE__ - namespace ZeroTier { -// Only permit one tap to be opened concurrently across the entire process -static Mutex __tapCreateLock; - -#ifdef __LINUX__ -EthernetTap::EthernetTap( - const RuntimeEnvironment *renv, - const char *tag, - const MAC &mac, - unsigned int mtu, - void (*handler)(void *,const MAC &,const MAC &,unsigned int,const Buffer<4096> &), - void *arg) - throw(std::runtime_error) : - _mac(mac), - _mtu(mtu), - _r(renv), - _handler(handler), - _arg(arg), - _fd(0) -{ - char procpath[128]; - struct stat sbuf; - Mutex::Lock _l(__tapCreateLock); // create only one tap at a time, globally - - if (mtu > 4096) - throw std::runtime_error("max tap MTU is 4096"); - - _fd = ::open("/dev/net/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)); - - // Try to recall our last device name, or pick an unused one if that fails. - bool recalledDevice = false; - if ((tag)&&(tag[0])) { - Utils::scopy(ifr.ifr_name,sizeof(ifr.ifr_name),tag); - Utils::snprintf(procpath,sizeof(procpath),"/proc/sys/net/ipv4/conf/%s",ifr.ifr_name); - recalledDevice = (stat(procpath,&sbuf) != 0); - } - if (!recalledDevice) { - int devno = 0; - do { - Utils::snprintf(ifr.ifr_name,sizeof(ifr.ifr_name),"zt%d",devno++); - Utils::snprintf(procpath,sizeof(procpath),"/proc/sys/net/ipv4/conf/%s",ifr.ifr_name); - } while (stat(procpath,&sbuf) == 0); // try zt#++ until we find one that does not exist - } - - ifr.ifr_flags = IFF_TAP | IFF_NO_PI; - if (ioctl(_fd,TUNSETIFF,(void *)&ifr) < 0) { - ::close(_fd); - throw std::runtime_error("unable to configure TUN/TAP device for TAP operation"); - } - - strcpy(_dev,ifr.ifr_name); - - ioctl(_fd,TUNSETPERSIST,0); // valgrind may generate a false alarm here - - // Open an arbitrary socket to talk to netlink - int sock = socket(AF_INET,SOCK_DGRAM,0); - if (sock <= 0) { - ::close(_fd); - throw std::runtime_error("unable to open netlink socket"); - } - - // Set MAC address - ifr.ifr_ifru.ifru_hwaddr.sa_family = ARPHRD_ETHER; - memcpy(ifr.ifr_ifru.ifru_hwaddr.sa_data,mac.data,6); - if (ioctl(sock,SIOCSIFHWADDR,(void *)&ifr) < 0) { - ::close(_fd); - ::close(sock); - throw std::runtime_error("unable to configure TAP hardware (MAC) address"); - return; - } - - // Set MTU - ifr.ifr_ifru.ifru_mtu = (int)mtu; - if (ioctl(sock,SIOCSIFMTU,(void *)&ifr) < 0) { - ::close(_fd); - ::close(sock); - throw std::runtime_error("unable to configure TAP MTU"); - } - - if (fcntl(_fd,F_SETFL,fcntl(_fd,F_GETFL) & ~O_NONBLOCK) == -1) { - ::close(_fd); - throw std::runtime_error("unable to set flags on file descriptor for TAP device"); - } - - /* Bring interface up */ - if (ioctl(sock,SIOCGIFFLAGS,(void *)&ifr) < 0) { - ::close(_fd); - ::close(sock); - throw std::runtime_error("unable to get TAP interface flags"); - } - ifr.ifr_flags |= IFF_UP; - if (ioctl(sock,SIOCSIFFLAGS,(void *)&ifr) < 0) { - ::close(_fd); - ::close(sock); - throw std::runtime_error("unable to set TAP interface flags"); - } - - ::close(sock); - - ::pipe(_shutdownSignalPipe); - - TRACE("tap %s created",_dev); - - _thread = Thread::start(this); -} -#endif // __LINUX__ - -#ifdef __APPLE__ -EthernetTap::EthernetTap( - const RuntimeEnvironment *renv, - const char *tag, - const MAC &mac, - unsigned int mtu, - void (*handler)(void *,const MAC &,const MAC &,unsigned int,const Buffer<4096> &), - void *arg) - throw(std::runtime_error) : - _mac(mac), - _mtu(mtu), - _r(renv), - _handler(handler), - _arg(arg), - _fd(0) -{ - char devpath[64],ethaddr[64],mtustr[16],tmp[4096]; - struct stat stattmp; - Mutex::Lock _l(__tapCreateLock); // create only one tap at a time, globally - - if (mtu > 4096) - throw std::runtime_error("max tap MTU is 4096"); - - // Check for existence of ZT tap devices, try to load module if not there - const char *kextload = UNIX_COMMANDS[ZT_MAC_KEXTLOAD_COMMAND]; - if ((stat("/dev/zt0",&stattmp))&&(kextload)) { - strcpy(tmp,_r->homePath.c_str()); - long kextpid = (long)vfork(); - if (kextpid == 0) { - chdir(tmp); - execl(kextload,kextload,"-q","-repository",tmp,"tap.kext",(const char *)0); - _exit(-1); - } else if (kextpid > 0) { - int exitcode = -1; - waitpid(kextpid,&exitcode,0); - usleep(500); - } else throw std::runtime_error("unable to create subprocess with fork()"); - } - if (stat("/dev/zt0",&stattmp)) - throw std::runtime_error("/dev/zt# tap devices do not exist and unable to load kernel extension"); - - // Try to reopen the last device we had, if we had one and it's still unused. - bool recalledDevice = false; - if ((tag)&&(tag[0])) { - Utils::snprintf(devpath,sizeof(devpath),"/dev/%s",tag); - if (stat(devpath,&stattmp) == 0) { - _fd = ::open(devpath,O_RDWR); - if (_fd > 0) { - Utils::scopy(_dev,sizeof(_dev),tag); - recalledDevice = true; - } - } - } - - // Open the first unused tap device if we didn't recall a previous one. - if (!recalledDevice) { - for(int i=0;i<256;++i) { - Utils::snprintf(devpath,sizeof(devpath),"/dev/zt%d",i); - if (stat(devpath,&stattmp)) - throw std::runtime_error("no more TAP devices available"); - _fd = ::open(devpath,O_RDWR); - if (_fd > 0) { - Utils::snprintf(_dev,sizeof(_dev),"zt%d",i); - break; - } - } - } - - if (_fd <= 0) - throw std::runtime_error("unable to open TAP device or no more devices available"); - - if (fcntl(_fd,F_SETFL,fcntl(_fd,F_GETFL) & ~O_NONBLOCK) == -1) { - ::close(_fd); - throw std::runtime_error("unable to set flags on file descriptor for TAP device"); - } - - const char *ifconfig = UNIX_COMMANDS[ZT_UNIX_IFCONFIG_COMMAND]; - if (!ifconfig) { - ::close(_fd); - throw std::runtime_error("unable to find 'ifconfig' command on system"); - } - - // Configure MAC address and MTU, bring interface up - Utils::snprintf(ethaddr,sizeof(ethaddr),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(int)mac[0],(int)mac[1],(int)mac[2],(int)mac[3],(int)mac[4],(int)mac[5]); - Utils::snprintf(mtustr,sizeof(mtustr),"%u",mtu); - long cpid; - if ((cpid = (long)vfork()) == 0) { - execl(ifconfig,ifconfig,_dev,"lladdr",ethaddr,"mtu",mtustr,"up",(const char *)0); - _exit(-1); - } else { - int exitcode = -1; - waitpid(cpid,&exitcode,0); - if (exitcode) { - ::close(_fd); - throw std::runtime_error("ifconfig failure setting link-layer address and activating tap interface"); - } - } - - _setIpv6Stuff(_dev,true,false); - - ::pipe(_shutdownSignalPipe); - - _thread = Thread::start(this); - - EthernetTap_instances_m.lock(); - ++EthernetTap_instances; - EthernetTap_instances_m.unlock(); -} -#endif // __APPLE__ - -EthernetTap::~EthernetTap() -{ - ::write(_shutdownSignalPipe[1],"\0",1); // causes thread to exit - Thread::join(_thread); - ::close(_fd); - -#ifdef __APPLE__ - EthernetTap_instances_m.lock(); - int instances = --EthernetTap_instances; - EthernetTap_instances_m.unlock(); - if (instances <= 0) { - // Unload OSX kernel extension on the deletion of the last EthernetTap - // instance. - const char *kextunload = UNIX_COMMANDS[ZT_MAC_KEXTUNLOAD_COMMAND]; - if (kextunload) { - char tmp[4096]; - sprintf(tmp,"%s/tap.kext",_r->homePath.c_str()); - long kextpid = (long)vfork(); - if (kextpid == 0) { - execl(kextunload,kextunload,tmp,(const char *)0); - _exit(-1); - } else if (kextpid > 0) { - int exitcode = -1; - waitpid(kextpid,&exitcode,0); - } - } - } -#endif // __APPLE__ -} - -void EthernetTap::setDisplayName(const char *dn) -{ -} - -#ifdef __LINUX__ -static bool ___removeIp(const char *_dev,const InetAddress &ip) -{ - const char *ipcmd = UNIX_COMMANDS[ZT_UNIX_IP_COMMAND]; - if (!ipcmd) - return false; - long cpid = (long)vfork(); - if (cpid == 0) { - execl(ipcmd,ipcmd,"addr","del",ip.toString().c_str(),"dev",_dev,(const char *)0); - _exit(-1); - } else { - int exitcode = -1; - waitpid(cpid,&exitcode,0); - return (exitcode == 0); - } -} - -bool EthernetTap::addIP(const InetAddress &ip) -{ - const char *ipcmd = UNIX_COMMANDS[ZT_UNIX_IP_COMMAND]; - if (!ipcmd) { - LOG("ERROR: could not configure IP address for %s: unable to find 'ip' command on system (checked /sbin, /bin, /usr/sbin, /usr/bin)",_dev); - return false; - } - - if (!ip) - return false; - - std::set<InetAddress> allIps(ips()); - if (allIps.count(ip) > 0) - return true; - - // Remove and reconfigure if address is the same but netmask is different - for(std::set<InetAddress>::iterator i(allIps.begin());i!=allIps.end();++i) { - if (i->ipsEqual(ip)) { - if (___removeIp(_dev,*i)) { - break; - } else { - LOG("WARNING: failed to remove old IP/netmask %s to replace with %s",i->toString().c_str(),ip.toString().c_str()); - } - } - } - - long cpid; - if ((cpid = (long)vfork()) == 0) { - execl(ipcmd,ipcmd,"addr","add",ip.toString().c_str(),"dev",_dev,(const char *)0); - _exit(-1); - } else if (cpid > 0) { - int exitcode = -1; - waitpid(cpid,&exitcode,0); - return (exitcode == 0); - } - - return false; -} -#endif // __LINUX__ - -#ifdef __APPLE__ -static bool ___removeIp(const char *_dev,const InetAddress &ip) -{ - const char *ifconfig = UNIX_COMMANDS[ZT_UNIX_IFCONFIG_COMMAND]; - if (!ifconfig) - return false; - long cpid; - if ((cpid = (long)vfork()) == 0) { - execl(ifconfig,ifconfig,_dev,"inet",ip.toIpString().c_str(),"-alias",(const char *)0); - _exit(-1); - } else { - int exitcode = -1; - waitpid(cpid,&exitcode,0); - return (exitcode == 0); - } - return false; // never reached, make compiler shut up about return value -} - -bool EthernetTap::addIP(const InetAddress &ip) +// Helper function to get an adapter's LUID and index from its GUID. The LUID is +// constant but the index can change, so go ahead and just look them both up by +// the GUID which is constant. (The GUID is the instance ID in the registry.) +static inline std::pair<NET_LUID,NET_IFINDEX> _findAdapterByGuid(const GUID &guid) + throw(std::runtime_error) { - const char *ifconfig = UNIX_COMMANDS[ZT_UNIX_IFCONFIG_COMMAND]; - if (!ifconfig) { - LOG("ERROR: could not configure IP address for %s: unable to find 'ifconfig' command on system (checked /sbin, /bin, /usr/sbin, /usr/bin)",_dev); - return false; - } - - if (!ip) - return false; - - std::set<InetAddress> allIps(ips()); - if (allIps.count(ip) > 0) - return true; // IP/netmask already assigned - - // Remove and reconfigure if address is the same but netmask is different - for(std::set<InetAddress>::iterator i(allIps.begin());i!=allIps.end();++i) { - if ((i->ipsEqual(ip))&&(i->netmaskBits() != ip.netmaskBits())) { - if (___removeIp(_dev,*i)) { - break; - } else { - LOG("WARNING: failed to remove old IP/netmask %s to replace with %s",i->toString().c_str(),ip.toString().c_str()); - } - } - } - - long cpid; - if ((cpid = (long)vfork()) == 0) { - execl(ifconfig,ifconfig,_dev,ip.isV4() ? "inet" : "inet6",ip.toString().c_str(),"alias",(const char *)0); - _exit(-1); - } else { - int exitcode = -1; - waitpid(cpid,&exitcode,0); - return (exitcode == 0); - } - - return false; -} -#endif // __APPLE__ + MIB_IF_TABLE2 *ift = (MIB_IF_TABLE2 *)0; -bool EthernetTap::removeIP(const InetAddress &ip) -{ - if (ips().count(ip) > 0) { - if (___removeIp(_dev,ip)) - return true; - } - return false; -} + if (GetIfTable2Ex(MibIfTableRaw,&ift) != NO_ERROR) + throw std::runtime_error("GetIfTable2Ex() failed"); -std::set<InetAddress> EthernetTap::ips() const -{ - struct ifaddrs *ifa = (struct ifaddrs *)0; - if (getifaddrs(&ifa)) - return std::set<InetAddress>(); - - std::set<InetAddress> r; - - struct ifaddrs *p = ifa; - while (p) { - if ((!strcmp(p->ifa_name,_dev))&&(p->ifa_addr)&&(p->ifa_netmask)&&(p->ifa_addr->sa_family == p->ifa_netmask->sa_family)) { - switch(p->ifa_addr->sa_family) { - case AF_INET: { - struct sockaddr_in *sin = (struct sockaddr_in *)p->ifa_addr; - struct sockaddr_in *nm = (struct sockaddr_in *)p->ifa_netmask; - r.insert(InetAddress(&(sin->sin_addr.s_addr),4,Utils::countBits((uint32_t)nm->sin_addr.s_addr))); - } break; - case AF_INET6: { - struct sockaddr_in6 *sin = (struct sockaddr_in6 *)p->ifa_addr; - struct sockaddr_in6 *nm = (struct sockaddr_in6 *)p->ifa_netmask; - uint32_t b[4]; - memcpy(b,nm->sin6_addr.s6_addr,sizeof(b)); - r.insert(InetAddress(sin->sin6_addr.s6_addr,16,Utils::countBits(b[0]) + Utils::countBits(b[1]) + Utils::countBits(b[2]) + Utils::countBits(b[3]))); - } break; - } + for(ULONG i=0;i<ift->NumEntries;++i) { + if (ift->Table[i].InterfaceGuid == guid) { + std::pair<NET_LUID,NET_IFINDEX> tmp(ift->Table[i].InterfaceLuid,ift->Table[i].InterfaceIndex); + FreeMibTable(ift); + return tmp; } - p = p->ifa_next; } - if (ifa) - freeifaddrs(ifa); - - return r; -} - -void EthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len) -{ - char putBuf[4096 + 14]; - if ((_fd > 0)&&(len <= _mtu)) { - for(int i=0;i<6;++i) - putBuf[i] = to.data[i]; - for(int i=0;i<6;++i) - putBuf[i+6] = from.data[i]; - *((uint16_t *)(putBuf + 12)) = htons((uint16_t)etherType); - memcpy(putBuf + 14,data,len); - len += 14; - - int n = ::write(_fd,putBuf,len); - if (n <= 0) { - LOG("error writing packet to Ethernet tap device: %s",strerror(errno)); - } else if (n != (int)len) { - // Saw this gremlin once, so log it if we see it again... OSX tap - // or something seems to have goofy issues with certain MTUs. - LOG("ERROR: write underrun: %s tap write() wrote %d of %u bytes of frame",_dev,n,len); - } - } -} + FreeMibTable(&ift); -std::string EthernetTap::deviceName() const -{ - return std::string(_dev); + throw std::runtime_error("interface not found"); } -std::string EthernetTap::persistentId() const -{ - return std::string(); -} +// Only create or manipulate devices one at a time to avoid weird driver layer demons +static Mutex _systemTapInitLock; -#ifdef __LINUX__ -bool EthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups) +// Compute some basic environment stuff on startup +class _WinSysEnv { - char *ptr,*ptr2; - unsigned char mac[6]; - std::set<MulticastGroup> newGroups; - - int fd = ::open("/proc/net/dev_mcast",O_RDONLY); - if (fd > 0) { - char buf[131072]; - int n = (int)::read(fd,buf,sizeof(buf)); - if ((n > 0)&&(n < (int)sizeof(buf))) { - buf[n] = (char)0; - for(char *l=strtok_r(buf,"\r\n",&ptr);(l);l=strtok_r((char *)0,"\r\n",&ptr)) { - int fno = 0; - char *devname = (char *)0; - char *mcastmac = (char *)0; - for(char *f=strtok_r(l," \t",&ptr2);(f);f=strtok_r((char *)0," \t",&ptr2)) { - if (fno == 1) - devname = f; - else if (fno == 4) - mcastmac = f; - ++fno; - } - if ((devname)&&(!strcmp(devname,_dev))&&(mcastmac)&&(Utils::unhex(mcastmac,mac,6) == 6)) - newGroups.insert(MulticastGroup(MAC(mac),0)); - } - } - ::close(fd); - } - +public: + _WinSysEnv() { - std::set<InetAddress> allIps(ips()); - for(std::set<InetAddress>::const_iterator i(allIps.begin());i!=allIps.end();++i) - newGroups.insert(MulticastGroup::deriveMulticastGroupForAddressResolution(*i)); - } - - bool changed = false; - - newGroups.insert(_blindWildcardMulticastGroup); // always join this - - for(std::set<MulticastGroup>::iterator mg(newGroups.begin());mg!=newGroups.end();++mg) { - if (!groups.count(*mg)) { - groups.insert(*mg); - changed = true; - } - } - for(std::set<MulticastGroup>::iterator mg(groups.begin());mg!=groups.end();) { - if (!newGroups.count(*mg)) { - groups.erase(mg++); - changed = true; - } else ++mg; +#ifdef _WIN64 + is64Bit = TRUE; + devcon = "\\devcon_x64.exe"; + tapDriver = "\\tap-windows\\x64\\zttap200.inf"; +#else + is64Bit = FALSE; + IsWow64Process(GetCurrentProcess(),&is64Bit); + devcon = ((is64Bit == TRUE) ? "\\devcon_x64.exe" : "\\devcon_x86.exe"); + tapDriver = ((is64Bit == TRUE) ? "\\tap-windows\\x64\\zttap200.inf" : "\\tap-windows\\x86\\zttap200.inf"); +#endif } - - return changed; -} -#endif // __LINUX__ - -#ifdef __APPLE__ -// -------------------------------------------------------------------------- -// This source is from: -// http://www.opensource.apple.com/source/Libinfo/Libinfo-406.17/gen.subproj/getifmaddrs.c?txt -// It's here because OSX 10.6 does not have this convenience function. - -#define SALIGN (sizeof(uint32_t) - 1) -#define SA_RLEN(sa) ((sa)->sa_len ? (((sa)->sa_len + SALIGN) & ~SALIGN) : \ -(SALIGN + 1)) -#define MAX_SYSCTL_TRY 5 -#define RTA_MASKS (RTA_GATEWAY | RTA_IFP | RTA_IFA) - -/* FreeBSD uses NET_RT_IFMALIST and RTM_NEWMADDR from <sys/socket.h> */ -/* We can use NET_RT_IFLIST2 and RTM_NEWMADDR2 on Darwin */ -//#define DARWIN_COMPAT - -//#ifdef DARWIN_COMPAT -#define GIM_SYSCTL_MIB NET_RT_IFLIST2 -#define GIM_RTM_ADDR RTM_NEWMADDR2 -//#else -//#define GIM_SYSCTL_MIB NET_RT_IFMALIST -//#define GIM_RTM_ADDR RTM_NEWMADDR -//#endif - -// Not in 10.6 includes so use our own -struct _intl_ifmaddrs { - struct _intl_ifmaddrs *ifma_next; - struct sockaddr *ifma_name; - struct sockaddr *ifma_addr; - struct sockaddr *ifma_lladdr; + BOOL is64Bit; + const char *devcon; + const char *tapDriver; }; +static const _WinSysEnv _winEnv; -static inline int _intl_getifmaddrs(struct _intl_ifmaddrs **pif) +static bool _disableTapDevice(const RuntimeEnvironment *_r,const std::string deviceInstanceId) { - int icnt = 1; - int dcnt = 0; - int ntry = 0; - size_t len; - size_t needed; - int mib[6]; - int i; - char *buf; - char *data; - char *next; - char *p; - struct ifma_msghdr2 *ifmam; - struct _intl_ifmaddrs *ifa, *ift; - struct rt_msghdr *rtm; - struct sockaddr *sa; - - mib[0] = CTL_NET; - mib[1] = PF_ROUTE; - mib[2] = 0; /* protocol */ - mib[3] = 0; /* wildcard address family */ - mib[4] = GIM_SYSCTL_MIB; - mib[5] = 0; /* no flags */ - do { - if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) - return (-1); - if ((buf = (char *)malloc(needed)) == NULL) - return (-1); - if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) { - if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) { - free(buf); - return (-1); - } - free(buf); - buf = NULL; - } - } while (buf == NULL); - - for (next = buf; next < buf + needed; next += rtm->rtm_msglen) { - rtm = (struct rt_msghdr *)(void *)next; - if (rtm->rtm_version != RTM_VERSION) - continue; - switch (rtm->rtm_type) { - case GIM_RTM_ADDR: - ifmam = (struct ifma_msghdr2 *)(void *)rtm; - if ((ifmam->ifmam_addrs & RTA_IFA) == 0) - break; - icnt++; - p = (char *)(ifmam + 1); - for (i = 0; i < RTAX_MAX; i++) { - if ((RTA_MASKS & ifmam->ifmam_addrs & - (1 << i)) == 0) - continue; - sa = (struct sockaddr *)(void *)p; - len = SA_RLEN(sa); - dcnt += len; - p += len; - } - break; - } - } + HANDLE devconLog = CreateFileA((_r->homePath + "\\devcon.log").c_str(),GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); + if (devconLog != INVALID_HANDLE_VALUE) + SetFilePointer(devconLog,0,0,FILE_END); - data = (char *)malloc(sizeof(struct _intl_ifmaddrs) * icnt + dcnt); - if (data == NULL) { - free(buf); - return (-1); + STARTUPINFOA startupInfo; + startupInfo.cb = sizeof(startupInfo); + if (devconLog != INVALID_HANDLE_VALUE) { + startupInfo.hStdOutput = devconLog; + startupInfo.hStdError = devconLog; } - - ifa = (struct _intl_ifmaddrs *)(void *)data; - data += sizeof(struct _intl_ifmaddrs) * icnt; - - memset(ifa, 0, sizeof(struct _intl_ifmaddrs) * icnt); - ift = ifa; - - for (next = buf; next < buf + needed; next += rtm->rtm_msglen) { - rtm = (struct rt_msghdr *)(void *)next; - if (rtm->rtm_version != RTM_VERSION) - continue; - - switch (rtm->rtm_type) { - case GIM_RTM_ADDR: - ifmam = (struct ifma_msghdr2 *)(void *)rtm; - if ((ifmam->ifmam_addrs & RTA_IFA) == 0) - break; - - p = (char *)(ifmam + 1); - for (i = 0; i < RTAX_MAX; i++) { - if ((RTA_MASKS & ifmam->ifmam_addrs & - (1 << i)) == 0) - continue; - sa = (struct sockaddr *)(void *)p; - len = SA_RLEN(sa); - switch (i) { - case RTAX_GATEWAY: - ift->ifma_lladdr = - (struct sockaddr *)(void *)data; - memcpy(data, p, len); - data += len; - break; - - case RTAX_IFP: - ift->ifma_name = - (struct sockaddr *)(void *)data; - memcpy(data, p, len); - data += len; - break; - - case RTAX_IFA: - ift->ifma_addr = - (struct sockaddr *)(void *)data; - memcpy(data, p, len); - data += len; - break; - - default: - data += len; - break; - } - p += len; - } - ift->ifma_next = ift + 1; - ift = ift->ifma_next; - break; - } + PROCESS_INFORMATION processInfo; + memset(&startupInfo,0,sizeof(STARTUPINFOA)); + memset(&processInfo,0,sizeof(PROCESS_INFORMATION)); + if (!CreateProcessA(NULL,(LPSTR)(std::string("\"") + _r->homePath + _winEnv.devcon + "\" disable @" + deviceInstanceId).c_str(),NULL,NULL,FALSE,0,NULL,NULL,&startupInfo,&processInfo)) { + if (devconLog != INVALID_HANDLE_VALUE) + CloseHandle(devconLog); + return false; } + WaitForSingleObject(processInfo.hProcess,INFINITE); + CloseHandle(processInfo.hProcess); + CloseHandle(processInfo.hThread); - free(buf); + if (devconLog != INVALID_HANDLE_VALUE) + CloseHandle(devconLog); - if (ift > ifa) { - ift--; - ift->ifma_next = NULL; - *pif = ifa; - } else { - *pif = NULL; - free(ifa); - } - return (0); + return true; } - -static inline void _intl_freeifmaddrs(struct _intl_ifmaddrs *ifmp) +static bool _enableTapDevice(const RuntimeEnvironment *_r,const std::string deviceInstanceId) { - free(ifmp); -} - -// -------------------------------------------------------------------------- + HANDLE devconLog = CreateFileA((_r->homePath + "\\devcon.log").c_str(),GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); + if (devconLog != INVALID_HANDLE_VALUE) + SetFilePointer(devconLog,0,0,FILE_END); -bool EthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups) -{ - std::set<MulticastGroup> newGroups; - struct _intl_ifmaddrs *ifmap = (struct _intl_ifmaddrs *)0; - if (!_intl_getifmaddrs(&ifmap)) { - struct _intl_ifmaddrs *p = ifmap; - while (p) { - if (p->ifma_addr->sa_family == AF_LINK) { - struct sockaddr_dl *in = (struct sockaddr_dl *)p->ifma_name; - struct sockaddr_dl *la = (struct sockaddr_dl *)p->ifma_addr; - if ((la->sdl_alen == 6)&&(in->sdl_nlen <= sizeof(_dev))&&(!memcmp(_dev,in->sdl_data,in->sdl_nlen))) - newGroups.insert(MulticastGroup(MAC(la->sdl_data + la->sdl_nlen),0)); - } - p = p->ifma_next; - } - _intl_freeifmaddrs(ifmap); + STARTUPINFOA startupInfo; + startupInfo.cb = sizeof(startupInfo); + if (devconLog != INVALID_HANDLE_VALUE) { + startupInfo.hStdOutput = devconLog; + startupInfo.hStdError = devconLog; } - - { - std::set<InetAddress> allIps(ips()); - for(std::set<InetAddress>::const_iterator i(allIps.begin());i!=allIps.end();++i) - newGroups.insert(MulticastGroup::deriveMulticastGroupForAddressResolution(*i)); + PROCESS_INFORMATION processInfo; + memset(&startupInfo,0,sizeof(STARTUPINFOA)); + memset(&processInfo,0,sizeof(PROCESS_INFORMATION)); + if (!CreateProcessA(NULL,(LPSTR)(std::string("\"") + _r->homePath + _winEnv.devcon + "\" enable @" + deviceInstanceId).c_str(),NULL,NULL,FALSE,0,NULL,NULL,&startupInfo,&processInfo)) { + if (devconLog != INVALID_HANDLE_VALUE) + CloseHandle(devconLog); + return false; } + WaitForSingleObject(processInfo.hProcess,INFINITE); + CloseHandle(processInfo.hProcess); + CloseHandle(processInfo.hThread); - bool changed = false; - - newGroups.insert(_blindWildcardMulticastGroup); // always join this - - for(std::set<MulticastGroup>::iterator mg(newGroups.begin());mg!=newGroups.end();++mg) { - if (!groups.count(*mg)) { - groups.insert(*mg); - changed = true; - } - } - for(std::set<MulticastGroup>::iterator mg(groups.begin());mg!=groups.end();) { - if (!newGroups.count(*mg)) { - groups.erase(mg++); - changed = true; - } else ++mg; - } + if (devconLog != INVALID_HANDLE_VALUE) + CloseHandle(devconLog); - return changed; + return true; } -#endif // __APPLE__ -void EthernetTap::threadMain() - throw() +static void _syncIpsWithRegistry(const std::set<InetAddress> &haveIps,const std::string netCfgInstanceId) { - fd_set readfds,nullfds; - MAC to,from; - int n,nfds,r; - char getBuf[8194]; - Buffer<4096> data; - - // Wait for a moment after startup -- wait for Network to finish - // constructing itself. - Thread::sleep(500); - - FD_ZERO(&readfds); - FD_ZERO(&nullfds); - nfds = (int)std::max(_shutdownSignalPipe[0],_fd) + 1; - - r = 0; - for(;;) { - FD_SET(_shutdownSignalPipe[0],&readfds); - FD_SET(_fd,&readfds); - select(nfds,&readfds,&nullfds,&nullfds,(struct timeval *)0); - - if (FD_ISSET(_shutdownSignalPipe[0],&readfds)) // writes to shutdown pipe terminate thread - break; - - if (FD_ISSET(_fd,&readfds)) { - n = (int)::read(_fd,getBuf + r,sizeof(getBuf) - r); - if (n < 0) { - if ((errno != EINTR)&&(errno != ETIMEDOUT)) { - TRACE("unexpected error reading from tap: %s",strerror(errno)); - break; - } - } else { - // Some tap drivers like to send the ethernet frame and the - // payload in two chunks, so handle that by accumulating - // data until we have at least a frame. - r += n; - if (r > 14) { - if (r > ((int)_mtu + 14)) // sanity check for weird TAP behavior on some platforms - r = _mtu + 14; - for(int i=0;i<6;++i) - to.data[i] = (unsigned char)getBuf[i]; - for(int i=0;i<6;++i) - from.data[i] = (unsigned char)getBuf[i + 6]; - unsigned int etherType = ntohs(((const uint16_t *)getBuf)[6]); - if (etherType != 0x8100) { // VLAN tagged frames are not supported! - data.copyFrom(getBuf + 14,(unsigned int)r - 14); - _handler(_arg,from,to,etherType,data); - } - r = 0; - } - } + // Update registry to contain all non-link-local IPs for this interface + std::string regMultiIps,regMultiNetmasks; + for(std::set<InetAddress>::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); } } -} - -bool EthernetTap::deletePersistentTapDevice(const RuntimeEnvironment *_r,const char *pid) -{ - return false; -} - -int EthernetTap::cleanPersistentTapDevices(const RuntimeEnvironment *_r,const std::set<std::string> &exceptThese,bool alsoRemoveUnassociatedDev) -{ - return 0; -} - -} // namespace ZeroTier - -#endif // __UNIX_LIKE__ ////////////////////////////////////////////////////// - -////////////////////////////////////////////////////////////////////////////// - -#ifdef __WINDOWS__ /////////////////////////////////////////////////////////// - -#include <stdio.h> -#include <stdlib.h> -#include <stdint.h> -#include <string.h> -#include <WinSock2.h> -#include <Windows.h> -#include <iphlpapi.h> -#include <ws2ipdef.h> -#include <WS2tcpip.h> -#include <tchar.h> -#include <winreg.h> -#include <wchar.h> -#include <nldef.h> -#include <netioapi.h> - -#include "..\windows\TapDriver\tap-windows.h" - -namespace ZeroTier { - -// Helper function to get an adapter's LUID and index from its GUID. The LUID is -// constant but the index can change, so go ahead and just look them both up by -// the GUID which is constant. (The GUID is the instance ID in the registry.) -static inline std::pair<NET_LUID,NET_IFINDEX> _findAdapterByGuid(const GUID &guid) - throw(std::runtime_error) -{ - MIB_IF_TABLE2 *ift = (MIB_IF_TABLE2 *)0; - - if (GetIfTable2Ex(MibIfTableRaw,&ift) != NO_ERROR) - throw std::runtime_error("GetIfTable2Ex() failed"); - - for(ULONG i=0;i<ift->NumEntries;++i) { - if (ift->Table[i].InterfaceGuid == guid) { - std::pair<NET_LUID,NET_IFINDEX> tmp(ift->Table[i].InterfaceLuid,ift->Table[i].InterfaceIndex); - FreeMibTable(ift); - return tmp; + 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,netCfgInstanceId.c_str(),"IPAddress",REG_MULTI_SZ,regMultiIps.data(),(DWORD)regMultiIps.length()); + RegSetKeyValueA(tcpIpInterfaces,netCfgInstanceId.c_str(),"SubnetMask",REG_MULTI_SZ,regMultiNetmasks.data(),(DWORD)regMultiNetmasks.length()); + } else { + RegDeleteKeyValueA(tcpIpInterfaces,netCfgInstanceId.c_str(),"IPAddress"); + RegDeleteKeyValueA(tcpIpInterfaces,netCfgInstanceId.c_str(),"SubnetMask"); } } - - FreeMibTable(&ift); - - throw std::runtime_error("interface not found"); + RegCloseKey(tcpIpInterfaces); } -static Mutex _systemTapInitLock; - -EthernetTap::EthernetTap( +WindowsEthernetTap::WindowsEthernetTap( const RuntimeEnvironment *renv, const char *tag, const MAC &mac, @@ -1071,15 +200,15 @@ EthernetTap::EthernetTap( void (*handler)(void *,const MAC &,const MAC &,unsigned int,const Buffer<4096> &), void *arg) throw(std::runtime_error) : - _mac(mac), - _mtu(mtu), + EthernetTap("WindowsEthernetTap",mac,mtu), _r(renv), _handler(handler), _arg(arg), _tap(INVALID_HANDLE_VALUE), _injectSemaphore(INVALID_HANDLE_VALUE), _run(true), - _initialized(false) + _initialized(false), + _enabled(false) { char subkeyName[4096]; char subkeyClass[4096]; @@ -1088,17 +217,6 @@ EthernetTap::EthernetTap( if (mtu > ZT_IF_MTU) throw std::runtime_error("MTU too large for Windows tap"); -#ifdef _WIN64 - BOOL is64Bit = TRUE; - const char *devcon = "\\devcon_x64.exe"; - const char *tapDriver = "\\tap-windows\\x64\\zttap200.inf"; -#else - BOOL is64Bit = FALSE; - IsWow64Process(GetCurrentProcess(),&is64Bit); - const char *devcon = ((is64Bit == TRUE) ? "\\devcon_x64.exe" : "\\devcon_x86.exe"); - const char *tapDriver = ((is64Bit == TRUE) ? "\\tap-windows\\x64\\zttap200.inf" : "\\tap-windows\\x86\\zttap200.inf"); -#endif - Mutex::Lock _l(_systemTapInitLock); // only one thread may mess with taps at a time, process-wide HKEY nwAdapters; @@ -1135,14 +253,14 @@ EthernetTap::EthernetTap( if (RegGetValueA(nwAdapters,subkeyName,"DeviceInstanceID",RRF_RT_ANY,&type,(PVOID)data,&dataLen) == ERROR_SUCCESS) instanceIdPath.assign(data,dataLen); - if ((_myDeviceInstanceId.length() == 0)&&(instanceId.length() != 0)&&(instanceIdPath.length() != 0)) { + if ((_netCfgInstanceId.length() == 0)&&(instanceId.length() != 0)&&(instanceIdPath.length() != 0)) { type = 0; dataLen = sizeof(data); if (RegGetValueA(nwAdapters,subkeyName,"_ZeroTierTapIdentifier",RRF_RT_ANY,&type,(PVOID)data,&dataLen) == ERROR_SUCCESS) { data[dataLen] = '\0'; if (!strcmp(data,tag)) { - _myDeviceInstanceId = instanceId; - _myDeviceInstanceIdPath = instanceIdPath; + _netCfgInstanceId = instanceId; + _deviceInstanceId = instanceIdPath; mySubkeyName = subkeyName; break; // found it! } @@ -1154,7 +272,7 @@ EthernetTap::EthernetTap( } // If there is no device, try to create one - if (_myDeviceInstanceId.length() == 0) { + if (_netCfgInstanceId.length() == 0) { // Log devcon output to a file HANDLE devconLog = CreateFileA((_r->homePath + "\\devcon.log").c_str(),GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); if (devconLog == INVALID_HANDLE_VALUE) { @@ -1174,11 +292,11 @@ EthernetTap::EthernetTap( PROCESS_INFORMATION processInfo; memset(&startupInfo,0,sizeof(STARTUPINFOA)); memset(&processInfo,0,sizeof(PROCESS_INFORMATION)); - if (!CreateProcessA(NULL,(LPSTR)(std::string("\"") + _r->homePath + devcon + "\" install \"" + _r->homePath + tapDriver + "\" zttap200").c_str(),NULL,NULL,FALSE,0,NULL,NULL,&startupInfo,&processInfo)) { + if (!CreateProcessA(NULL,(LPSTR)(std::string("\"") + _r->homePath + _winEnv.devcon + "\" install \"" + _r->homePath + _winEnv.tapDriver + "\" zttap200").c_str(),NULL,NULL,FALSE,0,NULL,NULL,&startupInfo,&processInfo)) { RegCloseKey(nwAdapters); if (devconLog != INVALID_HANDLE_VALUE) CloseHandle(devconLog); - throw std::runtime_error(std::string("unable to find or execute devcon at ")+devcon); + throw std::runtime_error(std::string("unable to find or execute devcon at ") + _winEnv.devcon); } WaitForSingleObject(processInfo.hProcess,INFINITE); CloseHandle(processInfo.hProcess); @@ -1207,18 +325,18 @@ EthernetTap::EthernetTap( if (RegGetValueA(nwAdapters,subkeyName,"NetCfgInstanceId",RRF_RT_ANY,&type,(PVOID)data,&dataLen) == ERROR_SUCCESS) { if (existingDeviceInstances.count(std::string(data,dataLen)) == 0) { RegSetKeyValueA(nwAdapters,subkeyName,"_ZeroTierTapIdentifier",REG_SZ,tag,(DWORD)(strlen(tag)+1)); - _myDeviceInstanceId.assign(data,dataLen); + _netCfgInstanceId.assign(data,dataLen); type = 0; dataLen = sizeof(data); if (RegGetValueA(nwAdapters,subkeyName,"DeviceInstanceID",RRF_RT_ANY,&type,(PVOID)data,&dataLen) == ERROR_SUCCESS) - _myDeviceInstanceIdPath.assign(data,dataLen); + _deviceInstanceId.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)); + RegSetKeyValueA(tcpIpInterfaces,_netCfgInstanceId.c_str(),"EnableDHCP",REG_DWORD,&enable,sizeof(enable)); RegCloseKey(tcpIpInterfaces); } @@ -1231,7 +349,7 @@ EthernetTap::EthernetTap( } } - if (_myDeviceInstanceId.length() > 0) { + if (_netCfgInstanceId.length() > 0) { char tmps[4096]; unsigned int tmpsl = Utils::snprintf(tmps,sizeof(tmps),"%.2X-%.2X-%.2X-%.2X-%.2X-%.2X",(unsigned int)mac.data[0],(unsigned int)mac.data[1],(unsigned int)mac.data[2],(unsigned int)mac.data[3],(unsigned int)mac.data[4],(unsigned int)mac.data[5]) + 1; RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"NetworkAddress",REG_SZ,tmps,tmpsl); @@ -1250,7 +368,7 @@ EthernetTap::EthernetTap( // Convert device GUID junk... blech... is there an easier way to do this? { char nobraces[128]; - const char *nbtmp1 = _myDeviceInstanceId.c_str(); + const char *nbtmp1 = _netCfgInstanceId.c_str(); char *nbtmp2 = nobraces; while (*nbtmp1) { if ((*nbtmp1 != '{')&&(*nbtmp1 != '}')) @@ -1263,59 +381,13 @@ EthernetTap::EthernetTap( } // Disable and enable interface to ensure registry settings take effect - { - HANDLE devconLog = CreateFileA((_r->homePath + "\\devcon.log").c_str(),GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); - if (devconLog != INVALID_HANDLE_VALUE) - SetFilePointer(devconLog,0,0,FILE_END); - { - STARTUPINFOA startupInfo; - startupInfo.cb = sizeof(startupInfo); - if (devconLog != INVALID_HANDLE_VALUE) { - startupInfo.hStdOutput = devconLog; - startupInfo.hStdError = devconLog; - } - PROCESS_INFORMATION processInfo; - memset(&startupInfo,0,sizeof(STARTUPINFOA)); - memset(&processInfo,0,sizeof(PROCESS_INFORMATION)); - if (!CreateProcessA(NULL,(LPSTR)(std::string("\"") + _r->homePath + devcon + "\" disable @" + _myDeviceInstanceIdPath).c_str(),NULL,NULL,FALSE,0,NULL,NULL,&startupInfo,&processInfo)) { - RegCloseKey(nwAdapters); - if (devconLog != INVALID_HANDLE_VALUE) - CloseHandle(devconLog); - throw std::runtime_error(std::string("unable to find or execute devcon at ")+devcon); - } - WaitForSingleObject(processInfo.hProcess,INFINITE); - CloseHandle(processInfo.hProcess); - CloseHandle(processInfo.hThread); - } - Sleep(250); - { - STARTUPINFOA startupInfo; - startupInfo.cb = sizeof(startupInfo); - if (devconLog != INVALID_HANDLE_VALUE) { - startupInfo.hStdOutput = devconLog; - startupInfo.hStdError = devconLog; - } - PROCESS_INFORMATION processInfo; - memset(&startupInfo,0,sizeof(STARTUPINFOA)); - memset(&processInfo,0,sizeof(PROCESS_INFORMATION)); - if (!CreateProcessA(NULL,(LPSTR)(std::string("\"") + _r->homePath + devcon + "\" enable @" + _myDeviceInstanceIdPath).c_str(),NULL,NULL,FALSE,0,NULL,NULL,&startupInfo,&processInfo)) { - RegCloseKey(nwAdapters); - if (devconLog != INVALID_HANDLE_VALUE) - CloseHandle(devconLog); - throw std::runtime_error(std::string("unable to find or execute devcon at ")+devcon); - } - WaitForSingleObject(processInfo.hProcess,INFINITE); - CloseHandle(processInfo.hProcess); - CloseHandle(processInfo.hThread); - } - if (devconLog != INVALID_HANDLE_VALUE) - CloseHandle(devconLog); - Sleep(250); - } + _disableTapDevice(_r,_deviceInstanceId); + if (!_enableTapDevice(_r,_deviceInstanceId)) + throw std::runtime_error("cannot enable tap device driver"); // Open the tap, which is in this weird Windows analog of /dev char tapPath[4096]; - Utils::snprintf(tapPath,sizeof(tapPath),"\\\\.\\Global\\%s.tap",_myDeviceInstanceId.c_str()); + Utils::snprintf(tapPath,sizeof(tapPath),"\\\\.\\Global\\%s.tap",_netCfgInstanceId.c_str()); for(int openTrials=0;;) { // Try multiple times, since there seem to be reports from the field // of driver init timing issues. Blech. @@ -1327,10 +399,11 @@ EthernetTap::EthernetTap( } else break; } - // Set media status to enabled - uint32_t tmpi = 1; - DWORD bytesReturned = 0; - DeviceIoControl(_tap,TAP_WIN_IOCTL_SET_MEDIA_STATUS,&tmpi,sizeof(tmpi),&tmpi,sizeof(tmpi),&bytesReturned,NULL); + setEnabled(true); + if (!_enabled) { + CloseHandle(_tap); + throw std::runtime_error("cannot enable tap device using IOCTL"); + } // Initialized overlapped I/O structures and related events memset(&_tapOvlRead,0,sizeof(_tapOvlRead)); @@ -1346,7 +419,7 @@ EthernetTap::EthernetTap( _initialized = true; } -EthernetTap::~EthernetTap() +WindowsEthernetTap::~WindowsEthernetTap() { _run = false; @@ -1358,48 +431,39 @@ EthernetTap::~EthernetTap() CloseHandle(_tapOvlWrite.hEvent); CloseHandle(_injectSemaphore); -#ifdef _WIN64 - BOOL is64Bit = TRUE; - const char *devcon = "\\devcon_x64.exe"; -#else - BOOL is64Bit = FALSE; - IsWow64Process(GetCurrentProcess(),&is64Bit); - const char *devcon = ((is64Bit == TRUE) ? "\\devcon_x64.exe" : "\\devcon_x86.exe"); -#endif + _disableTapDevice(_r,_deviceInstanceId); +} - // Disable network device on shutdown - HANDLE devconLog = CreateFileA((_r->homePath + "\\devcon.log").c_str(),GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); - STARTUPINFOA startupInfo; - startupInfo.cb = sizeof(startupInfo); - if (devconLog != INVALID_HANDLE_VALUE) { - SetFilePointer(devconLog,0,0,FILE_END); - startupInfo.hStdOutput = devconLog; - startupInfo.hStdError = devconLog; - } - PROCESS_INFORMATION processInfo; - memset(&startupInfo,0,sizeof(STARTUPINFOA)); - memset(&processInfo,0,sizeof(PROCESS_INFORMATION)); - if (CreateProcessA(NULL,(LPSTR)(std::string("\"") + _r->homePath + devcon + "\" disable @" + _myDeviceInstanceIdPath).c_str(),NULL,NULL,FALSE,0,NULL,NULL,&startupInfo,&processInfo)) { - WaitForSingleObject(processInfo.hProcess,INFINITE); - CloseHandle(processInfo.hProcess); - CloseHandle(processInfo.hThread); +void WindowsEthernetTap::setEnabled(bool en) +{ + if (_tap == INVALID_HANDLE_VALUE) { + _enabled = false; + } else { + uint32_t tmpi = (en ? 1 : 0); + DWORD bytesReturned = 0; + if (DeviceIoControl(_tap,TAP_WIN_IOCTL_SET_MEDIA_STATUS,&tmpi,sizeof(tmpi),&tmpi,sizeof(tmpi),&bytesReturned,NULL)) + _enabled = en; + else _enabled = false; } - if (devconLog != INVALID_HANDLE_VALUE) - CloseHandle(devconLog); } -void EthernetTap::setDisplayName(const char *dn) +bool WindowsEthernetTap::enabled() const +{ + return _enabled; +} + +void WindowsEthernetTap::setDisplayName(const char *dn) { if (!_initialized) return; HKEY ifp; - if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,(std::string("SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}\\") + _myDeviceInstanceId).c_str(),0,KEY_READ|KEY_WRITE,&ifp) == ERROR_SUCCESS) { + if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,(std::string("SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}\\") + _netCfgInstanceId).c_str(),0,KEY_READ|KEY_WRITE,&ifp) == ERROR_SUCCESS) { RegSetKeyValueA(ifp,"Connection","Name",REG_SZ,(LPCVOID)dn,(DWORD)(strlen(dn)+1)); RegCloseKey(ifp); } } -bool EthernetTap::addIP(const InetAddress &ip) +bool WindowsEthernetTap::addIP(const InetAddress &ip) { if (!_initialized) return false; @@ -1420,7 +484,9 @@ bool EthernetTap::addIP(const InetAddress &ip) ipr.Address.Ipv4.sin_addr.S_un.S_addr = *((const uint32_t *)ip.rawIpData()); ipr.OnLinkPrefixLength = ip.port(); } else if (ip.isV6()) { - // TODO + ipr.Address.Ipv6.sin6_family = AF_INET6; + memcpy(ipr.Address.Ipv6.sin6_addr.u.Byte,ip.rawIpData(),16); + ipr.OnLinkPrefixLength = ip.port(); } else return false; ipr.PrefixOrigin = IpPrefixOriginManual; @@ -1439,7 +505,7 @@ bool EthernetTap::addIP(const InetAddress &ip) } } - _syncIpsWithRegistry(haveIps); + _syncIpsWithRegistry(haveIps,_netCfgInstanceId); } catch (std::exception &exc) { LOG("unexpected exception adding IP address %s to %s: %s",ip.toString().c_str(),deviceName().c_str(),exc.what()); } catch ( ... ) { @@ -1448,7 +514,7 @@ bool EthernetTap::addIP(const InetAddress &ip) return false; } -bool EthernetTap::removeIP(const InetAddress &ip) +bool WindowsEthernetTap::removeIP(const InetAddress &ip) { if (!_initialized) return false; @@ -1472,7 +538,7 @@ bool EthernetTap::removeIP(const InetAddress &ip) if (addr == ip) { DeleteUnicastIpAddressEntry(&(ipt->Table[i])); FreeMibTable(ipt); - _syncIpsWithRegistry(ips()); + _syncIpsWithRegistry(ips(),_netCfgInstanceId); return true; } } @@ -1487,7 +553,7 @@ bool EthernetTap::removeIP(const InetAddress &ip) return false; } -std::set<InetAddress> EthernetTap::ips() const +std::set<InetAddress> WindowsEthernetTap::ips() const { static const InetAddress linkLocalLoopback("fe80::1",64); // what is this and why does Windows assign it? std::set<InetAddress> addrs; @@ -1518,14 +584,14 @@ std::set<InetAddress> EthernetTap::ips() const } FreeMibTable(ipt); } - } catch ( ... ) {} + } catch ( ... ) {} // sanity check, shouldn't happen unless out of memory return addrs; } -void EthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len) +void WindowsEthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len) { - if (!_initialized) + if ((!_initialized)||(!_enabled)) return; if (len > (ZT_IF_MTU)) return; // sanity check @@ -1544,17 +610,17 @@ void EthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const ReleaseSemaphore(_injectSemaphore,1,NULL); } -std::string EthernetTap::deviceName() const +std::string WindowsEthernetTap::deviceName() const { - return _myDeviceInstanceId; + return _netCfgInstanceId; } -std::string EthernetTap::persistentId() const +std::string WindowsEthernetTap::persistentId() const { - return _myDeviceInstanceIdPath; + return _deviceInstanceId; } -bool EthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups) +bool WindowsEthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups) { if (!_initialized) return false; @@ -1611,7 +677,7 @@ bool EthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups) return changed; } -void EthernetTap::threadMain() +void WindowsEthernetTap::threadMain() throw() { HANDLE wait4[3]; @@ -1623,6 +689,10 @@ void EthernetTap::threadMain() bool writeInProgress = false; for(;;) { + // Windows can DIAF + if (_enabled) + setEnabled(true); + if (!_run) break; DWORD r = WaitForMultipleObjectsEx(writeInProgress ? 3 : 2,wait4,FALSE,INFINITE,TRUE); if (!_run) break; @@ -1630,12 +700,14 @@ void EthernetTap::threadMain() if (HasOverlappedIoCompleted(&_tapOvlRead)) { DWORD bytesRead = 0; if (GetOverlappedResult(_tap,&_tapOvlRead,&bytesRead,FALSE)) { - if (bytesRead > 14) { + if ((bytesRead > 14)&&(_enabled)) { MAC to(_tapReadBuf); MAC from(_tapReadBuf + 6); - unsigned int etherType = Utils::ntoh(*((const uint16_t *)(_tapReadBuf + 12))); - Buffer<4096> tmp(_tapReadBuf + 14,bytesRead - 14); - _handler(_arg,from,to,etherType,tmp); + unsigned int etherType = ((((unsigned int)_tapReadBuf[12]) & 0xff) << 8) | (((unsigned int)_tapReadBuf[13]) & 0xff); + try { + Buffer<4096> tmp(_tapReadBuf + 14,bytesRead - 14); + _handler(_arg,from,to,etherType,tmp); + } catch ( ... ) {} // handlers should not throw } } ReadFile(_tap,_tapReadBuf,sizeof(_tapReadBuf),NULL,&_tapOvlRead); @@ -1660,17 +732,8 @@ void EthernetTap::threadMain() CancelIo(_tap); } -bool EthernetTap::deletePersistentTapDevice(const RuntimeEnvironment *_r,const char *pid) +bool WindowsEthernetTap::deletePersistentTapDevice(const RuntimeEnvironment *_r,const char *pid) { -#ifdef _WIN64 - BOOL is64Bit = TRUE; - const char *devcon = "\\devcon_x64.exe"; -#else - BOOL is64Bit = FALSE; - IsWow64Process(GetCurrentProcess(),&is64Bit); - const char *devcon = ((is64Bit == TRUE) ? "\\devcon_x64.exe" : "\\devcon_x86.exe"); -#endif - Mutex::Lock _l(_systemTapInitLock); // only one thread may mess with taps at a time, process-wide HANDLE devconLog = CreateFileA((_r->homePath + "\\devcon.log").c_str(),GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); @@ -1684,7 +747,7 @@ bool EthernetTap::deletePersistentTapDevice(const RuntimeEnvironment *_r,const c PROCESS_INFORMATION processInfo; memset(&startupInfo,0,sizeof(STARTUPINFOA)); memset(&processInfo,0,sizeof(PROCESS_INFORMATION)); - if (CreateProcessA(NULL,(LPSTR)(std::string("\"") + _r->homePath + devcon + "\" remove @" + pid).c_str(),NULL,NULL,FALSE,0,NULL,NULL,&startupInfo,&processInfo)) { + if (CreateProcessA(NULL,(LPSTR)(std::string("\"") + _r->homePath + _winEnv.devcon + "\" remove @" + pid).c_str(),NULL,NULL,FALSE,0,NULL,NULL,&startupInfo,&processInfo)) { WaitForSingleObject(processInfo.hProcess,INFINITE); CloseHandle(processInfo.hProcess); CloseHandle(processInfo.hThread); @@ -1698,7 +761,7 @@ bool EthernetTap::deletePersistentTapDevice(const RuntimeEnvironment *_r,const c return false; } -int EthernetTap::cleanPersistentTapDevices(const RuntimeEnvironment *_r,const std::set<std::string> &exceptThese,bool alsoRemoveUnassociatedDevices) +int WindowsEthernetTap::cleanPersistentTapDevices(const RuntimeEnvironment *_r,const std::set<std::string> &exceptThese,bool alsoRemoveUnassociatedDevices) { char subkeyName[4096]; char subkeyClass[4096]; @@ -1759,33 +822,4 @@ int EthernetTap::cleanPersistentTapDevices(const RuntimeEnvironment *_r,const st return removed; } -void EthernetTap::_syncIpsWithRegistry(const std::set<InetAddress> &haveIps) -{ - // Update registry to contain all non-link-local IPs for this interface - std::string regMultiIps,regMultiNetmasks; - for(std::set<InetAddress>::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); -} - } // namespace ZeroTier - -#endif // __WINDOWS__ //////////////////////////////////////////////////////// |
