|
|
/*++
Copyright(c) 1999-2000 Microsoft Corporation
Module Name:
brdgtbl.c
Abstract:
Ethernet MAC level bridge. MAC Table section
Author:
Mark Aiken (original bridge by Jameel Hyder)
Environment:
Kernel mode driver
Revision History:
Feb 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 <netevent.h>
#include "bridge.h"
#include "brdgtbl.h"
// ===========================================================================
//
// PRIVATE DECLARATIONS
//
// ===========================================================================
// Default age at which entries are removed from table
#define DEFAULT_MAX_TBL_AGE (300 * 1000) // 5 minutes in milliseconds
//
// Default cap on forwarding table size
//
#define DEFAULT_MAX_TBL_MEMORY (500 * 1024) // 500K in bytes
//
// Registry values that hold our config values
//
const PWCHAR gMaxTableMemoryParameterName = L"MaxTableMemory";
// Structure of a table entry
typedef struct _MAC_FWDTABLE_ENTRY {
HASH_TABLE_ENTRY hte; PADAPT pAdapt;
} MAC_FWDTABLE_ENTRY, *PMAC_FWDTABLE_ENTRY;
// ===========================================================================
//
// GLOBALS
//
// ===========================================================================
// The MAC forwarding table
PHASH_TABLE gMACForwardingTable;
// Number of hash buckets (needs to be N^2 for the hash function to work)
#define NUM_HASH_BUCKETS 256
// ===========================================================================
//
// PRIVATE FUNCTIONS
//
// ===========================================================================
// These would all be inlines except that we have to pass pointers to them
//
// Our hash function for Ethernet addresses. Uses the lower bits of byte #4.
//
// This hash function requires NUM_HASH_BUCKETS buckets; don't change this
// without updating the number of hash buckets available.
//
ULONG BrdgTblHashAddress( IN PUCHAR pAddr ) { return *((pAddr)+ETH_LENGTH_OF_ADDRESS-2) & (NUM_HASH_BUCKETS-1); }
//
// Returns TRUE if the pAdapt field in two entries match
//
BOOLEAN BrdgTblEntriesMatch( IN PHASH_TABLE_ENTRY pEntry, IN PVOID pAdapt ) { return (BOOLEAN)(((PMAC_FWDTABLE_ENTRY)pEntry)->pAdapt == (PADAPT)pAdapt); }
//
// Copies the MAC address from a table entry to a data buffer.
//
VOID BrdgTblCopyEntries( PHASH_TABLE_ENTRY pEntry, PUCHAR pDest ) { PMAC_FWDTABLE_ENTRY pMACEntry = (PMAC_FWDTABLE_ENTRY)pEntry;
// The MAC address is the key. Copy it to the target buffer.
ETH_COPY_NETWORK_ADDRESS( pDest, pMACEntry->hte.key ); }
// ===========================================================================
//
// PUBLIC FUNCTIONS
//
// ===========================================================================
NTSTATUS BrdgTblDriverInit() /*++
Routine Description:
Load-time initialization function
Arguments:
None
Return Value:
Status of initialization. A return code != STATUS_SUCCESS aborts driver load.
--*/ { NTSTATUS NtStatus; ULONG MaxMemory, MaxEntries;
NtStatus = BrdgReadRegDWord( &gRegistryPath, gMaxTableMemoryParameterName, &MaxMemory );
if( NtStatus != STATUS_SUCCESS ) { DBGPRINT(GENERAL, ("Failed to read MaxTableMemory value: %08x\n", NtStatus)); MaxMemory = DEFAULT_MAX_TBL_MEMORY; DBGPRINT(GENERAL, ( "Using DEFAULT maximum memory of %i\n", MaxMemory )); }
MaxEntries = MaxMemory / sizeof(MAC_FWDTABLE_ENTRY); DBGPRINT(GENERAL, ( "Forwarding table cap set at %i entries (%iK of memory)\n", MaxEntries, MaxMemory / 1024 ));
gMACForwardingTable = BrdgHashCreateTable( BrdgTblHashAddress, NUM_HASH_BUCKETS, sizeof(MAC_FWDTABLE_ENTRY), MaxEntries, DEFAULT_MAX_TBL_AGE, DEFAULT_MAX_TBL_AGE, ETH_LENGTH_OF_ADDRESS );
if( gMACForwardingTable == NULL ) { DBGPRINT(FWD, ("FAILED TO ALLOCATE MAC TABLE!\n")); return STATUS_INSUFFICIENT_RESOURCES; } else { return STATUS_SUCCESS; } }
//
// Creates a new table entry associating the given MAC address with the given
// adapter, or refreshes an existing entry.
//
VOID BrdgTblNoteAddress( IN PUCHAR pAddr, IN PADAPT pAdapt ) /*++
Routine Description:
Creates a new table entry associating the given MAC address with the given adapter, or refreshes an existing entry.
Arguments:
pAddr The MAC address to look up pAdapt The adapter to associate it with
Return Value:
None
--*/ { PMAC_FWDTABLE_ENTRY pEntry; BOOLEAN bIsNewEntry; LOCK_STATE LockState;
// Refuse to record non-unicast addresses
if( ETH_IS_MULTICAST(pAddr) ) { THROTTLED_DBGPRINT(FWD, ("## BRIDGE ## Not recording multicast address in BrdgTblNoteAddress\n")); return; }
pEntry = (PMAC_FWDTABLE_ENTRY)BrdgHashRefreshOrInsert( gMACForwardingTable, pAddr, &bIsNewEntry, &LockState);
if( pEntry != NULL ) { // Regardless of whether or not this is a new table entry or an existing one,
// just cram in the adapter pointer with an interlocked instruction.
InterlockedExchangePointer( &pEntry->pAdapt, pAdapt );
// Since the function came back != NULL, we must release the table lock.
NdisReleaseReadWriteLock( &gMACForwardingTable->tableLock, &LockState ); } }
PADAPT BrdgTblFindTargetAdapter( IN PUCHAR pAddr ) /*++
Routine Description:
Locates the adapter corresponding to a particular MAC address.
If an adapter is found, this function returns a PADAPT pointer after having INCREMENTED THE REFCOUNT for that adapter. This is to ensure that the adapter is not unbound until the caller is done using it. The caller should be sure to decrement the PADAPT's refcount when it is done using the pointer.
Arguments:
pAddr The MAC address to look up
Return Value:
A pointer to the ADAPT structure describing the adapter associated with the given MAC address, with its refcount INCREMENTED, or NULL if an entry associating the given MAC address to an adapter was not found.
--*/ { PMAC_FWDTABLE_ENTRY pEntry; LOCK_STATE LockState; PADAPT pAdapt = NULL;
pEntry = (PMAC_FWDTABLE_ENTRY)BrdgHashFindEntry( gMACForwardingTable, pAddr, &LockState );
if( pEntry != NULL ) { // Read this once since it can be changed even while we hold the RW lock
pAdapt = pEntry->pAdapt; SAFEASSERT( pAdapt != NULL );
//
// Increment this adapter's refcount while inside the RW lock for the table.
// This lets us close a race condition window for unbinding the adapter;
// the caller will hang on to the returned PADAPT after we return, leading
// to problems if the adapter is unbound before our caller is done using
// the PADAPT structure.
//
BrdgAcquireAdapterInLock( pAdapt );
// Release the table lock
NdisReleaseReadWriteLock( &gMACForwardingTable->tableLock, &LockState ); }
return pAdapt; }
//
// This function cleans all the adapters from the tables (this is in the case of a GPO changing
// our bridging settings)
//
VOID BrdgTblScrubAllAdapters() { PADAPT pAdapt = NULL; LOCK_STATE LockStateMACTable; LOCK_STATE LockStateAdapterList;
//
// We don't want the table to be modified while we're doing this, and we also don't want an adapter
// to go away while we're enumerating the list of adapters.
//
NdisAcquireReadWriteLock(&gMACForwardingTable->tableLock, FALSE /*Read Only*/, &LockStateMACTable); NdisAcquireReadWriteLock(&gAdapterListLock, FALSE /*Read Only*/, &LockStateAdapterList);
for( pAdapt = gAdapterList; pAdapt != NULL; pAdapt = pAdapt->Next ) { // Scrub adapter from the table.
BrdgTblScrubAdapter(pAdapt); }
NdisReleaseReadWriteLock(&gAdapterListLock, &LockStateAdapterList); NdisReleaseReadWriteLock(&gMACForwardingTable->tableLock, &LockStateMACTable); }
VOID BrdgTblCleanup() /*++
Routine Description:
Unload-time orderly shutdown
This function is guaranteed to be called exactly once
Arguments:
None
Return Value:
None
--*/ { SAFEASSERT( gMACForwardingTable != NULL ); BrdgHashFreeHashTable( gMACForwardingTable ); gMACForwardingTable = NULL; }
|