Browse Source

Tap driver basically builds in VS2012... fork of tap-windows from OpenVPN (compatible license).

Adam Ierymenko 12 years ago
parent
commit
e2effbd1ce

+ 2 - 0
.gitignore

@@ -16,3 +16,5 @@ mac-tap/tuntap/tap.kext
 *.log
 *.opensdf
 *.user
+/Win8Release
+/vsprojects/TapDriver/Win8Release

+ 103 - 0
ZeroTierOne.sln

@@ -3,6 +3,13 @@ Microsoft Visual Studio Solution File, Format Version 12.00
 # Visual Studio 2012
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SelfTest", "vsprojects\SelfTest\SelfTest.vcxproj", "{DCD73B97-0F44-4044-8BA4-95B59CCAB4BD}"
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TapDriver", "vsprojects\TapDriver\TapDriver.vcxproj", "{689210B1-467C-4850-BB7D-2E10D5B4A3DA}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TapDriver Package", "vsprojects\TapDriver Package\TapDriver Package.vcxproj", "{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}"
+	ProjectSection(ProjectDependencies) = postProject
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA} = {689210B1-467C-4850-BB7D-2E10D5B4A3DA}
+	EndProjectSection
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Win32 = Debug|Win32
@@ -53,6 +60,102 @@ Global
 		{DCD73B97-0F44-4044-8BA4-95B59CCAB4BD}.Win8 Release|Win32.Build.0 = Release|Win32
 		{DCD73B97-0F44-4044-8BA4-95B59CCAB4BD}.Win8 Release|Win32.Deploy.0 = Release|Win32
 		{DCD73B97-0F44-4044-8BA4-95B59CCAB4BD}.Win8 Release|x64.ActiveCfg = Release|Win32
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Debug|Win32.ActiveCfg = Win7 Debug|Win32
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Debug|Win32.Build.0 = Win7 Debug|Win32
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Debug|Win32.Deploy.0 = Win7 Debug|Win32
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Debug|x64.ActiveCfg = Win7 Debug|x64
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Debug|x64.Build.0 = Win7 Debug|x64
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Debug|x64.Deploy.0 = Win7 Debug|x64
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Release|Win32.ActiveCfg = Win8 Release|Win32
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Release|Win32.Build.0 = Win8 Release|Win32
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Release|Win32.Deploy.0 = Win8 Release|Win32
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Release|x64.ActiveCfg = Win8 Release|x64
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Release|x64.Build.0 = Win8 Release|x64
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Release|x64.Deploy.0 = Win8 Release|x64
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Vista Debug|Win32.ActiveCfg = Vista Debug|Win32
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Vista Debug|Win32.Build.0 = Vista Debug|Win32
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Vista Debug|Win32.Deploy.0 = Vista Debug|Win32
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Vista Debug|x64.ActiveCfg = Vista Debug|x64
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Vista Debug|x64.Build.0 = Vista Debug|x64
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Vista Debug|x64.Deploy.0 = Vista Debug|x64
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Vista Release|Win32.ActiveCfg = Vista Release|Win32
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Vista Release|Win32.Build.0 = Vista Release|Win32
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Vista Release|Win32.Deploy.0 = Vista Release|Win32
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Vista Release|x64.ActiveCfg = Vista Release|x64
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Vista Release|x64.Build.0 = Vista Release|x64
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Vista Release|x64.Deploy.0 = Vista Release|x64
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Win7 Debug|Win32.ActiveCfg = Win7 Debug|Win32
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Win7 Debug|Win32.Build.0 = Win7 Debug|Win32
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Win7 Debug|Win32.Deploy.0 = Win7 Debug|Win32
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Win7 Debug|x64.ActiveCfg = Win7 Debug|x64
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Win7 Debug|x64.Build.0 = Win7 Debug|x64
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Win7 Debug|x64.Deploy.0 = Win7 Debug|x64
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Win7 Release|Win32.ActiveCfg = Win7 Release|Win32
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Win7 Release|Win32.Build.0 = Win7 Release|Win32
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Win7 Release|Win32.Deploy.0 = Win7 Release|Win32
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Win7 Release|x64.ActiveCfg = Win7 Release|x64
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Win7 Release|x64.Build.0 = Win7 Release|x64
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Win7 Release|x64.Deploy.0 = Win7 Release|x64
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Win8 Debug|Win32.ActiveCfg = Win8 Debug|Win32
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Win8 Debug|Win32.Build.0 = Win8 Debug|Win32
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Win8 Debug|Win32.Deploy.0 = Win8 Debug|Win32
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Win8 Debug|x64.ActiveCfg = Win8 Debug|x64
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Win8 Debug|x64.Build.0 = Win8 Debug|x64
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Win8 Debug|x64.Deploy.0 = Win8 Debug|x64
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Win8 Release|Win32.ActiveCfg = Win8 Release|Win32
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Win8 Release|Win32.Build.0 = Win8 Release|Win32
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Win8 Release|Win32.Deploy.0 = Win8 Release|Win32
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Win8 Release|x64.ActiveCfg = Win8 Release|x64
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Win8 Release|x64.Build.0 = Win8 Release|x64
+		{689210B1-467C-4850-BB7D-2E10D5B4A3DA}.Win8 Release|x64.Deploy.0 = Win8 Release|x64
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Debug|Win32.ActiveCfg = Win7 Debug|Win32
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Debug|Win32.Build.0 = Win7 Debug|Win32
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Debug|Win32.Deploy.0 = Win7 Debug|Win32
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Debug|x64.ActiveCfg = Win7 Debug|x64
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Debug|x64.Build.0 = Win7 Debug|x64
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Debug|x64.Deploy.0 = Win7 Debug|x64
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Release|Win32.ActiveCfg = Win8 Release|Win32
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Release|Win32.Build.0 = Win8 Release|Win32
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Release|Win32.Deploy.0 = Win8 Release|Win32
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Release|x64.ActiveCfg = Win8 Release|x64
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Release|x64.Build.0 = Win8 Release|x64
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Release|x64.Deploy.0 = Win8 Release|x64
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Vista Debug|Win32.ActiveCfg = Vista Debug|Win32
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Vista Debug|Win32.Build.0 = Vista Debug|Win32
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Vista Debug|Win32.Deploy.0 = Vista Debug|Win32
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Vista Debug|x64.ActiveCfg = Vista Debug|x64
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Vista Debug|x64.Build.0 = Vista Debug|x64
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Vista Debug|x64.Deploy.0 = Vista Debug|x64
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Vista Release|Win32.ActiveCfg = Vista Release|Win32
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Vista Release|Win32.Build.0 = Vista Release|Win32
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Vista Release|Win32.Deploy.0 = Vista Release|Win32
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Vista Release|x64.ActiveCfg = Vista Release|x64
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Vista Release|x64.Build.0 = Vista Release|x64
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Vista Release|x64.Deploy.0 = Vista Release|x64
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Win7 Debug|Win32.ActiveCfg = Win7 Debug|Win32
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Win7 Debug|Win32.Build.0 = Win7 Debug|Win32
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Win7 Debug|Win32.Deploy.0 = Win7 Debug|Win32
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Win7 Debug|x64.ActiveCfg = Win7 Debug|x64
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Win7 Debug|x64.Build.0 = Win7 Debug|x64
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Win7 Debug|x64.Deploy.0 = Win7 Debug|x64
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Win7 Release|Win32.ActiveCfg = Win7 Release|Win32
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Win7 Release|Win32.Build.0 = Win7 Release|Win32
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Win7 Release|Win32.Deploy.0 = Win7 Release|Win32
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Win7 Release|x64.ActiveCfg = Win7 Release|x64
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Win7 Release|x64.Build.0 = Win7 Release|x64
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Win7 Release|x64.Deploy.0 = Win7 Release|x64
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Win8 Debug|Win32.ActiveCfg = Win8 Debug|Win32
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Win8 Debug|Win32.Build.0 = Win8 Debug|Win32
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Win8 Debug|Win32.Deploy.0 = Win8 Debug|Win32
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Win8 Debug|x64.ActiveCfg = Win8 Debug|x64
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Win8 Debug|x64.Build.0 = Win8 Debug|x64
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Win8 Debug|x64.Deploy.0 = Win8 Debug|x64
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Win8 Release|Win32.ActiveCfg = Win8 Release|Win32
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Win8 Release|Win32.Build.0 = Win8 Release|Win32
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Win8 Release|Win32.Deploy.0 = Win8 Release|Win32
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Win8 Release|x64.ActiveCfg = Win8 Release|x64
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Win8 Release|x64.Build.0 = Win8 Release|x64
+		{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}.Win8 Release|x64.Deploy.0 = Win8 Release|x64
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE

+ 30 - 178
node/EthernetTap.cpp

@@ -677,10 +677,14 @@ void EthernetTap::threadMain()
 
 #endif // __UNIX_LIKE__ //////////////////////////////////////////////////////
 
-#ifdef __WINDOWS__
+//////////////////////////////////////////////////////////////////////////////
 
-#include <queue>
+#ifdef __WINDOWS__ ///////////////////////////////////////////////////////////
 
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
 #include <WinSock2.h>
 #include <Windows.h>
 #include <iphlpapi.h>
@@ -691,165 +695,6 @@ 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(
@@ -864,20 +709,20 @@ EthernetTap::EthernetTap(
 	_mtu(mtu),
 	_r(renv),
 	_handler(handler),
-	_arg(arg),
-	_pcap((pcap_t *)0)
+	_arg(arg)
 {
-	char subkeyName[1024];
-	char subkeyClass[1024];
-	char data[1024];
+	char subkeyName[4096];
+	char subkeyClass[4096];
+	char data[4096];
 
-	Mutex::Lock _l(_systemTapInitLock); // only do one at a time, process-wide
+	Mutex::Lock _l(_systemTapInitLock); // only init one tap at a time, process-wide
 
 	HKEY nwAdapters;
 	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::set<std::string> existingDeviceInstances;
+	std::string mySubkeyName;
 
 	// Enumerate all Microsoft Loopback Adapter instances and look for one
 	// that matches our tag.
@@ -913,6 +758,7 @@ EthernetTap::EthernetTap(
 									dataLen = sizeof(data);
 									if (RegGetValueA(nwAdapters,subkeyName,"NetCfgInstanceId",RRF_RT_ANY,&type,(PVOID)data,&dataLen) == ERROR_SUCCESS) {
 										_myDeviceInstanceId = instanceId;
+										mySubkeyName = subkeyName;
 										subkeyIndex = -1; // break outer loop
 									}
 								}
@@ -971,6 +817,7 @@ EthernetTap::EthernetTap(
 								if (existingDeviceInstances.count(std::string(data,dataLen)) == 0) {
 									RegSetKeyValueA(nwAdapters,subkeyName,"_ZeroTierTapIdentifier",REG_SZ,tag,strlen(tag)+1);
 									_myDeviceInstanceId.assign(data,dataLen);
+									mySubkeyName = subkeyName;
 									subkeyIndex = -1; // break outer loop
 								}
 							}
@@ -981,20 +828,27 @@ EthernetTap::EthernetTap(
 		}
 	}
 
+	if (_myDeviceInstanceId.length() > 0) {
+		char tmps[4096];
+		sprintf_s(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]);
+		RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"NetworkAddress",REG_SZ,tmps,strlen(tmps)+1);
+		DWORD tmp = mtu;
+		RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"MTU",REG_DWORD,(LPCVOID)&tmp,sizeof(tmp));
+		tmp = 0;
+		RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"EnableDHCP",REG_DWORD,(LPCVOID)&tmp,sizeof(tmp));
+		RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"DriverDesc",REG_SZ,"ZeroTier One Virtual LAN",25);
+	}
+
 	RegCloseKey(nwAdapters);	
 
 	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);
+	//Thread::start(this);
 }
 
 EthernetTap::~EthernetTap()
 {
-	_pcapIoThread.removeTap(this);
-	if (_pcap)
-		pcap_close(_pcap);
 }
 
 void EthernetTap::whack()
@@ -1026,7 +880,7 @@ void EthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const
 		memcpy(d + 14,data,len);
 	}
 
-	ReleaseSemaphore(_pcapIoThread.updateSem,1,NULL);
+	//ReleaseSemaphore(_pcapIoThread.updateSem,1,NULL);
 }
 
 std::string EthernetTap::deviceName() const
@@ -1040,13 +894,11 @@ bool EthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups)
 	return false;
 }
 
