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