windows-nt/Source/XPSP1/NT/base/cluster/clusnet/xport/cnpif.c

2099 lines
52 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
cnpif.c
Abstract:
Interface management routines for the Cluster Network Protocol.
Author:
Mike Massa (mikemas) January 6, 1997
Revision History:
Who When What
-------- -------- ----------------------------------------------
mikemas 01-06-97 created
Notes:
--*/
#include "precomp.h"
#pragma hdrstop
#include "cnpif.tmh"
#include <ntddndis.h>
//
// Routines exported within CNP
//
BOOLEAN
CnpIsBetterInterface(
PCNP_INTERFACE Interface1,
PCNP_INTERFACE Interface2
)
{
if ( (Interface2 == NULL)
||
(Interface1->State > Interface2->State)
||
( (Interface1->State == Interface2->State)
&&
CnpIsHigherPriority(Interface1->Priority, Interface2->Priority)
)
)
{
return(TRUE);
}
return(FALSE);
}
VOID
CnpWalkInterfacesOnNode(
PCNP_NODE Node,
PCNP_INTERFACE_UPDATE_ROUTINE UpdateRoutine
)
/*++
Routine Description:
Walks the interface list of a node and performs a specified
operation on each interface.
Arguments:
Node - The node on which to operate.
UpdateRoutine - The operation to perform on each interface.
Return Value:
None.
Notes:
Called with node object lock held.
Valid Update Routines:
CnpOnlinePendingInterfaceWrapper
CnpOfflineInterfaceWrapper
--*/
{
PLIST_ENTRY entry, nextEntry;
PCNP_INTERFACE interface;
CnVerifyCpuLockMask(
CNP_NODE_OBJECT_LOCK, // Required
0, // Forbidden
CNP_NODE_OBJECT_LOCK_MAX // Maximum
);
entry = Node->InterfaceList.Flink;
while (entry != &(Node->InterfaceList)) {
//
// Save a pointer to the next entry now in case we delete the
// current entry.
//
nextEntry = entry->Flink;
interface = CONTAINING_RECORD(
entry,
CNP_INTERFACE,
NodeLinkage
);
CnAcquireLockAtDpc(&(interface->Network->Lock));
interface->Network->Irql = DISPATCH_LEVEL;
(*UpdateRoutine)(interface);
//
// The network object lock was released.
//
entry = nextEntry;
}
CnVerifyCpuLockMask(
CNP_NODE_OBJECT_LOCK, // Required
0, // Forbidden
CNP_NODE_OBJECT_LOCK_MAX // Maximum
);
return;
} // CnpWalkInterfacesOnNode
VOID
CnpWalkInterfacesOnNetwork(
PCNP_NETWORK Network,
PCNP_INTERFACE_UPDATE_ROUTINE UpdateRoutine
)
/*++
Routine Description:
Walks the node table and the interface list of each node looking
for interfaces on a specified network. Performs a specified operation
on each matching interface.
Arguments:
Network - The target network.
UpdateRoutine - The operation to perform on each matching interface.
Return Value:
None.
Notes:
Called with no locks held.
Valid Update Routines:
CnpOnlinePendingInterfaceWrapper
CnpOfflineInterfaceWrapper
CnpDeleteInterface
CnpRecalculateInterfacePriority
--*/
{
ULONG i;
CN_IRQL tableIrql;
PCNP_NODE node;
PLIST_ENTRY entry;
PCNP_INTERFACE interface;
CnVerifyCpuLockMask(
0, // Required
CNP_LOCK_RANGE, // Forbidden
CNP_PRECEEDING_LOCK_RANGE // Maximum
);
CnAcquireLock(&CnpNodeTableLock, &tableIrql);
CnAssert(CnMinValidNodeId != ClusterInvalidNodeId);
CnAssert(CnMaxValidNodeId != ClusterInvalidNodeId);
for (i=CnMinValidNodeId; i <= CnMaxValidNodeId; i++) {
node = CnpNodeTable[i];
if (node != NULL) {
CnAcquireLockAtDpc(&(node->Lock));
CnReleaseLockFromDpc(&CnpNodeTableLock);
node->Irql = tableIrql;
for (entry = node->InterfaceList.Flink;
entry != &(node->InterfaceList);
entry = entry->Flink
)
{
interface = CONTAINING_RECORD(
entry,
CNP_INTERFACE,
NodeLinkage
);
if (interface->Network == Network) {
CnAcquireLockAtDpc(&(Network->Lock));
Network->Irql = DISPATCH_LEVEL;
(*UpdateRoutine)(interface);
//
// The network object lock was released.
// The node object lock is still held.
//
break;
}
}
CnReleaseLock(&(node->Lock), node->Irql);
CnAcquireLock(&CnpNodeTableLock, &tableIrql);
}
}
CnReleaseLock(&CnpNodeTableLock, tableIrql);
CnVerifyCpuLockMask(
0, // Required
CNP_LOCK_RANGE, // Forbidden
CNP_PRECEEDING_LOCK_RANGE // Maximum
);
return;
} // CnpWalkInterfacesOnNetwork
NTSTATUS
CnpOnlinePendingInterface(
PCNP_INTERFACE Interface
)
/*++
Routine Description:
Changes an Offline interface to the OnlinePending state.
This will enable heartbeats over this interface. When a heartbeat
is established, the interface will move to the Online state.
Arguments:
Interface - A pointer to the interface to change.
Return Value:
An NT status value.
Notes:
Called with associated node and network locks held.
Returns with network lock released.
Conforms to calling convention for PCNP_INTERFACE_UPDATE_ROUTINE.
--*/
{
NTSTATUS status = STATUS_SUCCESS;
PCNP_NODE node = Interface->Node;
PCNP_NETWORK network = Interface->Network;
CnVerifyCpuLockMask(
(CNP_NODE_OBJECT_LOCK | CNP_NETWORK_OBJECT_LOCK), // Required
0, // Forbidden
CNP_NETWORK_OBJECT_LOCK_MAX // Maximum
);
if ( (Interface->State == ClusnetInterfaceStateOffline) &&
(network->State == ClusnetNetworkStateOnline)
)
{
IF_CNDBG( CN_DEBUG_IFOBJ )
CNPRINT((
"[CNP] Moving interface (%u, %u) to OnlinePending state.\n",
node->Id,
network->Id
));
Interface->State = ClusnetInterfaceStateOnlinePending;
Interface->MissedHBs = 0;
//
// Send multicast discovery packets.
//
if (Interface->Node != CnpLocalNode
&& CnpIsNetworkMulticastCapable(network)) {
Interface->McastDiscoverCount = CNP_INTERFACE_MCAST_DISCOVERY;
}
//
// Place an active reference on the associated network.
//
CnpActiveReferenceNetwork(network);
//
// Update the node's CurrentInterface if appropriate.
//
if ( !CnpIsNetworkRestricted(network) &&
CnpIsBetterInterface(Interface, node->CurrentInterface)
)
{
node->CurrentInterface = Interface;
IF_CNDBG( CN_DEBUG_IFOBJ )
CNPRINT((
"[CNP] Network %u is now the best route to node %u\n",
network->Id,
node->Id
));
}
}
else {
status = STATUS_CLUSTER_INVALID_REQUEST;
}
CnReleaseLockFromDpc(&(network->Lock));
CnVerifyCpuLockMask(
(CNP_NODE_OBJECT_LOCK), // Required
0, // Forbidden
CNP_NODE_OBJECT_LOCK_MAX // Maximum
);
return(status);
} // CnpOnlinePendingInterface
VOID
CnpOnlinePendingInterfaceWrapper(
PCNP_INTERFACE Interface
)
/*++
Routine Description:
Wrapper for CnpOnlinePendingInterface that conforms to the calling
convention for PCNP_INTERFACE_UPDATE_ROUTINE.
Arguments:
Interface - A pointer to the interface to change.
Return Value:
None.
Notes:
Called with associated node and network locks held.
Returns with network lock released.
--*/
{
(VOID) CnpOnlinePendingInterface(Interface);
return;
} // CnpOnlinePendingInterfaceWrapper
NTSTATUS
CnpOfflineInterface(
PCNP_INTERFACE Interface
)
/*++
Routine Description:
Called to change an interface to the Offline state
when the associated network goes offline or the interface
is being deleted.
Arguments:
Interface - A pointer to the interface to change.
Return Value:
An NT status value.
Notes:
Called with associated node and network locks held.
Returns with network lock released.
Conforms to calling convention for PCNP_INTERFACE_UPDATE_ROUTINE.
--*/
{
NTSTATUS status = STATUS_SUCCESS;
PCNP_NODE node = Interface->Node;
PCNP_NETWORK network = Interface->Network;
CnVerifyCpuLockMask(
(CNP_NODE_OBJECT_LOCK | CNP_NETWORK_OBJECT_LOCK), // Required
0, // Forbidden
CNP_NETWORK_OBJECT_LOCK_MAX // Maximum
);
if (Interface->State != ClusnetInterfaceStateOffline) {
IF_CNDBG( CN_DEBUG_IFOBJ )
CNPRINT((
"[CNP] Moving interface (%u, %u) to Offline state.\n",
node->Id,
network->Id
));
Interface->State = ClusnetInterfaceStateOffline;
//
// Release the network lock.
//
CnReleaseLock(&(network->Lock), network->Irql);
//
// Update the node's CurrentInterface value if appropriate.
//
if (node->CurrentInterface == Interface) {
CnpUpdateNodeCurrentInterface(node);
if ( !CnpIsNodeUnreachable(node)
&&
( (node->CurrentInterface == NULL) ||
( node->CurrentInterface->State <
ClusnetInterfaceStateOnlinePending
)
)
)
{
//
// This node is now unreachable.
//
CnpDeclareNodeUnreachable(node);
}
}
//
// Change the node's reachability status via this network.
//
CnpMulticastChangeNodeReachability(
network,
node,
FALSE, // not reachable
TRUE, // raise event
NULL // OUT new mask
);
//
// Remove the active reference on the associated network.
// This releases the network lock.
//
CnAcquireLock(&(network->Lock), &(network->Irql));
CnpActiveDereferenceNetwork(network);
}
else {
CnAssert(network->Irql == DISPATCH_LEVEL);
CnReleaseLockFromDpc(&(network->Lock));
status = STATUS_CLUSTER_INVALID_REQUEST;
}
CnVerifyCpuLockMask(
(CNP_NODE_OBJECT_LOCK), // Required
0, // Forbidden
CNP_NODE_OBJECT_LOCK_MAX // Maximum
);
return(status);
} // CnpOfflineInterface
VOID
CnpOfflineInterfaceWrapper(
PCNP_INTERFACE Interface
)
/*++
Routine Description:
Wrapper for CnpOfflineInterface that conforms to the calling
convention for PCNP_INTERFACE_UPDATE_ROUTINE.
Arguments:
Interface - A pointer to the interface to change.
Return Value:
None.
Notes:
Called with associated node and network locks held.
Returns with network lock released.
--*/
{
(VOID) CnpOfflineInterface(Interface);
return;
} // CnpOfflineInterfaceWrapper
NTSTATUS
CnpOnlineInterface(
PCNP_INTERFACE Interface
)
/*++
Routine Description:
Called to change an OnlinePending interface to the Online state
after a heartbeat has been (re)established.
Arguments:
Interface - A pointer to the interface to change.
Return Value:
None.
Notes:
Called with associated node and network locks held.
Returns with network lock released.
--*/
{
NTSTATUS status = STATUS_SUCCESS;
PCNP_NODE node = Interface->Node;
PCNP_NETWORK network = Interface->Network;
CnVerifyCpuLockMask(
(CNP_NODE_OBJECT_LOCK | CNP_NETWORK_OBJECT_LOCK), // Required
0, // Forbidden
CNP_NETWORK_OBJECT_LOCK_MAX // Maximum
);
if ( (network->State == ClusnetNetworkStateOnline) &&
( (Interface->State == ClusnetInterfaceStateOnlinePending) ||
(Interface->State == ClusnetInterfaceStateUnreachable)
)
)
{
IF_CNDBG( CN_DEBUG_IFOBJ )
CNPRINT((
"[CNP] Moving interface (%u, %u) to Online state.\n",
node->Id,
network->Id
));
//
// Move the interface to the online state.
//
Interface->State = ClusnetInterfaceStateOnline;
CnAssert(network->Irql == DISPATCH_LEVEL);
CnReleaseLockFromDpc(&(network->Lock));
//
// Update the node's CurrentInterface if appropriate.
//
if (!CnpIsNetworkRestricted(network)) {
if (CnpIsBetterInterface(Interface, node->CurrentInterface)) {
node->CurrentInterface = Interface;
IF_CNDBG( CN_DEBUG_IFOBJ )
CNPRINT((
"[CNP] Network %u is now the best route to node %u\n",
network->Id,
node->Id
));
}
if (CnpIsNodeUnreachable(node)) {
CnpDeclareNodeReachable(node);
}
}
}
else {
CnAssert(network->Irql == DISPATCH_LEVEL);
CnReleaseLockFromDpc(&(network->Lock));
status = STATUS_CLUSTER_INVALID_REQUEST;
}
CnVerifyCpuLockMask(
(CNP_NODE_OBJECT_LOCK), // Required
0, // Forbidden
CNP_NODE_OBJECT_LOCK_MAX // Maximum
);
return(status);
} // CnpOnlineInterface
NTSTATUS
CnpFailInterface(
PCNP_INTERFACE Interface
)
/*++
Routine Description:
Called to change an Online or OnlinePending interface to the Failed
state after the heartbeat has been lost for some time.
Arguments:
Interface - A pointer to the interface to change.
Return Value:
None.
Notes:
Called with associated node and network locks held.
Returns with network lock released.
--*/
{
NTSTATUS status = STATUS_SUCCESS;
PCNP_NODE node = Interface->Node;
PCNP_NETWORK network = Interface->Network;
CnVerifyCpuLockMask(
(CNP_NODE_OBJECT_LOCK | CNP_NETWORK_OBJECT_LOCK), // Required
0, // Forbidden
CNP_NETWORK_OBJECT_LOCK_MAX // Maximum
);
if ( (network->State == ClusnetNetworkStateOnline) &&
(Interface->State >= ClusnetInterfaceStateOnlinePending)
)
{
IF_CNDBG( CN_DEBUG_IFOBJ )
CNPRINT((
"[CNP] Moving interface (%u, %u) to Failed state.\n",
node->Id,
network->Id
));
Interface->State = ClusnetInterfaceStateUnreachable;
CnAssert(network->Irql == DISPATCH_LEVEL);
//
// Reference the network so that it can't be deleted
// while we release the lock.
//
CnpReferenceNetwork(network);
CnReleaseLockFromDpc(&(network->Lock));
//
// Update the node's CurrentInterface value if appropriate.
//
if (node->CurrentInterface == Interface) {
CnpUpdateNodeCurrentInterface(node);
if ( (node->CurrentInterface == NULL)
||
( node->CurrentInterface->State <
ClusnetInterfaceStateOnlinePending
)
)
{
//
// This node is now unreachable.
//
CnpDeclareNodeUnreachable(node);
}
}
//
// Change the node's reachability status via this network.
//
CnpMulticastChangeNodeReachability(
network,
node,
FALSE, // not reachable
TRUE, // raise event
NULL // OUT new mask
);
//
// Drop the network reference. This releases the network
// lock.
//
CnAcquireLock(&(network->Lock), &(network->Irql));
CnpDereferenceNetwork(network);
}
else {
CnAssert(network->Irql == DISPATCH_LEVEL);
CnReleaseLockFromDpc(&(network->Lock));
status = STATUS_CLUSTER_INVALID_REQUEST;
}
CnVerifyCpuLockMask(
(CNP_NODE_OBJECT_LOCK), // Required
0, // Forbidden
CNP_NODE_OBJECT_LOCK_MAX // Maximum
);
return(status);
} // CnpFailInterface
VOID
CnpDeleteInterface(
IN PCNP_INTERFACE Interface
)
/*++
/*++
Routine Description:
Called to delete an interface.
Arguments:
Interface - A pointer to the interface to delete.
Return Value:
None.
Notes:
Called with node and network object locks held.
Returns with the network lock released.
Conforms to calling convention for PCNP_INTERFACE_UPDATE_ROUTINE.
--*/
{
CL_NODE_ID nodeId = Interface->Node->Id;
CL_NETWORK_ID networkId = Interface->Network->Id;
PCNP_NODE node = Interface->Node;
PCNP_NETWORK network = Interface->Network;
BOOLEAN isLocal = FALSE;
CnVerifyCpuLockMask(
(CNP_NODE_OBJECT_LOCK | CNP_NETWORK_OBJECT_LOCK), // Required
0, // Forbidden
CNP_NETWORK_OBJECT_LOCK_MAX // Maximum
);
IF_CNDBG( CN_DEBUG_IFOBJ )
CNPRINT((
"[CNP] Deleting interface (%u, %u)\n",
nodeId,
networkId
));
if (Interface->State >= ClusnetInterfaceStateUnreachable) {
(VOID) CnpOfflineInterface(Interface);
//
// The call released the network lock.
// Reacquire it.
//
CnAcquireLockAtDpc(&(network->Lock));
network->Irql = DISPATCH_LEVEL;
}
//
// Remove the interface from the node's interface list.
//
#if DBG
{
PLIST_ENTRY entry;
PCNP_INTERFACE oldInterface = NULL;
for (entry = node->InterfaceList.Flink;
entry != &(node->InterfaceList);
entry = entry->Flink
)
{
oldInterface = CONTAINING_RECORD(
entry,
CNP_INTERFACE,
NodeLinkage
);
if (oldInterface == Interface) {
break;
}
}
CnAssert(oldInterface == Interface);
}
#endif // DBG
RemoveEntryList(&(Interface->NodeLinkage));
//
// Remove the base reference that this node had on the network.
// This releases the network lock.
//
CnpDereferenceNetwork(network);
//
// Update the node's CurrentInterface if appropriate.
//
if (node->CurrentInterface == Interface) {
if (IsListEmpty(&(node->InterfaceList))) {
node->CurrentInterface = NULL;
}
else {
CnpUpdateNodeCurrentInterface(node);
}
}
CnFreePool(Interface);
IF_CNDBG( CN_DEBUG_IFOBJ )
CNPRINT((
"[CNP] Deleted interface (%u, %u)\n",
nodeId,
networkId
));
CnVerifyCpuLockMask(
(CNP_NODE_OBJECT_LOCK), // Required
0, // Forbidden
CNP_NODE_OBJECT_LOCK_MAX // Maximum
);
return;
} // CnpDeleteInterface
VOID
CnpReevaluateInterfaceRole(
IN PCNP_INTERFACE Interface
)
/*++
Routine Description:
Reevaluates the role of an interface after the corresponding network's
restriction state has been changed.
Arguments:
Interface - A pointer to the interface on which to operate.
Return Value:
None.
Notes:
Called with associated node and network locks held.
Returns with network lock released.
Conforms to calling convention for PCNP_INTERFACE_UPDATE_ROUTINE.
--*/
{
PCNP_NODE node = Interface->Node;
PCNP_NETWORK network = Interface->Network;
BOOLEAN restricted = CnpIsNetworkRestricted(network);
CnVerifyCpuLockMask(
(CNP_NODE_OBJECT_LOCK | CNP_NETWORK_OBJECT_LOCK), // Required
0, // Forbidden
CNP_NETWORK_OBJECT_LOCK_MAX // Maximum
);
//
// We don't really need the network lock. It's just part of
// the calling convention.
//
CnReleaseLockFromDpc(&(network->Lock));
if (restricted) {
if (node->CurrentInterface == Interface) {
CnpUpdateNodeCurrentInterface(node);
}
}
else if (node->CurrentInterface != Interface) {
CnpUpdateNodeCurrentInterface(node);
}
CnVerifyCpuLockMask(
(CNP_NODE_OBJECT_LOCK), // Required
0, // Forbidden
CNP_NODE_OBJECT_LOCK_MAX // Maximum
);
return;
} // CnpReevaluateInterfaceRole
VOID
CnpRecalculateInterfacePriority(
IN PCNP_INTERFACE Interface
)
/*++
Routine Description:
Recalculates the priority of interfaces which get their
priority from their associated network. Called after the network's
priority changes.
Arguments:
Interface - A pointer to the interface on which to operate.
Return Value:
None.
Notes:
Called with associated node and network locks held.
Returns with network lock released.
Conforms to calling convention for PCNP_INTERFACE_UPDATE_ROUTINE.
--*/
{
PCNP_NODE node = Interface->Node;
PCNP_NETWORK network = Interface->Network;
ULONG networkPriority = network->Priority;
ULONG oldPriority = Interface->Priority;
BOOLEAN restricted = CnpIsNetworkRestricted(network);
CnVerifyCpuLockMask(
(CNP_NODE_OBJECT_LOCK | CNP_NETWORK_OBJECT_LOCK), // Required
0, // Forbidden
CNP_NETWORK_OBJECT_LOCK_MAX // Maximum
);
//
// We don't really need the network lock. It's just part of
// the calling convention.
//
CnReleaseLockFromDpc(&(network->Lock));
if (CnpIsInterfaceUsingNetworkPriority(Interface)) {
Interface->Priority = networkPriority;
if (!restricted) {
if (Interface == node->CurrentInterface) {
if (CnpIsLowerPriority(Interface->Priority, oldPriority)) {
//
// Our priority got worse. Recalculate the best route.
//
CnpUpdateNodeCurrentInterface(node);
}
//
// Else, priority same or better. Nothing to do.
//
}
else if ( CnpIsBetterInterface(
Interface,
node->CurrentInterface
)
)
{
//
// Our priority got better.
//
IF_CNDBG(( CN_DEBUG_NODEOBJ | CN_DEBUG_NETOBJ ))
CNPRINT((
"[CNP] Network %u is now the best route to node %u\n",
network->Id,
node->Id
));
node->CurrentInterface = Interface;
}
}
}
CnVerifyCpuLockMask(
(CNP_NODE_OBJECT_LOCK), // Required
0, // Forbidden
CNP_NODE_OBJECT_LOCK_MAX // Maximum
);
return;
} // CnpRecalculateInterfacePriority
VOID
CnpUpdateNodeCurrentInterface(
IN PCNP_NODE Node
)
/*++
Routine Description:
Called to determine the best available interface for a node
after one of its interfaces changes state or priority.
Arguments:
Node - A pointer to the node on which to operate.
Return Value:
None.
Notes:
Called with node object lock held.
--*/
{
PLIST_ENTRY entry;
PCNP_INTERFACE interface;
PCNP_INTERFACE bestInterface = NULL;
CnVerifyCpuLockMask(
CNP_NODE_OBJECT_LOCK, // Required
0, // Forbidden
CNP_NODE_OBJECT_LOCK_MAX // Maximum
);
CnAssert(!IsListEmpty(&(Node->InterfaceList)));
// CnAssert(Node->CurrentInterface != NULL);
for (entry = Node->InterfaceList.Flink;
entry != &(Node->InterfaceList);
entry = entry->Flink
)
{
interface = CONTAINING_RECORD(entry, CNP_INTERFACE, NodeLinkage);
if ( !CnpIsNetworkRestricted(interface->Network) &&
!CnpIsNetworkLocalDisconn(interface->Network) &&
CnpIsBetterInterface(interface, bestInterface)
)
{
bestInterface = interface;
}
}
Node->CurrentInterface = bestInterface;
IF_CNDBG(( CN_DEBUG_NODEOBJ | CN_DEBUG_NETOBJ )) {
if (bestInterface == NULL) {
CNPRINT((
"[CNP] No route for node %u!!!!\n",
Node->Id
));
}
else {
CNPRINT((
"[CNP] Best route for node %u is now network %u.\n",
Node->Id,
bestInterface->Network->Id
));
}
}
CnVerifyCpuLockMask(
CNP_NODE_OBJECT_LOCK, // Required
0, // Forbidden
CNP_NODE_OBJECT_LOCK_MAX // Maximum
);
return;
} // CnpUpdateNodeCurrentInterface
VOID
CnpResetAndOnlinePendingInterface(
IN PCNP_INTERFACE Interface
)
/*++
Routine Description:
Resets the sequence numbers used to authenticate packets
sent by a node over a particular network. Also takes the
node's interface online.
This operation is performed when a node is joining a cluster.
Arguments:
Interface - A pointer to the interface on which to operate.
Return Value:
None.
Notes:
Called with associated node and network locks held.
Returns with network lock released.
Conforms to calling convention for PCNP_INTERFACE_UPDATE_ROUTINE.
--*/
{
PCNP_NODE node = Interface->Node;
PCNP_NETWORK network = Interface->Network;
CnVerifyCpuLockMask(
(CNP_NODE_OBJECT_LOCK | CNP_NETWORK_OBJECT_LOCK), // Required
0, // Forbidden
CNP_NETWORK_OBJECT_LOCK_MAX // Maximum
);
IF_CNDBG(( CN_DEBUG_NODEOBJ | CN_DEBUG_NETOBJ ))
CNPRINT((
"[CNP] Reseting sequence numbers for node %u on network %u\n",
node->Id,
network->Id
));
Interface->SequenceToSend = 0;
Interface->LastSequenceReceived = 0;
//
// Take the interface online.
//
(VOID) CnpOnlinePendingInterface(Interface);
CnVerifyCpuLockMask(
(CNP_NODE_OBJECT_LOCK), // Required
0, // Forbidden
CNP_NODE_OBJECT_LOCK_MAX // Maximum
);
return;
} // CnpRecalculateInterfacePriority
NTSTATUS
CnpFindInterface(
IN CL_NODE_ID NodeId,
IN CL_NETWORK_ID NetworkId,
OUT PCNP_INTERFACE * Interface
)
{
NTSTATUS status;
PCNP_INTERFACE interface;
PCNP_NODE node;
PCNP_NETWORK network;
PLIST_ENTRY entry;
CnVerifyCpuLockMask(
0, // Required
0, // Forbidden
CNP_PRECEEDING_LOCK_RANGE // Maximum
);
status = CnpValidateAndFindNode(NodeId, &node);
if (status == STATUS_SUCCESS) {
network = CnpFindNetwork(NetworkId);
if (network != NULL) {
for (entry = node->InterfaceList.Flink;
entry != &(node->InterfaceList);
entry = entry->Flink
)
{
interface = CONTAINING_RECORD(
entry,
CNP_INTERFACE,
NodeLinkage
);
if (interface->Network == network) {
*Interface = interface;
CnVerifyCpuLockMask(
(CNP_NODE_OBJECT_LOCK | CNP_NETWORK_OBJECT_LOCK),
0, // Forbidden
CNP_NETWORK_OBJECT_LOCK_MAX // Maximum
);
return(STATUS_SUCCESS);
}
}
CnReleaseLock(&(network->Lock), network->Irql);
}
CnReleaseLock(&(node->Lock), node->Irql);
}
CnVerifyCpuLockMask(
0, // Required
0, // Forbidden
CNP_PRECEEDING_LOCK_RANGE // Maximum
);
return(STATUS_CLUSTER_NETINTERFACE_NOT_FOUND);
} // CnpFindInterface
//
// Cluster Transport Public Routines
//
NTSTATUS
CxRegisterInterface(
CL_NODE_ID NodeId,
CL_NETWORK_ID NetworkId,
ULONG Priority,
PUWSTR AdapterId,
ULONG AdapterIdLength,
ULONG TdiAddressLength,
PTRANSPORT_ADDRESS TdiAddress,
PULONG MediaStatus
)
{
NTSTATUS status;
PLIST_ENTRY entry;
PCNP_INTERFACE interface;
PCNP_NODE node;
PCNP_NETWORK network;
ULONG allocSize;
PWCHAR adapterDevNameBuffer = NULL;
HANDLE adapterDevHandle = NULL;
BOOLEAN localAdapter = FALSE;
CnVerifyCpuLockMask(
0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
//
// Allocate and initialize an interface object.
//
allocSize = FIELD_OFFSET(CNP_INTERFACE, TdiAddress) + TdiAddressLength;
interface = CnAllocatePool(allocSize);
if (interface == NULL) {
return(STATUS_INSUFFICIENT_RESOURCES);
}
RtlZeroMemory(interface, allocSize);
CN_INIT_SIGNATURE(interface, CNP_INTERFACE_SIG);
interface->State = ClusnetInterfaceStateOffline;
RtlMoveMemory(&(interface->TdiAddress), TdiAddress, TdiAddressLength);
interface->TdiAddressLength = TdiAddressLength;
//
// Register the new interface object
//
status = CnpValidateAndFindNode(NodeId, &node);
if (NT_SUCCESS(status)) {
//
// If this adapter is on the local node, use the adapter ID
// to find the corresponding WMI Provider ID.
//
localAdapter = (BOOLEAN)(node == CnpLocalNode);
if (localAdapter) {
PWCHAR adapterDevNamep, brace;
PFILE_OBJECT adapterFileObject;
PDEVICE_OBJECT adapterDeviceObject;
// first drop the node lock
CnReleaseLock(&(node->Lock), node->Irql);
// allocate a buffer for the adapter device name
allocSize = wcslen(L"\\Device\\") * sizeof(WCHAR)
+ AdapterIdLength
+ sizeof(UNICODE_NULL);
brace = L"{";
if (*((PWCHAR)AdapterId) != *brace) {
allocSize += 2 * sizeof(WCHAR);
}
adapterDevNameBuffer = CnAllocatePool(allocSize);
if (adapterDevNameBuffer == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto error_exit;
}
// build the adapter device name from the adapter ID
RtlZeroMemory(adapterDevNameBuffer, allocSize);
adapterDevNamep = adapterDevNameBuffer;
RtlCopyMemory(
adapterDevNamep,
L"\\Device\\",
wcslen(L"\\Device\\") * sizeof(WCHAR)
);
adapterDevNamep += wcslen(L"\\Device\\");
if (*((PWCHAR)AdapterId) != *brace) {
*adapterDevNamep = *brace;
adapterDevNamep++;
}
RtlCopyMemory(adapterDevNamep, AdapterId, AdapterIdLength);
if (*((PWCHAR)AdapterId) != *brace) {
brace = L"}";
adapterDevNamep =
(PWCHAR)((PUCHAR)adapterDevNamep + AdapterIdLength);
*adapterDevNamep = *brace;
}
// open the adapter device
status = CnpOpenDevice(
adapterDevNameBuffer,
&adapterDevHandle
);
if (!NT_SUCCESS(status)) {
IF_CNDBG( CN_DEBUG_IFOBJ )
CNPRINT((
"[CNP] Failed to open adapter "
"device %S while registering "
"interface (%u, %u), status %lx.\n",
adapterDevNameBuffer,
NodeId,
NetworkId,
status
));
goto error_exit;
}
status = ObReferenceObjectByHandle(
adapterDevHandle,
0L, // DesiredAccess
NULL,
KernelMode,
&adapterFileObject,
NULL
);
if (!NT_SUCCESS(status)) {
IF_CNDBG( CN_DEBUG_IFOBJ )
CNPRINT((
"[CNP] Failed to reference handle "
"for adapter device %S while "
"registering interface (%u, %u), "
"status %lx.\n",
adapterDevNameBuffer,
NodeId,
NetworkId,
status
));
ZwClose(adapterDevHandle);
adapterDevHandle = NULL;
goto error_exit;
}
adapterDeviceObject = IoGetRelatedDeviceObject(
adapterFileObject
);
// convert the adapter device object into the
// WMI provider ID
interface->AdapterWMIProviderId =
IoWMIDeviceObjectToProviderId(adapterDeviceObject);
IF_CNDBG( CN_DEBUG_IFOBJ )
CNPRINT((
"[CNP] Found WMI Provider ID %lx for adapter "
"device %S while "
"registering interface (%u, %u).\n",
interface->AdapterWMIProviderId,
adapterDevNameBuffer,
NodeId,
NetworkId
));
// we no longer need the file object or device name
// buffer, but we hold onto the adapter device handle
// in order to query the current media status.
ObDereferenceObject(adapterFileObject);
CnFreePool(adapterDevNameBuffer);
adapterDevNameBuffer = NULL;
// reacquire the local node lock
status = CnpValidateAndFindNode(NodeId, &node);
if (!NT_SUCCESS(status)) {
status = STATUS_CLUSTER_NODE_NOT_FOUND;
goto error_exit;
}
}
network = CnpFindNetwork(NetworkId);
if (network != NULL) {
//
// Check if the specified interface already exists.
//
status = STATUS_SUCCESS;
for (entry = node->InterfaceList.Flink;
entry != &(node->InterfaceList);
entry = entry->Flink
)
{
PCNP_INTERFACE oldInterface = CONTAINING_RECORD(
entry,
CNP_INTERFACE,
NodeLinkage
);
if (oldInterface->Network == network) {
status = STATUS_CLUSTER_NETINTERFACE_EXISTS;
break;
}
}
if (NT_SUCCESS(status)) {
interface->Node = node;
interface->Network = network;
if (Priority != 0) {
interface->Priority = Priority;
}
else {
interface->Priority = network->Priority;
interface->Flags |= CNP_IF_FLAG_USE_NETWORK_PRIORITY;
}
IF_CNDBG( CN_DEBUG_IFOBJ )
CNPRINT((
"[CNP] Registering interface (%u, %u) pri %u...\n",
NodeId,
NetworkId,
interface->Priority
));
//
// Place a reference on the network for this interface.
//
CnpReferenceNetwork(network);
//
// Insert the interface into the node's interface list.
//
InsertTailList(
&(node->InterfaceList),
&(interface->NodeLinkage)
);
//
// Update the node's CurrentInterface if appropriate.
//
if ( !CnpIsNetworkRestricted(network)
&&
CnpIsBetterInterface(interface, node->CurrentInterface)
)
{
IF_CNDBG( CN_DEBUG_IFOBJ )
CNPRINT((
"[CNP] Network %u is now the best route to node %u.\n",
network->Id,
node->Id
));
node->CurrentInterface = interface;
}
IF_CNDBG( CN_DEBUG_IFOBJ )
CNPRINT((
"[CNP] Registered interface (%u, %u).\n",
NodeId,
NetworkId
));
if (network->State == ClusnetNetworkStateOnline) {
(VOID) CnpOnlinePendingInterface(interface);
//
// The network lock was released.
//
}
else {
CnReleaseLockFromDpc(&(network->Lock));
}
CnReleaseLock(&(node->Lock), node->Irql);
//
// Determine the initial media status state of this
// interface if it is local.
//
if (localAdapter) {
CxQueryMediaStatus(
adapterDevHandle,
NetworkId,
MediaStatus
);
} else {
//
// Assume remote interfaces are connected
//
*MediaStatus = NdisMediaStateConnected;
}
if (adapterDevHandle != NULL) {
ZwClose(adapterDevHandle);
adapterDevHandle = NULL;
}
return(STATUS_SUCCESS);
}
CnReleaseLockFromDpc(&(network->Lock));
}
else {
status = STATUS_CLUSTER_NETWORK_NOT_FOUND;
}
CnReleaseLock(&(node->Lock), node->Irql);
}
else {
status = STATUS_CLUSTER_NODE_NOT_FOUND;
}
error_exit:
if (!NT_SUCCESS(status)) {
CnFreePool(interface);
}
if (adapterDevHandle != NULL) {
ZwClose(adapterDevHandle);
adapterDevHandle = NULL;
}
if (adapterDevNameBuffer != NULL) {
CnFreePool(adapterDevNameBuffer);
adapterDevNameBuffer = NULL;
}
CnVerifyCpuLockMask(
0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
return(status);
} // CxRegisterInterface
NTSTATUS
CxDeregisterInterface(
CL_NODE_ID NodeId,
CL_NETWORK_ID NetworkId
)
{
NTSTATUS status;
PCNP_INTERFACE interface;
ULONG i;
PCNP_NODE node;
PCNP_NETWORK network;
CN_IRQL tableIrql;
if ((NodeId == ClusterAnyNodeId) && (NetworkId == ClusterAnyNetworkId)) {
//
// Destroy all interfaces on all networks.
//
IF_CNDBG(( CN_DEBUG_IFOBJ | CN_DEBUG_CLEANUP ))
CNPRINT(("[CNP] Destroying all interfaces on all networks\n"));
CnAcquireLock(&CnpNodeTableLock, &tableIrql);
CnAssert(CnMinValidNodeId != ClusterInvalidNodeId);
CnAssert(CnMaxValidNodeId != ClusterInvalidNodeId);
for (i=CnMinValidNodeId; i <= CnMaxValidNodeId; i++) {
node = CnpNodeTable[i];
if (node != NULL) {
CnAcquireLockAtDpc(&(node->Lock));
CnReleaseLockFromDpc(&CnpNodeTableLock);
node->Irql = tableIrql;
CnpWalkInterfacesOnNode(node, CnpDeleteInterface);
CnReleaseLock(&(node->Lock), node->Irql);
CnAcquireLock(&CnpNodeTableLock, &tableIrql);
}
}
CnReleaseLock(&CnpNodeTableLock, tableIrql);
status = STATUS_SUCCESS;
}
else if (NodeId == ClusterAnyNodeId) {
//
// Destroy all interfaces on a specific network.
//
IF_CNDBG(( CN_DEBUG_IFOBJ | CN_DEBUG_NETOBJ | CN_DEBUG_CLEANUP ))
CNPRINT((
"[CNP] Destroying all interfaces on network %u\n",
NetworkId
));
network = CnpFindNetwork(NetworkId);
if (network != NULL) {
CnpReferenceNetwork(network);
CnReleaseLock(&(network->Lock), network->Irql);
CnpWalkInterfacesOnNetwork(network, CnpDeleteInterface);
CnAcquireLock(&(network->Lock), &(network->Irql));
CnpDereferenceNetwork(network);
status = STATUS_SUCCESS;
}
else {
status = STATUS_CLUSTER_NETWORK_NOT_FOUND;
}
}
else if (NetworkId == ClusterAnyNetworkId) {
//
// Destroy all interfaces on a specified node.
//
IF_CNDBG(( CN_DEBUG_IFOBJ | CN_DEBUG_NODEOBJ | CN_DEBUG_CLEANUP ))
CNPRINT((
"[CNP] Destroying all interfaces on node %u\n",
NodeId
));
status = CnpValidateAndFindNode(NodeId, &node);
if (status == STATUS_SUCCESS) {
CnpWalkInterfacesOnNode(node, CnpDeleteInterface);
CnReleaseLock(&(node->Lock), node->Irql);
}
}
else {
//
// Delete a specific interface
//
status = CnpFindInterface(NodeId, NetworkId, &interface);
if (NT_SUCCESS(status)) {
node = interface->Node;
CnpDeleteInterface(interface);
//
// The network lock was released.
//
CnReleaseLock(&(node->Lock), node->Irql);
}
}
return(status);
} // CxDeregisterNetwork
NTSTATUS
CxSetInterfacePriority(
IN CL_NODE_ID NodeId,
IN CL_NETWORK_ID NetworkId,
IN ULONG Priority
)
{
NTSTATUS status;
PCNP_INTERFACE interface;
PCNP_NODE node;
PCNP_NETWORK network;
ULONG oldPriority;
BOOLEAN restricted;
CnVerifyCpuLockMask(
0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
status = CnpFindInterface(NodeId, NetworkId, &interface);
if (status == STATUS_SUCCESS) {
node = interface->Node;
network = interface->Network;
oldPriority = interface->Priority;
restricted = CnpIsNetworkRestricted(network);
if (Priority != 0) {
interface->Priority = Priority;
interface->Flags &= ~(CNP_IF_FLAG_USE_NETWORK_PRIORITY);
}
else {
interface->Priority = network->Priority;
interface->Flags |= CNP_IF_FLAG_USE_NETWORK_PRIORITY;
}
IF_CNDBG( CN_DEBUG_IFOBJ )
CNPRINT((
"[CNP] Set interface (%u, %u) to priority %u\n",
NodeId,
NetworkId,
interface->Priority
));
CnReleaseLockFromDpc(&(network->Lock));
if (!restricted) {
if (interface == node->CurrentInterface) {
if (interface->Priority > oldPriority) {
//
// Our priority got worse. Recalculate the best route.
//
CnpUpdateNodeCurrentInterface(node);
}
//
// Else interface priority is same or better. Nothing to do.
//
}
else if ( CnpIsBetterInterface(
interface,
node->CurrentInterface
)
)
{
//
// Our priority got better.
//
IF_CNDBG(( CN_DEBUG_NODEOBJ | CN_DEBUG_NETOBJ ))
CNPRINT((
"[CNP] Network %u is now the best route to node %u\n",
network->Id,
node->Id
));
node->CurrentInterface = interface;
}
}
CnReleaseLock(&(node->Lock), node->Irql);
}
CnVerifyCpuLockMask(
0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
return(status);
} // CxSetInterfacePriority
NTSTATUS
CxGetInterfacePriority(
IN CL_NODE_ID NodeId,
IN CL_NETWORK_ID NetworkId,
OUT PULONG InterfacePriority,
OUT PULONG NetworkPriority
)
{
NTSTATUS status;
PCNP_INTERFACE interface;
PCNP_NODE node;
PCNP_NETWORK network;
CnVerifyCpuLockMask(
0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
status = CnpFindInterface(NodeId, NetworkId, &interface);
if (status == STATUS_SUCCESS) {
node = interface->Node;
network = interface->Network;
*NetworkPriority = network->Priority;
if (CnpIsInterfaceUsingNetworkPriority(interface)) {
*InterfacePriority = 0;
}
else {
*InterfacePriority = interface->Priority;
}
CnReleaseLockFromDpc(&(network->Lock));
CnReleaseLock(&(node->Lock), node->Irql);
}
CnVerifyCpuLockMask(
0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
return(status);
} // CxGetInterfacePriority
NTSTATUS
CxGetInterfaceState(
IN CL_NODE_ID NodeId,
IN CL_NETWORK_ID NetworkId,
OUT PCLUSNET_INTERFACE_STATE State
)
{
NTSTATUS status;
PCNP_INTERFACE interface;
PCNP_NODE node;
PCNP_NETWORK network;
CnVerifyCpuLockMask(
0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
status = CnpFindInterface(NodeId, NetworkId, &interface);
if (status == STATUS_SUCCESS) {
node = interface->Node;
network = interface->Network;
*State = interface->State;
CnAssert(network->Irql == DISPATCH_LEVEL);
CnReleaseLockFromDpc(&(network->Lock));
CnReleaseLock(&(node->Lock), node->Irql);
}
CnVerifyCpuLockMask(
0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
return(status);
} // CxGetInterfaceState
//
// Test APIs
//
#if DBG
NTSTATUS
CxOnlinePendingInterface(
IN CL_NODE_ID NodeId,
IN CL_NETWORK_ID NetworkId
)
{
NTSTATUS status;
PCNP_INTERFACE interface;
PCNP_NODE node;
PCNP_NETWORK network;
CnVerifyCpuLockMask(
0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
status = CnpFindInterface(NodeId, NetworkId, &interface);
if (status == STATUS_SUCCESS) {
node = interface->Node;
network = interface->Network;
status = CnpOnlinePendingInterface(interface);
//
// The network lock was released
//
CnReleaseLock(&(node->Lock), node->Irql);
}
CnVerifyCpuLockMask(
0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
return(status);
} // CxOnlinePendingInterface
NTSTATUS
CxOnlineInterface(
IN CL_NODE_ID NodeId,
IN CL_NETWORK_ID NetworkId
)
{
NTSTATUS status;
PCNP_INTERFACE interface;
PCNP_NODE node;
CnVerifyCpuLockMask(
0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
status = CnpFindInterface(NodeId, NetworkId, &interface);
if (status == STATUS_SUCCESS) {
node = interface->Node;
status = CnpOnlineInterface(interface);
//
// The network lock was released
//
CnReleaseLock(&(node->Lock), node->Irql);
}
CnVerifyCpuLockMask(
0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
return(status);
} // CxOnlineInterface
NTSTATUS
CxOfflineInterface(
IN CL_NODE_ID NodeId,
IN CL_NETWORK_ID NetworkId
)
{
NTSTATUS status;
PCNP_INTERFACE interface;
PCNP_NODE node;
CnVerifyCpuLockMask(
0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
status = CnpFindInterface(NodeId, NetworkId, &interface);
if (status == STATUS_SUCCESS) {
node = interface->Node;
status = CnpOfflineInterface(interface);
//
// The network lock was released
//
CnReleaseLock(&(node->Lock), node->Irql);
}
CnVerifyCpuLockMask(
0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
return(status);
} // CxOfflineInterface
NTSTATUS
CxFailInterface(
IN CL_NODE_ID NodeId,
IN CL_NETWORK_ID NetworkId
)
{
NTSTATUS status;
PCNP_INTERFACE interface;
PCNP_NODE node;
CnVerifyCpuLockMask(
0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
status = CnpFindInterface(NodeId, NetworkId, &interface);
if (status == STATUS_SUCCESS) {
node = interface->Node;
status = CnpFailInterface(interface);
//
// The network lock was released
//
CnReleaseLock(&(node->Lock), node->Irql);
}
CnVerifyCpuLockMask(
0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
return(status);
} // CxOfflineInterface
#endif // DBG