-void EthernetTap::_pcapHandler(u_char *user,const struct pcap_pkthdr *pkt_header,const u_char *pkt_data)
+void EthernetTap::threadMain()
+	throw()
 {
-	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
 
-#endif // __WINDOWS__
+#endif // __WINDOWS__ ////////////////////////////////////////////////////////

+ 2 - 17
node/EthernetTap.hpp

@@ -43,33 +43,21 @@
 #include "InetAddress.hpp"
 #include "MAC.hpp"
 #include "Mutex.hpp"
+#include "Condition.hpp"
 #include "MulticastGroup.hpp"
 #include "Thread.hpp"
 #include "Buffer.hpp"
 #include "Array.hpp"
 
-#ifdef __WINDOWS__
-#include <pcap/pcap.h>
-#include <pcap/bpf.h>
-#include <Win32-Extensions.h>
-#endif
-
 namespace ZeroTier {
 
 class RuntimeEnvironment;
-#ifdef __WINDOWS__
-class _WinEthernetTapPcapIOThread;
-#endif
 
 /**
  * System ethernet tap device
  */
 class EthernetTap
 {
-#ifdef __WINDOWS__
-	friend class _WinEthernetTapPcapIOThread;
-#endif
-
 public:
 	/**
 	 * Construct a new TAP device
@@ -189,13 +177,11 @@ public:
 	 */
 	bool updateMulticastGroups(std::set<MulticastGroup> &groups);
 
-#ifdef __UNIX_LIKE__
 	/**
 	 * Thread main method; do not call elsewhere
 	 */
 	void threadMain()
 		throw();
-#endif
 
 private:
 	const MAC _mac;
@@ -217,11 +203,10 @@ private:
 #endif
 
 #ifdef __WINDOWS__
-	pcap_t *_pcap;
 	std::string _myDeviceInstanceId;
 	std::queue< std::pair< Array<char,ZT_IF_MTU + 32>,unsigned int > > _injectPending;
 	Mutex _injectPending_m;
-	static void _pcapHandler(u_char *user,const struct pcap_pkthdr *pkt_header,const u_char *pkt_data);
+	Condition _injectPending_c;
 #endif
 };
 

+ 5 - 3
selftest.cpp

@@ -408,18 +408,19 @@ int _tmain(int argc, _TCHAR* argv[])
 int main(int argc,char **argv)
 #endif
 {
-	/* For testing windows tap
+	///* For testing windows tap
 	try {
 		RuntimeEnvironment renv;
 		renv.homePath = "C:";
-		EthernetTap tap(&renv,"test12345",MAC(),2800,NULL,NULL);
+		EthernetTap tap(&renv,"test12345",MAC(0x32),2800,NULL,NULL);
 		Thread::sleep(100000000);
 	} catch (std::exception &exc) {
 		std::cout << exc.what() << std::endl;
 		return 0;
 	}
-	*/
+	//*/
 
+	/*
 	int r = 0;
 
 	_initLibCrypto();
@@ -435,4 +436,5 @@ int main(int argc,char **argv)
 		std::cout << std::endl << "SOMETHING FAILED!" << std::endl;
 
 	return r;
+	*/
 }

+ 2 - 2
vsprojects/SelfTest/SelfTest.vcxproj

@@ -69,14 +69,14 @@
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <SDLCheck>true</SDLCheck>
-      <AdditionalIncludeDirectories>$(SolutionDir)\ext\bin\libcrypto\include;$(SolutionDir)\ext\bin\winpcap-devel\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>$(SolutionDir)\ext\bin\libcrypto\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
     <Link>
       <SubSystem>Console</SubSystem>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <EnableCOMDATFolding>true</EnableCOMDATFolding>
       <OptimizeReferences>true</OptimizeReferences>
-      <AdditionalDependencies>wsock32.lib;ws2_32.lib;iphlpapi.lib;$(SolutionDir)\ext\bin\libcrypto\win32-vs2012\libeay32.lib;$(SolutionDir)\ext\bin\winpcap-devel\lib\wpcap.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>wsock32.lib;ws2_32.lib;iphlpapi.lib;$(SolutionDir)\ext\bin\libcrypto\win32-vs2012\libeay32.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
     </Link>
   </ItemDefinitionGroup>

+ 155 - 0
vsprojects/TapDriver Package/TapDriver Package.vcxproj

@@ -0,0 +1,155 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Win8 Debug|Win32">
+      <Configuration>Win8 Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Win8 Release|Win32">
+      <Configuration>Win8 Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Win7 Debug|Win32">
+      <Configuration>Win7 Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Win7 Release|Win32">
+      <Configuration>Win7 Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Vista Debug|Win32">
+      <Configuration>Vista Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Vista Release|Win32">
+      <Configuration>Vista Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Win8 Debug|x64">
+      <Configuration>Win8 Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Win8 Release|x64">
+      <Configuration>Win8 Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Win7 Debug|x64">
+      <Configuration>Win7 Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Win7 Release|x64">
+      <Configuration>Win7 Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Vista Debug|x64">
+      <Configuration>Vista Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Vista Release|x64">
+      <Configuration>Vista Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{FDA1DD8D-1D56-4BC1-B402-FCC0B550D946}</ProjectGuid>
+    <TemplateGuid>{4605da2c-74a5-4865-98e1-152ef136825f}</TemplateGuid>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion>
+    <Configuration>Win8 Debug</Configuration>
+    <Platform Condition="'$(Platform)' == ''">Win32</Platform>
+  </PropertyGroup>
+  <PropertyGroup Label="Globals">
+    <RootNamespace>TapDriver_Package</RootNamespace>
+    <VCTargetsPath Condition="'$(VCTargetsPath11)' != '' and '$(VisualStudioVersion)' == '11.0'">$(VCTargetsPath11)</VCTargetsPath>
+  </PropertyGroup>
+  <PropertyGroup Label="PropertySheets">
+    <PlatformToolset>WindowsKernelModeDriver8.0</PlatformToolset>
+    <ConfigurationType>Utility</ConfigurationType>
+    <DriverType>Package</DriverType>
+    <DisableFastUpToDateCheck>true</DisableFastUpToDateCheck>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Win8 Debug|Win32'" Label="Configuration">
+    <TargetVersion>Windows8</TargetVersion>
+    <UseDebugLibraries>true</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Win8 Release|Win32'" Label="Configuration">
+    <TargetVersion>Windows8</TargetVersion>
+    <UseDebugLibraries>false</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Win7 Debug|Win32'" Label="Configuration">
+    <TargetVersion>Windows7</TargetVersion>
+    <UseDebugLibraries>true</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Win7 Release|Win32'" Label="Configuration">
+    <TargetVersion>Windows7</TargetVersion>
+    <UseDebugLibraries>false</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Vista Debug|Win32'" Label="Configuration">
+    <TargetVersion>Vista</TargetVersion>
+    <UseDebugLibraries>true</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Vista Release|Win32'" Label="Configuration">
+    <TargetVersion>Vista</TargetVersion>
+    <UseDebugLibraries>false</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Win8 Debug|x64'" Label="Configuration">
+    <TargetVersion>Windows8</TargetVersion>
+    <UseDebugLibraries>true</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Win8 Release|x64'" Label="Configuration">
+    <TargetVersion>Windows8</TargetVersion>
+    <UseDebugLibraries>false</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Win7 Debug|x64'" Label="Configuration">
+    <TargetVersion>Windows7</TargetVersion>
+    <UseDebugLibraries>true</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Win7 Release|x64'" Label="Configuration">
+    <TargetVersion>Windows7</TargetVersion>
+    <UseDebugLibraries>false</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Vista Debug|x64'" Label="Configuration">
+    <TargetVersion>Vista</TargetVersion>
+    <UseDebugLibraries>true</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Vista Release|x64'" Label="Configuration">
+    <TargetVersion>Vista</TargetVersion>
+    <UseDebugLibraries>false</UseDebugLibraries>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup />
+  <PropertyGroup>
+    <DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
+    <EnableDeployment>False</EnableDeployment>
+    <RemoveDriver>True</RemoveDriver>
+    <HardwareIdString />
+    <CommandLine />
+    <DeployFiles />
+    <EnableVerifier>False</EnableVerifier>
+    <AllDrivers>False</AllDrivers>
+    <VerifyProjectOutput>True</VerifyProjectOutput>
+    <VerifyDrivers />
+    <VerifyFlags>133563</VerifyFlags>
+    <PackageDir>$(OutDir)TapDriver Package</PackageDir>
+  </PropertyGroup>
+  <ItemDefinitionGroup>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\TapDriver\TapDriver.vcxproj">
+      <Project>{689210b1-467c-4850-bb7d-2e10d5b4a3da}</Project>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>

+ 9 - 0
vsprojects/TapDriver Package/TapDriver Package.vcxproj.filters

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Driver Files">
+      <UniqueIdentifier>{8E41214B-6785-4CFE-B992-037D68949A14}</UniqueIdentifier>
+      <Extensions>inf;inv;inx;mof;mc;</Extensions>
+    </Filter>
+  </ItemGroup>
+</Project>

+ 91 - 0
vsprojects/TapDriver/TapDriver.inf

@@ -0,0 +1,91 @@
+;
+; TapDriver.inf
+;
+
+[Version]
+Signature="$WINDOWS NT$"
+Class=Sample ; TODO: edit Class
+ClassGuid={78A1C341-4539-11d3-B88D-00C04FAD5171} ; TODO: edit ClassGuid
+Provider=%ManufacturerName%
+CatalogFile=TapDriver.cat
+DriverVer= ; TODO: set DriverVer in stampinf property pages
+
+[DestinationDirs]
+DefaultDestDir = 12
+
+; ================= Class section =====================
+
+[ClassInstall32]
+Addreg=SampleClassReg
+
+[SampleClassReg]
+HKR,,,0,%ClassName%
+HKR,,Icon,,-5
+
+[SourceDisksNames]
+1 = %DiskName%,,,""
+
+[SourceDisksFiles]
+TapDriver.sys  = 1,,
+
+;*****************************************
+; Install Section
+;*****************************************
+
+[Manufacturer]
+%ManufacturerName%=Standard,NT$ARCH$
+
+[Standard.NT$ARCH$]
+%TapDriver.DeviceDesc%=TapDriver_Device, Root\TapDriver ; TODO: edit hw-id
+
+[TapDriver_Device.NT]
+CopyFiles=Drivers_Dir
+
+[Drivers_Dir]
+TapDriver.sys
+
+;-------------- Service installation
+[TapDriver_Device.NT.Services]
+AddService = TapDriver,%SPSVCINST_ASSOCSERVICE%, TapDriver_Service_Inst
+
+; -------------- TapDriver driver install sections
+[TapDriver_Service_Inst]
+DisplayName    = %TapDriver.SVCDESC%
+ServiceType    = 1               ; SERVICE_KERNEL_DRIVER
+StartType      = 3               ; SERVICE_DEMAND_START
+ErrorControl   = 1               ; SERVICE_ERROR_NORMAL
+ServiceBinary  = %12%\TapDriver.sys
+LoadOrderGroup = Extended Base
+
+;
+;--- TapDriver_Device Coinstaller installation ------
+;
+
+[DestinationDirs]
+TapDriver_Device_CoInstaller_CopyFiles = 11
+
+[TapDriver_Device.NT.CoInstallers]
+AddReg=TapDriver_Device_CoInstaller_AddReg
+CopyFiles=TapDriver_Device_CoInstaller_CopyFiles
+
+[TapDriver_Device_CoInstaller_AddReg]
+HKR,,CoInstallers32,0x00010000, "WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll,WdfCoInstaller"
+
+[TapDriver_Device_CoInstaller_CopyFiles]
+WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll
+
+[SourceDisksFiles]
+WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll=1 ; make sure the number matches with SourceDisksNames
+
+[TapDriver_Device.NT.Wdf]
+KmdfService =  TapDriver, TapDriver_wdfsect
+[TapDriver_wdfsect]
+KmdfLibraryVersion = $KMDFVERSION$
+
+[Strings]
+SPSVCINST_ASSOCSERVICE= 0x00000002
+ManufacturerName="" ; TODO: add ManufacturerName
+ClassName="Samples" ; TODO: edit ClassName
+DiskName = "TapDriver Installation Disk"
+TapDriver.DeviceDesc = "TapDriver Device"
+TapDriver.SVCDESC = "TapDriver Service"

+ 183 - 0
vsprojects/TapDriver/TapDriver.vcxproj

@@ -0,0 +1,183 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Win8 Debug|Win32">
+      <Configuration>Win8 Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Win8 Release|Win32">
+      <Configuration>Win8 Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Win7 Debug|Win32">
+      <Configuration>Win7 Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Win7 Release|Win32">
+      <Configuration>Win7 Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Vista Debug|Win32">
+      <Configuration>Vista Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Vista Release|Win32">
+      <Configuration>Vista Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Win8 Debug|x64">
+      <Configuration>Win8 Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Win8 Release|x64">
+      <Configuration>Win8 Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Win7 Debug|x64">
+      <Configuration>Win7 Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Win7 Release|x64">
+      <Configuration>Win7 Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Vista Debug|x64">
+      <Configuration>Vista Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Vista Release|x64">
+      <Configuration>Vista Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{689210B1-467C-4850-BB7D-2E10D5B4A3DA}</ProjectGuid>
+    <TemplateGuid>{1bc93793-694f-48fe-9372-81e2b05556fd}</TemplateGuid>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion>
+    <Configuration>Win8 Debug</Configuration>
+    <Platform Condition="'$(Platform)' == ''">Win32</Platform>
+  </PropertyGroup>
+  <PropertyGroup Label="Globals">
+    <RootNamespace>TapDriver</RootNamespace>
+    <VCTargetsPath Condition="'$(VCTargetsPath11)' != '' and '$(VisualStudioVersion)' == '11.0'">$(VCTargetsPath11)</VCTargetsPath>
+  </PropertyGroup>
+  <PropertyGroup Label="PropertySheets">
+    <PlatformToolset>WindowsKernelModeDriver8.0</PlatformToolset>
+    <ConfigurationType>Driver</ConfigurationType>
+    <DriverType>KMDF</DriverType>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Win8 Debug|Win32'" Label="Configuration">
+    <TargetVersion>Windows8</TargetVersion>
+    <UseDebugLibraries>true</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Win8 Release|Win32'" Label="Configuration">
+    <TargetVersion>Windows8</TargetVersion>
+    <UseDebugLibraries>false</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Win7 Debug|Win32'" Label="Configuration">
+    <TargetVersion>Windows7</TargetVersion>
+    <UseDebugLibraries>true</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Win7 Release|Win32'" Label="Configuration">
+    <TargetVersion>Windows7</TargetVersion>
+    <UseDebugLibraries>false</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Vista Debug|Win32'" Label="Configuration">
+    <TargetVersion>Vista</TargetVersion>
+    <UseDebugLibraries>true</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Vista Release|Win32'" Label="Configuration">
+    <TargetVersion>Vista</TargetVersion>
+    <UseDebugLibraries>false</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Win8 Debug|x64'" Label="Configuration">
+    <TargetVersion>Windows8</TargetVersion>
+    <UseDebugLibraries>true</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Win8 Release|x64'" Label="Configuration">
+    <TargetVersion>Windows8</TargetVersion>
+    <UseDebugLibraries>false</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Win7 Debug|x64'" Label="Configuration">
+    <TargetVersion>Windows7</TargetVersion>
+    <UseDebugLibraries>true</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Win7 Release|x64'" Label="Configuration">
+    <TargetVersion>Windows7</TargetVersion>
+    <UseDebugLibraries>false</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Vista Debug|x64'" Label="Configuration">
+    <TargetVersion>Vista</TargetVersion>
+    <UseDebugLibraries>true</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Vista Release|x64'" Label="Configuration">
+    <TargetVersion>Vista</TargetVersion>
+    <UseDebugLibraries>false</UseDebugLibraries>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup />
+  <PropertyGroup>
+    <DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
+  </PropertyGroup>
+  <ItemDefinitionGroup>
+    <ClCompile>
+      <WppEnabled>false</WppEnabled>
+      <WppScanConfigurationData Condition="'%(ClCompile. ScanConfigurationData)'  == ''">trace.h</WppScanConfigurationData>
+      <WppKernelMode>true</WppKernelMode>
+      <WarningLevel Condition="'$(Configuration)|$(Platform)'=='Win8 Release|Win32'">Level2</WarningLevel>
+      <TreatWarningAsError Condition="'$(Configuration)|$(Platform)'=='Win8 Release|Win32'">false</TreatWarningAsError>
+    </ClCompile>
+    <Link>
+      <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Win8 Release|Win32'">C:\WinDDK\7600.16385.1\lib\wnet\i386\ndis.lib;C:\WinDDK\7600.16385.1\lib\wnet\i386\ntstrsafe.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <Inf Include="TapDriver.inf" />
+  </ItemGroup>
+  <ItemGroup>
+    <FilesToPackage Include="$(TargetPath)" />
+    <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="dhcp.c">
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Win8 Release|Win32'">true</ExcludedFromBuild>
+    </ClCompile>
+    <ClCompile Include="error.c">
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Win8 Release|Win32'">true</ExcludedFromBuild>
+    </ClCompile>
+    <ClCompile Include="instance.c">
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Win8 Release|Win32'">true</ExcludedFromBuild>
+    </ClCompile>
+    <ClCompile Include="macinfo.c">
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Win8 Release|Win32'">true</ExcludedFromBuild>
+    </ClCompile>
+    <ClCompile Include="mem.c">
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Win8 Release|Win32'">true</ExcludedFromBuild>
+    </ClCompile>
+    <ClCompile Include="tapdrvr.c" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="config.h" />
+    <ClInclude Include="constants.h" />
+    <ClInclude Include="dhcp.h" />
+    <ClInclude Include="endian.h" />
+    <ClInclude Include="error.h" />
+    <ClInclude Include="lock.h" />
+    <ClInclude Include="macinfo.h" />
+    <ClInclude Include="proto.h" />
+    <ClInclude Include="prototypes.h" />
+    <ClInclude Include="tap-windows.h" />
+    <ClInclude Include="types.h" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>

+ 81 - 0
vsprojects/TapDriver/TapDriver.vcxproj.filters

@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+    <Filter Include="Resource Files">
+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+    </Filter>
+    <Filter Include="Driver Files">
+      <UniqueIdentifier>{8E41214B-6785-4CFE-B992-037D68949A14}</UniqueIdentifier>
+      <Extensions>inf;inv;inx;mof;mc;</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <Inf Include="TapDriver.inf">
+      <Filter>Driver Files</Filter>
+    </Inf>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="dhcp.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="error.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="instance.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="macinfo.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="mem.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="tapdrvr.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="constants.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="dhcp.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="endian.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="error.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="lock.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="macinfo.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="proto.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="prototypes.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="tap-windows.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="types.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="config.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+  </ItemGroup>
+</Project>

+ 12 - 0
vsprojects/TapDriver/config.h

@@ -0,0 +1,12 @@
+#define PRODUCT_NAME			"ZeroTier One Ethernet Tap"
+#define PRODUCT_VERSION			"1.0.0"
+#define PRODUCT_VERSION_RESOURCE	1,0,0,1
+#define PRODUCT_TAP_WIN_COMPONENT_ID	"ztTap100"
+#define PRODUCT_TAP_WIN_MAJOR		1
+#define PRODUCT_TAP_WIN_MINOR		0
+#define PRODUCT_TAP_WIN_PROVIDER		"ZeroTier Networks"
+#define PRODUCT_TAP_WIN_DEVICE_DESCRIPTION	PRODUCT_NAME
+#define PRODUCT_TAP_WIN_RELDATE		"8/1/2013"
+
+#define TAP_DRIVER_MAJOR_VERSION PRODUCT_TAP_WIN_MAJOR
+#define TAP_DRIVER_MINOR_VERSION PRODUCT_TAP_WIN_MINOR

+ 52 - 0
vsprojects/TapDriver/constants.h

@@ -0,0 +1,52 @@
+/*
+ *  TAP-Windows -- A kernel driver to provide virtual tap
+ *                 device functionality on Windows.
+ *
+ *  This code was inspired by the CIPE-Win32 driver by Damion K. Wilson.
+ *
+ *  This source code is Copyright (C) 2002-2010 OpenVPN Technologies, Inc.,
+ *  and is released under the GPL version 2 (see below).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2
+ *  as published by the Free Software Foundation.
+ *
+ *  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 (see the file COPYING included with this
+ *  distribution); if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+//====================================================================
+//                        Product and Version public settings
+//====================================================================
+
+#define PRODUCT_STRING PRODUCT_TAP_DEVICE_DESCRIPTION
+
+#define TAP_NDIS_MAJOR_VERSION 5
+#define TAP_NDIS_MINOR_VERSION 0
+
+//===========================================================
+// Driver constants
+//===========================================================
+
+#define ETHERNET_HEADER_SIZE     (sizeof (ETH_HEADER))
+#define ETHERNET_MTU             2800 // ZeroTier One MTU
+#define ETHERNET_PACKET_SIZE     (ETHERNET_MTU + ETHERNET_HEADER_SIZE)
+#define DEFAULT_PACKET_LOOKAHEAD (ETHERNET_PACKET_SIZE)
+
+#define NIC_MAX_MCAST_LIST 32  // Max length of multicast address list
+
+#define MINIMUM_MTU 576        // USE TCP Minimum MTU
+#define MAXIMUM_MTU 65536      // IP maximum MTU
+
+#define PACKET_QUEUE_SIZE   64 // tap -> userspace queue size
+#define IRP_QUEUE_SIZE      16 // max number of simultaneous i/o operations from userspace
+#define INJECT_QUEUE_SIZE   16 // DHCP/ARP -> tap injection queue
+
+#define TAP_LITTLE_ENDIAN      // affects ntohs, htonl, etc. functions

+ 599 - 0
vsprojects/TapDriver/dhcp.c

@@ -0,0 +1,599 @@
+/*
+ *  TAP-Windows -- A kernel driver to provide virtual tap
+ *                 device functionality on Windows.
+ *
+ *  This code was inspired by the CIPE-Win32 driver by Damion K. Wilson.
+ *
+ *  This source code is Copyright (C) 2002-2010 OpenVPN Technologies, Inc.,
+ *  and is released under the GPL version 2 (see below).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2
+ *  as published by the Free Software Foundation.
+ *
+ *  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 (see the file COPYING included with this
+ *  distribution); if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+//=========================
+// Code to set DHCP options
+//=========================
+
+VOID
+SetDHCPOpt (DHCPMsg *m, void *data, unsigned int len)
+{
+  if (!m->overflow)
+    {
+      if (m->optlen + len <= DHCP_OPTIONS_BUFFER_SIZE)
+	{
+	  if (len)
+	    {
+	      NdisMoveMemory (m->msg.options + m->optlen, data, len);
+	      m->optlen += len;
+	    }
+	}
+      else
+	{
+	  m->overflow = TRUE;
+	}
+    }
+}
+
+VOID
+SetDHCPOpt0 (DHCPMsg *msg, int type)
+{
+  DHCPOPT0 opt;
+  opt.type = (UCHAR) type;
+  SetDHCPOpt (msg, &opt, sizeof (opt));
+}
+
+VOID
+SetDHCPOpt8 (DHCPMsg *msg, int type, ULONG data)
+{
+  DHCPOPT8 opt;
+  opt.type = (UCHAR) type;
+  opt.len = sizeof (opt.data);
+  opt.data = (UCHAR) data;
+  SetDHCPOpt (msg, &opt, sizeof (opt));
+}
+
+VOID
+SetDHCPOpt32 (DHCPMsg *msg, int type, ULONG data)
+{
+  DHCPOPT32 opt;
+  opt.type = (UCHAR) type;
+  opt.len = sizeof (opt.data);
+  opt.data = data;
+  SetDHCPOpt (msg, &opt, sizeof (opt));
+}
+
+//==============
+// Checksum code
+//==============
+
+USHORT
+ip_checksum (const UCHAR *buf, const int len_ip_header)
+{
+  USHORT word16;
+  ULONG sum = 0;
+  int i;
+    
+  // make 16 bit words out of every two adjacent 8 bit words in the packet
+  // and add them up
+  for (i = 0; i < len_ip_header - 1; i += 2) {
+    word16 = ((buf[i] << 8) & 0xFF00) + (buf[i+1] & 0xFF);
+    sum += (ULONG) word16;
+  }
+
+  // take only 16 bits out of the 32 bit sum and add up the carries
+  while (sum >> 16)
+    sum = (sum & 0xFFFF) + (sum >> 16);
+
+  // one's complement the result
+  return ((USHORT) ~sum);
+}
+
+USHORT
+udp_checksum (const UCHAR *buf,
+	      const int len_udp,
+	      const UCHAR *src_addr,
+	      const UCHAR *dest_addr)
+{
+  USHORT word16;
+  ULONG sum = 0;
+  int i;
+	
+  // make 16 bit words out of every two adjacent 8 bit words and 
+  // calculate the sum of all 16 bit words
+  for (i = 0; i < len_udp; i += 2){
+    word16 = ((buf[i] << 8) & 0xFF00) + ((i + 1 < len_udp) ? (buf[i+1] & 0xFF) : 0);
+    sum += word16;
+  }
+
+  // add the UDP pseudo header which contains the IP source and destination addresses
+  for (i = 0; i < 4; i += 2){
+    word16 =((src_addr[i] << 8) & 0xFF00) + (src_addr[i+1] & 0xFF);
+    sum += word16;
+  }
+  for (i = 0; i < 4; i += 2){
+    word16 =((dest_addr[i] << 8) & 0xFF00) + (dest_addr[i+1] & 0xFF);
+    sum += word16; 	
+  }
+
+  // the protocol number and the length of the UDP packet
+  sum += (USHORT) IPPROTO_UDP + (USHORT) len_udp;
+
+  // keep only the last 16 bits of the 32 bit calculated sum and add the carries
+  while (sum >> 16)
+    sum = (sum & 0xFFFF) + (sum >> 16);
+		
+  // Take the one's complement of sum
+  return ((USHORT) ~sum);
+}
+
+//================================
+// Set IP and UDP packet checksums
+//================================
+
+VOID
+SetChecksumDHCPMsg (DHCPMsg *m)
+{
+  // Set IP checksum
+  m->msg.pre.ip.check = htons (ip_checksum ((UCHAR *) &m->msg.pre.ip, sizeof (IPHDR)));
+
+  // Set UDP Checksum
+  m->msg.pre.udp.check = htons (udp_checksum ((UCHAR *) &m->msg.pre.udp, 
+					      sizeof (UDPHDR) + sizeof (DHCP) + m->optlen,
+					      (UCHAR *)&m->msg.pre.ip.saddr,
+					      (UCHAR *)&m->msg.pre.ip.daddr));
+}
+
+//===================
+// DHCP message tests
+//===================
+
+int
+GetDHCPMessageType (const DHCP *dhcp, const int optlen)
+{
+  const UCHAR *p = (UCHAR *) (dhcp + 1);
+  int i;
+
+  for (i = 0; i < optlen; ++i)
+    {
+      const UCHAR type = p[i];
+      const int room = optlen - i - 1;
+      if (type == DHCP_END)           // didn't find what we were looking for
+	return -1;
+      else if (type == DHCP_PAD)      // no-operation
+	;
+      else if (type == DHCP_MSG_TYPE) // what we are looking for
+	{
+	  if (room >= 2)
+	    {
+	      if (p[i+1] == 1)        // message length should be 1
+		return p[i+2];        // return message type
+	    }
+	  return -1;
+	}
+      else                            // some other message
+	{
+	  if (room >= 1)
+	    {
+	      const int len = p[i+1]; // get message length
+	      i += (len + 1);         // advance to next message
+	    }
+	}
+    }
+  return -1;
+}
+
+BOOLEAN
+DHCPMessageOurs (const TapAdapterPointer p_Adapter,
+		 const ETH_HEADER *eth,
+		 const IPHDR *ip,
+		 const UDPHDR *udp,
+		 const DHCP *dhcp)
+{
+  // Must be UDPv4 protocol
+  if (!(eth->proto == htons (ETH_P_IP) && ip->protocol == IPPROTO_UDP))
+    return FALSE;
+
+  // Source MAC must be our adapter
+  if (!MAC_EQUAL (eth->src, p_Adapter->m_MAC))
+    return FALSE;
+
+  // Dest MAC must be either broadcast or our virtual DHCP server
+  if (!(MAC_EQUAL (eth->dest, p_Adapter->m_MAC_Broadcast)
+	|| MAC_EQUAL (eth->dest, p_Adapter->m_dhcp_server_mac)))
+    return FALSE;
+
+  // Port numbers must be correct
+  if (!(udp->dest == htons (BOOTPS_PORT)
+	&& udp->source == htons (BOOTPC_PORT)))
+    return FALSE;
+
+  // Hardware address must be MAC addr sized
+  if (!(dhcp->hlen == sizeof (MACADDR)))
+    return FALSE;
+
+  // Hardware address must match our adapter
+  if (!MAC_EQUAL (eth->src, dhcp->chaddr))
+    return FALSE;
+
+  return TRUE;
+}
+
+
+//=====================================================
+// Build all of DHCP packet except for DHCP options.
+// Assume that *p has been zeroed before we are called.
+//=====================================================
+
+VOID
+BuildDHCPPre (const TapAdapterPointer a,
+	      DHCPPre *p,
+	      const ETH_HEADER *eth,
+	      const IPHDR *ip,
+	      const UDPHDR *udp,
+	      const DHCP *dhcp,
+	      const int optlen,
+	      const int type)
+{
+  // Should we broadcast or direct to a specific MAC / IP address?
+  const BOOLEAN broadcast = (type == DHCPNAK
+			     || MAC_EQUAL (eth->dest, a->m_MAC_Broadcast));
+  // Build ethernet header
+
+  COPY_MAC (p->eth.src, a->m_dhcp_server_mac);
+
+  if (broadcast)
+    COPY_MAC (p->eth.dest, a->m_MAC_Broadcast);
+  else
+    COPY_MAC (p->eth.dest, eth->src);
+
+  p->eth.proto = htons (ETH_P_IP);
+
+  // Build IP header
+
+  p->ip.version_len = (4 << 4) | (sizeof (IPHDR) >> 2);
+  p->ip.tos = 0;
+  p->ip.tot_len = htons (sizeof (IPHDR) + sizeof (UDPHDR) + sizeof (DHCP) + optlen);
+  p->ip.id = 0;
+  p->ip.frag_off = 0;
+  p->ip.ttl = 16;
+  p->ip.protocol = IPPROTO_UDP;
+  p->ip.check = 0;
+  p->ip.saddr = a->m_dhcp_server_ip;
+
+  if (broadcast)
+    p->ip.daddr = ~0;
+  else
+    p->ip.daddr = a->m_dhcp_addr;
+
+  // Build UDP header
+
+  p->udp.source = htons (BOOTPS_PORT);
+  p->udp.dest = htons (BOOTPC_PORT);
+  p->udp.len = htons (sizeof (UDPHDR) + sizeof (DHCP) + optlen);
+  p->udp.check = 0;
+
+  // Build DHCP response
+
+  p->dhcp.op = BOOTREPLY;
+  p->dhcp.htype = 1;
+  p->dhcp.hlen = sizeof (MACADDR);
+  p->dhcp.hops = 0;
+  p->dhcp.xid = dhcp->xid;
+  p->dhcp.secs = 0;
+  p->dhcp.flags = 0;
+  p->dhcp.ciaddr = 0;
+
+  if (type == DHCPNAK)
+    p->dhcp.yiaddr = 0;
+  else
+    p->dhcp.yiaddr = a->m_dhcp_addr;
+
+  p->dhcp.siaddr = a->m_dhcp_server_ip;
+  p->dhcp.giaddr = 0;
+  COPY_MAC (p->dhcp.chaddr, eth->src);
+  p->dhcp.magic = htonl (0x63825363);
+}
+//=============================
+// Build specific DHCP messages
+//=============================
+
+VOID
+SendDHCPMsg (const TapAdapterPointer a,
+	     const int type,
+	     const ETH_HEADER *eth,
+	     const IPHDR *ip,
+	     const UDPHDR *udp,
+	     const DHCP *dhcp)
+{
+  DHCPMsg *pkt;
+
+  if (!(type == DHCPOFFER || type == DHCPACK || type == DHCPNAK))
+    {
+      DEBUGP (("[TAP] SendDHCPMsg: Bad DHCP type: %d\n", type));
+      return;
+    }
+
+  pkt = (DHCPMsg *) MemAlloc (sizeof (DHCPMsg), TRUE);
+
+  if (pkt)
+    {
+      //-----------------------
+      // Build DHCP options
+      //-----------------------
+
+      // Message Type
+      SetDHCPOpt8 (pkt, DHCP_MSG_TYPE, type);
+
+      // Server ID
+      SetDHCPOpt32 (pkt, DHCP_SERVER_ID, a->m_dhcp_server_ip);
+
+      if (type == DHCPOFFER || type == DHCPACK)
+	{
+	  // Lease Time
+	  SetDHCPOpt32 (pkt, DHCP_LEASE_TIME, htonl (a->m_dhcp_lease_time));
+
+	  // Netmask
+	  SetDHCPOpt32 (pkt, DHCP_NETMASK, a->m_dhcp_netmask);
+
+	  // Other user-defined options
+	  SetDHCPOpt (pkt,
+		      a->m_dhcp_user_supplied_options_buffer,
+		      a->m_dhcp_user_supplied_options_buffer_len);
+	}
+
+      // End
+      SetDHCPOpt0 (pkt, DHCP_END);
+
+      if (!DHCPMSG_OVERFLOW (pkt))
+	{
+	  // The initial part of the DHCP message (not including options) gets built here
+	  BuildDHCPPre (a,
+			&pkt->msg.pre,
+			eth,
+			ip,
+			udp,
+			dhcp,
+			DHCPMSG_LEN_OPT (pkt),
+			type);
+
+	  SetChecksumDHCPMsg (pkt);
+
+	  DUMP_PACKET ("DHCPMsg",
+		       DHCPMSG_BUF (pkt),
+		       DHCPMSG_LEN_FULL (pkt));
+
+	  // Return DHCP response to kernel
+	  InjectPacketDeferred (a,
+				DHCPMSG_BUF (pkt),
+				DHCPMSG_LEN_FULL (pkt));
+	}
+      else
+	{
+	  DEBUGP (("[TAP] SendDHCPMsg: DHCP buffer overflow\n"));
+	}
+
+      MemFree (pkt, sizeof (DHCPMsg));
+    }
+}
+
+//===================================================================
+// Handle a BOOTPS packet produced by the local system to
+// resolve the address/netmask of this adapter.
+// If we are in TAP_WIN_IOCTL_CONFIG_DHCP_MASQ mode, reply
+// to the message.  Return TRUE if we processed the passed
+// message, so that downstream stages can ignore it.
+//===================================================================
+
+BOOLEAN
+ProcessDHCP (TapAdapterPointer p_Adapter,
+	     const ETH_HEADER *eth,
+	     const IPHDR *ip,
+	     const UDPHDR *udp,
+	     const DHCP *dhcp,
+	     int optlen)
+{
+  int msg_type;
+
+  // Sanity check IP header
+  if (!(ntohs (ip->tot_len) == sizeof (IPHDR) + sizeof (UDPHDR) + sizeof (DHCP) + optlen
+	&& (ntohs (ip->frag_off) & IP_OFFMASK) == 0))
+    return TRUE;
+
+  // Does this message belong to us?
+  if (!DHCPMessageOurs (p_Adapter, eth, ip, udp, dhcp))
+    return FALSE;
+
+  msg_type = GetDHCPMessageType (dhcp, optlen);
+
+  // Drop non-BOOTREQUEST messages
+  if (dhcp->op != BOOTREQUEST)
+    return TRUE;
+
+  // Drop any messages except DHCPDISCOVER or DHCPREQUEST
+  if (!(msg_type == DHCPDISCOVER || msg_type == DHCPREQUEST))
+    return TRUE;
+
+  // Should we reply with DHCPOFFER, DHCPACK, or DHCPNAK?
+  if (msg_type == DHCPREQUEST
+      && ((dhcp->ciaddr && dhcp->ciaddr != p_Adapter->m_dhcp_addr)
+	  || !p_Adapter->m_dhcp_received_discover
+	  || p_Adapter->m_dhcp_bad_requests >= BAD_DHCPREQUEST_NAK_THRESHOLD))
+    SendDHCPMsg (p_Adapter,
+		 DHCPNAK,
+		 eth, ip, udp, dhcp);
+  else
+    SendDHCPMsg (p_Adapter,
+		 (msg_type == DHCPDISCOVER ? DHCPOFFER : DHCPACK),
+		 eth, ip, udp, dhcp);
+
+  // Remember if we received a DHCPDISCOVER
+  if (msg_type == DHCPDISCOVER)
+    p_Adapter->m_dhcp_received_discover = TRUE;
+
+  // Is this a bad DHCPREQUEST?
+  if (msg_type == DHCPREQUEST && dhcp->ciaddr && dhcp->ciaddr != p_Adapter->m_dhcp_addr)
+    ++p_Adapter->m_dhcp_bad_requests;
+
+  return TRUE;
+}
+
+#if DBG
+
+const char *
+message_op_text (int op)
+{
+  switch (op)
+    {
+    case BOOTREQUEST:
+      return "BOOTREQUEST";
+    case BOOTREPLY:
+      return "BOOTREPLY";
+    default:
+      return "???";
+    }
+}
+
+const char *
+message_type_text (int type)
+{
+  switch (type)
+    {
+    case DHCPDISCOVER:
+      return "DHCPDISCOVER";
+    case DHCPOFFER:
+      return "DHCPOFFER";
+    case DHCPREQUEST:
+      return "DHCPREQUEST";
+    case DHCPDECLINE:
+      return "DHCPDECLINE";
+    case DHCPACK:
+      return "DHCPACK";
+    case DHCPNAK:
+      return "DHCPNAK";
+    case DHCPRELEASE:
+      return "DHCPRELEASE";
+    case DHCPINFORM:
+      return "DHCPINFORM";
+    default:
+      return "???";
+    }
+}
+
+const char *
+port_name (int port)
+{
+  switch (port)
+    {
+    case BOOTPS_PORT:
+      return "BOOTPS";
+    case BOOTPC_PORT:
+      return "BOOTPC";
+    default:
+      return "unknown";
+    }
+}
+
+VOID
+DumpDHCP (const ETH_HEADER *eth,
+	  const IPHDR *ip,
+	  const UDPHDR *udp,
+	  const DHCP *dhcp,
+	  const int optlen)
+{
+  DEBUGP ((" %s", message_op_text (dhcp->op)));
+  DEBUGP ((" %s ", message_type_text (GetDHCPMessageType (dhcp, optlen))));
+  PrIP (ip->saddr);
+  DEBUGP ((":%s[", port_name (ntohs (udp->source))));
+  PrMac (eth->src);
+  DEBUGP (("] -> "));
+  PrIP (ip->daddr);
+  DEBUGP ((":%s[", port_name (ntohs (udp->dest))));
+  PrMac (eth->dest);
+  DEBUGP (("]"));
+  if (dhcp->ciaddr)
+    {
+      DEBUGP ((" ci="));
+      PrIP (dhcp->ciaddr);
+    }
+  if (dhcp->yiaddr)
+    {
+      DEBUGP ((" yi="));
+      PrIP (dhcp->yiaddr);
+    }
+  if (dhcp->siaddr)
+    {
+      DEBUGP ((" si="));
+      PrIP (dhcp->siaddr);
+    }
+  if (dhcp->hlen == sizeof (MACADDR))
+    {
+      DEBUGP ((" ch="));
+      PrMac (dhcp->chaddr);
+    }
+
+  DEBUGP ((" xid=0x%08x", ntohl (dhcp->xid)));
+
+  if (ntohl (dhcp->magic) != 0x63825363)
+    DEBUGP ((" ma=0x%08x", ntohl (dhcp->magic)));
+  if (dhcp->htype != 1)
+    DEBUGP ((" htype=%d", dhcp->htype));
+  if (dhcp->hops)
+    DEBUGP ((" hops=%d", dhcp->hops));
+  if (ntohs (dhcp->secs))
+    DEBUGP ((" secs=%d", ntohs (dhcp->secs)));
+  if (ntohs (dhcp->flags))
+    DEBUGP ((" flags=0x%04x", ntohs (dhcp->flags)));
+
+  // extra stuff
+  
+  if (ip->version_len != 0x45)
+    DEBUGP ((" vl=0x%02x", ip->version_len));
+  if (ntohs (ip->tot_len) != sizeof (IPHDR) + sizeof (UDPHDR) + sizeof (DHCP) + optlen)
+    DEBUGP ((" tl=%d", ntohs (ip->tot_len)));
+  if (ntohs (udp->len) != sizeof (UDPHDR) + sizeof (DHCP) + optlen)
+    DEBUGP ((" ul=%d", ntohs (udp->len)));
+
+  if (ip->tos)
+    DEBUGP ((" tos=0x%02x", ip->tos));
+  if (ntohs (ip->id))
+    DEBUGP ((" id=0x%04x", ntohs (ip->id)));
+  if (ntohs (ip->frag_off))
+    DEBUGP ((" frag_off=0x%04x", ntohs (ip->frag_off)));
+  
+  DEBUGP ((" ttl=%d", ip->ttl));
+  DEBUGP ((" ic=0x%04x [0x%04x]", ntohs (ip->check),
+	    ip_checksum ((UCHAR*)ip, sizeof (IPHDR))));
+  DEBUGP ((" uc=0x%04x [0x%04x/%d]", ntohs (udp->check),
+	    udp_checksum ((UCHAR *) udp,
+			  sizeof (UDPHDR) + sizeof (DHCP) + optlen,
+			  (UCHAR *) &ip->saddr,
+			  (UCHAR *) &ip->daddr),
+	    optlen));
+
+  // Options
+  {
+    const UCHAR *opt = (UCHAR *) (dhcp + 1);
+    int i;
+
+    DEBUGP ((" OPT"));
+    for (i = 0; i < optlen; ++i)
+      {
+	const UCHAR data = opt[i];
+	DEBUGP ((".%d", data));
+      }
+  }
+}
+
+#endif /* DBG */

+ 164 - 0
vsprojects/TapDriver/dhcp.h

@@ -0,0 +1,164 @@
+/*
+ *  TAP-Windows -- A kernel driver to provide virtual tap
+ *                 device functionality on Windows.
+ *
+ *  This code was inspired by the CIPE-Win32 driver by Damion K. Wilson.
+ *
+ *  This source code is Copyright (C) 2002-2010 OpenVPN Technologies, Inc.,
+ *  and is released under the GPL version 2 (see below).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2
+ *  as published by the Free Software Foundation.
+ *
+ *  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 (see the file COPYING included with this
+ *  distribution); if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#pragma pack(1)
+
+//===================================================
+// How many bad DHCPREQUESTs do we receive before we
+// return a NAK?
+//
+// A bad DHCPREQUEST is defined to be one where the
+// requestor doesn't know its IP address.
+//===================================================
+
+#define BAD_DHCPREQUEST_NAK_THRESHOLD 3
+
+//==============================================
+// Maximum number of DHCP options bytes supplied
+//==============================================
+
+#define DHCP_USER_SUPPLIED_OPTIONS_BUFFER_SIZE 256
+#define DHCP_OPTIONS_BUFFER_SIZE               256
+
+//===================================
+// UDP port numbers of DHCP messages.
+//===================================
+
+#define BOOTPS_PORT 67
+#define BOOTPC_PORT 68
+
+//===========================
+// The DHCP message structure
+//===========================
+
+typedef struct {
+# define BOOTREQUEST 1
+# define BOOTREPLY   2
+  UCHAR op;          /* message op */
+
+  UCHAR  htype;      /* hardware address type (e.g. '1' = 10Mb Ethernet) */
+  UCHAR  hlen;       /* hardware address length (e.g. '6' for 10Mb Ethernet) */
+  UCHAR  hops;       /* client sets to 0, may be used by relay agents */
+  ULONG  xid;        /* transaction ID, chosen by client */
+  USHORT secs;       /* seconds since request process began, set by client */
+  USHORT flags;
+  ULONG  ciaddr;     /* client IP address, client sets if known */
+  ULONG  yiaddr;     /* 'your' IP address -- server's response to client */
+  ULONG  siaddr;     /* server IP address */
+  ULONG  giaddr;     /* relay agent IP address */
+  UCHAR  chaddr[16]; /* client hardware address */
+  UCHAR  sname[64];  /* optional server host name */
+  UCHAR  file[128];  /* boot file name */
+  ULONG  magic;      /* must be 0x63825363 (network order) */
+} DHCP;
+
+typedef struct {
+  ETH_HEADER eth;
+  IPHDR ip;
+  UDPHDR udp;
+  DHCP dhcp;
+} DHCPPre;
+
+typedef struct {
+  DHCPPre pre;
+  UCHAR options[DHCP_OPTIONS_BUFFER_SIZE];
+} DHCPFull;
+
+typedef struct {
+  unsigned int optlen;
+  BOOLEAN overflow;
+  DHCPFull msg;
+} DHCPMsg;
+
+//===================
+// Macros for DHCPMSG
+//===================
+
+#define DHCPMSG_LEN_BASE(p) (sizeof (DHCPPre))
+#define DHCPMSG_LEN_OPT(p)  ((p)->optlen)
+#define DHCPMSG_LEN_FULL(p) (DHCPMSG_LEN_BASE(p) + DHCPMSG_LEN_OPT(p))
+#define DHCPMSG_BUF(p)      ((UCHAR*) &(p)->msg)
+#define DHCPMSG_OVERFLOW(p) ((p)->overflow)
+
+//========================================
+// structs to hold individual DHCP options
+//========================================
+
+typedef struct {
+  UCHAR type;
+} DHCPOPT0;
+
+typedef struct {
+  UCHAR type;
+  UCHAR len;
+  UCHAR data;
+} DHCPOPT8;
+
+typedef struct {
+  UCHAR type;
+  UCHAR len;
+  ULONG data;
+} DHCPOPT32;
+
+#pragma pack()
+
+//==================
+// DHCP Option types
+//==================
+
+#define DHCP_MSG_TYPE    53  /* message type (u8) */
+#define DHCP_PARM_REQ    55  /* parameter request list: c1 (u8), ... */
+#define DHCP_CLIENT_ID   61  /* client ID: type (u8), i1 (u8), ... */
+#define DHCP_IP          50  /* requested IP addr (u32) */
+#define DHCP_NETMASK      1  /* subnet mask (u32) */
+#define DHCP_LEASE_TIME  51  /* lease time sec (u32) */
+#define DHCP_RENEW_TIME  58  /* renewal time sec (u32) */
+#define DHCP_REBIND_TIME 59  /* rebind time sec (u32) */
+#define DHCP_SERVER_ID   54  /* server ID: IP addr (u32) */
+#define DHCP_PAD          0
+#define DHCP_END        255
+
+//====================
+// DHCP Messages types
+//====================
+
+#define DHCPDISCOVER 1
+#define DHCPOFFER    2
+#define DHCPREQUEST  3
+#define DHCPDECLINE  4
+#define DHCPACK      5
+#define DHCPNAK      6
+#define DHCPRELEASE  7
+#define DHCPINFORM   8
+
+#if DBG
+
+VOID
+DumpDHCP (const ETH_HEADER *eth,
+	  const IPHDR *ip,
+	  const UDPHDR *udp,
+	  const DHCP *dhcp,
+	  const int optlen);
+
+#endif

+ 35 - 0
vsprojects/TapDriver/endian.h

@@ -0,0 +1,35 @@
+/*
+ *  TAP-Windows -- A kernel driver to provide virtual tap
+ *                 device functionality on Windows.
+ *
+ *  This code was inspired by the CIPE-Win32 driver by Damion K. Wilson.
+ *
+ *  This source code is Copyright (C) 2002-2010 OpenVPN Technologies, Inc.,
+ *  and is released under the GPL version 2 (see below).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2
+ *  as published by the Free Software Foundation.
+ *
+ *  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 (see the file COPYING included with this
+ *  distribution); if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifdef TAP_LITTLE_ENDIAN
+#define ntohs(x) RtlUshortByteSwap(x)
+#define htons(x) RtlUshortByteSwap(x)
+#define ntohl(x) RtlUlongByteSwap(x)
+#define htonl(x) RtlUlongByteSwap(x)
+#else
+#define ntohs(x) ((USHORT)(x))
+#define htons(x) ((USHORT)(x))
+#define ntohl(x) ((ULONG)(x))
+#define htonl(x) ((ULONG)(x))
+#endif

+ 385 - 0
vsprojects/TapDriver/error.c

@@ -0,0 +1,385 @@
+/*
+ *  TAP-Windows -- A kernel driver to provide virtual tap
+ *                 device functionality on Windows.
+ *
+ *  This code was inspired by the CIPE-Win32 driver by Damion K. Wilson.
+ *
+ *  This source code is Copyright (C) 2002-2010 OpenVPN Technologies, Inc.,
+ *  and is released under the GPL version 2 (see below).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2
+ *  as published by the Free Software Foundation.
+ *
+ *  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 (see the file COPYING included with this
+ *  distribution); if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+//-----------------
+// DEBUGGING OUTPUT
+//-----------------
+
+const char *g_LastErrorFilename;
+int g_LastErrorLineNumber;
+
+#if DBG
+
+DebugOutput g_Debug;
+
+BOOLEAN
+NewlineExists (const char *str, int len)
+{
+  while (len-- > 0)
+    {
+      const char c = *str++;
+      if (c == '\n')
+	return TRUE;
+      else if (c == '\0')
+	break;
+    }
+  return FALSE;
+}
+
+VOID
+MyDebugInit (unsigned int bufsiz)
+{
+  NdisZeroMemory (&g_Debug, sizeof (g_Debug));
+  g_Debug.text = (char *) MemAlloc (bufsiz, FALSE);
+  if (g_Debug.text)
+    g_Debug.capacity = bufsiz;
+}
+
+VOID
+MyDebugFree ()
+{
+  if (g_Debug.text)
+    MemFree (g_Debug.text, g_Debug.capacity);
+  NdisZeroMemory (&g_Debug, sizeof (g_Debug));
+}
+
+VOID
+MyDebugPrint (const unsigned char* format, ...)
+{
+  if (g_Debug.text && g_Debug.capacity > 0 && CAN_WE_PRINT)
+    {
+      BOOLEAN owned;
+      ACQUIRE_MUTEX_ADAPTIVE (&g_Debug.lock, owned);
+      if (owned)
+	{
+	  const int remaining = (int)g_Debug.capacity - (int)g_Debug.out;
+
+	  if (remaining > 0)
+	    {
+	      va_list args;
+	      NTSTATUS status;
+	      char *end;
+
+#ifdef DBG_PRINT
+	      va_start (args, format);
+	      vDbgPrintEx (DPFLTR_IHVNETWORK_ID, DPFLTR_INFO_LEVEL, format, args);
+	      va_end (args);
+#endif
+	      va_start (args, format);
+	      status = RtlStringCchVPrintfExA (g_Debug.text + g_Debug.out,
+					       remaining,
+					       &end,
+					       NULL,
+					       STRSAFE_NO_TRUNCATION | STRSAFE_IGNORE_NULLS,
+					       format,
+					       args);
+	      va_end (args);
+va_start (args, format);
+vDbgPrintEx(DPFLTR_IHVDRIVER_ID , 1, format, args);
+va_end (args);
+	      if (status == STATUS_SUCCESS)
+		g_Debug.out = (unsigned int) (end - g_Debug.text);
+	      else
+		g_Debug.error = TRUE;
+	    }
+	  else
+	    g_Debug.error = TRUE;
+
+	  RELEASE_MUTEX (&g_Debug.lock);
+	}
+      else
+	g_Debug.error = TRUE;
+    }
+}
+
+BOOLEAN
+GetDebugLine (char *buf, const int len)
+{
+  static const char *truncated = "[OUTPUT TRUNCATED]\n";
+  BOOLEAN ret = FALSE;
+
+  NdisZeroMemory (buf, len);
+
+  if (g_Debug.text && g_Debug.capacity > 0)
+    {
+      BOOLEAN owned;
+      ACQUIRE_MUTEX_ADAPTIVE (&g_Debug.lock, owned);
+      if (owned)
+	{
+	  int i = 0;
+
+	  if (g_Debug.error || NewlineExists (g_Debug.text + g_Debug.in, (int)g_Debug.out - (int)g_Debug.in))
+	    {
+	      while (i < (len - 1) && g_Debug.in < g_Debug.out)
+		{
+		  const char c = g_Debug.text[g_Debug.in++];
+		  if (c == '\n')
+		    break;
+		  buf[i++] = c;
+		}
+	      if (i < len)
+		buf[i] = '\0';
+	    }
+
+	  if (!i)
+	    {
+	      if (g_Debug.in == g_Debug.out)
+		{
+		  g_Debug.in = g_Debug.out = 0;
+		  if (g_Debug.error)
+		    {
+		      const unsigned int tlen = strlen (truncated);
+		      if (tlen < g_Debug.capacity)
+			{
+			  NdisMoveMemory (g_Debug.text, truncated, tlen+1);
+			  g_Debug.out = tlen;
+			}
+		      g_Debug.error = FALSE;
+		    }
+		}
+	    }
+	  else
+	    ret = TRUE;
+
+	  RELEASE_MUTEX (&g_Debug.lock);
+	}      
+    }
+  return ret;
+}
+
+VOID
+MyAssert (const unsigned char *file, int line)
+{
+      DEBUGP (("MYASSERT failed %s/%d\n", file, line));
+      KeBugCheckEx (0x0F00BABA,
+		    (ULONG_PTR) line,
+		    (ULONG_PTR) 0,
+		    (ULONG_PTR) 0,
+		    (ULONG_PTR) 0);
+}
+
+VOID
+PrMac (const MACADDR mac)
+{
+  DEBUGP (("%x:%x:%x:%x:%x:%x",
+	    mac[0], mac[1], mac[2],
+	    mac[3], mac[4], mac[5]));
+}
+
+VOID
+PrIP (IPADDR ip_addr)
+{
+  const unsigned char *ip = (const unsigned char *) &ip_addr;
+
+  DEBUGP (("%d.%d.%d.%d",
+	    ip[0], ip[1], ip[2], ip[3]));
+}
+
+const char *
+PrIPProto (int proto)
+{
+  switch (proto)
+    {
+    case IPPROTO_UDP:
+      return "UDP";
+    case IPPROTO_TCP:
+      return "TCP";
+    case IPPROTO_ICMP:
+      return "ICMP";
+    case IPPROTO_IGMP:
+      return "IGMP";
+    default:
+      return "???";
+    }
+}
+
+VOID
+DumpARP (const char *prefix, const ARP_PACKET *arp)
+{
+  DEBUGP (("%s ARP src=", prefix));
+  PrMac (arp->m_MAC_Source);
+  DEBUGP ((" dest="));
+  PrMac (arp->m_MAC_Destination);
+  DEBUGP ((" OP=0x%04x",
+	    (int)ntohs(arp->m_ARP_Operation)));
+  DEBUGP ((" M=0x%04x(%d)",
+	    (int)ntohs(arp->m_MAC_AddressType),
+	    (int)arp->m_MAC_AddressSize));
+  DEBUGP ((" P=0x%04x(%d)",
+	    (int)ntohs(arp->m_PROTO_AddressType),
+	    (int)arp->m_PROTO_AddressSize));
+
+  DEBUGP ((" MacSrc="));
+  PrMac (arp->m_ARP_MAC_Source);
+  DEBUGP ((" MacDest="));
+  PrMac (arp->m_ARP_MAC_Destination);
+
+  DEBUGP ((" IPSrc="));
+  PrIP (arp->m_ARP_IP_Source);
+  DEBUGP ((" IPDest="));
+  PrIP (arp->m_ARP_IP_Destination);
+
+  DEBUGP (("\n"));
+}
+
+struct ethpayload {
+  ETH_HEADER eth;
+  UCHAR payload[DEFAULT_PACKET_LOOKAHEAD];
+};
+
+VOID
+DumpPacket2 (const char *prefix,
+	     const ETH_HEADER *eth,
+	     const unsigned char *data,
+	     unsigned int len)
+{
+  struct ethpayload *ep = (struct ethpayload *) MemAlloc (sizeof (struct ethpayload), TRUE);
+  if (ep)
+    {
+      if (len > DEFAULT_PACKET_LOOKAHEAD)
+	len = DEFAULT_PACKET_LOOKAHEAD;
+      ep->eth = *eth;
+      NdisMoveMemory (ep->payload, data, len);
+      DumpPacket (prefix, (unsigned char *) ep, sizeof (ETH_HEADER) + len);
+      MemFree (ep, sizeof (struct ethpayload));
+    }
+}
+
+VOID
+DumpPacket (const char *prefix,
+	    const unsigned char *data,
+	    unsigned int len)
+{
+  const ETH_HEADER *eth = (const ETH_HEADER *) data;
+  const IPHDR *ip = (const IPHDR *) (data + sizeof (ETH_HEADER));
+
+  if (len < sizeof (ETH_HEADER))
+    {
+      DEBUGP (("%s TRUNCATED PACKET LEN=%d\n", prefix, len));
+      return;
+    }
+
+  // ARP Packet?
+  if (len >= sizeof (ARP_PACKET) && eth->proto == htons (ETH_P_ARP))
+    {
+      DumpARP (prefix, (const ARP_PACKET *) data);
+      return;
+    }
+
+  // IPv4 packet?
+  if (len >= (sizeof (IPHDR) + sizeof (ETH_HEADER))
+      && eth->proto == htons (ETH_P_IP)
+      && IPH_GET_VER (ip->version_len) == 4)
+    {
+      const int hlen = IPH_GET_LEN (ip->version_len);
+      const int blen = len - sizeof (ETH_HEADER);
+      BOOLEAN did = FALSE;
+
+      DEBUGP (("%s IPv4 %s[%d]", prefix, PrIPProto (ip->protocol), len));
+
+      if (!(ntohs (ip->tot_len) == blen && hlen <= blen))
+	{
+	  DEBUGP ((" XXX"));
+	  return;
+	}
+      
+      // TCP packet?
+      if (ip->protocol == IPPROTO_TCP
+	  && blen - hlen >= (sizeof (TCPHDR)))
+	{
+	  const TCPHDR *tcp = (TCPHDR *) (data + sizeof (ETH_HEADER) + hlen);
+	  DEBUGP ((" "));
+	  PrIP (ip->saddr);
+	  DEBUGP ((":%d", ntohs (tcp->source)));
+	  DEBUGP ((" -> "));
+	  PrIP (ip->daddr);
+	  DEBUGP ((":%d", ntohs (tcp->dest)));
+	  did = TRUE;
+	}
+
+      // UDP packet?
+      else if ((ntohs (ip->frag_off) & IP_OFFMASK) == 0
+	       && ip->protocol == IPPROTO_UDP
+	       && blen - hlen >= (sizeof (UDPHDR)))
+	{
+	  const UDPHDR *udp = (UDPHDR *) (data + sizeof (ETH_HEADER) + hlen);
+	  
+	  // DHCP packet?
+	  if ((udp->dest == htons (BOOTPC_PORT) || udp->dest == htons (BOOTPS_PORT))
+	      && blen - hlen >= (sizeof (UDPHDR) + sizeof (DHCP)))
+	    {
+	      const DHCP *dhcp = (DHCP *) (data
+					   + hlen
+					   + sizeof (ETH_HEADER)
+					   + sizeof (UDPHDR));
+	      
+	      int optlen = len
+		- sizeof (ETH_HEADER)
+		- hlen
+		- sizeof (UDPHDR)
+		- sizeof (DHCP);
+
+	      if (optlen < 0)
+		optlen = 0;
+
+	      DumpDHCP (eth, ip, udp, dhcp, optlen);
+	      did = TRUE;
+	    }
+
+	  if (!did)
+	    {
+	      DEBUGP ((" "));
+	      PrIP (ip->saddr);
+	      DEBUGP ((":%d", ntohs (udp->source)));
+	      DEBUGP ((" -> "));
+	      PrIP (ip->daddr);
+	      DEBUGP ((":%d", ntohs (udp->dest)));
+	      did = TRUE;
+	    }
+	}
+
+      if (!did)
+	{
+	  DEBUGP ((" ipproto=%d ", ip->protocol));
+	  PrIP (ip->saddr);
+	  DEBUGP ((" -> "));
+	  PrIP (ip->daddr);
+	}
+
+      DEBUGP (("\n"));
+      return;
+    }
+
+  {
+    DEBUGP (("%s ??? src=", prefix));
+    PrMac (eth->src);
+    DEBUGP ((" dest="));
+    PrMac (eth->dest);
+    DEBUGP ((" proto=0x%04x len=%d\n",
+	      (int) ntohs(eth->proto),
+	      len));
+  }
+}
+
+#endif

+ 88 - 0
vsprojects/TapDriver/error.h

@@ -0,0 +1,88 @@
+/*
+ *  TAP-Windows -- A kernel driver to provide virtual tap
+ *                 device functionality on Windows.
+ *
+ *  This code was inspired by the CIPE-Win32 driver by Damion K. Wilson.
+ *
+ *  This source code is Copyright (C) 2002-2010 OpenVPN Technologies, Inc.,
+ *  and is released under the GPL version 2 (see below).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2
+ *  as published by the Free Software Foundation.
+ *
+ *  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 (see the file COPYING included with this
+ *  distribution); if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+//-----------------
+// DEBUGGING OUTPUT
+//-----------------
+
+#define NOTE_ERROR() \
+{ \
+  g_LastErrorFilename = __FILE__; \
+  g_LastErrorLineNumber = __LINE__; \
+}
+
+#if DBG
+
+typedef struct {
+  unsigned int in;
+  unsigned int out;
+  unsigned int capacity;
+  char *text;
+  BOOLEAN error;
+  MUTEX lock;
+} DebugOutput;
+
+VOID MyDebugPrint (const unsigned char* format, ...);
+
+VOID MyAssert (const unsigned char *file, int line);
+
+VOID DumpPacket (const char *prefix,
+		 const unsigned char *data,
+		 unsigned int len);
+
+VOID DumpPacket2 (const char *prefix,
+		  const ETH_HEADER *eth,
+		  const unsigned char *data,
+		  unsigned int len);
+
+#define CAN_WE_PRINT (DEBUGP_AT_DISPATCH || KeGetCurrentIrql () < DISPATCH_LEVEL)
+
+#if ALSO_DBGPRINT
+#define DEBUGP(fmt) { MyDebugPrint fmt; if (CAN_WE_PRINT) DbgPrint fmt; }
+#else
+#define DEBUGP(fmt) { MyDebugPrint fmt; }
+#endif
+
+#define MYASSERT(exp) \
+{ \
+  if (!(exp)) \
+    { \
+      MyAssert(__FILE__, __LINE__); \
+    } \
+}
+
+#define DUMP_PACKET(prefix, data, len) \
+  DumpPacket (prefix, data, len)
+
+#define DUMP_PACKET2(prefix, eth, data, len) \
+  DumpPacket2 (prefix, eth, data, len)
+
+#else 
+
+#define DEBUGP(fmt)
+#define MYASSERT(exp)
+#define DUMP_PACKET(prefix, data, len)
+#define DUMP_PACKET2(prefix, eth, data, len)
+
+#endif

+ 241 - 0
vsprojects/TapDriver/instance.c

@@ -0,0 +1,241 @@
+/*
+ *  TAP-Windows -- A kernel driver to provide virtual tap
+ *                 device functionality on Windows.
+ *
+ *  This code was inspired by the CIPE-Win32 driver by Damion K. Wilson.
+ *
+ *  This source code is Copyright (C) 2002-2010 OpenVPN Technologies, Inc.,
+ *  and is released under the GPL version 2 (see below).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2
+ *  as published by the Free Software Foundation.
+ *
+ *  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 (see the file COPYING included with this
+ *  distribution); if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#define INSTANCE_KEY(a) ((PVOID)((a)->m_Extension.m_TapDevice))
+
+#define N_INSTANCE_BUCKETS 256
+
+typedef struct _INSTANCE {
+  struct _INSTANCE *next;
+  TapAdapterPointer m_Adapter;
+} INSTANCE;
+
+typedef struct {
+  INSTANCE *list;
+  MUTEX lock;
+} INSTANCE_BUCKET;
+
+typedef struct {
+  INSTANCE_BUCKET buckets[N_INSTANCE_BUCKETS];
+} INSTANCE_HASH;
+
+INSTANCE_HASH *g_InstanceHash = NULL;
+
+// must return a hash >= 0 and < N_INSTANCE_BUCKETS
+int
+InstanceHashValue (PVOID addr)
+{
+  UCHAR *p = (UCHAR *) &addr;
+
+  if (sizeof (addr) == 4)
+    return p[0] ^ p[1] ^ p[2] ^ p[3];
+  else if (sizeof (addr) == 8)
+    return p[0] ^ p[1] ^ p[2] ^ p[3] ^ p[4] ^ p[5] ^ p[6] ^ p[7];
+  else
+    {
+      MYASSERT (0);
+    }
+}
+
+BOOLEAN
+InitInstanceList (VOID)
+{
+  MYASSERT (g_InstanceHash == NULL);
+  g_InstanceHash = MemAlloc (sizeof (INSTANCE_HASH), TRUE);
+  if (g_InstanceHash)
+    {
+      int i;
+      for (i = 0; i < N_INSTANCE_BUCKETS; ++i)
+	INIT_MUTEX (&g_InstanceHash->buckets[i].lock);
+      return TRUE;
+    }
+  else
+    return FALSE;
+}
+
+int
+NInstances (VOID)
+{
+  int i, n = 0;
+
+  if (g_InstanceHash)
+    {
+      for (i = 0; i < N_INSTANCE_BUCKETS; ++i)
+	{
+	  BOOLEAN got_lock;
+	  INSTANCE_BUCKET *ib = &g_InstanceHash->buckets[i];
+	  ACQUIRE_MUTEX_ADAPTIVE (&ib->lock, got_lock);
+
+	  if (got_lock)
+	    {
+	      INSTANCE *current;
+	      for (current = ib->list; current != NULL; current = current->next)
+		++n;
+	      RELEASE_MUTEX (&ib->lock);
+	    }
+	  else
+	    return -1;
+	}
+    }
+
+  return n;
+}
+
+int
+InstanceMaxBucketSize (VOID)
+{
+  int i, n = 0;
+
+  if (g_InstanceHash)
+    {
+      for (i = 0; i < N_INSTANCE_BUCKETS; ++i)
+	{
+	  BOOLEAN got_lock;
+	  int bucket_size = 0;
+	  INSTANCE_BUCKET *ib = &g_InstanceHash->buckets[i];
+	  ACQUIRE_MUTEX_ADAPTIVE (&ib->lock, got_lock);
+
+	  if (got_lock)
+	    {
+	      INSTANCE *current;
+	      for (current = ib->list; current != NULL; current = current->next)
+		  ++bucket_size;
+	      if (bucket_size > n)
+		n = bucket_size;
+	      RELEASE_MUTEX (&ib->lock);
+	    }
+	  else
+	    return -1;
+	}
+    }
+
+  return n;
+}
+
+VOID
+FreeInstanceList (VOID)
+{
+  if (g_InstanceHash)
+    {
+      MYASSERT (NInstances() == 0);
+      MemFree (g_InstanceHash, sizeof (INSTANCE_HASH));
+      g_InstanceHash = NULL;
+    }
+}
+
+BOOLEAN
+AddAdapterToInstanceList (TapAdapterPointer p_Adapter)
+{
+  BOOLEAN got_lock;
+  BOOLEAN ret = FALSE;
+  const int hash = InstanceHashValue(INSTANCE_KEY(p_Adapter));
+  INSTANCE_BUCKET *ib = &g_InstanceHash->buckets[hash];
+
+  DEBUGP (("[TAP] AddAdapterToInstanceList hash=%d\n", hash));
+
+  ACQUIRE_MUTEX_ADAPTIVE (&ib->lock, got_lock);
+
+  if (got_lock)
+    {
+      INSTANCE *i = MemAlloc (sizeof (INSTANCE), FALSE);
+      if (i)
+	{
+	  MYASSERT (p_Adapter);
+	  i->m_Adapter = p_Adapter;
+	  i->next = ib->list;
+	  ib->list = i;
+	  ret = TRUE;
+	}
+      RELEASE_MUTEX (&ib->lock);
+    }
+
+  return ret;
+}
+
+BOOLEAN
+RemoveAdapterFromInstanceList (TapAdapterPointer p_Adapter)
+{
+  BOOLEAN got_lock;
+  BOOLEAN ret = FALSE;
+  INSTANCE_BUCKET *ib = &g_InstanceHash->buckets[InstanceHashValue(INSTANCE_KEY(p_Adapter))];
+
+  ACQUIRE_MUTEX_ADAPTIVE (&ib->lock, got_lock);
+
+  if (got_lock)
+    {
+      INSTANCE *current, *prev=NULL;
+      for (current = ib->list; current != NULL; current = current->next)
+	{
+	  if (current->m_Adapter == p_Adapter) // found match
+	    {
+	      if (prev)
+		prev->next = current->next;
+	      else
+		ib->list = current->next;
+	      MemFree (current->m_Adapter, sizeof (TapAdapter));
+	      MemFree (current, sizeof (INSTANCE));
+	      ret = TRUE;
+	      break;
+	    }
+	  prev = current;
+	}
+      RELEASE_MUTEX (&ib->lock);
+    }
+
+  return ret;
+}
+
+TapAdapterPointer
+LookupAdapterInInstanceList (PDEVICE_OBJECT p_DeviceObject)
+{
+  BOOLEAN got_lock;
+  TapAdapterPointer ret = NULL;
+  INSTANCE_BUCKET *ib = &g_InstanceHash->buckets[InstanceHashValue((PVOID)p_DeviceObject)];
+
+  ACQUIRE_MUTEX_ADAPTIVE (&ib->lock, got_lock);
+
+  if (got_lock)
+    {
+      INSTANCE *current, *prev=NULL;
+      for (current = ib->list; current != NULL; current = current->next)
+	{
+	  if (p_DeviceObject == INSTANCE_KEY (current->m_Adapter)) // found match
+	    {
+	      // move it to head of list
+	      if (prev)
+		{
+		  prev->next = current->next;
+		  current->next = ib->list;
+		  ib->list = current;
+		}
+	      ret = ib->list->m_Adapter;
+	      break;
+	    }
+	  prev = current;
+	}
+      RELEASE_MUTEX (&ib->lock);
+    }
+
+  return ret;
+}

+ 75 - 0
vsprojects/TapDriver/lock.h

@@ -0,0 +1,75 @@
+/*
+ *  TAP-Windows -- A kernel driver to provide virtual tap
+ *                 device functionality on Windows.
+ *
+ *  This code was inspired by the CIPE-Win32 driver by Damion K. Wilson.
+ *
+ *  This source code is Copyright (C) 2002-2010 OpenVPN Technologies, Inc.,
+ *  and is released under the GPL version 2 (see below).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2
+ *  as published by the Free Software Foundation.
+ *
+ *  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 (see the file COPYING included with this
+ *  distribution); if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+typedef struct
+{
+  volatile long count;
+} MUTEX;
+
+#define MUTEX_SLEEP_TIME  10000 // microseconds
+
+#define INIT_MUTEX(m) { (m)->count = 0; }
+
+#define ACQUIRE_MUTEX_BLOCKING(m)                         \
+{                                                         \
+    while (NdisInterlockedIncrement (&((m)->count)) != 1) \
+    {                                                     \
+        NdisInterlockedDecrement(&((m)->count));          \
+        NdisMSleep(MUTEX_SLEEP_TIME);                     \
+    }                                                     \
+}
+
+#define RELEASE_MUTEX(m)                                  \
+{                                                         \
+        NdisInterlockedDecrement(&((m)->count));          \
+}
+
+#define ACQUIRE_MUTEX_NONBLOCKING(m, result)              \
+{                                                         \
+    if (NdisInterlockedIncrement (&((m)->count)) != 1)    \
+    {                                                     \
+        NdisInterlockedDecrement(&((m)->count));          \
+        result = FALSE;                                   \
+    }                                                     \
+    else                                                  \
+    {                                                     \
+	result = TRUE;                                    \
+    }                                                     \
+}
+
+#define ACQUIRE_MUTEX_ADAPTIVE(m, result)                 \
+{                                                         \
+    result = TRUE;                                        \
+    while (NdisInterlockedIncrement (&((m)->count)) != 1) \
+    {                                                     \
+        NdisInterlockedDecrement(&((m)->count));          \
+        if (KeGetCurrentIrql () < DISPATCH_LEVEL)         \
+            NdisMSleep(MUTEX_SLEEP_TIME);                 \
+        else                                              \
+        {                                                 \
+	    result = FALSE;                               \
+	    break;                                        \
+        }                                                 \
+    }                                                     \
+}

+ 154 - 0
vsprojects/TapDriver/macinfo.c

@@ -0,0 +1,154 @@
+/*
+ *  TAP-Windows -- A kernel driver to provide virtual tap
+ *                 device functionality on Windows.
+ *
+ *  This code was inspired by the CIPE-Win32 driver by Damion K. Wilson.
+ *
+ *  This source code is Copyright (C) 2002-2010 OpenVPN Technologies, Inc.,
+ *  and is released under the GPL version 2 (see below).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2
+ *  as published by the Free Software Foundation.
+ *
+ *  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 (see the file COPYING included with this
+ *  distribution); if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "macinfo.h"
+
+int
+HexStringToDecimalInt (const int p_Character)
+{
+  int l_Value = 0;
+
+  if (p_Character >= 'A' && p_Character <= 'F')
+    l_Value = (p_Character - 'A') + 10;
+  else if (p_Character >= 'a' && p_Character <= 'f')
+    l_Value = (p_Character - 'a') + 10;
+  else if (p_Character >= '0' && p_Character <= '9')
+    l_Value = p_Character - '0';
+
+  return l_Value;
+}
+
+BOOLEAN
+ParseMAC (MACADDR dest, const char *src)
+{
+  int c;
+  int mac_index = 0;
+  BOOLEAN high_digit = FALSE;
+  int delim_action = 1;
+
+  MYASSERT (src);
+  MYASSERT (dest);
+
+  CLEAR_MAC (dest);
+
+  while (c = *src++)
+    {
+      if (IsMacDelimiter (c))
+	{
+	  mac_index += delim_action;
+	  high_digit = FALSE;
+	  delim_action = 1;
+	}
+      else if (IsHexDigit (c))
+	{
+	  const int digit = HexStringToDecimalInt (c);
+	  if (mac_index < sizeof (MACADDR))
+	    {
+	      if (!high_digit)
+		{
+		  dest[mac_index] = (char)(digit);
+		  high_digit = TRUE;
+		  delim_action = 1;
+		}
+	      else
+		{
+		  dest[mac_index] = (char)(dest[mac_index] * 16 + digit);
+		  ++mac_index;
+		  high_digit = FALSE;
+		  delim_action = 0;
+		}
+	    }
+	  else
+	    return FALSE;
+	}
+      else
+	return FALSE;
+    }
+
+  return (mac_index + delim_action) >= sizeof (MACADDR);
+}
+
+/*
+ * Generate a MAC using the GUID in the adapter name.
+ *
+ * The mac is constructed as 00:FF:xx:xx:xx:xx where
+ * the Xs are taken from the first 32 bits of the GUID in the
+ * adapter name.  This is similar to the Linux 2.4 tap MAC
+ * generator, except linux uses 32 random bits for the Xs.
+ *
+ * In general, this solution is reasonable for most
+ * applications except for very large bridged TAP networks,
+ * where the probability of address collisions becomes more
+ * than infintesimal.
+ *
+ * Using the well-known "birthday paradox", on a 1000 node
+ * network the probability of collision would be
+ * 0.000116292153.  On a 10,000 node network, the probability
+ * of collision would be 0.01157288998621678766.
+ */
+
+VOID GenerateRandomMac (MACADDR mac, const unsigned char *adapter_name)
+{
+  unsigned const char *cp = adapter_name;
+  unsigned char c;
+  unsigned int i = 2;
+  unsigned int byte = 0;
+  int brace = 0;
+  int state = 0;
+
+  CLEAR_MAC (mac);
+
+  mac[0] = 0x00;
+  mac[1] = 0xFF;
+
+  while (c = *cp++)
+    {
+      if (i >= sizeof (MACADDR))
+	break;
+      if (c == '{')
+	brace = 1;
+      if (IsHexDigit (c) && brace)
+	{
+	  const unsigned int digit = HexStringToDecimalInt (c);
+	  if (state)
+	    {
+	      byte <<= 4;
+	      byte |= digit;
+	      mac[i++] = (unsigned char) byte;
+	      state = 0;
+	    }
+	  else
+	    {
+	      byte = digit;
+	      state = 1;
+	    }
+	}
+    }
+}
+
+VOID GenerateRelatedMAC (MACADDR dest, const MACADDR src, const int delta)
+{
+  COPY_MAC (dest, src);
+  dest[2] += (UCHAR) delta;
+}

+ 38 - 0
vsprojects/TapDriver/macinfo.h

@@ -0,0 +1,38 @@
+/*
+ *  TAP-Windows -- A kernel driver to provide virtual tap
+ *                 device functionality on Windows.
+ *
+ *  This code was inspired by the CIPE-Win32 driver by Damion K. Wilson.
+ *
+ *  This source code is Copyright (C) 2002-2010 OpenVPN Technologies, Inc.,
+ *  and is released under the GPL version 2 (see below).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2
+ *  as published by the Free Software Foundation.
+ *
+ *  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 (see the file COPYING included with this
+ *  distribution); if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef MacInfoDefined
+#define MacInfoDefined
+
+//===================================================================================
+//                                      Macros
+//===================================================================================
+#define IsMacDelimiter(a) (a == ':' || a == '-' || a == '.')
+#define IsHexDigit(c) ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'))
+
+#define COPY_MAC(dest, src) NdisMoveMemory ((dest), (src), sizeof (MACADDR))
+#define CLEAR_MAC(dest)     NdisZeroMemory ((dest), sizeof (MACADDR))
+#define MAC_EQUAL(a,b)      (memcmp ((a), (b), sizeof (MACADDR)) == 0)
+
+#endif

+ 186 - 0
vsprojects/TapDriver/mem.c

@@ -0,0 +1,186 @@
+/*
+ *  TAP-Windows -- A kernel driver to provide virtual tap
+ *                 device functionality on Windows.
+ *
+ *  This code was inspired by the CIPE-Win32 driver by Damion K. Wilson.
+ *
+ *  This source code is Copyright (C) 2002-2010 OpenVPN Technologies, Inc.,
+ *  and is released under the GPL version 2 (see below).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2
+ *  as published by the Free Software Foundation.
+ *
+ *  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 (see the file COPYING included with this
+ *  distribution); if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+//------------------
+// Memory Management
+//------------------
+
+PVOID
+MemAlloc (ULONG p_Size, BOOLEAN zero)
+{
+  PVOID l_Return = NULL;
+
+  if (p_Size)
+    {
+      __try
+      {
+	if (NdisAllocateMemoryWithTag (&l_Return, p_Size, 'APAT')
+	    == NDIS_STATUS_SUCCESS)
+	  {
+	    if (zero)
+	      NdisZeroMemory (l_Return, p_Size);
+	  }
+	else
+	  l_Return = NULL;
+      }
+      __except (EXCEPTION_EXECUTE_HANDLER)
+      {
+	l_Return = NULL;
+      }
+    }
+
+  return l_Return;
+}
+
+VOID
+MemFree (PVOID p_Addr, ULONG p_Size)
+{
+  if (p_Addr && p_Size)
+    {
+      __try
+      {
+#if DBG
+	NdisZeroMemory (p_Addr, p_Size);
+#endif
+	NdisFreeMemory (p_Addr, p_Size, 0);
+      }
+      __except (EXCEPTION_EXECUTE_HANDLER)
+      {
+      }
+    }
+}
+
+/*
+ * Circular queue management routines.
+ */
+
+#define QUEUE_BYTE_ALLOCATION(size) \
+  (sizeof (Queue) + (size * sizeof (PVOID)))
+
+#define QUEUE_ADD_INDEX(var, inc) \
+{ \
+  var += inc; \
+  if (var >= q->capacity) \
+    var -= q->capacity; \
+  MYASSERT (var < q->capacity); \
+}
+
+#define QUEUE_SANITY_CHECK() \
+  MYASSERT (q != NULL && q->base < q->capacity && q->size <= q->capacity)
+
+#define QueueCount(q) (q->size)
+
+#define UPDATE_MAX_SIZE() \
+{ \
+  if (q->size > q->max_size) \
+    q->max_size = q->size; \
+}
+
+Queue *
+QueueInit (ULONG capacity)
+{
+  Queue *q;
+
+  MYASSERT (capacity > 0);
+  q = (Queue *) MemAlloc (QUEUE_BYTE_ALLOCATION (capacity), TRUE);
+  if (!q)
+    return NULL;
+
+  q->base = q->size = 0;
+  q->capacity = capacity;
+  q->max_size = 0;
+  return q;
+}
+
+VOID
+QueueFree (Queue *q)
+{
+  if (q)
+    {
+      QUEUE_SANITY_CHECK ();
+      MemFree (q, QUEUE_BYTE_ALLOCATION (q->capacity));
+    }
+}
+
+PVOID
+QueuePush (Queue *q, PVOID item)
+{
+  ULONG dest;
+  QUEUE_SANITY_CHECK ();
+  if (q->size == q->capacity)
+    return NULL;
+  dest = q->base;
+  QUEUE_ADD_INDEX (dest, q->size);
+  q->data[dest] = item;
+  ++q->size;
+  UPDATE_MAX_SIZE();
+  return item;
+}
+
+PVOID
+QueuePop (Queue *q)
+{
+  ULONG oldbase;
+  QUEUE_SANITY_CHECK ();
+  if (!q->size)
+    return NULL;
+  oldbase = q->base;
+  QUEUE_ADD_INDEX (q->base, 1);
+  --q->size;
+  UPDATE_MAX_SIZE();
+  return q->data[oldbase];
+}
+
+PVOID
+QueueExtract (Queue *q, PVOID item)
+{
+  ULONG src, dest, count, n;
+  QUEUE_SANITY_CHECK ();
+  n = 0;
+  src = dest = q->base;
+  count = q->size;
+  while (count--)
+    {
+      if (item == q->data[src])
+	{
+	  ++n;
+	  --q->size;
+	}
+      else
+	{
+	  q->data[dest] = q->data[src];
+	  QUEUE_ADD_INDEX (dest, 1);	  
+	}
+      QUEUE_ADD_INDEX (src, 1);
+    }
+  if (n)
+    return item;
+  else
+    return NULL;
+}
+
+#undef QUEUE_BYTE_ALLOCATION
+#undef QUEUE_ADD_INDEX
+#undef QUEUE_SANITY_CHECK
+#undef UPDATE_MAX_SIZE

+ 187 - 0
vsprojects/TapDriver/original-inf.txt

@@ -0,0 +1,187 @@
+; ****************************************************************************
+; * Copyright (C) 2002-2010 OpenVPN Technologies, Inc.                            *
+; *  This program is free software; you can redistribute it and/or modify    *
+; *  it under the terms of the GNU General Public License version 2          *
+; *  as published by the Free Software Foundation.                           *
+; ****************************************************************************
+
+; SYNTAX CHECKER
+; cd \WINDDK\3790\tools\chkinf
+; chkinf c:\src\openvpn\tap-win32\i386\oemwin2k.inf
+; OUTPUT -> file:///c:/WINDDK/3790/tools/chkinf/htm/c%23+src+openvpn+tap-win32+i386+__OemWin2k.htm
+
+; INSTALL/REMOVE DRIVER
+;   tapinstall install OemWin2k.inf TAP0901
+;   tapinstall update OemWin2k.inf TAP0901
+;   tapinstall remove TAP0901
+
+;*********************************************************
+; Note to Developers:
+;
+; If you are bundling the TAP-Windows driver with your app,
+; you should try to rename it in such a way that it will
+; not collide with other instances of TAP-Windows defined
+; by other apps.  Multiple versions of the TAP-Windows
+; driver, each installed by different apps, can coexist
+; on the same machine if you follow these guidelines.
+; NOTE: these instructions assume you are editing the
+; generated OemWin2k.inf file, not the source
+; OemWin2k.inf.in file which is preprocessed by winconfig
+; and uses macro definitions from settings.in.
+;
+; (1) Rename all tapXXXX instances in this file to
+;     something different (use at least 5 characters
+;     for this name!)
+; (2) Change the "!define TAP" definition in openvpn.nsi
+;     to match what you changed tapXXXX to.
+; (3) Change TARGETNAME in SOURCES to match what you
+;     changed tapXXXX to.
+; (4) Change TAP_COMPONENT_ID in common.h to match what
+;     you changed tapXXXX to.
+; (5) Change SZDEPENDENCIES in service.h to match what
+;     you changed tapXXXX to.
+; (6) Change DeviceDescription and Provider strings.
+; (7) Change PRODUCT_TAP_WIN_DEVICE_DESCRIPTION in constants.h to what you
+;     set DeviceDescription to.
+;
+;*********************************************************
+
+[Version]
+   Signature = "$Windows NT$"
+   CatalogFile = @[email protected]
+   ClassGUID = {4d36e972-e325-11ce-bfc1-08002be10318}
+   Provider = %Provider%
+   Class = Net
+
+; This version number should match the version
+; number given in SOURCES.
+   DriverVer=@PRODUCT_TAP_WIN_RELDATE@,@[email protected].@PRODUCT_TAP_WIN_MINOR@
+
+[Strings]
+   DeviceDescription = "@PRODUCT_TAP_WIN_DEVICE_DESCRIPTION@"
+   Provider = "@PRODUCT_TAP_WIN_PROVIDER@"
+
+;----------------------------------------------------------------
+;                      Manufacturer + Product Section (Done)
+;----------------------------------------------------------------
+[Manufacturer]
+   %Provider% = @PRODUCT_TAP_WIN_COMPONENT_ID@@INF_PROVIDER_SUFFIX@
+
+[@PRODUCT_TAP_WIN_COMPONENT_ID@@INF_SECTION_SUFFIX@]
+   %DeviceDescription% = @[email protected], @PRODUCT_TAP_WIN_COMPONENT_ID@
+
+;---------------------------------------------------------------
+;                             Driver Section (Done)
+;---------------------------------------------------------------
+
+;----------------- Characteristics ------------
+;    NCF_PHYSICAL = 0x04
+;    NCF_VIRTUAL = 0x01
+;    NCF_SOFTWARE_ENUMERATED = 0x02
+;    NCF_HIDDEN = 0x08
+;    NCF_NO_SERVICE = 0x10
+;    NCF_HAS_UI = 0x80
+;----------------- Characteristics ------------
+
+[@[email protected]]
+   CopyFiles       = @[email protected], @[email protected]
+   AddReg          = @[email protected]
+   AddReg          = @[email protected]
+   Characteristics = @PRODUCT_TAP_WIN_CHARACTERISTICS@
+
+[@[email protected]]
+   AddService = @PRODUCT_TAP_WIN_COMPONENT_ID@,        2, @[email protected]
+
+[@[email protected]]
+   HKR, Ndi,            Service,      0, "@PRODUCT_TAP_WIN_COMPONENT_ID@"
+   HKR, Ndi\Interfaces, UpperRange,   0, "ndis5"
+   HKR, Ndi\Interfaces, LowerRange,   0, "ethernet"
+   HKR, ,               Manufacturer, 0, "%Provider%"
+   HKR, ,               ProductName,  0, "%DeviceDescription%"
+
+[@[email protected]]
+   HKR, Ndi\params\MTU,                  ParamDesc, 0, "MTU"
+   HKR, Ndi\params\MTU,                  Type,      0, "int"
+   HKR, Ndi\params\MTU,                  Default,   0, "1500"
+   HKR, Ndi\params\MTU,                  Optional,  0, "0"
+   HKR, Ndi\params\MTU,                  Min,       0, "100"
+   HKR, Ndi\params\MTU,                  Max,       0, "1500"
+   HKR, Ndi\params\MTU,                  Step,      0, "1"
+   HKR, Ndi\params\MediaStatus,          ParamDesc, 0, "Media Status"
+   HKR, Ndi\params\MediaStatus,          Type,      0, "enum"
+   HKR, Ndi\params\MediaStatus,          Default,   0, "0"
+   HKR, Ndi\params\MediaStatus,          Optional,  0, "0"
+   HKR, Ndi\params\MediaStatus\enum,     "0",       0, "Application Controlled"
+   HKR, Ndi\params\MediaStatus\enum,     "1",       0, "Always Connected"
+   HKR, Ndi\params\MAC,                  ParamDesc, 0, "MAC Address"
+   HKR, Ndi\params\MAC,                  Type,      0, "edit"
+   HKR, Ndi\params\MAC,                  Optional,  0, "1"
+   HKR, Ndi\params\AllowNonAdmin,        ParamDesc, 0, "Non-Admin Access"
+   HKR, Ndi\params\AllowNonAdmin,        Type,      0, "enum"
+   HKR, Ndi\params\AllowNonAdmin,        Default,   0, "1"
+   HKR, Ndi\params\AllowNonAdmin,        Optional,  0, "0"
+   HKR, Ndi\params\AllowNonAdmin\enum,   "0",       0, "Not Allowed"
+   HKR, Ndi\params\AllowNonAdmin\enum,   "1",       0, "Allowed"
+
+;----------------------------------------------------------------
+;                             Service Section
+;----------------------------------------------------------------
+
+;---------- Service Type -------------
+;    SERVICE_KERNEL_DRIVER     = 0x01
+;    SERVICE_WIN32_OWN_PROCESS = 0x10
+;---------- Service Type -------------
+
+;---------- Start Mode ---------------
+;    SERVICE_BOOT_START   = 0x0
+;    SERVICE_SYSTEM_START = 0x1
+;    SERVICE_AUTO_START   = 0x2
+;    SERVICE_DEMAND_START = 0x3
+;    SERVICE_DISABLED     = 0x4
+;---------- Start Mode ---------------
+
+[@[email protected]]
+   DisplayName = %DeviceDescription%
+   ServiceType = 1
+   StartType = 3
+   ErrorControl = 1
+   LoadOrderGroup = NDIS
+   ServiceBinary = %12%\@[email protected]
+
+;-----------------------------------------------------------------
+;                                File Installation
+;-----------------------------------------------------------------
+
+;----------------- Copy Flags ------------
+;    COPYFLG_NOSKIP = 0x02
+;    COPYFLG_NOVERSIONCHECK = 0x04
+;----------------- Copy Flags ------------
+
+; SourceDisksNames
+; diskid = description[, [tagfile] [, <unused>, subdir]]
+; 1 = "Intel Driver Disk 1",e100bex.sys,,
+
+[SourceDisksNames]
+   1 = %DeviceDescription%, @[email protected]
+
+; SourceDisksFiles
+; filename_on_source = diskID[, [subdir][, size]]
+; e100bex.sys = 1,, ; on distribution disk 1
+
+[SourceDisksFiles]
+@[email protected] = 1
+
+[DestinationDirs]
+   @[email protected]  = 11
+   @[email protected] = 12
+
+[@[email protected]]
+;   TapPanel.cpl,,,6   ; COPYFLG_NOSKIP | COPYFLG_NOVERSIONCHECK
+;   cipsrvr.exe,,,6     ; COPYFLG_NOSKIP | COPYFLG_NOVERSIONCHECK
+
+[@[email protected]]
+   @[email protected],,,6     ; COPYFLG_NOSKIP | COPYFLG_NOVERSIONCHECK
+
+;---------------------------------------------------------------
+;                                      End
+;---------------------------------------------------------------

+ 224 - 0
vsprojects/TapDriver/proto.h

@@ -0,0 +1,224 @@
+/*
+ *  TAP-Windows -- A kernel driver to provide virtual tap
+ *                 device functionality on Windows.
+ *
+ *  This code was inspired by the CIPE-Win32 driver by Damion K. Wilson.
+ *
+ *  This source code is Copyright (C) 2002-2010 OpenVPN Technologies, Inc.,
+ *  and is released under the GPL version 2 (see below).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2
+ *  as published by the Free Software Foundation.
+ *
+ *  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 (see the file COPYING included with this
+ *  distribution); if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+//============================================================
+// MAC address, Ethernet header, and ARP
+//============================================================
+
+#pragma pack(1)
+
+#define IP_HEADER_SIZE 20
+#define IPV6_HEADER_SIZE 40
+
+typedef unsigned char MACADDR [6];
+typedef unsigned long IPADDR;
+typedef unsigned char IPV6ADDR [16];
+
+//-----------------
+// Ethernet address
+//-----------------
+
+typedef struct {
+  MACADDR addr;
+} ETH_ADDR;
+
+typedef struct {
+  ETH_ADDR list[NIC_MAX_MCAST_LIST];
+} MC_LIST;
+
+//----------------
+// Ethernet header
+//----------------
+
+typedef struct
+{
+  MACADDR dest;               /* destination eth addr	*/
+  MACADDR src;                /* source ether addr	*/
+
+# define ETH_P_IP   0x0800    /* IPv4 protocol */
+# define ETH_P_IPV6 0x86DD    /* IPv6 protocol */
+# define ETH_P_ARP  0x0806    /* ARP protocol */
+  USHORT proto;               /* packet type ID field	*/
+} ETH_HEADER, *PETH_HEADER;
+
+//----------------
+// ARP packet
+//----------------
+
+typedef struct
+   {
+    MACADDR        m_MAC_Destination;        // Reverse these two
+    MACADDR        m_MAC_Source;             // to answer ARP requests
+    USHORT         m_Proto;                  // 0x0806
+
+#   define MAC_ADDR_TYPE 0x0001
+    USHORT         m_MAC_AddressType;        // 0x0001
+
+    USHORT         m_PROTO_AddressType;      // 0x0800
+    UCHAR          m_MAC_AddressSize;        // 0x06
+    UCHAR          m_PROTO_AddressSize;      // 0x04
+
+#   define ARP_REQUEST 0x0001
+#   define ARP_REPLY   0x0002
+    USHORT         m_ARP_Operation;          // 0x0001 for ARP request, 0x0002 for ARP reply
+
+    MACADDR        m_ARP_MAC_Source;
+    IPADDR         m_ARP_IP_Source;
+    MACADDR        m_ARP_MAC_Destination;
+    IPADDR         m_ARP_IP_Destination;
+   }
+ARP_PACKET, *PARP_PACKET;
+
+//----------
+// IP Header
+//----------
+
+typedef struct {
+# define IPH_GET_VER(v) (((v) >> 4) & 0x0F)
+# define IPH_GET_LEN(v) (((v) & 0x0F) << 2)
+  UCHAR    version_len;
+
+  UCHAR    tos;
+  USHORT   tot_len;
+  USHORT   id;
+
+# define IP_OFFMASK 0x1fff
+  USHORT   frag_off;
+
+  UCHAR    ttl;
+
+# define IPPROTO_UDP  17  /* UDP protocol */
+# define IPPROTO_TCP   6  /* TCP protocol */
+# define IPPROTO_ICMP  1  /* ICMP protocol */
+# define IPPROTO_IGMP  2  /* IGMP protocol */
+  UCHAR    protocol;
+
+  USHORT   check;
+  ULONG    saddr;
+  ULONG    daddr;
+  /* The options start here. */
+} IPHDR;
+
+//-----------
+// UDP header
+//-----------
+
+typedef struct {
+  USHORT   source;
+  USHORT   dest;
+  USHORT   len;
+  USHORT   check;
+} UDPHDR;
+
+//--------------------------
+// TCP header, per RFC 793.
+//--------------------------
+
+typedef struct {
+  USHORT      source;    /* source port */
+  USHORT      dest;      /* destination port */
+  ULONG       seq;       /* sequence number */
+  ULONG       ack_seq;   /* acknowledgement number */
+
+# define TCPH_GET_DOFF(d) (((d) & 0xF0) >> 2)
+  UCHAR       doff_res;
+
+# define TCPH_FIN_MASK (1<<0)
+# define TCPH_SYN_MASK (1<<1)
+# define TCPH_RST_MASK (1<<2)
+# define TCPH_PSH_MASK (1<<3)
+# define TCPH_ACK_MASK (1<<4)
+# define TCPH_URG_MASK (1<<5)
+# define TCPH_ECE_MASK (1<<6)
+# define TCPH_CWR_MASK (1<<7)
+  UCHAR       flags;
+
+  USHORT      window;
+  USHORT      check;
+  USHORT      urg_ptr;
+} TCPHDR;
+
+#define	TCPOPT_EOL     0
+#define	TCPOPT_NOP     1
+#define	TCPOPT_MAXSEG  2
+#define TCPOLEN_MAXSEG 4
+
+//------------
+// IPv6 Header
+//------------
+
+typedef struct {
+  UCHAR    version_prio;
+  UCHAR    flow_lbl[3];
+  USHORT   payload_len;
+# define IPPROTO_ICMPV6  0x3a  /* ICMP protocol v6 */
+  UCHAR    nexthdr;
+  UCHAR    hop_limit;
+  IPV6ADDR saddr;
+  IPV6ADDR daddr;
+} IPV6HDR;
+
+//--------------------------------------------
+// IPCMPv6 NS/NA Packets (RFC4443 and RFC4861)
+//--------------------------------------------
+
+// Neighbor Solictiation - RFC 4861, 4.3
+// (this is just the ICMPv6 part of the packet)
+typedef struct {
+  UCHAR    type;
+# define ICMPV6_TYPE_NS	135		// neighbour solicitation
+  UCHAR    code;
+# define ICMPV6_CODE_0	0		// no specific sub-code for NS/NA
+  USHORT   checksum;
+  ULONG    reserved;
+  IPV6ADDR target_addr;
+} ICMPV6_NS;
+
+// Neighbor Advertisement - RFC 4861, 4.4 + 4.6/4.6.1
+// (this is just the ICMPv6 payload)
+typedef struct {
+  UCHAR    type;
+# define ICMPV6_TYPE_NA	136		// neighbour advertisement
+  UCHAR    code;
+# define ICMPV6_CODE_0	0		// no specific sub-code for NS/NA
+  USHORT   checksum;
+  UCHAR    rso_bits;			// Router(0), Solicited(2), Ovrrd(4)
+  UCHAR	   reserved[3];
+  IPV6ADDR target_addr;
+// always include "Target Link-layer Address" option (RFC 4861 4.6.1)
+  UCHAR    opt_type;
+#define ICMPV6_OPTION_TLLA 2
+  UCHAR    opt_length;
+#define ICMPV6_LENGTH_TLLA 1		// multiplied by 8 -> 1 = 8 bytes
+  MACADDR  target_macaddr;
+} ICMPV6_NA;
+
+// this is the complete packet with Ethernet and IPv6 headers
+typedef struct {
+  ETH_HEADER eth;
+  IPV6HDR    ipv6;
+  ICMPV6_NA  icmpv6;
+} ICMPV6_NA_PKT;
+
+#pragma pack()

