summaryrefslogtreecommitdiff
path: root/node/EthernetTap.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'node/EthernetTap.cpp')
-rw-r--r--node/EthernetTap.cpp210
1 files changed, 197 insertions, 13 deletions
diff --git a/node/EthernetTap.cpp b/node/EthernetTap.cpp
index b9524a02..d9a66b3e 100644
--- a/node/EthernetTap.cpp
+++ b/node/EthernetTap.cpp
@@ -679,6 +679,8 @@ void EthernetTap::threadMain()
#ifdef __WINDOWS__
+#include <queue>
+
#include <WinSock2.h>
#include <Windows.h>
#include <iphlpapi.h>
@@ -689,6 +691,165 @@ void EthernetTap::threadMain()
namespace ZeroTier {
+/* Background thread that handles I/O to/from all running
+ * EthernetTap instances using WinPcap -- this must be
+ * managed from a single thread because of the need to
+ * "reboot" npf every time a new interface is added. */
+class _WinEthernetTapPcapIOThread
+{
+public:
+ _WinEthernetTapPcapIOThread()
+ {
+ scManager = OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);
+ if (scManager == NULL)
+ fprintf(stderr,"ZeroTier One: WARNING: unable to OpenSCManager(), tap will have issues with new devices!\r\n");
+ updateSem = CreateSemaphore(NULL,0,1,NULL); // binary
+ run = true;
+ needReload = false;
+ thread = Thread::start(this);
+ }
+
+ ~_WinEthernetTapPcapIOThread()
+ {
+ run = false;
+ needReload = false;
+ ReleaseSemaphore(updateSem,1,NULL);
+ Thread::join(thread);
+ CloseHandle(updateSem);
+ if (scManager != NULL)
+ CloseServiceHandle(scManager);
+ }
+
+ void threadMain()
+ throw()
+ {
+ HANDLE *objects = new HANDLE[1];
+ objects[0] = updateSem;
+ DWORD numObjects = 1;
+
+ for(;;) {
+ if (!run) {
+ delete [] objects;
+ return;
+ }
+
+ printf("waiting on: %d\r\n",(int)numObjects);
+ WaitForMultipleObjects(numObjects,objects,FALSE,INFINITE);
+
+ {
+ Mutex::Lock _l(taps_m);
+
+ for(std::list<EthernetTap *>::iterator tapptr(taps.begin());tapptr!=taps.end();++tapptr) {
+ EthernetTap *tap = *tapptr;
+ if (tap->_pcap) {
+ {
+ Mutex::Lock _l2(tap->_injectPending_m);
+ while (!tap->_injectPending.empty()) {
+ pcap_sendpacket(tap->_pcap,(const u_char *)(tap->_injectPending.front().first.data),tap->_injectPending.front().second);
+ tap->_injectPending.pop();
+ }
+ }
+ printf("dispatch: %s\r\n",tap->_myDeviceInstanceId.c_str());
+ pcap_dispatch(tap->_pcap,-1,&EthernetTap::_pcapHandler,(u_char *)tap);
+ }
+ }
+
+ if (needReload) {
+ // Close all taps and restart WinPcap driver to scan for possible new
+ // interfaces... yuck. This is done because WinPcap does not support
+ // hot plug, so if we've added a new loopback adapter it won't show
+ // up without this full system refresh.
+ needReload = false;
+
+ for(std::list<EthernetTap *>::iterator tapptr(taps.begin());tapptr!=taps.end();++tapptr) {
+ if ((*tapptr)->_pcap) {
+ pcap_close((*tapptr)->_pcap);
+ (*tapptr)->_pcap = (pcap_t *)0;
+ }
+ }
+
+ if (scManager != NULL) {
+ Thread::sleep(250);
+ SC_HANDLE npfService = OpenServiceA(scManager,"npf",SERVICE_ALL_ACCESS);
+ if (npfService != NULL) {
+ SERVICE_STATUS sstatus;
+ memset(&sstatus,0,sizeof(SERVICE_STATUS));
+ ControlService(npfService,SERVICE_CONTROL_STOP,&sstatus);
+ printf("service restart\r\n");
+ CloseServiceHandle(npfService);
+ }
+ Thread::sleep(250);
+ }
+
+ delete [] objects;
+ objects = new HANDLE[taps.size() + 1];
+ objects[0] = updateSem;
+ numObjects = 1;
+
+ pcap_if_t *alldevs;
+ char pcaperrbuf[PCAP_ERRBUF_SIZE+1];
+ if (pcap_findalldevs(&alldevs,pcaperrbuf) != -1) {
+ for(std::list<EthernetTap *>::iterator tapptr(taps.begin());tapptr!=taps.end();++tapptr) {
+ EthernetTap *tap = *tapptr;
+ pcap_if_t *mydev = (pcap_if_t *)0;
+ for(mydev=alldevs;(mydev);mydev=mydev->next) {
+ if (strstr(mydev->name,tap->_myDeviceInstanceId.c_str()))
+ break;
+ }
+ if (mydev) {
+ tap->_pcap = pcap_open_live(mydev->name,65535,1,0,pcaperrbuf);
+ pcap_setnonblock(tap->_pcap,1,pcaperrbuf);
+ if (tap->_pcap) {
+ printf("pcap opened: %s\r\n",mydev->name);
+ objects[numObjects++] = pcap_getevent(tap->_pcap);
+ }
+ } else {
+ tap->_pcap = (pcap_t *)0;
+ }
+ }
+ pcap_freealldevs(alldevs);
+ }
+ }
+ }
+
+ if (!run) {
+ delete [] objects;
+ return;
+ }
+ }
+ }
+
+ inline void addTap(EthernetTap *t)
+ {
+ Mutex::Lock _l(taps_m);
+ taps.push_back(t);
+ needReload = true;
+ ReleaseSemaphore(updateSem,1,NULL);
+ }
+
+ inline void removeTap(EthernetTap *t)
+ {
+ Mutex::Lock _l(taps_m);
+ for(std::list<EthernetTap *>::iterator tapptr(taps.begin());tapptr!=taps.end();++tapptr) {
+ if (*tapptr == t) {
+ taps.erase(tapptr);
+ break;
+ }
+ }
+ needReload = true;
+ ReleaseSemaphore(updateSem,1,NULL);
+ }
+
+ std::list<EthernetTap *> taps;
+ Mutex taps_m;
+ SC_HANDLE scManager;
+ HANDLE updateSem;
+ volatile bool run;
+ volatile bool needReload;
+ Thread thread;
+};
+
+static _WinEthernetTapPcapIOThread _pcapIoThread;
static Mutex _systemTapInitLock;
EthernetTap::EthernetTap(
@@ -703,7 +864,8 @@ EthernetTap::EthernetTap(
_mtu(mtu),
_r(renv),
_handler(handler),
- _arg(arg)
+ _arg(arg),
+ _pcap((pcap_t *)0)
{
char subkeyName[1024];
char subkeyClass[1024];
@@ -715,7 +877,6 @@ EthernetTap::EthernetTap(
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,"SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}",0,KEY_READ|KEY_WRITE,&nwAdapters) != ERROR_SUCCESS)
throw std::runtime_error("unable to open registry key for network adapter enumeration");
- std::string myDeviceInstanceId;
std::set<std::string> existingDeviceInstances;
// Enumerate all Microsoft Loopback Adapter instances and look for one
@@ -742,7 +903,7 @@ EthernetTap::EthernetTap(
existingDeviceInstances.insert(instanceId);
}
- if ((myDeviceInstanceId.length() == 0)&&(instanceId.length() != 0)) {
+ if ((_myDeviceInstanceId.length() == 0)&&(instanceId.length() != 0)) {
type = 0;
dataLen = sizeof(data);
if (RegGetValueA(nwAdapters,subkeyName,"_ZeroTierTapIdentifier",RRF_RT_ANY,&type,(PVOID)data,&dataLen) == ERROR_SUCCESS) {
@@ -751,7 +912,7 @@ EthernetTap::EthernetTap(
type = 0;
dataLen = sizeof(data);
if (RegGetValueA(nwAdapters,subkeyName,"NetCfgInstanceId",RRF_RT_ANY,&type,(PVOID)data,&dataLen) == ERROR_SUCCESS) {
- myDeviceInstanceId = instanceId;
+ _myDeviceInstanceId = instanceId;
subkeyIndex = -1; // break outer loop
}
}
@@ -764,13 +925,13 @@ EthernetTap::EthernetTap(
}
// If there is no device, try to create one
- if (myDeviceInstanceId.length() == 0) {
+ if (_myDeviceInstanceId.length() == 0) {
// Execute devcon to install an instance of the Microsoft Loopback Adapter
#ifdef _WIN64
- std::string devcon(_r->homePath + "\\devcon64.exe");
+ const char *devcon = "\\devcon64.exe";
#else
BOOL f64 = FALSE;
- std::string devcon(_r->homePath + ((IsWow64Process(GetCurrentProcess(),&f64) == TRUE) ? "\\devcon64.exe" : "\\devcon32.exe"));
+ const char *devcon = ((IsWow64Process(GetCurrentProcess(),&f64) == TRUE) ? "\\devcon64.exe" : "\\devcon32.exe");
#endif
char windir[4096];
windir[0] = '\0';
@@ -780,7 +941,7 @@ EthernetTap::EthernetTap(
PROCESS_INFORMATION processInfo;
memset(&startupInfo,0,sizeof(STARTUPINFOA));
memset(&processInfo,0,sizeof(PROCESS_INFORMATION));
- if (!CreateProcessA(NULL,(LPSTR)(devcon + " install " + windir + "\\inf\\netloop.inf *msloop").c_str(),NULL,NULL,FALSE,0,NULL,NULL,&startupInfo,&processInfo)) {
+ if (!CreateProcessA(NULL,(LPSTR)(std::string("\"") + _r->homePath + devcon + "\" install " + windir + "\\inf\\netloop.inf *msloop").c_str(),NULL,NULL,FALSE,0,NULL,NULL,&startupInfo,&processInfo)) {
RegCloseKey(nwAdapters);
throw std::runtime_error(std::string("unable to find or execute devcon at ")+devcon);
}
@@ -809,7 +970,7 @@ 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,strlen(tag)+1);
- myDeviceInstanceId.assign(data,dataLen);
+ _myDeviceInstanceId.assign(data,dataLen);
subkeyIndex = -1; // break outer loop
}
}
@@ -822,12 +983,18 @@ EthernetTap::EthernetTap(
RegCloseKey(nwAdapters);
- if (myDeviceInstanceId.length() == 0)
+ if (_myDeviceInstanceId.length() == 0)
throw std::runtime_error("unable to create new loopback adapter for tap");
+
+ // pcap is opened and managed by the pcap I/O thread
+ _pcapIoThread.addTap(this);
}
EthernetTap::~EthernetTap()
{
+ _pcapIoThread.removeTap(this);
+ if (_pcap)
+ pcap_close(_pcap);
}
void EthernetTap::whack()
@@ -846,21 +1013,38 @@ bool EthernetTap::removeIP(const InetAddress &ip)
void EthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len)
{
+ if (len > (ZT_IF_MTU))
+ return;
+
+ {
+ Mutex::Lock _l(_injectPending_m);
+ _injectPending.push( std::pair<Array<char,ZT_IF_MTU + 32>,unsigned int>(Array<char,ZT_IF_MTU + 32>(),len + 14) );
+ char *d = _injectPending.back().first.data;
+ memcpy(d,to.data,6);
+ memcpy(d + 6,from.data,6);
+ *((uint16_t *)(d + 12)) = Utils::hton(etherType);
+ memcpy(d + 14,data,len);
+ }
+
+ ReleaseSemaphore(_pcapIoThread.updateSem,1,NULL);
}
std::string EthernetTap::deviceName() const
{
- return std::string();
+ return _myDeviceInstanceId;
}
bool EthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups)
{
+ // TODO
return false;
}
-void EthernetTap::threadMain()
- throw()
+void EthernetTap::_pcapHandler(u_char *user,const struct pcap_pkthdr *pkt_header,const u_char *pkt_data)
{
+ printf("got packet: %d\r\n",(int)pkt_header->len);
+ if (pkt_header->len > 14)
+ ((EthernetTap *)user)->_handler(((EthernetTap *)user)->_arg,MAC(pkt_data + 6),MAC(pkt_data),Utils::ntoh(*((const uint16_t *)(pkt_data + 12))),Buffer<4096>(pkt_data + 14,pkt_header->len - 14));
}
} // namespace ZeroTier