mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-01-18 10:46:33 +00:00
Fix a bunch of Windows tap issues. Turns out NDIS6 allowed us to ditch some NDIS5 cruft, but I did have to add one hack specific to that one instead. Seems reliable now.
This commit is contained in:
parent
1b895c45eb
commit
e744580b89
@ -53,7 +53,7 @@
|
||||
#include "WindowsEthernetTap.hpp"
|
||||
#include "OSUtils.hpp"
|
||||
|
||||
#include "..\windows\TapDriver\tap-windows.h"
|
||||
#include "..\windows\TapDriver6\tap-windows.h"
|
||||
|
||||
// ff:ff:ff:ff:ff:ff with no ADI
|
||||
//static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC(0xff),0);
|
||||
@ -89,11 +89,16 @@ public:
|
||||
};
|
||||
static const WindowsEthernetTapEnv WINENV;
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
// Only create or delete devices one at a time
|
||||
static Mutex _systemTapInitLock;
|
||||
|
||||
// Set to true if existing taps should close and reopen due to new device creation
|
||||
// This is a hack to get around what seems to be a bug that causes existing
|
||||
// devices to go into a coma after new device creation.
|
||||
static volatile bool _needReset = false;
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
WindowsEthernetTap::WindowsEthernetTap(
|
||||
const char *hp,
|
||||
const MAC &mac,
|
||||
@ -124,6 +129,9 @@ WindowsEthernetTap::WindowsEthernetTap(
|
||||
|
||||
Mutex::Lock _l(_systemTapInitLock);
|
||||
|
||||
// Use NDIS5 if it's installed, since we don't want to switch out the driver on
|
||||
// pre-existing installs (yet). We won't ship NDIS5 anymore so new installs will
|
||||
// use NDIS6.
|
||||
std::string tapDriverPath(_pathToHelpers + WINENV.tapDriverNdis5);
|
||||
const char *tapDriverName = "zttap200";
|
||||
if (::PathFileExistsA(tapDriverPath.c_str()) == FALSE) {
|
||||
@ -198,7 +206,7 @@ WindowsEthernetTap::WindowsEthernetTap(
|
||||
if (devconLog != INVALID_HANDLE_VALUE)
|
||||
SetFilePointer(devconLog,0,0,FILE_END);
|
||||
|
||||
// Execute devcon to install an instance of the Microsoft Loopback Adapter
|
||||
// Execute devcon to create a new tap device
|
||||
STARTUPINFOA startupInfo;
|
||||
startupInfo.cb = sizeof(startupInfo);
|
||||
if (devconLog != INVALID_HANDLE_VALUE) {
|
||||
@ -262,6 +270,9 @@ WindowsEthernetTap::WindowsEthernetTap(
|
||||
}
|
||||
} else break; // no more keys or error occurred
|
||||
}
|
||||
|
||||
// Cause all existing taps to reset
|
||||
_needReset = true;
|
||||
}
|
||||
|
||||
if (_netCfgInstanceId.length() > 0) {
|
||||
@ -306,15 +317,15 @@ WindowsEthernetTap::WindowsEthernetTap(
|
||||
if (ConvertInterfaceGuidToLuid(&_deviceGuid,&_deviceLuid) != NO_ERROR)
|
||||
throw std::runtime_error("unable to convert device interface GUID to LUID");
|
||||
|
||||
// Certain functions can now work (e.g. ips())
|
||||
_initialized = true;
|
||||
|
||||
if (friendlyName)
|
||||
setFriendlyName(friendlyName);
|
||||
|
||||
// Start background thread that actually performs I/O
|
||||
_injectSemaphore = CreateSemaphore(NULL,0,1,NULL);
|
||||
_thread = Thread::start(this);
|
||||
|
||||
// Certain functions can now work (e.g. ips())
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
WindowsEthernetTap::~WindowsEthernetTap()
|
||||
@ -566,38 +577,25 @@ void WindowsEthernetTap::threadMain()
|
||||
HANDLE wait4[3];
|
||||
char *tapReadBuf = (char *)0;
|
||||
|
||||
// Shouldn't be needed, but Windows does not overcommit. This Windows
|
||||
// tap code is defensive to schizoid paranoia degrees.
|
||||
if (!_enableTapDevice()) {
|
||||
_enabled = false;
|
||||
return; // only happens if devcon is missing or totally fails
|
||||
}
|
||||
|
||||
/* No idea why I did this. I did it a long time ago and there was only a
|
||||
* a snarky comment. But I'd never do crap like this without a reason, so
|
||||
* I am leaving it alone with a more descriptive snarky comment. */
|
||||
while (!tapReadBuf) {
|
||||
tapReadBuf = (char *)::malloc(ZT_IF_MTU + 32);
|
||||
if (!tapReadBuf)
|
||||
Sleep(1000);
|
||||
}
|
||||
|
||||
// Tap is in this weird Windows global pseudo file space
|
||||
Utils::snprintf(tapPath,sizeof(tapPath),"\\\\.\\Global\\%s.tap",_netCfgInstanceId.c_str());
|
||||
|
||||
/* More insanity: repetatively try to enable/disable tap device. The first
|
||||
* time we succeed, close it and do it again. This is to fix a driver init
|
||||
* bug that seems to be extremely non-deterministic and to only occur after
|
||||
* headless MSI upgrade. It cannot be reproduced in any other circumstance.
|
||||
*
|
||||
* Eventually when ZeroTier has actual money we will have someone create an
|
||||
* NDIS6 tap driver. Yes, we'll likely be cool and open source it. */
|
||||
bool throwOneAway = true;
|
||||
while (_run) {
|
||||
_disableTapDevice();
|
||||
Sleep(250);
|
||||
if (!_enableTapDevice()) {
|
||||
::free(tapReadBuf);
|
||||
_enabled = false;
|
||||
return; // only happens if devcon is missing or totally fails
|
||||
}
|
||||
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(500);
|
||||
fprintf(stderr,"Error opening %s -- retrying.\r\n",tapPath);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -605,8 +603,6 @@ void WindowsEthernetTap::threadMain()
|
||||
uint32_t tmpi = 1;
|
||||
DWORD bytesReturned = 0;
|
||||
DeviceIoControl(_tap,TAP_WIN_IOCTL_SET_MEDIA_STATUS,&tmpi,sizeof(tmpi),&tmpi,sizeof(tmpi),&bytesReturned,NULL);
|
||||
bytesReturned = 0;
|
||||
DeviceIoControl(_tap,TAP_WIN_IOCTL_SET_MEDIA_STATUS,&tmpi,sizeof(tmpi),&tmpi,sizeof(tmpi),&bytesReturned,NULL);
|
||||
}
|
||||
|
||||
{
|
||||
@ -688,75 +684,66 @@ void WindowsEthernetTap::threadMain()
|
||||
#endif
|
||||
}
|
||||
|
||||
if (throwOneAway) {
|
||||
throwOneAway = false;
|
||||
CloseHandle(_tap);
|
||||
_tap = INVALID_HANDLE_VALUE;
|
||||
Sleep(1000);
|
||||
continue;
|
||||
} else break;
|
||||
}
|
||||
memset(&tapOvlRead,0,sizeof(tapOvlRead));
|
||||
tapOvlRead.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
|
||||
memset(&tapOvlWrite,0,sizeof(tapOvlWrite));
|
||||
tapOvlWrite.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
|
||||
|
||||
memset(&tapOvlRead,0,sizeof(tapOvlRead));
|
||||
tapOvlRead.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
|
||||
memset(&tapOvlWrite,0,sizeof(tapOvlWrite));
|
||||
tapOvlWrite.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
|
||||
wait4[0] = _injectSemaphore;
|
||||
wait4[1] = tapOvlRead.hEvent;
|
||||
wait4[2] = tapOvlWrite.hEvent; // only included if writeInProgress is true
|
||||
|
||||
wait4[0] = _injectSemaphore;
|
||||
wait4[1] = tapOvlRead.hEvent;
|
||||
wait4[2] = tapOvlWrite.hEvent; // only included if writeInProgress is true
|
||||
ReadFile(_tap,tapReadBuf,sizeof(tapReadBuf),NULL,&tapOvlRead);
|
||||
bool writeInProgress = false;
|
||||
while ((_run)&&(!_needReset)) {
|
||||
DWORD r = WaitForMultipleObjectsEx(writeInProgress ? 3 : 2,wait4,FALSE,2500,TRUE);
|
||||
if (!_run) break; // will also break outer while(_run)
|
||||
|
||||
// Start overlapped read, which is always active
|
||||
ReadFile(_tap,tapReadBuf,sizeof(tapReadBuf),NULL,&tapOvlRead);
|
||||
bool writeInProgress = false;
|
||||
if ((r == WAIT_TIMEOUT)||(r == WAIT_FAILED))
|
||||
continue;
|
||||
|
||||
for(;;) {
|
||||
if (!_run) break;
|
||||
DWORD r = WaitForMultipleObjectsEx(writeInProgress ? 3 : 2,wait4,FALSE,5000,TRUE);
|
||||
if (!_run) break;
|
||||
|
||||
if ((r == WAIT_TIMEOUT)||(r == WAIT_FAILED))
|
||||
continue;
|
||||
|
||||
if (HasOverlappedIoCompleted(&tapOvlRead)) {
|
||||
DWORD bytesRead = 0;
|
||||
if (GetOverlappedResult(_tap,&tapOvlRead,&bytesRead,FALSE)) {
|
||||
if ((bytesRead > 14)&&(_enabled)) {
|
||||
MAC to(tapReadBuf,6);
|
||||
MAC from(tapReadBuf + 6,6);
|
||||
unsigned int etherType = ((((unsigned int)tapReadBuf[12]) & 0xff) << 8) | (((unsigned int)tapReadBuf[13]) & 0xff);
|
||||
try {
|
||||
// TODO: decode vlans
|
||||
_handler(_arg,_nwid,from,to,etherType,0,tapReadBuf + 14,bytesRead - 14);
|
||||
} catch ( ... ) {} // handlers should not throw
|
||||
if (HasOverlappedIoCompleted(&tapOvlRead)) {
|
||||
DWORD bytesRead = 0;
|
||||
if (GetOverlappedResult(_tap,&tapOvlRead,&bytesRead,FALSE)) {
|
||||
if ((bytesRead > 14)&&(_enabled)) {
|
||||
MAC to(tapReadBuf,6);
|
||||
MAC from(tapReadBuf + 6,6);
|
||||
unsigned int etherType = ((((unsigned int)tapReadBuf[12]) & 0xff) << 8) | (((unsigned int)tapReadBuf[13]) & 0xff);
|
||||
try {
|
||||
// TODO: decode vlans
|
||||
_handler(_arg,_nwid,from,to,etherType,0,tapReadBuf + 14,bytesRead - 14);
|
||||
} catch ( ... ) {} // handlers should not throw
|
||||
}
|
||||
}
|
||||
ReadFile(_tap,tapReadBuf,ZT_IF_MTU + 32,NULL,&tapOvlRead);
|
||||
}
|
||||
ReadFile(_tap,tapReadBuf,ZT_IF_MTU + 32,NULL,&tapOvlRead);
|
||||
|
||||
if (writeInProgress) {
|
||||
if (HasOverlappedIoCompleted(&tapOvlWrite)) {
|
||||
writeInProgress = false;
|
||||
_injectPending_m.lock();
|
||||
_injectPending.pop();
|
||||
} else continue; // still writing, so skip code below and wait
|
||||
} else _injectPending_m.lock();
|
||||
|
||||
if (!_injectPending.empty()) {
|
||||
WriteFile(_tap,_injectPending.front().first.data,_injectPending.front().second,NULL,&tapOvlWrite);
|
||||
writeInProgress = true;
|
||||
}
|
||||
|
||||
_injectPending_m.unlock();
|
||||
}
|
||||
|
||||
if (writeInProgress) {
|
||||
if (HasOverlappedIoCompleted(&tapOvlWrite)) {
|
||||
writeInProgress = false;
|
||||
_injectPending_m.lock();
|
||||
_injectPending.pop();
|
||||
} else continue; // still writing, so skip code below and wait
|
||||
} else _injectPending_m.lock();
|
||||
CancelIo(_tap);
|
||||
|
||||
if (!_injectPending.empty()) {
|
||||
WriteFile(_tap,_injectPending.front().first.data,_injectPending.front().second,NULL,&tapOvlWrite);
|
||||
writeInProgress = true;
|
||||
}
|
||||
CloseHandle(tapOvlRead.hEvent);
|
||||
CloseHandle(tapOvlWrite.hEvent);
|
||||
CloseHandle(_tap);
|
||||
_tap = INVALID_HANDLE_VALUE;
|
||||
|
||||
_injectPending_m.unlock();
|
||||
// We will restart and re-open the tap unless _run == false
|
||||
}
|
||||
|
||||
CancelIo(_tap);
|
||||
|
||||
CloseHandle(tapOvlRead.hEvent);
|
||||
CloseHandle(tapOvlWrite.hEvent);
|
||||
CloseHandle(_tap);
|
||||
_tap = INVALID_HANDLE_VALUE;
|
||||
|
||||
::free(tapReadBuf);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user