+ 260 - 0
vsprojects/TapDriver/prototypes.h

@@ -0,0 +1,260 @@
+/*
+ *  TAP-Windows -- A kernel driver to provide virtual tap
+ *                 device functionality on Windows.
+ *
+ *  This code was inspired by the CIPE-Win32 driver by Damion K. Wilson.
+ *
+ *  This source code is Copyright (C) 2002-2010 OpenVPN Technologies, Inc.,
+ *  and is released under the GPL version 2 (see below).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2
+ *  as published by the Free Software Foundation.
+ *
+ *  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 (see the file COPYING included with this
+ *  distribution); if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef TAP_PROTOTYPES_DEFINED
+#define TAP_PROTOTYPES_DEFINED
+
+NTSTATUS DriverEntry
+   (
+    IN PDRIVER_OBJECT p_DriverObject,
+    IN PUNICODE_STRING p_RegistryPath
+   );
+
+VOID TapDriverUnload
+   (
+    IN PDRIVER_OBJECT p_DriverObject
+   );
+
+NDIS_STATUS AdapterCreate
+   (
+    OUT PNDIS_STATUS p_ErrorStatus,
+    OUT PUINT p_MediaIndex,
+    IN PNDIS_MEDIUM p_Media,
+    IN UINT p_MediaCount,
+    IN NDIS_HANDLE p_AdapterHandle,
+    IN NDIS_HANDLE p_ConfigurationHandle
+   );
+
+VOID AdapterHalt
+   (
+    IN NDIS_HANDLE p_AdapterContext
+   );
+
+VOID AdapterFreeResources
+   (
+    TapAdapterPointer p_Adapter
+   );
+
+NDIS_STATUS AdapterReset
+   (
+    OUT PBOOLEAN p_AddressingReset,
+    IN NDIS_HANDLE p_AdapterContext
+   );
+
+NDIS_STATUS AdapterQuery
+   (
+    IN NDIS_HANDLE p_AdapterContext,
+    IN NDIS_OID p_OID,
+    IN PVOID p_Buffer,
+    IN ULONG p_BufferLength,
+    OUT PULONG p_BytesWritten,
+    OUT PULONG p_BytesNeeded
+   );
+
+NDIS_STATUS AdapterModify
+   (
+    IN NDIS_HANDLE p_AdapterContext,
+    IN NDIS_OID p_OID,
+    IN PVOID p_Buffer,
+    IN ULONG p_BufferLength,
+    OUT PULONG p_BytesRead,
+    OUT PULONG p_BytesNeeded
+   );
+
+NDIS_STATUS AdapterTransmit
+   (
+    IN NDIS_HANDLE p_AdapterContext,
+    IN PNDIS_PACKET p_Packet,
+    IN UINT p_Flags
+   );
+
+NDIS_STATUS AdapterReceive
+   (
+    OUT PNDIS_PACKET p_Packet,
+    OUT PUINT p_Transferred,
+    IN NDIS_HANDLE p_AdapterContext,
+    IN NDIS_HANDLE p_ReceiveContext,
+    IN UINT p_Offset,
+    IN UINT p_ToTransfer
+   );
+
+NTSTATUS TapDeviceHook
+   (
+    IN PDEVICE_OBJECT p_DeviceObject,
+    IN PIRP p_IRP
+   );
+
+NDIS_STATUS CreateTapDevice
+   (
+    TapExtensionPointer p_Extension,
+    const char *p_Name
+   );
+
+VOID DestroyTapDevice
+   (
+    TapExtensionPointer p_Extension
+   );
+
+VOID TapDeviceFreeResources
+   (
+    TapExtensionPointer p_Extension
+    );
+
+NTSTATUS CompleteIRP
+   (
+    IN PIRP p_IRP,
+    IN TapPacketPointer p_PacketBuffer,
+    IN CCHAR PriorityBoost
+   );
+
+VOID CancelIRPCallback
+   (
+    IN PDEVICE_OBJECT p_DeviceObject,
+    IN PIRP p_IRP
+   );
+
+VOID CancelIRP
+   (
+    TapExtensionPointer p_Extension,
+    IN PIRP p_IRP,
+    BOOLEAN callback
+   );
+
+VOID FlushQueues
+   (
+    TapExtensionPointer p_Extension
+   );
+
+VOID ResetTapAdapterState
+   (
+    TapAdapterPointer p_Adapter
+   );
+
+BOOLEAN ProcessARP
+   (
+    TapAdapterPointer p_Adapter,
+    const PARP_PACKET src,
+    const IPADDR adapter_ip,
+    const IPADDR ip_network,
+    const IPADDR ip_netmask,
+    const MACADDR mac
+   );
+
+VOID SetMediaStatus
+   (
+    TapAdapterPointer p_Adapter,
+    BOOLEAN state
+   );
+
+VOID InjectPacketDeferred
+   (
+    TapAdapterPointer p_Adapter,
+    UCHAR *packet,
+    const unsigned int len
+   );
+
+VOID InjectPacketNow
+   (
+    TapAdapterPointer p_Adapter,
+    UCHAR *packet,
+    const unsigned int len
+   );
+
+// for KDEFERRED_ROUTINE and Static Driver Verifier
+//#include <wdm.h>
+//KDEFERRED_ROUTINE InjectPacketDpc;
+
+VOID InjectPacketDpc
+   (
+    KDPC *Dpc,
+    PVOID DeferredContext,
+    PVOID SystemArgument1,
+    PVOID SystemArgument2
+    );
+
+VOID CheckIfDhcpAndTunMode
+   (
+    TapAdapterPointer p_Adapter
+   );
+
+VOID HookDispatchFunctions();
+
+#if ENABLE_NONADMIN
+
+#if defined(DDKVER_MAJOR) && DDKVER_MAJOR < 5600
+/*
+ * Better solution for use on Vista DDK, but possibly not compatible with
+ * earlier DDKs:
+ *
+ * Eliminate the definition of SECURITY_DESCRIPTOR (and even ZwSetSecurityObject),
+ * and at the top of tapdrv.c change:
+ *
+ * #include <ndis.h>
+ * #include <ntstrsafe.h>
+ * #include <ntddk.h>
+ *
+ * To
+ *
+ * #include <ntifs.h>
+ * #include <ndis.h>
+ * #include <ntstrsafe.h>
+ */
+typedef struct _SECURITY_DESCRIPTOR {
+  unsigned char opaque[64];
+} SECURITY_DESCRIPTOR;
+
+NTSYSAPI
+NTSTATUS
+NTAPI
+ZwSetSecurityObject (
+  IN HANDLE  Handle,
+  IN SECURITY_INFORMATION  SecurityInformation,
+  IN PSECURITY_DESCRIPTOR  SecurityDescriptor);
+
+#endif
+
+VOID AllowNonAdmin (TapExtensionPointer p_Extension);
+
+#endif
+
+struct WIN2K_NDIS_MINIPORT_BLOCK
+{
+  unsigned char  opaque[16];
+  UNICODE_STRING MiniportName;       // how mini-port refers to us
+};
+
+#if PACKET_TRUNCATION_CHECK
+
+VOID IPv4PacketSizeVerify
+   (
+    const UCHAR *data,
+    ULONG length,
+    BOOLEAN tun,
+    const char *prefix,
+    LONG *counter
+   );
+
+#endif
+
+#endif

