diff options
author | Adam Ierymenko <adam.ierymenko@zerotier.com> | 2015-04-24 23:31:51 -0700 |
---|---|---|
committer | Adam Ierymenko <adam.ierymenko@zerotier.com> | 2015-04-24 23:31:51 -0700 |
commit | 1ebe2ad920bce1f9ea85c56fce469674b83c650c (patch) | |
tree | 13c9739daf964b02ec778f6d7003daa9554e673e | |
parent | ce09e363dc95d801e8707a29a0d585089d6c3d09 (diff) | |
download | infinitytier-1ebe2ad920bce1f9ea85c56fce469674b83c650c.tar.gz infinitytier-1ebe2ad920bce1f9ea85c56fce469674b83c650c.zip |
NDIS6 driver with VS project to build -- builds correctly, not customized for ZT yet.
31 files changed, 10013 insertions, 0 deletions
diff --git a/windows/TapDriver6/TapDriver6.vcxproj b/windows/TapDriver6/TapDriver6.vcxproj new file mode 100644 index 00000000..280354ec --- /dev/null +++ b/windows/TapDriver6/TapDriver6.vcxproj @@ -0,0 +1,249 @@ +<?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>{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}</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>TapDriver6</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>false</WppKernelMode> + <WppMinimalRebuildFromTracking Condition="'$(Configuration)|$(Platform)'=='Vista Debug|Win32'">false</WppMinimalRebuildFromTracking> + <WppMinimalRebuildFromTracking Condition="'$(Configuration)|$(Platform)'=='Win8 Debug|Win32'">false</WppMinimalRebuildFromTracking> + <WppMinimalRebuildFromTracking Condition="'$(Configuration)|$(Platform)'=='Vista Release|Win32'">false</WppMinimalRebuildFromTracking> + <WppMinimalRebuildFromTracking Condition="'$(Configuration)|$(Platform)'=='Win7 Debug|Win32'">false</WppMinimalRebuildFromTracking> + <WppMinimalRebuildFromTracking Condition="'$(Configuration)|$(Platform)'=='Win7 Release|Win32'">false</WppMinimalRebuildFromTracking> + <WppMinimalRebuildFromTracking Condition="'$(Configuration)|$(Platform)'=='Win8 Release|Win32'">false</WppMinimalRebuildFromTracking> + <WppMinimalRebuildFromTracking Condition="'$(Configuration)|$(Platform)'=='Vista Debug|x64'">false</WppMinimalRebuildFromTracking> + <WppMinimalRebuildFromTracking Condition="'$(Configuration)|$(Platform)'=='Win8 Debug|x64'">false</WppMinimalRebuildFromTracking> + <WppMinimalRebuildFromTracking Condition="'$(Configuration)|$(Platform)'=='Vista Release|x64'">false</WppMinimalRebuildFromTracking> + <WppMinimalRebuildFromTracking Condition="'$(Configuration)|$(Platform)'=='Win7 Debug|x64'">false</WppMinimalRebuildFromTracking> + <WppMinimalRebuildFromTracking Condition="'$(Configuration)|$(Platform)'=='Win7 Release|x64'">false</WppMinimalRebuildFromTracking> + <WppMinimalRebuildFromTracking Condition="'$(Configuration)|$(Platform)'=='Win8 Release|x64'">false</WppMinimalRebuildFromTracking> + <WarningLevel Condition="'$(Configuration)|$(Platform)'=='Vista Debug|Win32'">Level1</WarningLevel> + <WarningLevel Condition="'$(Configuration)|$(Platform)'=='Win8 Debug|Win32'">Level1</WarningLevel> + <WarningLevel Condition="'$(Configuration)|$(Platform)'=='Vista Release|Win32'">Level1</WarningLevel> + <WarningLevel Condition="'$(Configuration)|$(Platform)'=='Win7 Debug|Win32'">Level1</WarningLevel> + <WarningLevel Condition="'$(Configuration)|$(Platform)'=='Win7 Release|Win32'">Level1</WarningLevel> + <WarningLevel Condition="'$(Configuration)|$(Platform)'=='Win8 Release|Win32'">Level1</WarningLevel> + <WarningLevel Condition="'$(Configuration)|$(Platform)'=='Vista Debug|x64'">Level1</WarningLevel> + <WarningLevel Condition="'$(Configuration)|$(Platform)'=='Win8 Debug|x64'">Level1</WarningLevel> + <WarningLevel Condition="'$(Configuration)|$(Platform)'=='Vista Release|x64'">Level1</WarningLevel> + <WarningLevel Condition="'$(Configuration)|$(Platform)'=='Win7 Debug|x64'">Level1</WarningLevel> + <WarningLevel Condition="'$(Configuration)|$(Platform)'=='Win7 Release|x64'">Level1</WarningLevel> + <WarningLevel Condition="'$(Configuration)|$(Platform)'=='Win8 Release|x64'">Level1</WarningLevel> + <CompileAs Condition="'$(Configuration)|$(Platform)'=='Vista Debug|Win32'">Default</CompileAs> + <CompileAs Condition="'$(Configuration)|$(Platform)'=='Win8 Debug|Win32'">Default</CompileAs> + <CompileAs Condition="'$(Configuration)|$(Platform)'=='Vista Release|Win32'">Default</CompileAs> + <CompileAs Condition="'$(Configuration)|$(Platform)'=='Win7 Debug|Win32'">Default</CompileAs> + <CompileAs Condition="'$(Configuration)|$(Platform)'=='Win7 Release|Win32'">Default</CompileAs> + <CompileAs Condition="'$(Configuration)|$(Platform)'=='Win8 Release|Win32'">Default</CompileAs> + <CompileAs Condition="'$(Configuration)|$(Platform)'=='Vista Debug|x64'">Default</CompileAs> + <CompileAs Condition="'$(Configuration)|$(Platform)'=='Win8 Debug|x64'">Default</CompileAs> + <CompileAs Condition="'$(Configuration)|$(Platform)'=='Vista Release|x64'">Default</CompileAs> + <CompileAs Condition="'$(Configuration)|$(Platform)'=='Win7 Debug|x64'">Default</CompileAs> + <CompileAs Condition="'$(Configuration)|$(Platform)'=='Win7 Release|x64'">Default</CompileAs> + <CompileAs Condition="'$(Configuration)|$(Platform)'=='Win8 Release|x64'">Default</CompileAs> + </ClCompile> + <Link> + <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Vista Debug|x64'">C:\WinDDK\7600.16385.1\lib\win7\amd64\ndis.lib;C:\WinDDK\7600.16385.1\lib\win7\amd64\ntstrsafe.lib;C:\WinDDK\7600.16385.1\lib\win7\amd64\wdmsec.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + <Link> + <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Win8 Debug|x64'">C:\WinDDK\7600.16385.1\lib\win7\amd64\ndis.lib;C:\WinDDK\7600.16385.1\lib\win7\amd64\ntstrsafe.lib;C:\WinDDK\7600.16385.1\lib\win7\amd64\wdmsec.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + <Link> + <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Vista Release|x64'">C:\WinDDK\7600.16385.1\lib\win7\amd64\ndis.lib;C:\WinDDK\7600.16385.1\lib\win7\amd64\ntstrsafe.lib;C:\WinDDK\7600.16385.1\lib\win7\amd64\wdmsec.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + <Link> + <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Win7 Debug|x64'">C:\WinDDK\7600.16385.1\lib\win7\amd64\ndis.lib;C:\WinDDK\7600.16385.1\lib\win7\amd64\ntstrsafe.lib;C:\WinDDK\7600.16385.1\lib\win7\amd64\wdmsec.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + <Link> + <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Win7 Release|x64'">C:\WinDDK\7600.16385.1\lib\win7\amd64\ndis.lib;C:\WinDDK\7600.16385.1\lib\win7\amd64\ntstrsafe.lib;C:\WinDDK\7600.16385.1\lib\win7\amd64\wdmsec.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + <Link> + <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Win8 Release|x64'">C:\WinDDK\7600.16385.1\lib\win7\amd64\ndis.lib;C:\WinDDK\7600.16385.1\lib\win7\amd64\ntstrsafe.lib;C:\WinDDK\7600.16385.1\lib\win7\amd64\wdmsec.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + <Link> + <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Vista Debug|Win32'">C:\WinDDK\7600.16385.1\lib\win7\i386\ndis.lib;C:\WinDDK\7600.16385.1\lib\win7\i386\ntstrsafe.lib;C:\WinDDK\7600.16385.1\lib\win7\i386\wdmsec.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + <Link> + <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Win8 Debug|Win32'">C:\WinDDK\7600.16385.1\lib\win7\i386\ndis.lib;C:\WinDDK\7600.16385.1\lib\win7\i386\ntstrsafe.lib;C:\WinDDK\7600.16385.1\lib\win7\i386\wdmsec.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + <Link> + <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Vista Release|Win32'">C:\WinDDK\7600.16385.1\lib\win7\i386\ndis.lib;C:\WinDDK\7600.16385.1\lib\win7\i386\ntstrsafe.lib;C:\WinDDK\7600.16385.1\lib\win7\i386\wdmsec.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + <Link> + <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Win7 Debug|Win32'">C:\WinDDK\7600.16385.1\lib\win7\i386\ndis.lib;C:\WinDDK\7600.16385.1\lib\win7\i386\ntstrsafe.lib;C:\WinDDK\7600.16385.1\lib\win7\i386\wdmsec.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + <Link> + <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Win7 Release|Win32'">C:\WinDDK\7600.16385.1\lib\win7\i386\ndis.lib;C:\WinDDK\7600.16385.1\lib\win7\i386\ntstrsafe.lib;C:\WinDDK\7600.16385.1\lib\win7\i386\wdmsec.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + <Link> + <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Win8 Release|Win32'">C:\WinDDK\7600.16385.1\lib\win7\i386\ndis.lib;C:\WinDDK\7600.16385.1\lib\win7\i386\ntstrsafe.lib;C:\WinDDK\7600.16385.1\lib\win7\i386\wdmsec.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <FilesToPackage Include="$(TargetPath)" /> + <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" /> + </ItemGroup> + <ItemGroup> + <ClCompile Include="adapter.c" /> + <ClCompile Include="device.c" /> + <ClCompile Include="dhcp.c" /> + <ClCompile Include="error.c" /> + <ClCompile Include="macinfo.c" /> + <ClCompile Include="mem.c" /> + <ClCompile Include="oidrequest.c" /> + <ClCompile Include="rxpath.c" /> + <ClCompile Include="tapdrvr.c" /> + <ClCompile Include="txpath.c" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="adapter.h" /> + <ClInclude Include="config.h" /> + <ClInclude Include="constants.h" /> + <ClInclude Include="device.h" /> + <ClInclude Include="dhcp.h" /> + <ClInclude Include="endian.h" /> + <ClInclude Include="error.h" /> + <ClInclude Include="hexdump.h" /> + <ClInclude Include="lock.h" /> + <ClInclude Include="macinfo.h" /> + <ClInclude Include="mem.h" /> + <ClInclude Include="proto.h" /> + <ClInclude Include="prototypes.h" /> + <ClInclude Include="tap-windows.h" /> + <ClInclude Include="tap.h" /> + <ClInclude Include="types.h" /> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="resource.rc" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project>
\ No newline at end of file diff --git a/windows/TapDriver6/TapDriver6.vcxproj.filters b/windows/TapDriver6/TapDriver6.vcxproj.filters new file mode 100644 index 00000000..9839a59c --- /dev/null +++ b/windows/TapDriver6/TapDriver6.vcxproj.filters @@ -0,0 +1,108 @@ +<?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> + <ClCompile Include="adapter.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="device.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="dhcp.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="error.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="oidrequest.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="rxpath.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="tapdrvr.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="txpath.c"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="adapter.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="config.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="constants.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="device.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="hexdump.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="mem.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.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> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="resource.rc"> + <Filter>Resource Files</Filter> + </ResourceCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/windows/TapDriver6/adapter.c b/windows/TapDriver6/adapter.c new file mode 100644 index 00000000..7ce4b310 --- /dev/null +++ b/windows/TapDriver6/adapter.c @@ -0,0 +1,1716 @@ +/* + * 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-2014 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 files. +// + +#include "tap.h" + +NDIS_OID TAPSupportedOids[] = +{ + OID_GEN_HARDWARE_STATUS, + OID_GEN_TRANSMIT_BUFFER_SPACE, + OID_GEN_RECEIVE_BUFFER_SPACE, + OID_GEN_TRANSMIT_BLOCK_SIZE, + OID_GEN_RECEIVE_BLOCK_SIZE, + OID_GEN_VENDOR_ID, + OID_GEN_VENDOR_DESCRIPTION, + OID_GEN_VENDOR_DRIVER_VERSION, + OID_GEN_CURRENT_PACKET_FILTER, + OID_GEN_CURRENT_LOOKAHEAD, + OID_GEN_DRIVER_VERSION, + OID_GEN_MAXIMUM_TOTAL_SIZE, + OID_GEN_XMIT_OK, + OID_GEN_RCV_OK, + OID_GEN_STATISTICS, +#ifdef IMPLEMENT_OPTIONAL_OIDS + OID_GEN_TRANSMIT_QUEUE_LENGTH, // Optional +#endif // IMPLEMENT_OPTIONAL_OIDS + OID_GEN_LINK_PARAMETERS, + OID_GEN_INTERRUPT_MODERATION, + OID_GEN_MEDIA_SUPPORTED, + OID_GEN_MEDIA_IN_USE, + OID_GEN_MAXIMUM_SEND_PACKETS, + OID_GEN_XMIT_ERROR, + OID_GEN_RCV_ERROR, + OID_GEN_RCV_NO_BUFFER, + OID_802_3_PERMANENT_ADDRESS, + OID_802_3_CURRENT_ADDRESS, + OID_802_3_MULTICAST_LIST, + OID_802_3_MAXIMUM_LIST_SIZE, + OID_802_3_RCV_ERROR_ALIGNMENT, + OID_802_3_XMIT_ONE_COLLISION, + OID_802_3_XMIT_MORE_COLLISIONS, +#ifdef IMPLEMENT_OPTIONAL_OIDS + OID_802_3_XMIT_DEFERRED, // Optional + OID_802_3_XMIT_MAX_COLLISIONS, // Optional + OID_802_3_RCV_OVERRUN, // Optional + OID_802_3_XMIT_UNDERRUN, // Optional + OID_802_3_XMIT_HEARTBEAT_FAILURE, // Optional + OID_802_3_XMIT_TIMES_CRS_LOST, // Optional + OID_802_3_XMIT_LATE_COLLISIONS, // Optional + OID_PNP_CAPABILITIES, // Optional +#endif // IMPLEMENT_OPTIONAL_OIDS +}; + +//====================================================================== +// TAP NDIS 6 Miniport Callbacks +//====================================================================== + +// Returns with reference count initialized to one. +PTAP_ADAPTER_CONTEXT +tapAdapterContextAllocate( + __in NDIS_HANDLE MiniportAdapterHandle +) +{ + PTAP_ADAPTER_CONTEXT adapter = NULL; + + adapter = (PTAP_ADAPTER_CONTEXT )NdisAllocateMemoryWithTagPriority( + GlobalData.NdisDriverHandle, + sizeof(TAP_ADAPTER_CONTEXT), + TAP_ADAPTER_TAG, + NormalPoolPriority + ); + + if(adapter) + { + NET_BUFFER_LIST_POOL_PARAMETERS nblPoolParameters = {0}; + + NdisZeroMemory(adapter,sizeof(TAP_ADAPTER_CONTEXT)); + + adapter->MiniportAdapterHandle = MiniportAdapterHandle; + + // Initialize cancel-safe IRP queue + tapIrpCsqInitialize(&adapter->PendingReadIrpQueue); + + // Initialize TAP send packet queue. + tapPacketQueueInitialize(&adapter->SendPacketQueue); + + // Allocate the adapter lock. + NdisAllocateSpinLock(&adapter->AdapterLock); + + // NBL pool for making TAP receive indications. + NdisZeroMemory(&nblPoolParameters, sizeof(NET_BUFFER_LIST_POOL_PARAMETERS)); + + // Initialize event used to determine when all receive NBLs have been returned. + NdisInitializeEvent(&adapter->ReceiveNblInFlightCountZeroEvent); + + nblPoolParameters.Header.Type = NDIS_OBJECT_TYPE_DEFAULT; + nblPoolParameters.Header.Revision = NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1; + nblPoolParameters.Header.Size = NDIS_SIZEOF_NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1; + nblPoolParameters.ProtocolId = NDIS_PROTOCOL_ID_DEFAULT; + nblPoolParameters.ContextSize = 0; + //nblPoolParameters.ContextSize = sizeof(RX_NETBUFLIST_RSVD); + nblPoolParameters.fAllocateNetBuffer = TRUE; + nblPoolParameters.PoolTag = TAP_RX_NBL_TAG; + +#pragma warning( suppress : 28197 ) + adapter->ReceiveNblPool = NdisAllocateNetBufferListPool( + adapter->MiniportAdapterHandle, + &nblPoolParameters); + + if (adapter->ReceiveNblPool == NULL) + { + DEBUGP (("[TAP] Couldn't allocate adapter receive NBL pool\n")); + NdisFreeMemory(adapter,0,0); + } + + // Add initial reference. Normally removed in AdapterHalt. + adapter->RefCount = 1; + + // Safe for multiple removes. + NdisInitializeListHead(&adapter->AdapterListLink); + + // + // The miniport adapter is initially powered up + // + adapter->CurrentPowerState = NdisDeviceStateD0; + } + + return adapter; +} + +VOID +tapReadPermanentAddress( + __in PTAP_ADAPTER_CONTEXT Adapter, + __in NDIS_HANDLE ConfigurationHandle, + __out MACADDR PermanentAddress + ) +{ + NDIS_STATUS status; + NDIS_CONFIGURATION_PARAMETER *configParameter; + NDIS_STRING macKey = NDIS_STRING_CONST("MAC"); + ANSI_STRING macString; + BOOLEAN macFromRegistry = FALSE; + + // Read MAC parameter from registry. + NdisReadConfiguration( + &status, + &configParameter, + ConfigurationHandle, + &macKey, + NdisParameterString + ); + + if (status == NDIS_STATUS_SUCCESS) + { + if( (configParameter->ParameterType == NdisParameterString) + && (configParameter->ParameterData.StringData.Length >= 12) + ) + { + if (RtlUnicodeStringToAnsiString( + &macString, + &configParameter->ParameterData.StringData, + TRUE) == STATUS_SUCCESS + ) + { + macFromRegistry = ParseMAC (PermanentAddress, macString.Buffer); + RtlFreeAnsiString (&macString); + } + } + } + + if(!macFromRegistry) + { + // + // There is no (valid) address stashed in the registry parameter. + // + // Make up a dummy mac address based on the ANSI representation of the + // NetCfgInstanceId GUID. + // + GenerateRandomMac(PermanentAddress, MINIPORT_INSTANCE_ID(Adapter)); + } +} + +NDIS_STATUS +tapReadConfiguration( + __in PTAP_ADAPTER_CONTEXT Adapter + ) +{ + NDIS_STATUS status = NDIS_STATUS_SUCCESS; + NDIS_CONFIGURATION_OBJECT configObject; + NDIS_HANDLE configHandle; + + DEBUGP (("[TAP] --> tapReadConfiguration\n")); + + // + // Setup defaults in case configuration cannot be opened. + // + Adapter->MtuSize = ETHERNET_MTU; + Adapter->MediaStateAlwaysConnected = FALSE; + Adapter->LogicalMediaState = FALSE; + Adapter->AllowNonAdmin = FALSE; + // + // Open the registry for this adapter to read advanced + // configuration parameters stored by the INF file. + // + NdisZeroMemory(&configObject, sizeof(configObject)); + + {C_ASSERT(sizeof(configObject) >= NDIS_SIZEOF_CONFIGURATION_OBJECT_REVISION_1);} + configObject.Header.Type = NDIS_OBJECT_TYPE_CONFIGURATION_OBJECT; + configObject.Header.Size = NDIS_SIZEOF_CONFIGURATION_OBJECT_REVISION_1; + configObject.Header.Revision = NDIS_CONFIGURATION_OBJECT_REVISION_1; + + configObject.NdisHandle = Adapter->MiniportAdapterHandle; + configObject.Flags = 0; + + status = NdisOpenConfigurationEx( + &configObject, + &configHandle + ); + + // Read on the opened configuration handle. + if(status == NDIS_STATUS_SUCCESS) + { + NDIS_CONFIGURATION_PARAMETER *configParameter; + NDIS_STRING mkey = NDIS_STRING_CONST("NetCfgInstanceId"); + + // + // Read NetCfgInstanceId from the registry. + // ------------------------------------ + // NetCfgInstanceId is required to create device and associated + // symbolic link for the adapter device. + // + // NetCfgInstanceId is a GUID string provided by NDIS that identifies + // the adapter instance. An example is: + // + // NetCfgInstanceId={410EB49D-2381-4FE7-9B36-498E22619DF0} + // + // Other names are derived from NetCfgInstanceId. For example, MiniportName: + // + // MiniportName=\DEVICE\{410EB49D-2381-4FE7-9B36-498E22619DF0} + // + NdisReadConfiguration ( + &status, + &configParameter, + configHandle, + &mkey, + NdisParameterString + ); + + if (status == NDIS_STATUS_SUCCESS) + { + if (configParameter->ParameterType == NdisParameterString) + { + DEBUGP (("[TAP] NdisReadConfiguration (NetCfgInstanceId=%wZ)\n", + &configParameter->ParameterData.StringData )); + + // Save NetCfgInstanceId as UNICODE_STRING. + Adapter->NetCfgInstanceId.Length = Adapter->NetCfgInstanceId.MaximumLength + = configParameter->ParameterData.StringData.Length; + + Adapter->NetCfgInstanceId.Buffer = Adapter->NetCfgInstanceIdBuffer; + + NdisMoveMemory( + Adapter->NetCfgInstanceId.Buffer, + configParameter->ParameterData.StringData.Buffer, + Adapter->NetCfgInstanceId.Length + ); + + // Save NetCfgInstanceId as ANSI_STRING as well. + if (RtlUnicodeStringToAnsiString ( + &Adapter->NetCfgInstanceIdAnsi, + &configParameter->ParameterData.StringData, + TRUE) != STATUS_SUCCESS + ) + { + DEBUGP (("[TAP] NetCfgInstanceId ANSI name conversion failed\n")); + status = NDIS_STATUS_RESOURCES; + } + } + else + { + DEBUGP (("[TAP] NetCfgInstanceId has invalid type\n")); + status = NDIS_STATUS_INVALID_DATA; + } + } + else + { + DEBUGP (("[TAP] NetCfgInstanceId failed\n")); + status = NDIS_STATUS_INVALID_DATA; + } + + if (status == NDIS_STATUS_SUCCESS) + { + NDIS_STATUS localStatus; // Use default if these fail. + NDIS_CONFIGURATION_PARAMETER *configParameter; + NDIS_STRING mtuKey = NDIS_STRING_CONST("MTU"); + NDIS_STRING mediaStatusKey = NDIS_STRING_CONST("MediaStatus"); +#if ENABLE_NONADMIN + NDIS_STRING allowNonAdminKey = NDIS_STRING_CONST("AllowNonAdmin"); +#endif + + // Read MTU from the registry. + NdisReadConfiguration ( + &localStatus, + &configParameter, + configHandle, + &mtuKey, + NdisParameterInteger + ); + + if (localStatus == NDIS_STATUS_SUCCESS) + { + if (configParameter->ParameterType == NdisParameterInteger) + { + int mtu = configParameter->ParameterData.IntegerData; + + if(mtu == 0) + { + mtu = ETHERNET_MTU; + } + + // Sanity check + if (mtu < MINIMUM_MTU) + { + mtu = MINIMUM_MTU; + } + else if (mtu > MAXIMUM_MTU) + { + mtu = MAXIMUM_MTU; + } + + Adapter->MtuSize = mtu; + } + } + + DEBUGP (("[%s] Using MTU %d\n", + MINIPORT_INSTANCE_ID (Adapter), + Adapter->MtuSize + )); + + // Read MediaStatus setting from registry. + NdisReadConfiguration ( + &localStatus, + &configParameter, + configHandle, + &mediaStatusKey, + NdisParameterInteger + ); + + if (localStatus == NDIS_STATUS_SUCCESS) + { + if (configParameter->ParameterType == NdisParameterInteger) + { + if(configParameter->ParameterData.IntegerData == 0) + { + // Connect state is appplication controlled. + DEBUGP(("[%s] Initial MediaConnectState: Application Controlled\n", + MINIPORT_INSTANCE_ID (Adapter))); + + Adapter->MediaStateAlwaysConnected = FALSE; + Adapter->LogicalMediaState = FALSE; + } + else + { + // Connect state is always connected. + DEBUGP(("[%s] Initial MediaConnectState: Always Connected\n", + MINIPORT_INSTANCE_ID (Adapter))); + + Adapter->MediaStateAlwaysConnected = TRUE; + Adapter->LogicalMediaState = TRUE; + } + } + } + + // Read MAC PermanentAddress setting from registry. + tapReadPermanentAddress( + Adapter, + configHandle, + Adapter->PermanentAddress + ); + + DEBUGP (("[%s] Using MAC PermanentAddress %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n", + MINIPORT_INSTANCE_ID (Adapter), + Adapter->PermanentAddress[0], + Adapter->PermanentAddress[1], + Adapter->PermanentAddress[2], + Adapter->PermanentAddress[3], + Adapter->PermanentAddress[4], + Adapter->PermanentAddress[5]) + ); + + // Now seed the current MAC address with the permanent address. + ETH_COPY_NETWORK_ADDRESS(Adapter->CurrentAddress, Adapter->PermanentAddress); + + DEBUGP (("[%s] Using MAC CurrentAddress %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n", + MINIPORT_INSTANCE_ID (Adapter), + Adapter->CurrentAddress[0], + Adapter->CurrentAddress[1], + Adapter->CurrentAddress[2], + Adapter->CurrentAddress[3], + Adapter->CurrentAddress[4], + Adapter->CurrentAddress[5]) + ); + + // Read optional AllowNonAdmin setting from registry. +#if ENABLE_NONADMIN + NdisReadConfiguration ( + &localStatus, + &configParameter, + configHandle, + &allowNonAdminKey, + NdisParameterInteger + ); + + if (localStatus == NDIS_STATUS_SUCCESS) + { + if (configParameter->ParameterType == NdisParameterInteger) + { + Adapter->AllowNonAdmin = TRUE; + } + } +#endif + } + + // Close the configuration handle. + NdisCloseConfiguration(configHandle); + } + else + { + DEBUGP (("[TAP] Couldn't open adapter registry\n")); + } + + DEBUGP (("[TAP] <-- tapReadConfiguration; status = %8.8X\n",status)); + + return status; +} + +VOID +tapAdapterContextAddToGlobalList( + __in PTAP_ADAPTER_CONTEXT Adapter + ) +{ + LOCK_STATE lockState; + PLIST_ENTRY listEntry = &Adapter->AdapterListLink; + + // Acquire global adapter list lock. + NdisAcquireReadWriteLock( + &GlobalData.Lock, + TRUE, // Acquire for write + &lockState + ); + + // Adapter context should NOT be in any list. + ASSERT( (listEntry->Flink == listEntry) && (listEntry->Blink == listEntry ) ); + + // Add reference to persist until after removal. + tapAdapterContextReference(Adapter); + + // Add the adapter context to the global list. + InsertTailList(&GlobalData.AdapterList,&Adapter->AdapterListLink); + + // Release global adapter list lock. + NdisReleaseReadWriteLock(&GlobalData.Lock,&lockState); +} + +VOID +tapAdapterContextRemoveFromGlobalList( + __in PTAP_ADAPTER_CONTEXT Adapter + ) +{ + LOCK_STATE lockState; + + // Acquire global adapter list lock. + NdisAcquireReadWriteLock( + &GlobalData.Lock, + TRUE, // Acquire for write + &lockState + ); + + // Remove the adapter context from the global list. + RemoveEntryList(&Adapter->AdapterListLink); + + // Safe for multiple removes. + NdisInitializeListHead(&Adapter->AdapterListLink); + + // Remove reference added in tapAdapterContextAddToGlobalList. + tapAdapterContextDereference(Adapter); + + // Release global adapter list lock. + NdisReleaseReadWriteLock(&GlobalData.Lock,&lockState); +} + +// Returns with added reference on adapter context. +PTAP_ADAPTER_CONTEXT +tapAdapterContextFromDeviceObject( + __in PDEVICE_OBJECT DeviceObject + ) +{ + LOCK_STATE lockState; + + // Acquire global adapter list lock. + NdisAcquireReadWriteLock( + &GlobalData.Lock, + FALSE, // Acquire for read + &lockState + ); + + if (!IsListEmpty(&GlobalData.AdapterList)) + { + PLIST_ENTRY entry = GlobalData.AdapterList.Flink; + PTAP_ADAPTER_CONTEXT adapter; + + while (entry != &GlobalData.AdapterList) + { + adapter = CONTAINING_RECORD(entry, TAP_ADAPTER_CONTEXT, AdapterListLink); + + // Match on DeviceObject + if(adapter->DeviceObject == DeviceObject ) + { + // Add reference to adapter context. + tapAdapterContextReference(adapter); + + // Release global adapter list lock. + NdisReleaseReadWriteLock(&GlobalData.Lock,&lockState); + + return adapter; + } + + // Move to next entry + entry = entry->Flink; + } + } + + // Release global adapter list lock. + NdisReleaseReadWriteLock(&GlobalData.Lock,&lockState); + + return (PTAP_ADAPTER_CONTEXT )NULL; +} + +NDIS_STATUS +AdapterSetOptions( + __in NDIS_HANDLE NdisDriverHandle, + __in NDIS_HANDLE DriverContext + ) +/*++ +Routine Description: + + The MiniportSetOptions function registers optional handlers. For each + optional handler that should be registered, this function makes a call + to NdisSetOptionalHandlers. + + MiniportSetOptions runs at IRQL = PASSIVE_LEVEL. + +Arguments: + + DriverContext The context handle + +Return Value: + + NDIS_STATUS_xxx code + +--*/ +{ + NDIS_STATUS status; + + DEBUGP (("[TAP] --> AdapterSetOptions\n")); + + // + // Set any optional handlers by filling out the appropriate struct and + // calling NdisSetOptionalHandlers here. + // + + status = NDIS_STATUS_SUCCESS; + + DEBUGP (("[TAP] <-- AdapterSetOptions; status = %8.8X\n",status)); + + return status; +} + +NDIS_STATUS +AdapterCreate( + __in NDIS_HANDLE MiniportAdapterHandle, + __in NDIS_HANDLE MiniportDriverContext, + __in PNDIS_MINIPORT_INIT_PARAMETERS MiniportInitParameters + ) +{ + PTAP_ADAPTER_CONTEXT adapter = NULL; + NDIS_STATUS status; + + UNREFERENCED_PARAMETER(MiniportDriverContext); + UNREFERENCED_PARAMETER(MiniportInitParameters); + + DEBUGP (("[TAP] --> AdapterCreate\n")); + + do + { + NDIS_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES regAttributes = {0}; + NDIS_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES genAttributes = {0}; + NDIS_PNP_CAPABILITIES pnpCapabilities = {0}; + + // + // Allocate adapter context structure and initialize all the + // memory resources for sending and receiving packets. + // + // Returns with reference count initialized to one. + // + adapter = tapAdapterContextAllocate(MiniportAdapterHandle); + + if(adapter == NULL) + { + DEBUGP (("[TAP] Couldn't allocate adapter memory\n")); + status = NDIS_STATUS_RESOURCES; + break; + } + + // Enter the Initializing state. + DEBUGP (("[TAP] Miniport State: Initializing\n")); + + tapAdapterAcquireLock(adapter,FALSE); + adapter->Locked.AdapterState = MiniportInitializingState; + tapAdapterReleaseLock(adapter,FALSE); + + // + // First read adapter configuration from registry. + // ----------------------------------------------- + // Subsequent device registration will fail if NetCfgInstanceId + // has not been successfully read. + // + status = tapReadConfiguration(adapter); + + // + // Set the registration attributes. + // + {C_ASSERT(sizeof(regAttributes) >= NDIS_SIZEOF_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES_REVISION_1);} + regAttributes.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES; + regAttributes.Header.Size = NDIS_SIZEOF_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES_REVISION_1; + regAttributes.Header.Revision = NDIS_SIZEOF_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES_REVISION_1; + + regAttributes.MiniportAdapterContext = adapter; + regAttributes.AttributeFlags = TAP_ADAPTER_ATTRIBUTES_FLAGS; + + regAttributes.CheckForHangTimeInSeconds = TAP_ADAPTER_CHECK_FOR_HANG_TIME_IN_SECONDS; + regAttributes.InterfaceType = TAP_INTERFACE_TYPE; + + //NDIS_DECLARE_MINIPORT_ADAPTER_CONTEXT(TAP_ADAPTER_CONTEXT); + status = NdisMSetMiniportAttributes( + MiniportAdapterHandle, + (PNDIS_MINIPORT_ADAPTER_ATTRIBUTES)®Attributes + ); + + if (status != NDIS_STATUS_SUCCESS) + { + DEBUGP (("[TAP] NdisSetOptionalHandlers failed; Status 0x%08x\n",status)); + break; + } + + // + // Next, set the general attributes. + // + {C_ASSERT(sizeof(genAttributes) >= NDIS_SIZEOF_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES_REVISION_1);} + genAttributes.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES; + genAttributes.Header.Size = NDIS_SIZEOF_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES_REVISION_1; + genAttributes.Header.Revision = NDIS_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES_REVISION_1; + + // + // Specify the medium type that the NIC can support but not + // necessarily the medium type that the NIC currently uses. + // + genAttributes.MediaType = TAP_MEDIUM_TYPE; + + // + // Specifiy medium type that the NIC currently uses. + // + genAttributes.PhysicalMediumType = TAP_PHYSICAL_MEDIUM; + + // + // Specifiy the maximum network frame size, in bytes, that the NIC + // supports excluding the header. + // + genAttributes.MtuSize = TAP_FRAME_MAX_DATA_SIZE; + genAttributes.MaxXmitLinkSpeed = TAP_XMIT_SPEED; + genAttributes.XmitLinkSpeed = TAP_XMIT_SPEED; + genAttributes.MaxRcvLinkSpeed = TAP_RECV_SPEED; + genAttributes.RcvLinkSpeed = TAP_RECV_SPEED; + + if(adapter->MediaStateAlwaysConnected) + { + DEBUGP(("[%s] Initial MediaConnectState: Connected\n", + MINIPORT_INSTANCE_ID (adapter))); + + genAttributes.MediaConnectState = MediaConnectStateConnected; + } + else + { + DEBUGP(("[%s] Initial MediaConnectState: Disconnected\n", + MINIPORT_INSTANCE_ID (adapter))); + + genAttributes.MediaConnectState = MediaConnectStateDisconnected; + } + + genAttributes.MediaDuplexState = MediaDuplexStateFull; + + // + // The maximum number of bytes the NIC can provide as lookahead data. + // If that value is different from the size of the lookahead buffer + // supported by bound protocols, NDIS will call MiniportOidRequest to + // set the size of the lookahead buffer provided by the miniport driver + // to the minimum of the miniport driver and protocol(s) values. If the + // driver always indicates up full packets with + // NdisMIndicateReceiveNetBufferLists, it should set this value to the + // maximum total frame size, which excludes the header. + // + // Upper-layer drivers examine lookahead data to determine whether a + // packet that is associated with the lookahead data is intended for + // one or more of their clients. If the underlying driver supports + // multipacket receive indications, bound protocols are given full net + // packets on every indication. Consequently, this value is identical + // to that returned for OID_GEN_RECEIVE_BLOCK_SIZE. + // + genAttributes.LookaheadSize = TAP_MAX_LOOKAHEAD; + genAttributes.MacOptions = TAP_MAC_OPTIONS; + genAttributes.SupportedPacketFilters = TAP_SUPPORTED_FILTERS; + + // + // The maximum number of multicast addresses the NIC driver can manage. + // This list is global for all protocols bound to (or above) the NIC. + // Consequently, a protocol can receive NDIS_STATUS_MULTICAST_FULL from + // the NIC driver when attempting to set the multicast address list, + // even if the number of elements in the given list is less than the + // number originally returned for this query. + // + genAttributes.MaxMulticastListSize = TAP_MAX_MCAST_LIST; + genAttributes.MacAddressLength = MACADDR_SIZE; + + // + // Return the MAC address of the NIC burnt in the hardware. + // + ETH_COPY_NETWORK_ADDRESS(genAttributes.PermanentMacAddress, adapter->PermanentAddress); + + // + // Return the MAC address the NIC is currently programmed to use. Note + // that this address could be different from the permananent address as + // the user can override using registry. Read NdisReadNetworkAddress + // doc for more info. + // + ETH_COPY_NETWORK_ADDRESS(genAttributes.CurrentMacAddress, adapter->CurrentAddress); + + genAttributes.RecvScaleCapabilities = NULL; + genAttributes.AccessType = TAP_ACCESS_TYPE; + genAttributes.DirectionType = TAP_DIRECTION_TYPE; + genAttributes.ConnectionType = TAP_CONNECTION_TYPE; + genAttributes.IfType = TAP_IFTYPE; + genAttributes.IfConnectorPresent = TAP_HAS_PHYSICAL_CONNECTOR; + genAttributes.SupportedStatistics = TAP_SUPPORTED_STATISTICS; + genAttributes.SupportedPauseFunctions = NdisPauseFunctionsUnsupported; // IEEE 802.3 pause frames + genAttributes.DataBackFillSize = 0; + genAttributes.ContextBackFillSize = 0; + + // + // The SupportedOidList is an array of OIDs for objects that the + // underlying driver or its NIC supports. Objects include general, + // media-specific, and implementation-specific objects. NDIS forwards a + // subset of the returned list to protocols that make this query. That + // is, NDIS filters any supported statistics OIDs out of the list + // because protocols never make statistics queries. + // + genAttributes.SupportedOidList = TAPSupportedOids; + genAttributes.SupportedOidListLength = sizeof(TAPSupportedOids); + genAttributes.AutoNegotiationFlags = NDIS_LINK_STATE_DUPLEX_AUTO_NEGOTIATED; + + // + // Set power management capabilities + // + NdisZeroMemory(&pnpCapabilities, sizeof(pnpCapabilities)); + pnpCapabilities.WakeUpCapabilities.MinMagicPacketWakeUp = NdisDeviceStateUnspecified; + pnpCapabilities.WakeUpCapabilities.MinPatternWakeUp = NdisDeviceStateUnspecified; + genAttributes.PowerManagementCapabilities = &pnpCapabilities; + + status = NdisMSetMiniportAttributes( + MiniportAdapterHandle, + (PNDIS_MINIPORT_ADAPTER_ATTRIBUTES)&genAttributes + ); + + if (status != NDIS_STATUS_SUCCESS) + { + DEBUGP (("[TAP] NdisMSetMiniportAttributes failed; Status 0x%08x\n",status)); + break; + } + + // + // Create the Win32 device I/O interface. + // + status = CreateTapDevice(adapter); + + if (status == NDIS_STATUS_SUCCESS) + { + // Add this adapter to the global adapter list. + tapAdapterContextAddToGlobalList(adapter); + } + else + { + DEBUGP (("[TAP] CreateTapDevice failed; Status 0x%08x\n",status)); + break; + } + } while(FALSE); + + if(status == NDIS_STATUS_SUCCESS) + { + // Enter the Paused state if initialization is complete. + DEBUGP (("[TAP] Miniport State: Paused\n")); + + tapAdapterAcquireLock(adapter,FALSE); + adapter->Locked.AdapterState = MiniportPausedState; + tapAdapterReleaseLock(adapter,FALSE); + } + else + { + if(adapter != NULL) + { + DEBUGP (("[TAP] Miniport State: Halted\n")); + + // + // Remove reference when adapter context was allocated + // --------------------------------------------------- + // This should result in freeing adapter context memory + // and assiciated resources. + // + tapAdapterContextDereference(adapter); + adapter = NULL; + } + } + + DEBUGP (("[TAP] <-- AdapterCreate; status = %8.8X\n",status)); + + return status; +} + +VOID +AdapterHalt( + __in NDIS_HANDLE MiniportAdapterContext, + __in NDIS_HALT_ACTION HaltAction + ) +/*++ + +Routine Description: + + Halt handler is called when NDIS receives IRP_MN_STOP_DEVICE, + IRP_MN_SUPRISE_REMOVE or IRP_MN_REMOVE_DEVICE requests from the PNP + manager. Here, the driver should free all the resources acquired in + MiniportInitialize and stop access to the hardware. NDIS will not submit + any further request once this handler is invoked. + + 1) Free and unmap all I/O resources. + 2) Disable interrupt and deregister interrupt handler. + 3) Deregister shutdown handler regsitered by + NdisMRegisterAdapterShutdownHandler . + 4) Cancel all queued up timer callbacks. + 5) Finally wait indefinitely for all the outstanding receive + packets indicated to the protocol to return. + + MiniportHalt runs at IRQL = PASSIVE_LEVEL. + + +Arguments: + + MiniportAdapterContext Pointer to the Adapter + HaltAction The reason for halting the adapter + +Return Value: + + None. + +--*/ +{ + PTAP_ADAPTER_CONTEXT adapter = (PTAP_ADAPTER_CONTEXT )MiniportAdapterContext; + + UNREFERENCED_PARAMETER(HaltAction); + + DEBUGP (("[TAP] --> AdapterHalt\n")); + + // Enter the Halted state. + DEBUGP (("[TAP] Miniport State: Halted\n")); + + tapAdapterAcquireLock(adapter,FALSE); + adapter->Locked.AdapterState = MiniportHaltedState; + tapAdapterReleaseLock(adapter,FALSE); + + // Remove this adapter from the global adapter list. + tapAdapterContextRemoveFromGlobalList(adapter); + + // BUGBUG!!! Call AdapterShutdownEx to do some of the work of stopping. + + // TODO!!! More... + + // + // Destroy the TAP Win32 device. + // + DestroyTapDevice(adapter); + + // + // Remove initial reference added in AdapterCreate. + // ------------------------------------------------ + // This should result in freeing adapter context memory + // and resources allocated in AdapterCreate. + // + tapAdapterContextDereference(adapter); + adapter = NULL; + + DEBUGP (("[TAP] <-- AdapterHalt\n")); +} + +VOID +tapWaitForReceiveNblInFlightCountZeroEvent( + __in PTAP_ADAPTER_CONTEXT Adapter + ) +{ + LONG nblCount; + + // + // Wait until higher-level protocol has returned all NBLs + // to the driver. + // + + // Add one NBL "bias" to insure allow event to be reset safely. + nblCount = NdisInterlockedIncrement(&Adapter->ReceiveNblInFlightCount); + ASSERT(nblCount > 0 ); + NdisResetEvent(&Adapter->ReceiveNblInFlightCountZeroEvent); + + // + // Now remove the bias and wait for the ReceiveNblInFlightCountZeroEvent + // if the count returned is not zero. + // + nblCount = NdisInterlockedDecrement(&Adapter->ReceiveNblInFlightCount); + ASSERT(nblCount >= 0); + + if(nblCount) + { + LARGE_INTEGER startTime, currentTime; + + NdisGetSystemUpTimeEx(&startTime); + + for (;;) + { + BOOLEAN waitResult = NdisWaitEvent( + &Adapter->ReceiveNblInFlightCountZeroEvent, + TAP_WAIT_POLL_LOOP_TIMEOUT + ); + + NdisGetSystemUpTimeEx(¤tTime); + + if (waitResult) + { + break; + } + + DEBUGP (("[%s] Waiting for %d in-flight receive NBLs to be returned.\n", + MINIPORT_INSTANCE_ID (Adapter), + Adapter->ReceiveNblInFlightCount + )); + } + + DEBUGP (("[%s] Waited %d ms for all in-flight NBLs to be returned.\n", + MINIPORT_INSTANCE_ID (Adapter), + (currentTime.LowPart - startTime.LowPart) + )); + } +} + +NDIS_STATUS +AdapterPause( + __in NDIS_HANDLE MiniportAdapterContext, + __in PNDIS_MINIPORT_PAUSE_PARAMETERS PauseParameters + ) +/*++ + +Routine Description: + + When a miniport receives a pause request, it enters into a Pausing state. + The miniport should not indicate up any more network data. Any pending + send requests must be completed, and new requests must be rejected with + NDIS_STATUS_PAUSED. + + Once all sends have been completed and all recieve NBLs have returned to + the miniport, the miniport enters the Paused state. + + While paused, the miniport can still service interrupts from the hardware + (to, for example, continue to indicate NDIS_STATUS_MEDIA_CONNECT + notifications). + + The miniport must continue to be able to handle status indications and OID + requests. MiniportPause is different from MiniportHalt because, in + general, the MiniportPause operation won't release any resources. + MiniportPause must not attempt to acquire any resources where allocation + can fail, since MiniportPause itself must not fail. + + + MiniportPause runs at IRQL = PASSIVE_LEVEL. + +Arguments: + + MiniportAdapterContext Pointer to the Adapter + MiniportPauseParameters Additional information about the pause operation + +Return Value: + + If the miniport is able to immediately enter the Paused state, it should + return NDIS_STATUS_SUCCESS. + + If the miniport must wait for send completions or pending receive NBLs, it + should return NDIS_STATUS_PENDING now, and call NDISMPauseComplete when the + miniport has entered the Paused state. + + No other return value is permitted. The pause operation must not fail. + +--*/ +{ + PTAP_ADAPTER_CONTEXT adapter = (PTAP_ADAPTER_CONTEXT )MiniportAdapterContext; + NDIS_STATUS status; + + UNREFERENCED_PARAMETER(PauseParameters); + + DEBUGP (("[TAP] --> AdapterPause\n")); + + // Enter the Pausing state. + DEBUGP (("[TAP] Miniport State: Pausing\n")); + + tapAdapterAcquireLock(adapter,FALSE); + adapter->Locked.AdapterState = MiniportPausingState; + tapAdapterReleaseLock(adapter,FALSE); + + // + // Stop the flow of network data through the receive path + // ------------------------------------------------------ + // In the Pausing and Paused state tapAdapterSendAndReceiveReady + // will prevent new calls to NdisMIndicateReceiveNetBufferLists + // to indicate additional receive NBLs to the host. + // + // However, there may be some in-flight NBLs owned by the driver + // that have been indicated to the host but have not yet been + // returned. + // + // Wait here for all in-flight receive indications to be returned. + // + tapWaitForReceiveNblInFlightCountZeroEvent(adapter); + + // + // Stop the flow of network data through the send path + // --------------------------------------------------- + // The initial implementation of the NDIS 6 send path follows the + // NDIS 5 pattern. Under this approach every send packet is copied + // into a driver-owned TAP_PACKET structure and the NBL owned by + // higher-level protocol is immediatly completed. + // + // With this deep-copy approach the driver never claims ownership + // of any send NBL. + // + // A future implementation may queue send NBLs and thereby eliminate + // the need for the unnecessary allocation and deep copy of each packet. + // + // So, nothing to do here for the send path for now... + + status = NDIS_STATUS_SUCCESS; + + // Enter the Paused state. + DEBUGP (("[TAP] Miniport State: Paused\n")); + + tapAdapterAcquireLock(adapter,FALSE); + adapter->Locked.AdapterState = MiniportPausedState; + tapAdapterReleaseLock(adapter,FALSE); + + DEBUGP (("[TAP] <-- AdapterPause; status = %8.8X\n",status)); + + return status; +} + +NDIS_STATUS +AdapterRestart( + __in NDIS_HANDLE MiniportAdapterContext, + __in PNDIS_MINIPORT_RESTART_PARAMETERS RestartParameters + ) +/*++ + +Routine Description: + + When a miniport receives a restart request, it enters into a Restarting + state. The miniport may begin indicating received data (e.g., using + NdisMIndicateReceiveNetBufferLists), handling status indications, and + processing OID requests in the Restarting state. However, no sends will be + requested while the miniport is in the Restarting state. + + Once the miniport is ready to send data, it has entered the Running state. + The miniport informs NDIS that it is in the Running state by returning + NDIS_STATUS_SUCCESS from this MiniportRestart function; or if this function + has already returned NDIS_STATUS_PENDING, by calling NdisMRestartComplete. + + + MiniportRestart runs at IRQL = PASSIVE_LEVEL. + +Arguments: + + MiniportAdapterContext Pointer to the Adapter + RestartParameters Additional information about the restart operation + +Return Value: + + If the miniport is able to immediately enter the Running state, it should + return NDIS_STATUS_SUCCESS. + + If the miniport is still in the Restarting state, it should return + NDIS_STATUS_PENDING now, and call NdisMRestartComplete when the miniport + has entered the Running state. + + Other NDIS_STATUS codes indicate errors. If an error is encountered, the + miniport must return to the Paused state (i.e., stop indicating receives). + +--*/ +{ + PTAP_ADAPTER_CONTEXT adapter = (PTAP_ADAPTER_CONTEXT )MiniportAdapterContext; + NDIS_STATUS status; + + UNREFERENCED_PARAMETER(RestartParameters); + + DEBUGP (("[TAP] --> AdapterRestart\n")); + + // Enter the Restarting state. + DEBUGP (("[TAP] Miniport State: Restarting\n")); + + tapAdapterAcquireLock(adapter,FALSE); + adapter->Locked.AdapterState = MiniportRestartingState; + tapAdapterReleaseLock(adapter,FALSE); + + status = NDIS_STATUS_SUCCESS; + + if(status == NDIS_STATUS_SUCCESS) + { + // Enter the Running state. + DEBUGP (("[TAP] Miniport State: Running\n")); + + tapAdapterAcquireLock(adapter,FALSE); + adapter->Locked.AdapterState = MiniportRunning; + tapAdapterReleaseLock(adapter,FALSE); + } + else + { + // Enter the Paused state if restart failed. + DEBUGP (("[TAP] Miniport State: Paused\n")); + + tapAdapterAcquireLock(adapter,FALSE); + adapter->Locked.AdapterState = MiniportPausedState; + tapAdapterReleaseLock(adapter,FALSE); + } + + DEBUGP (("[TAP] <-- AdapterRestart; status = %8.8X\n",status)); + + return status; +} + +BOOLEAN +tapAdapterReadAndWriteReady( + __in PTAP_ADAPTER_CONTEXT Adapter + ) +/*++ + +Routine Description: + + This routine determines whether the adapter device interface can + accept read and write operations. + +Arguments: + + Adapter Pointer to our adapter context + +Return Value: + + Returns TRUE if the adapter state allows it to queue IRPs passed to + the device read and write callbacks. +--*/ +{ + if(!Adapter->TapDeviceCreated) + { + // TAP device not created or is being destroyed. + return FALSE; + } + + if(Adapter->TapFileObject == NULL) + { + // TAP application file object not open. + return FALSE; + } + + if(!Adapter->TapFileIsOpen) + { + // TAP application file object may be closing. + return FALSE; + } + + if(!Adapter->LogicalMediaState) + { + // Don't handle read/write if media not connected. + return FALSE; + } + + if(Adapter->CurrentPowerState != NdisDeviceStateD0) + { + // Don't handle read/write if device is not fully powered. + return FALSE; + } + + return TRUE; +} + +NDIS_STATUS +tapAdapterSendAndReceiveReady( + __in PTAP_ADAPTER_CONTEXT Adapter + ) +/*++ + +Routine Description: + + This routine determines whether the adapter NDIS send and receive + paths are ready. + + This routine examines various adapter state variables and returns + a value that indicates whether the adapter NDIS interfaces can + accept send packets or indicate receive packets. + + In normal operation the adapter may temporarily enter and then exit + a not-ready condition. In particular, the adapter becomes not-ready + when in the Pausing/Paused states, but may become ready again when + Restarted. + + Runs at IRQL <= DISPATCH_LEVEL + +Arguments: + + Adapter Pointer to our adapter context + +Return Value: + + Returns NDIS_STATUS_SUCCESS if the adapter state allows it to + accept send packets and indicate receive packets. + + Otherwise it returns a NDIS_STATUS value other than NDIS_STATUS_SUCCESS. + These status values can be used directly as the completion status for + packets that must be completed immediatly in the send path. +--*/ +{ + NDIS_STATUS status = NDIS_STATUS_SUCCESS; + + // + // Check various state variables to insure adapter is ready. + // + tapAdapterAcquireLock(Adapter,FALSE); + + if(!Adapter->LogicalMediaState) + { + status = NDIS_STATUS_MEDIA_DISCONNECTED; + } + else if(Adapter->CurrentPowerState != NdisDeviceStateD0) + { + status = NDIS_STATUS_LOW_POWER_STATE; + } + else if(Adapter->ResetInProgress) + { + status = NDIS_STATUS_RESET_IN_PROGRESS; + } + else + { + switch(Adapter->Locked.AdapterState) + { + case MiniportPausingState: + case MiniportPausedState: + status = NDIS_STATUS_PAUSED; + break; + + case MiniportHaltedState: + status = NDIS_STATUS_INVALID_STATE; + break; + + default: + status = NDIS_STATUS_SUCCESS; + break; + } + } + + tapAdapterReleaseLock(Adapter,FALSE); + + return status; +} + +BOOLEAN +AdapterCheckForHangEx( + __in NDIS_HANDLE MiniportAdapterContext + ) +/*++ + +Routine Description: + + The MiniportCheckForHangEx handler is called to report the state of the + NIC, or to monitor the responsiveness of an underlying device driver. + This is an optional function. If this handler is not specified, NDIS + judges the driver unresponsive when the driver holds + MiniportQueryInformation or MiniportSetInformation requests for a + time-out interval (deafult 4 sec), and then calls the driver's + MiniportReset function. A NIC driver's MiniportInitialize function can + extend NDIS's time-out interval by calling NdisMSetAttributesEx to + avoid unnecessary resets. + + MiniportCheckForHangEx runs at IRQL <= DISPATCH_LEVEL. + +Arguments: + + MiniportAdapterContext Pointer to our adapter + +Return Value: + + TRUE NDIS calls the driver's MiniportReset function. + FALSE Everything is fine + +--*/ +{ + PTAP_ADAPTER_CONTEXT adapter = (PTAP_ADAPTER_CONTEXT )MiniportAdapterContext; + + //DEBUGP (("[TAP] --> AdapterCheckForHangEx\n")); + + //DEBUGP (("[TAP] <-- AdapterCheckForHangEx; status = FALSE\n")); + + return FALSE; // Everything is fine +} + +NDIS_STATUS +AdapterReset( + __in NDIS_HANDLE MiniportAdapterContext, + __out PBOOLEAN AddressingReset + ) +/*++ + +Routine Description: + + MiniportResetEx is a required to issue a hardware reset to the NIC + and/or to reset the driver's software state. + + 1) The miniport driver can optionally complete any pending + OID requests. NDIS will submit no further OID requests + to the miniport driver for the NIC being reset until + the reset operation has finished. After the reset, + NDIS will resubmit to the miniport driver any OID requests + that were pending but not completed by the miniport driver + before the reset. + + 2) A deserialized miniport driver must complete any pending send + operations. NDIS will not requeue pending send packets for + a deserialized driver since NDIS does not maintain the send + queue for such a driver. + + 3) If MiniportReset returns NDIS_STATUS_PENDING, the driver must + complete the original request subsequently with a call to + NdisMResetComplete. + + MiniportReset runs at IRQL <= DISPATCH_LEVEL. + +Arguments: + +AddressingReset - If multicast or functional addressing information + or the lookahead size, is changed by a reset, + MiniportReset must set the variable at AddressingReset + to TRUE before it returns control. This causes NDIS to + call the MiniportSetInformation function to restore + the information. + +MiniportAdapterContext - Pointer to our adapter + +Return Value: + + NDIS_STATUS + +--*/ +{ + PTAP_ADAPTER_CONTEXT adapter = (PTAP_ADAPTER_CONTEXT )MiniportAdapterContext; + NDIS_STATUS status; + + UNREFERENCED_PARAMETER(MiniportAdapterContext); + UNREFERENCED_PARAMETER(AddressingReset); + + DEBUGP (("[TAP] --> AdapterReset\n")); + + // Indicate that adapter reset is in progress. + adapter->ResetInProgress = TRUE; + + // See note above... + *AddressingReset = FALSE; + + // BUGBUG!!! TODO!!! Lots of work here... + + // Indicate that adapter reset has completed. + adapter->ResetInProgress = FALSE; + + status = NDIS_STATUS_SUCCESS; + + DEBUGP (("[TAP] <-- AdapterReset; status = %8.8X\n",status)); + + return status; +} + +VOID +AdapterDevicePnpEventNotify( + __in NDIS_HANDLE MiniportAdapterContext, + __in PNET_DEVICE_PNP_EVENT NetDevicePnPEvent + ) +{ + PTAP_ADAPTER_CONTEXT adapter = (PTAP_ADAPTER_CONTEXT )MiniportAdapterContext; + + DEBUGP (("[TAP] --> AdapterDevicePnpEventNotify\n")); + +/* + switch (NetDevicePnPEvent->DevicePnPEvent) + { + case NdisDevicePnPEventSurpriseRemoved: + // + // Called when NDIS receives IRP_MN_SUPRISE_REMOVAL. + // NDIS calls MiniportHalt function after this call returns. + // + MP_SET_FLAG(Adapter, fMP_ADAPTER_SURPRISE_REMOVED); + DEBUGP(MP_INFO, "[%p] MPDevicePnpEventNotify: NdisDevicePnPEventSurpriseRemoved\n", Adapter); + break; + + case NdisDevicePnPEventPowerProfileChanged: + // + // After initializing a miniport driver and after miniport driver + // receives an OID_PNP_SET_POWER notification that specifies + // a device power state of NdisDeviceStateD0 (the powered-on state), + // NDIS calls the miniport's MiniportPnPEventNotify function with + // PnPEvent set to NdisDevicePnPEventPowerProfileChanged. + // + DEBUGP(MP_INFO, "[%p] MPDevicePnpEventNotify: NdisDevicePnPEventPowerProfileChanged\n", Adapter); + + if (NetDevicePnPEvent->InformationBufferLength == sizeof(ULONG)) + { + ULONG NdisPowerProfile = *((PULONG)NetDevicePnPEvent->InformationBuffer); + + if (NdisPowerProfile == NdisPowerProfileBattery) + { + DEBUGP(MP_INFO, "[%p] The host system is running on battery power\n", Adapter); + } + if (NdisPowerProfile == NdisPowerProfileAcOnLine) + { + DEBUGP(MP_INFO, "[%p] The host system is running on AC power\n", Adapter); + } + } + break; + + default: + DEBUGP(MP_ERROR, "[%p] MPDevicePnpEventNotify: unknown PnP event 0x%x\n", Adapter, NetDevicePnPEvent->DevicePnPEvent); + } +*/ + DEBUGP (("[TAP] <-- AdapterDevicePnpEventNotify\n")); +} + +VOID +AdapterShutdownEx( + __in NDIS_HANDLE MiniportAdapterContext, + __in NDIS_SHUTDOWN_ACTION ShutdownAction + ) +/*++ + +Routine Description: + + The MiniportShutdownEx handler restores hardware to its initial state when + the system is shut down, whether by the user or because an unrecoverable + system error occurred. This is to ensure that the NIC is in a known + state and ready to be reinitialized when the machine is rebooted after + a system shutdown occurs for any reason, including a crash dump. + + Here just disable the interrupt and stop the DMA engine. Do not free + memory resources or wait for any packet transfers to complete. Do not call + into NDIS at this time. + + This can be called at aribitrary IRQL, including in the context of a + bugcheck. + +Arguments: + + MiniportAdapterContext Pointer to our adapter + ShutdownAction The reason why NDIS called the shutdown function + +Return Value: + + None. + +--*/ +{ + PTAP_ADAPTER_CONTEXT adapter = (PTAP_ADAPTER_CONTEXT )MiniportAdapterContext; + + UNREFERENCED_PARAMETER(ShutdownAction); + UNREFERENCED_PARAMETER(MiniportAdapterContext); + + DEBUGP (("[TAP] --> AdapterShutdownEx\n")); + + // Enter the Shutdown state. + DEBUGP (("[TAP] Miniport State: Shutdown\n")); + + tapAdapterAcquireLock(adapter,FALSE); + adapter->Locked.AdapterState = MiniportShutdownState; + tapAdapterReleaseLock(adapter,FALSE); + + // + // BUGBUG!!! FlushIrpQueues??? + // + + DEBUGP (("[TAP] <-- AdapterShutdownEx\n")); +} + + +// Free adapter context memory and associated resources. +VOID +tapAdapterContextFree( + __in PTAP_ADAPTER_CONTEXT Adapter + ) +{ + PLIST_ENTRY listEntry = &Adapter->AdapterListLink; + + DEBUGP (("[TAP] --> tapAdapterContextFree\n")); + + // Adapter context should already be removed. + ASSERT( (listEntry->Flink == listEntry) && (listEntry->Blink == listEntry ) ); + + // Insure that adapter context has been removed from global adapter list. + RemoveEntryList(&Adapter->AdapterListLink); + + // Free the adapter lock. + NdisFreeSpinLock(&Adapter->AdapterLock); + + // Free the ANSI NetCfgInstanceId buffer. + if(Adapter->NetCfgInstanceIdAnsi.Buffer != NULL) + { + RtlFreeAnsiString(&Adapter->NetCfgInstanceIdAnsi); + } + + Adapter->NetCfgInstanceIdAnsi.Buffer = NULL; + + // Free the receive NBL pool. + if(Adapter->ReceiveNblPool != NULL ) + { + NdisFreeNetBufferListPool(Adapter->ReceiveNblPool); + } + + Adapter->ReceiveNblPool = NULL; + + NdisFreeMemory(Adapter,0,0); + + DEBUGP (("[TAP] <-- tapAdapterContextFree\n")); +} +ULONG +tapGetNetBufferFrameType( + __in PNET_BUFFER NetBuffer + ) +/*++ + +Routine Description: + + Reads the network frame's destination address to determine the type + (broadcast, multicast, etc) + + Runs at IRQL <= DISPATCH_LEVEL. + +Arguments: + + NetBuffer The NB to examine + +Return Value: + + NDIS_PACKET_TYPE_BROADCAST + NDIS_PACKET_TYPE_MULTICAST + NDIS_PACKET_TYPE_DIRECTED + +--*/ +{ + PETH_HEADER ethernetHeader; + + ethernetHeader = (PETH_HEADER )NdisGetDataBuffer( + NetBuffer, + sizeof(ETH_HEADER), + NULL, + 1, + 0 + ); + + ASSERT(ethernetHeader); + + if (ETH_IS_BROADCAST(ethernetHeader->dest)) + { + return NDIS_PACKET_TYPE_BROADCAST; + } + else if(ETH_IS_MULTICAST(ethernetHeader->dest)) + { + return NDIS_PACKET_TYPE_MULTICAST; + } + else + { + return NDIS_PACKET_TYPE_DIRECTED; + } + +} + +ULONG +tapGetNetBufferCountsFromNetBufferList( + __in PNET_BUFFER_LIST NetBufferList, + __inout_opt PULONG TotalByteCount // Of all linked NBs + ) +/*++ + +Routine Description: + + Returns the number of net buffers linked to the net buffer list. + + Optionally retuens the total byte count of all net buffers linked + to the net buffer list + + Runs at IRQL <= DISPATCH_LEVEL. + +Arguments: + + NetBufferList The NBL to examine + +Return Value: + + The number of net buffers linked to the net buffer list. + +--*/ +{ + ULONG netBufferCount = 0; + PNET_BUFFER currentNb; + + if(TotalByteCount) + { + *TotalByteCount = 0; + } + + currentNb = NET_BUFFER_LIST_FIRST_NB(NetBufferList); + + while(currentNb) + { + ++netBufferCount; + + if(TotalByteCount) + { + *TotalByteCount += NET_BUFFER_DATA_LENGTH(currentNb); + } + + // Move to next NB + currentNb = NET_BUFFER_NEXT_NB(currentNb); + } + + return netBufferCount; +} + +VOID +tapAdapterAcquireLock( + __in PTAP_ADAPTER_CONTEXT Adapter, + __in BOOLEAN DispatchLevel + ) +{ + ASSERT(!DispatchLevel || (DISPATCH_LEVEL == KeGetCurrentIrql())); + + if (DispatchLevel) + { + NdisDprAcquireSpinLock(&Adapter->AdapterLock); + } + else + { + NdisAcquireSpinLock(&Adapter->AdapterLock); + } +} + +VOID +tapAdapterReleaseLock( + __in PTAP_ADAPTER_CONTEXT Adapter, + __in BOOLEAN DispatchLevel + ) +{ + ASSERT(!DispatchLevel || (DISPATCH_LEVEL == KeGetCurrentIrql())); + + if (DispatchLevel) + { + NdisDprReleaseSpinLock(&Adapter->AdapterLock); + } + else + { + NdisReleaseSpinLock(&Adapter->AdapterLock); + } +} + + diff --git a/windows/TapDriver6/adapter.h b/windows/TapDriver6/adapter.h new file mode 100644 index 00000000..4f267ec0 --- /dev/null +++ b/windows/TapDriver6/adapter.h @@ -0,0 +1,348 @@ +/* + * 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-2014 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_ADAPTER_CONTEXT_H_ +#define __TAP_ADAPTER_CONTEXT_H_ + +#include "tap.h" + +// Memory allocation tags. +#define TAP_ADAPTER_TAG ((ULONG)'ApaT') // "TapA +#define TAP_RX_NBL_TAG ((ULONG)'RpaT') // "TapR +#define TAP_RX_INJECT_BUFFER_TAG ((ULONG)'IpaT') // "TapI + +#define TAP_MAX_NDIS_NAME_LENGTH 64 // 38 character GUID string plus extra.. + +// TAP receive indication NBL flag definitions. +#define TAP_RX_NBL_FLAGS NBL_FLAGS_MINIPORT_RESERVED +#define TAP_RX_NBL_FLAGS_CLEAR_ALL(_NBL) ((_NBL)->Flags &= ~TAP_RX_NBL_FLAGS) +#define TAP_RX_NBL_FLAG_SET(_NBL, _F) ((_NBL)->Flags |= ((_F) & TAP_RX_NBL_FLAGS)) +#define TAP_RX_NBL_FLAG_CLEAR(_NBL, _F) ((_NBL)->Flags &= ~((_F) & TAP_RX_NBL_FLAGS)) +#define TAP_RX_NBL_FLAG_TEST(_NBL, _F) (((_NBL)->Flags & ((_F) & TAP_RX_NBL_FLAGS)) != 0) + +#define TAP_RX_NBL_FLAGS_IS_P2P 0x00001000 +#define TAP_RX_NBL_FLAGS_IS_INJECTED 0x00002000 + +// MSDN Ref: http://msdn.microsoft.com/en-us/library/windows/hardware/ff560490(v=vs.85).aspx +typedef +enum _TAP_MINIPORT_ADAPTER_STATE +{ + // The Halted state is the initial state of all adapters. When an + // adapter is in the Halted state, NDIS can call the driver's + // MiniportInitializeEx function to initialize the adapter. + MiniportHaltedState, + + // In the Shutdown state, a system shutdown and restart must occur + // before the system can use the adapter again. + MiniportShutdownState, + + // In the Initializing state, a miniport driver completes any + //operations that are required to initialize an adapter. + MiniportInitializingState, + + // Entering the Paused state... + MiniportPausingState, + + // In the Paused state, the adapter does not indicate received + // network data or accept send requests. + MiniportPausedState, + + // In the Running state, a miniport driver performs send and + // receive processing for an adapter. + MiniportRunning, + + // In the Restarting state, a miniport driver completes any + // operations that are required to restart send and receive + // operations for an adapter. + MiniportRestartingState +} TAP_MINIPORT_ADAPTER_STATE, *PTAP_MINIPORT_ADAPTER_STATE; + +// +// Each adapter managed by this driver has a TapAdapter struct. +// ------------------------------------------------------------ +// Since there is a one-to-one relationship between adapter instances +// and device instances this structure is the device extension as well. +// +typedef struct _TAP_ADAPTER_CONTEXT +{ + LIST_ENTRY AdapterListLink; + + volatile LONG RefCount; + + NDIS_HANDLE MiniportAdapterHandle; + + NDIS_SPIN_LOCK AdapterLock; // Lock for protection of state and outstanding sends and recvs + + // + // All fields that are protected by the AdapterLock are included + // in the Locked structure to remind us to take the Lock + // before accessing them :) + // + struct + { + TAP_MINIPORT_ADAPTER_STATE AdapterState; + } Locked; + + BOOLEAN ResetInProgress; + + // + // NetCfgInstanceId as UNICODE_STRING + // ---------------------------------- + // This a GUID string provided by NDIS that identifies the adapter instance. + // An example is: + // + // NetCfgInstanceId={410EB49D-2381-4FE7-9B36-498E22619DF0} + // + // Other names are derived from NetCfgInstanceId. For example, MiniportName: + // + // MiniportName=\DEVICE\{410EB49D-2381-4FE7-9B36-498E22619DF0} + // + NDIS_STRING NetCfgInstanceId; + WCHAR NetCfgInstanceIdBuffer[TAP_MAX_NDIS_NAME_LENGTH]; + +# define MINIPORT_INSTANCE_ID(a) ((a)->NetCfgInstanceIdAnsi.Buffer) + ANSI_STRING NetCfgInstanceIdAnsi; // Used occasionally + + ULONG MtuSize; // 1500 byte (typical) + + // TRUE if adapter should always be "connected" even when device node + // is not open by a userspace process. + // + // FALSE if connection state is application controlled. + BOOLEAN MediaStateAlwaysConnected; + + // TRUE if device is "connected". + BOOLEAN LogicalMediaState; + + NDIS_DEVICE_POWER_STATE CurrentPowerState; + + BOOLEAN AllowNonAdmin; + + MACADDR PermanentAddress; // From registry, if available + MACADDR CurrentAddress; + + // Device registration parameters from NdisRegisterDeviceEx. + NDIS_STRING DeviceName; + WCHAR DeviceNameBuffer[TAP_MAX_NDIS_NAME_LENGTH]; + + NDIS_STRING LinkName; + WCHAR LinkNameBuffer[TAP_MAX_NDIS_NAME_LENGTH]; + + NDIS_HANDLE DeviceHandle; + PDEVICE_OBJECT DeviceObject; + BOOLEAN TapDeviceCreated; // WAS: m_TapIsRunning + + PFILE_OBJECT TapFileObject; // Exclusive access + BOOLEAN TapFileIsOpen; // WAS: m_TapOpens + LONG TapFileOpenCount; // WAS: m_NumTapOpens + + // Cancel-Safe read IRP queue. + TAP_IRP_CSQ PendingReadIrpQueue; + + // Queue containing TAP packets representing host send NBs. These are + // waiting to be read by user-mode application. + TAP_PACKET_QUEUE SendPacketQueue; + + // NBL pool for making TAP receive indications. + NDIS_HANDLE ReceiveNblPool; + + volatile LONG ReceiveNblInFlightCount; +#define TAP_WAIT_POLL_LOOP_TIMEOUT 3000 // 3 seconds + NDIS_EVENT ReceiveNblInFlightCountZeroEvent; + + // 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 + + // Info 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; + + // Multicast list. Fixed size. + ULONG ulMCListSize; + UCHAR MCList[TAP_MAX_MCAST_LIST][MACADDR_SIZE]; + + ULONG PacketFilter; + ULONG ulLookahead; + + // + // Statistics + // ------------------------------------------------------------------------- + // + + // Packet counts + ULONG64 FramesRxDirected; + ULONG64 FramesRxMulticast; + ULONG64 FramesRxBroadcast; + ULONG64 FramesTxDirected; + ULONG64 FramesTxMulticast; + ULONG64 FramesTxBroadcast; + + // Byte counts + ULONG64 BytesRxDirected; + ULONG64 BytesRxMulticast; + ULONG64 BytesRxBroadcast; + ULONG64 BytesTxDirected; + ULONG64 BytesTxMulticast; + ULONG64 BytesTxBroadcast; + + // Count of transmit errors + ULONG TxAbortExcessCollisions; + ULONG TxLateCollisions; + ULONG TxDmaUnderrun; + ULONG TxLostCRS; + ULONG TxOKButDeferred; + ULONG OneRetry; + ULONG MoreThanOneRetry; + ULONG TotalRetries; + ULONG TransmitFailuresOther; + + // Count of receive errors + ULONG RxCrcErrors; + ULONG RxAlignmentErrors; + ULONG RxResourceErrors; + ULONG RxDmaOverrunErrors; + ULONG RxCdtFrames; + ULONG RxRuntErrors; + +#if PACKET_TRUNCATION_CHECK + LONG m_RxTrunc, m_TxTrunc; +#endif + + BOOLEAN m_InterfaceIsRunning; + LONG m_Rx, m_RxErr; + NDIS_MEDIUM m_Medium; + + // Help to tear down the adapter by keeping + // some state information on allocated + // resources. + BOOLEAN m_CalledAdapterFreeResources; + BOOLEAN m_RegisteredAdapterShutdownHandler; + +} TAP_ADAPTER_CONTEXT, *PTAP_ADAPTER_CONTEXT; + +FORCEINLINE +LONG +tapAdapterContextReference( + __in PTAP_ADAPTER_CONTEXT Adapter + ) +{ + LONG refCount = NdisInterlockedIncrement(&Adapter->RefCount); + + ASSERT(refCount>1); // Cannot dereference a zombie. + + return refCount; +} + +VOID +tapAdapterContextFree( + __in PTAP_ADAPTER_CONTEXT Adapter + ); + +FORCEINLINE +LONG +tapAdapterContextDereference( + IN PTAP_ADAPTER_CONTEXT Adapter + ) +{ + LONG refCount = NdisInterlockedDecrement(&Adapter->RefCount); + ASSERT(refCount >= 0); + if (!refCount) + { + tapAdapterContextFree(Adapter); + } + + return refCount; +} + +VOID +tapAdapterAcquireLock( + __in PTAP_ADAPTER_CONTEXT Adapter, + __in BOOLEAN DispatchLevel + ); + +VOID +tapAdapterReleaseLock( + __in PTAP_ADAPTER_CONTEXT Adapter, + __in BOOLEAN DispatchLevel + ); + +// Returns with added reference on adapter context. +PTAP_ADAPTER_CONTEXT +tapAdapterContextFromDeviceObject( + __in PDEVICE_OBJECT DeviceObject + ); + +BOOLEAN +tapAdapterReadAndWriteReady( + __in PTAP_ADAPTER_CONTEXT Adapter + ); + +NDIS_STATUS +tapAdapterSendAndReceiveReady( + __in PTAP_ADAPTER_CONTEXT Adapter + ); + +ULONG +tapGetNetBufferFrameType( + __in PNET_BUFFER NetBuffer + ); + +ULONG +tapGetNetBufferCountsFromNetBufferList( + __in PNET_BUFFER_LIST NetBufferList, + __inout_opt PULONG TotalByteCount // Of all linked NBs + ); + +// Prototypes for standard NDIS miniport entry points +MINIPORT_SET_OPTIONS AdapterSetOptions; +MINIPORT_INITIALIZE AdapterCreate; +MINIPORT_HALT AdapterHalt; +MINIPORT_UNLOAD TapDriverUnload; +MINIPORT_PAUSE AdapterPause; +MINIPORT_RESTART AdapterRestart; +MINIPORT_OID_REQUEST AdapterOidRequest; +MINIPORT_SEND_NET_BUFFER_LISTS AdapterSendNetBufferLists; +MINIPORT_RETURN_NET_BUFFER_LISTS AdapterReturnNetBufferLists; +MINIPORT_CANCEL_SEND AdapterCancelSend; +MINIPORT_CHECK_FOR_HANG AdapterCheckForHangEx; +MINIPORT_RESET AdapterReset; +MINIPORT_DEVICE_PNP_EVENT_NOTIFY AdapterDevicePnpEventNotify; +MINIPORT_SHUTDOWN AdapterShutdownEx; +MINIPORT_CANCEL_OID_REQUEST AdapterCancelOidRequest; + +#endif // __TAP_ADAPTER_CONTEXT_H_
\ No newline at end of file diff --git a/windows/TapDriver6/config.h b/windows/TapDriver6/config.h new file mode 100644 index 00000000..37e26a92 --- /dev/null +++ b/windows/TapDriver6/config.h @@ -0,0 +1,20 @@ +/* +#define PRODUCT_NAME "@PRODUCT_NAME@" +#define PRODUCT_VERSION "@PRODUCT_VERSION@" +#define PRODUCT_VERSION_RESOURCE @PRODUCT_VERSION_RESOURCE@ +#define PRODUCT_TAP_WIN_COMPONENT_ID "@PRODUCT_TAP_WIN_COMPONENT_ID@" +#define PRODUCT_TAP_WIN_MAJOR @PRODUCT_TAP_WIN_MAJOR@ +#define PRODUCT_TAP_WIN_MINOR @PRODUCT_TAP_WIN_MINOR@ +#define PRODUCT_TAP_WIN_PROVIDER "@PRODUCT_TAP_WIN_PROVIDER@" +#define PRODUCT_TAP_WIN_DEVICE_DESCRIPTION "@PRODUCT_TAP_WIN_DEVICE_DESCRIPTION@" +#define PRODUCT_TAP_WIN_RELDATE "@PRODUCT_TAP_WIN_RELDATE@" +*/ +#define PRODUCT_NAME "ZeroTier One Virtual Network Port" +#define PRODUCT_VERSION "3.0.0" +#define PRODUCT_VERSION_RESOURCE 3,0,0,1 +#define PRODUCT_TAP_WIN_COMPONENT_ID "zttap300" +#define PRODUCT_TAP_WIN_MAJOR 3 +#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 "04/24/2015" diff --git a/windows/TapDriver6/constants.h b/windows/TapDriver6/constants.h new file mode 100644 index 00000000..31b2d545 --- /dev/null +++ b/windows/TapDriver6/constants.h @@ -0,0 +1,195 @@ +/* + * 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-2014 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 + + +// +// Update the driver version number every time you release a new driver +// The high word is the major version. The low word is the minor version. +// Also make sure that VER_FILEVERSION specified in the .RC file also +// matches with the driver version because NDISTESTER checks for that. +// +#ifndef TAP_DRIVER_MAJOR_VERSION + +#define TAP_DRIVER_MAJOR_VERSION 0x04 +#define TAP_DRIVER_MINOR_VERSION 0x02 + +#endif + +#define TAP_DRIVER_VENDOR_VERSION ((TAP_DRIVER_MAJOR_VERSION << 16) | TAP_DRIVER_MINOR_VERSION) + +// +// Define the NDIS miniport interface version that this driver targets. +// +#if defined(NDIS60_MINIPORT) +# define TAP_NDIS_MAJOR_VERSION 6 +# define TAP_NDIS_MINOR_VERSION 0 +#elif defined(NDIS61_MINIPORT) +# define TAP_NDIS_MAJOR_VERSION 6 +# define TAP_NDIS_MINOR_VERSION 1 +#elif defined(NDIS620_MINIPORT) +# define TAP_NDIS_MAJOR_VERSION 6 +# define TAP_NDIS_MINOR_VERSION 20 +#elif defined(NDIS630_MINIPORT) +# define TAP_NDIS_MAJOR_VERSION 6 +# define TAP_NDIS_MINOR_VERSION 30 +#else +#define TAP_NDIS_MAJOR_VERSION 5 +#define TAP_NDIS_MINOR_VERSION 0 +#endif + +//=========================================================== +// Driver constants +//=========================================================== + +#define ETHERNET_HEADER_SIZE (sizeof (ETH_HEADER)) +#define ETHERNET_MTU 1500 +#define ETHERNET_PACKET_SIZE (ETHERNET_MTU + ETHERNET_HEADER_SIZE) +#define DEFAULT_PACKET_LOOKAHEAD (ETHERNET_PACKET_SIZE) +#define VLAN_TAG_SIZE 4 + +//=========================================================== +// Medium properties +//=========================================================== + +#define TAP_FRAME_HEADER_SIZE ETHERNET_HEADER_SIZE +#define TAP_FRAME_MAX_DATA_SIZE ETHERNET_MTU +#define TAP_MAX_FRAME_SIZE (TAP_FRAME_HEADER_SIZE + TAP_FRAME_MAX_DATA_SIZE) +#define TAP_MIN_FRAME_SIZE 60 + +#define TAP_MEDIUM_TYPE NdisMedium802_3 + +//=========================================================== +// Physical adapter properties +//=========================================================== + +// The bus that connects the adapter to the PC. +// (Example: PCI adapters should use NdisInterfacePci). +#define TAP_INTERFACE_TYPE NdisInterfaceInternal + +#define TAP_VENDOR_DESC PRODUCT_TAP_WIN_DEVICE_DESCRIPTION + +// Highest byte is the NIC byte plus three vendor bytes. This is normally +// obtained from the NIC. +#define TAP_VENDOR_ID 0x00FFFFFF + +// If you have physical hardware on 802.3, use NdisPhysicalMedium802_3. +#define TAP_PHYSICAL_MEDIUM NdisPhysicalMediumUnspecified + +// Claim to be 100mbps duplex +#define MEGABITS_PER_SECOND 1000000ULL +#define TAP_XMIT_SPEED (100ULL*MEGABITS_PER_SECOND) +#define TAP_RECV_SPEED (100ULL*MEGABITS_PER_SECOND) + +// Max number of multicast addresses supported in hardware +#define TAP_MAX_MCAST_LIST 32 + +#define TAP_MAX_LOOKAHEAD TAP_FRAME_MAX_DATA_SIZE +#define TAP_BUFFER_SIZE TAP_MAX_FRAME_SIZE + +// Set this value to TRUE if there is a physical adapter. +#define TAP_HAS_PHYSICAL_CONNECTOR FALSE +#define TAP_ACCESS_TYPE NET_IF_ACCESS_BROADCAST +#define TAP_DIRECTION_TYPE NET_IF_DIRECTION_SENDRECEIVE +#define TAP_CONNECTION_TYPE NET_IF_CONNECTION_DEDICATED + +// This value must match the *IfType in the driver .inf file +#define TAP_IFTYPE IF_TYPE_ETHERNET_CSMACD + +// +// This is a virtual device, so it can tolerate surprise removal and +// suspend. Ensure the correct flags are set for your hardware. +// +#define TAP_ADAPTER_ATTRIBUTES_FLAGS (\ + NDIS_MINIPORT_ATTRIBUTES_SURPRISE_REMOVE_OK | NDIS_MINIPORT_ATTRIBUTES_NDIS_WDM) + +#define TAP_SUPPORTED_FILTERS ( \ + NDIS_PACKET_TYPE_DIRECTED | \ + NDIS_PACKET_TYPE_MULTICAST | \ + NDIS_PACKET_TYPE_BROADCAST | \ + NDIS_PACKET_TYPE_ALL_LOCAL | \ + NDIS_PACKET_TYPE_PROMISCUOUS | \ + NDIS_PACKET_TYPE_ALL_MULTICAST) + +#define TAP_MAX_MCAST_LIST 32 // Max length of multicast address list + +// +// Specify a bitmask that defines optional properties of the NIC. +// This miniport indicates receive with NdisMIndicateReceiveNetBufferLists +// function. Such a driver should set this NDIS_MAC_OPTION_TRANSFERS_NOT_PEND +// flag. +// +// NDIS_MAC_OPTION_NO_LOOPBACK tells NDIS that NIC has no internal +// loopback support so NDIS will manage loopbacks on behalf of +// this driver. +// +// NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA tells the protocol that +// our receive buffer is not on a device-specific card. If +// NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA is not set, multi-buffer +// indications are copied to a single flat buffer. +// + +#define TAP_MAC_OPTIONS (\ + NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA | \ + NDIS_MAC_OPTION_TRANSFERS_NOT_PEND | \ + NDIS_MAC_OPTION_NO_LOOPBACK) + +#define TAP_ADAPTER_CHECK_FOR_HANG_TIME_IN_SECONDS 4 + + +// NDIS 6.x miniports must support all counters in OID_GEN_STATISTICS. +#define TAP_SUPPORTED_STATISTICS (\ + NDIS_STATISTICS_FLAGS_VALID_DIRECTED_FRAMES_RCV | \ + NDIS_STATISTICS_FLAGS_VALID_MULTICAST_FRAMES_RCV | \ + NDIS_STATISTICS_FLAGS_VALID_BROADCAST_FRAMES_RCV | \ + NDIS_STATISTICS_FLAGS_VALID_BYTES_RCV | \ + NDIS_STATISTICS_FLAGS_VALID_RCV_DISCARDS | \ + NDIS_STATISTICS_FLAGS_VALID_RCV_ERROR | \ + NDIS_STATISTICS_FLAGS_VALID_DIRECTED_FRAMES_XMIT | \ + NDIS_STATISTICS_FLAGS_VALID_MULTICAST_FRAMES_XMIT | \ + NDIS_STATISTICS_FLAGS_VALID_BROADCAST_FRAMES_XMIT | \ + NDIS_STATISTICS_FLAGS_VALID_BYTES_XMIT | \ + NDIS_STATISTICS_FLAGS_VALID_XMIT_ERROR | \ + NDIS_STATISTICS_FLAGS_VALID_XMIT_DISCARDS | \ + NDIS_STATISTICS_FLAGS_VALID_DIRECTED_BYTES_RCV | \ + NDIS_STATISTICS_FLAGS_VALID_MULTICAST_BYTES_RCV | \ + NDIS_STATISTICS_FLAGS_VALID_BROADCAST_BYTES_RCV | \ + NDIS_STATISTICS_FLAGS_VALID_DIRECTED_BYTES_XMIT | \ + NDIS_STATISTICS_FLAGS_VALID_MULTICAST_BYTES_XMIT | \ + NDIS_STATISTICS_FLAGS_VALID_BROADCAST_BYTES_XMIT) + + +#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 diff --git a/windows/TapDriver6/device.c b/windows/TapDriver6/device.c new file mode 100644 index 00000000..2b7ba9b1 --- /dev/null +++ b/windows/TapDriver6/device.c @@ -0,0 +1,1169 @@ +/* + * 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-2014 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 files. +// + +#include "tap.h" +#include <wdmsec.h> // for SDDLs + +//====================================================================== +// TAP Win32 Device I/O Callbacks +//====================================================================== + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( PAGE, TapDeviceCreate) +#pragma alloc_text( PAGE, TapDeviceControl) +#pragma alloc_text( PAGE, TapDeviceCleanup) +#pragma alloc_text( PAGE, TapDeviceClose) +#endif // ALLOC_PRAGMA + +//=================================================================== +// Go back to default TAP mode from Point-To-Point mode. +// Also reset (i.e. disable) DHCP Masq mode. +//=================================================================== +VOID tapResetAdapterState( + __in PTAP_ADAPTER_CONTEXT Adapter + ) +{ + // Point-To-Point + Adapter->m_tun = FALSE; + Adapter->m_localIP = 0; + Adapter->m_remoteNetwork = 0; + Adapter->m_remoteNetmask = 0; + NdisZeroMemory (&Adapter->m_TapToUser, sizeof (Adapter->m_TapToUser)); + NdisZeroMemory (&Adapter->m_UserToTap, sizeof (Adapter->m_UserToTap)); + NdisZeroMemory (&Adapter->m_UserToTap_IPv6, sizeof (Adapter->m_UserToTap_IPv6)); + + // DHCP Masq + Adapter->m_dhcp_enabled = FALSE; + Adapter->m_dhcp_server_arp = FALSE; + Adapter->m_dhcp_user_supplied_options_buffer_len = 0; + Adapter->m_dhcp_addr = 0; + Adapter->m_dhcp_netmask = 0; + Adapter->m_dhcp_server_ip = 0; + Adapter->m_dhcp_lease_time = 0; + Adapter->m_dhcp_received_discover = FALSE; + Adapter->m_dhcp_bad_requests = 0; + NdisZeroMemory (Adapter->m_dhcp_server_mac, MACADDR_SIZE); +} + +// IRP_MJ_CREATE +NTSTATUS +TapDeviceCreate( + PDEVICE_OBJECT DeviceObject, + PIRP Irp + ) +/*++ + +Routine Description: + + This routine is called by the I/O system when the device is opened. + + No action is performed other than completing the request successfully. + +Arguments: + + DeviceObject - a pointer to the object that represents the device + that I/O is to be done on. + + Irp - a pointer to the I/O Request Packet for this request. + +Return Value: + + NT status code + +--*/ +{ + NDIS_STATUS status; + PIO_STACK_LOCATION irpSp;// Pointer to current stack location + PTAP_ADAPTER_CONTEXT adapter = NULL; + PFILE_OBJECT originalFileObject; + + PAGED_CODE(); + + DEBUGP (("[TAP] --> TapDeviceCreate\n")); + + irpSp = IoGetCurrentIrpStackLocation(Irp); + + // + // Invalidate file context + // + irpSp->FileObject->FsContext = NULL; + irpSp->FileObject->FsContext2 = NULL; + + // + // Find adapter context for this device. + // ------------------------------------- + // Returns with added reference on adapter context. + // + adapter = tapAdapterContextFromDeviceObject(DeviceObject); + + // Insure that adapter exists. + ASSERT(adapter); + + if(adapter == NULL ) + { + DEBUGP (("[TAP] release [%d.%d] open request; adapter not found\n", + TAP_DRIVER_MAJOR_VERSION, + TAP_DRIVER_MINOR_VERSION + )); + + Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST; + Irp->IoStatus.Information = 0; + + IoCompleteRequest( Irp, IO_NO_INCREMENT ); + + return STATUS_DEVICE_DOES_NOT_EXIST; + } + + DEBUGP(("[%s] [TAP] release [%d.%d] open request (TapFileIsOpen=%d)\n", + MINIPORT_INSTANCE_ID(adapter), + TAP_DRIVER_MAJOR_VERSION, + TAP_DRIVER_MINOR_VERSION, + adapter->TapFileIsOpen + )); + + // Enforce exclusive access + originalFileObject = InterlockedCompareExchangePointer( + &adapter->TapFileObject, + irpSp->FileObject, + NULL + ); + + if(originalFileObject == NULL) + { + irpSp->FileObject->FsContext = adapter; // Quick reference + + status = STATUS_SUCCESS; + } + else + { + status = STATUS_UNSUCCESSFUL; + } + + // Release the lock. + //tapAdapterReleaseLock(adapter,FALSE); + + if(status == STATUS_SUCCESS) + { + // Reset adapter state on successful open. + tapResetAdapterState(adapter); + + adapter->TapFileIsOpen = 1; // Legacy... + + // NOTE!!! Reference added by tapAdapterContextFromDeviceObject + // will be removed when file is closed. + } + else + { + DEBUGP (("[%s] TAP is presently unavailable (TapFileIsOpen=%d)\n", + MINIPORT_INSTANCE_ID(adapter), adapter->TapFileIsOpen + )); + + NOTE_ERROR(); + + // Remove reference added by tapAdapterContextFromDeviceObject. + tapAdapterContextDereference(adapter); + } + + // Complete the IRP. + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = 0; + + IoCompleteRequest( Irp, IO_NO_INCREMENT ); + + DEBUGP (("[TAP] <-- TapDeviceCreate; status = %8.8X\n",status)); + + return status; +} + +//=================================================== +// Tell Windows whether the TAP device should be +// considered "connected" or "disconnected". +// +// Allows application control of media connect state. +//=================================================== +VOID +tapSetMediaConnectStatus( + __in PTAP_ADAPTER_CONTEXT Adapter, + __in BOOLEAN LogicalMediaState + ) +{ + NDIS_STATUS_INDICATION statusIndication; + NDIS_LINK_STATE linkState; + + NdisZeroMemory(&statusIndication, sizeof(NDIS_STATUS_INDICATION)); + NdisZeroMemory(&linkState, sizeof(NDIS_LINK_STATE)); + + // + // Fill in object headers + // + statusIndication.Header.Type = NDIS_OBJECT_TYPE_STATUS_INDICATION; + statusIndication.Header.Revision = NDIS_STATUS_INDICATION_REVISION_1; + statusIndication.Header.Size = sizeof(NDIS_STATUS_INDICATION); + + linkState.Header.Revision = NDIS_LINK_STATE_REVISION_1; + linkState.Header.Type = NDIS_OBJECT_TYPE_DEFAULT; + linkState.Header.Size = sizeof(NDIS_LINK_STATE); + + // + // Link state buffer + // + if(Adapter->LogicalMediaState == TRUE) + { + linkState.MediaConnectState = MediaConnectStateConnected; + } + + linkState.MediaDuplexState = MediaDuplexStateFull; + linkState.RcvLinkSpeed = TAP_RECV_SPEED; + linkState.XmitLinkSpeed = TAP_XMIT_SPEED; + + // + // Fill in the status buffer + // + statusIndication.StatusCode = NDIS_STATUS_LINK_STATE; + statusIndication.SourceHandle = Adapter->MiniportAdapterHandle; + statusIndication.DestinationHandle = NULL; + statusIndication.RequestId = 0; + + statusIndication.StatusBuffer = &linkState; + statusIndication.StatusBufferSize = sizeof(NDIS_LINK_STATE); + + // Fill in new media connect state. + if ( (Adapter->LogicalMediaState != LogicalMediaState) && !Adapter->MediaStateAlwaysConnected) + { + Adapter->LogicalMediaState = LogicalMediaState; + + if (LogicalMediaState == TRUE) + { + linkState.MediaConnectState = MediaConnectStateConnected; + + DEBUGP (("[TAP] Set MediaConnectState: Connected.\n")); + } + else + { + linkState.MediaConnectState = MediaConnectStateDisconnected; + + DEBUGP (("[TAP] Set MediaConnectState: Disconnected.\n")); + } + } + + // Make the status indication. + if(Adapter->Locked.AdapterState != MiniportHaltedState) + { + NdisMIndicateStatusEx(Adapter->MiniportAdapterHandle, &statusIndication); + } +} + +//====================================================== +// 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 ( + __in PTAP_ADAPTER_CONTEXT Adapter + ) +{ + if (Adapter->m_tun && Adapter->m_dhcp_enabled) + { + if ((Adapter->m_dhcp_server_ip & Adapter->m_remoteNetmask) == Adapter->m_remoteNetwork) + { + ETH_COPY_NETWORK_ADDRESS (Adapter->m_dhcp_server_mac, Adapter->m_TapToUser.dest); + Adapter->m_dhcp_server_arp = FALSE; + } + } +} + +// IRP_MJ_DEVICE_CONTROL callback. +NTSTATUS +TapDeviceControl( + PDEVICE_OBJECT DeviceObject, + PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to perform a device I/O + control function. + +Arguments: + + DeviceObject - a pointer to the object that represents the device + that I/O is to be done on. + + Irp - a pointer to the I/O Request Packet for this request. + +Return Value: + + NT status code + +--*/ + +{ + NTSTATUS ntStatus = STATUS_SUCCESS; // Assume success + PIO_STACK_LOCATION irpSp; // Pointer to current stack location + PTAP_ADAPTER_CONTEXT adapter = NULL; + ULONG inBufLength; // Input buffer length + ULONG outBufLength; // Output buffer length + PCHAR inBuf, outBuf; // pointer to Input and output buffer + PMDL mdl = NULL; + PCHAR buffer = NULL; + + PAGED_CODE(); + + irpSp = IoGetCurrentIrpStackLocation( Irp ); + + // + // Fetch adapter context for this device. + // -------------------------------------- + // Adapter pointer was stashed in FsContext when handle was opened. + // + adapter = (PTAP_ADAPTER_CONTEXT )(irpSp->FileObject)->FsContext; + + ASSERT(adapter); + + inBufLength = irpSp->Parameters.DeviceIoControl.InputBufferLength; + outBufLength = irpSp->Parameters.DeviceIoControl.OutputBufferLength; + + if (!inBufLength || !outBufLength) + { + ntStatus = STATUS_INVALID_PARAMETER; + goto End; + } + + // + // Determine which I/O control code was specified. + // + switch ( irpSp->Parameters.DeviceIoControl.IoControlCode ) + { + case TAP_WIN_IOCTL_GET_MAC: + { + if (outBufLength >= MACADDR_SIZE ) + { + ETH_COPY_NETWORK_ADDRESS( + Irp->AssociatedIrp.SystemBuffer, + adapter->CurrentAddress + ); + + Irp->IoStatus.Information = MACADDR_SIZE; + } + else + { + NOTE_ERROR(); + Irp->IoStatus.Status = ntStatus = STATUS_BUFFER_TOO_SMALL; + } + } + break; + + case TAP_WIN_IOCTL_GET_VERSION: + { + const ULONG size = sizeof (ULONG) * 3; + + if (outBufLength >= size) + { + ((PULONG) (Irp->AssociatedIrp.SystemBuffer))[0] + = TAP_DRIVER_MAJOR_VERSION; + + ((PULONG) (Irp->AssociatedIrp.SystemBuffer))[1] + = TAP_DRIVER_MINOR_VERSION; + + ((PULONG) (Irp->AssociatedIrp.SystemBuffer))[2] +#if DBG + = 1; +#else + = 0; +#endif + Irp->IoStatus.Information = size; + } + else + { + NOTE_ERROR(); + Irp->IoStatus.Status = ntStatus = STATUS_BUFFER_TOO_SMALL; + } + } + break; + + case TAP_WIN_IOCTL_GET_MTU: + { + const ULONG size = sizeof (ULONG) * 1; + + if (outBufLength >= size) + { + ((PULONG) (Irp->AssociatedIrp.SystemBuffer))[0] + = adapter->MtuSize; + + Irp->IoStatus.Information = size; + } + else + { + NOTE_ERROR(); + Irp->IoStatus.Status = ntStatus = STATUS_BUFFER_TOO_SMALL; + } + } + break; + + case TAP_WIN_IOCTL_CONFIG_TUN: + { + if(inBufLength >= sizeof(IPADDR)*3) + { + MACADDR dest; + + adapter->m_tun = FALSE; + + GenerateRelatedMAC (dest, adapter->CurrentAddress, 1); + + adapter->m_localIP = ((IPADDR*) (Irp->AssociatedIrp.SystemBuffer))[0]; + adapter->m_remoteNetwork = ((IPADDR*) (Irp->AssociatedIrp.SystemBuffer))[1]; + adapter->m_remoteNetmask = ((IPADDR*) (Irp->AssociatedIrp.SystemBuffer))[2]; + + // Sanity check on network/netmask + if ((adapter->m_remoteNetwork & adapter->m_remoteNetmask) != adapter->m_remoteNetwork) + { + NOTE_ERROR(); + Irp->IoStatus.Status = ntStatus = STATUS_INVALID_PARAMETER; + break; + } + + ETH_COPY_NETWORK_ADDRESS (adapter->m_TapToUser.src, adapter->CurrentAddress); + ETH_COPY_NETWORK_ADDRESS (adapter->m_TapToUser.dest, dest); + ETH_COPY_NETWORK_ADDRESS (adapter->m_UserToTap.src, dest); + ETH_COPY_NETWORK_ADDRESS (adapter->m_UserToTap.dest, adapter->CurrentAddress); + + adapter->m_TapToUser.proto = adapter->m_UserToTap.proto = htons (NDIS_ETH_TYPE_IPV4); + adapter->m_UserToTap_IPv6 = adapter->m_UserToTap; + adapter->m_UserToTap_IPv6.proto = htons(NDIS_ETH_TYPE_IPV6); + + adapter->m_tun = TRUE; + + CheckIfDhcpAndTunMode (adapter); + + Irp->IoStatus.Information = 1; // Simple boolean value + + DEBUGP (("[TAP] Set TUN mode.\n")); + } + else + { + NOTE_ERROR(); + Irp->IoStatus.Status = ntStatus = STATUS_INVALID_PARAMETER; + } + } + break; + + case TAP_WIN_IOCTL_CONFIG_POINT_TO_POINT: + { + if(inBufLength >= sizeof(IPADDR)*2) + { + MACADDR dest; + + adapter->m_tun = FALSE; + + GenerateRelatedMAC (dest, adapter->CurrentAddress, 1); + + adapter->m_localIP = ((IPADDR*) (Irp->AssociatedIrp.SystemBuffer))[0]; + adapter->m_remoteNetwork = ((IPADDR*) (Irp->AssociatedIrp.SystemBuffer))[1]; + adapter->m_remoteNetmask = ~0; + + ETH_COPY_NETWORK_ADDRESS (adapter->m_TapToUser.src, adapter->CurrentAddress); + ETH_COPY_NETWORK_ADDRESS (adapter->m_TapToUser.dest, dest); + ETH_COPY_NETWORK_ADDRESS (adapter->m_UserToTap.src, dest); + ETH_COPY_NETWORK_ADDRESS (adapter->m_UserToTap.dest, adapter->CurrentAddress); + + adapter->m_TapToUser.proto = adapter->m_UserToTap.proto = htons (NDIS_ETH_TYPE_IPV4); + adapter->m_UserToTap_IPv6 = adapter->m_UserToTap; + adapter->m_UserToTap_IPv6.proto = htons(NDIS_ETH_TYPE_IPV6); + + adapter->m_tun = TRUE; + + CheckIfDhcpAndTunMode (adapter); + + Irp->IoStatus.Information = 1; // Simple boolean value + + DEBUGP (("[TAP] Set P2P mode.\n")); + } + else + { + NOTE_ERROR(); + Irp->IoStatus.Status = ntStatus = STATUS_INVALID_PARAMETER; + } + } + break; + + case TAP_WIN_IOCTL_CONFIG_DHCP_MASQ: + { + if(inBufLength >= sizeof(IPADDR)*4) + { + adapter->m_dhcp_enabled = FALSE; + adapter->m_dhcp_server_arp = FALSE; + adapter->m_dhcp_user_supplied_options_buffer_len = 0; + + // Adapter IP addr / netmask + adapter->m_dhcp_addr = + ((IPADDR*) (Irp->AssociatedIrp.SystemBuffer))[0]; + adapter->m_dhcp_netmask = + ((IPADDR*) (Irp->AssociatedIrp.SystemBuffer))[1]; + + // IP addr of DHCP masq server + adapter->m_dhcp_server_ip = + ((IPADDR*) (Irp->AssociatedIrp.SystemBuffer))[2]; + + // Lease time in seconds + adapter->m_dhcp_lease_time = + ((IPADDR*) (Irp->AssociatedIrp.SystemBuffer))[3]; + + GenerateRelatedMAC( + adapter->m_dhcp_server_mac, + adapter->CurrentAddress, + 2 + ); + + adapter->m_dhcp_enabled = TRUE; + adapter->m_dhcp_server_arp = TRUE; + + CheckIfDhcpAndTunMode (adapter); + + Irp->IoStatus.Information = 1; // Simple boolean value + + DEBUGP (("[TAP] Configured DHCP MASQ.\n")); + } + else + { + NOTE_ERROR(); + Irp->IoStatus.Status = ntStatus = STATUS_INVALID_PARAMETER; + } + } + break; + + case TAP_WIN_IOCTL_CONFIG_DHCP_SET_OPT: + { + if (inBufLength <= DHCP_USER_SUPPLIED_OPTIONS_BUFFER_SIZE + && adapter->m_dhcp_enabled) + { + adapter->m_dhcp_user_supplied_options_buffer_len = 0; + + NdisMoveMemory( + adapter->m_dhcp_user_supplied_options_buffer, + Irp->AssociatedIrp.SystemBuffer, + inBufLength + ); + + adapter->m_dhcp_user_supplied_options_buffer_len = + inBufLength; + + Irp->IoStatus.Information = 1; // Simple boolean value + + DEBUGP (("[TAP] Set DHCP OPT.\n")); + } + else + { + NOTE_ERROR(); + Irp->IoStatus.Status = ntStatus = STATUS_INVALID_PARAMETER; + } + } + break; + + case TAP_WIN_IOCTL_GET_INFO: + { + char state[16]; + + // Fetch adapter (miniport) state. + if (tapAdapterSendAndReceiveReady(adapter) == NDIS_STATUS_SUCCESS) + state[0] = 'A'; + else + state[0] = 'a'; + + if (tapAdapterReadAndWriteReady(adapter)) + state[1] = 'T'; + else + state[1] = 't'; + + state[2] = '0' + adapter->CurrentPowerState; + + if (adapter->MediaStateAlwaysConnected) + state[3] = 'C'; + else + state[3] = 'c'; + + state[4] = '\0'; + + // BUGBUG!!! What follows, and is not yet implemented, is a real mess. + // BUGBUG!!! Tied closely to the NDIS 5 implementation. Need to map + // as much as possible to the NDIS 6 implementation. + Irp->IoStatus.Status = ntStatus = RtlStringCchPrintfExA ( + ((LPTSTR) (Irp->AssociatedIrp.SystemBuffer)), + outBufLength, + 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)adapter->TapFileOpenCount, + (int)(adapter->FramesTxDirected + adapter->FramesTxMulticast + adapter->FramesTxBroadcast), + (int)adapter->TransmitFailuresOther, +#if PACKET_TRUNCATION_CHECK + (int)adapter->m_TxTrunc, +#endif + (int)adapter->m_Rx, + (int)adapter->m_RxErr, +#if PACKET_TRUNCATION_CHECK + (int)adapter->m_RxTrunc, +#endif + (int)adapter->PendingReadIrpQueue.Count, + (int)adapter->PendingReadIrpQueue.MaxCount, + (int)IRP_QUEUE_SIZE, // Ignored in NDIS 6 driver... + + (int)adapter->SendPacketQueue.Count, + (int)adapter->SendPacketQueue.MaxCount, + (int)PACKET_QUEUE_SIZE, + + (int)0, // adapter->InjectPacketQueue.Count - Unused + (int)0, // adapter->InjectPacketQueue.MaxCount - Unused + (int)INJECT_QUEUE_SIZE + ); + + Irp->IoStatus.Information = outBufLength; + + // BUGBUG!!! Fail because this is not completely implemented. + ntStatus = STATUS_INVALID_DEVICE_REQUEST; + } + break; + +#if DBG + case TAP_WIN_IOCTL_GET_LOG_LINE: + { + if (GetDebugLine( (LPTSTR)Irp->AssociatedIrp.SystemBuffer,outBufLength)) + { + Irp->IoStatus.Status = ntStatus = STATUS_SUCCESS; + } + else + { + Irp->IoStatus.Status = ntStatus = STATUS_UNSUCCESSFUL; + } + + Irp->IoStatus.Information = outBufLength; + + break; + } +#endif + + case TAP_WIN_IOCTL_SET_MEDIA_STATUS: + { + if(inBufLength >= sizeof(ULONG)) + { + ULONG parm = ((PULONG) (Irp->AssociatedIrp.SystemBuffer))[0]; + tapSetMediaConnectStatus (adapter, (BOOLEAN) parm); + Irp->IoStatus.Information = 1; + } + else + { + NOTE_ERROR(); + Irp->IoStatus.Status = ntStatus = STATUS_INVALID_PARAMETER; + } + } + break; + + default: + + // + // The specified I/O control code is unrecognized by this driver. + // + ntStatus = STATUS_INVALID_DEVICE_REQUEST; + break; + } + +End: + + // + // Finish the I/O operation by simply completing the packet and returning + // the same status as in the packet itself. + // + Irp->IoStatus.Status = ntStatus; + + IoCompleteRequest( Irp, IO_NO_INCREMENT ); + + return ntStatus; +} + +// Flush the pending read IRP queue. +VOID +tapFlushIrpQueues( + __in PTAP_ADAPTER_CONTEXT Adapter + ) +{ + + DEBUGP (("[TAP] tapFlushIrpQueues: Flushing %d pending read IRPs\n", + Adapter->PendingReadIrpQueue.Count)); + + tapIrpCsqFlush(&Adapter->PendingReadIrpQueue); +} + +// IRP_MJ_CLEANUP +NTSTATUS +TapDeviceCleanup( + PDEVICE_OBJECT DeviceObject, + PIRP Irp + ) +/*++ + +Routine Description: + + Receipt of this request indicates that the last handle for a file + object that is associated with the target device object has been closed + (but, due to outstanding I/O requests, might not have been released). + + A driver that holds pending IRPs internally must implement a routine for + IRP_MJ_CLEANUP. When the routine is called, the driver should cancel all + the pending IRPs that belong to the file object identified by the IRP_MJ_CLEANUP + call. + + In other words, it should cancel all the IRPs that have the same file-object + pointer as the one supplied in the current I/O stack location of the IRP for the + IRP_MJ_CLEANUP call. Of course, IRPs belonging to other file objects should + not be canceled. Also, if an outstanding IRP is completed immediately, the + driver does not have to cancel it. + +Arguments: + + DeviceObject - a pointer to the object that represents the device + to be cleaned up. + + Irp - a pointer to the I/O Request Packet for this request. + +Return Value: + + NT status code + +--*/ + +{ + NDIS_STATUS status = NDIS_STATUS_SUCCESS; // Always succeed. + PIO_STACK_LOCATION irpSp; // Pointer to current stack location + PTAP_ADAPTER_CONTEXT adapter = NULL; + + PAGED_CODE(); + + DEBUGP (("[TAP] --> TapDeviceCleanup\n")); + + irpSp = IoGetCurrentIrpStackLocation(Irp); + + // + // Fetch adapter context for this device. + // -------------------------------------- + // Adapter pointer was stashed in FsContext when handle was opened. + // + adapter = (PTAP_ADAPTER_CONTEXT )(irpSp->FileObject)->FsContext; + + // Insure that adapter exists. + ASSERT(adapter); + + if(adapter == NULL ) + { + DEBUGP (("[TAP] release [%d.%d] cleanup request; adapter not found\n", + TAP_DRIVER_MAJOR_VERSION, + TAP_DRIVER_MINOR_VERSION + )); + } + + if(adapter != NULL ) + { + adapter->TapFileIsOpen = 0; // Legacy... + + // Disconnect from media. + tapSetMediaConnectStatus(adapter,FALSE); + + // Reset adapter state when cleaning up; + tapResetAdapterState(adapter); + + // BUGBUG!!! Use RemoveLock??? + + // + // Flush pending send TAP packet queue. + // + tapFlushSendPacketQueue(adapter); + + ASSERT(adapter->SendPacketQueue.Count == 0); + + // + // Flush the pending IRP queues + // + tapFlushIrpQueues(adapter); + + ASSERT(adapter->PendingReadIrpQueue.Count == 0); + } + + // Complete the IRP. + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = 0; + + IoCompleteRequest( Irp, IO_NO_INCREMENT ); + + DEBUGP (("[TAP] <-- TapDeviceCleanup; status = %8.8X\n",status)); + + return status; +} + +// IRP_MJ_CLOSE +NTSTATUS +TapDeviceClose( + PDEVICE_OBJECT DeviceObject, + PIRP Irp + ) +/*++ + +Routine Description: + + Receipt of this request indicates that the last handle of the file + object that is associated with the target device object has been closed + and released. + + All outstanding I/O requests have been completed or canceled. + +Arguments: + + DeviceObject - a pointer to the object that represents the device + to be closed. + + Irp - a pointer to the I/O Request Packet for this request. + +Return Value: + + NT status code + +--*/ + +{ + NDIS_STATUS status = NDIS_STATUS_SUCCESS; // Always succeed. + PIO_STACK_LOCATION irpSp; // Pointer to current stack location + PTAP_ADAPTER_CONTEXT adapter = NULL; + + PAGED_CODE(); + + DEBUGP (("[TAP] --> TapDeviceClose\n")); + + irpSp = IoGetCurrentIrpStackLocation(Irp); + + // + // Fetch adapter context for this device. + // -------------------------------------- + // Adapter pointer was stashed in FsContext when handle was opened. + // + adapter = (PTAP_ADAPTER_CONTEXT )(irpSp->FileObject)->FsContext; + + // Insure that adapter exists. + ASSERT(adapter); + + if(adapter == NULL ) + { + DEBUGP (("[TAP] release [%d.%d] close request; adapter not found\n", + TAP_DRIVER_MAJOR_VERSION, + TAP_DRIVER_MINOR_VERSION + )); + } + + if(adapter != NULL ) + { + if(adapter->TapFileObject == NULL) + { + // Should never happen!!! + ASSERT(FALSE); + } + else + { + ASSERT(irpSp->FileObject->FsContext == adapter); + + ASSERT(adapter->TapFileObject == irpSp->FileObject); + } + + adapter->TapFileObject = NULL; + irpSp->FileObject = NULL; + + // Remove reference added by when handle was opened. + tapAdapterContextDereference(adapter); + } + + // Complete the IRP. + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = 0; + + IoCompleteRequest( Irp, IO_NO_INCREMENT ); + + DEBUGP (("[TAP] <-- TapDeviceClose; status = %8.8X\n",status)); + + return status; +} + +NTSTATUS +tapConcatenateNdisStrings( + __inout PNDIS_STRING DestinationString, + __in_opt PNDIS_STRING SourceString1, + __in_opt PNDIS_STRING SourceString2, + __in_opt PNDIS_STRING SourceString3 + ) +{ + NTSTATUS status; + + ASSERT(SourceString1 && SourceString2 && SourceString3); + + status = RtlAppendUnicodeStringToString( + DestinationString, + SourceString1 + ); + + if(status == STATUS_SUCCESS) + { + status = RtlAppendUnicodeStringToString( + DestinationString, + SourceString2 + ); + + if(status == STATUS_SUCCESS) + { + status = RtlAppendUnicodeStringToString( + DestinationString, + SourceString3 + ); + } + } + + return status; +} + +NTSTATUS +tapMakeDeviceNames( + __in PTAP_ADAPTER_CONTEXT Adapter + ) +{ + NDIS_STATUS status; + NDIS_STRING deviceNamePrefix = NDIS_STRING_CONST("\\Device\\"); + NDIS_STRING tapNameSuffix = NDIS_STRING_CONST(".tap"); + + // Generate DeviceName from NetCfgInstanceId. + Adapter->DeviceName.Buffer = Adapter->DeviceNameBuffer; + Adapter->DeviceName.MaximumLength = sizeof(Adapter->DeviceNameBuffer); + + status = tapConcatenateNdisStrings( + &Adapter->DeviceName, + &deviceNamePrefix, + &Adapter->NetCfgInstanceId, + &tapNameSuffix + ); + + if(status == STATUS_SUCCESS) + { + NDIS_STRING linkNamePrefix = NDIS_STRING_CONST("\\DosDevices\\Global\\"); + + Adapter->LinkName.Buffer = Adapter->LinkNameBuffer; + Adapter->LinkName.MaximumLength = sizeof(Adapter->LinkNameBuffer); + + status = tapConcatenateNdisStrings( + &Adapter->LinkName, + &linkNamePrefix, + &Adapter->NetCfgInstanceId, + &tapNameSuffix + ); + } + + return status; +} + +NDIS_STATUS +CreateTapDevice( + __in PTAP_ADAPTER_CONTEXT Adapter + ) +{ + NDIS_STATUS status; + NDIS_DEVICE_OBJECT_ATTRIBUTES deviceAttribute; + PDRIVER_DISPATCH dispatchTable[IRP_MJ_MAXIMUM_FUNCTION+1]; + + DEBUGP (("[TAP] version [%d.%d] creating tap device: %wZ\n", + TAP_DRIVER_MAJOR_VERSION, + TAP_DRIVER_MINOR_VERSION, + &Adapter->NetCfgInstanceId)); + + // Generate DeviceName and LinkName from NetCfgInstanceId. + status = tapMakeDeviceNames(Adapter); + + if (NT_SUCCESS(status)) + { + DEBUGP (("[TAP] DeviceName: %wZ\n",&Adapter->DeviceName)); + DEBUGP (("[TAP] LinkName: %wZ\n",&Adapter->LinkName)); + + // Initialize dispatch table. + NdisZeroMemory(dispatchTable, (IRP_MJ_MAXIMUM_FUNCTION+1) * sizeof(PDRIVER_DISPATCH)); + + dispatchTable[IRP_MJ_CREATE] = TapDeviceCreate; + dispatchTable[IRP_MJ_CLEANUP] = TapDeviceCleanup; + dispatchTable[IRP_MJ_CLOSE] = TapDeviceClose; + dispatchTable[IRP_MJ_READ] = TapDeviceRead; + dispatchTable[IRP_MJ_WRITE] = TapDeviceWrite; + dispatchTable[IRP_MJ_DEVICE_CONTROL] = TapDeviceControl; + + // + // Create a device object and register dispatch handlers + // + NdisZeroMemory(&deviceAttribute, sizeof(NDIS_DEVICE_OBJECT_ATTRIBUTES)); + + deviceAttribute.Header.Type = NDIS_OBJECT_TYPE_DEVICE_OBJECT_ATTRIBUTES; + deviceAttribute.Header.Revision = NDIS_DEVICE_OBJECT_ATTRIBUTES_REVISION_1; + deviceAttribute.Header.Size = sizeof(NDIS_DEVICE_OBJECT_ATTRIBUTES); + + deviceAttribute.DeviceName = &Adapter->DeviceName; + deviceAttribute.SymbolicName = &Adapter->LinkName; + deviceAttribute.MajorFunctions = &dispatchTable[0]; + //deviceAttribute.ExtensionSize = sizeof(FILTER_DEVICE_EXTENSION); + +#if ENABLE_NONADMIN + if(Adapter->AllowNonAdmin) + { + // + // SDDL_DEVOBJ_SYS_ALL_WORLD_RWX_RES_RWX allows the kernel and system complete + // control over the device. By default the admin can access the entire device, + // but cannot change the ACL (the admin must take control of the device first) + // + // Everyone else, including "restricted" or "untrusted" code can read or write + // to the device. Traversal beneath the device is also granted (removing it + // would only effect storage devices, except if the "bypass-traversal" + // privilege was revoked). + // + deviceAttribute.DefaultSDDLString = &SDDL_DEVOBJ_SYS_ALL_ADM_RWX_WORLD_RWX_RES_RWX; + } +#endif + + status = NdisRegisterDeviceEx( + Adapter->MiniportAdapterHandle, + &deviceAttribute, + &Adapter->DeviceObject, + &Adapter->DeviceHandle + ); + } + + ASSERT(NT_SUCCESS(status)); + + if (NT_SUCCESS(status)) + { + // Set TAP device flags. + (Adapter->DeviceObject)->Flags &= ~DO_BUFFERED_IO; + (Adapter->DeviceObject)->Flags |= DO_DIRECT_IO;; + + //======================== + // Finalize initialization + //======================== + + Adapter->TapDeviceCreated = TRUE; + + DEBUGP (("[%wZ] successfully created TAP device [%wZ]\n", + &Adapter->NetCfgInstanceId, + &Adapter->DeviceName + )); + } + + DEBUGP (("[TAP] <-- CreateTapDevice; status = %8.8X\n",status)); + + return status; +} + +// +// DestroyTapDevice is called from AdapterHalt and NDIS miniport +// is in Halted state. Prior to entering the Halted state the +// miniport would have passed through the Pausing and Paused +// states. These miniport states have responsibility for waiting +// until NDIS network operations have completed. +// +VOID +DestroyTapDevice( + __in PTAP_ADAPTER_CONTEXT Adapter + ) +{ + DEBUGP (("[TAP] --> DestroyTapDevice; Adapter: %wZ\n", + &Adapter->NetCfgInstanceId)); + + // + // Let clients know we are shutting down + // + Adapter->TapDeviceCreated = FALSE; + + // + // Flush pending send TAP packet queue. + // + tapFlushSendPacketQueue(Adapter); + + ASSERT(Adapter->SendPacketQueue.Count == 0); + + // + // Flush IRP queues. Wait for pending I/O. Etc. + // -------------------------------------------- + // Exhaust IRP and packet queues. Any pending IRPs will + // be cancelled, causing user-space to get this error + // on overlapped reads: + // + // ERROR_OPERATION_ABORTED, code=995 + // + // "The I/O operation has been aborted because of either a + // thread exit or an application request." + // + // It's important that user-space close the device handle + // when this code is returned, so that when we finally + // do a NdisMDeregisterDeviceEx, the device reference count + // is 0. Otherwise the driver will not unload even if the + // the last adapter has been halted. + // + // The act of flushing the queues at this point should result in the user-mode + // application closing the adapter's device handle. Closing the handle will + // result in the TapDeviceCleanup call being made, followed by the a call to + // the TapDeviceClose callback. + // + tapFlushIrpQueues(Adapter); + + ASSERT(Adapter->PendingReadIrpQueue.Count == 0); + + // + // Deregister the Win32 device. + // ---------------------------- + // When a driver calls NdisDeregisterDeviceEx, the I/O manager deletes the + // target device object if there are no outstanding references to it. However, + // if any outstanding references remain, the I/O manager marks the device + // object as "delete pending" and deletes the device object when the references + // are finally released. + // + if(Adapter->DeviceHandle) + { + DEBUGP (("[TAP] Calling NdisDeregisterDeviceEx\n")); + NdisDeregisterDeviceEx(Adapter->DeviceHandle); + } + + Adapter->DeviceHandle = NULL; + + DEBUGP (("[TAP] <-- DestroyTapDevice\n")); +} + diff --git a/windows/TapDriver6/device.h b/windows/TapDriver6/device.h new file mode 100644 index 00000000..93dae0d9 --- /dev/null +++ b/windows/TapDriver6/device.h @@ -0,0 +1,50 @@ +/* + * 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-2014 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_DEVICE_H_ +#define __TAP_DEVICE_H_ + +//====================================================================== +// TAP Prototypes for standard Win32 device I/O entry points +//====================================================================== + +__drv_dispatchType(IRP_MJ_CREATE) +DRIVER_DISPATCH TapDeviceCreate; + +__drv_dispatchType(IRP_MJ_READ) +DRIVER_DISPATCH TapDeviceRead; + +__drv_dispatchType(IRP_MJ_WRITE) +DRIVER_DISPATCH TapDeviceWrite; + +__drv_dispatchType(IRP_MJ_DEVICE_CONTROL) +DRIVER_DISPATCH TapDeviceControl; + +__drv_dispatchType(IRP_MJ_CLEANUP) +DRIVER_DISPATCH TapDeviceCleanup; + +__drv_dispatchType(IRP_MJ_CLOSE) +DRIVER_DISPATCH TapDeviceClose; + +#endif // __TAP_DEVICE_H_
\ No newline at end of file diff --git a/windows/TapDriver6/dhcp.c b/windows/TapDriver6/dhcp.c new file mode 100644 index 00000000..30b22f4f --- /dev/null +++ b/windows/TapDriver6/dhcp.c @@ -0,0 +1,710 @@ +/* + * 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-2014 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 "tap.h" + +//========================= +// Code to set DHCP options +//========================= + +VOID +SetDHCPOpt( + __in DHCPMsg *m, + __in void *data, + __in 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( + __in DHCPMsg *msg, + __in int type + ) +{ + DHCPOPT0 opt; + opt.type = (UCHAR) type; + SetDHCPOpt (msg, &opt, sizeof (opt)); +} + +VOID +SetDHCPOpt8( + __in DHCPMsg *msg, + __in int type, + __in ULONG data + ) +{ + DHCPOPT8 opt; + opt.type = (UCHAR) type; + opt.len = sizeof (opt.data); + opt.data = (UCHAR) data; + SetDHCPOpt (msg, &opt, sizeof (opt)); +} + +VOID +SetDHCPOpt32( + __in DHCPMsg *msg, + __in int type, + __in 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( + __in const UCHAR *buf, + __in 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 ( + __in const UCHAR *buf, + __in const int len_udp, + __in const UCHAR *src_addr, + __in 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( + __in 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( + __in const DHCP *dhcp, + __in 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 ( + __in const PTAP_ADAPTER_CONTEXT Adapter, + __in const ETH_HEADER *eth, + __in const IPHDR *ip, + __in const UDPHDR *udp, + __in const DHCP *dhcp + ) +{ + // Must be UDPv4 protocol + if (!(eth->proto == htons (NDIS_ETH_TYPE_IPV4) && ip->protocol == IPPROTO_UDP)) + { + return FALSE; + } + + // Source MAC must be our adapter + if (!MAC_EQUAL (eth->src, Adapter->CurrentAddress)) + { + return FALSE; + } + + // Dest MAC must be either broadcast or our virtual DHCP server + if (!(ETH_IS_BROADCAST(eth->dest) + || MAC_EQUAL (eth->dest, 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 ( + __in const PTAP_ADAPTER_CONTEXT Adapter, + __inout DHCPPre *p, + __in const ETH_HEADER *eth, + __in const IPHDR *ip, + __in const UDPHDR *udp, + __in const DHCP *dhcp, + __in const int optlen, + __in const int type) +{ + // Should we broadcast or direct to a specific MAC / IP address? + const BOOLEAN broadcast = (type == DHCPNAK + || ETH_IS_BROADCAST(eth->dest)); + + // + // Build ethernet header + // + ETH_COPY_NETWORK_ADDRESS (p->eth.src, Adapter->m_dhcp_server_mac); + + if (broadcast) + { + memset(p->eth.dest,0xFF,ETH_LENGTH_OF_ADDRESS); + } + else + { + ETH_COPY_NETWORK_ADDRESS (p->eth.dest, eth->src); + } + + p->eth.proto = htons (NDIS_ETH_TYPE_IPV4); + + // + // 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 = Adapter->m_dhcp_server_ip; + + if (broadcast) + { + p->ip.daddr = ~0; + } + else + { + p->ip.daddr = Adapter->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 = Adapter->m_dhcp_addr; + } + + p->dhcp.siaddr = Adapter->m_dhcp_server_ip; + p->dhcp.giaddr = 0; + ETH_COPY_NETWORK_ADDRESS (p->dhcp.chaddr, eth->src); + p->dhcp.magic = htonl (0x63825363); +} + +//============================= +// Build specific DHCP messages +//============================= + +VOID +SendDHCPMsg( + __in PTAP_ADAPTER_CONTEXT Adapter, + __in const int type, + __in const ETH_HEADER *eth, + __in const IPHDR *ip, + __in const UDPHDR *udp, + __in 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, Adapter->m_dhcp_server_ip); + + if (type == DHCPOFFER || type == DHCPACK) + { + // Lease Time + SetDHCPOpt32 (pkt, DHCP_LEASE_TIME, htonl (Adapter->m_dhcp_lease_time)); + + // Netmask + SetDHCPOpt32 (pkt, DHCP_NETMASK, Adapter->m_dhcp_netmask); + + // Other user-defined options + SetDHCPOpt ( + pkt, + Adapter->m_dhcp_user_supplied_options_buffer, + Adapter->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 ( + Adapter, + &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 + IndicateReceivePacket( + Adapter, + 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( + __in PTAP_ADAPTER_CONTEXT Adapter, + __in const ETH_HEADER *eth, + __in const IPHDR *ip, + __in const UDPHDR *udp, + __in const DHCP *dhcp, + __in 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 (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 != Adapter->m_dhcp_addr) + || !Adapter->m_dhcp_received_discover + || Adapter->m_dhcp_bad_requests >= BAD_DHCPREQUEST_NAK_THRESHOLD)) + { + SendDHCPMsg( + Adapter, + DHCPNAK, + eth, ip, udp, dhcp + ); + } + else + { + SendDHCPMsg( + Adapter, + (msg_type == DHCPDISCOVER ? DHCPOFFER : DHCPACK), + eth, ip, udp, dhcp + ); + } + + // Remember if we received a DHCPDISCOVER + if (msg_type == DHCPDISCOVER) + { + Adapter->m_dhcp_received_discover = TRUE; + } + + // Is this a bad DHCPREQUEST? + if (msg_type == DHCPREQUEST && dhcp->ciaddr && dhcp->ciaddr != Adapter->m_dhcp_addr) + { + ++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 */ diff --git a/windows/TapDriver6/dhcp.h b/windows/TapDriver6/dhcp.h new file mode 100644 index 00000000..b594a5e9 --- /dev/null +++ b/windows/TapDriver6/dhcp.h @@ -0,0 +1,165 @@ +/* + * 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-2014 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 once + +#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 diff --git a/windows/TapDriver6/endian.h b/windows/TapDriver6/endian.h new file mode 100644 index 00000000..b7d34499 --- /dev/null +++ b/windows/TapDriver6/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-2014 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 diff --git a/windows/TapDriver6/error.c b/windows/TapDriver6/error.c new file mode 100644 index 00000000..1fad1d3a --- /dev/null +++ b/windows/TapDriver6/error.c @@ -0,0 +1,398 @@ +/* + * 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-2014 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 "tap.h" + +//----------------- +// 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 ( + __in char *buf, + __in 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 +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]; +}; + +#ifdef ALLOW_PACKET_DUMP + +VOID +DumpPacket2( + __in const char *prefix, + __in const ETH_HEADER *eth, + __in const unsigned char *data, + __in 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( + __in const char *prefix, + __in const unsigned char *data, + __in 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 // ALLOW_PACKET_DUMP + +#endif diff --git a/windows/TapDriver6/error.h b/windows/TapDriver6/error.h new file mode 100644 index 00000000..2ba39cc1 --- /dev/null +++ b/windows/TapDriver6/error.h @@ -0,0 +1,114 @@ +/* + * 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-2014 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 +//----------------- + +extern const char *g_LastErrorFilename; +extern int g_LastErrorLineNumber; + +// Debug info output +#define ALSO_DBGPRINT 1 +#define DEBUGP_AT_DISPATCH 1 + +// Uncomment line below to allow packet dumps +//#define ALLOW_PACKET_DUMP 1 + +#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 PrMac (const MACADDR mac); + +VOID PrIP (IPADDR ip_addr); + +#ifdef ALLOW_PACKET_DUMP + +VOID +DumpPacket( + __in const char *prefix, + __in const unsigned char *data, + __in unsigned int len + ); + +DumpPacket2( + __in const char *prefix, + __in const ETH_HEADER *eth, + __in const unsigned char *data, + __in unsigned int len + ); + +#else +#define DUMP_PACKET(prefix, data, len) +#define DUMP_PACKET2(prefix, eth, data, len) +#endif + +#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 + +#ifdef ALLOW_PACKET_DUMP + +#define DUMP_PACKET(prefix, data, len) \ + DumpPacket (prefix, data, len) + +#define DUMP_PACKET2(prefix, eth, data, len) \ + DumpPacket2 (prefix, eth, data, len) + +#endif + +BOOLEAN +GetDebugLine ( + __in char *buf, + __in const int len + ); + +#else + +#define DEBUGP(fmt) +#define DUMP_PACKET(prefix, data, len) +#define DUMP_PACKET2(prefix, eth, data, len) + +#endif diff --git a/windows/TapDriver6/hexdump.h b/windows/TapDriver6/hexdump.h new file mode 100644 index 00000000..d6275c18 --- /dev/null +++ b/windows/TapDriver6/hexdump.h @@ -0,0 +1,63 @@ +/* + * 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-2014 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 HEXDUMP_DEFINED +#define HEXDUMP_DEFINED + +#ifdef __cplusplus +extern "C" { +#endif + +//===================================================================================== +// Debug Routines +//===================================================================================== + +#ifndef NDIS_MINIPORT_DRIVER +# include <stdio.h> +# include <ctype.h> +# include <windows.h> +# include <winnt.h> +# include <memory.h> + +# ifndef DEBUGP +# define DEBUGP(fmt) { DbgMessage fmt; } +# endif + + extern VOID (*DbgMessage)(char *p_Format, ...); + + VOID DisplayDebugString (char *p_Format, ...); +#endif + +//=================================================================================== +// Reporting / Debugging +//=================================================================================== +#define IfPrint(c) (c >= 32 && c < 127 ? c : '.') + +VOID HexDump (unsigned char *p_Buffer, unsigned long p_Size); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/windows/TapDriver6/lock.h b/windows/TapDriver6/lock.h new file mode 100644 index 00000000..c80b164c --- /dev/null +++ b/windows/TapDriver6/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-2014 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; \ + } \ + } \ +} diff --git a/windows/TapDriver6/macinfo.c b/windows/TapDriver6/macinfo.c new file mode 100644 index 00000000..dfd0a075 --- /dev/null +++ b/windows/TapDriver6/macinfo.c @@ -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-2014 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 "tap.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; + + ASSERT (src); + ASSERT (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( + __in MACADDR mac, + __in 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( + __in MACADDR dest, + __in const MACADDR src, + __in const int delta + ) +{ + ETH_COPY_NETWORK_ADDRESS (dest, src); + dest[2] += (UCHAR) delta; +} diff --git a/windows/TapDriver6/macinfo.h b/windows/TapDriver6/macinfo.h new file mode 100644 index 00000000..dd88b6f8 --- /dev/null +++ b/windows/TapDriver6/macinfo.h @@ -0,0 +1,53 @@ +/* + * 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-2014 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 CLEAR_MAC(dest) NdisZeroMemory ((dest), sizeof (MACADDR)) +#define MAC_EQUAL(a,b) (memcmp ((a), (b), sizeof (MACADDR)) == 0) + +BOOLEAN +ParseMAC (MACADDR dest, const char *src); + +VOID +GenerateRandomMac( + __in MACADDR mac, + __in const unsigned char *adapter_name + ); + +VOID +GenerateRelatedMAC( + __in MACADDR dest, + __in const MACADDR src, + __in const int delta + ); + +#endif diff --git a/windows/TapDriver6/mem.c b/windows/TapDriver6/mem.c new file mode 100644 index 00000000..ae2e3d40 --- /dev/null +++ b/windows/TapDriver6/mem.c @@ -0,0 +1,401 @@ +/* + * 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-2014 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 +//------------------ + +#include "tap.h" + +PVOID +MemAlloc( + __in ULONG p_Size, + __in 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( + __in PVOID p_Addr, + __in 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) + { + } + } +} + +//====================================================================== +// TAP Packet Queue Support +//====================================================================== + +VOID +tapPacketQueueInsertTail( + __in PTAP_PACKET_QUEUE TapPacketQueue, + __in PTAP_PACKET TapPacket + ) +{ + KIRQL irql; + + KeAcquireSpinLock(&TapPacketQueue->QueueLock,&irql); + + InsertTailList(&TapPacketQueue->Queue,&TapPacket->QueueLink); + + // BUGBUG!!! Enforce PACKET_QUEUE_SIZE queue count limit??? + // For NDIS 6 there is no per-packet status, so this will need to + // be handled on per-NBL basis in AdapterSendNetBufferLists... + + // Update counts + ++TapPacketQueue->Count; + + if(TapPacketQueue->Count > TapPacketQueue->MaxCount) + { + TapPacketQueue->MaxCount = TapPacketQueue->Count; + + DEBUGP (("[TAP] tapPacketQueueInsertTail: New MAX queued packet count = %d\n", + TapPacketQueue->MaxCount)); + } + + KeReleaseSpinLock(&TapPacketQueue->QueueLock,irql); +} + +// Call with QueueLock held +PTAP_PACKET +tapPacketRemoveHeadLocked( + __in PTAP_PACKET_QUEUE TapPacketQueue + ) +{ + PTAP_PACKET tapPacket = NULL; + PLIST_ENTRY listEntry; + + listEntry = RemoveHeadList(&TapPacketQueue->Queue); + + if(listEntry != &TapPacketQueue->Queue) + { + tapPacket = CONTAINING_RECORD(listEntry, TAP_PACKET, QueueLink); + + // Update counts + --TapPacketQueue->Count; + } + + return tapPacket; +} + +PTAP_PACKET +tapPacketRemoveHead( + __in PTAP_PACKET_QUEUE TapPacketQueue + ) +{ + PTAP_PACKET tapPacket = NULL; + KIRQL irql; + + KeAcquireSpinLock(&TapPacketQueue->QueueLock,&irql); + + tapPacket = tapPacketRemoveHeadLocked(TapPacketQueue); + + KeReleaseSpinLock(&TapPacketQueue->QueueLock,irql); + + return tapPacket; +} + +VOID +tapPacketQueueInitialize( + __in PTAP_PACKET_QUEUE TapPacketQueue + ) +{ + KeInitializeSpinLock(&TapPacketQueue->QueueLock); + + NdisInitializeListHead(&TapPacketQueue->Queue); +} + +//====================================================================== +// TAP Cancel-Safe Queue Support +//====================================================================== + +VOID +tapIrpCsqInsert ( + __in struct _IO_CSQ *Csq, + __in PIRP Irp + ) +{ + PTAP_IRP_CSQ tapIrpCsq; + + tapIrpCsq = (PTAP_IRP_CSQ )Csq; + + InsertTailList( + &tapIrpCsq->Queue, + &Irp->Tail.Overlay.ListEntry + ); + + // Update counts + ++tapIrpCsq->Count; + + if(tapIrpCsq->Count > tapIrpCsq->MaxCount) + { + tapIrpCsq->MaxCount = tapIrpCsq->Count; + + DEBUGP (("[TAP] tapIrpCsqInsert: New MAX queued IRP count = %d\n", + tapIrpCsq->MaxCount)); + } +} + +VOID +tapIrpCsqRemoveIrp( + __in PIO_CSQ Csq, + __in PIRP Irp + ) +{ + PTAP_IRP_CSQ tapIrpCsq; + + tapIrpCsq = (PTAP_IRP_CSQ )Csq; + + // Update counts + --tapIrpCsq->Count; + + RemoveEntryList(&Irp->Tail.Overlay.ListEntry); +} + + +PIRP +tapIrpCsqPeekNextIrp( + __in PIO_CSQ Csq, + __in PIRP Irp, + __in PVOID PeekContext + ) +{ + PTAP_IRP_CSQ tapIrpCsq; + PIRP nextIrp = NULL; + PLIST_ENTRY nextEntry; + PLIST_ENTRY listHead; + PIO_STACK_LOCATION irpStack; + + tapIrpCsq = (PTAP_IRP_CSQ )Csq; + + listHead = &tapIrpCsq->Queue; + + // + // If the IRP is NULL, we will start peeking from the listhead, else + // we will start from that IRP onwards. This is done under the + // assumption that new IRPs are always inserted at the tail. + // + + if (Irp == NULL) + { + nextEntry = listHead->Flink; + } + else + { + nextEntry = Irp->Tail.Overlay.ListEntry.Flink; + } + + while(nextEntry != listHead) + { + nextIrp = CONTAINING_RECORD(nextEntry, IRP, Tail.Overlay.ListEntry); + + irpStack = IoGetCurrentIrpStackLocation(nextIrp); + + // + // If context is present, continue until you find a matching one. + // Else you break out as you got next one. + // + if (PeekContext) + { + if (irpStack->FileObject == (PFILE_OBJECT) PeekContext) + { + break; + } + } + else + { + break; + } + + nextIrp = NULL; + nextEntry = nextEntry->Flink; + } + + return nextIrp; +} + +// +// tapIrpCsqAcquireQueueLock modifies the execution level of the current processor. +// +// KeAcquireSpinLock raises the execution level to Dispatch Level and stores +// the current execution level in the Irql parameter to be restored at a later +// time. KeAcqurieSpinLock also requires us to be running at no higher than +// Dispatch level when it is called. +// +// The annotations reflect these changes and requirments. +// + +__drv_raisesIRQL(DISPATCH_LEVEL) +__drv_maxIRQL(DISPATCH_LEVEL) +VOID +tapIrpCsqAcquireQueueLock( + __in PIO_CSQ Csq, + __out PKIRQL Irql + ) +{ + PTAP_IRP_CSQ tapIrpCsq; + + tapIrpCsq = (PTAP_IRP_CSQ )Csq; + + // + // Suppressing because the address below csq is valid since it's + // part of TAP_ADAPTER_CONTEXT structure. + // +#pragma prefast(suppress: __WARNING_BUFFER_UNDERFLOW, "Underflow using expression 'adapter->PendingReadCsqQueueLock'") + KeAcquireSpinLock(&tapIrpCsq->QueueLock, Irql); +} + +// +// tapIrpCsqReleaseQueueLock modifies the execution level of the current processor. +// +// KeReleaseSpinLock assumes we already hold the spin lock and are therefore +// running at Dispatch level. It will use the Irql parameter saved in a +// previous call to KeAcquireSpinLock to return the thread back to it's original +// execution level. +// +// The annotations reflect these changes and requirments. +// + +__drv_requiresIRQL(DISPATCH_LEVEL) +VOID +tapIrpCsqReleaseQueueLock( + __in PIO_CSQ Csq, + __in KIRQL Irql + ) +{ + PTAP_IRP_CSQ tapIrpCsq; + + tapIrpCsq = (PTAP_IRP_CSQ )Csq; + + // + // Suppressing because the address below csq is valid since it's + // part of TAP_ADAPTER_CONTEXT structure. + // +#pragma prefast(suppress: __WARNING_BUFFER_UNDERFLOW, "Underflow using expression 'adapter->PendingReadCsqQueueLock'") + KeReleaseSpinLock(&tapIrpCsq->QueueLock, Irql); +} + +VOID +tapIrpCsqCompleteCanceledIrp( + __in PIO_CSQ pCsq, + __in PIRP Irp + ) +{ + UNREFERENCED_PARAMETER(pCsq); + + Irp->IoStatus.Status = STATUS_CANCELLED; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NO_INCREMENT); +} + +VOID +tapIrpCsqInitialize( + __in PTAP_IRP_CSQ TapIrpCsq + ) +{ + KeInitializeSpinLock(&TapIrpCsq->QueueLock); + + NdisInitializeListHead(&TapIrpCsq->Queue); + + IoCsqInitialize( + &TapIrpCsq->CsqQueue, + tapIrpCsqInsert, + tapIrpCsqRemoveIrp, + tapIrpCsqPeekNextIrp, + tapIrpCsqAcquireQueueLock, + tapIrpCsqReleaseQueueLock, + tapIrpCsqCompleteCanceledIrp + ); +} + +VOID +tapIrpCsqFlush( + __in PTAP_IRP_CSQ TapIrpCsq + ) +{ + PIRP pendingIrp; + + // + // Flush the pending read IRP queue. + // + pendingIrp = IoCsqRemoveNextIrp( + &TapIrpCsq->CsqQueue, + NULL + ); + + while(pendingIrp) + { + // Cancel the IRP + pendingIrp->IoStatus.Information = 0; + pendingIrp->IoStatus.Status = STATUS_CANCELLED; + IoCompleteRequest(pendingIrp, IO_NO_INCREMENT); + + pendingIrp = IoCsqRemoveNextIrp( + &TapIrpCsq->CsqQueue, + NULL + ); + } + + ASSERT(IsListEmpty(&TapIrpCsq->Queue)); +} diff --git a/windows/TapDriver6/mem.h b/windows/TapDriver6/mem.h new file mode 100644 index 00000000..a8359e19 --- /dev/null +++ b/windows/TapDriver6/mem.h @@ -0,0 +1,113 @@ +/* + * 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-2014 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( + __in ULONG p_Size, + __in BOOLEAN zero + ); + +VOID +MemFree( + __in PVOID p_Addr, + __in ULONG p_Size + ); + +//====================================================================== +// TAP Packet Queue +//====================================================================== + +typedef +struct _TAP_PACKET +{ + LIST_ENTRY QueueLink; + +# define TAP_PACKET_SIZE(data_size) (sizeof (TAP_PACKET) + (data_size)) +# define TP_TUN 0x80000000 +# define TP_SIZE_MASK (~TP_TUN) + ULONG m_SizeFlags; + + // m_Data must be the last struct member + UCHAR m_Data []; +} TAP_PACKET, *PTAP_PACKET; + +#define TAP_PACKET_TAG '6PAT' // "TAP6" + +typedef struct _TAP_PACKET_QUEUE +{ + KSPIN_LOCK QueueLock; + LIST_ENTRY Queue; + ULONG Count; // Count of currently queued items + ULONG MaxCount; +} TAP_PACKET_QUEUE, *PTAP_PACKET_QUEUE; + +VOID +tapPacketQueueInsertTail( + __in PTAP_PACKET_QUEUE TapPacketQueue, + __in PTAP_PACKET TapPacket + ); + + +// Call with QueueLock held +PTAP_PACKET +tapPacketRemoveHeadLocked( + __in PTAP_PACKET_QUEUE TapPacketQueue + ); + +PTAP_PACKET +tapPacketRemoveHead( + __in PTAP_PACKET_QUEUE TapPacketQueue + ); + +VOID +tapPacketQueueInitialize( + __in PTAP_PACKET_QUEUE TapPacketQueue + ); + +//---------------------- +// Cancel-Safe IRP Queue +//---------------------- + +typedef struct _TAP_IRP_CSQ +{ + IO_CSQ CsqQueue; + KSPIN_LOCK QueueLock; + LIST_ENTRY Queue; + ULONG Count; // Count of currently queued items + ULONG MaxCount; +} TAP_IRP_CSQ, *PTAP_IRP_CSQ; + +VOID +tapIrpCsqInitialize( + __in PTAP_IRP_CSQ TapIrpCsq + ); + +VOID +tapIrpCsqFlush( + __in PTAP_IRP_CSQ TapIrpCsq + ); diff --git a/windows/TapDriver6/oidrequest.c b/windows/TapDriver6/oidrequest.c new file mode 100644 index 00000000..a6882f89 --- /dev/null +++ b/windows/TapDriver6/oidrequest.c @@ -0,0 +1,1028 @@ +/* + * 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-2014 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 files. +// + +#include "tap.h" + +#ifndef DBG + +#define DBG_PRINT_OID_NAME + +#else + +VOID +DBG_PRINT_OID_NAME( + __in NDIS_OID Oid + ) +{ + PCHAR oidName = NULL; + + switch (Oid){ + + #undef MAKECASE + #define MAKECASE(oidx) case oidx: oidName = #oidx "\n"; break; + + /* Operational OIDs */ + MAKECASE(OID_GEN_SUPPORTED_LIST) + MAKECASE(OID_GEN_HARDWARE_STATUS) + MAKECASE(OID_GEN_MEDIA_SUPPORTED) + MAKECASE(OID_GEN_MEDIA_IN_USE) + MAKECASE(OID_GEN_MAXIMUM_LOOKAHEAD) + MAKECASE(OID_GEN_MAXIMUM_FRAME_SIZE) + MAKECASE(OID_GEN_LINK_SPEED) + MAKECASE(OID_GEN_TRANSMIT_BUFFER_SPACE) + MAKECASE(OID_GEN_RECEIVE_BUFFER_SPACE) + MAKECASE(OID_GEN_TRANSMIT_BLOCK_SIZE) + MAKECASE(OID_GEN_RECEIVE_BLOCK_SIZE) + MAKECASE(OID_GEN_VENDOR_ID) + MAKECASE(OID_GEN_VENDOR_DESCRIPTION) + MAKECASE(OID_GEN_VENDOR_DRIVER_VERSION) + MAKECASE(OID_GEN_CURRENT_PACKET_FILTER) + MAKECASE(OID_GEN_CURRENT_LOOKAHEAD) + MAKECASE(OID_GEN_DRIVER_VERSION) + MAKECASE(OID_GEN_MAXIMUM_TOTAL_SIZE) + MAKECASE(OID_GEN_PROTOCOL_OPTIONS) + MAKECASE(OID_GEN_MAC_OPTIONS) + MAKECASE(OID_GEN_MEDIA_CONNECT_STATUS) + MAKECASE(OID_GEN_MAXIMUM_SEND_PACKETS) + MAKECASE(OID_GEN_SUPPORTED_GUIDS) + MAKECASE(OID_GEN_NETWORK_LAYER_ADDRESSES) + MAKECASE(OID_GEN_TRANSPORT_HEADER_OFFSET) + MAKECASE(OID_GEN_MEDIA_CAPABILITIES) + MAKECASE(OID_GEN_PHYSICAL_MEDIUM) + MAKECASE(OID_GEN_MACHINE_NAME) + MAKECASE(OID_GEN_VLAN_ID) + MAKECASE(OID_GEN_RNDIS_CONFIG_PARAMETER) + + /* Operational OIDs for NDIS 6.0 */ + MAKECASE(OID_GEN_MAX_LINK_SPEED) + MAKECASE(OID_GEN_LINK_STATE) + MAKECASE(OID_GEN_LINK_PARAMETERS) + MAKECASE(OID_GEN_MINIPORT_RESTART_ATTRIBUTES) + MAKECASE(OID_GEN_ENUMERATE_PORTS) + MAKECASE(OID_GEN_PORT_STATE) + MAKECASE(OID_GEN_PORT_AUTHENTICATION_PARAMETERS) + MAKECASE(OID_GEN_INTERRUPT_MODERATION) + MAKECASE(OID_GEN_PHYSICAL_MEDIUM_EX) + + /* Statistical OIDs */ + MAKECASE(OID_GEN_XMIT_OK) + MAKECASE(OID_GEN_RCV_OK) + MAKECASE(OID_GEN_XMIT_ERROR) + MAKECASE(OID_GEN_RCV_ERROR) + MAKECASE(OID_GEN_RCV_NO_BUFFER) + MAKECASE(OID_GEN_DIRECTED_BYTES_XMIT) + MAKECASE(OID_GEN_DIRECTED_FRAMES_XMIT) + MAKECASE(OID_GEN_MULTICAST_BYTES_XMIT) + MAKECASE(OID_GEN_MULTICAST_FRAMES_XMIT) + MAKECASE(OID_GEN_BROADCAST_BYTES_XMIT) + MAKECASE(OID_GEN_BROADCAST_FRAMES_XMIT) + MAKECASE(OID_GEN_DIRECTED_BYTES_RCV) + MAKECASE(OID_GEN_DIRECTED_FRAMES_RCV) + MAKECASE(OID_GEN_MULTICAST_BYTES_RCV) + MAKECASE(OID_GEN_MULTICAST_FRAMES_RCV) + MAKECASE(OID_GEN_BROADCAST_BYTES_RCV) + MAKECASE(OID_GEN_BROADCAST_FRAMES_RCV) + MAKECASE(OID_GEN_RCV_CRC_ERROR) + MAKECASE(OID_GEN_TRANSMIT_QUEUE_LENGTH) + + /* Statistical OIDs for NDIS 6.0 */ + MAKECASE(OID_GEN_STATISTICS) + MAKECASE(OID_GEN_BYTES_RCV) + MAKECASE(OID_GEN_BYTES_XMIT) + MAKECASE(OID_GEN_RCV_DISCARDS) + MAKECASE(OID_GEN_XMIT_DISCARDS) + + /* Misc OIDs */ + MAKECASE(OID_GEN_GET_TIME_CAPS) + MAKECASE(OID_GEN_GET_NETCARD_TIME) + MAKECASE(OID_GEN_NETCARD_LOAD) + MAKECASE(OID_GEN_DEVICE_PROFILE) + MAKECASE(OID_GEN_INIT_TIME_MS) + MAKECASE(OID_GEN_RESET_COUNTS) + MAKECASE(OID_GEN_MEDIA_SENSE_COUNTS) + + /* PnP power management operational OIDs */ + MAKECASE(OID_PNP_CAPABILITIES) + MAKECASE(OID_PNP_SET_POWER) + MAKECASE(OID_PNP_QUERY_POWER) + MAKECASE(OID_PNP_ADD_WAKE_UP_PATTERN) + MAKECASE(OID_PNP_REMOVE_WAKE_UP_PATTERN) + MAKECASE(OID_PNP_ENABLE_WAKE_UP) + MAKECASE(OID_PNP_WAKE_UP_PATTERN_LIST) + + /* PnP power management statistical OIDs */ + MAKECASE(OID_PNP_WAKE_UP_ERROR) + MAKECASE(OID_PNP_WAKE_UP_OK) + + /* Ethernet operational OIDs */ + MAKECASE(OID_802_3_PERMANENT_ADDRESS) + MAKECASE(OID_802_3_CURRENT_ADDRESS) + MAKECASE(OID_802_3_MULTICAST_LIST) + MAKECASE(OID_802_3_MAXIMUM_LIST_SIZE) + MAKECASE(OID_802_3_MAC_OPTIONS) + + /* Ethernet operational OIDs for NDIS 6.0 */ + MAKECASE(OID_802_3_ADD_MULTICAST_ADDRESS) + MAKECASE(OID_802_3_DELETE_MULTICAST_ADDRESS) + + /* Ethernet statistical OIDs */ + MAKECASE(OID_802_3_RCV_ERROR_ALIGNMENT) + MAKECASE(OID_802_3_XMIT_ONE_COLLISION) + MAKECASE(OID_802_3_XMIT_MORE_COLLISIONS) + MAKECASE(OID_802_3_XMIT_DEFERRED) + MAKECASE(OID_802_3_XMIT_MAX_COLLISIONS) + MAKECASE(OID_802_3_RCV_OVERRUN) + MAKECASE(OID_802_3_XMIT_UNDERRUN) + MAKECASE(OID_802_3_XMIT_HEARTBEAT_FAILURE) + MAKECASE(OID_802_3_XMIT_TIMES_CRS_LOST) + MAKECASE(OID_802_3_XMIT_LATE_COLLISIONS) + + /* TCP/IP OIDs */ + MAKECASE(OID_TCP_TASK_OFFLOAD) + MAKECASE(OID_TCP_TASK_IPSEC_ADD_SA) + MAKECASE(OID_TCP_TASK_IPSEC_DELETE_SA) + MAKECASE(OID_TCP_SAN_SUPPORT) + MAKECASE(OID_TCP_TASK_IPSEC_ADD_UDPESP_SA) + MAKECASE(OID_TCP_TASK_IPSEC_DELETE_UDPESP_SA) + MAKECASE(OID_TCP4_OFFLOAD_STATS) + MAKECASE(OID_TCP6_OFFLOAD_STATS) + MAKECASE(OID_IP4_OFFLOAD_STATS) + MAKECASE(OID_IP6_OFFLOAD_STATS) + + /* TCP offload OIDs for NDIS 6 */ + MAKECASE(OID_TCP_OFFLOAD_CURRENT_CONFIG) + MAKECASE(OID_TCP_OFFLOAD_PARAMETERS) + MAKECASE(OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES) + MAKECASE(OID_TCP_CONNECTION_OFFLOAD_CURRENT_CONFIG) + MAKECASE(OID_TCP_CONNECTION_OFFLOAD_HARDWARE_CAPABILITIES) + MAKECASE(OID_OFFLOAD_ENCAPSULATION) + +#if (NDIS_SUPPORT_NDIS620) + /* VMQ OIDs for NDIS 6.20 */ + MAKECASE(OID_RECEIVE_FILTER_FREE_QUEUE) + MAKECASE(OID_RECEIVE_FILTER_CLEAR_FILTER) + MAKECASE(OID_RECEIVE_FILTER_ALLOCATE_QUEUE) + MAKECASE(OID_RECEIVE_FILTER_QUEUE_ALLOCATION_COMPLETE) + MAKECASE(OID_RECEIVE_FILTER_SET_FILTER) +#endif + +#if (NDIS_SUPPORT_NDIS630) + /* NDIS QoS OIDs for NDIS 6.30 */ + MAKECASE(OID_QOS_PARAMETERS) +#endif + } + + if (oidName) + { + DEBUGP(("OID: %s", oidName)); + } + else + { + DEBUGP(("<** Unknown OID 0x%08x **>\n", Oid)); + } +} + +#endif // DBG + +//====================================================================== +// TAP NDIS 6 OID Request Callbacks +//====================================================================== + +NDIS_STATUS +tapSetMulticastList( + __in PTAP_ADAPTER_CONTEXT Adapter, + __in PNDIS_OID_REQUEST OidRequest + ) +{ + NDIS_STATUS status = NDIS_STATUS_SUCCESS; + + // + // Initialize. + // + OidRequest->DATA.SET_INFORMATION.BytesNeeded = MACADDR_SIZE; + OidRequest->DATA.SET_INFORMATION.BytesRead + = OidRequest->DATA.SET_INFORMATION.InformationBufferLength; + + + do + { + if (OidRequest->DATA.SET_INFORMATION.InformationBufferLength % MACADDR_SIZE) + { + status = NDIS_STATUS_INVALID_LENGTH; + break; + } + + if (OidRequest->DATA.SET_INFORMATION.InformationBufferLength > (TAP_MAX_MCAST_LIST * MACADDR_SIZE)) + { + status = NDIS_STATUS_MULTICAST_FULL; + OidRequest->DATA.SET_INFORMATION.BytesNeeded = TAP_MAX_MCAST_LIST * MACADDR_SIZE; + break; + } + + // BUGBUG!!! Is lock needed??? If so, use NDIS_RW_LOCK. Also apply to packet filter. + + NdisZeroMemory(Adapter->MCList, + TAP_MAX_MCAST_LIST * MACADDR_SIZE); + + NdisMoveMemory(Adapter->MCList, + OidRequest->DATA.SET_INFORMATION.InformationBuffer, + OidRequest->DATA.SET_INFORMATION.InformationBufferLength); + + Adapter->ulMCListSize = OidRequest->DATA.SET_INFORMATION.InformationBufferLength / MACADDR_SIZE; + + } while(FALSE); + return status; +} + +NDIS_STATUS +tapSetPacketFilter( + __in PTAP_ADAPTER_CONTEXT Adapter, + __in ULONG PacketFilter + ) +{ + NDIS_STATUS status = NDIS_STATUS_SUCCESS; + + // any bits not supported? + if (PacketFilter & ~(TAP_SUPPORTED_FILTERS)) + { + DEBUGP (("[TAP] Unsupported packet filter: 0x%08x\n", PacketFilter)); + status = NDIS_STATUS_NOT_SUPPORTED; + } + else + { + // Any actual filtering changes? + if (PacketFilter != Adapter->PacketFilter) + { + // + // Change the filtering modes on hardware + // + + // Save the new packet filter value + Adapter->PacketFilter = PacketFilter; + } + } + + return status; +} + +NDIS_STATUS +AdapterSetPowerD0( + __in PTAP_ADAPTER_CONTEXT Adapter + ) +/*++ +Routine Description: + + NIC power has been restored to the working power state (D0). + Prepare the NIC for normal operation: + - Restore hardware context (packet filters, multicast addresses, MAC address, etc.) + - Enable interrupts and the NIC's DMA engine. + +Arguments: + + Adapter - Pointer to adapter block + +Return Value: + + NDIS_STATUS + +--*/ +{ + NDIS_STATUS status = NDIS_STATUS_SUCCESS; + + DEBUGP (("[TAP] PowerState: Fully powered\n")); + + // Start data path... + + return status; +} + +NDIS_STATUS +AdapterSetPowerLow( + __in PTAP_ADAPTER_CONTEXT Adapter, + __in NDIS_DEVICE_POWER_STATE PowerState + ) +/*++ +Routine Description: + + The NIC is about to be transitioned to a low power state. + Prepare the NIC for the sleeping state: + - Disable interrupts and the NIC's DMA engine, cancel timers. + - Save any hardware context that the NIC cannot preserve in + a sleeping state (packet filters, multicast addresses, + the current MAC address, etc.) + A miniport driver cannot access the NIC hardware after + the NIC has been set to the D3 state by the bus driver. + + Miniport drivers NDIS v6.30 and above + Do NOT wait for NDIS to return the ownership of all + NBLs from outstanding receive indications + Retain ownership of all the receive descriptors and + packet buffers previously owned by the hardware. + +Arguments: + + Adapter - Pointer to adapter block + PowerState - New power state + +Return Value: + + NDIS_STATUS + +--*/ +{ + NDIS_STATUS status = NDIS_STATUS_SUCCESS; + + DEBUGP (("[TAP] PowerState: Low-power\n")); + + // + // Miniport drivers NDIS v6.20 and below are + // paused prior the low power transition + // + + // Check for paused state... + // Verify data path stopped... + + return status; +} + +NDIS_STATUS +tapSetInformation( + __in PTAP_ADAPTER_CONTEXT Adapter, + __in PNDIS_OID_REQUEST OidRequest + ) +/*++ + +Routine Description: + + Helper function to perform a set OID request + +Arguments: + + Adapter - + NdisSetRequest - The OID to set + +Return Value: + + NDIS_STATUS + +--*/ +{ + NDIS_STATUS status = NDIS_STATUS_SUCCESS; + + DBG_PRINT_OID_NAME(OidRequest->DATA.SET_INFORMATION.Oid); + + switch(OidRequest->DATA.SET_INFORMATION.Oid) + { + case OID_802_3_MULTICAST_LIST: + // + // Set the multicast address list on the NIC for packet reception. + // The NIC driver can set a limit on the number of multicast + // addresses bound protocol drivers can enable simultaneously. + // NDIS returns NDIS_STATUS_MULTICAST_FULL if a protocol driver + // exceeds this limit or if it specifies an invalid multicast + // address. + // + status = tapSetMulticastList(Adapter,OidRequest); + break; + + case OID_GEN_CURRENT_LOOKAHEAD: + // + // A protocol driver can set a suggested value for the number + // of bytes to be used in its binding; however, the underlying + // NIC driver is never required to limit its indications to + // the value set. + // + if (OidRequest->DATA.SET_INFORMATION.InformationBufferLength != sizeof(ULONG)) + { + OidRequest->DATA.SET_INFORMATION.BytesNeeded = sizeof(ULONG); + status = NDIS_STATUS_INVALID_LENGTH; + break; + } + + Adapter->ulLookahead = *(PULONG)OidRequest->DATA.SET_INFORMATION.InformationBuffer; + + OidRequest->DATA.SET_INFORMATION.BytesRead = sizeof(ULONG); + status = NDIS_STATUS_SUCCESS; + break; + + case OID_GEN_CURRENT_PACKET_FILTER: + // + // Program the hardware to indicate the packets + // of certain filter types. + // + if(OidRequest->DATA.SET_INFORMATION.InformationBufferLength != sizeof(ULONG)) + { + OidRequest->DATA.SET_INFORMATION.BytesNeeded = sizeof(ULONG); + status = NDIS_STATUS_INVALID_LENGTH; + break; + } + + OidRequest->DATA.SET_INFORMATION.BytesRead + = OidRequest->DATA.SET_INFORMATION.InformationBufferLength; + + status = tapSetPacketFilter( + Adapter, + *((PULONG)OidRequest->DATA.SET_INFORMATION.InformationBuffer) + ); + + break; + + case OID_PNP_SET_POWER: + { + // Sanity check. + if (OidRequest->DATA.SET_INFORMATION.InformationBufferLength + < sizeof(NDIS_DEVICE_POWER_STATE) + ) + { + status = NDIS_STATUS_INVALID_LENGTH; + } + else + { + NDIS_DEVICE_POWER_STATE PowerState; + + PowerState = *(PNDIS_DEVICE_POWER_STATE UNALIGNED)OidRequest->DATA.SET_INFORMATION.InformationBuffer; + OidRequest->DATA.SET_INFORMATION.BytesRead = sizeof(NDIS_DEVICE_POWER_STATE); + + if(PowerState < NdisDeviceStateD0 || + PowerState > NdisDeviceStateD3) + { + status = NDIS_STATUS_INVALID_DATA; + } + else + { + Adapter->CurrentPowerState = PowerState; + + if (PowerState == NdisDeviceStateD0) + { + status = AdapterSetPowerD0(Adapter); + } + else + { + status = AdapterSetPowerLow(Adapter, PowerState); + } + } + } + } + break; + +#if (NDIS_SUPPORT_NDIS61) + case OID_PNP_ADD_WAKE_UP_PATTERN: + case OID_PNP_REMOVE_WAKE_UP_PATTERN: + case OID_PNP_ENABLE_WAKE_UP: +#endif + ASSERT(!"NIC does not support wake on LAN OIDs"); + default: + // + // The entry point may by used by other requests + // + status = NDIS_STATUS_NOT_SUPPORTED; + break; + } + + return status; +} + +NDIS_STATUS +tapQueryInformation( + __in PTAP_ADAPTER_CONTEXT Adapter, + __in PNDIS_OID_REQUEST OidRequest + ) +/*++ + +Routine Description: + + Helper function to perform a query OID request + +Arguments: + + Adapter - + OidRequest - The OID request that is being queried + +Return Value: + + NDIS_STATUS + +--*/ +{ + NDIS_STATUS status = NDIS_STATUS_SUCCESS; + NDIS_MEDIUM Medium = TAP_MEDIUM_TYPE; + NDIS_HARDWARE_STATUS HardwareStatus = NdisHardwareStatusReady; + UCHAR VendorDesc[] = TAP_VENDOR_DESC; + ULONG ulInfo; + USHORT usInfo; + ULONG64 ulInfo64; + + // Default to returning the ULONG value + PVOID pInfo=NULL; + ULONG ulInfoLen = sizeof(ulInfo); + + // ATTENTION!!! Ignore OIDs to noisy to print... + if((OidRequest->DATA.QUERY_INFORMATION.Oid != OID_GEN_STATISTICS) + && (OidRequest->DATA.QUERY_INFORMATION.Oid != OID_IP4_OFFLOAD_STATS) + && (OidRequest->DATA.QUERY_INFORMATION.Oid != OID_IP6_OFFLOAD_STATS) + ) + { + DBG_PRINT_OID_NAME(OidRequest->DATA.QUERY_INFORMATION.Oid); + } + + // Dispatch based on object identifier (OID). + switch(OidRequest->DATA.QUERY_INFORMATION.Oid) + { + case OID_GEN_HARDWARE_STATUS: + // + // Specify the current hardware status of the underlying NIC as + // one of the following NDIS_HARDWARE_STATUS-type values. + // + pInfo = (PVOID) &HardwareStatus; + ulInfoLen = sizeof(NDIS_HARDWARE_STATUS); + break; + + case OID_802_3_PERMANENT_ADDRESS: + // + // Return the MAC address of the NIC burnt in the hardware. + // + pInfo = Adapter->PermanentAddress; + ulInfoLen = MACADDR_SIZE; + break; + + case OID_802_3_CURRENT_ADDRESS: + // + // Return the MAC address the NIC is currently programmed to + // use. Note that this address could be different from the + // permananent address as the user can override using + // registry. Read NdisReadNetworkAddress doc for more info. + // + pInfo = Adapter->CurrentAddress; + ulInfoLen = MACADDR_SIZE; + break; + + case OID_GEN_MEDIA_SUPPORTED: + // + // Return an array of media that are supported by the miniport. + // This miniport only supports one medium (Ethernet), so the OID + // returns identical results to OID_GEN_MEDIA_IN_USE. + // + + __fallthrough; + + case OID_GEN_MEDIA_IN_USE: + // + // Return an array of media that are currently in use by the + // miniport. This array should be a subset of the array returned + // by OID_GEN_MEDIA_SUPPORTED. + // + pInfo = &Medium; + ulInfoLen = sizeof(Medium); + break; + + case OID_GEN_MAXIMUM_TOTAL_SIZE: + // + // Specify the maximum total packet length, in bytes, the NIC + // supports including the header. A protocol driver might use + // this returned length as a gauge to determine the maximum + // size packet that a NIC driver could forward to the + // protocol driver. The miniport driver must never indicate + // up to the bound protocol driver packets received over the + // network that are longer than the packet size specified by + // OID_GEN_MAXIMUM_TOTAL_SIZE. + // + + __fallthrough; + + case OID_GEN_TRANSMIT_BLOCK_SIZE: + // + // The OID_GEN_TRANSMIT_BLOCK_SIZE OID specifies the minimum + // number of bytes that a single net packet occupies in the + // transmit buffer space of the NIC. In our case, the transmit + // block size is identical to its maximum packet size. + __fallthrough; + + case OID_GEN_RECEIVE_BLOCK_SIZE: + // + // The OID_GEN_RECEIVE_BLOCK_SIZE OID specifies the amount of + // storage, in bytes, that a single packet occupies in the receive + // buffer space of the NIC. + // + ulInfo = (ULONG) TAP_MAX_FRAME_SIZE; + pInfo = &ulInfo; + break; + + case OID_GEN_INTERRUPT_MODERATION: + { + PNDIS_INTERRUPT_MODERATION_PARAMETERS moderationParams + = (PNDIS_INTERRUPT_MODERATION_PARAMETERS)OidRequest->DATA.QUERY_INFORMATION.InformationBuffer; + + moderationParams->Header.Type = NDIS_OBJECT_TYPE_DEFAULT; + moderationParams->Header.Revision = NDIS_INTERRUPT_MODERATION_PARAMETERS_REVISION_1; + moderationParams->Header.Size = NDIS_SIZEOF_INTERRUPT_MODERATION_PARAMETERS_REVISION_1; + moderationParams->Flags = 0; + moderationParams->InterruptModeration = NdisInterruptModerationNotSupported; + ulInfoLen = NDIS_SIZEOF_INTERRUPT_MODERATION_PARAMETERS_REVISION_1; + } + break; + + case OID_PNP_QUERY_POWER: + // Simply succeed this. + break; + + case OID_GEN_VENDOR_ID: + // + // Specify a three-byte IEEE-registered vendor code, followed + // by a single byte that the vendor assigns to identify a + // particular NIC. The IEEE code uniquely identifies the vendor + // and is the same as the three bytes appearing at the beginning + // of the NIC hardware address. Vendors without an IEEE-registered + // code should use the value 0xFFFFFF. + // + + ulInfo = TAP_VENDOR_ID; + pInfo = &ulInfo; + break; + + case OID_GEN_VENDOR_DESCRIPTION: + // + // Specify a zero-terminated string describing the NIC vendor. + // + pInfo = VendorDesc; + ulInfoLen = sizeof(VendorDesc); + break; + + case OID_GEN_VENDOR_DRIVER_VERSION: + // + // Specify the vendor-assigned version number of the NIC driver. + // The low-order half of the return value specifies the minor + // version; the high-order half specifies the major version. + // + + ulInfo = TAP_DRIVER_VENDOR_VERSION; + pInfo = &ulInfo; + break; + + case OID_GEN_DRIVER_VERSION: + // + // Specify the NDIS version in use by the NIC driver. The high + // byte is the major version number; the low byte is the minor + // version number. + // + usInfo = (USHORT) (TAP_NDIS_MAJOR_VERSION<<8) + TAP_NDIS_MINOR_VERSION; + pInfo = (PVOID) &usInfo; + ulInfoLen = sizeof(USHORT); + break; + + case OID_802_3_MAXIMUM_LIST_SIZE: + // + // The maximum number of multicast addresses the NIC driver + // can manage. This list is global for all protocols bound + // to (or above) the NIC. Consequently, a protocol can receive + // NDIS_STATUS_MULTICAST_FULL from the NIC driver when + // attempting to set the multicast address list, even if + // the number of elements in the given list is less than + // the number originally returned for this query. + // + + ulInfo = TAP_MAX_MCAST_LIST; + pInfo = &ulInfo; + break; + + case OID_GEN_XMIT_ERROR: + ulInfo = (ULONG) + (Adapter->TxAbortExcessCollisions + + Adapter->TxDmaUnderrun + + Adapter->TxLostCRS + + Adapter->TxLateCollisions+ + Adapter->TransmitFailuresOther); + pInfo = &ulInfo; + break; + + case OID_GEN_RCV_ERROR: + ulInfo = (ULONG) + (Adapter->RxCrcErrors + + Adapter->RxAlignmentErrors + + Adapter->RxDmaOverrunErrors + + Adapter->RxRuntErrors); + pInfo = &ulInfo; + break; + + case OID_GEN_RCV_DISCARDS: + ulInfo = (ULONG)Adapter->RxResourceErrors; + pInfo = &ulInfo; + break; + + case OID_GEN_RCV_NO_BUFFER: + ulInfo = (ULONG)Adapter->RxResourceErrors; + pInfo = &ulInfo; + break; + + case OID_GEN_XMIT_OK: + ulInfo64 = Adapter->FramesTxBroadcast + + Adapter->FramesTxMulticast + + Adapter->FramesTxDirected; + pInfo = &ulInfo64; + if (OidRequest->DATA.QUERY_INFORMATION.InformationBufferLength >= sizeof(ULONG64) || + OidRequest->DATA.QUERY_INFORMATION.InformationBufferLength == 0) + { + ulInfoLen = sizeof(ULONG64); + } + else + { + ulInfoLen = sizeof(ULONG); + } + + // We should always report that only 8 bytes are required to keep ndistest happy + OidRequest->DATA.QUERY_INFORMATION.BytesNeeded = sizeof(ULONG64); + break; + + case OID_GEN_RCV_OK: + ulInfo64 = Adapter->FramesRxBroadcast + + Adapter->FramesRxMulticast + + Adapter->FramesRxDirected; + + pInfo = &ulInfo64; + + if (OidRequest->DATA.QUERY_INFORMATION.InformationBufferLength >= sizeof(ULONG64) || + OidRequest->DATA.QUERY_INFORMATION.InformationBufferLength == 0) + { + ulInfoLen = sizeof(ULONG64); + } + else + { + ulInfoLen = sizeof(ULONG); + } + + // We should always report that only 8 bytes are required to keep ndistest happy + OidRequest->DATA.QUERY_INFORMATION.BytesNeeded = sizeof(ULONG64); + break; + + case OID_802_3_RCV_ERROR_ALIGNMENT: + + ulInfo = Adapter->RxAlignmentErrors; + pInfo = &ulInfo; + break; + + case OID_802_3_XMIT_ONE_COLLISION: + + ulInfo = Adapter->OneRetry; + pInfo = &ulInfo; + break; + + case OID_802_3_XMIT_MORE_COLLISIONS: + + ulInfo = Adapter->MoreThanOneRetry; + pInfo = &ulInfo; + break; + + case OID_802_3_XMIT_DEFERRED: + + ulInfo = Adapter->TxOKButDeferred; + pInfo = &ulInfo; + break; + + case OID_802_3_XMIT_MAX_COLLISIONS: + + ulInfo = Adapter->TxAbortExcessCollisions; + pInfo = &ulInfo; + break; + + case OID_802_3_RCV_OVERRUN: + + ulInfo = Adapter->RxDmaOverrunErrors; + pInfo = &ulInfo; + break; + + case OID_802_3_XMIT_UNDERRUN: + + ulInfo = Adapter->TxDmaUnderrun; + pInfo = &ulInfo; + break; + + case OID_GEN_STATISTICS: + + if (OidRequest->DATA.QUERY_INFORMATION.InformationBufferLength < sizeof(NDIS_STATISTICS_INFO)) + { + status = NDIS_STATUS_INVALID_LENGTH; + OidRequest->DATA.QUERY_INFORMATION.BytesNeeded = sizeof(NDIS_STATISTICS_INFO); + break; + } + else + { + PNDIS_STATISTICS_INFO Statistics + = (PNDIS_STATISTICS_INFO)OidRequest->DATA.QUERY_INFORMATION.InformationBuffer; + + {C_ASSERT(sizeof(NDIS_STATISTICS_INFO) >= NDIS_SIZEOF_STATISTICS_INFO_REVISION_1);} + Statistics->Header.Type = NDIS_OBJECT_TYPE_DEFAULT; + Statistics->Header.Size = NDIS_SIZEOF_STATISTICS_INFO_REVISION_1; + Statistics->Header.Revision = NDIS_STATISTICS_INFO_REVISION_1; + + Statistics->SupportedStatistics = TAP_SUPPORTED_STATISTICS; + + /* Bytes in */ + Statistics->ifHCInOctets = + Adapter->BytesRxDirected + + Adapter->BytesRxMulticast + + Adapter->BytesRxBroadcast; + + Statistics->ifHCInUcastOctets = + Adapter->BytesRxDirected; + + Statistics->ifHCInMulticastOctets = + Adapter->BytesRxMulticast; + + Statistics->ifHCInBroadcastOctets = + Adapter->BytesRxBroadcast; + + /* Packets in */ + Statistics->ifHCInUcastPkts = + Adapter->FramesRxDirected; + + Statistics->ifHCInMulticastPkts = + Adapter->FramesRxMulticast; + + Statistics->ifHCInBroadcastPkts = + Adapter->FramesRxBroadcast; + + /* Errors in */ + Statistics->ifInErrors = + Adapter->RxCrcErrors + + Adapter->RxAlignmentErrors + + Adapter->RxDmaOverrunErrors + + Adapter->RxRuntErrors; + + Statistics->ifInDiscards = + Adapter->RxResourceErrors; + + + /* Bytes out */ + Statistics->ifHCOutOctets = + Adapter->BytesTxDirected + + Adapter->BytesTxMulticast + + Adapter->BytesTxBroadcast; + + Statistics->ifHCOutUcastOctets = + Adapter->BytesTxDirected; + + Statistics->ifHCOutMulticastOctets = + Adapter->BytesTxMulticast; + + Statistics->ifHCOutBroadcastOctets = + Adapter->BytesTxBroadcast; + + /* Packets out */ + Statistics->ifHCOutUcastPkts = + Adapter->FramesTxDirected; + + Statistics->ifHCOutMulticastPkts = + Adapter->FramesTxMulticast; + + Statistics->ifHCOutBroadcastPkts = + Adapter->FramesTxBroadcast; + + /* Errors out */ + Statistics->ifOutErrors = + Adapter->TxAbortExcessCollisions + + Adapter->TxDmaUnderrun + + Adapter->TxLostCRS + + Adapter->TxLateCollisions+ + Adapter->TransmitFailuresOther; + + Statistics->ifOutDiscards = 0ULL; + + ulInfoLen = NDIS_SIZEOF_STATISTICS_INFO_REVISION_1; + } + + break; + + // TODO: Inplement these query information requests. + case OID_GEN_RECEIVE_BUFFER_SPACE: + case OID_GEN_MAXIMUM_SEND_PACKETS: + case OID_GEN_TRANSMIT_QUEUE_LENGTH: + case OID_802_3_XMIT_HEARTBEAT_FAILURE: + case OID_802_3_XMIT_TIMES_CRS_LOST: + case OID_802_3_XMIT_LATE_COLLISIONS: + + default: + // + // The entry point may by used by other requests + // + status = NDIS_STATUS_NOT_SUPPORTED; + break; + } + + if (status == NDIS_STATUS_SUCCESS) + { + ASSERT(ulInfoLen > 0); + + if (ulInfoLen <= OidRequest->DATA.QUERY_INFORMATION.InformationBufferLength) + { + if(pInfo) + { + // Copy result into InformationBuffer + NdisMoveMemory( + OidRequest->DATA.QUERY_INFORMATION.InformationBuffer, + pInfo, + ulInfoLen + ); + } + + OidRequest->DATA.QUERY_INFORMATION.BytesWritten = ulInfoLen; + } + else + { + // too short + OidRequest->DATA.QUERY_INFORMATION.BytesNeeded = ulInfoLen; + status = NDIS_STATUS_BUFFER_TOO_SHORT; + } + } + + return status; +} + +NDIS_STATUS +AdapterOidRequest( + __in NDIS_HANDLE MiniportAdapterContext, + __in PNDIS_OID_REQUEST OidRequest + ) +/*++ + +Routine Description: + + Entry point called by NDIS to get or set the value of a specified OID. + +Arguments: + + MiniportAdapterContext - Our adapter handle + NdisRequest - The OID request to handle + +Return Value: + + Return code from the NdisRequest below. + +--*/ +{ + PTAP_ADAPTER_CONTEXT adapter = (PTAP_ADAPTER_CONTEXT )MiniportAdapterContext; + NDIS_STATUS status; + + // Dispatch based on request type. + switch (OidRequest->RequestType) + { + case NdisRequestSetInformation: + status = tapSetInformation(adapter,OidRequest); + break; + + case NdisRequestQueryInformation: + case NdisRequestQueryStatistics: + status = tapQueryInformation(adapter,OidRequest); + break; + + case NdisRequestMethod: // TAP doesn't need to respond to this request type. + default: + // + // The entry point may by used by other requests + // + status = NDIS_STATUS_NOT_SUPPORTED; + break; + } + + return status; +} + +VOID +AdapterCancelOidRequest( + __in NDIS_HANDLE MiniportAdapterContext, + __in PVOID RequestId + ) +{ + PTAP_ADAPTER_CONTEXT adapter = (PTAP_ADAPTER_CONTEXT )MiniportAdapterContext; + + UNREFERENCED_PARAMETER(RequestId); + + // + // This miniport sample does not pend any OID requests, so we don't have + // to worry about cancelling them. + // +} + diff --git a/windows/TapDriver6/proto.h b/windows/TapDriver6/proto.h new file mode 100644 index 00000000..cc23de6d --- /dev/null +++ b/windows/TapDriver6/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-2014 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 + +#define MACADDR_SIZE 6 +typedef unsigned char MACADDR[MACADDR_SIZE]; + +typedef unsigned long IPADDR; +typedef unsigned char IPV6ADDR[16]; + +//----------------- +// Ethernet address +//----------------- + +typedef struct { + MACADDR addr; +} ETH_ADDR; + +typedef struct { + ETH_ADDR list[TAP_MAX_MCAST_LIST]; +} MC_LIST; + + +// BUGBUG!!! Consider using ststem defines in netiodef.h!!! + +//---------------- +// Ethernet header +//---------------- +typedef struct +{ + MACADDR dest; /* destination eth addr */ + MACADDR src; /* source ether addr */ + 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() diff --git a/windows/TapDriver6/prototypes.h b/windows/TapDriver6/prototypes.h new file mode 100644 index 00000000..ad70261d --- /dev/null +++ b/windows/TapDriver6/prototypes.h @@ -0,0 +1,87 @@ +/* + * 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-2014 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 + +DRIVER_INITIALIZE DriverEntry; + +//VOID AdapterFreeResources +// ( +// TapAdapterPointer p_Adapter +// ); +// + +// +//NTSTATUS TapDeviceHook +// ( +// IN PDEVICE_OBJECT p_DeviceObject, +// IN PIRP p_IRP +// ); +// + +NDIS_STATUS +CreateTapDevice( + __in PTAP_ADAPTER_CONTEXT Adapter + ); + +VOID +DestroyTapDevice( + __in PTAP_ADAPTER_CONTEXT Adapter + ); + +// Flush the pending send TAP packet queue. +VOID +tapFlushSendPacketQueue( + __in PTAP_ADAPTER_CONTEXT Adapter + ); + +VOID +IndicateReceivePacket( + __in PTAP_ADAPTER_CONTEXT Adapter, + __in PUCHAR packetData, + __in const unsigned int packetLength + ); + +BOOLEAN +ProcessDHCP( + __in PTAP_ADAPTER_CONTEXT Adapter, + __in const ETH_HEADER *eth, + __in const IPHDR *ip, + __in const UDPHDR *udp, + __in const DHCP *dhcp, + __in int optlen + ); + +BOOLEAN +ProcessARP( + __in PTAP_ADAPTER_CONTEXT Adapter, + __in const PARP_PACKET src, + __in const IPADDR adapter_ip, + __in const IPADDR ip_network, + __in const IPADDR ip_netmask, + __in const MACADDR mac + ); + +#endif diff --git a/windows/TapDriver6/resource.aps b/windows/TapDriver6/resource.aps Binary files differnew file mode 100644 index 00000000..1552712d --- /dev/null +++ b/windows/TapDriver6/resource.aps diff --git a/windows/TapDriver6/resource.rc b/windows/TapDriver6/resource.rc new file mode 100644 index 00000000..fbe27758 --- /dev/null +++ b/windows/TapDriver6/resource.rc @@ -0,0 +1,62 @@ +#include <windows.h> +#include <ntverp.h> + +#include "config.h" + +#undef VER_PRODUCTVERSION +#undef VER_PRODUCTVERSION_STR +#undef VER_COMPANYNAME_STR +#undef VER_PRODUCTNAME_STR + +/* VER_FILETYPE, VER_FILESUBTYPE, VER_FILEDESCRIPTION_STR + * and VER_INTERNALNAME_STR must be defined before including COMMON.VER + * The strings don't need a '\0', since common.ver has them. + */ + +#define VER_FILETYPE VFT_DRV +/* possible values: VFT_UNKNOWN + VFT_APP + VFT_DLL + VFT_DRV + VFT_FONT + VFT_VXD + VFT_STATIC_LIB +*/ +#define VER_FILESUBTYPE VFT2_DRV_NETWORK +/* possible values VFT2_UNKNOWN + VFT2_DRV_PRINTER + VFT2_DRV_KEYBOARD + VFT2_DRV_LANGUAGE + VFT2_DRV_DISPLAY + VFT2_DRV_MOUSE + VFT2_DRV_NETWORK + VFT2_DRV_SYSTEM + VFT2_DRV_INSTALLABLE + VFT2_DRV_SOUND + VFT2_DRV_COMM +*/ + +#define VER_COMPANYNAME_STR "The OpenVPN Project" +#define VER_FILEDESCRIPTION_STR "TAP-Windows Virtual Network Driver (NDIS 6.0)" +#define VER_ORIGINALFILENAME_STR PRODUCT_TAP_WIN_COMPONENT_ID ".sys" +#define VER_LEGALCOPYRIGHT_YEARS "2003-2014" +#define VER_LEGALCOPYRIGHT_STR "OpenVPN Technologies, Inc." + + +#define VER_PRODUCTNAME_STR VER_FILEDESCRIPTION_STR +#define VER_PRODUCTVERSION PRODUCT_TAP_WIN_MAJOR,00,00,PRODUCT_TAP_WIN_MINOR + +#define XSTR(s) STR(s) +#define STR(s) #s + +#define VSTRING PRODUCT_VERSION " " XSTR(PRODUCT_TAP_WIN_MAJOR) "/" XSTR(PRODUCT_TAP_WIN_MINOR) + +#ifdef DBG +#define VER_PRODUCTVERSION_STR VSTRING " (DEBUG)" +#else +#define VER_PRODUCTVERSION_STR VSTRING +#endif + +#define VER_INTERNALNAME_STR VER_ORIGINALFILENAME_STR + +#include "common.ver" diff --git a/windows/TapDriver6/rxpath.c b/windows/TapDriver6/rxpath.c new file mode 100644 index 00000000..7415b5e4 --- /dev/null +++ b/windows/TapDriver6/rxpath.c @@ -0,0 +1,667 @@ +/* + * 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-2014 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 files. +// + +#include "tap.h" + +//====================================================================== +// TAP Receive Path Support +//====================================================================== + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( PAGE, TapDeviceWrite) +#endif // ALLOC_PRAGMA + +//=============================================================== +// 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. +//=============================================================== + +VOID +IndicateReceivePacket( + __in PTAP_ADAPTER_CONTEXT Adapter, + __in PUCHAR packetData, + __in const unsigned int packetLength + ) +{ + PUCHAR injectBuffer; + + // + // Handle miniport Pause + // --------------------- + // NDIS 6 miniports implement a temporary "Pause" state normally followed + // by the Restart. While in the Pause state it is forbidden for the miniport + // to indicate receive NBLs. + // + // That is: The device interface may be "up", but the NDIS miniport send/receive + // interface may be temporarily "down". + // + // BUGBUG!!! In the initial implementation of the NDIS 6 TapOas inject path + // the code below will simply ignore inject packets passed to the driver while + // the miniport is in the Paused state. + // + // The correct implementation is to go ahead and build the NBLs corresponding + // to the inject packet - but queue them. When Restart is entered the + // queued NBLs would be dequeued and indicated to the host. + // + if(tapAdapterSendAndReceiveReady(Adapter) != NDIS_STATUS_SUCCESS) + { + DEBUGP (("[%s] Lying send in IndicateReceivePacket while adapter paused\n", + MINIPORT_INSTANCE_ID (Adapter))); + + return; + } + + // Allocate flat buffer for packet data. + injectBuffer = (PUCHAR )NdisAllocateMemoryWithTagPriority( + Adapter->MiniportAdapterHandle, + packetLength, + TAP_RX_INJECT_BUFFER_TAG, + NormalPoolPriority + ); + + if( injectBuffer) + { + PMDL mdl; + + // Copy packet data to flat buffer. + NdisMoveMemory (injectBuffer, packetData, packetLength); + + // Allocate MDL for flat buffer. + mdl = NdisAllocateMdl( + Adapter->MiniportAdapterHandle, + injectBuffer, + packetLength + ); + + if( mdl ) + { + PNET_BUFFER_LIST netBufferList; + + mdl->Next = NULL; // No next MDL + + // Allocate the NBL and NB. Link MDL chain to NB. + netBufferList = NdisAllocateNetBufferAndNetBufferList( + Adapter->ReceiveNblPool, + 0, // ContextSize + 0, // ContextBackFill + mdl, // MDL chain + 0, + packetLength + ); + + if(netBufferList != NULL) + { + ULONG receiveFlags = 0; + LONG nblCount; + + NET_BUFFER_LIST_NEXT_NBL(netBufferList) = NULL; // Only one NBL + + if(KeGetCurrentIrql() == DISPATCH_LEVEL) + { + receiveFlags |= NDIS_RECEIVE_FLAGS_DISPATCH_LEVEL; + } + + // Set flag indicating that this is an injected packet + TAP_RX_NBL_FLAGS_CLEAR_ALL(netBufferList); + TAP_RX_NBL_FLAG_SET(netBufferList,TAP_RX_NBL_FLAGS_IS_INJECTED); + + netBufferList->MiniportReserved[0] = NULL; + netBufferList->MiniportReserved[1] = NULL; + + // Increment in-flight receive NBL count. + nblCount = NdisInterlockedIncrement(&Adapter->ReceiveNblInFlightCount); + ASSERT(nblCount > 0 ); + + netBufferList->SourceHandle = Adapter->MiniportAdapterHandle; + + // + // Indicate the packet + // ------------------- + // Irp->AssociatedIrp.SystemBuffer with length irpSp->Parameters.Write.Length + // contains the complete packet including Ethernet header and payload. + // + NdisMIndicateReceiveNetBufferLists( + Adapter->MiniportAdapterHandle, + netBufferList, + NDIS_DEFAULT_PORT_NUMBER, + 1, // NumberOfNetBufferLists + receiveFlags + ); + + return; + } + else + { + DEBUGP (("[%s] NdisAllocateNetBufferAndNetBufferList failed in IndicateReceivePacket\n", + MINIPORT_INSTANCE_ID (Adapter))); + NOTE_ERROR (); + + NdisFreeMdl(mdl); + NdisFreeMemory(injectBuffer,0,0); + } + } + else + { + DEBUGP (("[%s] NdisAllocateMdl failed in IndicateReceivePacket\n", + MINIPORT_INSTANCE_ID (Adapter))); + NOTE_ERROR (); + + NdisFreeMemory(injectBuffer,0,0); + } + } + else + { + DEBUGP (("[%s] NdisAllocateMemoryWithTagPriority failed in IndicateReceivePacket\n", + MINIPORT_INSTANCE_ID (Adapter))); + NOTE_ERROR (); + } +} + +VOID +tapCompleteIrpAndFreeReceiveNetBufferList( + __in PTAP_ADAPTER_CONTEXT Adapter, + __in PNET_BUFFER_LIST NetBufferList, // Only one NB here... + __in NTSTATUS IoCompletionStatus + ) +{ + PIRP irp; + ULONG frameType, netBufferCount, byteCount; + LONG nblCount; + + // Fetch NB frame type. + frameType = tapGetNetBufferFrameType(NET_BUFFER_LIST_FIRST_NB(NetBufferList)); + + // Fetch statistics for all NBs linked to the NB. + netBufferCount = tapGetNetBufferCountsFromNetBufferList( + NetBufferList, + &byteCount + ); + + // Update statistics by frame type + if(IoCompletionStatus == STATUS_SUCCESS) + { + switch(frameType) + { + case NDIS_PACKET_TYPE_DIRECTED: + Adapter->FramesRxDirected += netBufferCount; + Adapter->BytesRxDirected += byteCount; + break; + + case NDIS_PACKET_TYPE_BROADCAST: + Adapter->FramesRxBroadcast += netBufferCount; + Adapter->BytesRxBroadcast += byteCount; + break; + + case NDIS_PACKET_TYPE_MULTICAST: + Adapter->FramesRxMulticast += netBufferCount; + Adapter->BytesRxMulticast += byteCount; + break; + + default: + ASSERT(FALSE); + break; + } + } + + // + // Handle P2P Packet + // ----------------- + // Free MDL allocated for P2P Ethernet header. + // + if(TAP_RX_NBL_FLAG_TEST(NetBufferList,TAP_RX_NBL_FLAGS_IS_P2P)) + { + PNET_BUFFER netBuffer; + PMDL mdl; + + netBuffer = NET_BUFFER_LIST_FIRST_NB(NetBufferList); + mdl = NET_BUFFER_FIRST_MDL(netBuffer); + mdl->Next = NULL; + + NdisFreeMdl(mdl); + } + + // + // Handle Injected Packet + // ----------------------- + // Free MDL and data buffer allocated for injected packet. + // + if(TAP_RX_NBL_FLAG_TEST(NetBufferList,TAP_RX_NBL_FLAGS_IS_INJECTED)) + { + PNET_BUFFER netBuffer; + PMDL mdl; + PUCHAR injectBuffer; + + netBuffer = NET_BUFFER_LIST_FIRST_NB(NetBufferList); + mdl = NET_BUFFER_FIRST_MDL(netBuffer); + + injectBuffer = (PUCHAR )MmGetSystemAddressForMdlSafe(mdl,NormalPagePriority); + + if(injectBuffer) + { + NdisFreeMemory(injectBuffer,0,0); + } + + NdisFreeMdl(mdl); + } + + // + // Complete the IRP + // + irp = (PIRP )NetBufferList->MiniportReserved[0]; + + if(irp) + { + irp->IoStatus.Status = IoCompletionStatus; + IoCompleteRequest(irp, IO_NO_INCREMENT); + } + + // Decrement in-flight receive NBL count. + nblCount = NdisInterlockedDecrement(&Adapter->ReceiveNblInFlightCount); + ASSERT(nblCount >= 0 ); + if (0 == nblCount) + { + NdisSetEvent(&Adapter->ReceiveNblInFlightCountZeroEvent); + } + + // Free the NBL + NdisFreeNetBufferList(NetBufferList); +} + +VOID +AdapterReturnNetBufferLists( + __in NDIS_HANDLE MiniportAdapterContext, + __in PNET_BUFFER_LIST NetBufferLists, + __in ULONG ReturnFlags + ) +{ + PTAP_ADAPTER_CONTEXT adapter = (PTAP_ADAPTER_CONTEXT )MiniportAdapterContext; + PNET_BUFFER_LIST currentNbl, nextNbl; + + UNREFERENCED_PARAMETER(ReturnFlags); + + // + // Process each NBL individually + // + currentNbl = NetBufferLists; + while (currentNbl) + { + PNET_BUFFER_LIST nextNbl; + + nextNbl = NET_BUFFER_LIST_NEXT_NBL(currentNbl); + NET_BUFFER_LIST_NEXT_NBL(currentNbl) = NULL; + + // Complete write IRP and free NBL and associated resources. + tapCompleteIrpAndFreeReceiveNetBufferList( + adapter, + currentNbl, + STATUS_SUCCESS + ); + + // Move to next NBL + currentNbl = nextNbl; + } +} + +// IRP_MJ_WRITE callback. +NTSTATUS +TapDeviceWrite( + PDEVICE_OBJECT DeviceObject, + PIRP Irp + ) +{ + NTSTATUS ntStatus = STATUS_SUCCESS;// Assume success + PIO_STACK_LOCATION irpSp;// Pointer to current stack location + PTAP_ADAPTER_CONTEXT adapter = NULL; + ULONG dataLength; + + PAGED_CODE(); + + irpSp = IoGetCurrentIrpStackLocation( Irp ); + + // + // Fetch adapter context for this device. + // -------------------------------------- + // Adapter pointer was stashed in FsContext when handle was opened. + // + adapter = (PTAP_ADAPTER_CONTEXT )(irpSp->FileObject)->FsContext; + + ASSERT(adapter); + + // + // Sanity checks on state variables + // + if (!tapAdapterReadAndWriteReady(adapter)) + { + //DEBUGP (("[%s] Interface is down in IRP_MJ_WRITE\n", + // MINIPORT_INSTANCE_ID (adapter))); + //NOTE_ERROR(); + + Irp->IoStatus.Status = ntStatus = STATUS_CANCELLED; + Irp->IoStatus.Information = 0; + IoCompleteRequest (Irp, IO_NO_INCREMENT); + + return ntStatus; + } + + // Save IRP-accessible copy of buffer length + Irp->IoStatus.Information = irpSp->Parameters.Write.Length; + + if (Irp->MdlAddress == NULL) + { + DEBUGP (("[%s] MdlAddress is NULL for IRP_MJ_WRITE\n", + MINIPORT_INSTANCE_ID (adapter))); + + NOTE_ERROR(); + Irp->IoStatus.Status = ntStatus = STATUS_INVALID_PARAMETER; + Irp->IoStatus.Information = 0; + IoCompleteRequest (Irp, IO_NO_INCREMENT); + + return ntStatus; + } + + // + // Try to get a virtual address for the MDL. + // + NdisQueryMdl( + Irp->MdlAddress, + &Irp->AssociatedIrp.SystemBuffer, + &dataLength, + NormalPagePriority + ); + + if (Irp->AssociatedIrp.SystemBuffer == NULL) + { + DEBUGP (("[%s] Could not map address in IRP_MJ_WRITE\n", + MINIPORT_INSTANCE_ID (adapter))); + + NOTE_ERROR(); + Irp->IoStatus.Status = ntStatus = STATUS_INSUFFICIENT_RESOURCES; + Irp->IoStatus.Information = 0; + IoCompleteRequest (Irp, IO_NO_INCREMENT); + + return ntStatus; + } + + ASSERT(dataLength == irpSp->Parameters.Write.Length); + + Irp->IoStatus.Information = irpSp->Parameters.Write.Length; + + // + // Handle miniport Pause + // --------------------- + // NDIS 6 miniports implement a temporary "Pause" state normally followed + // by the Restart. While in the Pause state it is forbidden for the miniport + // to indicate receive NBLs. + // + // That is: The device interface may be "up", but the NDIS miniport send/receive + // interface may be temporarily "down". + // + // BUGBUG!!! In the initial implementation of the NDIS 6 TapOas receive path + // the code below will perform a "lying send" for write IRPs passed to the + // driver while the miniport is in the Paused state. + // + // The correct implementation is to go ahead and build the NBLs corresponding + // to the user-mode write - but queue them. When Restart is entered the + // queued NBLs would be dequeued and indicated to the host. + // + if(tapAdapterSendAndReceiveReady(adapter) == NDIS_STATUS_SUCCESS) + { + if (!adapter->m_tun && ((irpSp->Parameters.Write.Length) >= ETHERNET_HEADER_SIZE)) + { + PNET_BUFFER_LIST netBufferList; + + DUMP_PACKET ("IRP_MJ_WRITE ETH", + (unsigned char *) Irp->AssociatedIrp.SystemBuffer, + irpSp->Parameters.Write.Length); + + //===================================================== + // If IPv4 packet, check whether or not packet + // was truncated. + //===================================================== +#if PACKET_TRUNCATION_CHECK + IPv4PacketSizeVerify ( + (unsigned char *) Irp->AssociatedIrp.SystemBuffer, + irpSp->Parameters.Write.Length, + FALSE, + "RX", + &adapter->m_RxTrunc + ); +#endif + (Irp->MdlAddress)->Next = NULL; // No next MDL + + // Allocate the NBL and NB. Link MDL chain to NB. + netBufferList = NdisAllocateNetBufferAndNetBufferList( + adapter->ReceiveNblPool, + 0, // ContextSize + 0, // ContextBackFill + Irp->MdlAddress, // MDL chain + 0, + dataLength + ); + + if(netBufferList != NULL) + { + LONG nblCount; + + NET_BUFFER_LIST_NEXT_NBL(netBufferList) = NULL; // Only one NBL + + // Stash IRP pointer in NBL MiniportReserved[0] field. + netBufferList->MiniportReserved[0] = Irp; + netBufferList->MiniportReserved[1] = NULL; + + // This IRP is pended. + IoMarkIrpPending(Irp); + + // This IRP cannot be cancelled while in-flight. + IoSetCancelRoutine(Irp,NULL); + + TAP_RX_NBL_FLAGS_CLEAR_ALL(netBufferList); + + // Increment in-flight receive NBL count. + nblCount = NdisInterlockedIncrement(&adapter->ReceiveNblInFlightCount); + ASSERT(nblCount > 0 ); + + // + // Indicate the packet + // ------------------- + // Irp->AssociatedIrp.SystemBuffer with length irpSp->Parameters.Write.Length + // contains the complete packet including Ethernet header and payload. + // + NdisMIndicateReceiveNetBufferLists( + adapter->MiniportAdapterHandle, + netBufferList, + NDIS_DEFAULT_PORT_NUMBER, + 1, // NumberOfNetBufferLists + 0 // ReceiveFlags + ); + + ntStatus = STATUS_PENDING; + } + else + { + DEBUGP (("[%s] NdisMIndicateReceiveNetBufferLists failed in IRP_MJ_WRITE\n", + MINIPORT_INSTANCE_ID (adapter))); + NOTE_ERROR (); + + // Fail the IRP + Irp->IoStatus.Information = 0; + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + } + else if (adapter->m_tun && ((irpSp->Parameters.Write.Length) >= IP_HEADER_SIZE)) + { + PETH_HEADER p_UserToTap = &adapter->m_UserToTap; + PMDL mdl; // Head of MDL chain. + + // For IPv6, need to use Ethernet header with IPv6 proto + if ( IPH_GET_VER( ((IPHDR*) Irp->AssociatedIrp.SystemBuffer)->version_len) == 6 ) + { + p_UserToTap = &adapter->m_UserToTap_IPv6; + } + + DUMP_PACKET2 ("IRP_MJ_WRITE P2P", + p_UserToTap, + (unsigned char *) Irp->AssociatedIrp.SystemBuffer, + irpSp->Parameters.Write.Length); + + //===================================================== + // If IPv4 packet, check whether or not packet + // was truncated. + //===================================================== +#if PACKET_TRUNCATION_CHECK + IPv4PacketSizeVerify ( + (unsigned char *) Irp->AssociatedIrp.SystemBuffer, + irpSp->Parameters.Write.Length, + TRUE, + "RX", + &adapter->m_RxTrunc + ); +#endif + + // + // Allocate MDL for Ethernet header + // -------------------------------- + // Irp->AssociatedIrp.SystemBuffer with length irpSp->Parameters.Write.Length + // contains the only the Ethernet payload. Prepend the user-mode provided + // payload with the Ethernet header pointed to by p_UserToTap. + // + mdl = NdisAllocateMdl( + adapter->MiniportAdapterHandle, + p_UserToTap, + sizeof(ETH_HEADER) + ); + + if(mdl != NULL) + { + PNET_BUFFER_LIST netBufferList; + + // Chain user's Ethernet payload behind Ethernet header. + mdl->Next = Irp->MdlAddress; + (Irp->MdlAddress)->Next = NULL; // No next MDL + + // Allocate the NBL and NB. Link MDL chain to NB. + netBufferList = NdisAllocateNetBufferAndNetBufferList( + adapter->ReceiveNblPool, + 0, // ContextSize + 0, // ContextBackFill + mdl, // MDL chain + 0, + sizeof(ETH_HEADER) + dataLength + ); + + if(netBufferList != NULL) + { + LONG nblCount; + + NET_BUFFER_LIST_NEXT_NBL(netBufferList) = NULL; // Only one NBL + + // This IRP is pended. + IoMarkIrpPending(Irp); + + // This IRP cannot be cancelled while in-flight. + IoSetCancelRoutine(Irp,NULL); + + // Stash IRP pointer in NBL MiniportReserved[0] field. + netBufferList->MiniportReserved[0] = Irp; + netBufferList->MiniportReserved[1] = NULL; + + // Set flag indicating that this is P2P packet + TAP_RX_NBL_FLAGS_CLEAR_ALL(netBufferList); + TAP_RX_NBL_FLAG_SET(netBufferList,TAP_RX_NBL_FLAGS_IS_P2P); + + // Increment in-flight receive NBL count. + nblCount = NdisInterlockedIncrement(&adapter->ReceiveNblInFlightCount); + ASSERT(nblCount > 0 ); + + // + // Indicate the packet + // + NdisMIndicateReceiveNetBufferLists( + adapter->MiniportAdapterHandle, + netBufferList, + NDIS_DEFAULT_PORT_NUMBER, + 1, // NumberOfNetBufferLists + 0 // ReceiveFlags + ); + + ntStatus = STATUS_PENDING; + } + else + { + mdl->Next = NULL; + NdisFreeMdl(mdl); + + DEBUGP (("[%s] NdisMIndicateReceiveNetBufferLists failed in IRP_MJ_WRITE\n", + MINIPORT_INSTANCE_ID (adapter))); + NOTE_ERROR (); + + // Fail the IRP + Irp->IoStatus.Information = 0; + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + } + else + { + DEBUGP (("[%s] NdisAllocateMdl failed in IRP_MJ_WRITE\n", + MINIPORT_INSTANCE_ID (adapter))); + NOTE_ERROR (); + + // Fail the IRP + Irp->IoStatus.Information = 0; + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + } + else + { + DEBUGP (("[%s] Bad buffer size in IRP_MJ_WRITE, len=%d\n", + MINIPORT_INSTANCE_ID (adapter), + irpSp->Parameters.Write.Length)); + NOTE_ERROR (); + + Irp->IoStatus.Information = 0; // ETHERNET_HEADER_SIZE; + Irp->IoStatus.Status = ntStatus = STATUS_BUFFER_TOO_SMALL; + } + } + else + { + DEBUGP (("[%s] Lying send in IRP_MJ_WRITE while adapter paused\n", + MINIPORT_INSTANCE_ID (adapter))); + + ntStatus = STATUS_SUCCESS; + } + + if (ntStatus != STATUS_PENDING) + { + Irp->IoStatus.Status = ntStatus; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + } + + return ntStatus; +} + diff --git a/windows/TapDriver6/tap-windows.h b/windows/TapDriver6/tap-windows.h new file mode 100644 index 00000000..d546a5b1 --- /dev/null +++ b/windows/TapDriver6/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-2014 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 // __TAP_WIN_H diff --git a/windows/TapDriver6/tap.h b/windows/TapDriver6/tap.h new file mode 100644 index 00000000..61a97850 --- /dev/null +++ b/windows/TapDriver6/tap.h @@ -0,0 +1,90 @@ +/* + * 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-2014 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_H +#define __TAP_H + +#ifndef NDIS_SUPPORT_NDIS6 +#define NDIS_SUPPORT_NDIS6 1 +#define NDIS_SUPPORT_NDIS61 1 +#define NDIS_WDM1 1 +#define NDIS61_MINIPORT 1 +#endif + +#include <ntifs.h> +#include <ndis.h> +#include <ntstrsafe.h> +#include <netioapi.h> + +#include "config.h" +#include "lock.h" +#include "constants.h" +#include "proto.h" +#include "mem.h" +#include "macinfo.h" +#include "dhcp.h" +#include "error.h" +#include "endian.h" +#include "dhcp.h" +#include "types.h" +#include "adapter.h" +#include "device.h" +#include "prototypes.h" +#include "tap-windows.h" + +//======================================================== +// 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 + +// +// The driver has exactly one instance of the TAP_GLOBAL structure. NDIS keeps +// an opaque handle to this data, (it doesn't attempt to read or interpret this +// data), and it passes the handle back to the miniport in MiniportSetOptions +// and MiniportInitializeEx. +// +typedef struct _TAP_GLOBAL +{ + LIST_ENTRY AdapterList; + + NDIS_RW_LOCK Lock; + + NDIS_HANDLE NdisDriverHandle; // From NdisMRegisterMiniportDriver + +} TAP_GLOBAL, *PTAP_GLOBAL; + + +// Global data +extern TAP_GLOBAL GlobalData; + +#endif // __TAP_H diff --git a/windows/TapDriver6/tapdrvr.c b/windows/TapDriver6/tapdrvr.c new file mode 100644 index 00000000..6c537f1d --- /dev/null +++ b/windows/TapDriver6/tapdrvr.c @@ -0,0 +1,232 @@ +/* + * 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-2014 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 Windows Vista or higher +// versions of Windows. +// +// It is SMP-safe and handles 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 files. +// + +#include <string.h> + +#include "tap.h" + + +// Global data +TAP_GLOBAL GlobalData; + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( INIT, DriverEntry ) +#pragma alloc_text( PAGE, TapDriverUnload) +#endif // ALLOC_PRAGMA + +NTSTATUS +DriverEntry( + __in PDRIVER_OBJECT DriverObject, + __in PUNICODE_STRING RegistryPath + ) +/*++ +Routine Description: + + In the context of its DriverEntry function, a miniport driver associates + itself with NDIS, specifies the NDIS version that it is using, and + registers its entry points. + + +Arguments: + PVOID DriverObject - pointer to the driver object. + PVOID RegistryPath - pointer to the driver registry path. + + Return Value: + + NTSTATUS code + +--*/ +{ + NTSTATUS status; + + UNREFERENCED_PARAMETER(RegistryPath); + + DEBUGP (("[TAP] --> DriverEntry; version [%d.%d] %s %s\n", + TAP_DRIVER_MAJOR_VERSION, + TAP_DRIVER_MINOR_VERSION, + __DATE__, + __TIME__)); + + DEBUGP (("[TAP] Registry Path: '%wZ'\n", RegistryPath)); + + // + // Initialize any driver-global variables here. + // + NdisZeroMemory(&GlobalData, sizeof(GlobalData)); + + // + // The ApaterList in the GlobalData structure is used to track multiple + // adapters controlled by this miniport. + // + NdisInitializeListHead(&GlobalData.AdapterList); + + // + // This lock protects the AdapterList. + // + NdisInitializeReadWriteLock(&GlobalData.Lock); + + do + { + NDIS_MINIPORT_DRIVER_CHARACTERISTICS miniportCharacteristics; + + NdisZeroMemory(&miniportCharacteristics, sizeof(miniportCharacteristics)); + + {C_ASSERT(sizeof(miniportCharacteristics) >= NDIS_SIZEOF_MINIPORT_DRIVER_CHARACTERISTICS_REVISION_2);} + miniportCharacteristics.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_DRIVER_CHARACTERISTICS; + miniportCharacteristics.Header.Size = NDIS_SIZEOF_MINIPORT_DRIVER_CHARACTERISTICS_REVISION_2; + miniportCharacteristics.Header.Revision = NDIS_MINIPORT_DRIVER_CHARACTERISTICS_REVISION_2; + + miniportCharacteristics.MajorNdisVersion = TAP_NDIS_MAJOR_VERSION; + miniportCharacteristics.MinorNdisVersion = TAP_NDIS_MINOR_VERSION; + + miniportCharacteristics.MajorDriverVersion = TAP_DRIVER_MAJOR_VERSION; + miniportCharacteristics.MinorDriverVersion = TAP_DRIVER_MINOR_VERSION; + + miniportCharacteristics.Flags = 0; + + //miniportCharacteristics.SetOptionsHandler = MPSetOptions; // Optional + miniportCharacteristics.InitializeHandlerEx = AdapterCreate; + miniportCharacteristics.HaltHandlerEx = AdapterHalt; + miniportCharacteristics.UnloadHandler = TapDriverUnload; + miniportCharacteristics.PauseHandler = AdapterPause; + miniportCharacteristics.RestartHandler = AdapterRestart; + miniportCharacteristics.OidRequestHandler = AdapterOidRequest; + miniportCharacteristics.SendNetBufferListsHandler = AdapterSendNetBufferLists; + miniportCharacteristics.ReturnNetBufferListsHandler = AdapterReturnNetBufferLists; + miniportCharacteristics.CancelSendHandler = AdapterCancelSend; + miniportCharacteristics.CheckForHangHandlerEx = AdapterCheckForHangEx; + miniportCharacteristics.ResetHandlerEx = AdapterReset; + miniportCharacteristics.DevicePnPEventNotifyHandler = AdapterDevicePnpEventNotify; + miniportCharacteristics.ShutdownHandlerEx = AdapterShutdownEx; + miniportCharacteristics.CancelOidRequestHandler = AdapterCancelOidRequest; + + // + // Associate the miniport driver with NDIS by calling the + // NdisMRegisterMiniportDriver. This function returns an NdisDriverHandle. + // The miniport driver must retain this handle but it should never attempt + // to access or interpret this handle. + // + // By calling NdisMRegisterMiniportDriver, the driver indicates that it + // is ready for NDIS to call the driver's MiniportSetOptions and + // MiniportInitializeEx handlers. + // + DEBUGP (("[TAP] Calling NdisMRegisterMiniportDriver...\n")); + //NDIS_DECLARE_MINIPORT_DRIVER_CONTEXT(TAP_GLOBAL); + status = NdisMRegisterMiniportDriver( + DriverObject, + RegistryPath, + &GlobalData, + &miniportCharacteristics, + &GlobalData.NdisDriverHandle + ); + + if (NDIS_STATUS_SUCCESS == status) + { + DEBUGP (("[TAP] Registered miniport successfully\n")); + } + else + { + DEBUGP(("[TAP] NdisMRegisterMiniportDriver failed: %8.8X\n", status)); + TapDriverUnload(DriverObject); + status = NDIS_STATUS_FAILURE; + break; + } + } while(FALSE); + + DEBUGP (("[TAP] <-- DriverEntry; status = %8.8X\n",status)); + + return status; +} + +VOID +TapDriverUnload( + __in PDRIVER_OBJECT DriverObject + ) +/*++ + +Routine Description: + + The unload handler is called during driver unload to free up resources + acquired in DriverEntry. This handler is registered in DriverEntry through + NdisMRegisterMiniportDriver. Note that an unload handler differs from + a MiniportHalt function in that this unload handler releases resources that + are global to the driver, while the halt handler releases resource for a + particular adapter. + + Runs at IRQL = PASSIVE_LEVEL. + +Arguments: + + DriverObject Not used + +Return Value: + + None. + +--*/ +{ + PDEVICE_OBJECT deviceObject = DriverObject->DeviceObject; + UNICODE_STRING uniWin32NameString; + + DEBUGP (("[TAP] --> TapDriverUnload; version [%d.%d] %s %s unloaded\n", + TAP_DRIVER_MAJOR_VERSION, + TAP_DRIVER_MINOR_VERSION, + __DATE__, + __TIME__ + )); + + PAGED_CODE(); + + // + // Clean up all globals that were allocated in DriverEntry + // + + ASSERT(IsListEmpty(&GlobalData.AdapterList)); + + if(GlobalData.NdisDriverHandle != NULL ) + { + NdisMDeregisterMiniportDriver(GlobalData.NdisDriverHandle); + } + + DEBUGP (("[TAP] <-- TapDriverUnload\n")); +} + diff --git a/windows/TapDriver6/txpath.c b/windows/TapDriver6/txpath.c new file mode 100644 index 00000000..6e084dfc --- /dev/null +++ b/windows/TapDriver6/txpath.c @@ -0,0 +1,1168 @@ +/* + * 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-2014 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 files. +// + +#include "tap.h" + +//====================================================================== +// TAP Send Path Support +//====================================================================== + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( PAGE, TapDeviceRead) +#endif // ALLOC_PRAGMA + +// checksum code for ICMPv6 packet, taken from dhcp.c / udp_checksum +// see RFC 4443, 2.3, and RFC 2460, 8.1 +USHORT +icmpv6_checksum( + __in const UCHAR *buf, + __in const int len_icmpv6, + __in const UCHAR *saddr6, + __in 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( + __in PTAP_ADAPTER_CONTEXT Adapter, + __in 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(NDIS_ETH_TYPE_IPV6); + ETH_COPY_NETWORK_ADDRESS(na->eth.dest, Adapter->PermanentAddress); + ETH_COPY_NETWORK_ADDRESS(na->eth.src, 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; + ETH_COPY_NETWORK_ADDRESS( na->icmpv6.target_macaddr, 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)); + + IndicateReceivePacket (Adapter, (UCHAR *) na, sizeof (ICMPV6_NA_PKT)); + + MemFree (na, sizeof (ICMPV6_NA_PKT)); + + return TRUE; // all fine +} + +//=================================================== +// Generate an ARP reply message for specific kinds +// ARP queries. +//=================================================== +BOOLEAN +ProcessARP( + __in PTAP_ADAPTER_CONTEXT Adapter, + __in const PARP_PACKET src, + __in const IPADDR adapter_ip, + __in const IPADDR ip_network, + __in const IPADDR ip_netmask, + __in const MACADDR mac + ) +{ + //----------------------------------------------- + // Is this the kind of packet we are looking for? + //----------------------------------------------- + if (src->m_Proto == htons (NDIS_ETH_TYPE_ARP) + && MAC_EQUAL (src->m_MAC_Source, Adapter->PermanentAddress) + && MAC_EQUAL (src->m_ARP_MAC_Source, Adapter->PermanentAddress) + && ETH_IS_BROADCAST(src->m_MAC_Destination) + && 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 (NDIS_ETH_TYPE_IPV4) + && 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 (NDIS_ETH_TYPE_ARP); + arp->m_MAC_AddressType = htons (MAC_ADDR_TYPE); + arp->m_PROTO_AddressType = htons (NDIS_ETH_TYPE_IPV4); + arp->m_MAC_AddressSize = sizeof (MACADDR); + arp->m_PROTO_AddressSize = sizeof (IPADDR); + arp->m_ARP_Operation = htons (ARP_REPLY); + + //---------------------------------------------- + // ARP addresses + //---------------------------------------------- + ETH_COPY_NETWORK_ADDRESS (arp->m_MAC_Source, mac); + ETH_COPY_NETWORK_ADDRESS (arp->m_MAC_Destination, Adapter->PermanentAddress); + ETH_COPY_NETWORK_ADDRESS (arp->m_ARP_MAC_Source, mac); + ETH_COPY_NETWORK_ADDRESS (arp->m_ARP_MAC_Destination, Adapter->PermanentAddress); + 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)); + + IndicateReceivePacket (Adapter, (UCHAR *) arp, sizeof (ARP_PACKET)); + + MemFree (arp, sizeof (ARP_PACKET)); + } + + return TRUE; + } + else + return FALSE; +} + +//============================================================= +// 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. +//============================================================= + +VOID +tapCompletePendingReadIrp( + __in PIRP Irp, + __in PTAP_PACKET TapPacket + ) +{ + int offset; + int len; + NTSTATUS status = STATUS_UNSUCCESSFUL; + + ASSERT(Irp); + ASSERT(TapPacket); + + //------------------------------------------- + // While TapPacket always contains a + // full ethernet packet, including the + // ethernet header, in point-to-point mode, + // we only want to return the IPv4 + // component. + //------------------------------------------- + + if (TapPacket->m_SizeFlags & TP_TUN) + { + offset = ETHERNET_HEADER_SIZE; + len = (int) (TapPacket->m_SizeFlags & TP_SIZE_MASK) - ETHERNET_HEADER_SIZE; + } + else + { + offset = 0; + len = (TapPacket->m_SizeFlags & TP_SIZE_MASK); + } + + if (len < 0 || (int) Irp->IoStatus.Information < len) + { + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = status = STATUS_BUFFER_OVERFLOW; + NOTE_ERROR (); + } + else + { + Irp->IoStatus.Information = len; + Irp->IoStatus.Status = status = STATUS_SUCCESS; + + // Copy packet data + NdisMoveMemory( + Irp->AssociatedIrp.SystemBuffer, + TapPacket->m_Data + offset, + len + ); + } + + // Free the TAP packet + NdisFreeMemory(TapPacket,0,0); + + // Complete the IRP + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); +} + +VOID +tapProcessSendPacketQueue( + __in PTAP_ADAPTER_CONTEXT Adapter + ) +{ + KIRQL irql; + + // Process the send packet queue + KeAcquireSpinLock(&Adapter->SendPacketQueue.QueueLock,&irql); + + while(Adapter->SendPacketQueue.Count > 0 ) + { + PIRP irp; + PTAP_PACKET tapPacket; + + // Fetch a read IRP + irp = IoCsqRemoveNextIrp( + &Adapter->PendingReadIrpQueue.CsqQueue, + NULL + ); + + if( irp == NULL ) + { + // No IRP to satisfy + break; + } + + // Fetch a queued TAP send packet + tapPacket = tapPacketRemoveHeadLocked( + &Adapter->SendPacketQueue + ); + + ASSERT(tapPacket); + + // BUGBUG!!! Investigate whether release/reacquire can cause + // out-of-order IRP completion. Also, whether user-mode can + // tolerate out-of-order packets. + + // Release packet queue lock while completing the IRP + //KeReleaseSpinLock(&Adapter->SendPacketQueue.QueueLock,irql); + + // Complete the read IRP from queued TAP send packet. + tapCompletePendingReadIrp(irp,tapPacket); + + // Reqcquire packet queue lock after completing the IRP + //KeAcquireSpinLock(&Adapter->SendPacketQueue.QueueLock,&irql); + } + + KeReleaseSpinLock(&Adapter->SendPacketQueue.QueueLock,irql); +} + +// Flush the pending send TAP packet queue. +VOID +tapFlushSendPacketQueue( + __in PTAP_ADAPTER_CONTEXT Adapter + ) +{ + KIRQL irql; + + // Process the send packet queue + KeAcquireSpinLock(&Adapter->SendPacketQueue.QueueLock,&irql); + + DEBUGP (("[TAP] tapFlushSendPacketQueue: Flushing %d TAP packets\n", + Adapter->SendPacketQueue.Count)); + + while(Adapter->SendPacketQueue.Count > 0 ) + { + PTAP_PACKET tapPacket; + + // Fetch a queued TAP send packet + tapPacket = tapPacketRemoveHeadLocked( + &Adapter->SendPacketQueue + ); + + ASSERT(tapPacket); + + // Free the TAP packet + NdisFreeMemory(tapPacket,0,0); + } + + KeReleaseSpinLock(&Adapter->SendPacketQueue.QueueLock,irql); +} + +VOID +tapAdapterTransmit( + __in PTAP_ADAPTER_CONTEXT Adapter, + __in PNET_BUFFER NetBuffer, + __in BOOLEAN DispatchLevel + ) +/*++ + +Routine Description: + + This routine is called to transmit an individual net buffer using a + style similar to the previous NDIS 5 AdapterTransmit function. + + In this implementation adapter state and NB length checks have already + been done before this function has been called. + + The net buffer will be completed by the calling routine after this + routine exits. So, under this design it is necessary to make a deep + copy of frame data in the net buffer. + + This routine creates a flat buffer copy of NB frame data. This is an + unnecessary performance bottleneck. However, the bottleneck is probably + not significant or measurable except for adapters running at 1Gbps or + greater speeds. Since this adapter is currently running at 100Mbps this + defect can be ignored. + + Runs at IRQL <= DISPATCH_LEVEL + +Arguments: + + Adapter Pointer to our adapter context + NetBuffer Pointer to the net buffer to transmit + DispatchLevel TRUE if called at IRQL == DISPATCH_LEVEL + +Return Value: + + None. + + In the Microsoft NDIS 6 architecture there is no per-packet status. + +--*/ +{ + NDIS_STATUS status; + ULONG packetLength; + PTAP_PACKET tapPacket; + PVOID packetData; + + packetLength = NET_BUFFER_DATA_LENGTH(NetBuffer); + + // Allocate TAP packet memory + tapPacket = (PTAP_PACKET )NdisAllocateMemoryWithTagPriority( + Adapter->MiniportAdapterHandle, + TAP_PACKET_SIZE (packetLength), + TAP_PACKET_TAG, + NormalPoolPriority + ); + + if(tapPacket == NULL) + { + DEBUGP (("[TAP] tapAdapterTransmit: TAP packet allocation failed\n")); + return; + } + + tapPacket->m_SizeFlags = (packetLength & TP_SIZE_MASK); + + // + // Reassemble packet contents + // -------------------------- + // NdisGetDataBuffer does most of the work. There are two cases: + // + // 1.) If the NB data was not contiguous it will copy the entire + // NB's data to m_data and return pointer to m_data. + // 2.) If the NB data was contiguous it returns a pointer to the + // first byte of the contiguous data instead of a pointer to m_Data. + // In this case the data will not have been copied to m_Data. Copy + // to m_Data will need to be done in an extra step. + // + // Case 1.) is the most likely in normal operation. + // + packetData = NdisGetDataBuffer(NetBuffer,packetLength,tapPacket->m_Data,1,0); + + if(packetData == NULL) + { + DEBUGP (("[TAP] tapAdapterTransmit: Could not get packet data\n")); + + NdisFreeMemory(tapPacket,0,0); + + return; + } + + if(packetData != tapPacket->m_Data) + { + // Packet data was contiguous and not yet copied to m_Data. + NdisMoveMemory(tapPacket->m_Data,packetData,packetLength); + } + + DUMP_PACKET ("AdapterTransmit", tapPacket->m_Data, packetLength); + + //===================================================== + // If IPv4 packet, check whether or not packet + // was truncated. + //===================================================== +#if PACKET_TRUNCATION_CHECK + IPv4PacketSizeVerify( + tapPacket->m_Data, + packetLength, + FALSE, + "TX", + &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 (Adapter->m_dhcp_enabled) + { + const ETH_HEADER *eth = (ETH_HEADER *) tapPacket->m_Data; + const IPHDR *ip = (IPHDR *) (tapPacket->m_Data + sizeof (ETH_HEADER)); + const UDPHDR *udp = (UDPHDR *) (tapPacket->m_Data + sizeof (ETH_HEADER) + sizeof (IPHDR)); + + // ARP packet? + if (packetLength == sizeof (ARP_PACKET) + && eth->proto == htons (NDIS_ETH_TYPE_ARP) + && Adapter->m_dhcp_server_arp + ) + { + if (ProcessARP( + Adapter, + (PARP_PACKET) tapPacket->m_Data, + Adapter->m_dhcp_addr, + Adapter->m_dhcp_server_ip, + ~0, + Adapter->m_dhcp_server_mac) + ) + { + goto no_queue; + } + } + + // DHCP packet? + else if (packetLength >= sizeof (ETH_HEADER) + sizeof (IPHDR) + sizeof (UDPHDR) + sizeof (DHCP) + && eth->proto == htons (NDIS_ETH_TYPE_IPV4) + && ip->version_len == 0x45 // IPv4, 20 byte header + && ip->protocol == IPPROTO_UDP + && udp->dest == htons (BOOTPS_PORT) + ) + { + const DHCP *dhcp = (DHCP *) (tapPacket->m_Data + + sizeof (ETH_HEADER) + + sizeof (IPHDR) + + sizeof (UDPHDR)); + + const int optlen = packetLength + - sizeof (ETH_HEADER) + - sizeof (IPHDR) + - sizeof (UDPHDR) + - sizeof (DHCP); + + if (optlen > 0) // we must have at least one DHCP option + { + if (ProcessDHCP (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 (Adapter->m_tun) + { + ETH_HEADER *e; + + e = (ETH_HEADER *) tapPacket->m_Data; + + switch (ntohs (e->proto)) + { + case NDIS_ETH_TYPE_ARP: + + // Make sure that packet is the right size for ARP. + if (packetLength != sizeof (ARP_PACKET)) + { + goto no_queue; + } + + ProcessARP ( + Adapter, + (PARP_PACKET) tapPacket->m_Data, + Adapter->m_localIP, + Adapter->m_remoteNetwork, + Adapter->m_remoteNetmask, + Adapter->m_TapToUser.dest + ); + + default: + goto no_queue; + + case NDIS_ETH_TYPE_IPV4: + + // Make sure that packet is large enough to be IPv4. + if (packetLength < (ETHERNET_HEADER_SIZE + IP_HEADER_SIZE)) + { + goto no_queue; + } + + // Only accept directed packets, not broadcasts. + if (memcmp (e, &Adapter->m_TapToUser, ETHERNET_HEADER_SIZE)) + { + goto no_queue; + } + + // Packet looks like IPv4, queue it. :-) + tapPacket->m_SizeFlags |= TP_TUN; + break; + + case NDIS_ETH_TYPE_IPV6: + // Make sure that packet is large enough to be IPv6. + if (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(Adapter,tapPacket->m_Data) ) + { + goto no_queue; + } + + // Packet looks like IPv6, queue it. :-) + tapPacket->m_SizeFlags |= TP_TUN; + } + } + + //=============================================== + // Push packet onto queue to wait for read from + // userspace. + //=============================================== + if(tapAdapterReadAndWriteReady(Adapter)) + { + tapPacketQueueInsertTail(&Adapter->SendPacketQueue,tapPacket); + } + else + { + // + // Tragedy. All this work and the packet is of no use... + // + NdisFreeMemory(tapPacket,0,0); + } + + // Return after queuing or freeing TAP packet. + return; + + // Free TAP packet without queuing. +no_queue: + if(tapPacket != NULL ) + { + NdisFreeMemory(tapPacket,0,0); + } + +exit_success: + return; +} + +VOID +tapSendNetBufferListsComplete( + __in PTAP_ADAPTER_CONTEXT Adapter, + __in PNET_BUFFER_LIST NetBufferLists, + __in NDIS_STATUS SendCompletionStatus, + __in BOOLEAN DispatchLevel + ) +{ + PNET_BUFFER_LIST currentNbl; + PNET_BUFFER_LIST nextNbl = NULL; + ULONG sendCompleteFlags = 0; + + for ( + currentNbl = NetBufferLists; + currentNbl != NULL; + currentNbl = nextNbl + ) + { + ULONG frameType; + ULONG netBufferCount; + ULONG byteCount; + + nextNbl = NET_BUFFER_LIST_NEXT_NBL(currentNbl); + + // Set NBL completion status. + NET_BUFFER_LIST_STATUS(currentNbl) = SendCompletionStatus; + + // Fetch first NBs frame type. All linked NBs will have same type. + frameType = tapGetNetBufferFrameType(NET_BUFFER_LIST_FIRST_NB(currentNbl)); + + // Fetch statistics for all NBs linked to the NB. + netBufferCount = tapGetNetBufferCountsFromNetBufferList( + currentNbl, + &byteCount + ); + + // Update statistics by frame type + if(SendCompletionStatus == NDIS_STATUS_SUCCESS) + { + switch(frameType) + { + case NDIS_PACKET_TYPE_DIRECTED: + Adapter->FramesTxDirected += netBufferCount; + Adapter->BytesTxDirected += byteCount; + break; + + case NDIS_PACKET_TYPE_BROADCAST: + Adapter->FramesTxBroadcast += netBufferCount; + Adapter->BytesTxBroadcast += byteCount; + break; + + case NDIS_PACKET_TYPE_MULTICAST: + Adapter->FramesTxMulticast += netBufferCount; + Adapter->BytesTxMulticast += byteCount; + break; + + default: + ASSERT(FALSE); + break; + } + } + else + { + // Transmit error. + Adapter->TransmitFailuresOther += netBufferCount; + } + + currentNbl = nextNbl; + } + + if(DispatchLevel) + { + sendCompleteFlags |= NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL; + } + + // Complete the NBLs + NdisMSendNetBufferListsComplete( + Adapter->MiniportAdapterHandle, + NetBufferLists, + sendCompleteFlags + ); +} + +BOOLEAN +tapNetBufferListNetBufferLengthsValid( + __in PTAP_ADAPTER_CONTEXT Adapter, + __in PNET_BUFFER_LIST NetBufferLists + ) +/*++ + +Routine Description: + + Scan all NBLs and their linked NBs for valid lengths. + + Fairly absurd to find and packets with bogus lengths, but wise + to check anyway. If ANY packet has a bogus length, then abort the + entire send. + + The only time that one might see this check fail might be during + HCK driver testing. The HKC test might send oversize packets to + determine if the miniport can gracefully deal with them. + + This check is fairly fast. Unlike NDIS 5 packets, fetching NDIS 6 + packets lengths do not require any computation. + +Arguments: + + Adapter Pointer to our adapter context + NetBufferLists Head of a list of NBLs to examine + +Return Value: + + Returns TRUE if all NBs have reasonable lengths. + Otherwise, returns FALSE. + +--*/ +{ + PNET_BUFFER_LIST currentNbl; + + currentNbl = NetBufferLists; + + while (currentNbl) + { + PNET_BUFFER_LIST nextNbl; + PNET_BUFFER currentNb; + + // Locate next NBL + nextNbl = NET_BUFFER_LIST_NEXT_NBL(currentNbl); + + // Locate first NB (aka "packet") + currentNb = NET_BUFFER_LIST_FIRST_NB(currentNbl); + + // + // Process all NBs linked to this NBL + // + while(currentNb) + { + PNET_BUFFER nextNb; + ULONG packetLength; + + // Locate next NB + nextNb = NET_BUFFER_NEXT_NB(currentNb); + + packetLength = NET_BUFFER_DATA_LENGTH(currentNb); + + // Minimum packet size is size of Ethernet plus IPv4 headers. + ASSERT(packetLength >= (ETHERNET_HEADER_SIZE + IP_HEADER_SIZE)); + + if(packetLength < (ETHERNET_HEADER_SIZE + IP_HEADER_SIZE)) + { + return FALSE; + } + + // Maximum size should be Ethernet header size plus MTU plus modest pad for + // VLAN tag. + ASSERT( packetLength <= (ETHERNET_HEADER_SIZE + VLAN_TAG_SIZE + Adapter->MtuSize)); + + if(packetLength > (ETHERNET_HEADER_SIZE + VLAN_TAG_SIZE + Adapter->MtuSize)) + { + return FALSE; + } + + // Move to next NB + currentNb = nextNb; + } + + // Move to next NBL + currentNbl = nextNbl; + } + + return TRUE; +} + +VOID +AdapterSendNetBufferLists( + __in NDIS_HANDLE MiniportAdapterContext, + __in PNET_BUFFER_LIST NetBufferLists, + __in NDIS_PORT_NUMBER PortNumber, + __in ULONG SendFlags + ) +/*++ + +Routine Description: + + Send Packet Array handler. Called by NDIS whenever a protocol + bound to our miniport sends one or more packets. + + The input packet descriptor pointers have been ordered according + to the order in which the packets should be sent over the network + by the protocol driver that set up the packet array. The NDIS + library preserves the protocol-determined ordering when it submits + each packet array to MiniportSendPackets + + As a deserialized driver, we are responsible for holding incoming send + packets in our internal queue until they can be transmitted over the + network and for preserving the protocol-determined ordering of packet + descriptors incoming to its MiniportSendPackets function. + A deserialized miniport driver must complete each incoming send packet + with NdisMSendComplete, and it cannot call NdisMSendResourcesAvailable. + + Runs at IRQL <= DISPATCH_LEVEL + +Arguments: + + MiniportAdapterContext Pointer to our adapter + NetBufferLists Head of a list of NBLs to send + PortNumber A miniport adapter port. Default is 0. + SendFlags Additional flags for the send operation + +Return Value: + + None. Write status directly into each NBL with the NET_BUFFER_LIST_STATUS + macro. + +--*/ +{ + NDIS_STATUS status; + PTAP_ADAPTER_CONTEXT adapter = (PTAP_ADAPTER_CONTEXT )MiniportAdapterContext; + BOOLEAN DispatchLevel = (SendFlags & NDIS_SEND_FLAGS_DISPATCH_LEVEL); + PNET_BUFFER_LIST currentNbl; + BOOLEAN validNbLengths; + + UNREFERENCED_PARAMETER(NetBufferLists); + UNREFERENCED_PARAMETER(PortNumber); + UNREFERENCED_PARAMETER(SendFlags); + + ASSERT(PortNumber == 0); // Only the default port is supported + + // + // Can't process sends if TAP device is not open. + // ---------------------------------------------- + // Just perform a "lying send" and return packets as if they + // were successfully sent. + // + if(adapter->TapFileObject == NULL) + { + // + // Complete all NBLs and return if adapter not ready. + // + tapSendNetBufferListsComplete( + adapter, + NetBufferLists, + NDIS_STATUS_SUCCESS, + DispatchLevel + ); + + return; + } + + // + // Check Adapter send/receive ready state. + // + status = tapAdapterSendAndReceiveReady(adapter); + + if(status != NDIS_STATUS_SUCCESS) + { + // + // Complete all NBLs and return if adapter not ready. + // + tapSendNetBufferListsComplete( + adapter, + NetBufferLists, + status, + DispatchLevel + ); + + return; + } + + // + // Scan all NBLs and linked packets for valid lengths. + // --------------------------------------------------- + // If _ANY_ NB length is invalid, then fail the entire send operation. + // + // BUGBUG!!! Perhaps this should be less agressive. Fail only individual + // NBLs... + // + // If length check is valid, then TAP_PACKETS can be safely allocated + // and processed for all NBs being sent. + // + validNbLengths = tapNetBufferListNetBufferLengthsValid( + adapter, + NetBufferLists + ); + + if(!validNbLengths) + { + // + // Complete all NBLs and return if and NB length is invalid. + // + tapSendNetBufferListsComplete( + adapter, + NetBufferLists, + NDIS_STATUS_INVALID_LENGTH, + DispatchLevel + ); + + return; + } + + // + // Process each NBL individually + // + currentNbl = NetBufferLists; + + while (currentNbl) + { + PNET_BUFFER_LIST nextNbl; + PNET_BUFFER currentNb; + + // Locate next NBL + nextNbl = NET_BUFFER_LIST_NEXT_NBL(currentNbl); + + // Locate first NB (aka "packet") + currentNb = NET_BUFFER_LIST_FIRST_NB(currentNbl); + + // Transmit all NBs linked to this NBL + while(currentNb) + { + PNET_BUFFER nextNb; + + // Locate next NB + nextNb = NET_BUFFER_NEXT_NB(currentNb); + + // Transmit the NB + tapAdapterTransmit(adapter,currentNb,DispatchLevel); + + // Move to next NB + currentNb = nextNb; + } + + // Move to next NBL + currentNbl = nextNbl; + } + + // Complete all NBLs + tapSendNetBufferListsComplete( + adapter, + NetBufferLists, + NDIS_STATUS_SUCCESS, + DispatchLevel + ); + + // Attempt to complete pending read IRPs from pending TAP + // send packet queue. + tapProcessSendPacketQueue(adapter); +} + +VOID +AdapterCancelSend( + __in NDIS_HANDLE MiniportAdapterContext, + __in PVOID CancelId + ) +{ + PTAP_ADAPTER_CONTEXT adapter = (PTAP_ADAPTER_CONTEXT )MiniportAdapterContext; + + // + // This miniport completes its sends quickly, so it isn't strictly + // neccessary to implement MiniportCancelSend. + // + // If we did implement it, we'd have to walk the Adapter->SendWaitList + // and look for any NB that points to a NBL where the CancelId matches + // NDIS_GET_NET_BUFFER_LIST_CANCEL_ID(Nbl). For any NB that so matches, + // we'd remove the NB from the SendWaitList and set the NBL's status to + // NDIS_STATUS_SEND_ABORTED, then complete the NBL. + // +} + +// IRP_MJ_READ callback. +NTSTATUS +TapDeviceRead( + PDEVICE_OBJECT DeviceObject, + PIRP Irp + ) +{ + NTSTATUS ntStatus = STATUS_SUCCESS;// Assume success + PIO_STACK_LOCATION irpSp;// Pointer to current stack location + PTAP_ADAPTER_CONTEXT adapter = NULL; + + PAGED_CODE(); + + irpSp = IoGetCurrentIrpStackLocation( Irp ); + + // + // Fetch adapter context for this device. + // -------------------------------------- + // Adapter pointer was stashed in FsContext when handle was opened. + // + adapter = (PTAP_ADAPTER_CONTEXT )(irpSp->FileObject)->FsContext; + + ASSERT(adapter); + + // + // Sanity checks on state variables + // + if (!tapAdapterReadAndWriteReady(adapter)) + { + //DEBUGP (("[%s] Interface is down in IRP_MJ_READ\n", + // MINIPORT_INSTANCE_ID (adapter))); + //NOTE_ERROR(); + + Irp->IoStatus.Status = ntStatus = STATUS_CANCELLED; + Irp->IoStatus.Information = 0; + IoCompleteRequest (Irp, IO_NO_INCREMENT); + + return ntStatus; + } + + // Save IRP-accessible copy of buffer length + Irp->IoStatus.Information = irpSp->Parameters.Read.Length; + + if (Irp->MdlAddress == NULL) + { + DEBUGP (("[%s] MdlAddress is NULL for IRP_MJ_READ\n", + MINIPORT_INSTANCE_ID (adapter))); + + NOTE_ERROR(); + Irp->IoStatus.Status = ntStatus = STATUS_INVALID_PARAMETER; + Irp->IoStatus.Information = 0; + IoCompleteRequest (Irp, IO_NO_INCREMENT); + + return ntStatus; + } + + if ((Irp->AssociatedIrp.SystemBuffer + = MmGetSystemAddressForMdlSafe( + Irp->MdlAddress, + NormalPagePriority + ) ) == NULL + ) + { + DEBUGP (("[%s] Could not map address in IRP_MJ_READ\n", + MINIPORT_INSTANCE_ID (adapter))); + + NOTE_ERROR(); + Irp->IoStatus.Status = ntStatus = STATUS_INSUFFICIENT_RESOURCES; + Irp->IoStatus.Information = 0; + IoCompleteRequest (Irp, IO_NO_INCREMENT); + + return ntStatus; + } + + // BUGBUG!!! Use RemoveLock??? + + // + // Queue the IRP and return STATUS_PENDING. + // ---------------------------------------- + // Note: IoCsqInsertIrp marks the IRP pending. + // + + // BUGBUG!!! NDIS 5 implementation has IRP_QUEUE_SIZE of 16 and + // does not queue IRP if this capacity is exceeded. + // + // Is this needed??? + // + IoCsqInsertIrp(&adapter->PendingReadIrpQueue.CsqQueue, Irp, NULL); + + // Attempt to complete pending read IRPs from pending TAP + // send packet queue. + tapProcessSendPacketQueue(adapter); + + ntStatus = STATUS_PENDING; + + return ntStatus; +} + diff --git a/windows/TapDriver6/types.h b/windows/TapDriver6/types.h new file mode 100644 index 00000000..acea175c --- /dev/null +++ b/windows/TapDriver6/types.h @@ -0,0 +1,90 @@ +/* + * 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-2014 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 _TAP_PACKET; + +//typedef struct _TapExtension +//{ +// // TAP device object and packet queues +// Queue *m_PacketQueue, *m_IrpQueue; +// PDEVICE_OBJECT m_TapDevice; +// NDIS_HANDLE m_TapDeviceHandle; +// ULONG TapFileIsOpen; +// +// // 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 TapFileOpenCount; +// +// // Flags +// BOOLEAN TapDeviceCreated; +// 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 _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; + +#endif diff --git a/windows/ZeroTierOne.sln b/windows/ZeroTierOne.sln index 09a0874b..3aebacb5 100644 --- a/windows/ZeroTierOne.sln +++ b/windows/ZeroTierOne.sln @@ -5,6 +5,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TapDriver", "TapDriver\TapD EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ZeroTierOne", "ZeroTierOne\ZeroTierOne.vcxproj", "{B00A4957-5977-4AC1-9EF4-571DC27EADA2}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TapDriver6", "TapDriver6\TapDriver6.vcxproj", "{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution CD_ROM|Any CPU = CD_ROM|Any CPU @@ -295,6 +297,149 @@ Global {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Win8 Release|x86.ActiveCfg = Release|Win32 {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Win8 Release|x86.Build.0 = Release|Win32 {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Win8 Release|x86.Deploy.0 = Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.CD_ROM|Any CPU.ActiveCfg = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.CD_ROM|Mixed Platforms.ActiveCfg = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.CD_ROM|Mixed Platforms.Build.0 = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.CD_ROM|Mixed Platforms.Deploy.0 = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.CD_ROM|Win32.ActiveCfg = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.CD_ROM|Win32.Build.0 = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.CD_ROM|Win32.Deploy.0 = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.CD_ROM|x64.ActiveCfg = Win8 Release|x64 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.CD_ROM|x64.Build.0 = Win8 Release|x64 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.CD_ROM|x64.Deploy.0 = Win8 Release|x64 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.CD_ROM|x86.ActiveCfg = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.CD_ROM|x86.Build.0 = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.CD_ROM|x86.Deploy.0 = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Debug|Any CPU.ActiveCfg = Win7 Debug|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Debug|Mixed Platforms.ActiveCfg = Win7 Debug|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Debug|Mixed Platforms.Build.0 = Win7 Debug|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Debug|Mixed Platforms.Deploy.0 = Win7 Debug|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Debug|Win32.ActiveCfg = Win7 Debug|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Debug|Win32.Build.0 = Win7 Debug|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Debug|Win32.Deploy.0 = Win7 Debug|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Debug|x64.ActiveCfg = Win7 Debug|x64 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Debug|x64.Build.0 = Win7 Debug|x64 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Debug|x64.Deploy.0 = Win7 Debug|x64 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Debug|x86.ActiveCfg = Win7 Debug|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Debug|x86.Build.0 = Win7 Debug|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Debug|x86.Deploy.0 = Win7 Debug|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.DVD-5|Any CPU.ActiveCfg = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.DVD-5|Mixed Platforms.ActiveCfg = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.DVD-5|Mixed Platforms.Build.0 = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.DVD-5|Mixed Platforms.Deploy.0 = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.DVD-5|Win32.ActiveCfg = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.DVD-5|Win32.Build.0 = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.DVD-5|Win32.Deploy.0 = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.DVD-5|x64.ActiveCfg = Win8 Release|x64 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.DVD-5|x64.Build.0 = Win8 Release|x64 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.DVD-5|x64.Deploy.0 = Win8 Release|x64 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.DVD-5|x86.ActiveCfg = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.DVD-5|x86.Build.0 = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.DVD-5|x86.Deploy.0 = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Release|Any CPU.ActiveCfg = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Release|Mixed Platforms.ActiveCfg = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Release|Mixed Platforms.Build.0 = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Release|Mixed Platforms.Deploy.0 = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Release|Win32.ActiveCfg = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Release|Win32.Build.0 = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Release|Win32.Deploy.0 = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Release|x64.ActiveCfg = Win8 Release|x64 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Release|x64.Build.0 = Win8 Release|x64 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Release|x64.Deploy.0 = Win8 Release|x64 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Release|x86.ActiveCfg = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Release|x86.Build.0 = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Release|x86.Deploy.0 = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.SingleImage|Any CPU.ActiveCfg = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.SingleImage|Mixed Platforms.ActiveCfg = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.SingleImage|Mixed Platforms.Build.0 = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.SingleImage|Mixed Platforms.Deploy.0 = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.SingleImage|Win32.ActiveCfg = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.SingleImage|Win32.Build.0 = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.SingleImage|Win32.Deploy.0 = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.SingleImage|x64.ActiveCfg = Win8 Release|x64 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.SingleImage|x64.Build.0 = Win8 Release|x64 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.SingleImage|x64.Deploy.0 = Win8 Release|x64 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.SingleImage|x86.ActiveCfg = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.SingleImage|x86.Build.0 = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.SingleImage|x86.Deploy.0 = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Vista Debug|Any CPU.ActiveCfg = Vista Debug|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Vista Debug|Mixed Platforms.ActiveCfg = Vista Debug|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Vista Debug|Mixed Platforms.Build.0 = Vista Debug|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Vista Debug|Mixed Platforms.Deploy.0 = Vista Debug|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Vista Debug|Win32.ActiveCfg = Vista Debug|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Vista Debug|Win32.Build.0 = Vista Debug|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Vista Debug|Win32.Deploy.0 = Vista Debug|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Vista Debug|x64.ActiveCfg = Vista Debug|x64 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Vista Debug|x64.Build.0 = Vista Debug|x64 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Vista Debug|x64.Deploy.0 = Vista Debug|x64 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Vista Debug|x86.ActiveCfg = Vista Debug|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Vista Debug|x86.Build.0 = Vista Debug|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Vista Debug|x86.Deploy.0 = Vista Debug|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Vista Release|Any CPU.ActiveCfg = Vista Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Vista Release|Mixed Platforms.ActiveCfg = Vista Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Vista Release|Mixed Platforms.Build.0 = Vista Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Vista Release|Mixed Platforms.Deploy.0 = Vista Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Vista Release|Win32.ActiveCfg = Vista Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Vista Release|Win32.Build.0 = Vista Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Vista Release|Win32.Deploy.0 = Vista Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Vista Release|x64.ActiveCfg = Vista Release|x64 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Vista Release|x64.Build.0 = Vista Release|x64 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Vista Release|x64.Deploy.0 = Vista Release|x64 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Vista Release|x86.ActiveCfg = Vista Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Vista Release|x86.Build.0 = Vista Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Vista Release|x86.Deploy.0 = Vista Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win7 Debug|Any CPU.ActiveCfg = Win7 Debug|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win7 Debug|Mixed Platforms.ActiveCfg = Win7 Debug|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win7 Debug|Mixed Platforms.Build.0 = Win7 Debug|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win7 Debug|Mixed Platforms.Deploy.0 = Win7 Debug|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win7 Debug|Win32.ActiveCfg = Win7 Debug|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win7 Debug|Win32.Build.0 = Win7 Debug|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win7 Debug|Win32.Deploy.0 = Win7 Debug|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win7 Debug|x64.ActiveCfg = Win7 Debug|x64 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win7 Debug|x64.Build.0 = Win7 Debug|x64 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win7 Debug|x64.Deploy.0 = Win7 Debug|x64 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win7 Debug|x86.ActiveCfg = Win7 Debug|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win7 Debug|x86.Build.0 = Win7 Debug|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win7 Debug|x86.Deploy.0 = Win7 Debug|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win7 Release|Any CPU.ActiveCfg = Win7 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win7 Release|Mixed Platforms.ActiveCfg = Win7 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win7 Release|Mixed Platforms.Build.0 = Win7 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win7 Release|Mixed Platforms.Deploy.0 = Win7 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win7 Release|Win32.ActiveCfg = Win7 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win7 Release|Win32.Build.0 = Win7 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win7 Release|Win32.Deploy.0 = Win7 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win7 Release|x64.ActiveCfg = Win7 Release|x64 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win7 Release|x64.Build.0 = Win7 Release|x64 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win7 Release|x64.Deploy.0 = Win7 Release|x64 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win7 Release|x86.ActiveCfg = Win7 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win7 Release|x86.Build.0 = Win7 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win7 Release|x86.Deploy.0 = Win7 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win8 Debug|Any CPU.ActiveCfg = Win8 Debug|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win8 Debug|Mixed Platforms.ActiveCfg = Win8 Debug|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win8 Debug|Mixed Platforms.Build.0 = Win8 Debug|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win8 Debug|Mixed Platforms.Deploy.0 = Win8 Debug|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win8 Debug|Win32.ActiveCfg = Win8 Debug|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win8 Debug|Win32.Build.0 = Win8 Debug|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win8 Debug|Win32.Deploy.0 = Win8 Debug|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win8 Debug|x64.ActiveCfg = Win8 Debug|x64 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win8 Debug|x64.Build.0 = Win8 Debug|x64 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win8 Debug|x64.Deploy.0 = Win8 Debug|x64 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win8 Debug|x86.ActiveCfg = Win8 Debug|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win8 Debug|x86.Build.0 = Win8 Debug|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win8 Debug|x86.Deploy.0 = Win8 Debug|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win8 Release|Any CPU.ActiveCfg = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win8 Release|Mixed Platforms.ActiveCfg = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win8 Release|Mixed Platforms.Build.0 = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win8 Release|Mixed Platforms.Deploy.0 = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win8 Release|Win32.ActiveCfg = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win8 Release|Win32.Build.0 = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win8 Release|Win32.Deploy.0 = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win8 Release|x64.ActiveCfg = Win8 Release|x64 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win8 Release|x64.Build.0 = Win8 Release|x64 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win8 Release|x64.Deploy.0 = Win8 Release|x64 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win8 Release|x86.ActiveCfg = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win8 Release|x86.Build.0 = Win8 Release|Win32 + {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win8 Release|x86.Deploy.0 = Win8 Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE |