|
|
|
|
@ -208,7 +208,7 @@ WindowsEthernetTap::WindowsEthernetTap(
|
|
|
|
|
_injectSemaphore(INVALID_HANDLE_VALUE), |
|
|
|
|
_run(true), |
|
|
|
|
_initialized(false), |
|
|
|
|
_enabled(false) |
|
|
|
|
_enabled(true) |
|
|
|
|
{ |
|
|
|
|
char subkeyName[4096]; |
|
|
|
|
char subkeyClass[4096]; |
|
|
|
|
@ -380,37 +380,6 @@ WindowsEthernetTap::WindowsEthernetTap(
|
|
|
|
|
throw std::runtime_error("unable to convert instance ID GUID to native GUID (invalid NetCfgInstanceId in registry?)"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Disable and enable interface to ensure registry settings take effect
|
|
|
|
|
_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",_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.
|
|
|
|
|
_tap = CreateFileA(tapPath,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_SYSTEM|FILE_FLAG_OVERLAPPED,NULL); |
|
|
|
|
if (_tap == INVALID_HANDLE_VALUE) { |
|
|
|
|
if (++openTrials >= 3) |
|
|
|
|
throw std::runtime_error(std::string("unable to open tap device ")+tapPath); |
|
|
|
|
else Sleep(500); |
|
|
|
|
} else break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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)); |
|
|
|
|
_tapOvlRead.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL); |
|
|
|
|
memset(&_tapOvlWrite,0,sizeof(_tapOvlWrite)); |
|
|
|
|
_tapOvlWrite.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL); |
|
|
|
|
|
|
|
|
|
// Start background thread that actually performs I/O
|
|
|
|
|
_injectSemaphore = CreateSemaphore(NULL,0,1,NULL); |
|
|
|
|
_thread = Thread::start(this); |
|
|
|
|
@ -422,29 +391,15 @@ WindowsEthernetTap::WindowsEthernetTap(
|
|
|
|
|
WindowsEthernetTap::~WindowsEthernetTap() |
|
|
|
|
{ |
|
|
|
|
_run = false; |
|
|
|
|
|
|
|
|
|
ReleaseSemaphore(_injectSemaphore,1,NULL); |
|
|
|
|
Thread::join(_thread); |
|
|
|
|
|
|
|
|
|
CloseHandle(_tap); |
|
|
|
|
CloseHandle(_tapOvlRead.hEvent); |
|
|
|
|
CloseHandle(_tapOvlWrite.hEvent); |
|
|
|
|
CloseHandle(_injectSemaphore); |
|
|
|
|
|
|
|
|
|
_disableTapDevice(_r,_deviceInstanceId); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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; |
|
|
|
|
} |
|
|
|
|
_enabled = en; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool WindowsEthernetTap::enabled() const |
|
|
|
|
@ -624,6 +579,9 @@ bool WindowsEthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups)
|
|
|
|
|
{ |
|
|
|
|
if (!_initialized) |
|
|
|
|
return false; |
|
|
|
|
HANDLE t = _tap; |
|
|
|
|
if (t == INVALID_HANDLE_VALUE) |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
std::set<MulticastGroup> newGroups; |
|
|
|
|
|
|
|
|
|
@ -640,7 +598,7 @@ bool WindowsEthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups)
|
|
|
|
|
// pretty much anything work... IPv4, IPv6, IPX, oldskool Netbios, who knows...
|
|
|
|
|
unsigned char mcastbuf[TAP_WIN_IOCTL_GET_MULTICAST_MEMBERSHIPS_OUTPUT_BUF_SIZE]; |
|
|
|
|
DWORD bytesReturned = 0; |
|
|
|
|
if (DeviceIoControl(_tap,TAP_WIN_IOCTL_GET_MULTICAST_MEMBERSHIPS,(LPVOID)0,0,(LPVOID)mcastbuf,sizeof(mcastbuf),&bytesReturned,NULL)) { |
|
|
|
|
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) { |
|
|
|
|
@ -680,41 +638,95 @@ bool WindowsEthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups)
|
|
|
|
|
void WindowsEthernetTap::threadMain() |
|
|
|
|
throw() |
|
|
|
|
{ |
|
|
|
|
char tapPath[256]; |
|
|
|
|
OVERLAPPED tapOvlRead,tapOvlWrite; |
|
|
|
|
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.
|
|
|
|
|
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.
|
|
|
|
|
bool throwOneAway = true; |
|
|
|
|
while (_run) { |
|
|
|
|
_disableTapDevice(_r,_deviceInstanceId); |
|
|
|
|
Sleep(250); |
|
|
|
|
if (!_enableTapDevice(_r,_deviceInstanceId)) { |
|
|
|
|
::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); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
uint32_t tmpi = 1; |
|
|
|
|
DWORD bytesReturned = 0; |
|
|
|
|
DeviceIoControl(_tap,TAP_WIN_IOCTL_SET_MEDIA_STATUS,&tmpi,sizeof(tmpi),&tmpi,sizeof(tmpi),&bytesReturned,NULL); |
|
|
|
|
|
|
|
|
|
if (throwOneAway) { |
|
|
|
|
throwOneAway = false; |
|
|
|
|
CloseHandle(_tap); |
|
|
|
|
_tap = INVALID_HANDLE_VALUE; |
|
|
|
|
Sleep(250); |
|
|
|
|
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); |
|
|
|
|
|
|
|
|
|
wait4[0] = _injectSemaphore; |
|
|
|
|
wait4[1] = _tapOvlRead.hEvent; |
|
|
|
|
wait4[2] = _tapOvlWrite.hEvent; // only included if writeInProgress is true
|
|
|
|
|
wait4[1] = tapOvlRead.hEvent; |
|
|
|
|
wait4[2] = tapOvlWrite.hEvent; // only included if writeInProgress is true
|
|
|
|
|
|
|
|
|
|
ReadFile(_tap,_tapReadBuf,sizeof(_tapReadBuf),NULL,&_tapOvlRead); |
|
|
|
|
// Start overlapped read, which is always active
|
|
|
|
|
ReadFile(_tap,tapReadBuf,sizeof(tapReadBuf),NULL,&tapOvlRead); |
|
|
|
|
bool writeInProgress = false; |
|
|
|
|
|
|
|
|
|
for(;;) { |
|
|
|
|
// Windows can DIAF
|
|
|
|
|
if (_enabled) |
|
|
|
|
setEnabled(true); |
|
|
|
|
|
|
|
|
|
if (!_run) break; |
|
|
|
|
DWORD r = WaitForMultipleObjectsEx(writeInProgress ? 3 : 2,wait4,FALSE,INFINITE,TRUE); |
|
|
|
|
DWORD r = WaitForMultipleObjectsEx(writeInProgress ? 3 : 2,wait4,FALSE,5000,TRUE); |
|
|
|
|
if (!_run) break; |
|
|
|
|
|
|
|
|
|
if (HasOverlappedIoCompleted(&_tapOvlRead)) { |
|
|
|
|
if ((r == WAIT_TIMEOUT)||(r == WAIT_FAILED)) |
|
|
|
|
continue; |
|
|
|
|
|
|
|
|
|
if (HasOverlappedIoCompleted(&tapOvlRead)) { |
|
|
|
|
DWORD bytesRead = 0; |
|
|
|
|
if (GetOverlappedResult(_tap,&_tapOvlRead,&bytesRead,FALSE)) { |
|
|
|
|
if (GetOverlappedResult(_tap,&tapOvlRead,&bytesRead,FALSE)) { |
|
|
|
|
if ((bytesRead > 14)&&(_enabled)) { |
|
|
|
|
MAC to(_tapReadBuf); |
|
|
|
|
MAC from(_tapReadBuf + 6); |
|
|
|
|
unsigned int etherType = ((((unsigned int)_tapReadBuf[12]) & 0xff) << 8) | (((unsigned int)_tapReadBuf[13]) & 0xff); |
|
|
|
|
MAC to(tapReadBuf); |
|
|
|
|
MAC from(tapReadBuf + 6); |
|
|
|
|
unsigned int etherType = ((((unsigned int)tapReadBuf[12]) & 0xff) << 8) | (((unsigned int)tapReadBuf[13]) & 0xff); |
|
|
|
|
try { |
|
|
|
|
Buffer<4096> tmp(_tapReadBuf + 14,bytesRead - 14); |
|
|
|
|
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); |
|
|
|
|
ReadFile(_tap,tapReadBuf,ZT_IF_MTU + 32,NULL,&tapOvlRead); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (writeInProgress) { |
|
|
|
|
if (HasOverlappedIoCompleted(&_tapOvlWrite)) { |
|
|
|
|
if (HasOverlappedIoCompleted(&tapOvlWrite)) { |
|
|
|
|
writeInProgress = false; |
|
|
|
|
_injectPending_m.lock(); |
|
|
|
|
_injectPending.pop(); |
|
|
|
|
@ -722,7 +734,7 @@ void WindowsEthernetTap::threadMain()
|
|
|
|
|
} else _injectPending_m.lock(); |
|
|
|
|
|
|
|
|
|
if (!_injectPending.empty()) { |
|
|
|
|
WriteFile(_tap,_injectPending.front().first.data,_injectPending.front().second,NULL,&_tapOvlWrite); |
|
|
|
|
WriteFile(_tap,_injectPending.front().first.data,_injectPending.front().second,NULL,&tapOvlWrite); |
|
|
|
|
writeInProgress = true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -730,6 +742,13 @@ void WindowsEthernetTap::threadMain()
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
CancelIo(_tap); |
|
|
|
|
|
|
|
|
|
CloseHandle(tapOvlRead.hEvent); |
|
|
|
|
CloseHandle(tapOvlWrite.hEvent); |
|
|
|
|
CloseHandle(_tap); |
|
|
|
|
_tap = INVALID_HANDLE_VALUE; |
|
|
|
|
|
|
|
|
|
::free(tapReadBuf); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool WindowsEthernetTap::deletePersistentTapDevice(const RuntimeEnvironment *_r,const char *pid) |
|
|
|
|
|