+ 74 - 0
vsprojects/TapDriver/tap-windows.h

@@ -0,0 +1,74 @@
+/*
+ *  TAP-Windows -- A kernel driver to provide virtual tap
+ *                 device functionality on Windows.
+ *
+ *  This code was inspired by the CIPE-Win32 driver by Damion K. Wilson.
+ *
+ *  This source code is Copyright (C) 2002-2010 OpenVPN Technologies, Inc.,
+ *  and is released under the GPL version 2 (see below).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2
+ *  as published by the Free Software Foundation.
+ *
+ *  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 (see the file COPYING included with this
+ *  distribution); if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __TAP_WIN_H
+#define __TAP_WIN_H
+
+/*
+ * =============
+ * TAP IOCTLs
+ * =============
+ */
+
+#define TAP_WIN_CONTROL_CODE(request,method) \
+  CTL_CODE (FILE_DEVICE_UNKNOWN, request, method, FILE_ANY_ACCESS)
+
+/* Present in 8.1 */
+
+#define TAP_WIN_IOCTL_GET_MAC               TAP_WIN_CONTROL_CODE (1, METHOD_BUFFERED)
+#define TAP_WIN_IOCTL_GET_VERSION           TAP_WIN_CONTROL_CODE (2, METHOD_BUFFERED)
+#define TAP_WIN_IOCTL_GET_MTU               TAP_WIN_CONTROL_CODE (3, METHOD_BUFFERED)
+#define TAP_WIN_IOCTL_GET_INFO              TAP_WIN_CONTROL_CODE (4, METHOD_BUFFERED)
+#define TAP_WIN_IOCTL_CONFIG_POINT_TO_POINT TAP_WIN_CONTROL_CODE (5, METHOD_BUFFERED)
+#define TAP_WIN_IOCTL_SET_MEDIA_STATUS      TAP_WIN_CONTROL_CODE (6, METHOD_BUFFERED)
+#define TAP_WIN_IOCTL_CONFIG_DHCP_MASQ      TAP_WIN_CONTROL_CODE (7, METHOD_BUFFERED)
+#define TAP_WIN_IOCTL_GET_LOG_LINE          TAP_WIN_CONTROL_CODE (8, METHOD_BUFFERED)
+#define TAP_WIN_IOCTL_CONFIG_DHCP_SET_OPT   TAP_WIN_CONTROL_CODE (9, METHOD_BUFFERED)
+
+/* Added in 8.2 */
+
+/* obsoletes TAP_WIN_IOCTL_CONFIG_POINT_TO_POINT */
+#define TAP_WIN_IOCTL_CONFIG_TUN            TAP_WIN_CONTROL_CODE (10, METHOD_BUFFERED)
+
+/*
+ * =================
+ * Registry keys
+ * =================
+ */
+
+#define ADAPTER_KEY "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
+
+#define NETWORK_CONNECTIONS_KEY "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
+
+/*
+ * ======================
+ * Filesystem prefixes
+ * ======================
+ */
+
+#define USERMODEDEVICEDIR "\\\\.\\Global\\"
+#define SYSDEVICEDIR      "\\Device\\"
+#define USERDEVICEDIR     "\\DosDevices\\Global\\"
+#define TAP_WIN_SUFFIX    ".tap"
+
+#endif

+ 3147 - 0
vsprojects/TapDriver/tapdrvr.c

