You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2990 lines
80 KiB
2990 lines
80 KiB
/*++
|
|
|
|
Copyright(c) 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
brdgsta.c
|
|
|
|
Abstract:
|
|
|
|
Ethernet MAC level bridge.
|
|
Spanning Tree Algorithm section
|
|
|
|
Author:
|
|
|
|
Mark Aiken
|
|
|
|
Environment:
|
|
|
|
Kernel mode driver
|
|
|
|
Revision History:
|
|
|
|
June 2000 - Original version
|
|
|
|
--*/
|
|
|
|
#define NDIS_MINIPORT_DRIVER
|
|
#define NDIS50_MINIPORT 1
|
|
#define NDIS_WDM 1
|
|
|
|
#pragma warning( push, 3 )
|
|
#include <ndis.h>
|
|
#pragma warning( pop )
|
|
|
|
#include "bridge.h"
|
|
#include "brdgsta.h"
|
|
|
|
#include "brdgmini.h"
|
|
#include "brdgprot.h"
|
|
#include "brdgbuf.h"
|
|
#include "brdgfwd.h"
|
|
#include "brdgtbl.h"
|
|
#include "brdgctl.h"
|
|
|
|
// ===========================================================================
|
|
//
|
|
// TYPES
|
|
//
|
|
// ===========================================================================
|
|
|
|
// BPDU types
|
|
typedef enum
|
|
{
|
|
ConfigBPDU,
|
|
TopologyChangeBPDU
|
|
} BPDU_TYPE;
|
|
|
|
// ===========================================================================
|
|
//
|
|
// CONSTANTS
|
|
//
|
|
// ===========================================================================
|
|
|
|
// These values measured in STA units (1/256ths of a second)
|
|
#define DEFAULT_MAX_AGE (8 * 256) // 8 seconds
|
|
#define DEFAULT_HELLO_TIME (2 * 256) // 2 seconds
|
|
#define DEFAULT_FORWARD_DELAY (5 * 256) // 5 seconds
|
|
#define MESSAGE_AGE_INCREMENT 1 // 1 STA time unit
|
|
|
|
// These values measured in milliseconds
|
|
#define HOLD_TIMER_PERIOD (1 * 1000) // 1 second in milliseconds
|
|
|
|
// Normal size, in bytes, of a full-size (non-TCN) STA packet
|
|
#define CONFIG_BPDU_PACKET_SIZE 35
|
|
|
|
// Size, in bytes, of a TCN STA packet
|
|
#define TCN_BPDU_PACKET_SIZE 4
|
|
|
|
// The name of the registry entry that causes the STA to be disabled
|
|
const PWCHAR gDisableSTAParameterName = L"DisableSTA";
|
|
|
|
// The size of an 802.3 header with LLC
|
|
#define _802_3_HEADER_SIZE 17
|
|
|
|
// Value to be added to port IDs; must leave the bottom byte clear to store
|
|
// actual port ID
|
|
#define PORT_PRIORITY 0x8000
|
|
|
|
// ===========================================================================
|
|
//
|
|
// STRUCTURES
|
|
//
|
|
// ===========================================================================
|
|
|
|
//
|
|
// This structure holds the information for a complete BPDU (although
|
|
// it is not laid out as the BPDU is actually transmitted on the wire)
|
|
//
|
|
typedef struct _CONFIG_BPDU
|
|
{
|
|
BPDU_TYPE Type;
|
|
UCHAR RootID[BRIDGE_ID_LEN];
|
|
PATH_COST RootCost;
|
|
UCHAR BridgeID[BRIDGE_ID_LEN];
|
|
PORT_ID PortID;
|
|
STA_TIME MessageAge;
|
|
STA_TIME MaxAge;
|
|
STA_TIME HelloTime;
|
|
STA_TIME ForwardDelay;
|
|
BOOLEAN bTopologyChangeAck;
|
|
BOOLEAN bTopologyChange;
|
|
} CONFIG_BPDU, *PCONFIG_BPDU;
|
|
|
|
// ===========================================================================
|
|
//
|
|
// GLOBALS
|
|
//
|
|
// ===========================================================================
|
|
|
|
// Global spin lock protects all STA data accesses (for data stored in adapters
|
|
// as well as globals)
|
|
NDIS_SPIN_LOCK gSTALock;
|
|
|
|
// The bridge we believe is the root bridge
|
|
UCHAR gDesignatedRootID[BRIDGE_ID_LEN];
|
|
|
|
// Our own unique ID
|
|
UCHAR gOurID[BRIDGE_ID_LEN];
|
|
|
|
// Whether our ID has been set yet
|
|
BOOLEAN gHaveID = FALSE;
|
|
|
|
// Our cost to reach the root
|
|
PATH_COST gRootCost = 0;
|
|
|
|
// Our root port (adapter)
|
|
PADAPT gRootAdapter = NULL;
|
|
|
|
// Whether we have detected a topology change
|
|
BOOLEAN gTopologyChangeDetected = FALSE;
|
|
|
|
// Whether we tell other bridges that the topology has changed
|
|
BOOLEAN gTopologyChange = FALSE;
|
|
|
|
// Current bridge maximum message age
|
|
STA_TIME gMaxAge = DEFAULT_MAX_AGE;
|
|
|
|
// Current bridge Hello time
|
|
STA_TIME gHelloTime = DEFAULT_HELLO_TIME;
|
|
|
|
// Current bridge forward delay
|
|
STA_TIME gForwardDelay = DEFAULT_FORWARD_DELAY;
|
|
|
|
// Every adapter must have a unique ID number, but there is no requirement that
|
|
// that number be unique over the lifetime of the bridge. This array is used as
|
|
// a bitfield that records which IDs are in use.
|
|
ULONG gUsedPortIDs[MAX_ADAPTERS / sizeof(ULONG) / 8];
|
|
|
|
//
|
|
// Timers
|
|
//
|
|
BRIDGE_TIMER gTopologyChangeTimer;
|
|
BRIDGE_TIMER gTopologyChangeNotificationTimer;
|
|
BRIDGE_TIMER gHelloTimer;
|
|
|
|
// TRUE if the STA is disabled for the lifetime of the bridge
|
|
BOOLEAN gDisableSTA = FALSE;
|
|
|
|
// ===========================================================================
|
|
//
|
|
// PRIVATE PROTOTYPES
|
|
//
|
|
// ===========================================================================
|
|
|
|
VOID
|
|
BrdgSTARootSelection();
|
|
|
|
VOID
|
|
BrdgSTADesignatedPortSelection();
|
|
|
|
BOOLEAN
|
|
BrdgSTAPortStateSelection();
|
|
|
|
VOID
|
|
BrdgSTAGenerateConfigBPDUs();
|
|
|
|
VOID
|
|
BrdgSTABecomeDesignatedPort(
|
|
IN PADAPT pAdapt
|
|
);
|
|
|
|
VOID
|
|
BrdgSTAProcessTCNBPDU(
|
|
IN PADAPT pAdapt
|
|
);
|
|
|
|
VOID
|
|
BrdgSTAProcessConfigBPDU(
|
|
IN PADAPT pAdapt,
|
|
IN PCONFIG_BPDU pbpdu
|
|
);
|
|
|
|
BOOLEAN
|
|
BrdgSTATopologyChangeDetected();
|
|
|
|
VOID
|
|
BrdgSTACopyFromPacketToBuffer(
|
|
OUT PUCHAR pPacketOut,
|
|
IN ULONG BufferSize,
|
|
OUT PULONG pWrittenCount,
|
|
IN PNDIS_PACKET pPacketIn
|
|
);
|
|
|
|
VOID
|
|
BrdgSTADeferredSetAdapterState(
|
|
IN PVOID Arg
|
|
);
|
|
|
|
VOID
|
|
BrdgSTAHelloTimerExpiry(
|
|
IN PVOID Unused
|
|
);
|
|
|
|
VOID
|
|
BrdgSTAMessageAgeTimerExpiry(
|
|
IN PVOID Context
|
|
);
|
|
|
|
VOID
|
|
BrdgSTAForwardDelayTimerExpiry(
|
|
IN PVOID Context
|
|
);
|
|
|
|
VOID
|
|
BrdgSTATopologyChangeNotificationTimerExpiry(
|
|
IN PVOID Unused
|
|
);
|
|
|
|
VOID
|
|
BrdgSTATopologyChangeTimerExpiry(
|
|
IN PVOID Unused
|
|
);
|
|
|
|
VOID
|
|
BrdgSTAHoldTimerExpiry(
|
|
IN PVOID Context
|
|
);
|
|
|
|
VOID
|
|
BrdgSTATransmitTCNPacket();
|
|
|
|
VOID
|
|
BrdgSTASetAdapterState(
|
|
IN PADAPT pAdapt,
|
|
IN PORT_STATE NewState
|
|
);
|
|
|
|
// ===========================================================================
|
|
//
|
|
// INLINES / MACROS
|
|
//
|
|
// ===========================================================================
|
|
|
|
//
|
|
// Does a complete re-evaluation of STA info
|
|
//
|
|
// ASSUMES the caller has acquired gSTALock
|
|
//
|
|
__forceinline
|
|
VOID
|
|
BrdgSTAConfigUpdate()
|
|
{
|
|
BrdgSTARootSelection();
|
|
BrdgSTADesignatedPortSelection();
|
|
}
|
|
|
|
//
|
|
// Sets an NDIS timer using a time expressed in STA units
|
|
//
|
|
// No requirements on caller-held locks
|
|
//
|
|
__forceinline
|
|
VOID
|
|
BrdgSTASetTimerWithSTATime(
|
|
IN PBRIDGE_TIMER pTimer,
|
|
IN STA_TIME Time,
|
|
IN BOOLEAN bRecurring
|
|
)
|
|
{
|
|
BrdgSetTimer( pTimer, Time * 1000 / 256, bRecurring );
|
|
}
|
|
|
|
//
|
|
// Compares two bridge IDs.
|
|
//
|
|
// -1 : A < B
|
|
// 0 : A == B
|
|
// 1 : A > B
|
|
//
|
|
// No requirements on caller-held locks
|
|
//
|
|
__forceinline
|
|
INT
|
|
BrdgSTABridgeIDCmp(
|
|
IN PUCHAR pIDa,
|
|
IN PUCHAR pIDb
|
|
)
|
|
{
|
|
UINT i;
|
|
|
|
for( i = 0; i < BRIDGE_ID_LEN; i++ )
|
|
{
|
|
if( pIDa[i] > pIDb[i] )
|
|
{
|
|
return 1;
|
|
}
|
|
else if( pIDa[i] < pIDb[i] )
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Returns whether or not we currently believe ourselves to be the root
|
|
// bridge.
|
|
//
|
|
// ASSUMES the caller has acquired gSTALock
|
|
//
|
|
__forceinline
|
|
BOOLEAN
|
|
BrdgSTAWeAreRoot()
|
|
{
|
|
SAFEASSERT( gHaveID );
|
|
return (BOOLEAN)(BrdgSTABridgeIDCmp( gOurID, gDesignatedRootID ) == 0);
|
|
}
|
|
|
|
//
|
|
// Copies a bridge ID from pIDSrc to pIDDest.
|
|
//
|
|
// No requirements on caller-held locks
|
|
//
|
|
__forceinline
|
|
VOID
|
|
BrdgSTACopyID(
|
|
IN PUCHAR pIDDest,
|
|
IN PUCHAR pIDSrc
|
|
)
|
|
{
|
|
UINT i;
|
|
|
|
for( i = 0; i < BRIDGE_ID_LEN; i++ )
|
|
{
|
|
pIDDest[i] = pIDSrc[i];
|
|
}
|
|
}
|
|
|
|
//
|
|
// Calculates the STA path cost from an adapter's link speed.
|
|
// Follows IEEE 802.1D-1990 recommendation that the link cost be set
|
|
// to 1000 / (Speed in Mbits/s).
|
|
//
|
|
// No requirements on caller-held locks
|
|
//
|
|
__forceinline
|
|
PATH_COST
|
|
BrdgSTALinkCostFromLinkSpeed(
|
|
IN ULONG LinkSpeed
|
|
)
|
|
{
|
|
ULONG retVal;
|
|
|
|
// Link speed is reported in units of 100bps
|
|
if( LinkSpeed == 0L )
|
|
{
|
|
// Avoid div by zero and return very high path cost
|
|
DBGPRINT(STA, ("Zero link speed reported\n"));
|
|
retVal = 0xFFFFFFFF;
|
|
}
|
|
else
|
|
{
|
|
retVal = (PATH_COST)(10000000L / LinkSpeed);
|
|
}
|
|
|
|
if( retVal == 0L )
|
|
{
|
|
// STA spec calls for path costs to always be at least 1
|
|
return 1L;
|
|
}
|
|
else
|
|
{
|
|
return retVal;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Updates the global gTopologyChange flag. When this flag is set,
|
|
// we must use a forwarding table timeout value equal to the bridge's
|
|
// current forwarding delay. When the flag is not set, we use
|
|
// the table's default timeout value.
|
|
//
|
|
// ASSUMES the caller has acquired gSTALock
|
|
//
|
|
__forceinline
|
|
VOID
|
|
BrdgSTAUpdateTopologyChange(
|
|
IN BOOLEAN NewValue
|
|
)
|
|
{
|
|
if( gTopologyChange != NewValue )
|
|
{
|
|
gTopologyChange = NewValue;
|
|
|
|
if( gTopologyChange )
|
|
{
|
|
// Convert the forward delay to ms
|
|
BrdgTblSetTimeout( gForwardDelay * 1000 / 256 );
|
|
}
|
|
else
|
|
{
|
|
BrdgTblRevertTimeout();
|
|
}
|
|
}
|
|
}
|
|
|
|
// ===========================================================================
|
|
//
|
|
// PUBLIC FUNCTIONS
|
|
//
|
|
// ===========================================================================
|
|
|
|
VOID
|
|
BrdgSTAGetAdapterSTAInfo(
|
|
IN PADAPT pAdapt,
|
|
PBRIDGE_STA_ADAPTER_INFO pInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Copies STA information for a particular adapter into a structure
|
|
|
|
Called to collect information for user-mode components
|
|
|
|
Arguments:
|
|
|
|
pAdapt The adapter
|
|
pInfo Structure to receive STA information
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Locking Constraints:
|
|
|
|
Top-level function. Assumes no locks are held by caller.
|
|
|
|
--*/
|
|
{
|
|
NdisAcquireSpinLock( &gSTALock );
|
|
|
|
pInfo->ID = pAdapt->STAInfo.ID;
|
|
pInfo->PathCost = pAdapt->STAInfo.PathCost;
|
|
BrdgSTACopyID( pInfo->DesignatedRootID, pAdapt->STAInfo.DesignatedRootID );
|
|
pInfo->DesignatedCost = pAdapt->STAInfo.DesignatedCost;
|
|
BrdgSTACopyID( pInfo->DesignatedBridgeID, pAdapt->STAInfo.DesignatedBridgeID );
|
|
pInfo->DesignatedPort = pAdapt->STAInfo.DesignatedPort;
|
|
|
|
NdisReleaseSpinLock( &gSTALock );
|
|
}
|
|
|
|
VOID
|
|
BrdgSTAGetSTAInfo(
|
|
PBRIDGE_STA_GLOBAL_INFO pInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Copies global STA information into a structure
|
|
|
|
Called to collect information for user-mode components
|
|
|
|
Arguments:
|
|
|
|
pInfo Structure to receive STA information
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Locking Constraints:
|
|
|
|
Top-level function. Assumes no locks are held by caller.
|
|
|
|
--*/
|
|
{
|
|
NdisAcquireSpinLock( &gSTALock );
|
|
|
|
SAFEASSERT( gHaveID );
|
|
|
|
BrdgSTACopyID( pInfo->OurID, gOurID );
|
|
BrdgSTACopyID( pInfo->DesignatedRootID, gDesignatedRootID );
|
|
pInfo->RootCost = gRootCost;
|
|
pInfo->RootAdapter = (BRIDGE_ADAPTER_HANDLE)gRootAdapter;
|
|
pInfo->bTopologyChangeDetected = gTopologyChangeDetected;
|
|
pInfo->bTopologyChange = gTopologyChange;
|
|
pInfo->MaxAge = gMaxAge;
|
|
pInfo->HelloTime = gHelloTime;
|
|
pInfo->ForwardDelay = gForwardDelay;
|
|
|
|
NdisReleaseSpinLock( &gSTALock );
|
|
}
|
|
|
|
VOID
|
|
BrdgSTAUpdateAdapterCost(
|
|
IN PADAPT pAdapt,
|
|
ULONG LinkSpeed
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Updates an adapter's path cost to reflect an updated link speed
|
|
|
|
Arguments:
|
|
|
|
pAdapt The adapter
|
|
LinkSpeed The adapter's new link speed
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Locking Constraints:
|
|
|
|
Top-level function. Assumes no locks are held by caller.
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN bTransmitTCN = FALSE;
|
|
|
|
NdisAcquireSpinLock( &gSTALock );
|
|
|
|
if( pAdapt->bSTAInited )
|
|
{
|
|
pAdapt->STAInfo.PathCost = BrdgSTALinkCostFromLinkSpeed(LinkSpeed);
|
|
|
|
// Do a global re-evaluation of STA info
|
|
BrdgSTAConfigUpdate();
|
|
bTransmitTCN = BrdgSTAPortStateSelection();
|
|
}
|
|
else
|
|
{
|
|
DBGPRINT(STA, ("BrdgSTAUpdateAdapterCost() called with uninitialized adapter; ignoring!\n"));
|
|
}
|
|
|
|
NdisReleaseSpinLock( &gSTALock );
|
|
|
|
if( bTransmitTCN )
|
|
{
|
|
BrdgSTATransmitTCNPacket();
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
BrdgSTADriverInit()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Driver load-time initialization
|
|
|
|
Return Value:
|
|
|
|
Status of initialization
|
|
|
|
Locking Constraints:
|
|
|
|
Top-level function. Assumes no locks are held by caller.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS NtStatus;
|
|
UINT i;
|
|
ULONG regValue;
|
|
|
|
NdisAllocateSpinLock( &gSTALock );
|
|
|
|
BrdgInitializeTimer( &gTopologyChangeTimer, BrdgSTATopologyChangeTimerExpiry, NULL );
|
|
BrdgInitializeTimer( &gTopologyChangeNotificationTimer, BrdgSTATopologyChangeNotificationTimerExpiry, NULL );
|
|
BrdgInitializeTimer( &gHelloTimer, BrdgSTAHelloTimerExpiry, NULL );
|
|
|
|
// We haven't used any port IDs yet...
|
|
for( i = 0; i < sizeof(gUsedPortIDs) / sizeof(ULONG); i++ )
|
|
{
|
|
gUsedPortIDs[i] = 0;
|
|
}
|
|
|
|
// Check if we're supposed to disable the STA
|
|
NtStatus = BrdgReadRegDWord( &gRegistryPath, gDisableSTAParameterName, ®Value );
|
|
|
|
if( (NtStatus == STATUS_SUCCESS) &&
|
|
(regValue != 0L) )
|
|
{
|
|
gDisableSTA = TRUE;
|
|
DBGPRINT(STA, ("DISABLING SPANNING TREE ALGORITHM\n"));
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
BrdgSTADeferredInit(
|
|
IN PUCHAR pBridgeMACAddress
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Second initialization pass; called when we determine the bridge's
|
|
MAC address (which is needed for STA operations)
|
|
|
|
Arguments:
|
|
|
|
pBridgeMACAddress The bridge miniport's MAC address
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Locking Constraints:
|
|
|
|
Top-level function. Assumes no locks are held by caller.
|
|
|
|
--*/
|
|
{
|
|
UINT i;
|
|
|
|
// Our identifier consists of our MAC address preceeded with 0x8000
|
|
gOurID[0] = 0x80;
|
|
gOurID[1] = 0x00;
|
|
|
|
for( i = BRIDGE_ID_LEN - ETH_LENGTH_OF_ADDRESS; i < BRIDGE_ID_LEN; i++ )
|
|
{
|
|
gOurID[i] = pBridgeMACAddress[i - (BRIDGE_ID_LEN - ETH_LENGTH_OF_ADDRESS)];
|
|
}
|
|
|
|
// Set the root bridge ID as our own to start out with
|
|
BrdgSTACopyID( gDesignatedRootID, gOurID );
|
|
gHaveID = TRUE;
|
|
|
|
if (BrdgFwdBridgingNetworks())
|
|
{
|
|
// Don't use locks; rely on this function being non-reentrant and always run
|
|
// before any other functions
|
|
if( BrdgSTAPortStateSelection() )
|
|
{
|
|
BrdgSTATransmitTCNPacket();
|
|
}
|
|
|
|
BrdgSTAGenerateConfigBPDUs();
|
|
BrdgSTASetTimerWithSTATime( &gHelloTimer, gHelloTime, TRUE );
|
|
}
|
|
}
|
|
|
|
VOID
|
|
BrdgSTACleanup()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Driver unload-time cleanup
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Locking Constraints:
|
|
|
|
Top-level function. Assumes no locks are held by caller.
|
|
|
|
--*/
|
|
{
|
|
BrdgShutdownTimer( &gTopologyChangeTimer );
|
|
BrdgShutdownTimer( &gTopologyChangeNotificationTimer );
|
|
BrdgShutdownTimer( &gHelloTimer );
|
|
}
|
|
|
|
VOID
|
|
BrdgSTAEnableAdapter(
|
|
IN PADAPT pAdapt
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Enables STA operations on an adapter. Can be called multiple times
|
|
(in conjunction with BrdgSTADisableAdapter()) for a given adapter
|
|
|
|
Arguments:
|
|
|
|
pAdapt The adapter
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Locking Constraints:
|
|
|
|
Top-level function. Assumes no locks are held by caller.
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN bTransmitTCN = FALSE;
|
|
|
|
DBGPRINT(STA, ("ENABLING adapter %p\n", pAdapt));
|
|
|
|
NdisAcquireSpinLock( &gSTALock );
|
|
|
|
if( pAdapt->bSTAInited )
|
|
{
|
|
BrdgSTABecomeDesignatedPort(pAdapt);
|
|
BrdgSTASetAdapterState( pAdapt, Blocking );
|
|
|
|
pAdapt->STAInfo.bTopologyChangeAck = FALSE;
|
|
pAdapt->STAInfo.bConfigPending = FALSE;
|
|
|
|
bTransmitTCN = BrdgSTAPortStateSelection();
|
|
}
|
|
else
|
|
{
|
|
DBGPRINT(STA, ("BrdgSTAEnableAdapter() called with uninitialized adapter; ignoring!\n"));
|
|
}
|
|
|
|
NdisReleaseSpinLock( &gSTALock );
|
|
|
|
if( bTransmitTCN && BrdgFwdBridgingNetworks())
|
|
{
|
|
BrdgSTATransmitTCNPacket();
|
|
}
|
|
}
|
|
|
|
VOID
|
|
BrdgSTAInitializeAdapter(
|
|
IN PADAPT pAdapt
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
One-time initialization for a new adatper
|
|
|
|
Arguments:
|
|
|
|
pAdapt The adapter
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Locking Constraints:
|
|
|
|
Top-level function. Assumes no locks are held by caller.
|
|
ASSUMES the adapter has already been added to the global list
|
|
|
|
--*/
|
|
{
|
|
if( BrdgAcquireAdapter(pAdapt) )
|
|
{
|
|
UINT i, j;
|
|
|
|
// Adapters should always be disabled when being initialized, either because they are
|
|
// brand new and this is how they start out, or as a way of checking that they were
|
|
// correctly stopped when they were last disconnected.
|
|
SAFEASSERT( pAdapt->State == Disabled );
|
|
|
|
pAdapt->STAInfo.PathCost = BrdgSTALinkCostFromLinkSpeed(pAdapt->LinkSpeed);
|
|
|
|
BrdgInitializeTimer( &pAdapt->STAInfo.MessageAgeTimer, BrdgSTAMessageAgeTimerExpiry, pAdapt );
|
|
BrdgInitializeTimer( &pAdapt->STAInfo.ForwardDelayTimer, BrdgSTAForwardDelayTimerExpiry, pAdapt );
|
|
BrdgInitializeTimer( &pAdapt->STAInfo.HoldTimer, BrdgSTAHoldTimerExpiry, pAdapt );
|
|
pAdapt->STAInfo.LastConfigTime = 0L;
|
|
|
|
// Find an unused port number in the bitfield
|
|
NdisAcquireSpinLock( &gSTALock );
|
|
for( i = 0; i < sizeof(gUsedPortIDs) / sizeof(ULONG); i++ )
|
|
{
|
|
for( j = 0; j < sizeof(ULONG) * 8; j++ )
|
|
{
|
|
if( (gUsedPortIDs[i] & (1 << j)) == 0 )
|
|
{
|
|
pAdapt->STAInfo.ID = (PORT_ID)(PORT_PRIORITY | ((i * sizeof(ULONG) * 8) + j));
|
|
DBGPRINT(STA, ("Adapter %p gets ID %i\n", pAdapt, pAdapt->STAInfo.ID));
|
|
gUsedPortIDs[i] |= (1 << j);
|
|
goto doneID;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Should be impossible to not have an available ID
|
|
SAFEASSERT( FALSE );
|
|
pAdapt->STAInfo.ID = PORT_PRIORITY | 0xFF;
|
|
|
|
doneID:
|
|
// Set this before releasing the lock
|
|
pAdapt->bSTAInited = TRUE;
|
|
|
|
NdisReleaseSpinLock( &gSTALock );
|
|
|
|
// Start the adapter off enabled / disabled based on its media state
|
|
// The enable / disable functions take locks
|
|
if( pAdapt->MediaState == NdisMediaStateConnected )
|
|
{
|
|
BrdgSTAEnableAdapter( pAdapt );
|
|
}
|
|
else
|
|
{
|
|
SAFEASSERT( pAdapt->MediaState == NdisMediaStateDisconnected );
|
|
BrdgSTADisableAdapter( pAdapt );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SAFEASSERT( FALSE );
|
|
}
|
|
}
|
|
|
|
VOID
|
|
BrdgSTADisableAdapter(
|
|
IN PADAPT pAdapt
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Disable STA operation on an adapter. Can be called multiple times
|
|
(in conjunction with BrdgSTAEnableAdapter()) on a given adapter
|
|
|
|
Arguments:
|
|
|
|
pAdapt The adapter
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Locking Constraints:
|
|
|
|
Top-level function. Assumes no locks are held by caller.
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN bWereRoot, bTransmitTCN = FALSE;
|
|
|
|
DBGPRINT(STA, ("DISABLING adapter %p\n", pAdapt));
|
|
|
|
NdisAcquireSpinLock( &gSTALock );
|
|
|
|
if( pAdapt->bSTAInited )
|
|
{
|
|
bWereRoot = BrdgSTAWeAreRoot();
|
|
|
|
BrdgSTABecomeDesignatedPort(pAdapt);
|
|
BrdgSTASetAdapterState( pAdapt, Disabled );
|
|
|
|
pAdapt->STAInfo.bTopologyChangeAck = FALSE;
|
|
pAdapt->STAInfo.bConfigPending = FALSE;
|
|
|
|
BrdgCancelTimer( &pAdapt->STAInfo.MessageAgeTimer );
|
|
pAdapt->STAInfo.LastConfigTime = 0L;
|
|
BrdgCancelTimer( &pAdapt->STAInfo.ForwardDelayTimer );
|
|
|
|
BrdgSTAConfigUpdate();
|
|
bTransmitTCN = BrdgSTAPortStateSelection();
|
|
|
|
if( BrdgSTAWeAreRoot() && (! bWereRoot) )
|
|
{
|
|
// We're the root bridge now
|
|
DBGPRINT(STA, ("Became root through disabling of adapter %p\n", pAdapt));
|
|
|
|
gMaxAge = DEFAULT_MAX_AGE;
|
|
gHelloTime = DEFAULT_HELLO_TIME;
|
|
gForwardDelay = DEFAULT_FORWARD_DELAY;
|
|
|
|
bTransmitTCN = BrdgSTATopologyChangeDetected();
|
|
BrdgCancelTimer( &gTopologyChangeNotificationTimer );
|
|
|
|
// Don't do packet sends with a spin lock held
|
|
NdisReleaseSpinLock( &gSTALock );
|
|
|
|
if (BrdgFwdBridgingNetworks())
|
|
{
|
|
BrdgSTAGenerateConfigBPDUs();
|
|
BrdgSTASetTimerWithSTATime( &gHelloTimer, gHelloTime, TRUE );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NdisReleaseSpinLock( &gSTALock );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NdisReleaseSpinLock( &gSTALock );
|
|
DBGPRINT(STA, ("BrdgSTADisableAdapter() called with uninitialized adapter; ignoring!\n"));
|
|
}
|
|
|
|
if( bTransmitTCN )
|
|
{
|
|
BrdgSTATransmitTCNPacket();
|
|
}
|
|
}
|
|
|
|
VOID
|
|
BrdgSTAShutdownAdapter(
|
|
IN PADAPT pAdapt
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
One-time teardown of an adapter
|
|
|
|
Arguments:
|
|
|
|
pAdapt The adapter
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Locking Constraints:
|
|
|
|
Top-level function. Assumes no locks are held by caller.
|
|
ASSUMES the adapter has been taken out of the global list
|
|
|
|
--*/
|
|
{
|
|
UINT i;
|
|
PORT_ID ActualID = pAdapt->STAInfo.ID & (~PORT_PRIORITY);
|
|
|
|
// Shouldn't be possible to go through a formal shutdown without
|
|
// having completed initialization.
|
|
SAFEASSERT( pAdapt->bSTAInited );
|
|
|
|
// Shutdown all this adapter's timers
|
|
BrdgShutdownTimer( &pAdapt->STAInfo.HoldTimer );
|
|
BrdgShutdownTimer( &pAdapt->STAInfo.ForwardDelayTimer );
|
|
BrdgShutdownTimer( &pAdapt->STAInfo.MessageAgeTimer );
|
|
|
|
// Disable the adapter
|
|
BrdgSTADisableAdapter( pAdapt );
|
|
|
|
// Note that this adapter's port ID is now free
|
|
NdisAcquireSpinLock( &gSTALock );
|
|
i = (UINT)(ActualID / (sizeof(ULONG) * 8));
|
|
SAFEASSERT( i < sizeof(gUsedPortIDs) / sizeof(ULONG) );
|
|
gUsedPortIDs[i] &= ~(1 << (ActualID % (sizeof(ULONG) * 8)));
|
|
NdisReleaseSpinLock( &gSTALock );
|
|
|
|
// We're all done with this adapter structure
|
|
SAFEASSERT( gRootAdapter != pAdapt );
|
|
BrdgReleaseAdapter( pAdapt );
|
|
}
|
|
|
|
VOID
|
|
BrdgSTAReceivePacket(
|
|
IN PADAPT pAdapt,
|
|
IN PNDIS_PACKET pPacket
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Function to handle the processing of a packet received on the reserved
|
|
STA multicast channel
|
|
|
|
Arguments:
|
|
|
|
pAdapt The adapter the packet was received on
|
|
pPacket The received packet
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Locking Constraints:
|
|
|
|
Top-level function. Assumes no locks are held by caller.
|
|
|
|
--*/
|
|
{
|
|
UCHAR STAPacket[CONFIG_BPDU_PACKET_SIZE + _802_3_HEADER_SIZE];
|
|
ULONG written;
|
|
SHORT dataLen;
|
|
|
|
// Copy the data from the packet into our data buffer
|
|
BrdgSTACopyFromPacketToBuffer( STAPacket, sizeof(STAPacket), &written, pPacket );
|
|
|
|
if( written < TCN_BPDU_PACKET_SIZE + _802_3_HEADER_SIZE )
|
|
{
|
|
THROTTLED_DBGPRINT(STA, ("Undersize STA packet received on %p\n", pAdapt));
|
|
return;
|
|
}
|
|
|
|
// The LLC header must identify the STA protocol
|
|
if( (STAPacket[14] != 0x42) || (STAPacket[15] != 0x42) )
|
|
{
|
|
THROTTLED_DBGPRINT(STA, ("Packet with bad protocol type received on %p\n", pAdapt));
|
|
return;
|
|
}
|
|
|
|
// Bytes 13 and 14 encode the length of data.
|
|
dataLen = STAPacket[12] << 8;
|
|
dataLen |= STAPacket[13];
|
|
|
|
// The first two bytes are the protocol identifier and must be zero.
|
|
// The third byte is the version identifier and must be zero.
|
|
|
|
if( (STAPacket[_802_3_HEADER_SIZE] != 0) ||
|
|
(STAPacket[_802_3_HEADER_SIZE + 1] != 0) ||
|
|
(STAPacket[_802_3_HEADER_SIZE + 2] != 0) )
|
|
{
|
|
THROTTLED_DBGPRINT(STA, ("Invalid STA packet received\n"));
|
|
return;
|
|
}
|
|
|
|
if( STAPacket[_802_3_HEADER_SIZE + 3] == 0x80 )
|
|
{
|
|
// The length of the frame with LLC header must be 7 bytes for a TCN BPDU
|
|
if( dataLen != 7 )
|
|
{
|
|
THROTTLED_DBGPRINT(STA, ("Bad header size for TCN BPDU on %p\n", pAdapt));
|
|
return;
|
|
}
|
|
|
|
// This is a Topology Change BPDU.
|
|
BrdgSTAProcessTCNBPDU( pAdapt );
|
|
}
|
|
else if( STAPacket[_802_3_HEADER_SIZE + 3] == 0x00 )
|
|
{
|
|
CONFIG_BPDU bpdu;
|
|
|
|
if( written < CONFIG_BPDU_PACKET_SIZE + _802_3_HEADER_SIZE )
|
|
{
|
|
THROTTLED_DBGPRINT(STA, ("Undersize config BPDU received on %p\n", pAdapt));
|
|
return;
|
|
}
|
|
|
|
// The length of the frame with LLC header must be 38 bytes for a Config BPDU
|
|
if( dataLen != 38 )
|
|
{
|
|
THROTTLED_DBGPRINT(STA, ("Bad header size for Config BPDU on %p\n", pAdapt));
|
|
return;
|
|
}
|
|
|
|
bpdu.Type = ConfigBPDU;
|
|
|
|
// The high bit of byte 5 encodes the topology change acknowledge flag
|
|
bpdu.bTopologyChangeAck = (BOOLEAN)((STAPacket[_802_3_HEADER_SIZE + 4] & 0x80) != 0);
|
|
|
|
// The low bit of byte 5 encodes the topology change flag
|
|
bpdu.bTopologyChange = (BOOLEAN)((STAPacket[_802_3_HEADER_SIZE + 4] & 0x01) != 0);
|
|
|
|
// Bytes 6 thru 13 encode the root bridge ID
|
|
bpdu.RootID[0] = STAPacket[_802_3_HEADER_SIZE + 5];
|
|
bpdu.RootID[1] = STAPacket[_802_3_HEADER_SIZE + 6];
|
|
bpdu.RootID[2] = STAPacket[_802_3_HEADER_SIZE + 7];
|
|
bpdu.RootID[3] = STAPacket[_802_3_HEADER_SIZE + 8];
|
|
bpdu.RootID[4] = STAPacket[_802_3_HEADER_SIZE + 9];
|
|
bpdu.RootID[5] = STAPacket[_802_3_HEADER_SIZE + 10];
|
|
bpdu.RootID[6] = STAPacket[_802_3_HEADER_SIZE + 11];
|
|
bpdu.RootID[7] = STAPacket[_802_3_HEADER_SIZE + 12];
|
|
|
|
// Bytes 14 thru 17 encode the root path cost
|
|
bpdu.RootCost = 0;
|
|
bpdu.RootCost |= STAPacket[_802_3_HEADER_SIZE + 13] << 24;
|
|
bpdu.RootCost |= STAPacket[_802_3_HEADER_SIZE + 14] << 16;
|
|
bpdu.RootCost |= STAPacket[_802_3_HEADER_SIZE + 15] << 8;
|
|
bpdu.RootCost |= STAPacket[_802_3_HEADER_SIZE + 16];
|
|
|
|
// Bytes 18 thru 15 encode the designated bridge ID
|
|
bpdu.BridgeID[0] = STAPacket[_802_3_HEADER_SIZE + 17];
|
|
bpdu.BridgeID[1] = STAPacket[_802_3_HEADER_SIZE + 18];
|
|
bpdu.BridgeID[2] = STAPacket[_802_3_HEADER_SIZE + 19];
|
|
bpdu.BridgeID[3] = STAPacket[_802_3_HEADER_SIZE + 20];
|
|
bpdu.BridgeID[4] = STAPacket[_802_3_HEADER_SIZE + 21];
|
|
bpdu.BridgeID[5] = STAPacket[_802_3_HEADER_SIZE + 22];
|
|
bpdu.BridgeID[6] = STAPacket[_802_3_HEADER_SIZE + 23];
|
|
bpdu.BridgeID[7] = STAPacket[_802_3_HEADER_SIZE + 24];
|
|
|
|
// Bytes 26 and 27 encode the port identifier
|
|
bpdu.PortID = 0;
|
|
bpdu.PortID |= STAPacket[_802_3_HEADER_SIZE + 25] << 8;
|
|
bpdu.PortID |= STAPacket[_802_3_HEADER_SIZE + 26];
|
|
|
|
// Bytes 28 and 29 encode the message age
|
|
bpdu.MessageAge = 0;
|
|
bpdu.MessageAge |= STAPacket[_802_3_HEADER_SIZE + 27] << 8;
|
|
bpdu.MessageAge |= STAPacket[_802_3_HEADER_SIZE + 28];
|
|
|
|
// Bytes 30 and 31 encode the Max Age
|
|
bpdu.MaxAge = 0;
|
|
bpdu.MaxAge |= STAPacket[_802_3_HEADER_SIZE + 29] << 8;
|
|
bpdu.MaxAge |= STAPacket[_802_3_HEADER_SIZE + 30];
|
|
|
|
if( bpdu.MaxAge == 0 )
|
|
{
|
|
THROTTLED_DBGPRINT(STA, ("Ignoring BPDU packet with zero MaxAge on adapter %p\n", pAdapt));
|
|
return;
|
|
}
|
|
|
|
// Bytes 32 and 33 encode the Hello Time
|
|
bpdu.HelloTime = 0;
|
|
bpdu.HelloTime |= STAPacket[_802_3_HEADER_SIZE + 31] << 8;
|
|
bpdu.HelloTime |= STAPacket[_802_3_HEADER_SIZE + 32];
|
|
|
|
if( bpdu.HelloTime == 0 )
|
|
{
|
|
THROTTLED_DBGPRINT(STA, ("Ignoring BPDU packet with zero HelloTime on adapter %p\n", pAdapt));
|
|
return;
|
|
}
|
|
|
|
// Bytes 34 and 35 encode the forwarding delay
|
|
bpdu.ForwardDelay = 0;
|
|
bpdu.ForwardDelay |= STAPacket[_802_3_HEADER_SIZE + 33] << 8;
|
|
bpdu.ForwardDelay |= STAPacket[_802_3_HEADER_SIZE + 34];
|
|
|
|
if( bpdu.ForwardDelay == 0 )
|
|
{
|
|
THROTTLED_DBGPRINT(STA, ("Ignoring BPDU packet with zero ForwardDelay on adapter %p\n", pAdapt));
|
|
return;
|
|
}
|
|
|
|
BrdgSTAProcessConfigBPDU( pAdapt, &bpdu );
|
|
}
|
|
else
|
|
{
|
|
THROTTLED_DBGPRINT(STA, ("Packet with unrecognized BPDU type received on %p\n", pAdapt));
|
|
return;
|
|
}
|
|
}
|
|
|
|
// ===========================================================================
|
|
//
|
|
// PRIVATE FUNCTIONS
|
|
//
|
|
// ===========================================================================
|
|
|
|
VOID
|
|
BrdgSTASetAdapterState(
|
|
IN PADAPT pAdapt,
|
|
IN PORT_STATE NewState
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Updates an adapter's forwarding state correctly
|
|
|
|
This function is designed to be callable at high IRQL, so it defers
|
|
the actual call to BrdgProtDoAdapterStateChange, which must be called
|
|
at low IRQL.
|
|
|
|
Arguments:
|
|
|
|
pAdapt The adapter the packet was received on
|
|
pPacket The received packet
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Locking Constraints:
|
|
|
|
No requirements on caller-held locks
|
|
|
|
--*/
|
|
|
|
{
|
|
LOCK_STATE LockState;
|
|
BOOLEAN bailOut = FALSE;
|
|
|
|
// Set the adapter's new state.
|
|
NdisAcquireReadWriteLock( &gAdapterCharacteristicsLock, TRUE/*Write access*/, &LockState );
|
|
if( pAdapt->State == NewState )
|
|
{
|
|
bailOut = TRUE;
|
|
}
|
|
else
|
|
{
|
|
pAdapt->State = NewState;
|
|
}
|
|
NdisReleaseReadWriteLock( &gAdapterCharacteristicsLock, &LockState );
|
|
|
|
// Don't do additional work if the adapter is already in the requested state
|
|
if( bailOut )
|
|
{
|
|
return;
|
|
}
|
|
|
|
#if DBG
|
|
switch( NewState )
|
|
{
|
|
case Blocking:
|
|
DBGPRINT(STA, ("Adapter %p becomes BLOCKING\n", pAdapt));
|
|
break;
|
|
|
|
case Listening:
|
|
DBGPRINT(STA, ("Adapter %p becomes LISTENING\n", pAdapt));
|
|
break;
|
|
|
|
case Learning:
|
|
DBGPRINT(STA, ("Adapter %p becomes LEARNING\n", pAdapt));
|
|
break;
|
|
|
|
case Forwarding:
|
|
DBGPRINT(STA, ("Adapter %p becomes FORWARDING\n", pAdapt));
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// We will be hanging onto the adapter pointer in order to defer the call
|
|
// to BrdgSTADeferredSetAdapterState.
|
|
//
|
|
if( BrdgAcquireAdapter(pAdapt) )
|
|
{
|
|
NDIS_STATUS Status;
|
|
|
|
// We need to defer the call to BrdgProtDoAdapterStateChange since it must run
|
|
// at PASSIVE_IRQL
|
|
Status = BrdgDeferFunction( BrdgSTADeferredSetAdapterState, pAdapt );
|
|
|
|
if( Status != NDIS_STATUS_SUCCESS )
|
|
{
|
|
DBGPRINT(STA, ("Unable to defer call to BrdgSTADeferredSetAdapterState\n", pAdapt));
|
|
BrdgReleaseAdapter( pAdapt );
|
|
}
|
|
// else adapter will be released in BrdgSTADeferredSetAdapterState
|
|
}
|
|
else
|
|
{
|
|
DBGPRINT(STA, ("Adapter %p already shutting down when attempted to set adapter state\n", pAdapt));
|
|
}
|
|
}
|
|
|
|
VOID
|
|
BrdgSTADeferredSetAdapterState(
|
|
IN PVOID Arg
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Deferred function from BrdgSTASetAdapterState; does housekeeping associated with
|
|
changing an adapter's forwarding state.
|
|
|
|
Must be called at PASSIVE
|
|
|
|
Arguments:
|
|
|
|
Arg The adapter that needs updating
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Locking Constraints:
|
|
|
|
No requirements on caller-held locks
|
|
|
|
--*/
|
|
{
|
|
PADAPT pAdapt = (PADAPT)Arg;
|
|
|
|
SAFEASSERT( CURRENT_IRQL == PASSIVE_LEVEL );
|
|
|
|
BrdgProtDoAdapterStateChange( pAdapt );
|
|
|
|
// Tell user-mode about the change
|
|
BrdgCtlNotifyAdapterChange( pAdapt, BrdgNotifyAdapterStateChange );
|
|
|
|
// Adapter was acquired in BrdgSTASetAdapterState()
|
|
BrdgReleaseAdapter( pAdapt );
|
|
}
|
|
|
|
VOID
|
|
BrdgSTATransmitConfigBPDUPacket(
|
|
IN PADAPT pAdapt,
|
|
PCONFIG_BPDU pbpdu
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Transmits a config BPDU packet on a particular adapter
|
|
|
|
Arguments:
|
|
|
|
pAdapt The adapter to transmit on
|
|
pbpdu A structure describing the BPDU information
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Locking Constraints:
|
|
|
|
No requirements on caller-held locks
|
|
|
|
--*/
|
|
{
|
|
UCHAR STAPacket[CONFIG_BPDU_PACKET_SIZE + _802_3_HEADER_SIZE];
|
|
NDIS_STATUS Status;
|
|
|
|
if (BrdgProtGetAdapterCount() < 2)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!BrdgFwdBridgingNetworks())
|
|
{
|
|
DBGPRINT(STA, ("Not Transmitting STA Packet (we're not bridging)\r\n"));
|
|
return;
|
|
}
|
|
|
|
//
|
|
// First encode the Ethernet header.
|
|
//
|
|
// Destination MAC address of packet must be STA multicast address
|
|
STAPacket[0] = STA_MAC_ADDR[0];
|
|
STAPacket[1] = STA_MAC_ADDR[1];
|
|
STAPacket[2] = STA_MAC_ADDR[2];
|
|
STAPacket[3] = STA_MAC_ADDR[3];
|
|
STAPacket[4] = STA_MAC_ADDR[4];
|
|
STAPacket[5] = STA_MAC_ADDR[5];
|
|
|
|
// The the source MAC address to the adapter's own MAC address
|
|
STAPacket[6] = pAdapt->MACAddr[0];
|
|
STAPacket[7] = pAdapt->MACAddr[1];
|
|
STAPacket[8] = pAdapt->MACAddr[2];
|
|
STAPacket[9] = pAdapt->MACAddr[3];
|
|
STAPacket[10] = pAdapt->MACAddr[4];
|
|
STAPacket[11] = pAdapt->MACAddr[5];
|
|
|
|
// Next two bytes are the size of the frame (38 bytes)
|
|
STAPacket[12] = 0x00;
|
|
STAPacket[13] = 0x26;
|
|
|
|
// Next two bytes are the LLC DSAP and SSAP fields, set to 0x42 for STA
|
|
STAPacket[14] = 0x42;
|
|
STAPacket[15] = 0x42;
|
|
|
|
// Next byte is the LLC frame type, 3 for unnumbered
|
|
STAPacket[16] = 0x03;
|
|
|
|
//
|
|
// Now we are encoding the payload.
|
|
//
|
|
// First 4 bytes are the protocol identifier, version and BPDU type, all zero
|
|
STAPacket[_802_3_HEADER_SIZE] = STAPacket[_802_3_HEADER_SIZE + 1] =
|
|
STAPacket[_802_3_HEADER_SIZE + 2] = STAPacket[_802_3_HEADER_SIZE + 3] = 0x00;
|
|
|
|
// Byte 5 encodes the Topology Change Ack flag in the high bit and the
|
|
// Topology Change flag in the low bit.
|
|
STAPacket[_802_3_HEADER_SIZE + 4] = 0;
|
|
|
|
if( pbpdu->bTopologyChangeAck )
|
|
{
|
|
STAPacket[_802_3_HEADER_SIZE + 4] |= 0x80;
|
|
}
|
|
|
|
if( pbpdu->bTopologyChange )
|
|
{
|
|
STAPacket[_802_3_HEADER_SIZE + 4] |= 0x01;
|
|
}
|
|
|
|
// Bytes 6-13 encode the root bridge ID
|
|
STAPacket[_802_3_HEADER_SIZE + 5] = pbpdu->RootID[0];
|
|
STAPacket[_802_3_HEADER_SIZE + 6] = pbpdu->RootID[1];
|
|
STAPacket[_802_3_HEADER_SIZE + 7] = pbpdu->RootID[2];
|
|
STAPacket[_802_3_HEADER_SIZE + 8] = pbpdu->RootID[3];
|
|
STAPacket[_802_3_HEADER_SIZE + 9] = pbpdu->RootID[4];
|
|
STAPacket[_802_3_HEADER_SIZE + 10] = pbpdu->RootID[5];
|
|
STAPacket[_802_3_HEADER_SIZE + 11] = pbpdu->RootID[6];
|
|
STAPacket[_802_3_HEADER_SIZE + 12] = pbpdu->RootID[7];
|
|
|
|
// Bytes 14 - 17 encode the root path cost
|
|
STAPacket[_802_3_HEADER_SIZE + 13] = (UCHAR)(pbpdu->RootCost >> 24);
|
|
STAPacket[_802_3_HEADER_SIZE + 14] = (UCHAR)(pbpdu->RootCost >> 16);
|
|
STAPacket[_802_3_HEADER_SIZE + 15] = (UCHAR)(pbpdu->RootCost >> 8);
|
|
STAPacket[_802_3_HEADER_SIZE + 16] = (UCHAR)(pbpdu->RootCost);
|
|
|
|
// Bytes 18-25 encode the designated bridge ID
|
|
STAPacket[_802_3_HEADER_SIZE + 17] = pbpdu->BridgeID[0];
|
|
STAPacket[_802_3_HEADER_SIZE + 18] = pbpdu->BridgeID[1];
|
|
STAPacket[_802_3_HEADER_SIZE + 19] = pbpdu->BridgeID[2];
|
|
STAPacket[_802_3_HEADER_SIZE + 20] = pbpdu->BridgeID[3];
|
|
STAPacket[_802_3_HEADER_SIZE + 21] = pbpdu->BridgeID[4];
|
|
STAPacket[_802_3_HEADER_SIZE + 22] = pbpdu->BridgeID[5];
|
|
STAPacket[_802_3_HEADER_SIZE + 23] = pbpdu->BridgeID[6];
|
|
STAPacket[_802_3_HEADER_SIZE + 24] = pbpdu->BridgeID[7];
|
|
|
|
// Bytes 26 and 27 encode the port identifier
|
|
STAPacket[_802_3_HEADER_SIZE + 25] = (UCHAR)(pbpdu->PortID >> 8);
|
|
STAPacket[_802_3_HEADER_SIZE + 26] = (UCHAR)(pbpdu->PortID);
|
|
|
|
// Bytes 28 and 29 encode the message age
|
|
STAPacket[_802_3_HEADER_SIZE + 27] = (UCHAR)(pbpdu->MessageAge >> 8);
|
|
STAPacket[_802_3_HEADER_SIZE + 28] = (UCHAR)(pbpdu->MessageAge);
|
|
|
|
// Bytes 30 and 31 encode the max age
|
|
STAPacket[_802_3_HEADER_SIZE + 29] = (UCHAR)(pbpdu->MaxAge >> 8);
|
|
STAPacket[_802_3_HEADER_SIZE + 30] = (UCHAR)(pbpdu->MaxAge);
|
|
|
|
// Bytes 32 and 33 encode the hello time
|
|
STAPacket[_802_3_HEADER_SIZE + 31] = (UCHAR)(pbpdu->HelloTime >> 8);
|
|
STAPacket[_802_3_HEADER_SIZE + 32] = (UCHAR)(pbpdu->HelloTime);
|
|
|
|
// Bytes 34 and 35 encode the forward delay
|
|
STAPacket[_802_3_HEADER_SIZE + 33] = (UCHAR)(pbpdu->ForwardDelay >> 8);
|
|
STAPacket[_802_3_HEADER_SIZE + 34] = (UCHAR)(pbpdu->ForwardDelay);
|
|
|
|
// Send the finished packet
|
|
Status = BrdgFwdSendBuffer( pAdapt, STAPacket, sizeof(STAPacket) );
|
|
|
|
if( Status != NDIS_STATUS_SUCCESS )
|
|
{
|
|
THROTTLED_DBGPRINT(STA, ("BPDU packet send failed: %08x\n", Status));
|
|
}
|
|
}
|
|
|
|
VOID
|
|
BrdgSTATransmitTCNPacket()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Transmits a Topology Change Notification BPDU packet on the root adapter
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Locking Constraints:
|
|
|
|
ASSUMES the caller has NOT acquired gSTALock
|
|
|
|
--*/
|
|
{
|
|
UCHAR STAPacket[TCN_BPDU_PACKET_SIZE + _802_3_HEADER_SIZE];
|
|
NDIS_STATUS Status;
|
|
PADAPT pRootAdapter;
|
|
BOOLEAN bAcquired;
|
|
|
|
if (BrdgProtGetAdapterCount() < 2)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!BrdgFwdBridgingNetworks())
|
|
{
|
|
DBGPRINT(STA, ("Not Transmitting STA Packet (we're not bridging)\r\n"));
|
|
return;
|
|
}
|
|
|
|
NdisAcquireSpinLock( &gSTALock );
|
|
|
|
// Freeze this value for the rest of the function
|
|
pRootAdapter = gRootAdapter;
|
|
|
|
if( pRootAdapter == NULL )
|
|
{
|
|
NdisReleaseSpinLock( &gSTALock );
|
|
return;
|
|
}
|
|
|
|
bAcquired = BrdgAcquireAdapter( pRootAdapter );
|
|
NdisReleaseSpinLock( &gSTALock );
|
|
|
|
if( ! bAcquired )
|
|
{
|
|
SAFEASSERT( FALSE );
|
|
return;
|
|
}
|
|
|
|
SAFEASSERT( gHaveID );
|
|
|
|
//
|
|
// First encode the Ethernet header.
|
|
//
|
|
// Destination MAC address of packet must be STA multicast address
|
|
STAPacket[0] = STA_MAC_ADDR[0];
|
|
STAPacket[1] = STA_MAC_ADDR[1];
|
|
STAPacket[2] = STA_MAC_ADDR[2];
|
|
STAPacket[3] = STA_MAC_ADDR[3];
|
|
STAPacket[4] = STA_MAC_ADDR[4];
|
|
STAPacket[5] = STA_MAC_ADDR[5];
|
|
|
|
// Set the packet's MAC address to the adapter's own MAC address
|
|
STAPacket[6] = pRootAdapter->MACAddr[0];
|
|
STAPacket[7] = pRootAdapter->MACAddr[1];
|
|
STAPacket[8] = pRootAdapter->MACAddr[2];
|
|
STAPacket[9] = pRootAdapter->MACAddr[3];
|
|
STAPacket[10] = pRootAdapter->MACAddr[4];
|
|
STAPacket[11] = pRootAdapter->MACAddr[5];
|
|
|
|
// Next two bytes are the size of the frame (7 bytes)
|
|
STAPacket[12] = 0x00;
|
|
STAPacket[13] = 0x07;
|
|
|
|
// Next two bytes are the LLC DSAP and SSAP fields, set to 0x42 for STA
|
|
STAPacket[14] = 0x42;
|
|
STAPacket[15] = 0x42;
|
|
|
|
// Next byte is the LLC frame type, 3 for unnumbered
|
|
STAPacket[16] = 0x03;
|
|
|
|
//
|
|
// Now we are encoding the payload.
|
|
//
|
|
// First 3 bytes are the protocol identifier and protocol version number, all zero
|
|
STAPacket[_802_3_HEADER_SIZE] = STAPacket[_802_3_HEADER_SIZE + 1] =
|
|
STAPacket[_802_3_HEADER_SIZE + 2] = 0x00;
|
|
|
|
// Byte 4 is the BPDU type, which is 0x80 for TCN.
|
|
STAPacket[_802_3_HEADER_SIZE + 3] = 0x80;
|
|
|
|
// Send the finished packet
|
|
Status = BrdgFwdSendBuffer( pRootAdapter, STAPacket, sizeof(STAPacket) );
|
|
|
|
// We are done with the root adapter
|
|
BrdgReleaseAdapter( pRootAdapter );
|
|
|
|
if( Status != NDIS_STATUS_SUCCESS )
|
|
{
|
|
THROTTLED_DBGPRINT(STA, ("BPDU packet send failed: %08x\n", Status));
|
|
}
|
|
}
|
|
|
|
VOID
|
|
BrdgSTACopyFromPacketToBuffer(
|
|
OUT PUCHAR pPacketOut,
|
|
IN ULONG BufferSize,
|
|
OUT PULONG pWrittenCount,
|
|
IN PNDIS_PACKET pPacketIn
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Copies data out of a packet descriptor into a flat buffer
|
|
|
|
Arguments:
|
|
|
|
pPacketOut Data buffer to copy info
|
|
BufferSize Size of pPacketOut
|
|
pWrittenCount Number of bytes actually written
|
|
pPacketIn Packet to copy from
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Locking Constraints:
|
|
|
|
No requirements on caller-held locks
|
|
|
|
--*/
|
|
{
|
|
PNDIS_BUFFER pBuf;
|
|
|
|
*pWrittenCount = 0L;
|
|
pBuf = BrdgBufPacketHeadBuffer(pPacketIn);
|
|
|
|
while( pBuf != NULL )
|
|
{
|
|
PVOID pData;
|
|
UINT Len;
|
|
|
|
NdisQueryBufferSafe( pBuf, &pData, &Len, NormalPagePriority );
|
|
|
|
if( pData != NULL )
|
|
{
|
|
ULONG BytesToWrite;
|
|
|
|
if( *pWrittenCount + Len > BufferSize )
|
|
{
|
|
BytesToWrite = BufferSize - *pWrittenCount;
|
|
}
|
|
else
|
|
{
|
|
BytesToWrite = Len;
|
|
}
|
|
|
|
NdisMoveMemory( pPacketOut, pData, BytesToWrite );
|
|
pPacketOut += BytesToWrite;
|
|
*pWrittenCount += BytesToWrite;
|
|
|
|
if( BytesToWrite < Len )
|
|
{
|
|
// We're full, so we're done.
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Shouldn't happen
|
|
SAFEASSERT( FALSE );
|
|
}
|
|
|
|
NdisGetNextBuffer( pBuf, &pBuf );
|
|
}
|
|
}
|
|
|
|
VOID
|
|
BrdgSTATransmitConfig(
|
|
PADAPT pAdapt
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Transmits a config BPDU on a particular adapter. Collects appropriate
|
|
information and calls BrdgSTATransmitConfigBPDUPacket().
|
|
|
|
Arguments:
|
|
|
|
pAdapt The adapter to transmit on
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Locking Constraints:
|
|
|
|
ASSUMES the caller DOES NOT hold gSTALock
|
|
|
|
--*/
|
|
{
|
|
NdisAcquireSpinLock( &gSTALock );
|
|
|
|
if( BrdgTimerIsRunning( &pAdapt->STAInfo.HoldTimer ) )
|
|
{
|
|
// We have sent a config packet recently. Wait until the hold timer
|
|
// expires before sending another one so we don't flood other bridges.
|
|
pAdapt->STAInfo.bConfigPending = TRUE;
|
|
|
|
NdisReleaseSpinLock( &gSTALock );
|
|
}
|
|
else
|
|
{
|
|
CONFIG_BPDU bpdu;
|
|
|
|
// Fill out the BPDU information structure
|
|
bpdu.Type = ConfigBPDU;
|
|
SAFEASSERT( gHaveID );
|
|
BrdgSTACopyID( bpdu.RootID, gDesignatedRootID );
|
|
bpdu.RootCost = gRootCost;
|
|
BrdgSTACopyID( bpdu.BridgeID, gOurID );
|
|
bpdu.PortID = pAdapt->STAInfo.ID;
|
|
|
|
if( BrdgSTAWeAreRoot() )
|
|
{
|
|
// We are the root, so the age of this config information is zero.
|
|
bpdu.MessageAge = 0;
|
|
}
|
|
else
|
|
{
|
|
// The MessageAge field is to be set to the age of the last received
|
|
// config BPDU on the root port.
|
|
if( (gRootAdapter != NULL) && BrdgAcquireAdapter(gRootAdapter) )
|
|
{
|
|
ULONG CurrentTime, deltaTime;
|
|
|
|
NdisGetSystemUpTime( &CurrentTime );
|
|
|
|
// The message age timer should be running on the root adapter if
|
|
// we are not root.
|
|
SAFEASSERT( BrdgTimerIsRunning(&gRootAdapter->STAInfo.MessageAgeTimer) );
|
|
SAFEASSERT( gRootAdapter->STAInfo.LastConfigTime != 0L );
|
|
|
|
// The last parameter is the max acceptable delta. We should have
|
|
// received the last piece of config information from the root no more
|
|
// than gMaxAge STA units ago, since if it was longer than that, we
|
|
// should have become root. Allow an additional second for processing.
|
|
deltaTime = BrdgDeltaSafe( gRootAdapter->STAInfo.LastConfigTime, CurrentTime,
|
|
(ULONG)(((gMaxAge * 1000) / 256) + 1000) );
|
|
|
|
// STA times are in 1/256ths of a second.
|
|
bpdu.MessageAge = (STA_TIME)((deltaTime * 256) / 1000);
|
|
|
|
// MESSAGE_AGE_INCREMENT allows for the transmission time, etc.
|
|
bpdu.MessageAge += MESSAGE_AGE_INCREMENT;
|
|
|
|
BrdgReleaseAdapter(gRootAdapter);
|
|
}
|
|
else
|
|
{
|
|
// Why isn't there a root port if we're not root?
|
|
SAFEASSERT( FALSE );
|
|
bpdu.MessageAge = 0;
|
|
}
|
|
|
|
}
|
|
|
|
bpdu.MaxAge = gMaxAge;
|
|
bpdu.HelloTime = gHelloTime;
|
|
bpdu.ForwardDelay = gForwardDelay;
|
|
|
|
// Are we supposed to acknowledge a topology change signal?
|
|
bpdu.bTopologyChangeAck = pAdapt->STAInfo.bTopologyChangeAck;
|
|
|
|
// We just sent out the topology change ack if there was one to send
|
|
pAdapt->STAInfo.bTopologyChangeAck = FALSE;
|
|
|
|
bpdu.bTopologyChange = gTopologyChange;
|
|
|
|
// Start the hold timer to make sure another BPDU isn't sent prematurely
|
|
pAdapt->STAInfo.bConfigPending = FALSE;
|
|
|
|
// Don't send a packet with the spin lock held
|
|
NdisReleaseSpinLock( &gSTALock );
|
|
|
|
// Send off the config BPDU
|
|
BrdgSTATransmitConfigBPDUPacket( pAdapt, &bpdu );
|
|
|
|
BrdgSetTimer( &pAdapt->STAInfo.HoldTimer, HOLD_TIMER_PERIOD, FALSE /*Not periodic*/ );
|
|
}
|
|
}
|
|
|
|
BOOLEAN
|
|
BrdgSTASupersedesPortInfo(
|
|
IN PADAPT pAdapt,
|
|
IN PCONFIG_BPDU pbpdu
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determines whether a given bpdu's information supersedes the information
|
|
already associated with a particular adapter
|
|
|
|
Arguments:
|
|
|
|
pAdapt The adapter
|
|
pbpdu Received BPDU information to examine
|
|
|
|
Return Value:
|
|
|
|
TRUE if the given information supersedes (i.e., is better) than the information
|
|
previously held by the adapter. FALSE otherwise.
|
|
|
|
Locking Constraints:
|
|
|
|
ASSUMES the caller has acquired gSTALock
|
|
|
|
--*/
|
|
{
|
|
INT cmp;
|
|
|
|
/* The information advertised on a given link supersedes the existing information for that
|
|
link if the following conditions hold (applied in order; TRUE at any step causes immediate
|
|
success)
|
|
|
|
(1) The advertised root has a lower ID than the previous root
|
|
(2) The advertised root is the same as the previous root and the new cost-to-root is
|
|
lower than the previous value
|
|
(3) The root IDs and costs are the same, and the ID of the advertising bridge is
|
|
lower than the previous value
|
|
(4) The root ID, cost-to-root and bridge IDs are the same and the advertising bridge
|
|
is not us
|
|
(5) The root ID, cost-to-root, bridge IDs are the same, the bridge is us, and the
|
|
advertised port number is lower than the previous value (this happens if we have
|
|
more than one port on the same physical link and we see the advertisement from
|
|
our other port).
|
|
*/
|
|
|
|
// Compare the advertised root ID to the adapter's previous designated root ID
|
|
cmp = BrdgSTABridgeIDCmp( pbpdu->RootID, pAdapt->STAInfo.DesignatedRootID );
|
|
|
|
if( cmp == -1 ) // (1)
|
|
{
|
|
return TRUE;
|
|
}
|
|
else if( cmp == 0 )
|
|
{
|
|
if( pbpdu->RootCost < pAdapt->STAInfo.DesignatedCost ) // (2)
|
|
{
|
|
return TRUE;
|
|
}
|
|
else if( pbpdu->RootCost == pAdapt->STAInfo.DesignatedCost )
|
|
{
|
|
// Compare the advertised bridge ID to the previous designated bridge ID
|
|
cmp = BrdgSTABridgeIDCmp( pbpdu->BridgeID, pAdapt->STAInfo.DesignatedBridgeID );
|
|
|
|
if( cmp == -1 )
|
|
{
|
|
return TRUE; // (3)
|
|
}
|
|
else if( cmp == 0 )
|
|
{
|
|
SAFEASSERT( gHaveID );
|
|
|
|
// Compare the advertised bridge ID to our own ID
|
|
cmp = BrdgSTABridgeIDCmp( pbpdu->BridgeID, gOurID );
|
|
|
|
if( cmp != 0 )
|
|
{
|
|
return TRUE; // (4)
|
|
}
|
|
else if( cmp == 0 )
|
|
{
|
|
return (BOOLEAN)(pbpdu->PortID <= pAdapt->STAInfo.DesignatedPort); // (5)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
VOID
|
|
BrdgSTARecordConfigInfo(
|
|
IN PADAPT pAdapt,
|
|
IN PCONFIG_BPDU pbpdu
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Associates the information from a received BPDU with a particular adapter
|
|
|
|
Arguments:
|
|
|
|
pAdapt The adapter
|
|
pbpdu Received BPDU information to record
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Locking Constraints:
|
|
|
|
ASSUMES the caller has acquired gSTALock
|
|
|
|
--*/
|
|
{
|
|
ULONG msgAgeInMs = (pbpdu->MessageAge / 256) * 1000;
|
|
|
|
// Update the port's information with the new data
|
|
BrdgSTACopyID( pAdapt->STAInfo.DesignatedRootID, pbpdu->RootID );
|
|
pAdapt->STAInfo.DesignatedCost = pbpdu->RootCost;
|
|
BrdgSTACopyID( pAdapt->STAInfo.DesignatedBridgeID, pbpdu->BridgeID );
|
|
pAdapt->STAInfo.DesignatedPort = pbpdu->PortID;
|
|
|
|
// Start the message age timer. It is specified to expire after
|
|
// gMaxAge - MessageAge STA time units.
|
|
if( pbpdu->MessageAge < gMaxAge )
|
|
{
|
|
BrdgSTASetTimerWithSTATime( &pAdapt->STAInfo.MessageAgeTimer,
|
|
gMaxAge - pbpdu->MessageAge, FALSE /*Not periodic*/ );
|
|
}
|
|
else
|
|
{
|
|
// How odd. The message was already too old. Start the timer so that it
|
|
// will expire immediately.
|
|
|
|
THROTTLED_DBGPRINT(STA, ("Received over-age BPDU (%i / %i) on adapter %p", pbpdu->MessageAge,
|
|
gMaxAge, pAdapt));
|
|
|
|
BrdgSTASetTimerWithSTATime( &pAdapt->STAInfo.MessageAgeTimer, 0, FALSE /*Not periodic*/ );
|
|
}
|
|
|
|
NdisGetSystemUpTime( &pAdapt->STAInfo.LastConfigTime );
|
|
|
|
// Roll back by the age of the info we got.
|
|
SAFEASSERT( msgAgeInMs < pAdapt->STAInfo.LastConfigTime );
|
|
pAdapt->STAInfo.LastConfigTime -= msgAgeInMs;
|
|
}
|
|
|
|
VOID
|
|
BrdgSTARecordTimeoutInfo(
|
|
IN PCONFIG_BPDU pbpdu
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Records timeout information conveyed by a BPDU received from the root bridge
|
|
|
|
Arguments:
|
|
|
|
pbpdu Received BPDU information to record
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Locking Constraints:
|
|
|
|
ASSUMES the caller has acquired gSTALock
|
|
|
|
--*/
|
|
{
|
|
gMaxAge = pbpdu->MaxAge;
|
|
gHelloTime = pbpdu->HelloTime;
|
|
gForwardDelay = pbpdu->ForwardDelay;
|
|
BrdgSTAUpdateTopologyChange( pbpdu->bTopologyChange );
|
|
}
|
|
|
|
VOID
|
|
BrdgSTAGenerateConfigBPDUs()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sends configuration BPDUs out every designated port
|
|
|
|
Arguments:
|
|
|
|
pbpdu Received BPDU information to record
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Locking Constraints:
|
|
|
|
ASSUMES the caller does NOT hold gSTALock
|
|
|
|
--*/
|
|
{
|
|
LOCK_STATE LockState;
|
|
PADAPT Adapters[MAX_ADAPTERS];
|
|
INT cmpID;
|
|
PADAPT pAdapt;
|
|
UINT numAdapters = 0, i;
|
|
|
|
NdisAcquireSpinLock( &gSTALock );
|
|
SAFEASSERT( gHaveID );
|
|
NdisAcquireReadWriteLock( &gAdapterListLock, FALSE /*read only*/, &LockState );
|
|
|
|
for( pAdapt = gAdapterList; pAdapt != NULL; pAdapt = pAdapt->Next )
|
|
{
|
|
if( (pAdapt->bSTAInited) && (pAdapt->State != Disabled) )
|
|
{
|
|
cmpID = BrdgSTABridgeIDCmp( pAdapt->STAInfo.DesignatedBridgeID, gOurID );
|
|
|
|
if( (cmpID == 0) && (pAdapt->STAInfo.ID == pAdapt->STAInfo.DesignatedPort) )
|
|
{
|
|
// This adapter is a designated port. We will send a config BPDU out it.
|
|
BrdgAcquireAdapterInLock( pAdapt );
|
|
Adapters[numAdapters] = pAdapt;
|
|
numAdapters++;
|
|
}
|
|
}
|
|
}
|
|
|
|
NdisReleaseReadWriteLock( &gAdapterListLock, &LockState );
|
|
NdisReleaseSpinLock( &gSTALock );
|
|
|
|
// Send a BPDU out every adapter that we chose
|
|
for( i = 0; i < numAdapters; i++ )
|
|
{
|
|
BrdgSTATransmitConfig(Adapters[i]);
|
|
BrdgReleaseAdapter(Adapters[i]);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
BrdgSTARootSelection()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Examines the information associated with every bridge port to determine
|
|
the root bridge ID and root port
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Locking Constraints:
|
|
|
|
ASSUMES the caller has acquired gSTALock
|
|
|
|
--*/
|
|
{
|
|
LOCK_STATE LockState;
|
|
PADAPT pAdapt, pRootAdapt = NULL;
|
|
INT cmp;
|
|
|
|
SAFEASSERT( gHaveID );
|
|
NdisAcquireReadWriteLock( &gAdapterListLock, FALSE /*read only*/, &LockState );
|
|
|
|
for( pAdapt = gAdapterList; pAdapt != NULL; pAdapt = pAdapt->Next )
|
|
{
|
|
if( (pAdapt->bSTAInited) && (pAdapt->State != Disabled) )
|
|
{
|
|
/*
|
|
We consider the information advertised on each link to determine which port should
|
|
be the new root port. If no link advertises sufficiently attractive information, we declare
|
|
ourselves to be the root. A port is acceptable as the root port if all the following
|
|
conditions hold:
|
|
|
|
(1) The port receiving the advertisement must not be a designated port
|
|
(2) The link's advertised root must have a lower ID than us
|
|
(3) The link's advertised root ID must be lower than the advertised root ID on any other link
|
|
(4) If the advertised root ID is the same as another advertised root, the cost-to-root
|
|
must be lower
|
|
(5) If the root ID and cost are the same, the designated bridge on the port must have a
|
|
lower ID than the designated bridge on other ports (this chooses arbitrarily
|
|
between two bridges that can reach the root with the same cost)
|
|
(6) If the root ID, cost-to-root and designated bridge IDs are the same, the designated
|
|
port must be less than on other ports (this happens if two links have the same
|
|
designated bridge)
|
|
(7) If the root ID, cost-to-root, designated bridge ID and designated port IDs are all
|
|
the same, the port number of the port itself must be lower (this only happens if
|
|
we have more than one port onto the same physical link; we pick the lower-numbered
|
|
one as the root port)
|
|
*/
|
|
|
|
cmp = BrdgSTABridgeIDCmp( pAdapt->STAInfo.DesignatedBridgeID, gOurID );
|
|
|
|
if( (cmp != 0) || (pAdapt->STAInfo.ID != pAdapt->STAInfo.DesignatedPort) ) // (1)
|
|
{
|
|
cmp = BrdgSTABridgeIDCmp(pAdapt->STAInfo.DesignatedRootID, gOurID);
|
|
|
|
if( cmp == -1 ) // (2)
|
|
{
|
|
BOOLEAN betterRoot = FALSE;
|
|
|
|
if( pRootAdapt == NULL )
|
|
{
|
|
// Hadn't seen a root better than ourselves before now; take this one.
|
|
betterRoot = TRUE;
|
|
}
|
|
else
|
|
{
|
|
// Compare the advertised root ID to our previous best
|
|
cmp = BrdgSTABridgeIDCmp(pAdapt->STAInfo.DesignatedRootID, pRootAdapt->STAInfo.DesignatedRootID);
|
|
|
|
if( cmp == -1 ) // (3)
|
|
{
|
|
betterRoot = TRUE;
|
|
}
|
|
else if( cmp == 0 )
|
|
{
|
|
PATH_COST thisCost = pAdapt->STAInfo.DesignatedCost + pAdapt->STAInfo.PathCost,
|
|
prevBestCost = pRootAdapt->STAInfo.DesignatedCost + pRootAdapt->STAInfo.PathCost;
|
|
|
|
if( thisCost < prevBestCost )
|
|
{
|
|
betterRoot = TRUE; // (4)
|
|
}
|
|
else if( thisCost == prevBestCost )
|
|
{
|
|
// Compare the IDs of the designated bridge
|
|
cmp = BrdgSTABridgeIDCmp(pAdapt->STAInfo.DesignatedBridgeID, pRootAdapt->STAInfo.DesignatedBridgeID);
|
|
|
|
if( cmp == -1 )
|
|
{
|
|
betterRoot = TRUE; // (5)
|
|
}
|
|
else if( cmp == 0 )
|
|
{
|
|
if( pAdapt->STAInfo.DesignatedPort < pRootAdapt->STAInfo.DesignatedPort )
|
|
{
|
|
betterRoot = TRUE; // (6)
|
|
}
|
|
else if( pAdapt->STAInfo.DesignatedPort == pRootAdapt->STAInfo.DesignatedPort )
|
|
{
|
|
if( pAdapt->STAInfo.ID < pRootAdapt->STAInfo.ID )
|
|
{
|
|
betterRoot = TRUE; // (7)
|
|
}
|
|
else
|
|
{
|
|
// Sanity-check that the two adapters' IDs are different!
|
|
SAFEASSERT( pAdapt->STAInfo.ID != pRootAdapt->STAInfo.ID );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if( betterRoot )
|
|
{
|
|
// We have a better root port.
|
|
pRootAdapt = pAdapt;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if( pRootAdapt == NULL )
|
|
{
|
|
gRootAdapter = NULL;
|
|
BrdgSTACopyID( gDesignatedRootID, gOurID );
|
|
gRootCost = 0;
|
|
}
|
|
else
|
|
{
|
|
gRootAdapter = pRootAdapt;
|
|
BrdgSTACopyID( gDesignatedRootID, pRootAdapt->STAInfo.DesignatedRootID );
|
|
gRootCost = pRootAdapt->STAInfo.DesignatedCost + pRootAdapt->STAInfo.PathCost;
|
|
}
|
|
|
|
NdisReleaseReadWriteLock( &gAdapterListLock, &LockState );
|
|
}
|
|
|
|
VOID
|
|
BrdgSTABecomeDesignatedPort(
|
|
IN PADAPT pAdapt
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sets the information associated with an adapter to make it a designated port
|
|
|
|
Arguments:
|
|
|
|
pAdapt The adapter to make designated
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Locking Constraints:
|
|
|
|
ASSUMES the caller has acquired gSTALock
|
|
|
|
--*/
|
|
{
|
|
SAFEASSERT( gHaveID );
|
|
BrdgSTACopyID( pAdapt->STAInfo.DesignatedRootID, gDesignatedRootID );
|
|
pAdapt->STAInfo.DesignatedCost = gRootCost;
|
|
BrdgSTACopyID( pAdapt->STAInfo.DesignatedBridgeID, gOurID );
|
|
pAdapt->STAInfo.DesignatedPort = pAdapt->STAInfo.ID;
|
|
}
|
|
|
|
VOID
|
|
BrdgSTADesignatedPortSelection()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Examines the information associated with each port to determine
|
|
which should become designated ports
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Locking Constraints:
|
|
|
|
ASSUMES the caller has acquired gSTALock
|
|
|
|
--*/
|
|
{
|
|
LOCK_STATE LockState;
|
|
PADAPT pAdapt;
|
|
INT cmp;
|
|
|
|
SAFEASSERT( gHaveID );
|
|
NdisAcquireReadWriteLock( &gAdapterListLock, FALSE /*read only*/, &LockState );
|
|
|
|
for( pAdapt = gAdapterList; pAdapt != NULL; pAdapt = pAdapt->Next )
|
|
{
|
|
if( pAdapt->bSTAInited )
|
|
{
|
|
BOOLEAN becomeDesignated = FALSE;
|
|
|
|
/* We consider each port to determine whether it should become a designated port
|
|
(if it previously was not one). A port becomes a designated port if the
|
|
following conditions hold:
|
|
|
|
(1) The port is the link's designated port by advertised info
|
|
(2) The link's previous designated root is not the correct root
|
|
(3) Our cost-to-root is lower than the current cost advertised on the link
|
|
(4) We have a same cost-to-root but a lower ID than the current designated
|
|
bridge on the link
|
|
(5) We have the same cost-to-root and ID as the designated bridge on the link
|
|
but a lower port number (this only happens if we have two or more ports
|
|
on the same physical link)
|
|
*/
|
|
|
|
// See if the link's designated bridge is already us
|
|
cmp = BrdgSTABridgeIDCmp(pAdapt->STAInfo.DesignatedBridgeID, gOurID);
|
|
|
|
if( (cmp == 0) && (pAdapt->STAInfo.DesignatedPort == pAdapt->STAInfo.ID) )
|
|
{
|
|
becomeDesignated = TRUE; // (1)
|
|
}
|
|
else
|
|
{
|
|
// Compare the link's advertised root to the one we believe is root
|
|
cmp = BrdgSTABridgeIDCmp(pAdapt->STAInfo.DesignatedRootID, gDesignatedRootID);
|
|
|
|
if( cmp != 0 )
|
|
{
|
|
becomeDesignated = TRUE; // (2)
|
|
}
|
|
else if( gRootCost < pAdapt->STAInfo.DesignatedCost )
|
|
{
|
|
becomeDesignated = TRUE; // (3)
|
|
}
|
|
else if( gRootCost == pAdapt->STAInfo.DesignatedCost )
|
|
{
|
|
// Compare the link's designated bridge to our own ID
|
|
cmp = BrdgSTABridgeIDCmp(gOurID, pAdapt->STAInfo.DesignatedBridgeID);
|
|
|
|
if( cmp == -1 )
|
|
{
|
|
becomeDesignated = TRUE; // (4)
|
|
}
|
|
else if( cmp == 0 )
|
|
{
|
|
if( pAdapt->STAInfo.ID < pAdapt->STAInfo.DesignatedPort )
|
|
{
|
|
becomeDesignated = TRUE; // (5)
|
|
}
|
|
else
|
|
{
|
|
// If this SAFEASSERT fires, we should have succeeded on test (1)
|
|
SAFEASSERT( pAdapt->STAInfo.ID > pAdapt->STAInfo.DesignatedPort );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if( becomeDesignated )
|
|
{
|
|
BrdgSTABecomeDesignatedPort( pAdapt );
|
|
}
|
|
}
|
|
}
|
|
|
|
NdisReleaseReadWriteLock( &gAdapterListLock, &LockState );
|
|
}
|
|
|
|
BOOLEAN
|
|
BrdgSTATopologyChangeDetected()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Takes appropriate action when a topology change is detected. If we are
|
|
the root, this consists of setting the TopologyChange flag in future
|
|
BPDUs until the expiry of the gTopologyChangeTimer. If we are not the
|
|
root, this consists of sending a TCN BPDU periodically until it is
|
|
acknowledged
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
TRUE means the caller should arrange to send a TCN BPDU from outside the
|
|
gSTALock. FALSE means it is not necessary to send such a packet.
|
|
|
|
Locking Constraints:
|
|
|
|
ASSUMES the caller has acquired gSTALock
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN rc = FALSE;
|
|
|
|
if( BrdgSTAWeAreRoot() )
|
|
{
|
|
BrdgSTAUpdateTopologyChange( TRUE );
|
|
BrdgSTASetTimerWithSTATime( &gTopologyChangeTimer, DEFAULT_MAX_AGE + DEFAULT_FORWARD_DELAY, FALSE /*Not periodic*/ );
|
|
}
|
|
else
|
|
{
|
|
rc = TRUE;
|
|
BrdgSTASetTimerWithSTATime( &gTopologyChangeNotificationTimer, DEFAULT_HELLO_TIME, FALSE /*Not periodic*/ );
|
|
}
|
|
|
|
gTopologyChangeDetected = TRUE;
|
|
|
|
return rc;
|
|
}
|
|
|
|
VOID
|
|
BrdgSTAMakeForwarding(
|
|
IN PADAPT pAdapt
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Starts the process of putting an adapter in the forwarding state.
|
|
|
|
Adapters must pass through the Listening and Learning states before entering
|
|
the Forwarding state.
|
|
|
|
Arguments:
|
|
|
|
pAdapt The adapter
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Locking Constraints:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
if( pAdapt->State == Blocking )
|
|
{
|
|
BrdgSTASetAdapterState( pAdapt, Listening );
|
|
BrdgSTASetTimerWithSTATime( &pAdapt->STAInfo.ForwardDelayTimer, gForwardDelay, FALSE /*Not periodic*/ );
|
|
}
|
|
}
|
|
|
|
BOOLEAN
|
|
BrdgSTAMakeBlocking(
|
|
IN PADAPT pAdapt
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Puts an adapter in the blocking state
|
|
|
|
Arguments:
|
|
|
|
pAdapt The adapter
|
|
|
|
Return Value:
|
|
|
|
TRUE means the caller should arrange to send a TCN BPDU from outside the
|
|
gSTALock. FALSE means it is not necessary to send such a packet.
|
|
|
|
Locking Constraints:
|
|
|
|
ASSUMES the caller has acquired gSTALock
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN rc = FALSE;
|
|
|
|
if( pAdapt->State != Blocking )
|
|
{
|
|
if( (pAdapt->State == Forwarding) ||
|
|
(pAdapt->State == Learning) )
|
|
{
|
|
rc = BrdgSTATopologyChangeDetected();
|
|
}
|
|
|
|
BrdgSTASetAdapterState( pAdapt, Blocking );
|
|
BrdgCancelTimer( &pAdapt->STAInfo.ForwardDelayTimer );
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
BOOLEAN
|
|
BrdgSTAPortStateSelection()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Examines all ports and puts them in an appropriate state
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
TRUE means the caller should arrange to send a TCN BPDU from outside the
|
|
gSTALock. FALSE means it is not necessary to send such a packet.
|
|
|
|
Locking Constraints:
|
|
|
|
ASSUMES the caller has acquired gSTALock
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN rc = FALSE;
|
|
LOCK_STATE LockState;
|
|
PADAPT pAdapt;
|
|
|
|
SAFEASSERT( gHaveID );
|
|
|
|
NdisAcquireReadWriteLock( &gAdapterListLock, FALSE /*read only*/, &LockState );
|
|
|
|
for( pAdapt = gAdapterList; pAdapt != NULL; pAdapt = pAdapt->Next )
|
|
{
|
|
if( pAdapt->bSTAInited )
|
|
{
|
|
if( pAdapt == gRootAdapter )
|
|
{
|
|
pAdapt->STAInfo.bConfigPending = FALSE;
|
|
pAdapt->STAInfo.bTopologyChangeAck = FALSE;
|
|
BrdgSTAMakeForwarding( pAdapt );
|
|
}
|
|
else if( (BrdgSTABridgeIDCmp(pAdapt->STAInfo.DesignatedBridgeID, gOurID) == 0) &&
|
|
(pAdapt->STAInfo.DesignatedPort == pAdapt->STAInfo.ID) )
|
|
{
|
|
// This port is a designated port.
|
|
BrdgCancelTimer( &pAdapt->STAInfo.MessageAgeTimer );
|
|
pAdapt->STAInfo.LastConfigTime = 0L;
|
|
BrdgSTAMakeForwarding( pAdapt );
|
|
}
|
|
else
|
|
{
|
|
pAdapt->STAInfo.bConfigPending = FALSE;
|
|
pAdapt->STAInfo.bTopologyChangeAck = FALSE;
|
|
rc = BrdgSTAMakeBlocking( pAdapt );
|
|
}
|
|
}
|
|
}
|
|
|
|
NdisReleaseReadWriteLock( &gAdapterListLock, &LockState );
|
|
|
|
return rc;
|
|
}
|
|
|
|
VOID
|
|
BrdgSTATopologyChangeAcknowledged()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called when we receive an acknowledgement from the root bridge that
|
|
our topology change notification has been noted.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Locking Constraints:
|
|
|
|
ASSUMES the caller has acquired gSTALock
|
|
|
|
--*/
|
|
{
|
|
DBGPRINT(STA, ("BrdgSTATopologyChangeAcknowledged\n"));
|
|
gTopologyChangeDetected = FALSE;
|
|
BrdgCancelTimer( &gTopologyChangeNotificationTimer );
|
|
}
|
|
|
|
VOID
|
|
BrdgSTAAcknowledgeTopologyChange(
|
|
IN PADAPT pAdapt
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called when we are the root bridge to send a config BPDU acknowledging
|
|
another bridge's topology change notification
|
|
|
|
Arguments:
|
|
|
|
pAdapt The adapter on which the TCN was received
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Locking Constraints:
|
|
|
|
ASSUMES the caller DOES NOT have gSTALock
|
|
|
|
--*/
|
|
{
|
|
DBGPRINT(STA, ("BrdgSTAAcknowledgeTopologyChange\n"));
|
|
pAdapt->STAInfo.bTopologyChangeAck = TRUE;
|
|
BrdgSTATransmitConfig( pAdapt );
|
|
}
|
|
|
|
VOID
|
|
BrdgSTAProcessConfigBPDU(
|
|
IN PADAPT pAdapt,
|
|
IN PCONFIG_BPDU pbpdu
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Processes received BPDU information
|
|
|
|
Arguments:
|
|
|
|
pAdapt The adapter on which the BPDU was received
|
|
pbpdu The received information
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Locking Constraints:
|
|
|
|
ASSUMES the caller does NOT have gSTALock
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN bWasRoot;
|
|
|
|
NdisAcquireSpinLock( &gSTALock );
|
|
|
|
bWasRoot = BrdgSTAWeAreRoot();
|
|
|
|
if( BrdgSTASupersedesPortInfo(pAdapt, pbpdu) )
|
|
{
|
|
BOOLEAN bTransmitTCN = FALSE;
|
|
|
|
// The new information is better than what we had before. Use it.
|
|
BrdgSTARecordConfigInfo(pAdapt, pbpdu);
|
|
BrdgSTAConfigUpdate();
|
|
bTransmitTCN = BrdgSTAPortStateSelection();
|
|
|
|
if( bWasRoot && (! BrdgSTAWeAreRoot()) )
|
|
{
|
|
// We used to be the root bridge but now we're not!
|
|
DBGPRINT(STA, ("Saw superseding information that made us NOT root on adapter %p\n", pAdapt));
|
|
|
|
BrdgCancelTimer( &gHelloTimer );
|
|
|
|
if( gTopologyChangeDetected )
|
|
{
|
|
BrdgCancelTimer( &gTopologyChangeTimer );
|
|
bTransmitTCN = TRUE;
|
|
BrdgSTASetTimerWithSTATime( &gTopologyChangeNotificationTimer, DEFAULT_HELLO_TIME, FALSE /*Not periodic*/ );
|
|
}
|
|
}
|
|
|
|
if( pAdapt == gRootAdapter )
|
|
{
|
|
// This is the root port. Heed config information from the root and pass along
|
|
// its information.
|
|
BrdgSTARecordTimeoutInfo( pbpdu );
|
|
|
|
if( pbpdu->bTopologyChangeAck )
|
|
{
|
|
BrdgSTATopologyChangeAcknowledged();
|
|
}
|
|
|
|
// Don't send packets from inside the spin lock
|
|
NdisReleaseSpinLock( &gSTALock );
|
|
|
|
BrdgSTAGenerateConfigBPDUs();
|
|
}
|
|
else
|
|
{
|
|
NdisReleaseSpinLock( &gSTALock );
|
|
}
|
|
|
|
if( bTransmitTCN )
|
|
{
|
|
BrdgSTATransmitTCNPacket();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// The received information does not supersede our previous info
|
|
SAFEASSERT( gHaveID );
|
|
|
|
if( (BrdgSTABridgeIDCmp(pAdapt->STAInfo.DesignatedBridgeID, gOurID) == 0) &&
|
|
(pAdapt->STAInfo.DesignatedPort == pAdapt->STAInfo.ID) )
|
|
{
|
|
NdisReleaseSpinLock( &gSTALock );
|
|
|
|
// This is the designated port for this link, and the information we just received
|
|
// is inferior to the information we already have. Reply by sending out our own info.
|
|
BrdgSTATransmitConfig(pAdapt);
|
|
}
|
|
else
|
|
{
|
|
NdisReleaseSpinLock( &gSTALock );
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
BrdgSTAProcessTCNBPDU(
|
|
IN PADAPT pAdapt
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Processes a received TopologyChangeNotification BPDU
|
|
|
|
Arguments:
|
|
|
|
pAdapt The adapter on which the TCN was received
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Locking Constraints:
|
|
|
|
ASSUMES the caller does NOT have gSTALock
|
|
|
|
--*/
|
|
{
|
|
DBGPRINT(STA, ("BrdgSTAProcessTCNBPDU()\n"));
|
|
SAFEASSERT( gHaveID );
|
|
|
|
NdisAcquireSpinLock( &gSTALock );
|
|
|
|
if( (BrdgSTABridgeIDCmp(pAdapt->STAInfo.DesignatedBridgeID, gOurID) == 0) &&
|
|
(pAdapt->STAInfo.DesignatedPort == pAdapt->STAInfo.ID) )
|
|
{
|
|
BOOLEAN bTransmitTCN = FALSE;
|
|
|
|
// This is a designated port.
|
|
bTransmitTCN = BrdgSTATopologyChangeDetected();
|
|
NdisReleaseSpinLock( &gSTALock );
|
|
|
|
if( bTransmitTCN )
|
|
{
|
|
BrdgSTATransmitTCNPacket();
|
|
}
|
|
|
|
BrdgSTAAcknowledgeTopologyChange(pAdapt);
|
|
}
|
|
else
|
|
{
|
|
NdisReleaseSpinLock( &gSTALock );
|
|
}
|
|
}
|
|
|
|
VOID
|
|
BrdgSTAHelloTimerExpiry(
|
|
IN PVOID Unused
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called when the Hello Timer expires. Sends another Config BPDU.
|
|
|
|
Arguments:
|
|
|
|
Unused
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Locking Constraints:
|
|
|
|
ASSUMES the caller does NOT have gSTALock
|
|
|
|
--*/
|
|
{
|
|
BrdgSTAGenerateConfigBPDUs();
|
|
}
|
|
|
|
VOID
|
|
BrdgSTAMessageAgeTimerExpiry(
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called when the Message Age Timer expires. Recalculates STA information
|
|
given the fact that no bridge is being heard on the given port anymore.
|
|
|
|
Arguments:
|
|
|
|
Context The adapter on which the timer expired
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Locking Constraints:
|
|
|
|
ASSUMES the caller does NOT have gSTALock
|
|
|
|
--*/
|
|
{
|
|
PADAPT pAdapt;
|
|
BOOLEAN bWasRoot, bTransmitTCN = FALSE;
|
|
|
|
NdisAcquireSpinLock( &gSTALock );
|
|
|
|
pAdapt = (PADAPT)Context;
|
|
pAdapt->STAInfo.LastConfigTime = 0L;
|
|
bWasRoot = BrdgSTAWeAreRoot();
|
|
|
|
BrdgSTABecomeDesignatedPort(pAdapt);
|
|
BrdgSTAConfigUpdate();
|
|
bTransmitTCN = BrdgSTAPortStateSelection();
|
|
|
|
if( BrdgSTAWeAreRoot() && (! bWasRoot) )
|
|
{
|
|
DBGPRINT(STA, ("Became root through message age timer expiry of %p\n", pAdapt));
|
|
|
|
// We just became root.
|
|
gMaxAge = DEFAULT_MAX_AGE;
|
|
gHelloTime = DEFAULT_HELLO_TIME;
|
|
gForwardDelay = DEFAULT_FORWARD_DELAY;
|
|
|
|
bTransmitTCN = BrdgSTATopologyChangeDetected();
|
|
BrdgCancelTimer( &gTopologyChangeNotificationTimer );
|
|
|
|
NdisReleaseSpinLock( &gSTALock );
|
|
|
|
BrdgSTAGenerateConfigBPDUs();
|
|
BrdgSTASetTimerWithSTATime( &gHelloTimer, gHelloTime, TRUE /*Periodic*/ );
|
|
}
|
|
else
|
|
{
|
|
NdisReleaseSpinLock( &gSTALock );
|
|
}
|
|
|
|
if( bTransmitTCN )
|
|
{
|
|
BrdgSTATransmitTCNPacket();
|
|
}
|
|
}
|
|
|
|
VOID
|
|
BrdgSTAForwardDelayTimerExpiry(
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called when the Forward Delay Timer expires. Continues stepping an
|
|
adapter through the process of becoming Forwarding.
|
|
|
|
Arguments:
|
|
|
|
Context The adapter on which the timer expired
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Locking Constraints:
|
|
|
|
ASSUMES the caller does NOT have gSTALock
|
|
|
|
--*/
|
|
{
|
|
PADAPT pAdapt = (PADAPT)Context;
|
|
BOOLEAN bTransmitTCN = FALSE;
|
|
|
|
NdisAcquireSpinLock( &gSTALock );
|
|
|
|
SAFEASSERT( gHaveID );
|
|
|
|
if( pAdapt->State == Listening )
|
|
{
|
|
// Move to learning state
|
|
BrdgSTASetAdapterState( pAdapt, Learning );
|
|
BrdgSTASetTimerWithSTATime( &pAdapt->STAInfo.ForwardDelayTimer, gForwardDelay, FALSE /*Not periodic*/ );
|
|
}
|
|
else if( pAdapt->State == Learning )
|
|
{
|
|
LOCK_STATE LockState;
|
|
PADAPT anAdapt;
|
|
|
|
// Move to forwarding state
|
|
BrdgSTASetAdapterState( pAdapt, Forwarding );
|
|
|
|
// If we are the designated port on any link, we need to signal a topology change
|
|
// notification.
|
|
NdisAcquireReadWriteLock( &gAdapterListLock, FALSE/*Read-only*/, &LockState );
|
|
|
|
for( anAdapt = gAdapterList; anAdapt != NULL; anAdapt = anAdapt->Next )
|
|
{
|
|
if( anAdapt->bSTAInited )
|
|
{
|
|
if( BrdgSTABridgeIDCmp(anAdapt->STAInfo.DesignatedBridgeID, gOurID) == 0 )
|
|
{
|
|
bTransmitTCN = BrdgSTATopologyChangeDetected();
|
|
}
|
|
}
|
|
}
|
|
|
|
NdisReleaseReadWriteLock( &gAdapterListLock, &LockState );
|
|
}
|
|
|
|
NdisReleaseSpinLock( &gSTALock );
|
|
|
|
if( bTransmitTCN )
|
|
{
|
|
BrdgSTATransmitTCNPacket();
|
|
}
|
|
}
|
|
|
|
VOID
|
|
BrdgSTATopologyChangeNotificationTimerExpiry(
|
|
IN PVOID Unused
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called when the Topology Change Notification Timer expires.
|
|
Transmits another TCN packet.
|
|
|
|
Arguments:
|
|
|
|
Unused
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Locking Constraints:
|
|
|
|
ASSUMES the caller does NOT have gSTALock
|
|
|
|
--*/
|
|
{
|
|
if (BrdgFwdBridgingNetworks())
|
|
{
|
|
BrdgSTATransmitTCNPacket();
|
|
BrdgSTASetTimerWithSTATime( &gTopologyChangeNotificationTimer, DEFAULT_HELLO_TIME, FALSE /*Not periodic*/ );
|
|
}
|
|
}
|
|
|
|
VOID
|
|
BrdgSTATopologyChangeTimerExpiry(
|
|
IN PVOID Unused
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called when the Topology Change Timer expires. Stops setting the TopologyChange
|
|
flag in outbound Config BPDUs.
|
|
|
|
Arguments:
|
|
|
|
Unused
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Locking Constraints:
|
|
|
|
ASSUMES the caller does NOT have gSTALock
|
|
|
|
--*/
|
|
{
|
|
NdisAcquireSpinLock( &gSTALock );
|
|
gTopologyChangeDetected = FALSE;
|
|
BrdgSTAUpdateTopologyChange( FALSE );
|
|
NdisReleaseSpinLock( &gSTALock );
|
|
}
|
|
|
|
VOID
|
|
BrdgSTAHoldTimerExpiry(
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called when the Hold Timer expires. Sends a Config BPDU.
|
|
|
|
Arguments:
|
|
|
|
Context The adapter on which the timer expired
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Locking Constraints:
|
|
|
|
ASSUMES the caller does NOT have gSTALock
|
|
|
|
--*/
|
|
{
|
|
PADAPT pAdapt = (PADAPT)Context;
|
|
|
|
NdisAcquireSpinLock( &gSTALock );
|
|
|
|
if( pAdapt->STAInfo.bConfigPending )
|
|
{
|
|
NdisReleaseSpinLock( &gSTALock );
|
|
BrdgSTATransmitConfig( pAdapt );
|
|
}
|
|
else
|
|
{
|
|
NdisReleaseSpinLock( &gSTALock );
|
|
}
|
|
}
|
|
|
|
VOID
|
|
BrdgSTACancelTimersGPO()
|
|
{
|
|
LOCK_STATE LockState;
|
|
PADAPT pAdapt = NULL;
|
|
|
|
//
|
|
// We need to cancel the general STA timers.
|
|
//
|
|
BrdgCancelTimer( &gTopologyChangeTimer );
|
|
BrdgCancelTimer( &gTopologyChangeNotificationTimer );
|
|
BrdgCancelTimer( &gHelloTimer );
|
|
|
|
//
|
|
// And the individual HoldTimers and MessageAgeTimers
|
|
//
|
|
NdisAcquireReadWriteLock( &gAdapterListLock, FALSE/*Read-only*/, &LockState );
|
|
|
|
for( pAdapt = gAdapterList; pAdapt != NULL; pAdapt = pAdapt->Next )
|
|
{
|
|
// This will only cancel the timer if it is running.
|
|
BrdgCancelTimer(&pAdapt->STAInfo.HoldTimer);
|
|
BrdgCancelTimer(&pAdapt->STAInfo.MessageAgeTimer);
|
|
}
|
|
|
|
NdisReleaseReadWriteLock( &gAdapterListLock, &LockState );
|
|
}
|
|
|
|
VOID
|
|
BrdgSTARestartTimersGPO()
|
|
{
|
|
BrdgSTASetTimerWithSTATime( &gHelloTimer, gHelloTime, TRUE );
|
|
}
|
|
|
|
VOID
|
|
BrdgSTAResetSTAInfoGPO()
|
|
{
|
|
BOOLEAN PortSelection = FALSE;
|
|
|
|
NdisAcquireSpinLock(&gSTALock);
|
|
|
|
PortSelection = BrdgSTAPortStateSelection();
|
|
|
|
// Release the spinlock before we send packets over the wire.
|
|
NdisReleaseSpinLock(&gSTALock);
|
|
|
|
BrdgSTASetTimerWithSTATime( &gTopologyChangeNotificationTimer, DEFAULT_HELLO_TIME, FALSE /*Not periodic*/ );
|
|
|
|
if (PortSelection)
|
|
{
|
|
BrdgSTATransmitTCNPacket();
|
|
}
|
|
|
|
if (!BrdgSTAWeAreRoot())
|
|
{
|
|
// Set the timer on the root adapter to expire immediately, this will force us to re-determine
|
|
// our state.
|
|
BrdgSTASetTimerWithSTATime( &gRootAdapter->STAInfo.MessageAgeTimer, 0, FALSE /*Not periodic*/ );
|
|
}
|
|
else
|
|
{
|
|
BrdgSTAGenerateConfigBPDUs();
|
|
}
|
|
BrdgSTARestartTimersGPO();
|
|
}
|