1201 lines
33 KiB
C
1201 lines
33 KiB
C
|
/*++
|
||
|
|
||
|
Copyright(c) 1999-2000 Microsoft Corporation
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
brdgctl.c
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
Ethernet MAC level bridge.
|
||
|
IOCTL processing code
|
||
|
|
||
|
Author:
|
||
|
|
||
|
Mark Aiken
|
||
|
|
||
|
Environment:
|
||
|
|
||
|
Kernel mode driver
|
||
|
|
||
|
Revision History:
|
||
|
|
||
|
Apr 2000 - Original version
|
||
|
|
||
|
--*/
|
||
|
|
||
|
#define NDIS_MINIPORT_DRIVER
|
||
|
#define NDIS50_MINIPORT 1
|
||
|
#define NDIS_WDM 1
|
||
|
|
||
|
#pragma warning( push, 3 )
|
||
|
#include <ndis.h>
|
||
|
#include <ntddk.h>
|
||
|
#pragma warning( pop )
|
||
|
|
||
|
#include "bridge.h"
|
||
|
|
||
|
#include "brdgmini.h"
|
||
|
#include "brdgtbl.h"
|
||
|
#include "brdgctl.h"
|
||
|
#include "brdgfwd.h"
|
||
|
#include "brdgprot.h"
|
||
|
#include "brdgbuf.h"
|
||
|
#include "brdgsta.h"
|
||
|
|
||
|
// IoSetCancelRoutine causes these warnings; disable them
|
||
|
#pragma warning( disable: 4054 )
|
||
|
#pragma warning( disable: 4055 )
|
||
|
|
||
|
// ===========================================================================
|
||
|
//
|
||
|
// CONSTANTS
|
||
|
//
|
||
|
// ===========================================================================
|
||
|
|
||
|
//
|
||
|
// Maximum number of notifications we will queue up if the user-mode
|
||
|
// code hasn't given us any IRPs to use
|
||
|
//
|
||
|
#define MAX_NOTIFY_QUEUE_LENGTH 20
|
||
|
|
||
|
// ===========================================================================
|
||
|
//
|
||
|
// PRIVATE DECLARATIONS
|
||
|
//
|
||
|
// ===========================================================================
|
||
|
|
||
|
// Structure for queueing a notification
|
||
|
typedef struct _DEFERRED_NOTIFY
|
||
|
{
|
||
|
|
||
|
BSINGLE_LIST_ENTRY List; // For queuing
|
||
|
UINT DataSize; // Size of data at end
|
||
|
|
||
|
BRIDGE_NOTIFY_HEADER Header; // The notification header
|
||
|
// DataSize bytes of data follows
|
||
|
|
||
|
} DEFERRED_NOTIFY, *PDEFERRED_NOTIFY;
|
||
|
|
||
|
// Type of function to pass to BrdgCtlCommonNotify
|
||
|
typedef VOID (*PNOTIFY_COPY_FUNC)(PVOID, PVOID);
|
||
|
|
||
|
// ===========================================================================
|
||
|
//
|
||
|
// GLOBALS
|
||
|
//
|
||
|
// ===========================================================================
|
||
|
|
||
|
// Queue of pending notifications
|
||
|
BSINGLE_LIST_HEAD gNotificationsList;
|
||
|
NDIS_SPIN_LOCK gNotificationsListLock;
|
||
|
|
||
|
// Queue of pending notification IRPs
|
||
|
LIST_ENTRY gIRPList;
|
||
|
NDIS_SPIN_LOCK gIRPListLock;
|
||
|
|
||
|
// A flag controlling whether new entries are allowed onto the queue of pending
|
||
|
// notifications or not. != 0 means new entries are allowed
|
||
|
ULONG gAllowQueuedNotifies = 0L;
|
||
|
|
||
|
// ===========================================================================
|
||
|
//
|
||
|
// LOCAL PROTOTYPES
|
||
|
//
|
||
|
// ===========================================================================
|
||
|
|
||
|
PIRP
|
||
|
BrdgCtlDequeuePendingIRP();
|
||
|
|
||
|
VOID
|
||
|
BrdgCtlCopyAdapterInfo(
|
||
|
OUT PBRIDGE_ADAPTER_INFO pInfo,
|
||
|
IN PADAPT pAdapt
|
||
|
);
|
||
|
|
||
|
NTSTATUS
|
||
|
BrdgCtlQueueAndPendIRP(
|
||
|
IN PIRP pIrp
|
||
|
);
|
||
|
|
||
|
PADAPT
|
||
|
BrdgCtlValidateAcquireAdapter(
|
||
|
IN BRIDGE_ADAPTER_HANDLE Handle
|
||
|
);
|
||
|
|
||
|
VOID
|
||
|
BrdgCtlEmptyIRPList(
|
||
|
PLIST_ENTRY pList
|
||
|
);
|
||
|
|
||
|
VOID
|
||
|
BrdgCtlCancelPendingIRPs();
|
||
|
|
||
|
VOID
|
||
|
BrdgCtlReleaseQueuedNotifications();
|
||
|
|
||
|
// ===========================================================================
|
||
|
//
|
||
|
// PUBLIC FUNCTIONS
|
||
|
//
|
||
|
// ===========================================================================
|
||
|
|
||
|
NTSTATUS
|
||
|
BrdgCtlDriverInit()
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Main driver entry point. Called at driver load time
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
None
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
Status of our initialization. A status != STATUS_SUCCESS aborts the
|
||
|
driver load and we don't get called again.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
BrdgInitializeSingleList( &gNotificationsList );
|
||
|
InitializeListHead( &gIRPList );
|
||
|
|
||
|
NdisAllocateSpinLock( &gNotificationsListLock );
|
||
|
NdisAllocateSpinLock( &gIRPListLock );
|
||
|
|
||
|
return STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
BrdgCtlHandleCreate()
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Called when a user-mode component opens our device object. We allow notifications
|
||
|
to be queued up until we are closed.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
None
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
DBGPRINT(CTL, ("BrdgCtlHandleCreate()\n"));
|
||
|
|
||
|
// Permit notifications to be queued
|
||
|
InterlockedExchangeULong( &gAllowQueuedNotifies, 1L );
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
BrdgCtlHandleCleanup()
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Called when our device object has no more references to it. We disallow notification
|
||
|
queuing and flush existing queued notifications and pending IRPs.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
None
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
// Forbid new notifications from being queued
|
||
|
ULONG prev = InterlockedExchangeULong( &gAllowQueuedNotifies, 0L );
|
||
|
|
||
|
DBGPRINT(CTL, ("BrdgCtlHandleCleanup()\n"));
|
||
|
|
||
|
// Write in this roundabout way otherwise compiler complains about
|
||
|
// prev not being used in the FRE build
|
||
|
if( prev == 0L )
|
||
|
{
|
||
|
SAFEASSERT( FALSE );
|
||
|
}
|
||
|
|
||
|
// Complete any pending IRPs
|
||
|
BrdgCtlCancelPendingIRPs();
|
||
|
|
||
|
// Ditch any queued notifications
|
||
|
BrdgCtlReleaseQueuedNotifications();
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
BrdgCtlCommonNotify(
|
||
|
IN PADAPT pAdapt,
|
||
|
IN BRIDGE_NOTIFICATION_TYPE Type,
|
||
|
IN ULONG DataSize,
|
||
|
IN OPTIONAL PNOTIFY_COPY_FUNC pFunc,
|
||
|
IN PVOID Param1
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Common processing for notifications to user-mode
|
||
|
|
||
|
This routine completes a pending IRP from user mode if one is
|
||
|
available. Otherwise, it queues up a new DEFERRED_NOTIFY
|
||
|
structure with the notification data.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pAdapt The adapter involved in the notification
|
||
|
Type Type of notification
|
||
|
|
||
|
DataSize Required amount of data required to store
|
||
|
the notification information
|
||
|
|
||
|
pFunc A function that can copy the notification
|
||
|
data to an IRP's buffer or a new DEFERRED_NOTIFY
|
||
|
structure. Can be NULL if no copying is
|
||
|
required.
|
||
|
|
||
|
Param1 A context pointer to pass to the helper
|
||
|
function
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
PIRP pIrp;
|
||
|
|
||
|
// Check if there is an IRP waiting to receive this notification
|
||
|
pIrp = BrdgCtlDequeuePendingIRP();
|
||
|
|
||
|
if( pIrp != NULL )
|
||
|
{
|
||
|
PBRIDGE_NOTIFY_HEADER pHeader;
|
||
|
|
||
|
// There's an IRP waiting to be completed. Fill it in
|
||
|
pHeader = (PBRIDGE_NOTIFY_HEADER)pIrp->AssociatedIrp.SystemBuffer;
|
||
|
|
||
|
// Fill in the notification header
|
||
|
pHeader->Handle = (BRIDGE_ADAPTER_HANDLE)pAdapt;
|
||
|
pHeader->NotifyType = Type;
|
||
|
|
||
|
// Fill in the remaining data if necessary
|
||
|
if( pFunc != NULL )
|
||
|
{
|
||
|
(*pFunc)( ((PUCHAR)pIrp->AssociatedIrp.SystemBuffer) + sizeof(BRIDGE_NOTIFY_HEADER), Param1 );
|
||
|
}
|
||
|
|
||
|
// Complete the IRP
|
||
|
pIrp->IoStatus.Status = STATUS_SUCCESS;
|
||
|
pIrp->IoStatus.Information = sizeof(BRIDGE_NOTIFY_HEADER) + DataSize;
|
||
|
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// No pending IRP. Queue up the notification if that's currently allowed.
|
||
|
if( gAllowQueuedNotifies )
|
||
|
{
|
||
|
NDIS_STATUS Status;
|
||
|
PDEFERRED_NOTIFY pNewNotify, pOldEntry = NULL;
|
||
|
|
||
|
Status = NdisAllocateMemoryWithTag( &pNewNotify, sizeof(DEFERRED_NOTIFY) + DataSize, 'gdrB' );
|
||
|
|
||
|
if( Status != NDIS_STATUS_SUCCESS )
|
||
|
{
|
||
|
DBGPRINT(CTL, ("Failed to allocate memory for an adapter change notification: %08x\n", Status));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Fill in the notification
|
||
|
pNewNotify->DataSize = DataSize;
|
||
|
pNewNotify->Header.Handle = (BRIDGE_ADAPTER_HANDLE)pAdapt;
|
||
|
pNewNotify->Header.NotifyType = Type;
|
||
|
|
||
|
// Fill the remaining data if necessary
|
||
|
if( pFunc != NULL )
|
||
|
{
|
||
|
(*pFunc)( ((PUCHAR)pNewNotify) + sizeof(DEFERRED_NOTIFY), Param1 );
|
||
|
}
|
||
|
|
||
|
NdisAcquireSpinLock( &gNotificationsListLock );
|
||
|
SAFEASSERT( BrdgQuerySingleListLength(&gNotificationsList) <= MAX_NOTIFY_QUEUE_LENGTH );
|
||
|
|
||
|
// Enforce the maximum notification queue length
|
||
|
if( BrdgQuerySingleListLength(&gNotificationsList) == MAX_NOTIFY_QUEUE_LENGTH )
|
||
|
{
|
||
|
// Dequeue and ditch the head (oldest) notification
|
||
|
pOldEntry = (PDEFERRED_NOTIFY)BrdgRemoveHeadSingleList( &gNotificationsList );
|
||
|
}
|
||
|
|
||
|
// Enqueue our entry
|
||
|
BrdgInsertTailSingleList( &gNotificationsList, &pNewNotify->List );
|
||
|
|
||
|
NdisReleaseSpinLock( &gNotificationsListLock );
|
||
|
|
||
|
if( pOldEntry != NULL )
|
||
|
{
|
||
|
// Release the old entry that we bumped off
|
||
|
NdisFreeMemory( pOldEntry, sizeof(DEFERRED_NOTIFY) + pOldEntry->DataSize, 0 );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
BrdgCtlNotifyAdapterChange(
|
||
|
IN PADAPT pAdapt,
|
||
|
IN BRIDGE_NOTIFICATION_TYPE Type
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Produces a notification to user-mode signalling a change in an adapter.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pAdapt The adapter involved
|
||
|
Type Type of notification
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
if( Type == BrdgNotifyRemoveAdapter )
|
||
|
{
|
||
|
// We don't pass any additional data in the notification for remove events
|
||
|
BrdgCtlCommonNotify( pAdapt, Type, 0, NULL, NULL );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
BrdgCtlCommonNotify( pAdapt, Type, sizeof(BRIDGE_ADAPTER_INFO), BrdgCtlCopyAdapterInfo, pAdapt );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
NTSTATUS
|
||
|
BrdgCtlHandleIoDeviceControl(
|
||
|
IN PIRP Irp,
|
||
|
IN PFILE_OBJECT FileObject,
|
||
|
IN OUT PVOID Buffer,
|
||
|
IN ULONG InputBufferLength,
|
||
|
IN ULONG OutputBufferLength,
|
||
|
IN ULONG IoControlCode,
|
||
|
OUT PULONG Information
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This routine handles all Device-control requests.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Irp The IRP
|
||
|
FileObject The file object of the bridge
|
||
|
Buffer Input / output buffer
|
||
|
InputBufferLength Size of inbound data
|
||
|
OutputBufferLength Maximum allowable output data
|
||
|
IoControlCode The control code
|
||
|
|
||
|
Information Code-specific information returned
|
||
|
(usually the number of written bytes or
|
||
|
bytes required on overflow)
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
Status of the operation
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
NTSTATUS status = STATUS_SUCCESS;
|
||
|
|
||
|
*Information = 0;
|
||
|
|
||
|
switch (IoControlCode)
|
||
|
{
|
||
|
//
|
||
|
// Request for notification
|
||
|
//
|
||
|
case BRIDGE_IOCTL_REQUEST_NOTIFY:
|
||
|
{
|
||
|
PDEFERRED_NOTIFY pDeferred = NULL;
|
||
|
|
||
|
if( OutputBufferLength < sizeof(BRIDGE_NOTIFY_HEADER) + MAX_PACKET_SIZE )
|
||
|
{
|
||
|
status = STATUS_BUFFER_TOO_SMALL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// See if there is a pending notification waiting for an IRP
|
||
|
NdisAcquireSpinLock( &gNotificationsListLock );
|
||
|
|
||
|
if( BrdgQuerySingleListLength(&gNotificationsList) > 0L )
|
||
|
{
|
||
|
PBSINGLE_LIST_ENTRY pList = BrdgRemoveHeadSingleList(&gNotificationsList);
|
||
|
|
||
|
if( pList != NULL )
|
||
|
{
|
||
|
pDeferred = CONTAINING_RECORD( pList, DEFERRED_NOTIFY, List );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Should be impossible
|
||
|
SAFEASSERT(FALSE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
NdisReleaseSpinLock( &gNotificationsListLock );
|
||
|
|
||
|
if( pDeferred != NULL )
|
||
|
{
|
||
|
UINT SizeToCopy = sizeof(BRIDGE_NOTIFY_HEADER) + pDeferred->DataSize;
|
||
|
|
||
|
// We have a notification to return immediately
|
||
|
NdisMoveMemory( Buffer, &pDeferred->Header, SizeToCopy );
|
||
|
*Information = SizeToCopy;
|
||
|
|
||
|
// Free the holding structure
|
||
|
NdisFreeMemory( pDeferred, sizeof(DEFERRED_NOTIFY) + pDeferred->DataSize, 0 );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// No pending notification to send. queue the IRP for use later
|
||
|
status = BrdgCtlQueueAndPendIRP( Irp );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
//
|
||
|
// Request to be notified about all adapters
|
||
|
//
|
||
|
case BRIDGE_IOCTL_GET_ADAPTERS:
|
||
|
{
|
||
|
// Send a notification for each adapter
|
||
|
PADAPT pAdapt;
|
||
|
LOCK_STATE LockState;
|
||
|
|
||
|
NdisAcquireReadWriteLock( &gAdapterListLock, FALSE/*Read only*/, &LockState );
|
||
|
|
||
|
for( pAdapt = gAdapterList; pAdapt != NULL; pAdapt = pAdapt->Next )
|
||
|
{
|
||
|
BrdgCtlNotifyAdapterChange( pAdapt, BrdgNotifyEnumerateAdapters );
|
||
|
}
|
||
|
|
||
|
NdisReleaseReadWriteLock( &gAdapterListLock, &LockState );
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
//
|
||
|
// Request for an adapter's device name
|
||
|
//
|
||
|
case BRIDGE_IOCTL_GET_ADAPT_DEVICE_NAME:
|
||
|
{
|
||
|
if( InputBufferLength < sizeof(BRIDGE_ADAPTER_HANDLE) )
|
||
|
{
|
||
|
status = STATUS_BUFFER_TOO_SMALL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
PADAPT pAdapt = BrdgCtlValidateAcquireAdapter(*((PBRIDGE_ADAPTER_HANDLE)Buffer));
|
||
|
|
||
|
if( pAdapt == NULL )
|
||
|
{
|
||
|
// The handle passed in doesn't actually indicate an adapter
|
||
|
status = STATUS_INVALID_PARAMETER;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ULONG bytesToCopy;
|
||
|
|
||
|
// We need enough room to add a trailing NULL
|
||
|
if( OutputBufferLength < pAdapt->DeviceName.Length + sizeof(WCHAR) )
|
||
|
{
|
||
|
if( OutputBufferLength >= sizeof(WCHAR) )
|
||
|
{
|
||
|
bytesToCopy = OutputBufferLength - sizeof(WCHAR);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
bytesToCopy = 0L;
|
||
|
}
|
||
|
|
||
|
status = STATUS_BUFFER_OVERFLOW;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
bytesToCopy = pAdapt->DeviceName.Length;
|
||
|
}
|
||
|
|
||
|
if( bytesToCopy > 0L )
|
||
|
{
|
||
|
NdisMoveMemory( Buffer, pAdapt->DeviceName.Buffer, bytesToCopy );
|
||
|
}
|
||
|
|
||
|
// Put a trailing NULL WCHAR at the end
|
||
|
*((PWCHAR)((PUCHAR)Buffer + bytesToCopy)) = 0x0000;
|
||
|
|
||
|
// Tell the caller how many bytes we wrote / are needed
|
||
|
*Information = pAdapt->DeviceName.Length + sizeof(WCHAR);
|
||
|
|
||
|
BrdgReleaseAdapter(pAdapt);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
//
|
||
|
// Request for an adapter's friendly name
|
||
|
//
|
||
|
case BRIDGE_IOCTL_GET_ADAPT_FRIENDLY_NAME:
|
||
|
{
|
||
|
if( InputBufferLength < sizeof(BRIDGE_ADAPTER_HANDLE) )
|
||
|
{
|
||
|
status = STATUS_BUFFER_TOO_SMALL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
PADAPT pAdapt = BrdgCtlValidateAcquireAdapter(*((PBRIDGE_ADAPTER_HANDLE)Buffer));
|
||
|
|
||
|
if( pAdapt == NULL )
|
||
|
{
|
||
|
// The handle passed in doesn't actually indicate an adapter
|
||
|
status = STATUS_INVALID_PARAMETER;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ULONG bytesToCopy;
|
||
|
|
||
|
// We need enough room to add a trailing NULL
|
||
|
if( OutputBufferLength < pAdapt->DeviceDesc.Length + sizeof(WCHAR) )
|
||
|
{
|
||
|
if( OutputBufferLength >= sizeof(WCHAR) )
|
||
|
{
|
||
|
bytesToCopy = OutputBufferLength - sizeof(WCHAR);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
bytesToCopy = 0L;
|
||
|
}
|
||
|
|
||
|
status = STATUS_BUFFER_OVERFLOW;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
bytesToCopy = pAdapt->DeviceDesc.Length;
|
||
|
}
|
||
|
|
||
|
if( bytesToCopy > 0L )
|
||
|
{
|
||
|
NdisMoveMemory( Buffer, pAdapt->DeviceDesc.Buffer, bytesToCopy );
|
||
|
}
|
||
|
|
||
|
// Put a trailing NULL WCHAR at the end
|
||
|
*((PWCHAR)((PUCHAR)Buffer + bytesToCopy)) = 0x0000;
|
||
|
|
||
|
// Tell the caller how many bytes we wrote / are needed
|
||
|
*Information = pAdapt->DeviceDesc.Length + sizeof(WCHAR);
|
||
|
|
||
|
BrdgReleaseAdapter(pAdapt);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
//
|
||
|
// Request to retrieve the bridge's MAC address
|
||
|
//
|
||
|
case BRIDGE_IOCTL_GET_MAC_ADDRESS:
|
||
|
{
|
||
|
if( OutputBufferLength < ETH_LENGTH_OF_ADDRESS )
|
||
|
{
|
||
|
status = STATUS_BUFFER_TOO_SMALL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if( ! BrdgMiniReadMACAddress((PUCHAR)Buffer) )
|
||
|
{
|
||
|
// We don't actually have a MAC address right now
|
||
|
// (shouldn't really be possible since the user-mode code would have
|
||
|
// to be making this request before we bound to any adapters)
|
||
|
status = STATUS_UNSUCCESSFUL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*Information = ETH_LENGTH_OF_ADDRESS;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
//
|
||
|
// Request to retrieve packet-handling statistics
|
||
|
//
|
||
|
case BRIDGE_IOCTL_GET_PACKET_STATS:
|
||
|
{
|
||
|
if( OutputBufferLength < sizeof(BRIDGE_PACKET_STATISTICS) )
|
||
|
{
|
||
|
status = STATUS_BUFFER_TOO_SMALL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
PBRIDGE_PACKET_STATISTICS pStats = (PBRIDGE_PACKET_STATISTICS)Buffer;
|
||
|
|
||
|
// These are only statistics and have no associated locks so just read them
|
||
|
// without protection
|
||
|
pStats->TransmittedFrames = gStatTransmittedFrames;
|
||
|
pStats->TransmittedErrorFrames = gStatTransmittedErrorFrames;
|
||
|
pStats->TransmittedBytes = gStatTransmittedBytes;
|
||
|
pStats->DirectedTransmittedFrames = gStatDirectedTransmittedFrames;
|
||
|
pStats->MulticastTransmittedFrames = gStatMulticastTransmittedFrames;
|
||
|
pStats->BroadcastTransmittedFrames = gStatBroadcastTransmittedFrames;
|
||
|
pStats->DirectedTransmittedBytes = gStatDirectedTransmittedBytes;
|
||
|
pStats->MulticastTransmittedBytes = gStatMulticastTransmittedBytes;
|
||
|
pStats->BroadcastTransmittedBytes = gStatBroadcastTransmittedBytes;
|
||
|
pStats->IndicatedFrames = gStatIndicatedFrames;
|
||
|
pStats->IndicatedDroppedFrames = gStatIndicatedDroppedFrames;
|
||
|
pStats->IndicatedBytes = gStatIndicatedBytes;
|
||
|
pStats->DirectedIndicatedFrames = gStatDirectedIndicatedFrames;
|
||
|
pStats->MulticastIndicatedFrames = gStatMulticastIndicatedFrames;
|
||
|
pStats->BroadcastIndicatedFrames = gStatBroadcastIndicatedFrames;
|
||
|
pStats->DirectedIndicatedBytes = gStatDirectedIndicatedBytes;
|
||
|
pStats->MulticastIndicatedBytes = gStatMulticastIndicatedBytes;
|
||
|
pStats->BroadcastIndicatedBytes = gStatBroadcastIndicatedBytes;
|
||
|
pStats->ReceivedFrames = gStatReceivedFrames;
|
||
|
pStats->ReceivedBytes = gStatReceivedBytes;
|
||
|
pStats->ReceivedCopyFrames = gStatReceivedCopyFrames;
|
||
|
pStats->ReceivedCopyBytes = gStatReceivedCopyBytes;
|
||
|
pStats->ReceivedNoCopyFrames = gStatReceivedNoCopyFrames;
|
||
|
pStats->ReceivedNoCopyBytes = gStatReceivedNoCopyBytes;
|
||
|
|
||
|
*Information = sizeof(BRIDGE_PACKET_STATISTICS);
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
//
|
||
|
// Request to retrieve packet-handling statistics for an adapter
|
||
|
//
|
||
|
case BRIDGE_IOCTL_GET_ADAPTER_PACKET_STATS:
|
||
|
{
|
||
|
if( (InputBufferLength < sizeof(BRIDGE_ADAPTER_HANDLE)) ||
|
||
|
(OutputBufferLength < sizeof(BRIDGE_ADAPTER_PACKET_STATISTICS)) )
|
||
|
{
|
||
|
status = STATUS_BUFFER_TOO_SMALL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
PADAPT pAdapt = BrdgCtlValidateAcquireAdapter(*((PBRIDGE_ADAPTER_HANDLE)Buffer));
|
||
|
|
||
|
if( pAdapt == NULL )
|
||
|
{
|
||
|
// The handle passed in doesn't actually indicate an adapter
|
||
|
status = STATUS_INVALID_PARAMETER;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
PBRIDGE_ADAPTER_PACKET_STATISTICS pStats = (PBRIDGE_ADAPTER_PACKET_STATISTICS)Buffer;
|
||
|
|
||
|
// These are only statistics and have no associated locks so just read them
|
||
|
// without protection
|
||
|
pStats->SentFrames = pAdapt->SentFrames;
|
||
|
pStats->SentBytes = pAdapt->SentBytes;
|
||
|
pStats->SentLocalFrames = pAdapt->SentLocalFrames;
|
||
|
pStats->SentLocalBytes = pAdapt->SentLocalBytes;
|
||
|
pStats->ReceivedFrames = pAdapt->ReceivedFrames;
|
||
|
pStats->ReceivedBytes = pAdapt->ReceivedBytes;
|
||
|
|
||
|
*Information = sizeof(BRIDGE_ADAPTER_PACKET_STATISTICS);
|
||
|
|
||
|
BrdgReleaseAdapter(pAdapt);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
//
|
||
|
// Request to retrieve buffer-handling statistics
|
||
|
//
|
||
|
case BRIDGE_IOCTL_GET_BUFFER_STATS:
|
||
|
{
|
||
|
if( OutputBufferLength < sizeof(BRIDGE_BUFFER_STATISTICS) )
|
||
|
{
|
||
|
status = STATUS_BUFFER_TOO_SMALL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
BrdgBufGetStatistics((PBRIDGE_BUFFER_STATISTICS)Buffer);
|
||
|
|
||
|
*Information = sizeof(BRIDGE_BUFFER_STATISTICS);
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
//
|
||
|
// Request to alter the packet-retention policy
|
||
|
//
|
||
|
case BRIDGE_IOCTL_RETAIN_PACKETS:
|
||
|
case BRIDGE_IOCTL_NO_RETAIN_PACKETS:
|
||
|
{
|
||
|
// This global flag is not protected by any lock
|
||
|
gRetainNICPackets = (BOOLEAN)(IoControlCode == BRIDGE_IOCTL_RETAIN_PACKETS);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
//
|
||
|
// Request to retrieve the contents of the forwarding table for
|
||
|
// a particular adapter
|
||
|
//
|
||
|
case BRIDGE_IOCTL_GET_TABLE_ENTRIES:
|
||
|
{
|
||
|
if( InputBufferLength < sizeof(BRIDGE_ADAPTER_HANDLE) )
|
||
|
{
|
||
|
status = STATUS_BUFFER_TOO_SMALL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
PADAPT pAdapt = BrdgCtlValidateAcquireAdapter(*((PBRIDGE_ADAPTER_HANDLE)Buffer));
|
||
|
|
||
|
if( pAdapt == NULL )
|
||
|
{
|
||
|
// The handle passed in doesn't actually indicate an adapter
|
||
|
status = STATUS_INVALID_PARAMETER;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ULONG ReqdBytes;
|
||
|
|
||
|
// Try to read the contents of the forwarding table for this adapter
|
||
|
ReqdBytes = BrdgTblReadTable( pAdapt, Buffer, OutputBufferLength );
|
||
|
|
||
|
if( ReqdBytes > OutputBufferLength )
|
||
|
{
|
||
|
status = STATUS_BUFFER_OVERFLOW;
|
||
|
}
|
||
|
|
||
|
*Information = ReqdBytes;
|
||
|
|
||
|
BrdgReleaseAdapter(pAdapt);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case BRIDGE_IOCTL_GET_ADAPTER_STA_INFO:
|
||
|
{
|
||
|
if( gDisableSTA )
|
||
|
{
|
||
|
// Can't collect STA information when it's not running!
|
||
|
status = STATUS_INVALID_PARAMETER;
|
||
|
}
|
||
|
else if( InputBufferLength < sizeof(BRIDGE_ADAPTER_HANDLE) ||
|
||
|
OutputBufferLength < sizeof(BRIDGE_STA_ADAPTER_INFO) )
|
||
|
{
|
||
|
status = STATUS_BUFFER_TOO_SMALL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
PADAPT pAdapt = BrdgCtlValidateAcquireAdapter(*((PBRIDGE_ADAPTER_HANDLE)Buffer));
|
||
|
|
||
|
if( pAdapt == NULL )
|
||
|
{
|
||
|
// The handle passed in doesn't actually indicate an adapter
|
||
|
status = STATUS_INVALID_PARAMETER;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
BrdgSTAGetAdapterSTAInfo( pAdapt, (PBRIDGE_STA_ADAPTER_INFO)Buffer );
|
||
|
*Information = sizeof(BRIDGE_STA_ADAPTER_INFO);
|
||
|
BrdgReleaseAdapter(pAdapt);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case BRIDGE_IOCTL_GET_GLOBAL_STA_INFO:
|
||
|
{
|
||
|
if( gDisableSTA )
|
||
|
{
|
||
|
// Can't collect STA information when it's not running!
|
||
|
status = STATUS_INVALID_PARAMETER;
|
||
|
}
|
||
|
else if( OutputBufferLength < sizeof(BRIDGE_STA_GLOBAL_INFO) )
|
||
|
{
|
||
|
status = STATUS_BUFFER_TOO_SMALL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
BrdgSTAGetSTAInfo( (PBRIDGE_STA_GLOBAL_INFO)Buffer );
|
||
|
*Information = sizeof(BRIDGE_STA_GLOBAL_INFO);
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
status = STATUS_INVALID_PARAMETER;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
// ===========================================================================
|
||
|
//
|
||
|
// PRIVATE FUNCTIONS
|
||
|
//
|
||
|
// ===========================================================================
|
||
|
|
||
|
VOID
|
||
|
BrdgCtlCopyAdapterInfo(
|
||
|
OUT PBRIDGE_ADAPTER_INFO pInfo,
|
||
|
IN PADAPT pAdapt
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Helper function for BrdgCtlCommonNotify. Copies data about an adapter
|
||
|
to a buffer.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pInfo Structure to fill with information
|
||
|
pAdapt Adapter to copy from
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
LOCK_STATE LockState;
|
||
|
|
||
|
// Take a read lock on gAdapterCharacteristicsLock to ensure that all these
|
||
|
// are consistent
|
||
|
NdisAcquireReadWriteLock( &gAdapterCharacteristicsLock, FALSE/*Read only*/, &LockState );
|
||
|
|
||
|
pInfo->LinkSpeed = pAdapt->LinkSpeed;
|
||
|
pInfo->MediaState = pAdapt->MediaState;
|
||
|
pInfo->State = pAdapt->State;
|
||
|
|
||
|
NdisReleaseReadWriteLock( &gAdapterCharacteristicsLock, &LockState );
|
||
|
|
||
|
// These values don't change after assignment, and so need no lock.
|
||
|
ETH_COPY_NETWORK_ADDRESS( pInfo->MACAddress, pAdapt->MACAddr );
|
||
|
pInfo->PhysicalMedium = pAdapt->PhysicalMedium;
|
||
|
}
|
||
|
|
||
|
PADAPT
|
||
|
BrdgCtlValidateAcquireAdapter(
|
||
|
IN BRIDGE_ADAPTER_HANDLE Handle
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Checks to ensure that a BRIDGE_ADAPTER_HANDLE passed from user-mode code
|
||
|
actually corresponds to an adapter still in our list.
|
||
|
|
||
|
If the adapter is found, its refcount is incremented.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Handle A handle from user-mode code
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
The handle recast as a PADAPT, or NULL if the adapter could not be found.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
PADAPT pAdapt = (PADAPT)Handle, anAdapt;
|
||
|
LOCK_STATE LockState;
|
||
|
|
||
|
NdisAcquireReadWriteLock( &gAdapterListLock, FALSE/*Read only*/, &LockState );
|
||
|
|
||
|
for( anAdapt = gAdapterList; anAdapt != NULL; anAdapt = anAdapt->Next )
|
||
|
{
|
||
|
if( anAdapt == pAdapt )
|
||
|
{
|
||
|
// The adapter is in the list. Increment its refcount inside the lock
|
||
|
// and return
|
||
|
BrdgAcquireAdapterInLock( pAdapt );
|
||
|
NdisReleaseReadWriteLock( &gAdapterListLock, &LockState );
|
||
|
return pAdapt;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
NdisReleaseReadWriteLock( &gAdapterListLock, &LockState );
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
BrdgCtlCancelIoctl(
|
||
|
PDEVICE_OBJECT DeviceObject,
|
||
|
PIRP pIrp
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
IRP Cancellation function
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
DeviceObject The bridge's device-object
|
||
|
|
||
|
pIrp The IRP to be cancelled
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
none.
|
||
|
|
||
|
Environment:
|
||
|
|
||
|
Invoked with the cancel spin-lock held by the I/O manager.
|
||
|
It is this routine's responsibility to release the lock.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
IoReleaseCancelSpinLock(pIrp->CancelIrql);
|
||
|
|
||
|
// Take the IRP off our list
|
||
|
NdisAcquireSpinLock( &gIRPListLock );
|
||
|
RemoveEntryList( &pIrp->Tail.Overlay.ListEntry );
|
||
|
InitializeListHead( &pIrp->Tail.Overlay.ListEntry );
|
||
|
NdisReleaseSpinLock( &gIRPListLock );
|
||
|
|
||
|
// Complete the IRP
|
||
|
pIrp->IoStatus.Status = STATUS_CANCELLED;
|
||
|
pIrp->IoStatus.Information = 0;
|
||
|
IoCompleteRequest( pIrp, IO_NO_INCREMENT );
|
||
|
}
|
||
|
|
||
|
|
||
|
NTSTATUS
|
||
|
BrdgCtlQueueAndPendIRP(
|
||
|
IN PIRP pIrp
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Safely inserts an IRP into our queue of pending IRPs.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pIrp The IRP to queue
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
The status to return from IRP processing (can be STATUS_CANCELLED
|
||
|
if the IRP was cancelled right after we received it. Otherwise
|
||
|
is STATUS_PENDING so caller knows the IRP is pending).
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
KIRQL CancelIrql;
|
||
|
|
||
|
// If the IRP has already been cancelled, forget it.
|
||
|
IoAcquireCancelSpinLock( &CancelIrql );
|
||
|
NdisDprAcquireSpinLock( &gIRPListLock );
|
||
|
|
||
|
if ( pIrp->Cancel )
|
||
|
{
|
||
|
NdisDprReleaseSpinLock( &gIRPListLock );
|
||
|
IoReleaseCancelSpinLock(CancelIrql);
|
||
|
return STATUS_CANCELLED;
|
||
|
}
|
||
|
|
||
|
// Queue the IRP
|
||
|
InsertTailList( &gIRPList, &pIrp->Tail.Overlay.ListEntry);
|
||
|
|
||
|
// Install our cancel-routine
|
||
|
IoMarkIrpPending( pIrp );
|
||
|
IoSetCancelRoutine( pIrp, BrdgCtlCancelIoctl );
|
||
|
|
||
|
NdisDprReleaseSpinLock( &gIRPListLock );
|
||
|
IoReleaseCancelSpinLock( CancelIrql );
|
||
|
|
||
|
return STATUS_PENDING;
|
||
|
}
|
||
|
|
||
|
PIRP
|
||
|
BrdgCtlDequeuePendingIRP()
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Safely dequeues an IRP on our pending list for use to communicate
|
||
|
a notification
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
A dequeued IRP if one was available; NULL otherwise.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
PLIST_ENTRY Link;
|
||
|
PIRP pIrp = NULL;
|
||
|
|
||
|
while( pIrp == NULL )
|
||
|
{
|
||
|
NdisAcquireSpinLock( &gIRPListLock );
|
||
|
|
||
|
if ( IsListEmpty(&gIRPList) )
|
||
|
{
|
||
|
NdisReleaseSpinLock( &gIRPListLock );
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
// Dequeue a pending IRP
|
||
|
Link = RemoveHeadList( &gIRPList );
|
||
|
pIrp = CONTAINING_RECORD( Link, IRP, Tail.Overlay.ListEntry );
|
||
|
|
||
|
// After this call, it is safe for our cancel routine to call
|
||
|
// RemoveHeadList again on this IRP
|
||
|
InitializeListHead( Link );
|
||
|
|
||
|
// Make the IRP uncancellable so we can complete it.
|
||
|
if( IoSetCancelRoutine( pIrp, NULL ) == NULL )
|
||
|
{
|
||
|
// This IRP must have already been cancelled but our cancel
|
||
|
// routine hasn't gotten control yet. Loop again to get a
|
||
|
// usable IRP.
|
||
|
pIrp = NULL;
|
||
|
}
|
||
|
|
||
|
NdisReleaseSpinLock( &gIRPListLock );
|
||
|
}
|
||
|
|
||
|
return pIrp;
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
BrdgCtlCancelPendingIRPs()
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Cancels all pending IRPs
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
PIRP pIrp;
|
||
|
|
||
|
NdisAcquireSpinLock( &gIRPListLock );
|
||
|
|
||
|
while ( !IsListEmpty(&gIRPList) )
|
||
|
{
|
||
|
//
|
||
|
// Take the next IRP off the list
|
||
|
//
|
||
|
pIrp = CONTAINING_RECORD( gIRPList.Flink, IRP, Tail.Overlay.ListEntry );
|
||
|
RemoveEntryList( &pIrp->Tail.Overlay.ListEntry );
|
||
|
|
||
|
// Clean up the ListEntry in case our cancel routine gets called
|
||
|
InitializeListHead( &pIrp->Tail.Overlay.ListEntry );
|
||
|
|
||
|
// Cancel it if necessary
|
||
|
if ( IoSetCancelRoutine( pIrp, NULL ) != NULL )
|
||
|
{
|
||
|
// Our cancel routine will not be called. Complete this IRP ourselves.
|
||
|
NdisReleaseSpinLock( &gIRPListLock );
|
||
|
|
||
|
// Complete the IRP
|
||
|
pIrp->IoStatus.Status = STATUS_CANCELLED;
|
||
|
pIrp->IoStatus.Information = 0;
|
||
|
IoCompleteRequest( pIrp, IO_NO_INCREMENT );
|
||
|
|
||
|
// Resume emptying the list
|
||
|
NdisAcquireSpinLock( &gIRPListLock );
|
||
|
}
|
||
|
// else our cancel routine will be called for this IRP
|
||
|
}
|
||
|
|
||
|
NdisReleaseSpinLock( &gIRPListLock );
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
BrdgCtlReleaseQueuedNotifications()
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Frees any queued notifications
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
BSINGLE_LIST_HEAD list;
|
||
|
|
||
|
NdisAcquireSpinLock( &gNotificationsListLock );
|
||
|
|
||
|
// Grab a copy of the whole list head
|
||
|
list = gNotificationsList;
|
||
|
|
||
|
// Set the list head back to empty
|
||
|
BrdgInitializeSingleList( &gNotificationsList );
|
||
|
|
||
|
NdisReleaseSpinLock( &gNotificationsListLock );
|
||
|
|
||
|
// Now free all the items on the list
|
||
|
while( BrdgQuerySingleListLength(&list) > 0L )
|
||
|
{
|
||
|
PDEFERRED_NOTIFY pDeferred = NULL;
|
||
|
PBSINGLE_LIST_ENTRY pList = BrdgRemoveHeadSingleList(&list);
|
||
|
|
||
|
if( pList != NULL )
|
||
|
{
|
||
|
pDeferred = CONTAINING_RECORD( pList, DEFERRED_NOTIFY, List );
|
||
|
NdisFreeMemory( pDeferred, sizeof(DEFERRED_NOTIFY) + pDeferred->DataSize, 0 );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Should be impossible
|
||
|
SAFEASSERT(FALSE);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
BrdgCtlCleanup()
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Cleanup routine; called at shutdown
|
||
|
|
||
|
This function is guaranteed to be called exactly once
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
BrdgCtlCancelPendingIRPs();
|
||
|
BrdgCtlReleaseQueuedNotifications();
|
||
|
}
|