@@ -0,0 +1,3147 @@
+/*
+ *  TAP-Windows -- A kernel driver to provide virtual tap
+ *                 device functionality on Windows.
+ *
+ *  This code was inspired by the CIPE-Win32 driver by Damion K. Wilson.
+ *
+ *  This source code is Copyright (C) 2002-2010 OpenVPN Technologies, Inc.,
+ *  and is released under the GPL version 2 (see below).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2
+ *  as published by the Free Software Foundation.
+ *
+ *  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 (see the file COPYING included with this
+ *  distribution); if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+//======================================================
+// This driver is designed to work on Win 2000 or higher
+// versions of Windows.
+//
+// It is SMP-safe and handles NDIS 5 power management.
+//
+// By default we operate as a "tap" virtual ethernet
+// 802.3 interface, but we can emulate a "tun"
+// interface (point-to-point IPv4) through the
+// TAP_WIN_IOCTL_CONFIG_POINT_TO_POINT or
+// TAP_WIN_IOCTL_CONFIG_TUN ioctl.
+//======================================================
+
+#include "tap-windows.h"
+#include "config.h"
+
+#define NDIS_MINIPORT_DRIVER
+#define BINARY_COMPATIBLE 0
+#define NDIS50_MINIPORT 1
+#define NDIS_WDM 0
+#define NDIS50 1
+#define NTSTRSAFE_LIB
+
+// Debug info output
+#define ALSO_DBGPRINT           1
+#define DEBUGP_AT_DISPATCH      0
+
+//========================================================
+// Check for truncated IPv4 packets, log errors if found.
+//========================================================
+#define PACKET_TRUNCATION_CHECK 0
+
+//========================================================
+// EXPERIMENTAL -- Configure TAP device object to be
+// accessible from non-administrative accounts, based
+// on an advanced properties setting.
+//
+// Duplicates the functionality of OpenVPN's
+// --allow-nonadmin directive.
+//========================================================
+#define ENABLE_NONADMIN 1
+
+#if defined(DDKVER_MAJOR) && DDKVER_MAJOR < 5600
+#include <ndis.h>
+#include <ntstrsafe.h>
+#include <ntddk.h>
+#else
+#include <ntifs.h>
+#include <ndis.h>
+#include <ntstrsafe.h>
+#endif
+
+#include "lock.h"
+#include "constants.h"
+#include "proto.h"
+#include "error.h"
+#include "endian.h"
+#include "dhcp.h"
+#include "types.h"
+#include "prototypes.h"
+
+#include "mem.c"
+#include "macinfo.c"
+#include "error.c"
+#include "dhcp.c"
+#include "instance.c"
+
+#define IS_UP(ta) \
+  ((ta)->m_InterfaceIsRunning && (ta)->m_Extension.m_TapIsRunning)
+
+#define INCREMENT_STAT(s) ++(s)
+
+#define NAME_BUFFER_SIZE 80
+
+//========================================================
+//                            Globals
+//========================================================
+
+NDIS_HANDLE g_NdisWrapperHandle;
+
+const UINT g_SupportedOIDList[] = {
+  OID_GEN_HARDWARE_STATUS,
+  OID_GEN_MEDIA_SUPPORTED,
+  OID_GEN_MEDIA_IN_USE,
+  OID_GEN_MAXIMUM_LOOKAHEAD,
+  OID_GEN_MAC_OPTIONS,
+  OID_GEN_LINK_SPEED,
+  OID_GEN_TRANSMIT_BLOCK_SIZE,
+  OID_GEN_RECEIVE_BLOCK_SIZE,
+  OID_GEN_VENDOR_DESCRIPTION,
+  OID_GEN_DRIVER_VERSION,
+  OID_GEN_XMIT_OK,
+  OID_GEN_RCV_OK,
+  OID_GEN_XMIT_ERROR,
+  OID_GEN_RCV_ERROR,
+  OID_802_3_PERMANENT_ADDRESS,
+  OID_802_3_CURRENT_ADDRESS,
+  OID_GEN_RCV_NO_BUFFER,
+  OID_802_3_RCV_ERROR_ALIGNMENT,
+  OID_802_3_XMIT_ONE_COLLISION,
+  OID_802_3_XMIT_MORE_COLLISIONS,
+  OID_802_3_MULTICAST_LIST,
+  OID_802_3_MAXIMUM_LIST_SIZE,
+  OID_GEN_VENDOR_ID,
+  OID_GEN_CURRENT_LOOKAHEAD,
+  OID_GEN_CURRENT_PACKET_FILTER,
+  OID_GEN_PROTOCOL_OPTIONS,
+  OID_GEN_MAXIMUM_TOTAL_SIZE,
+  OID_GEN_TRANSMIT_BUFFER_SPACE,
+  OID_GEN_RECEIVE_BUFFER_SPACE,
+  OID_GEN_MAXIMUM_FRAME_SIZE,
+  OID_GEN_VENDOR_DRIVER_VERSION,
+  OID_GEN_MAXIMUM_SEND_PACKETS,
+  OID_GEN_MEDIA_CONNECT_STATUS,
+  OID_GEN_SUPPORTED_LIST
+};
+
+//============================================================
+//                         Driver Entry
+//============================================================
+#pragma NDIS_INIT_FUNCTION (DriverEntry)
+
+DRIVER_INITIALIZE DriverEntry;
+NTSTATUS
+DriverEntry (IN PDRIVER_OBJECT p_DriverObject,
+	     IN PUNICODE_STRING p_RegistryPath)
+{
+  NDIS_STATUS l_Status = NDIS_STATUS_FAILURE;
+  NDIS_MINIPORT_CHARACTERISTICS *l_Properties = NULL;
+
+  //========================================================
+  // Notify NDIS that a new miniport driver is initializing.
+  //========================================================
+
+  NdisMInitializeWrapper (&g_NdisWrapperHandle,
+			  p_DriverObject,
+			  p_RegistryPath, NULL);
+
+  //======================
+  // Global initialization
+  //======================
+
+#if DBG
+  MyDebugInit (10000); // Allocate debugging text space
+#endif
+
+  if (!InitInstanceList ())
+    {
+      DEBUGP (("[TAP] Allocation failed for adapter instance list\n"));
+      goto cleanup;
+    }
+
+  //=======================================
+  // Set and register miniport entry points
+  //=======================================
+
+  l_Properties = MemAlloc (sizeof (NDIS_MINIPORT_CHARACTERISTICS), TRUE);
+
+  if (l_Properties == NULL)
+    {
+      DEBUGP (("[TAP] Allocation failed for miniport entry points\n"));
+      goto cleanup;
+    }
+
+  l_Properties->MajorNdisVersion = TAP_NDIS_MAJOR_VERSION;
+  l_Properties->MinorNdisVersion = TAP_NDIS_MINOR_VERSION;
+  l_Properties->InitializeHandler = AdapterCreate;
+  l_Properties->HaltHandler = AdapterHalt;
+  l_Properties->ResetHandler = AdapterReset;               /* DISPATCH_LEVEL */
+  l_Properties->TransferDataHandler = AdapterReceive;      /* DISPATCH_LEVEL */
+  l_Properties->SendHandler = AdapterTransmit;             /* DISPATCH_LEVEL */
+  l_Properties->QueryInformationHandler = AdapterQuery;    /* DISPATCH_LEVEL */
+  l_Properties->SetInformationHandler = AdapterModify;     /* DISPATCH_LEVEL */
+
+  switch (l_Status =
+	  NdisMRegisterMiniport (g_NdisWrapperHandle, l_Properties,
+				 sizeof (NDIS_MINIPORT_CHARACTERISTICS)))
+    {
+    case NDIS_STATUS_SUCCESS:
+      {
+	DEBUGP (("[TAP] version [%d.%d] %s %s registered miniport successfully\n",
+		 TAP_DRIVER_MAJOR_VERSION,
+		 TAP_DRIVER_MINOR_VERSION,
+		 __DATE__,
+		 __TIME__));
+	DEBUGP (("Registry Path: '%.*S'\n", p_RegistryPath->Length/2, p_RegistryPath->Buffer));
+	break;
+      }
+
+    case NDIS_STATUS_BAD_CHARACTERISTICS:
+      {
+	DEBUGP (("[TAP] Miniport characteristics were badly defined\n"));
+	NdisTerminateWrapper (g_NdisWrapperHandle, NULL);
+	break;
+      }
+
+    case NDIS_STATUS_BAD_VERSION:
+      {
+	DEBUGP
+	  (("[TAP] NDIS Version is wrong for the given characteristics\n"));
+	NdisTerminateWrapper (g_NdisWrapperHandle, NULL);
+	break;
+      }
+
+    case NDIS_STATUS_RESOURCES:
+      {
+	DEBUGP (("[TAP] Insufficient resources\n"));
+	NdisTerminateWrapper (g_NdisWrapperHandle, NULL);
+	break;
+      }
+
+    default:
+    case NDIS_STATUS_FAILURE:
+      {
+	DEBUGP (("[TAP] Unknown fatal registration error\n"));
+	NdisTerminateWrapper (g_NdisWrapperHandle, NULL);
+	break;
+      }
+    }
+
+ cleanup:
+  if (l_Properties)
+    MemFree (l_Properties, sizeof (NDIS_MINIPORT_CHARACTERISTICS));
+
+  if (l_Status == NDIS_STATUS_SUCCESS)
+    NdisMRegisterUnloadHandler (g_NdisWrapperHandle, TapDriverUnload);
+  else
+    TapDriverUnload (p_DriverObject);
+
+  return l_Status;
+}
+
+//============================================================
+//                         Driver Unload
+//============================================================
+DRIVER_UNLOAD TapDriverUnload;
+VOID 
+TapDriverUnload (IN PDRIVER_OBJECT p_DriverObject)
+{
+  DEBUGP (("[TAP] version [%d.%d] %s %s unloaded, instances=%d, imbs=%d\n",
+	   TAP_DRIVER_MAJOR_VERSION,
+	   TAP_DRIVER_MINOR_VERSION,
+	   __DATE__,
+	   __TIME__,
+	   NInstances(),
+	   InstanceMaxBucketSize()));
+
+  FreeInstanceList ();
+
+  //==============================
+  // Free debugging text space
+  //==============================
+#if DBG
+  MyDebugFree ();
+#endif
+}
+
+//==========================================================
+//                            Adapter Initialization
+//==========================================================
+NDIS_STATUS AdapterCreate
+(OUT PNDIS_STATUS p_ErrorStatus,
+ OUT PUINT p_MediaIndex,
+ IN PNDIS_MEDIUM p_Media,
+ IN UINT p_MediaCount,
+ IN NDIS_HANDLE p_AdapterHandle,
+ IN NDIS_HANDLE p_ConfigurationHandle)
+{
+  TapAdapterPointer l_Adapter = NULL;
+
+  NDIS_MEDIUM l_PreferredMedium = NdisMedium802_3; // Ethernet
+  BOOLEAN l_MacFromRegistry = FALSE;
+  UINT l_Index;
+  NDIS_STATUS status;
+
+#if ENABLE_NONADMIN
+  BOOLEAN enable_non_admin = FALSE;
+#endif
+
+  DEBUGP (("[TAP] AdapterCreate called\n"));
+
+  //====================================
+  // Make sure adapter type is supported
+  //====================================
+
+  for (l_Index = 0;
+       l_Index < p_MediaCount && p_Media[l_Index] != l_PreferredMedium;
+       ++l_Index);
+
+  if (l_Index == p_MediaCount)
+    {
+      DEBUGP (("[TAP] Unsupported adapter type [wanted: %d]\n",
+	       l_PreferredMedium));
+      return NDIS_STATUS_UNSUPPORTED_MEDIA;
+    }
+
+  *p_MediaIndex = l_Index;
+
+  //=========================================
+  // Allocate memory for TapAdapter structure
+  //=========================================
+
+  l_Adapter = MemAlloc (sizeof (TapAdapter), TRUE);
+
+  if (l_Adapter == NULL)
+    {
+      DEBUGP (("[TAP] Couldn't allocate adapter memory\n"));
+      return NDIS_STATUS_RESOURCES;
+    }
+
+  //==========================================
+  // Inform the NDIS library about significant
+  // features of our virtual NIC.
+  //==========================================
+
+  NdisMSetAttributesEx
+    (p_AdapterHandle,
+     (NDIS_HANDLE) l_Adapter,
+     16,
+     NDIS_ATTRIBUTE_DESERIALIZE
+     | NDIS_ATTRIBUTE_IGNORE_PACKET_TIMEOUT
+     | NDIS_ATTRIBUTE_IGNORE_REQUEST_TIMEOUT
+     | NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND,
+     NdisInterfaceInternal);
+
+  //=====================================
+  // Initialize simple Adapter parameters
+  //=====================================
+
+  l_Adapter->m_Lookahead = DEFAULT_PACKET_LOOKAHEAD;
+  l_Adapter->m_Medium = l_PreferredMedium;
+  l_Adapter->m_DeviceState = '?';
+  l_Adapter->m_MiniportAdapterHandle = p_AdapterHandle;
+
+  //==================================
+  // Allocate spinlock for controlling
+  // access to multicast address list.
+  //==================================
+  NdisAllocateSpinLock (&l_Adapter->m_MCLock);
+  l_Adapter->m_MCLockAllocated = TRUE;
+
+  //====================================================
+  // Register a shutdown handler which will be called
+  // on system restart/shutdown to halt our virtual NIC.
+  //====================================================
+
+  NdisMRegisterAdapterShutdownHandler (p_AdapterHandle, l_Adapter,
+				       AdapterHalt);
+  l_Adapter->m_RegisteredAdapterShutdownHandler = TRUE;
+
+  //============================================
+  // Get parameters from registry which were set
+  // in the adapter advanced properties dialog.
+  //============================================
+  {
+    NDIS_STATUS status;
+    NDIS_HANDLE configHandle;
+    NDIS_CONFIGURATION_PARAMETER *parm;
+
+    // set defaults in case our registry query fails
+    l_Adapter->m_MTU = ETHERNET_MTU;
+    l_Adapter->m_MediaStateAlwaysConnected = FALSE;
+    l_Adapter->m_MediaState = FALSE;
+
+    NdisOpenConfiguration (&status, &configHandle, p_ConfigurationHandle);
+    if (status != NDIS_STATUS_SUCCESS)
+      {
+	DEBUGP (("[TAP] Couldn't open adapter registry\n"));
+	AdapterFreeResources (l_Adapter);
+	return status;
+      }
+
+    //====================================
+    // Allocate and construct adapter name
+    //====================================
+    {
+      
+      NDIS_STRING mkey = NDIS_STRING_CONST("MiniportName");
+      NDIS_STRING vkey = NDIS_STRING_CONST("NdisVersion");
+      NDIS_STATUS vstatus;
+      NDIS_CONFIGURATION_PARAMETER *vparm;
+
+      NdisReadConfiguration (&vstatus, &vparm, configHandle, &vkey, NdisParameterInteger);
+      if (vstatus == NDIS_STATUS_SUCCESS)
+	DEBUGP (("[TAP] NdisReadConfiguration NdisVersion=%X\n", vparm->ParameterData.IntegerData));
+
+      NdisReadConfiguration (&status, &parm, configHandle, &mkey, NdisParameterString);
+      if (status == NDIS_STATUS_SUCCESS)
+	{
+	  if (parm->ParameterType == NdisParameterString)
+	    {
+	      DEBUGP (("[TAP] NdisReadConfiguration (MiniportName=%.*S)\n",
+		       parm->ParameterData.StringData.Length/2,
+		       parm->ParameterData.StringData.Buffer));
+
+	      if (RtlUnicodeStringToAnsiString (
+						&l_Adapter->m_NameAnsi,
+						&parm->ParameterData.StringData,
+						TRUE) != STATUS_SUCCESS)
+		{
+		  DEBUGP (("[TAP] MiniportName failed\n"));
+		  status = NDIS_STATUS_RESOURCES;
+		}
+	    }
+	}
+      else
+	{
+	  /* "MiniportName" is available only XP and above.  Not on Windows 2000. */
+	  if (vstatus == NDIS_STATUS_SUCCESS && vparm->ParameterData.IntegerData == 0x50000)
+	    {
+	      /* Fallback for Windows 2000 with NDIS version 5.00.00
+		 Don't use this on Vista, 'NDIS_MINIPORT_BLOCK' was changed! */
+	      if (RtlUnicodeStringToAnsiString (&l_Adapter->m_NameAnsi,
+						&((struct WIN2K_NDIS_MINIPORT_BLOCK *) p_AdapterHandle)->MiniportName,
+						TRUE) != STATUS_SUCCESS)
+		{
+		  DEBUGP (("[TAP] MiniportName (W2K) failed\n"));
+		  status = NDIS_STATUS_RESOURCES;
+		}
+	      else
+		{
+		  DEBUGP (("[TAP] MiniportName (W2K) succeeded: %s\n", l_Adapter->m_NameAnsi.Buffer));
+		  status = NDIS_STATUS_SUCCESS;
+		}
+	    }
+	}
+    }
+
+    /* Can't continue without name (see macro 'NAME') */
+    if (status != NDIS_STATUS_SUCCESS || !l_Adapter->m_NameAnsi.Buffer)
+      {
+	NdisCloseConfiguration (configHandle);
+	AdapterFreeResources (l_Adapter);
+	DEBUGP (("[TAP] failed to get miniport name\n"));
+	return NDIS_STATUS_RESOURCES;
+      }
+
+    /* Read MTU setting from registry */
+    {
+      NDIS_STRING key = NDIS_STRING_CONST("MTU");
+      NdisReadConfiguration (&status, &parm, configHandle,
+			     &key, NdisParameterInteger);
+      if (status == NDIS_STATUS_SUCCESS)
+	{
+	  if (parm->ParameterType == NdisParameterInteger)
+	    {
+	      int mtu = parm->ParameterData.IntegerData;
+	      if (mtu < MINIMUM_MTU)
+		mtu = MINIMUM_MTU;
+	      if (mtu > MAXIMUM_MTU)
+		mtu = MAXIMUM_MTU;
+	      l_Adapter->m_MTU = mtu;
+	    }
+	}
+    }
+
+    /* Read Media Status setting from registry */
+    {
+      NDIS_STRING key = NDIS_STRING_CONST("MediaStatus");
+      NdisReadConfiguration (&status, &parm, configHandle,
+			     &key, NdisParameterInteger);
+      if (status == NDIS_STATUS_SUCCESS)
+	{
+	  if (parm->ParameterType == NdisParameterInteger)
+	    {
+	      if (parm->ParameterData.IntegerData)
+		{
+		  l_Adapter->m_MediaStateAlwaysConnected = TRUE;
+		  l_Adapter->m_MediaState = TRUE;
+		}
+	    }
+	}
+    }
+
+#if ENABLE_NONADMIN
+    /* Read AllowNonAdmin setting from registry */
+    {
+      NDIS_STRING key = NDIS_STRING_CONST("AllowNonAdmin");
+      NdisReadConfiguration (&status, &parm, configHandle,
+			     &key, NdisParameterInteger);
+      if (status == NDIS_STATUS_SUCCESS)
+	{
+	  if (parm->ParameterType == NdisParameterInteger)
+	    {
+	      if (parm->ParameterData.IntegerData)
+		{
+		  enable_non_admin = TRUE;
+		}
+	    }
+	}
+    }
+#endif
+
+    /* Read optional MAC setting from registry */
+    {
+      NDIS_STRING key = NDIS_STRING_CONST("MAC");
+      ANSI_STRING mac_string;
+      NdisReadConfiguration (&status, &parm, configHandle,
+			     &key, NdisParameterString);
+      if (status == NDIS_STATUS_SUCCESS)
+	{
+	  if (parm->ParameterType == NdisParameterString)
+	    {
+	      if (RtlUnicodeStringToAnsiString (&mac_string, &parm->ParameterData.StringData, TRUE) == STATUS_SUCCESS)
+		{
+		  l_MacFromRegistry = ParseMAC (l_Adapter->m_MAC, mac_string.Buffer);
+		  RtlFreeAnsiString (&mac_string);
+		}
+	    }
+	}
+    }
+
+    NdisCloseConfiguration (configHandle);
+
+    DEBUGP (("[%s] MTU=%d\n", NAME (l_Adapter), l_Adapter->m_MTU));
+  }
+
+  //==================================
+  // Store and update MAC address info
+  //==================================
+
+  if (!l_MacFromRegistry)
+    GenerateRandomMac (l_Adapter->m_MAC, NAME (l_Adapter));
+
+  DEBUGP (("[%s] Using MAC %x:%x:%x:%x:%x:%x\n",
+	   NAME (l_Adapter),
+	   l_Adapter->m_MAC[0], l_Adapter->m_MAC[1], l_Adapter->m_MAC[2],
+	   l_Adapter->m_MAC[3], l_Adapter->m_MAC[4], l_Adapter->m_MAC[5]));
+
+  //==================
+  // Set broadcast MAC
+  //==================
+  {
+    int i;
+    for (i = 0; i < sizeof (MACADDR); ++i)
+      l_Adapter->m_MAC_Broadcast[i] = 0xFF;
+  }
+
+  //====================================
+  // Initialize TAP device
+  //====================================
+  {
+    NDIS_STATUS tap_status;
+    tap_status = CreateTapDevice (&l_Adapter->m_Extension, NAME (l_Adapter));
+    if (tap_status != NDIS_STATUS_SUCCESS)
+      {
+	AdapterFreeResources (l_Adapter);
+	DEBUGP (("[TAP] CreateTapDevice failed\n"));
+	return tap_status;
+      }
+  }
+
+  if (!AddAdapterToInstanceList (l_Adapter))
+    {
+      NOTE_ERROR ();
+      TapDeviceFreeResources (&l_Adapter->m_Extension);
+      AdapterFreeResources (l_Adapter);
+      DEBUGP (("[TAP] AddAdapterToInstanceList failed\n"));
+      return NDIS_STATUS_RESOURCES;
+    }
+
+  l_Adapter->m_InterfaceIsRunning = TRUE;
+
+#if ENABLE_NONADMIN
+  if (enable_non_admin)
+    AllowNonAdmin (&l_Adapter->m_Extension);
+#endif
+
+  return NDIS_STATUS_SUCCESS;
+}
+
+VOID
+AdapterHalt (IN NDIS_HANDLE p_AdapterContext)
+{
+  BOOLEAN status;
+
+  TapAdapterPointer l_Adapter = (TapAdapterPointer) p_AdapterContext;
+
+  NOTE_ERROR ();
+
+  l_Adapter->m_InterfaceIsRunning = FALSE;
+
+  DEBUGP (("[%s] is being halted\n", NAME (l_Adapter)));
+  
+  DestroyTapDevice (&l_Adapter->m_Extension);
+
+  // Free resources
+  DEBUGP (("[%s] Freeing Resources\n", NAME (l_Adapter)));
+  AdapterFreeResources (l_Adapter);
+
+  status = RemoveAdapterFromInstanceList (l_Adapter);
+  DEBUGP (("[TAP] RemoveAdapterFromInstanceList returned %d\n", (int) status));
+
+  DEBUGP (("[TAP] version [%d.%d] %s %s AdapterHalt returning\n",
+	   TAP_DRIVER_MAJOR_VERSION,
+	   TAP_DRIVER_MINOR_VERSION,
+	   __DATE__,
+	   __TIME__));
+}
+
+VOID
+AdapterFreeResources (TapAdapterPointer p_Adapter)
+{
+  MYASSERT (!p_Adapter->m_CalledAdapterFreeResources);
+  p_Adapter->m_CalledAdapterFreeResources = TRUE;
+
+  if (p_Adapter->m_NameAnsi.Buffer)
+    RtlFreeAnsiString (&p_Adapter->m_NameAnsi);
+  
+  if (p_Adapter->m_RegisteredAdapterShutdownHandler)
+    NdisMDeregisterAdapterShutdownHandler (p_Adapter->m_MiniportAdapterHandle);
+
+  if (p_Adapter->m_MCLockAllocated)
+    NdisFreeSpinLock (&p_Adapter->m_MCLock);
+}
+
+VOID
+DestroyTapDevice (TapExtensionPointer p_Extension)
+{
+  DEBUGP (("[%s] Destroying tap device\n", p_Extension->m_TapName));
+
+  //======================================
+  // Let clients know we are shutting down
+  //======================================
+  p_Extension->m_TapIsRunning = FALSE;
+  p_Extension->m_TapOpens = 0;
+  p_Extension->m_Halt = TRUE;
+
+  //=====================================
+  // If we are concurrently executing in
+  // TapDeviceHook or AdapterTransmit,
+  // give those calls time to finish.
+  // Note that we must be running at IRQL
+  // < DISPATCH_LEVEL in order to call
+  // NdisMSleep.
+  //=====================================
+  NdisMSleep (500000);
+
+  //===========================================================
+  // Exhaust IRP and packet queues.  Any pending IRPs will
+  // be cancelled, causing user-space to get this error
+  // on overlapped reads:
+  //   The I/O operation has been aborted because of either a
+  //   thread exit or an application request.   (code=995)
+  // It's important that user-space close the device handle
+  // when this code is returned, so that when we finally
+  // do a NdisMDeregisterDevice, the device reference count
+  // is 0.  Otherwise the driver will not unload even if the
+  // the last adapter has been halted.
+  //===========================================================
+  FlushQueues (p_Extension);
+  NdisMSleep (500000); // give user space time to respond to IRP cancel
+
+  TapDeviceFreeResources (p_Extension);
+}
+
+VOID
+TapDeviceFreeResources (TapExtensionPointer p_Extension)
+{
+  MYASSERT (p_Extension);
+  MYASSERT (!p_Extension->m_CalledTapDeviceFreeResources);
+  p_Extension->m_CalledTapDeviceFreeResources = TRUE;
+
+  if (p_Extension->m_PacketQueue)
+    QueueFree (p_Extension->m_PacketQueue);
+  if (p_Extension->m_IrpQueue)
+    QueueFree (p_Extension->m_IrpQueue);
+  if (p_Extension->m_InjectQueue)
+    QueueFree (p_Extension->m_InjectQueue);
+
+  if (p_Extension->m_CreatedUnicodeLinkName)
+    RtlFreeUnicodeString (&p_Extension->m_UnicodeLinkName);
+
+  //==========================================================
+  // According to DDK docs, the device is not actually deleted
+  // until its reference count falls to zero.  That means we
+  // still need to gracefully fail TapDeviceHook requests
+  // after this point, otherwise ugly things would happen if
+  // the device was disabled (e.g. in the network connections
+  // control panel) while a userspace app still held an open
+  // file handle to it.
+  //==========================================================
+  
+  if (p_Extension->m_TapDevice)
+    {
+      BOOLEAN status;
+      status = (NdisMDeregisterDevice (p_Extension->m_TapDeviceHandle)
+		== NDIS_STATUS_SUCCESS);
+      DEBUGP (("[TAP] Deregistering TAP device, status=%d\n", (int)status));
+    }
+
+  if (p_Extension->m_TapName)
+    MemFree (p_Extension->m_TapName, NAME_BUFFER_SIZE);
+  
+  if (p_Extension->m_InjectDpcInitialized)
+    KeRemoveQueueDpc (&p_Extension->m_InjectDpc);
+
+  if (p_Extension->m_AllocatedSpinlocks)
+    {
+      NdisFreeSpinLock (&p_Extension->m_QueueLock);
+      NdisFreeSpinLock (&p_Extension->m_InjectLock);
+    }
+}
+
+//========================================================================
+//                             Tap Device Initialization
+//========================================================================
+
+NDIS_STATUS
+CreateTapDevice (TapExtensionPointer p_Extension, const char *p_Name)
+{
+# define SIZEOF_DISPATCH (sizeof(PDRIVER_DISPATCH) * (IRP_MJ_MAXIMUM_FUNCTION + 1))
+  PDRIVER_DISPATCH *l_Dispatch = NULL;
+  ANSI_STRING l_TapString, l_LinkString;
+  UNICODE_STRING l_TapUnicode;
+  BOOLEAN l_FreeTapUnicode = FALSE;
+  NTSTATUS l_Status, l_Return = NDIS_STATUS_SUCCESS;
+  const char *l_UsableName;
+
+  DEBUGP (("[TAP] version [%d.%d] creating tap device: %s\n",
+	   TAP_DRIVER_MAJOR_VERSION,
+	   TAP_DRIVER_MINOR_VERSION,
+	   p_Name));
+
+  NdisZeroMemory (p_Extension, sizeof (TapExtension));
+
+  INIT_MUTEX (&p_Extension->m_OpenCloseMutex);
+
+  l_LinkString.Buffer = NULL;
+  l_TapString.Buffer = NULL;
+
+  l_TapString.MaximumLength = l_LinkString.MaximumLength = NAME_BUFFER_SIZE;
+
+  //=======================================
+  // Set TAP device entry points
+  //=======================================
+
+  if ((l_Dispatch = MemAlloc (SIZEOF_DISPATCH, TRUE)) == NULL)
+    {
+      DEBUGP (("[%s] couldn't alloc TAP dispatch table\n", p_Name));
+      l_Return = NDIS_STATUS_RESOURCES;
+      goto cleanup;
+    }
+
+  l_Dispatch[IRP_MJ_DEVICE_CONTROL] = TapDeviceHook;
+  l_Dispatch[IRP_MJ_READ] = TapDeviceHook;
+  l_Dispatch[IRP_MJ_WRITE] = TapDeviceHook;
+  l_Dispatch[IRP_MJ_CREATE] = TapDeviceHook;
+  l_Dispatch[IRP_MJ_CLOSE] = TapDeviceHook;
+
+  //==================================
+  // Find the beginning of the GUID
+  //==================================
+  l_UsableName = p_Name;
+  while (*l_UsableName != '{')
+    {
+      if (*l_UsableName == '\0')
+	{
+	  DEBUGP (("[%s] couldn't find leading '{' in name\n", p_Name));
+	  l_Return = NDIS_STATUS_RESOURCES;
+	  goto cleanup;
+	}
+      ++l_UsableName;
+    }
+
+  //==================================
+  // Allocate pool for TAP device name
+  //==================================
+
+  if ((p_Extension->m_TapName = l_TapString.Buffer =
+       MemAlloc (NAME_BUFFER_SIZE, TRUE)) == NULL)
+    {
+      DEBUGP (("[%s] couldn't alloc TAP name buffer\n", p_Name));
+      l_Return = NDIS_STATUS_RESOURCES;
+      goto cleanup;
+    }
+
+  //================================================
+  // Allocate pool for TAP symbolic link name buffer
+  //================================================
+
+  if ((l_LinkString.Buffer =
+       MemAlloc (NAME_BUFFER_SIZE, TRUE)) == NULL)
+    {
+      DEBUGP (("[%s] couldn't alloc TAP symbolic link name buffer\n",
+	       p_Name));
+      l_Return = NDIS_STATUS_RESOURCES;
+      goto cleanup;
+    }
+
+  //=======================================================
+  // Set TAP device name
+  //=======================================================
+
+  l_Status = RtlStringCchPrintfExA
+    (l_TapString.Buffer,
+     l_TapString.MaximumLength,
+     NULL,
+     NULL,
+     STRSAFE_FILL_BEHIND_NULL | STRSAFE_IGNORE_NULLS,
+     "%s%s%s",
+     SYSDEVICEDIR,
+     l_UsableName,
+     TAP_WIN_SUFFIX);
+
+  if (l_Status != STATUS_SUCCESS)
+    {
+      DEBUGP (("[%s] couldn't format TAP device name\n",
+	       p_Name));
+      l_Return = NDIS_STATUS_RESOURCES;
+      goto cleanup;
+    }
+  l_TapString.Length = (USHORT) strlen (l_TapString.Buffer);
+
+  DEBUGP (("TAP DEV NAME: '%s'\n", l_TapString.Buffer));
+
+  //=======================================================
+  // Set TAP link name
+  //=======================================================
+
+  l_Status = RtlStringCchPrintfExA
+    (l_LinkString.Buffer,
+     l_LinkString.MaximumLength,
+     NULL,
+     NULL,
+     STRSAFE_FILL_BEHIND_NULL | STRSAFE_IGNORE_NULLS,
+     "%s%s%s",
+     USERDEVICEDIR,
+     l_UsableName,
+     TAP_WIN_SUFFIX);
+
+  if (l_Status != STATUS_SUCCESS)
+    {
+      DEBUGP (("[%s] couldn't format TAP device symbolic link\n",
+	       p_Name));
+      l_Return = NDIS_STATUS_RESOURCES;
+      goto cleanup;
+    }
+  l_LinkString.Length = (USHORT) strlen (l_LinkString.Buffer);
+
+  DEBUGP (("TAP LINK NAME: '%s'\n", l_LinkString.Buffer));
+
+  //==================================================
+  // Convert strings to unicode
+  //==================================================
+  if (RtlAnsiStringToUnicodeString (&l_TapUnicode, &l_TapString, TRUE) !=
+      STATUS_SUCCESS)
+    {
+      DEBUGP (("[%s] couldn't alloc TAP unicode name buffer\n",
+		p_Name));
+      l_Return = NDIS_STATUS_RESOURCES;
+      goto cleanup;
+    }
+  l_FreeTapUnicode = TRUE;
+
+  if (RtlAnsiStringToUnicodeString
+      (&p_Extension->m_UnicodeLinkName, &l_LinkString, TRUE)
+      != STATUS_SUCCESS)
+    {
+      DEBUGP
+	(("[%s] Couldn't allocate unicode string for symbolic link name\n",
+	 p_Name));
+      l_Return = NDIS_STATUS_RESOURCES;
+      goto cleanup;
+    }
+  p_Extension->m_CreatedUnicodeLinkName = TRUE;
+
+  //==================================================
+  // Create new TAP device with symbolic
+  // link and associate with adapter.
+  //==================================================
+
+  l_Status = NdisMRegisterDevice
+    (g_NdisWrapperHandle,
+     &l_TapUnicode,
+     &p_Extension->m_UnicodeLinkName,
+     l_Dispatch,
+     &p_Extension->m_TapDevice,
+     &p_Extension->m_TapDeviceHandle
+     );
+
+  if (l_Status != STATUS_SUCCESS)
+    {
+      DEBUGP (("[%s] couldn't be created\n", p_Name));
+      l_Return = NDIS_STATUS_RESOURCES;
+      goto cleanup;
+    }
+
+  /* Set TAP device flags */
+  p_Extension->m_TapDevice->Flags |= DO_DIRECT_IO;
+
+  //========================================================
+  // Initialize Packet and IRP queues.
+  //
+  // The packet queue is used to buffer data which has been
+  // "transmitted" by the virtual NIC, before user space
+  // has had a chance to read it.
+  //
+  // The IRP queue is used to buffer pending I/O requests
+  // from userspace, i.e. read requests on the TAP device
+  // waiting for the system to "transmit" something through
+  // the virtual NIC.
+  //
+  // Basically, packets in the packet queue are used
+  // to satisfy IRP requests in the IRP queue.
+  //
+  // QueueLock is used to lock the packet queue used
+  // for the TAP-Windows NIC -> User Space packet flow direction.
+  //
+  // All accesses to packet or IRP queues should be
+  // bracketed by the QueueLock spinlock,
+  // in order to be SMP-safe.
+  //========================================================
+
+  NdisAllocateSpinLock (&p_Extension->m_QueueLock);
+  NdisAllocateSpinLock (&p_Extension->m_InjectLock);
+  p_Extension->m_AllocatedSpinlocks = TRUE;
+
+  p_Extension->m_PacketQueue = QueueInit (PACKET_QUEUE_SIZE);
+  p_Extension->m_IrpQueue = QueueInit (IRP_QUEUE_SIZE);
+  p_Extension->m_InjectQueue = QueueInit (INJECT_QUEUE_SIZE);
+  if (!p_Extension->m_PacketQueue
+      || !p_Extension->m_IrpQueue
+      || !p_Extension->m_InjectQueue)
+    {
+      DEBUGP (("[%s] couldn't alloc TAP queues\n", p_Name));
+      l_Return = NDIS_STATUS_RESOURCES;
+      goto cleanup;
+    }
+
+  //=================================================================
+  // Initialize deferred procedure call for DHCP/ARP packet injection
+  //=================================================================
+
+  KeInitializeDpc (&p_Extension->m_InjectDpc, InjectPacketDpc, NULL);
+  p_Extension->m_InjectDpcInitialized = TRUE;
+
+  //========================
+  // Finalize initialization
+  //========================
+
+  p_Extension->m_TapIsRunning = TRUE;
+
+  DEBUGP (("[%s] successfully created TAP device [%s]\n", p_Name,
+	    p_Extension->m_TapName));
+
+ cleanup:
+  if (l_FreeTapUnicode)
+    RtlFreeUnicodeString (&l_TapUnicode);
+  if (l_LinkString.Buffer)
+    MemFree (l_LinkString.Buffer, NAME_BUFFER_SIZE);
+  if (l_Dispatch)
+    MemFree (l_Dispatch, SIZEOF_DISPATCH);
+
+  if (l_Return != NDIS_STATUS_SUCCESS)
+    TapDeviceFreeResources (p_Extension);
+
+  return l_Return;
+}
+#undef SIZEOF_DISPATCH
+
+//========================================================
+//                      Adapter Control
+//========================================================
+NDIS_STATUS
+AdapterReset (OUT PBOOLEAN p_AddressingReset, IN NDIS_HANDLE p_AdapterContext)
+{
+  TapAdapterPointer l_Adapter = (TapAdapterPointer) p_AdapterContext;
+  DEBUGP (("[%s] is resetting\n", NAME (l_Adapter)));
+  return NDIS_STATUS_SUCCESS;
+}
+
+NDIS_STATUS AdapterReceive
+  (OUT PNDIS_PACKET p_Packet,
+   OUT PUINT p_Transferred,
+   IN NDIS_HANDLE p_AdapterContext,
+   IN NDIS_HANDLE p_ReceiveContext,
+   IN UINT p_Offset,
+   IN UINT p_ToTransfer)
+{
+  return NDIS_STATUS_SUCCESS;
+}
+
+//==============================================================
+//                  Adapter Option Query/Modification
+//==============================================================
+NDIS_STATUS AdapterQuery
+(IN NDIS_HANDLE p_AdapterContext,
+ IN NDIS_OID p_OID,
+ IN PVOID p_Buffer,
+ IN ULONG p_BufferLength,
+ OUT PULONG p_BytesWritten, OUT PULONG p_BytesNeeded)
+{
+  TapAdapterPointer l_Adapter = (TapAdapterPointer) p_AdapterContext;
+  TapAdapterQuery l_Query, *l_QueryPtr = &l_Query;
+  NDIS_STATUS l_Status = NDIS_STATUS_SUCCESS;
+  UINT l_QueryLength = 4;
+  BOOLEAN lock_succeeded;
+
+  NdisZeroMemory (&l_Query, sizeof (l_Query));
+
+  switch (p_OID)
+    {
+      //===================================================================
+      //                       Vendor & Driver version Info
+      //===================================================================
+    case OID_GEN_VENDOR_DESCRIPTION:
+      l_QueryPtr = (TapAdapterQueryPointer) PRODUCT_TAP_WIN_DEVICE_DESCRIPTION;
+      l_QueryLength = strlen (PRODUCT_TAP_WIN_DEVICE_DESCRIPTION) + 1;
+      break;
+
+    case OID_GEN_VENDOR_ID:
+      l_Query.m_Long = 0xffffff;
+      break;
+
+    case OID_GEN_DRIVER_VERSION:
+      l_Query.m_Short =
+	(((USHORT) TAP_NDIS_MAJOR_VERSION) << 8 | (USHORT)
+	 TAP_NDIS_MINOR_VERSION);
+      l_QueryLength = sizeof (unsigned short);
+      break;
+
+    case OID_GEN_VENDOR_DRIVER_VERSION:
+      l_Query.m_Long =
+	(((USHORT) PRODUCT_TAP_WIN_MAJOR) << 8 | (USHORT)
+	 PRODUCT_TAP_WIN_MINOR);
+      break;
+
+      //=================================================================
+      //                             Statistics
+      //=================================================================
+    case OID_GEN_RCV_NO_BUFFER:
+      l_Query.m_Long = 0;
+      break;
+
+    case OID_802_3_RCV_ERROR_ALIGNMENT:
+      l_Query.m_Long = 0;
+      break;
+
+    case OID_802_3_XMIT_ONE_COLLISION:
+      l_Query.m_Long = 0;
+      break;
+
+    case OID_802_3_XMIT_MORE_COLLISIONS:
+      l_Query.m_Long = 0;
+      break;
+
+    case OID_GEN_XMIT_OK:
+      l_Query.m_Long = l_Adapter->m_Tx;
+      break;
+
+    case OID_GEN_RCV_OK:
+      l_Query.m_Long = l_Adapter->m_Rx;
+      break;
+
+    case OID_GEN_XMIT_ERROR:
+      l_Query.m_Long = l_Adapter->m_TxErr;
+      break;
+
+    case OID_GEN_RCV_ERROR:
+      l_Query.m_Long = l_Adapter->m_RxErr;
+      break;
+
+      //===================================================================
+      //                       Device & Protocol Options
+      //===================================================================
+    case OID_GEN_SUPPORTED_LIST:
+      l_QueryPtr = (TapAdapterQueryPointer) g_SupportedOIDList;
+      l_QueryLength = sizeof (g_SupportedOIDList);
+      break;
+
+    case OID_GEN_MAC_OPTIONS:
+      // This MUST be here !!!
+      l_Query.m_Long = (NDIS_MAC_OPTION_RECEIVE_SERIALIZED
+			| NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA
+			| NDIS_MAC_OPTION_NO_LOOPBACK
+			| NDIS_MAC_OPTION_TRANSFERS_NOT_PEND);
+
+      break;
+
+    case OID_GEN_CURRENT_PACKET_FILTER:
+      l_Query.m_Long =
+	(NDIS_PACKET_TYPE_ALL_LOCAL |
+	 NDIS_PACKET_TYPE_BROADCAST |
+	 NDIS_PACKET_TYPE_DIRECTED | NDIS_PACKET_TYPE_ALL_FUNCTIONAL);
+
+      break;
+
+    case OID_GEN_PROTOCOL_OPTIONS:
+      l_Query.m_Long = 0;
+      break;
+
+      //==================================================================
+      //                            Device Info
+      //==================================================================
+    case OID_GEN_MEDIA_CONNECT_STATUS:
+      l_Query.m_Long = l_Adapter->m_MediaState
+	? NdisMediaStateConnected : NdisMediaStateDisconnected;
+      break;
+
+    case OID_GEN_HARDWARE_STATUS:
+      l_Query.m_HardwareStatus = NdisHardwareStatusReady;
+      l_QueryLength = sizeof (NDIS_HARDWARE_STATUS);
+      break;
+
+    case OID_GEN_MEDIA_SUPPORTED:
+    case OID_GEN_MEDIA_IN_USE:
+      l_Query.m_Medium = l_Adapter->m_Medium;
+      l_QueryLength = sizeof (NDIS_MEDIUM);
+      break;
+
+    case OID_GEN_PHYSICAL_MEDIUM:
+      l_Query.m_PhysicalMedium = NdisPhysicalMediumUnspecified;
+      l_QueryLength = sizeof (NDIS_PHYSICAL_MEDIUM);
+      break;
+      
+    case OID_GEN_LINK_SPEED:
+      l_Query.m_Long = 100000; // rate / 100 bps
+      break;
+
+    case OID_802_3_PERMANENT_ADDRESS:
+    case OID_802_3_CURRENT_ADDRESS:
+      COPY_MAC (l_Query.m_MacAddress, l_Adapter->m_MAC);
+      l_QueryLength = sizeof (MACADDR);
+      break;
+
+      //==================================================================
+      //                             Limits
+      //==================================================================
+
+    case OID_GEN_MAXIMUM_SEND_PACKETS:
+      l_Query.m_Long = 1;
+      break;
+
+    case OID_802_3_MAXIMUM_LIST_SIZE:
+      l_Query.m_Long = NIC_MAX_MCAST_LIST;
+      break;
+
+    case OID_GEN_CURRENT_LOOKAHEAD:
+      l_Query.m_Long = l_Adapter->m_Lookahead;
+      break;
+
+    case OID_GEN_MAXIMUM_LOOKAHEAD:
+    case OID_GEN_MAXIMUM_TOTAL_SIZE:
+    case OID_GEN_RECEIVE_BUFFER_SPACE:
+    case OID_GEN_RECEIVE_BLOCK_SIZE:
+      l_Query.m_Long = DEFAULT_PACKET_LOOKAHEAD;
+      break;
+
+    case OID_GEN_MAXIMUM_FRAME_SIZE:
+    case OID_GEN_TRANSMIT_BLOCK_SIZE:
+    case OID_GEN_TRANSMIT_BUFFER_SPACE:
+      l_Query.m_Long = l_Adapter->m_MTU;
+      break;
+
+    case OID_PNP_CAPABILITIES:
+      do
+	{
+	  PNDIS_PNP_CAPABILITIES pPNPCapabilities;
+	  PNDIS_PM_WAKE_UP_CAPABILITIES pPMstruct;
+
+	  if (p_BufferLength >= sizeof (NDIS_PNP_CAPABILITIES))
+	    {
+	      pPNPCapabilities = (PNDIS_PNP_CAPABILITIES) (p_Buffer);
+
+	      //
+	      // Setting up the buffer to be returned
+	      // to the Protocol above the Passthru miniport
+	      //
+	      pPMstruct = &pPNPCapabilities->WakeUpCapabilities;
+	      pPMstruct->MinMagicPacketWakeUp = NdisDeviceStateUnspecified;
+	      pPMstruct->MinPatternWakeUp = NdisDeviceStateUnspecified;
+	      pPMstruct->MinLinkChangeWakeUp = NdisDeviceStateUnspecified;
+	    }
+	  l_QueryLength = sizeof (NDIS_PNP_CAPABILITIES);
+	}
+      while (FALSE);
+      break;
+    case OID_PNP_QUERY_POWER:
+      break;
+
+      // Required OIDs that we don't support
+
+    case OID_GEN_SUPPORTED_GUIDS:
+    case OID_GEN_MEDIA_CAPABILITIES:
+    case OID_TCP_TASK_OFFLOAD:
+    case OID_FFP_SUPPORT:
+      l_Status = NDIS_STATUS_INVALID_OID;
+      break;
+
+      // Optional stats OIDs
+
+    case OID_GEN_DIRECTED_BYTES_XMIT:
+    case OID_GEN_DIRECTED_FRAMES_XMIT:
+    case OID_GEN_MULTICAST_BYTES_XMIT:
+    case OID_GEN_MULTICAST_FRAMES_XMIT:
+    case OID_GEN_BROADCAST_BYTES_XMIT:
+    case OID_GEN_BROADCAST_FRAMES_XMIT:
+    case OID_GEN_DIRECTED_BYTES_RCV:
+    case OID_GEN_DIRECTED_FRAMES_RCV:
+    case OID_GEN_MULTICAST_BYTES_RCV:
+    case OID_GEN_MULTICAST_FRAMES_RCV:
+    case OID_GEN_BROADCAST_BYTES_RCV:
+    case OID_GEN_BROADCAST_FRAMES_RCV:
+      l_Status = NDIS_STATUS_INVALID_OID;
+      break;
+
+      //===================================================================
+      //                          Not Handled
+      //===================================================================
+    default:
+      DEBUGP (("[%s] Unhandled OID %lx\n", NAME (l_Adapter), p_OID));
+      l_Status = NDIS_STATUS_INVALID_OID;
+      break;
+    }
+
+  if (l_Status != NDIS_STATUS_SUCCESS)
+    ;
+  else if (l_QueryLength > p_BufferLength)
+    {
+      l_Status = NDIS_STATUS_INVALID_LENGTH;
+      *p_BytesNeeded = l_QueryLength;
+    }
+  else
+    NdisMoveMemory (p_Buffer, (PVOID) l_QueryPtr,
+		    (*p_BytesWritten = l_QueryLength));
+
+  return l_Status;
+}
+
+NDIS_STATUS AdapterModify
+(IN NDIS_HANDLE p_AdapterContext,
+ IN NDIS_OID p_OID,
+ IN PVOID p_Buffer,
+ IN ULONG p_BufferLength,
+ OUT PULONG p_BytesRead,
+ OUT PULONG p_BytesNeeded)
+{
+  TapAdapterQueryPointer l_Query = (TapAdapterQueryPointer) p_Buffer;
+  TapAdapterPointer l_Adapter = (TapAdapterPointer) p_AdapterContext;
+  NDIS_STATUS l_Status = NDIS_STATUS_INVALID_OID;
+  ULONG l_Long;
+
+  switch (p_OID)
+    {
+      //==================================================================
+      //                            Device Info
+      //==================================================================
+    case OID_802_3_MULTICAST_LIST:
+      DEBUGP (("[%s] Setting [OID_802_3_MULTICAST_LIST]\n",
+	       NAME (l_Adapter)));
+
+      *p_BytesNeeded = sizeof (ETH_ADDR);
+      *p_BytesRead = p_BufferLength;
+
+      if (p_BufferLength % sizeof (ETH_ADDR))
+	l_Status = NDIS_STATUS_INVALID_LENGTH;
+      else if (p_BufferLength > sizeof (MC_LIST))
+	{
+	  l_Status = NDIS_STATUS_MULTICAST_FULL;
+	  *p_BytesNeeded = sizeof (MC_LIST);
+	}
+      else
+	{
+	  NdisAcquireSpinLock (&l_Adapter->m_MCLock);
+
+	  NdisZeroMemory(&l_Adapter->m_MCList, sizeof (MC_LIST));
+        
+	  NdisMoveMemory(&l_Adapter->m_MCList,
+			 p_Buffer,
+			 p_BufferLength);
+
+	  l_Adapter->m_MCListSize = p_BufferLength / sizeof (ETH_ADDR);
+        
+	  NdisReleaseSpinLock (&l_Adapter->m_MCLock);
+
+	  l_Status = NDIS_STATUS_SUCCESS;
+	}
+      break;
+
+    case OID_GEN_CURRENT_PACKET_FILTER:
+      l_Status = NDIS_STATUS_INVALID_LENGTH;
+      *p_BytesNeeded = 4;
+
+      if (p_BufferLength >= sizeof (ULONG))
+	{
+	  DEBUGP
+	    (("[%s] Setting [OID_GEN_CURRENT_PACKET_FILTER] to [0x%02lx]\n",
+	      NAME (l_Adapter), l_Query->m_Long));
+	  l_Status = NDIS_STATUS_SUCCESS;
+	  *p_BytesRead = sizeof (ULONG);
+	}
+      break;
+
+    case OID_GEN_CURRENT_LOOKAHEAD:
+      if (p_BufferLength < sizeof (ULONG))
+	{
+	  l_Status = NDIS_STATUS_INVALID_LENGTH;
+	  *p_BytesNeeded = 4;
+	}
+      else if (l_Query->m_Long > DEFAULT_PACKET_LOOKAHEAD
+	       || l_Query->m_Long <= 0)
+	{
+	  l_Status = NDIS_STATUS_INVALID_DATA;
+	}
+      else
+	{
+	  DEBUGP (("[%s] Setting [OID_GEN_CURRENT_LOOKAHEAD] to [%d]\n",
+		   NAME (l_Adapter), l_Query->m_Long));
+	  l_Adapter->m_Lookahead = l_Query->m_Long;
+	  l_Status = NDIS_STATUS_SUCCESS;
+	  *p_BytesRead = sizeof (ULONG);
+	}
+      break;
+
+    case OID_GEN_NETWORK_LAYER_ADDRESSES:
+      l_Status = NDIS_STATUS_SUCCESS;
+      *p_BytesRead = *p_BytesNeeded = 0;
+      break;
+
+    case OID_GEN_TRANSPORT_HEADER_OFFSET:
+      l_Status = NDIS_STATUS_SUCCESS;
+      *p_BytesRead = *p_BytesNeeded = 0;
+      break;
+
+    case OID_PNP_SET_POWER:
+      do
+	{
+	  NDIS_DEVICE_POWER_STATE NewDeviceState;
+
+	  NewDeviceState = (*(PNDIS_DEVICE_POWER_STATE) p_Buffer);
+
+	  switch (NewDeviceState)
+	    {
+	    case NdisDeviceStateD0:
+	      l_Adapter->m_DeviceState = '0';
+	      break;
+	    case NdisDeviceStateD1:
+	      l_Adapter->m_DeviceState = '1';
+	      break;
+	    case NdisDeviceStateD2:
+	      l_Adapter->m_DeviceState = '2';
+	      break;
+	    case NdisDeviceStateD3:
+	      l_Adapter->m_DeviceState = '3';
+	      break;
+	    default:
+	      l_Adapter->m_DeviceState = '?';
+	      break;
+	    }
+
+	  l_Status = NDIS_STATUS_FAILURE;
+
+	  //
+	  // Check for invalid length
+	  //
+	  if (p_BufferLength < sizeof (NDIS_DEVICE_POWER_STATE))
+	    {
+	      l_Status = NDIS_STATUS_INVALID_LENGTH;
+	      break;
+	    }
+
+	  if (NewDeviceState > NdisDeviceStateD0)
+	    {
+	      l_Adapter->m_InterfaceIsRunning = FALSE;
+	      DEBUGP (("[%s] Power management device state OFF\n",
+		       NAME (l_Adapter)));
+	    }
+	  else
+	    {
+	      l_Adapter->m_InterfaceIsRunning = TRUE;
+	      DEBUGP (("[%s] Power management device state ON\n",
+		       NAME (l_Adapter)));
+	    }
+
+	  l_Status = NDIS_STATUS_SUCCESS;
+	}
+      while (FALSE);
+
+      if (l_Status == NDIS_STATUS_SUCCESS)
+	{
+	  *p_BytesRead = sizeof (NDIS_DEVICE_POWER_STATE);
+	  *p_BytesNeeded = 0;
+	}
+      else
+	{
+	  *p_BytesRead = 0;
+	  *p_BytesNeeded = sizeof (NDIS_DEVICE_POWER_STATE);
+	}
+      break;
+
+    case OID_PNP_REMOVE_WAKE_UP_PATTERN:
+    case OID_PNP_ADD_WAKE_UP_PATTERN:
+      l_Status = NDIS_STATUS_SUCCESS;
+      *p_BytesRead = *p_BytesNeeded = 0;
+      break;
+
+    default:
+      DEBUGP (("[%s] Can't set value for OID %lx\n", NAME (l_Adapter),
+	       p_OID));
+      l_Status = NDIS_STATUS_INVALID_OID;
+      *p_BytesRead = *p_BytesNeeded = 0;
+      break;
+    }
+
+  return l_Status;
+}
+
+// checksum code for ICMPv6 packet, taken from dhcp.c / udp_checksum
+// see RFC 4443, 2.3, and RFC 2460, 8.1
+USHORT
+icmpv6_checksum (const UCHAR *buf,
+	         const int len_icmpv6,
+	         const UCHAR *saddr6,
+	         const UCHAR *daddr6)
+{
+  USHORT word16;
+  ULONG sum = 0;
+  int i;
+
+  // make 16 bit words out of every two adjacent 8 bit words and
+  // calculate the sum of all 16 bit words
+  for (i = 0; i < len_icmpv6; i += 2){
+    word16 = ((buf[i] << 8) & 0xFF00) + ((i + 1 < len_icmpv6) ? (buf[i+1] & 0xFF) : 0);
+    sum += word16;
+  }
+
+  // add the IPv6 pseudo header which contains the IP source and destination addresses
+  for (i = 0; i < 16; i += 2){
+    word16 =((saddr6[i] << 8) & 0xFF00) + (saddr6[i+1] & 0xFF);
+    sum += word16;
+  }
+  for (i = 0; i < 16; i += 2){
+    word16 =((daddr6[i] << 8) & 0xFF00) + (daddr6[i+1] & 0xFF);
+    sum += word16;
+  }
+
+  // the next-header number and the length of the ICMPv6 packet
+  sum += (USHORT) IPPROTO_ICMPV6 + (USHORT) len_icmpv6;
+
+  // keep only the last 16 bits of the 32 bit calculated sum and add the carries
+  while (sum >> 16)
+    sum = (sum & 0xFFFF) + (sum >> 16);
+
+  // Take the one's complement of sum
+  return ((USHORT) ~sum);
+}
+
+// check IPv6 packet for "is this an IPv6 Neighbor Solicitation that
+// the tap driver needs to answer?"
+// see RFC 4861 4.3 for the different cases
+static IPV6ADDR IPV6_NS_TARGET_MCAST =
+	{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+          0x00, 0x00, 0x00, 0x01, 0xff, 0x00, 0x00, 0x08 };
+static IPV6ADDR IPV6_NS_TARGET_UNICAST =
+	{ 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08 };
+
+BOOLEAN
+HandleIPv6NeighborDiscovery( TapAdapterPointer p_Adapter, UCHAR * m_Data )
+{
+    const ETH_HEADER * e = (ETH_HEADER *) m_Data;
+    const IPV6HDR *ipv6 = (IPV6HDR *) (m_Data + sizeof (ETH_HEADER));
+    const ICMPV6_NS * icmpv6_ns = (ICMPV6_NS *) (m_Data + sizeof (ETH_HEADER) + sizeof (IPV6HDR));
+    ICMPV6_NA_PKT *na;
+    USHORT icmpv6_len, icmpv6_csum;
+
+    // we don't really care about the destination MAC address here
+    // - it's either a multicast MAC, or the userland destination MAC
+    // but since the TAP driver is point-to-point, all packets are "for us"
+
+    // IPv6 target address must be ff02::1::ff00:8 (multicast for
+    // initial NS) or fe80::1 (unicast for recurrent NUD)
+    if ( memcmp( ipv6->daddr, IPV6_NS_TARGET_MCAST,
+		 sizeof(IPV6ADDR) ) != 0 &&
+         memcmp( ipv6->daddr, IPV6_NS_TARGET_UNICAST,
+		 sizeof(IPV6ADDR) ) != 0 )
+    {
+	return FALSE;				// wrong target address
+    }
+
+    // IPv6 Next-Header must be ICMPv6
+    if ( ipv6->nexthdr != IPPROTO_ICMPV6 )
+    {
+	return FALSE;				// wrong next-header
+    }
+
+    // ICMPv6 type+code must be 135/0 for NS
+    if ( icmpv6_ns->type != ICMPV6_TYPE_NS ||
+	 icmpv6_ns->code != ICMPV6_CODE_0 )
+    {
+	return FALSE;				// wrong ICMPv6 type
+    }
+
+    // ICMPv6 target address must be fe80::8 (magic)
+    if ( memcmp( icmpv6_ns->target_addr, IPV6_NS_TARGET_UNICAST,
+	         sizeof(IPV6ADDR) ) != 0 )
+    {
+	return FALSE;				// not for us
+    }
+
+    // packet identified, build magic response packet
+
+    na = (ICMPV6_NA_PKT *) MemAlloc (sizeof (ICMPV6_NA_PKT), TRUE);
+    if ( !na ) return FALSE;
+
+    //------------------------------------------------
+    // Initialize Neighbour Advertisement reply packet
+    //------------------------------------------------
+
+    // ethernet header
+    na->eth.proto = htons(ETH_P_IPV6);
+    COPY_MAC(na->eth.dest, p_Adapter->m_MAC);
+    COPY_MAC(na->eth.src, p_Adapter->m_TapToUser.dest);
+
+    // IPv6 header
+    na->ipv6.version_prio = ipv6->version_prio;
+    NdisMoveMemory( na->ipv6.flow_lbl, ipv6->flow_lbl,
+		    sizeof(na->ipv6.flow_lbl) );
+    icmpv6_len = sizeof(ICMPV6_NA_PKT) - sizeof(ETH_HEADER) - sizeof(IPV6HDR);
+    na->ipv6.payload_len = htons(icmpv6_len);
+    na->ipv6.nexthdr = IPPROTO_ICMPV6;
+    na->ipv6.hop_limit = 255;
+    NdisMoveMemory( na->ipv6.saddr, IPV6_NS_TARGET_UNICAST,
+		    sizeof(IPV6ADDR) );
+    NdisMoveMemory( na->ipv6.daddr, ipv6->saddr,
+		    sizeof(IPV6ADDR) );
+
+    // ICMPv6
+    na->icmpv6.type = ICMPV6_TYPE_NA;
+    na->icmpv6.code = ICMPV6_CODE_0;
+    na->icmpv6.checksum = 0;
+    na->icmpv6.rso_bits = 0x60;		// Solicited + Override
+    NdisZeroMemory( na->icmpv6.reserved, sizeof(na->icmpv6.reserved) );
+    NdisMoveMemory( na->icmpv6.target_addr, IPV6_NS_TARGET_UNICAST,
+		    sizeof(IPV6ADDR) );
+
+    // ICMPv6 option "Target Link Layer Address"
+    na->icmpv6.opt_type = ICMPV6_OPTION_TLLA;
+    na->icmpv6.opt_length = ICMPV6_LENGTH_TLLA;
+    COPY_MAC( na->icmpv6.target_macaddr, p_Adapter->m_TapToUser.dest );
+
+    // calculate and set checksum
+    icmpv6_csum = icmpv6_checksum ( (UCHAR*) &(na->icmpv6),
+				    icmpv6_len,
+				    na->ipv6.saddr,
+				    na->ipv6.daddr );
+    na->icmpv6.checksum = htons( icmpv6_csum );
+
+    DUMP_PACKET ("HandleIPv6NeighborDiscovery",
+		 (unsigned char *) na,
+		 sizeof (ICMPV6_NA_PKT));
+
+    InjectPacketDeferred (p_Adapter, (UCHAR *) na, sizeof (ICMPV6_NA_PKT));
+
+    MemFree (na, sizeof (ICMPV6_NA_PKT));
+
+    return TRUE;				// all fine
+}
+
+//====================================================================
+//                               Adapter Transmission
+//====================================================================
+NDIS_STATUS
+AdapterTransmit (IN NDIS_HANDLE p_AdapterContext,
+		 IN PNDIS_PACKET p_Packet,
+		 IN UINT p_Flags)
+{
+  TapAdapterPointer l_Adapter = (TapAdapterPointer) p_AdapterContext;
+  ULONG l_Index = 0, l_PacketLength = 0;
+  UINT l_BufferLength = 0;
+  PIRP l_IRP;
+  TapPacketPointer l_PacketBuffer;
+  PNDIS_BUFFER l_NDIS_Buffer;
+  PUCHAR l_Buffer;
+  PVOID result;
+
+  NdisQueryPacket (p_Packet, NULL, NULL, &l_NDIS_Buffer, &l_PacketLength);
+
+  //====================================================
+  // Here we abandon the transmission attempt if any of
+  // the parameters is wrong or memory allocation fails
+  // but we do not indicate failure. The packet is
+  // silently dropped.
+  //====================================================
+
+  if (l_PacketLength < ETHERNET_HEADER_SIZE || l_PacketLength > 65535)
+    goto exit_fail;
+  else if (!l_Adapter->m_Extension.m_TapOpens || !l_Adapter->m_MediaState)
+    goto exit_success;              // Nothing is bound to the TAP device
+
+  if (NdisAllocateMemoryWithTag (&l_PacketBuffer,
+				 TAP_PACKET_SIZE (l_PacketLength),
+				 '5PAT') != NDIS_STATUS_SUCCESS)
+    goto exit_no_resources;
+
+  if (l_PacketBuffer == NULL)
+    goto exit_no_resources;
+
+  l_PacketBuffer->m_SizeFlags = (l_PacketLength & TP_SIZE_MASK);
+
+  //===========================
+  // Reassemble packet contents
+  //===========================
+
+  __try
+  {
+    l_Index = 0;
+    while (l_NDIS_Buffer && l_Index < l_PacketLength)
+      {
+	ULONG newlen;
+	NdisQueryBuffer (l_NDIS_Buffer, (PVOID *) & l_Buffer,
+			 &l_BufferLength);
+	newlen = l_Index + l_BufferLength;
+	if (newlen > l_PacketLength)
+	  {
+	    NOTE_ERROR ();
+	    goto no_queue; /* overflow */
+	  }
+	NdisMoveMemory (l_PacketBuffer->m_Data + l_Index, l_Buffer,
+			l_BufferLength);
+	l_Index = newlen;
+	NdisGetNextBuffer (l_NDIS_Buffer, &l_NDIS_Buffer);
+      }
+    if (l_Index != l_PacketLength)
+      {
+	NOTE_ERROR ();
+	goto no_queue; /* underflow */
+      }
+
+    DUMP_PACKET ("AdapterTransmit", l_PacketBuffer->m_Data, l_PacketLength);
+
+    //=====================================================
+    // If IPv4 packet, check whether or not packet
+    // was truncated.
+    //=====================================================
+#if PACKET_TRUNCATION_CHECK
+    IPv4PacketSizeVerify (l_PacketBuffer->m_Data, l_PacketLength, FALSE, "TX", &l_Adapter->m_TxTrunc);
+#endif
+
+    //=====================================================
+    // Are we running in DHCP server masquerade mode?
+    //
+    // If so, catch both DHCP requests and ARP queries
+    // to resolve the address of our virtual DHCP server.
+    //=====================================================
+    if (l_Adapter->m_dhcp_enabled)
+      {
+	const ETH_HEADER *eth = (ETH_HEADER *) l_PacketBuffer->m_Data;
+	const IPHDR *ip = (IPHDR *) (l_PacketBuffer->m_Data + sizeof (ETH_HEADER));
+	const UDPHDR *udp = (UDPHDR *) (l_PacketBuffer->m_Data + sizeof (ETH_HEADER) + sizeof (IPHDR));
+
+	// ARP packet?
+	if (l_PacketLength == sizeof (ARP_PACKET)
+	    && eth->proto == htons (ETH_P_ARP)
+	    && l_Adapter->m_dhcp_server_arp)
+	  {
+	    if (ProcessARP (l_Adapter,
+			    (PARP_PACKET) l_PacketBuffer->m_Data,
+			    l_Adapter->m_dhcp_addr,
+			    l_Adapter->m_dhcp_server_ip,
+			    ~0,
+			    l_Adapter->m_dhcp_server_mac))
+	      goto no_queue;
+	  }
+
+	// DHCP packet?
+	else if (l_PacketLength >= sizeof (ETH_HEADER) + sizeof (IPHDR) + sizeof (UDPHDR) + sizeof (DHCP)
+		 && eth->proto == htons (ETH_P_IP)
+		 && ip->version_len == 0x45 // IPv4, 20 byte header
+		 && ip->protocol == IPPROTO_UDP
+		 && udp->dest == htons (BOOTPS_PORT))
+	  {
+	    const DHCP *dhcp = (DHCP *) (l_PacketBuffer->m_Data
+					 + sizeof (ETH_HEADER)
+					 + sizeof (IPHDR)
+					 + sizeof (UDPHDR));
+
+	    const int optlen = l_PacketLength
+	      - sizeof (ETH_HEADER)
+	      - sizeof (IPHDR)
+	      - sizeof (UDPHDR)
+	      - sizeof (DHCP);
+
+	    if (optlen > 0) // we must have at least one DHCP option
+	      {
+		if (ProcessDHCP (l_Adapter, eth, ip, udp, dhcp, optlen))
+		  goto no_queue;
+	      }
+	    else
+	      goto no_queue;
+	  }
+      }
+
+    //===============================================
+    // In Point-To-Point mode, check to see whether
+    // packet is ARP (handled) or IPv4 (sent to app).
+    // IPv6 packets are inspected for neighbour discovery
+    // (to be handled locally), and the rest is forwarded
+    // all other protocols are dropped
+    //===============================================
+    if (l_Adapter->m_tun)
+      {
+	ETH_HEADER *e;
+
+	if (l_PacketLength < ETHERNET_HEADER_SIZE)
+	  goto no_queue;
+
+	e = (ETH_HEADER *) l_PacketBuffer->m_Data;
+
+	switch (ntohs (e->proto))
+	  {
+	  case ETH_P_ARP:
+
+	    // Make sure that packet is the
+	    // right size for ARP.
+	    if (l_PacketLength != sizeof (ARP_PACKET))
+	      goto no_queue;
+
+	    ProcessARP (l_Adapter,
+			(PARP_PACKET) l_PacketBuffer->m_Data,
+			l_Adapter->m_localIP,
+			l_Adapter->m_remoteNetwork,
+			l_Adapter->m_remoteNetmask,
+			l_Adapter->m_TapToUser.dest);
+
+	  default:
+	    goto no_queue;
+
+	  case ETH_P_IP:
+
+	    // Make sure that packet is large
+	    // enough to be IPv4.
+	    if (l_PacketLength
+		< ETHERNET_HEADER_SIZE + IP_HEADER_SIZE)
+	      goto no_queue;
+
+	    // Only accept directed packets,
+	    // not broadcasts.
+	    if (memcmp (e, &l_Adapter->m_TapToUser, ETHERNET_HEADER_SIZE))
+	      goto no_queue;
+
+	    // Packet looks like IPv4, queue it.
+	    l_PacketBuffer->m_SizeFlags |= TP_TUN;
+	    break;
+
+	  case ETH_P_IPV6:
+	    // make sure that packet is large
+	    // enough to be IPv6
+	    if (l_PacketLength
+		< ETHERNET_HEADER_SIZE + IPV6_HEADER_SIZE)
+	      goto no_queue;
+
+	    // broadcasts and multicasts are handled specially
+	    // (to be implemented)
+
+	    // neighbor discovery packets to fe80::8 are special
+	    // OpenVPN sets this next-hop to signal "handled by tapdrv"
+	    if ( HandleIPv6NeighborDiscovery( l_Adapter,
+					      l_PacketBuffer->m_Data ))
+	      {
+		goto no_queue;
+	      }
+
+	    // Packet looks like IPv6, queue it :-)
+	    l_PacketBuffer->m_SizeFlags |= TP_TUN;
+	  }
+      }
+
+    //===============================================
+    // Push packet onto queue to wait for read from
+    // userspace.
+    //===============================================
+
+    NdisAcquireSpinLock (&l_Adapter->m_Extension.m_QueueLock);
+
+    result = NULL;
+    if (IS_UP (l_Adapter))
+      result = QueuePush (l_Adapter->m_Extension.m_PacketQueue, l_PacketBuffer);
+
+    NdisReleaseSpinLock (&l_Adapter->m_Extension.m_QueueLock);
+
+    if ((TapPacketPointer) result != l_PacketBuffer)
+      {
+	// adapter receive overrun
+	INCREMENT_STAT (l_Adapter->m_TxErr);
+	goto no_queue;
+      }
+    else
+      {
+	INCREMENT_STAT (l_Adapter->m_Tx);
+      }
+
+    //============================================================
+    // Cycle through IRPs and packets, try to satisfy each pending
+    // IRP with a queued packet.
+    //============================================================
+    while (TRUE)
+      {
+	l_IRP = NULL;
+	l_PacketBuffer = NULL;
+
+	NdisAcquireSpinLock (&l_Adapter->m_Extension.m_QueueLock);
+
+	if (IS_UP (l_Adapter)
+	    && QueueCount (l_Adapter->m_Extension.m_PacketQueue)
+	    && QueueCount (l_Adapter->m_Extension.m_IrpQueue))
+	  {
+	    l_IRP = (PIRP) QueuePop (l_Adapter->m_Extension.m_IrpQueue);
+	    l_PacketBuffer = (TapPacketPointer)
+	      QueuePop (l_Adapter->m_Extension.m_PacketQueue);
+	  }
+
+	NdisReleaseSpinLock (&l_Adapter->m_Extension.m_QueueLock);
+
+	MYASSERT ((l_IRP != NULL) + (l_PacketBuffer != NULL) != 1);
+
+	if (l_IRP && l_PacketBuffer)
+	  {
+	    CompleteIRP (l_IRP,
+			 l_PacketBuffer, 
+			 IO_NETWORK_INCREMENT);
+	  }
+	else
+	  break;
+      }
+  }
+  __except (EXCEPTION_EXECUTE_HANDLER)
+    {
+    }
+
+  return NDIS_STATUS_SUCCESS;
+
+ no_queue:
+  NdisFreeMemory (l_PacketBuffer,
+		  TAP_PACKET_SIZE (l_PacketLength),
+		  0);
+  
+ exit_success:
+  return NDIS_STATUS_SUCCESS;
+    
+ exit_fail:
+  return NDIS_STATUS_FAILURE;
+
+ exit_no_resources:
+  return NDIS_STATUS_RESOURCES;
+}
+
+//======================================================================
+// Hooks for catching TAP device IRP's.
+//======================================================================
+
+DRIVER_DISPATCH TapDeviceHook;
+NTSTATUS
+TapDeviceHook (IN PDEVICE_OBJECT p_DeviceObject, IN PIRP p_IRP)
+{
+  TapAdapterPointer l_Adapter = LookupAdapterInInstanceList (p_DeviceObject);
+  PIO_STACK_LOCATION l_IrpSp;
+  NTSTATUS l_Status = STATUS_SUCCESS;
+  BOOLEAN accessible;
+
+  l_IrpSp = IoGetCurrentIrpStackLocation (p_IRP);
+
+  p_IRP->IoStatus.Status = STATUS_SUCCESS;
+  p_IRP->IoStatus.Information = 0;
+
+  if (!l_Adapter || l_Adapter->m_Extension.m_Halt)
+    {
+      DEBUGP (("TapDeviceHook called when TAP device is halted, MajorFunction=%d\n",
+	       (int)l_IrpSp->MajorFunction));
+
+      if (l_IrpSp->MajorFunction == IRP_MJ_CLOSE)
+	{
+	  IoCompleteRequest (p_IRP, IO_NO_INCREMENT);
+	  return STATUS_SUCCESS;
+	}
+      else
+	{
+	  p_IRP->IoStatus.Status = STATUS_NO_SUCH_DEVICE;
+	  IoCompleteRequest (p_IRP, IO_NO_INCREMENT);
+	  return STATUS_NO_SUCH_DEVICE;
+	}
+    }
+
+  switch (l_IrpSp->MajorFunction)
+    {
+      //===========================================================
+      //                 Ioctl call handlers
+      //===========================================================
+    case IRP_MJ_DEVICE_CONTROL:
+      {
+	switch (l_IrpSp->Parameters.DeviceIoControl.IoControlCode)
+	  {
+	  case TAP_WIN_IOCTL_GET_MAC:
+	    {
+	      if (l_IrpSp->Parameters.DeviceIoControl.OutputBufferLength
+		  >= sizeof (MACADDR))
+		{
+		  COPY_MAC (p_IRP->AssociatedIrp.SystemBuffer,
+			    l_Adapter->m_MAC);
+		  p_IRP->IoStatus.Information = sizeof (MACADDR);
+		}
+	      else
+		{
+		  NOTE_ERROR ();
+		  p_IRP->IoStatus.Status = l_Status = STATUS_BUFFER_TOO_SMALL;
+		}
+	      break;
+	    }
+	  case TAP_WIN_IOCTL_GET_VERSION:
+	    {
+	      const ULONG size = sizeof (ULONG) * 3;
+	      if (l_IrpSp->Parameters.DeviceIoControl.OutputBufferLength
+		  >= size)
+		{
+		  ((PULONG) (p_IRP->AssociatedIrp.SystemBuffer))[0]
+		    = TAP_DRIVER_MAJOR_VERSION;
+		  ((PULONG) (p_IRP->AssociatedIrp.SystemBuffer))[1]
+		    = TAP_DRIVER_MINOR_VERSION;
+		  ((PULONG) (p_IRP->AssociatedIrp.SystemBuffer))[2]
+#if DBG
+		    = 1;
+#else
+		  = 0;
+#endif
+		  p_IRP->IoStatus.Information = size;
+		}
+	      else
+		{
+		  NOTE_ERROR ();
+		  p_IRP->IoStatus.Status = l_Status = STATUS_BUFFER_TOO_SMALL;
+		}
+
+	      break;
+	    }
+	  case TAP_WIN_IOCTL_GET_MTU:
+	    {
+	      const ULONG size = sizeof (ULONG) * 1;
+	      if (l_IrpSp->Parameters.DeviceIoControl.OutputBufferLength
+		  >= size)
+		{
+		  ((PULONG) (p_IRP->AssociatedIrp.SystemBuffer))[0]
+		    = l_Adapter->m_MTU;
+		  p_IRP->IoStatus.Information = size;
+		}
+	      else
+		{
+		  NOTE_ERROR ();
+		  p_IRP->IoStatus.Status = l_Status = STATUS_BUFFER_TOO_SMALL;
+		}
+
+	      break;
+	    }
+	  case TAP_WIN_IOCTL_GET_INFO:
+	    {
+	      char state[16];
+	      if (l_Adapter->m_InterfaceIsRunning)
+		state[0] = 'A';
+	      else
+		state[0] = 'a';
+	      if (l_Adapter->m_Extension.m_TapIsRunning)
+		state[1] = 'T';
+	      else
+		state[1] = 't';
+	      state[2] = l_Adapter->m_DeviceState;
+	      if (l_Adapter->m_MediaStateAlwaysConnected)
+		state[3] = 'C';
+	      else
+		state[3] = 'c';
+	      state[4] = '\0';
+
+	      p_IRP->IoStatus.Status = l_Status = RtlStringCchPrintfExA (
+	        ((LPTSTR) (p_IRP->AssociatedIrp.SystemBuffer)),
+		l_IrpSp->Parameters.DeviceIoControl.OutputBufferLength,
+		NULL,
+		NULL,
+		STRSAFE_FILL_BEHIND_NULL | STRSAFE_IGNORE_NULLS,
+#if PACKET_TRUNCATION_CHECK
+		"State=%s Err=[%s/%d] #O=%d Tx=[%d,%d,%d] Rx=[%d,%d,%d] IrpQ=[%d,%d,%d] PktQ=[%d,%d,%d] InjQ=[%d,%d,%d]",
+#else
+		"State=%s Err=[%s/%d] #O=%d Tx=[%d,%d] Rx=[%d,%d] IrpQ=[%d,%d,%d] PktQ=[%d,%d,%d] InjQ=[%d,%d,%d]",
+#endif
+		state,
+		g_LastErrorFilename,
+		g_LastErrorLineNumber,
+		(int)l_Adapter->m_Extension.m_NumTapOpens,
+		(int)l_Adapter->m_Tx,
+		(int)l_Adapter->m_TxErr,
+#if PACKET_TRUNCATION_CHECK
+		(int)l_Adapter->m_TxTrunc,
+#endif
+		(int)l_Adapter->m_Rx,
+		(int)l_Adapter->m_RxErr,
+#if PACKET_TRUNCATION_CHECK
+		(int)l_Adapter->m_RxTrunc,
+#endif
+		(int)l_Adapter->m_Extension.m_IrpQueue->size,
+		(int)l_Adapter->m_Extension.m_IrpQueue->max_size,
+		(int)IRP_QUEUE_SIZE,
+		(int)l_Adapter->m_Extension.m_PacketQueue->size,
+		(int)l_Adapter->m_Extension.m_PacketQueue->max_size,
+		(int)PACKET_QUEUE_SIZE,
+		(int)l_Adapter->m_Extension.m_InjectQueue->size,
+		(int)l_Adapter->m_Extension.m_InjectQueue->max_size,
+		(int)INJECT_QUEUE_SIZE
+		);
+
+	      p_IRP->IoStatus.Information
+		= l_IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+
+	      break;
+	    }
+
+#if DBG
+	  case TAP_WIN_IOCTL_GET_LOG_LINE:
+	    {
+	      if (GetDebugLine ((LPTSTR)p_IRP->AssociatedIrp.SystemBuffer,
+				l_IrpSp->Parameters.DeviceIoControl.OutputBufferLength))
+		p_IRP->IoStatus.Status = l_Status = STATUS_SUCCESS;
+	      else
+		p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL;
+
+	      p_IRP->IoStatus.Information
+		= l_IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+
+	      break;
+	    }
+#endif
+
+	  case TAP_WIN_IOCTL_CONFIG_TUN:
+	    {
+	      if (l_IrpSp->Parameters.DeviceIoControl.InputBufferLength >=
+		  (sizeof (IPADDR) * 3))
+		{
+		  MACADDR dest;
+
+		  l_Adapter->m_tun = FALSE;
+
+		  GenerateRelatedMAC (dest, l_Adapter->m_MAC, 1);
+
+		  l_Adapter->m_localIP =       ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[0];
+		  l_Adapter->m_remoteNetwork = ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[1];
+		  l_Adapter->m_remoteNetmask = ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[2];
+
+		  // sanity check on network/netmask
+		  if ((l_Adapter->m_remoteNetwork & l_Adapter->m_remoteNetmask) != l_Adapter->m_remoteNetwork)
+		    {
+		      NOTE_ERROR ();
+		      p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER;
+		      break;
+		    }
+
+		  COPY_MAC (l_Adapter->m_TapToUser.src, l_Adapter->m_MAC);
+		  COPY_MAC (l_Adapter->m_TapToUser.dest, dest);
+		  COPY_MAC (l_Adapter->m_UserToTap.src, dest);
+		  COPY_MAC (l_Adapter->m_UserToTap.dest, l_Adapter->m_MAC);
+
+		  l_Adapter->m_TapToUser.proto = l_Adapter->m_UserToTap.proto = htons (ETH_P_IP);
+		  l_Adapter->m_UserToTap_IPv6 = l_Adapter->m_UserToTap;
+		  l_Adapter->m_UserToTap_IPv6.proto = htons(ETH_P_IPV6);
+
+		  l_Adapter->m_tun = TRUE;
+
+		  CheckIfDhcpAndTunMode (l_Adapter);
+
+		  p_IRP->IoStatus.Information = 1; // Simple boolean value
+		}
+	      else
+		{
+		  NOTE_ERROR ();
+		  p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER;
+		}
+	      
+	      break;
+	    }
+
+	  case TAP_WIN_IOCTL_CONFIG_POINT_TO_POINT: // Obsoleted by TAP_WIN_IOCTL_CONFIG_TUN
+	    {
+	      if (l_IrpSp->Parameters.DeviceIoControl.InputBufferLength >=
+		  (sizeof (IPADDR) * 2))
+		{
+		  MACADDR dest;
+
+		  l_Adapter->m_tun = FALSE;
+
+		  GenerateRelatedMAC (dest, l_Adapter->m_MAC, 1);
+
+		  l_Adapter->m_localIP =       ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[0];
+		  l_Adapter->m_remoteNetwork = ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[1];
+		  l_Adapter->m_remoteNetmask = ~0;
+
+		  COPY_MAC (l_Adapter->m_TapToUser.src, l_Adapter->m_MAC);
+		  COPY_MAC (l_Adapter->m_TapToUser.dest, dest);
+		  COPY_MAC (l_Adapter->m_UserToTap.src, dest);
+		  COPY_MAC (l_Adapter->m_UserToTap.dest, l_Adapter->m_MAC);
+
+		  l_Adapter->m_TapToUser.proto = l_Adapter->m_UserToTap.proto = htons (ETH_P_IP);
+		  l_Adapter->m_UserToTap_IPv6 = l_Adapter->m_UserToTap;
+		  l_Adapter->m_UserToTap_IPv6.proto = htons(ETH_P_IPV6);
+
+		  l_Adapter->m_tun = TRUE;
+
+		  CheckIfDhcpAndTunMode (l_Adapter);
+
+		  p_IRP->IoStatus.Information = 1; // Simple boolean value
+		}
+	      else
+		{
+		  NOTE_ERROR ();
+		  p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER;
+		}
+	      
+	      break;
+	    }
+
+	  case TAP_WIN_IOCTL_SET_MEDIA_STATUS:
+	    {
+	      if (l_IrpSp->Parameters.DeviceIoControl.InputBufferLength >=
+		  (sizeof (ULONG) * 1))
+		{
+		  ULONG parm = ((PULONG) (p_IRP->AssociatedIrp.SystemBuffer))[0];
+		  SetMediaStatus (l_Adapter, (BOOLEAN) parm);
+		  p_IRP->IoStatus.Information = 1;
+		}
+	      else
+		{
+		  NOTE_ERROR ();
+		  p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER;
+		}
+	      break;
+	    }
+
+	  case TAP_WIN_IOCTL_CONFIG_DHCP_MASQ:
+	    {
+	      if (l_IrpSp->Parameters.DeviceIoControl.InputBufferLength >=
+		  (sizeof (IPADDR) * 4))
+		{
+		  l_Adapter->m_dhcp_enabled = FALSE;
+		  l_Adapter->m_dhcp_server_arp = FALSE;
+		  l_Adapter->m_dhcp_user_supplied_options_buffer_len = 0;
+
+		  // Adapter IP addr / netmask
+		  l_Adapter->m_dhcp_addr =
+		    ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[0];
+		  l_Adapter->m_dhcp_netmask =
+		    ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[1];
+
+		  // IP addr of DHCP masq server
+		  l_Adapter->m_dhcp_server_ip =
+		    ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[2];
+
+		  // Lease time in seconds
+		  l_Adapter->m_dhcp_lease_time =
+		    ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[3];
+
+		  GenerateRelatedMAC (l_Adapter->m_dhcp_server_mac, l_Adapter->m_MAC, 2);
+
+		  l_Adapter->m_dhcp_enabled = TRUE;
+		  l_Adapter->m_dhcp_server_arp = TRUE;
+
+		  CheckIfDhcpAndTunMode (l_Adapter);
+
+		  p_IRP->IoStatus.Information = 1; // Simple boolean value
+		}
+	      else
+		{
+		  NOTE_ERROR ();
+		  p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER;
+		}
+	      
+	      break;
+	    }
+
+	  case TAP_WIN_IOCTL_CONFIG_DHCP_SET_OPT:
+	    {
+	      if (l_IrpSp->Parameters.DeviceIoControl.InputBufferLength <=
+		  DHCP_USER_SUPPLIED_OPTIONS_BUFFER_SIZE
+		  && l_Adapter->m_dhcp_enabled)
+		{
+		  l_Adapter->m_dhcp_user_supplied_options_buffer_len = 0;
+
+		  NdisMoveMemory (l_Adapter->m_dhcp_user_supplied_options_buffer,
+				  p_IRP->AssociatedIrp.SystemBuffer,
+				  l_IrpSp->Parameters.DeviceIoControl.InputBufferLength);
+		  
+		  l_Adapter->m_dhcp_user_supplied_options_buffer_len = 
+		    l_IrpSp->Parameters.DeviceIoControl.InputBufferLength;
+
+		  p_IRP->IoStatus.Information = 1; // Simple boolean value
+		}
+	      else
+		{
+		  NOTE_ERROR ();
+		  p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER;
+		}
+	      
+	      break;
+	    }
+
+	  default:
+	    {
+	      NOTE_ERROR ();
+	      p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER;
+	      break;
+	    }
+	  }
+
+	IoCompleteRequest (p_IRP, IO_NO_INCREMENT);
+	break;
+      }
+
+      //===========================================================
+      // User mode thread issued a read request on the tap device
+      // If there are packets waiting to be read, then the request
+      // will be satisfied here. If not, then the request will be
+      // queued and satisfied by any packet that is not used to
+      // satisfy requests ahead of it.
+      //===========================================================
+    case IRP_MJ_READ:
+      {
+	TapPacketPointer l_PacketBuffer;
+	BOOLEAN pending = FALSE;
+
+	// Save IRP-accessible copy of buffer length
+	p_IRP->IoStatus.Information = l_IrpSp->Parameters.Read.Length;
+
+	if (p_IRP->MdlAddress == NULL)
+	  {
+	    DEBUGP (("[%s] MdlAddress is NULL for IRP_MJ_READ\n",
+		     NAME (l_Adapter)));
+	    NOTE_ERROR ();
+	    p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER;
+	    p_IRP->IoStatus.Information = 0;
+	    IoCompleteRequest (p_IRP, IO_NO_INCREMENT);
+	    break;
+	  }
+	else if ((p_IRP->AssociatedIrp.SystemBuffer =
+		  MmGetSystemAddressForMdlSafe
+		  (p_IRP->MdlAddress, NormalPagePriority)) == NULL)
+	  {
+	    DEBUGP (("[%s] Could not map address in IRP_MJ_READ\n",
+		     NAME (l_Adapter)));
+	    NOTE_ERROR ();
+	    p_IRP->IoStatus.Status = l_Status = STATUS_INSUFFICIENT_RESOURCES;
+	    p_IRP->IoStatus.Information = 0;
+	    IoCompleteRequest (p_IRP, IO_NO_INCREMENT);
+	    break;
+	  }
+	else if (!l_Adapter->m_InterfaceIsRunning)
+	  {
+	    DEBUGP (("[%s] Interface is down in IRP_MJ_READ\n",
+		     NAME (l_Adapter)));
+	    NOTE_ERROR ();
+	    p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL;
+	    p_IRP->IoStatus.Information = 0;
+	    IoCompleteRequest (p_IRP, IO_NO_INCREMENT);
+	    break;
+	  }
+
+	//==================================
+	// Can we provide immediate service?
+	//==================================
+
+	l_PacketBuffer = NULL;
+
+	NdisAcquireSpinLock (&l_Adapter->m_Extension.m_QueueLock);
+
+	if (IS_UP (l_Adapter)
+	    && QueueCount (l_Adapter->m_Extension.m_PacketQueue)
+	    && QueueCount (l_Adapter->m_Extension.m_IrpQueue) == 0)
+	  {
+	    l_PacketBuffer = (TapPacketPointer)
+	      QueuePop (l_Adapter->m_Extension.m_PacketQueue);
+	  }
+
+	NdisReleaseSpinLock (&l_Adapter->m_Extension.m_QueueLock);
+
+	if (l_PacketBuffer)
+	  {
+	    l_Status = CompleteIRP (p_IRP,
+				    l_PacketBuffer,
+				    IO_NO_INCREMENT);
+	    break;
+	  }
+
+	//=============================
+	// Attempt to pend read request
+	//=============================
+
+	NdisAcquireSpinLock (&l_Adapter->m_Extension.m_QueueLock);
+
+	if (IS_UP (l_Adapter)
+	    && QueuePush (l_Adapter->m_Extension.m_IrpQueue, p_IRP) == (PIRP) p_IRP)
+	  {
+	    IoSetCancelRoutine (p_IRP, CancelIRPCallback);
+	    l_Status = STATUS_PENDING;
+	    IoMarkIrpPending (p_IRP);
+	    pending = TRUE;
+	  }
+
+	NdisReleaseSpinLock (&l_Adapter->m_Extension.m_QueueLock);
+
+	if (pending)
+	  break;
+
+	// Can't queue anymore IRP's
+	DEBUGP (("[%s] TAP [%s] read IRP overrun\n",
+		 NAME (l_Adapter), l_Adapter->m_Extension.m_TapName));
+	NOTE_ERROR ();
+	p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL;
+	p_IRP->IoStatus.Information = 0;
+	IoCompleteRequest (p_IRP, IO_NO_INCREMENT);
+	break;
+      }
+
+      //==============================================================
+      // User mode issued a WriteFile request on the TAP file handle.
+      // The request will always get satisfied here.  The call may
+      // fail if there are too many pending packets (queue full).
+      //==============================================================
+    case IRP_MJ_WRITE:
+      {
+	if (p_IRP->MdlAddress == NULL)
+	  {
+	    DEBUGP (("[%s] MdlAddress is NULL for IRP_MJ_WRITE\n",
+		     NAME (l_Adapter)));
+	    NOTE_ERROR ();
+	    p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER;
+	    p_IRP->IoStatus.Information = 0;
+	  }
+	else if ((p_IRP->AssociatedIrp.SystemBuffer =
+		  MmGetSystemAddressForMdlSafe
+		  (p_IRP->MdlAddress, NormalPagePriority)) == NULL)
+	  {
+	    DEBUGP (("[%s] Could not map address in IRP_MJ_WRITE\n",
+		     NAME (l_Adapter)));
+	    NOTE_ERROR ();
+	    p_IRP->IoStatus.Status = l_Status = STATUS_INSUFFICIENT_RESOURCES;
+	    p_IRP->IoStatus.Information = 0;
+	  }
+	else if (!l_Adapter->m_InterfaceIsRunning)
+	  {
+	    DEBUGP (("[%s] Interface is down in IRP_MJ_WRITE\n",
+		     NAME (l_Adapter)));
+	    NOTE_ERROR ();
+	    p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL;
+	    p_IRP->IoStatus.Information = 0;
+	  }
+	else if (!l_Adapter->m_tun && ((l_IrpSp->Parameters.Write.Length) >= ETHERNET_HEADER_SIZE))
+	  {
+	    __try
+	      {
+		p_IRP->IoStatus.Information = l_IrpSp->Parameters.Write.Length;
+
+		DUMP_PACKET ("IRP_MJ_WRITE ETH",
+			     (unsigned char *) p_IRP->AssociatedIrp.SystemBuffer,
+			     l_IrpSp->Parameters.Write.Length);
+
+    //=====================================================
+    // If IPv4 packet, check whether or not packet
+    // was truncated.
+    //=====================================================
+#if PACKET_TRUNCATION_CHECK
+		IPv4PacketSizeVerify ((unsigned char *) p_IRP->AssociatedIrp.SystemBuffer,
+				      l_IrpSp->Parameters.Write.Length,
+				      FALSE,
+				      "RX",
+				      &l_Adapter->m_RxTrunc);
+#endif
+
+		NdisMEthIndicateReceive
+		  (l_Adapter->m_MiniportAdapterHandle,
+		   (NDIS_HANDLE) l_Adapter,
+		   (unsigned char *) p_IRP->AssociatedIrp.SystemBuffer,
+		   ETHERNET_HEADER_SIZE,
+		   (unsigned char *) p_IRP->AssociatedIrp.SystemBuffer + ETHERNET_HEADER_SIZE,
+		   l_IrpSp->Parameters.Write.Length - ETHERNET_HEADER_SIZE,
+		   l_IrpSp->Parameters.Write.Length - ETHERNET_HEADER_SIZE);
+		
+		NdisMEthIndicateReceiveComplete (l_Adapter->m_MiniportAdapterHandle);
+
+		p_IRP->IoStatus.Status = l_Status = STATUS_SUCCESS;
+	      }
+	    __except (EXCEPTION_EXECUTE_HANDLER)
+	      {
+		DEBUGP (("[%s] NdisMEthIndicateReceive failed in IRP_MJ_WRITE\n",
+			 NAME (l_Adapter)));
+		NOTE_ERROR ();
+		p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL;
+		p_IRP->IoStatus.Information = 0;
+	      }
+	  }
+	else if (l_Adapter->m_tun && ((l_IrpSp->Parameters.Write.Length) >= IP_HEADER_SIZE))
+	  {
+	    __try
+	      {
+		ETH_HEADER * p_UserToTap = &l_Adapter->m_UserToTap;
+
+		// for IPv6, need to use ethernet header with IPv6 proto
+		if ( IPH_GET_VER( ((IPHDR*) p_IRP->AssociatedIrp.SystemBuffer)->version_len) == 6 )
+		  {
+		    p_UserToTap = &l_Adapter->m_UserToTap_IPv6;
+		  }
+
+		p_IRP->IoStatus.Information = l_IrpSp->Parameters.Write.Length;
+
+		DUMP_PACKET2 ("IRP_MJ_WRITE P2P",
+			      p_UserToTap,
+			      (unsigned char *) p_IRP->AssociatedIrp.SystemBuffer,
+			      l_IrpSp->Parameters.Write.Length);
+
+    //=====================================================
+    // If IPv4 packet, check whether or not packet
+    // was truncated.
+    //=====================================================
+#if PACKET_TRUNCATION_CHECK
+		IPv4PacketSizeVerify ((unsigned char *) p_IRP->AssociatedIrp.SystemBuffer,
+				      l_IrpSp->Parameters.Write.Length,
+				      TRUE,
+				      "RX",
+				      &l_Adapter->m_RxTrunc);
+#endif
+
+		NdisMEthIndicateReceive
+		  (l_Adapter->m_MiniportAdapterHandle,
+		   (NDIS_HANDLE) l_Adapter,
+		   (unsigned char *) p_UserToTap,
+		   sizeof (ETH_HEADER),
+		   (unsigned char *) p_IRP->AssociatedIrp.SystemBuffer,
+		   l_IrpSp->Parameters.Write.Length,
+		   l_IrpSp->Parameters.Write.Length);
+
+		NdisMEthIndicateReceiveComplete (l_Adapter->m_MiniportAdapterHandle);
+
+		p_IRP->IoStatus.Status = l_Status = STATUS_SUCCESS;
+	      }
+	    __except (EXCEPTION_EXECUTE_HANDLER)
+	      {
+		DEBUGP (("[%s] NdisMEthIndicateReceive failed in IRP_MJ_WRITE (P2P)\n",
+			 NAME (l_Adapter)));
+		NOTE_ERROR ();
+		p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL;
+		p_IRP->IoStatus.Information = 0;
+	      }
+	  }
+	else
+	  {
+	    DEBUGP (("[%s] Bad buffer size in IRP_MJ_WRITE, len=%d\n",
+		     NAME (l_Adapter),
+		     l_IrpSp->Parameters.Write.Length));
+	    NOTE_ERROR ();
+	    p_IRP->IoStatus.Information = 0;	// ETHERNET_HEADER_SIZE;
+	    p_IRP->IoStatus.Status = l_Status = STATUS_BUFFER_TOO_SMALL;
+	  }
+
+	if (l_Status == STATUS_SUCCESS)
+	  INCREMENT_STAT (l_Adapter->m_Rx);
+	else
+	  INCREMENT_STAT (l_Adapter->m_RxErr);
+
+	IoCompleteRequest (p_IRP, IO_NO_INCREMENT);
+	break;
+      }
+
+      //--------------------------------------------------------------
+      //   User mode thread has called CreateFile() on the tap device
+      //--------------------------------------------------------------
+    case IRP_MJ_CREATE:
+      {
+	BOOLEAN succeeded = FALSE;
+	BOOLEAN mutex_succeeded;
+
+	DEBUGP
+	  (("[%s] [TAP] release [%d.%d] open request (m_TapOpens=%d)\n",
+	    NAME (l_Adapter), TAP_DRIVER_MAJOR_VERSION,
+	    TAP_DRIVER_MINOR_VERSION, l_Adapter->m_Extension.m_TapOpens));
+
+	ACQUIRE_MUTEX_ADAPTIVE (&l_Adapter->m_Extension.m_OpenCloseMutex, mutex_succeeded);
+	if (mutex_succeeded)
+	  {
+	    if (l_Adapter->m_Extension.m_TapIsRunning && !l_Adapter->m_Extension.m_TapOpens)
+	      {
+		ResetTapAdapterState (l_Adapter);
+		l_Adapter->m_Extension.m_TapOpens = 1;
+		succeeded = TRUE;
+	      }
+
+	    if (succeeded)
+	      {
+		INCREMENT_STAT (l_Adapter->m_Extension.m_NumTapOpens);
+		p_IRP->IoStatus.Status = l_Status = STATUS_SUCCESS;
+		p_IRP->IoStatus.Information = 0;
+	      }
+	    else
+	      {
+		DEBUGP (("[%s] TAP is presently unavailable (m_TapOpens=%d)\n",
+			 NAME (l_Adapter), l_Adapter->m_Extension.m_TapOpens));
+		NOTE_ERROR ();
+		p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL;
+		p_IRP->IoStatus.Information = 0;
+	      }
+
+	    RELEASE_MUTEX (&l_Adapter->m_Extension.m_OpenCloseMutex);
+	  }
+	else
+	  {
+	    DEBUGP (("[%s] TAP is presently locked (m_TapOpens=%d)\n",
+		     NAME (l_Adapter), l_Adapter->m_Extension.m_TapOpens));
+	    NOTE_ERROR ();
+	    p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL;
+	    p_IRP->IoStatus.Information = 0;
+	  }
+	
+	IoCompleteRequest (p_IRP, IO_NO_INCREMENT);
+	break;
+      }
+      
+      //-----------------------------------------------------------
+      //  User mode thread called CloseHandle() on the tap device
+      //-----------------------------------------------------------
+    case IRP_MJ_CLOSE:
+      {
+	BOOLEAN mutex_succeeded;
+
+	DEBUGP (("[%s] [TAP] release [%d.%d] close/cleanup request\n",
+		 NAME (l_Adapter), TAP_DRIVER_MAJOR_VERSION,
+		 TAP_DRIVER_MINOR_VERSION));
+
+	ACQUIRE_MUTEX_ADAPTIVE (&l_Adapter->m_Extension.m_OpenCloseMutex, mutex_succeeded);
+	if (mutex_succeeded)
+	  {
+	    l_Adapter->m_Extension.m_TapOpens = 0;
+	    ResetTapAdapterState (l_Adapter);
+	    FlushQueues (&l_Adapter->m_Extension);
+	    SetMediaStatus (l_Adapter, FALSE);
+	    RELEASE_MUTEX (&l_Adapter->m_Extension.m_OpenCloseMutex);
+	  }
+	else
+	  {
+	    DEBUGP (("[%s] TAP is presently locked (m_TapOpens=%d)\n",
+		     NAME (l_Adapter), l_Adapter->m_Extension.m_TapOpens));
+	    NOTE_ERROR ();
+	    p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL;
+	    p_IRP->IoStatus.Information = 0;
+	  }
+	
+	IoCompleteRequest (p_IRP, IO_NO_INCREMENT);
+	break;
+      }
+
+      //------------------
+      // Strange Request
+      //------------------
+    default:
+      {
+	//NOTE_ERROR ();
+	p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL;
+	IoCompleteRequest (p_IRP, IO_NO_INCREMENT);
+	break;
+      }
+    }
+
+  return l_Status;
+}
+
+//=============================================================
+// CompleteIRP is normally called with an adapter -> userspace
+// network packet and an IRP (Pending I/O request) from userspace.
+//
+// The IRP will normally represent a queued overlapped read
+// operation from userspace that is in a wait state.
+//
+// Use the ethernet packet to satisfy the IRP.
+//=============================================================
+
+NTSTATUS
+CompleteIRP (IN PIRP p_IRP,
+	     IN TapPacketPointer p_PacketBuffer,
+	     IN CCHAR PriorityBoost)
+{
+  NTSTATUS l_Status = STATUS_UNSUCCESSFUL;
+
+  int offset;
+  int len;
+
+  MYASSERT (p_IRP);
+  MYASSERT (p_PacketBuffer);
+
+  IoSetCancelRoutine (p_IRP, NULL);  // Disable cancel routine
+
+  //-------------------------------------------
+  // While p_PacketBuffer always contains a
+  // full ethernet packet, including the
+  // ethernet header, in point-to-point mode,
+  // we only want to return the IPv4
+  // component.
+  //-------------------------------------------
+
+  if (p_PacketBuffer->m_SizeFlags & TP_TUN)
+    {
+      offset = ETHERNET_HEADER_SIZE;
+      len = (int) (p_PacketBuffer->m_SizeFlags & TP_SIZE_MASK) - ETHERNET_HEADER_SIZE;
+    }
+  else
+    {
+      offset = 0;
+      len = (p_PacketBuffer->m_SizeFlags & TP_SIZE_MASK);
+    }
+
+  if (len < 0 || (int) p_IRP->IoStatus.Information < len)
+    {
+      p_IRP->IoStatus.Information = 0;
+      p_IRP->IoStatus.Status = STATUS_BUFFER_OVERFLOW;
+      NOTE_ERROR ();
+    }
+  else
+    {
+      p_IRP->IoStatus.Information = len;
+      p_IRP->IoStatus.Status = l_Status = STATUS_SUCCESS;
+
+      __try
+	{
+	  NdisMoveMemory (p_IRP->AssociatedIrp.SystemBuffer,
+			  p_PacketBuffer->m_Data + offset,
+			  len);
+	}
+      __except (EXCEPTION_EXECUTE_HANDLER)
+	{
+	  NOTE_ERROR ();
+	  p_IRP->IoStatus.Status = STATUS_UNSUCCESSFUL;
+	  p_IRP->IoStatus.Information = 0;
+	}
+    }
+
+  __try
+    {
+      NdisFreeMemory (p_PacketBuffer,
+		      TAP_PACKET_SIZE (p_PacketBuffer->m_SizeFlags & TP_SIZE_MASK),
+		      0);
+    }
+  __except (EXCEPTION_EXECUTE_HANDLER)
+    {
+    }
+  
+  if (l_Status == STATUS_SUCCESS)
+    {
+      IoCompleteRequest (p_IRP, PriorityBoost);
+    }
+  else
+    IoCompleteRequest (p_IRP, IO_NO_INCREMENT);
+
+  return l_Status;
+}
+
+//==============================================
+// IRPs get cancelled for a number of reasons.
+//
+// The TAP device could be closed by userspace
+// when there are still pending read operations.
+//
+// The user could disable the TAP adapter in the
+// network connections control panel, while the
+// device is still open by a process.
+//==============================================
+VOID
+CancelIRPCallback (IN PDEVICE_OBJECT p_DeviceObject,
+		   IN PIRP p_IRP)
+{
+  TapAdapterPointer l_Adapter = LookupAdapterInInstanceList (p_DeviceObject);
+  CancelIRP (l_Adapter ? &l_Adapter->m_Extension : NULL, p_IRP, TRUE);
+}
+
+VOID
+CancelIRP (TapExtensionPointer p_Extension,
+	   IN PIRP p_IRP,
+	   BOOLEAN callback)
+{
+  BOOLEAN exists = FALSE;
+
+  MYASSERT (p_IRP);
+
+  if (p_Extension)
+    {
+      NdisAcquireSpinLock (&p_Extension->m_QueueLock);
+      exists = (QueueExtract (p_Extension->m_IrpQueue, p_IRP) == p_IRP);
+      NdisReleaseSpinLock (&p_Extension->m_QueueLock);
+    }
+  else
+    exists = TRUE;
+
+  if (exists)
+    {
+      IoSetCancelRoutine (p_IRP, NULL);
+      p_IRP->IoStatus.Status = STATUS_CANCELLED;
+      p_IRP->IoStatus.Information = 0;
+    }
+     
+  if (callback)
+    IoReleaseCancelSpinLock (p_IRP->CancelIrql);
+
+  if (exists)
+    IoCompleteRequest (p_IRP, IO_NO_INCREMENT);
+}
+
+//===========================================
+// Exhaust packet, IRP, and injection queues.
+//===========================================
+VOID
+FlushQueues (TapExtensionPointer p_Extension)
+{
+  PIRP l_IRP;
+  TapPacketPointer l_PacketBuffer;
+  InjectPacketPointer l_InjectBuffer;
+  int n_IRP=0, n_Packet=0, n_Inject=0;
+
+  MYASSERT (p_Extension);
+  MYASSERT (p_Extension->m_TapDevice);
+
+  while (TRUE)
+    {
+      NdisAcquireSpinLock (&p_Extension->m_QueueLock);
+      l_IRP = QueuePop (p_Extension->m_IrpQueue);
+      NdisReleaseSpinLock (&p_Extension->m_QueueLock);
+      if (l_IRP)
+	{
+	  ++n_IRP;
+	  CancelIRP (NULL, l_IRP, FALSE);
+	}
+      else
+	break;
+    }
+
+  while (TRUE)
+    {
+      NdisAcquireSpinLock (&p_Extension->m_QueueLock);
+      l_PacketBuffer = QueuePop (p_Extension->m_PacketQueue);
+      NdisReleaseSpinLock (&p_Extension->m_QueueLock);
+      if (l_PacketBuffer)
+	{
+	  ++n_Packet;
+	  MemFree (l_PacketBuffer, TAP_PACKET_SIZE (l_PacketBuffer->m_SizeFlags & TP_SIZE_MASK));
+	}
+      else
+	break;
+    }
+
+  while (TRUE)
+    {
+      NdisAcquireSpinLock (&p_Extension->m_InjectLock);
+      l_InjectBuffer = QueuePop (p_Extension->m_InjectQueue);
+      NdisReleaseSpinLock (&p_Extension->m_InjectLock);
+      if (l_InjectBuffer)
+	{
+	  ++n_Inject;
+	  INJECT_PACKET_FREE(l_InjectBuffer);
+	}
+      else
+	break;
+    }
+
+  DEBUGP ((
+	   "[%s] [TAP] FlushQueues n_IRP=[%d,%d,%d] n_Packet=[%d,%d,%d] n_Inject=[%d,%d,%d]\n",
+	   p_Extension->m_TapName,
+	   n_IRP,
+	   p_Extension->m_IrpQueue->max_size,
+	   IRP_QUEUE_SIZE,
+	   n_Packet,
+	   p_Extension->m_PacketQueue->max_size,
+	   PACKET_QUEUE_SIZE,
+	   n_Inject,
+	   p_Extension->m_InjectQueue->max_size,
+	   INJECT_QUEUE_SIZE
+	   ));
+}
+
+//===================================================
+// Tell Windows whether the TAP device should be
+// considered "connected" or "disconnected".
+//===================================================
+VOID
+SetMediaStatus (TapAdapterPointer p_Adapter, BOOLEAN state)
+{
+  if (p_Adapter->m_MediaState != state && !p_Adapter->m_MediaStateAlwaysConnected)
+    {
+      if (state)
+	NdisMIndicateStatus (p_Adapter->m_MiniportAdapterHandle,
+			     NDIS_STATUS_MEDIA_CONNECT, NULL, 0);
+      else
+	NdisMIndicateStatus (p_Adapter->m_MiniportAdapterHandle,
+			     NDIS_STATUS_MEDIA_DISCONNECT, NULL, 0);
+
+      NdisMIndicateStatusComplete (p_Adapter->m_MiniportAdapterHandle);
+      p_Adapter->m_MediaState = state;
+    }
+}
+
+
+//======================================================
+// If DHCP mode is used together with tun
+// mode, consider the fact that the P2P remote subnet
+// might enclose the DHCP masq server address.
+//======================================================
+VOID
+CheckIfDhcpAndTunMode (TapAdapterPointer p_Adapter)
+{
+  if (p_Adapter->m_tun && p_Adapter->m_dhcp_enabled)
+    {
+      if ((p_Adapter->m_dhcp_server_ip & p_Adapter->m_remoteNetmask) == p_Adapter->m_remoteNetwork)
+	{
+	  COPY_MAC (p_Adapter->m_dhcp_server_mac, p_Adapter->m_TapToUser.dest);
+	  p_Adapter->m_dhcp_server_arp = FALSE;
+	}
+    }
+}
+
+//===================================================
+// Generate an ARP reply message for specific kinds
+// ARP queries.
+//===================================================
+BOOLEAN
+ProcessARP (TapAdapterPointer p_Adapter,
+	    const PARP_PACKET src,
+	    const IPADDR adapter_ip,
+	    const IPADDR ip_network,
+	    const IPADDR ip_netmask,
+	    const MACADDR mac)
+{
+  //-----------------------------------------------
+  // Is this the kind of packet we are looking for?
+  //-----------------------------------------------
+  if (src->m_Proto == htons (ETH_P_ARP)
+      && MAC_EQUAL (src->m_MAC_Source, p_Adapter->m_MAC)
+      && MAC_EQUAL (src->m_ARP_MAC_Source, p_Adapter->m_MAC)
+      && MAC_EQUAL (src->m_MAC_Destination, p_Adapter->m_MAC_Broadcast)
+      && src->m_ARP_Operation == htons (ARP_REQUEST)
+      && src->m_MAC_AddressType == htons (MAC_ADDR_TYPE)
+      && src->m_MAC_AddressSize == sizeof (MACADDR)
+      && src->m_PROTO_AddressType == htons (ETH_P_IP)
+      && src->m_PROTO_AddressSize == sizeof (IPADDR)
+      && src->m_ARP_IP_Source == adapter_ip
+      && (src->m_ARP_IP_Destination & ip_netmask) == ip_network
+      && src->m_ARP_IP_Destination != adapter_ip)
+    {
+      ARP_PACKET *arp = (ARP_PACKET *) MemAlloc (sizeof (ARP_PACKET), TRUE);
+      if (arp)
+	{
+	  //----------------------------------------------
+	  // Initialize ARP reply fields
+	  //----------------------------------------------
+	  arp->m_Proto = htons (ETH_P_ARP);
+	  arp->m_MAC_AddressType = htons (MAC_ADDR_TYPE);
+	  arp->m_PROTO_AddressType = htons (ETH_P_IP);
+	  arp->m_MAC_AddressSize = sizeof (MACADDR);
+	  arp->m_PROTO_AddressSize = sizeof (IPADDR);
+	  arp->m_ARP_Operation = htons (ARP_REPLY);
+
+	  //----------------------------------------------
+	  // ARP addresses
+	  //----------------------------------------------      
+	  COPY_MAC (arp->m_MAC_Source, mac);
+	  COPY_MAC (arp->m_MAC_Destination, p_Adapter->m_MAC);
+	  COPY_MAC (arp->m_ARP_MAC_Source, mac);
+	  COPY_MAC (arp->m_ARP_MAC_Destination, p_Adapter->m_MAC);
+	  arp->m_ARP_IP_Source = src->m_ARP_IP_Destination;
+	  arp->m_ARP_IP_Destination = adapter_ip;
+
+	  DUMP_PACKET ("ProcessARP",
+		       (unsigned char *) arp,
+		       sizeof (ARP_PACKET));
+
+	  InjectPacketDeferred (p_Adapter, (UCHAR *) arp, sizeof (ARP_PACKET));
+
+	  MemFree (arp, sizeof (ARP_PACKET));
+	}
+
+      return TRUE;
+    }
+  else
+    return FALSE;
+}
+
+//===============================================================
+// Used in cases where internally generated packets such as
+// ARP or DHCP replies must be returned to the kernel, to be
+// seen as an incoming packet "arriving" on the interface.
+//===============================================================
+
+// Defer packet injection till IRQL < DISPATCH_LEVEL
+VOID
+InjectPacketDeferred (TapAdapterPointer p_Adapter,
+		      UCHAR *packet,
+		      const unsigned int len)
+{
+  InjectPacketPointer l_InjectBuffer;
+  PVOID result;
+
+  if (NdisAllocateMemoryWithTag (&l_InjectBuffer,
+				 INJECT_PACKET_SIZE (len),
+				 'IPAT') == NDIS_STATUS_SUCCESS)
+    {
+      l_InjectBuffer->m_Size = len;
+      NdisMoveMemory (l_InjectBuffer->m_Data, packet, len);
+      NdisAcquireSpinLock (&p_Adapter->m_Extension.m_InjectLock);
+      result = QueuePush (p_Adapter->m_Extension.m_InjectQueue, l_InjectBuffer);
+      NdisReleaseSpinLock (&p_Adapter->m_Extension.m_InjectLock);
+      if (result)
+	KeInsertQueueDpc (&p_Adapter->m_Extension.m_InjectDpc, p_Adapter, NULL);
+      else
+	INJECT_PACKET_FREE(l_InjectBuffer);
+    }
+}
+
+// Handle the injection of previously deferred packets
+VOID
+InjectPacketDpc(KDPC *Dpc,
+		PVOID DeferredContext,
+		PVOID SystemArgument1,
+		PVOID SystemArgument2)
+{
+  InjectPacketPointer l_InjectBuffer;
+  TapAdapterPointer l_Adapter = (TapAdapterPointer)SystemArgument1;
+  while (TRUE)
+    {
+      NdisAcquireSpinLock (&l_Adapter->m_Extension.m_InjectLock);
+      l_InjectBuffer = QueuePop (l_Adapter->m_Extension.m_InjectQueue);
+      NdisReleaseSpinLock (&l_Adapter->m_Extension.m_InjectLock);
+      if (l_InjectBuffer)
+	{
+	  InjectPacketNow(l_Adapter, l_InjectBuffer->m_Data, l_InjectBuffer->m_Size);
+	  INJECT_PACKET_FREE(l_InjectBuffer);
+	}
+      else
+	break;
+    }
+}
+
+// Do packet injection now
+VOID
+InjectPacketNow (TapAdapterPointer p_Adapter,
+		 UCHAR *packet,
+		 const unsigned int len)
+{
+  MYASSERT (len >= ETHERNET_HEADER_SIZE);
+
+  __try
+    {
+      //------------------------------------------------------------
+      // NdisMEthIndicateReceive and NdisMEthIndicateReceiveComplete
+      // could potentially be called reentrantly both here and in
+      // TapDeviceHook/IRP_MJ_WRITE.
+      //
+      // The DDK docs imply that this is okay.
+      //
+      // Note that reentrant behavior could only occur if the
+      // non-deferred version of InjectPacket is used.
+      //------------------------------------------------------------
+      NdisMEthIndicateReceive
+	(p_Adapter->m_MiniportAdapterHandle,
+	 (NDIS_HANDLE) p_Adapter,
+	 packet,
+	 ETHERNET_HEADER_SIZE,
+	 packet + ETHERNET_HEADER_SIZE,
+	 len - ETHERNET_HEADER_SIZE,
+	 len - ETHERNET_HEADER_SIZE);
+      
+      NdisMEthIndicateReceiveComplete (p_Adapter->m_MiniportAdapterHandle);
+    }
+  __except (EXCEPTION_EXECUTE_HANDLER)
+    {
+      DEBUGP (("[%s] NdisMEthIndicateReceive failed in InjectPacketNow\n",
+	       NAME (p_Adapter)));
+      NOTE_ERROR ();
+    }
+}
+
+//===================================================================
+// Go back to default TAP mode from Point-To-Point mode.
+// Also reset (i.e. disable) DHCP Masq mode.
+//===================================================================
+VOID ResetTapAdapterState (TapAdapterPointer p_Adapter)
+{
+  // Point-To-Point
+  p_Adapter->m_tun = FALSE;
+  p_Adapter->m_localIP = 0;
+  p_Adapter->m_remoteNetwork = 0;
+  p_Adapter->m_remoteNetmask = 0;
+  NdisZeroMemory (&p_Adapter->m_TapToUser, sizeof (p_Adapter->m_TapToUser));
+  NdisZeroMemory (&p_Adapter->m_UserToTap, sizeof (p_Adapter->m_UserToTap));
+  NdisZeroMemory (&p_Adapter->m_UserToTap_IPv6, sizeof (p_Adapter->m_UserToTap_IPv6));
+
+  // DHCP Masq
+  p_Adapter->m_dhcp_enabled = FALSE;
+  p_Adapter->m_dhcp_server_arp = FALSE;
+  p_Adapter->m_dhcp_user_supplied_options_buffer_len = 0;
+  p_Adapter->m_dhcp_addr = 0;
+  p_Adapter->m_dhcp_netmask = 0;
+  p_Adapter->m_dhcp_server_ip = 0;
+  p_Adapter->m_dhcp_lease_time = 0;
+  p_Adapter->m_dhcp_received_discover = FALSE;
+  p_Adapter->m_dhcp_bad_requests = 0;
+  NdisZeroMemory (p_Adapter->m_dhcp_server_mac, sizeof (MACADDR));
+}
+
+#if ENABLE_NONADMIN
+
+//===================================================================
+// Set TAP device handle to be accessible without admin privileges.
+//===================================================================
+VOID AllowNonAdmin (TapExtensionPointer p_Extension)
+{
+  NTSTATUS stat;
+  SECURITY_DESCRIPTOR sd;
+  OBJECT_ATTRIBUTES oa;
+  IO_STATUS_BLOCK isb;
+  HANDLE hand = NULL;
+
+  NdisZeroMemory (&sd, sizeof (sd));
+  NdisZeroMemory (&oa, sizeof (oa));
+  NdisZeroMemory (&isb, sizeof (isb));
+
+  if (!p_Extension->m_CreatedUnicodeLinkName)
+    {
+      DEBUGP (("[TAP] AllowNonAdmin: UnicodeLinkName is uninitialized\n"));
+      NOTE_ERROR ();
+      return;
+    }
+
+  stat = RtlCreateSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION);
+  if (stat != STATUS_SUCCESS)
+    {
+      DEBUGP (("[TAP] AllowNonAdmin: RtlCreateSecurityDescriptor failed\n"));
+      NOTE_ERROR ();
+      return;
+    }
+
+  InitializeObjectAttributes (
+    &oa,
+    &p_Extension->m_UnicodeLinkName,
+    OBJ_KERNEL_HANDLE,
+    NULL,
+    NULL
+    );
+
+  stat = ZwOpenFile (
+    &hand,
+    WRITE_DAC,
+    &oa,
+    &isb,
+    0,
+    0
+    );
+  if (stat != STATUS_SUCCESS)
+    {
+      DEBUGP (("[TAP] AllowNonAdmin: ZwOpenFile failed, status=0x%08x\n", (unsigned int)stat));
+      NOTE_ERROR ();
+      return;
+    }
+
+  stat = ZwSetSecurityObject (hand, DACL_SECURITY_INFORMATION, &sd);
+  if (stat != STATUS_SUCCESS)
+    {
+      DEBUGP (("[TAP] AllowNonAdmin: ZwSetSecurityObject failed\n"));
+      NOTE_ERROR ();
+      return;
+    }
+
+  stat = ZwClose (hand);
+  if (stat != STATUS_SUCCESS)
+    {
+      DEBUGP (("[TAP] AllowNonAdmin: ZwClose failed\n"));
+      NOTE_ERROR ();
+      return;
+    }
+
+  DEBUGP (("[TAP] AllowNonAdmin: SUCCEEDED\n"));
+}
+
+#endif
+
+#if PACKET_TRUNCATION_CHECK
+
+VOID
+IPv4PacketSizeVerify (const UCHAR *data, ULONG length, BOOLEAN tun, const char *prefix, LONG *counter)
+{
+  const IPHDR *ip;
+  int len = length;
+
+  if (tun)
+    {
+      ip = (IPHDR *) data;
+    }
+  else
+    {
+      if (length >= sizeof (ETH_HEADER))
+	{
+	  const ETH_HEADER *eth = (ETH_HEADER *) data;
+
+	  if (eth->proto != htons (ETH_P_IP))
+	    return;
+
+	  ip = (IPHDR *) (data + sizeof (ETH_HEADER));
+	  len -= sizeof (ETH_HEADER);
+	}
+      else
+	return;
+    }
+
+  if (len >= sizeof (IPHDR))
+    {
+      const int totlen = ntohs (ip->tot_len);
+
+      DEBUGP (("[TAP] IPv4PacketSizeVerify %s len=%d totlen=%d\n", prefix, len, totlen));
+
+      if (len != totlen)
+	++(*counter);
+    }
+}
+
+#endif
+
+//======================================================================
+//                                    End of Source
+//======================================================================

