mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2024-11-22 21:40:19 -08:00
1718 lines
53 KiB
C
1718 lines
53 KiB
C
/*
|
|
* 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
|
|
&& configParameter->ParameterData.StringData.Length <= sizeof(Adapter->NetCfgInstanceIdBuffer) - sizeof(WCHAR))
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
|
|
|