+ 178 - 0
vsprojects/TapDriver/types.h

@@ -0,0 +1,178 @@
+/*
+ *  TAP-Windows -- A kernel driver to provide virtual tap
+ *                 device functionality on Windows.
+ *
+ *  This code was inspired by the CIPE-Win32 driver by Damion K. Wilson.
+ *
+ *  This source code is Copyright (C) 2002-2010 OpenVPN Technologies, Inc.,
+ *  and is released under the GPL version 2 (see below).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2
+ *  as published by the Free Software Foundation.
+ *
+ *  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 (see the file COPYING included with this
+ *  distribution); if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef TAP_TYPES_DEFINED
+#define TAP_TYPES_DEFINED
+
+typedef struct _Queue
+{
+  ULONG base;
+  ULONG size;
+  ULONG capacity;
+  ULONG max_size;
+  PVOID data[];
+} Queue;
+
+typedef struct _TapAdapter;
+typedef struct _TapPacket;
+
+typedef union _TapAdapterQuery
+{
+  NDIS_HARDWARE_STATUS m_HardwareStatus;
+  NDIS_MEDIUM m_Medium;
+  NDIS_PHYSICAL_MEDIUM m_PhysicalMedium;
+  UCHAR m_MacAddress [6];
+  UCHAR m_Buffer [256];
+  ULONG m_Long;
+  USHORT m_Short;
+  UCHAR m_Byte;
+}
+TapAdapterQuery, *TapAdapterQueryPointer;
+
+typedef struct _TapExtension
+{
+  // TAP device object and packet queues
+  Queue *m_PacketQueue, *m_IrpQueue;
+  PDEVICE_OBJECT m_TapDevice;
+  NDIS_HANDLE m_TapDeviceHandle;
+  ULONG m_TapOpens;
+
+  // Used to lock packet queues
+  NDIS_SPIN_LOCK m_QueueLock;
+  BOOLEAN m_AllocatedSpinlocks;
+
+  // Used to bracket open/close
+  // state changes.
+  MUTEX m_OpenCloseMutex;
+
+  // True if device has been permanently halted
+  BOOLEAN m_Halt;
+
+  // TAP device name
+  unsigned char *m_TapName;
+  UNICODE_STRING m_UnicodeLinkName;
+  BOOLEAN m_CreatedUnicodeLinkName;
+
+  // Used for device status ioctl only
+  const char *m_LastErrorFilename;
+  int m_LastErrorLineNumber;
+  LONG m_NumTapOpens;
+
+  // Flags
+  BOOLEAN m_TapIsRunning;
+  BOOLEAN m_CalledTapDeviceFreeResources;
+
+  // DPC queue for deferred packet injection
+  BOOLEAN m_InjectDpcInitialized;
+  KDPC m_InjectDpc;
+  NDIS_SPIN_LOCK m_InjectLock;
+  Queue *m_InjectQueue;
+}
+TapExtension, *TapExtensionPointer;
+
+typedef struct _TapPacket
+   {
+#   define TAP_PACKET_SIZE(data_size) (sizeof (TapPacket) + (data_size))
+#   define TP_TUN 0x80000000
+#   define TP_SIZE_MASK      (~TP_TUN)
+    ULONG m_SizeFlags;
+    UCHAR m_Data []; // m_Data must be the last struct member
+   }
+TapPacket, *TapPacketPointer;
+
+typedef struct _InjectPacket
+   {
+#   define INJECT_PACKET_SIZE(data_size) (sizeof (InjectPacket) + (data_size))
+#   define INJECT_PACKET_FREE(ib)  NdisFreeMemory ((ib), INJECT_PACKET_SIZE ((ib)->m_Size), 0)
+    ULONG m_Size;
+    UCHAR m_Data []; // m_Data must be the last struct member
+   }
+InjectPacket, *InjectPacketPointer;
+
+typedef struct _TapAdapter
+{
+# define NAME(a) ((a)->m_NameAnsi.Buffer)
+  ANSI_STRING m_NameAnsi;
+  MACADDR m_MAC;
+  BOOLEAN m_InterfaceIsRunning;
+  NDIS_HANDLE m_MiniportAdapterHandle;
+  LONG m_Rx, m_Tx, m_RxErr, m_TxErr;
+#if PACKET_TRUNCATION_CHECK
+  LONG m_RxTrunc, m_TxTrunc;
+#endif
+  NDIS_MEDIUM m_Medium;
+  ULONG m_Lookahead;
+  ULONG m_MTU;
+
+  // TRUE if adapter should always be
+  // "connected" even when device node
+  // is not open by a userspace process.
+  BOOLEAN m_MediaStateAlwaysConnected;
+
+  // TRUE if device is "connected"
+  BOOLEAN m_MediaState;
+
+  // Adapter power state
+  char m_DeviceState;
+
+  // Info for point-to-point mode
+  BOOLEAN m_tun;
+  IPADDR m_localIP;
+  IPADDR m_remoteNetwork;
+  IPADDR m_remoteNetmask;
+  ETH_HEADER m_TapToUser;
+  ETH_HEADER m_UserToTap;
+  ETH_HEADER m_UserToTap_IPv6;		// same as UserToTap but proto=ipv6
+  MACADDR m_MAC_Broadcast;
+
+  // Used for DHCP server masquerade
+  BOOLEAN m_dhcp_enabled;
+  IPADDR m_dhcp_addr;
+  ULONG m_dhcp_netmask;
+  IPADDR m_dhcp_server_ip;
+  BOOLEAN m_dhcp_server_arp;
+  MACADDR m_dhcp_server_mac;
+  ULONG m_dhcp_lease_time;
+  UCHAR m_dhcp_user_supplied_options_buffer[DHCP_USER_SUPPLIED_OPTIONS_BUFFER_SIZE];
+  ULONG m_dhcp_user_supplied_options_buffer_len;
+  BOOLEAN m_dhcp_received_discover;
+  ULONG m_dhcp_bad_requests;
+
+  // Help to tear down the adapter by keeping
+  // some state information on allocated
+  // resources.
+  BOOLEAN m_CalledAdapterFreeResources;
+  BOOLEAN m_RegisteredAdapterShutdownHandler;
+
+  // Multicast list info
+  NDIS_SPIN_LOCK m_MCLock;
+  BOOLEAN m_MCLockAllocated;
+  ULONG m_MCListSize;
+  MC_LIST m_MCList;
+
+  // Information on the TAP device
+  TapExtension m_Extension;
+} TapAdapter, *TapAdapterPointer;
+
+#endif