|
|
/*++
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; BOOLEAN networkLocked = TRUE;
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) &&
// Place an active reference on the associated network,
// and verify that it is not going away.
(CnpActiveReferenceNetwork(network)) ) { 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;
//
// Update the node's CurrentInterface if appropriate.
//
if ( !CnpIsNetworkRestricted(network) && !CnpIsNetworkLocalDisconn(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 ));
if (CnpIsNodeUnreachable(node)) { CnTrace( CNP_IF_DETAIL, CnpTraceOnlinePendingIfReach, "[CNP] Declaring node %u reachable after " "setting interface on network %u to online pending.\n", node->Id, network->Id ); CnpDeclareNodeReachable(node); } }
//
// Clear multicast-received flag and start multicast discovery
// packets.
//
// This call releases the network lock.
//
if (CnpIsNetworkMulticastCapable(network)) { CnpStartInterfaceMcastTransition(Interface); networkLocked = FALSE; } } else { status = STATUS_CLUSTER_INVALID_REQUEST; }
if (networkLocked) { CnReleaseLockFromDpc(&(network->Lock)); networkLocked = FALSE; }
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.
//
CnTrace( CNP_IF_DETAIL, CnpTraceOfflineIfUnreach, "[CNP] Declaring node %u unreachable after " "taking interface on network %u offline.\n", node->Id, network->Id );
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) && !CnpIsNetworkLocalDisconn(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)) { CnTrace( CNP_IF_DETAIL, CnpTraceOnlineIfReach, "[CNP] Declaring node %u reachable after " "bring interface on network %u online.\n", node->Id, network->Id ); 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;
//
// Clear the multicast received flag so that we "rediscover"
// multicast if ever this interface comes back up.
//
if (Interface->Node != CnpLocalNode) { CnpInterfaceClearReceivedMulticast(Interface); }
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.
//
CnTrace( CNP_IF_DETAIL, CnpTraceFailIfUnreach, "[CNP] Declaring node %u unreachable after " "marking interface on network %u failed.\n", node->Id, network->Id );
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); }
if (node->CurrentInterface == NULL && !CnpIsNodeUnreachable(node)) {
//
// This node is now unreachable.
//
CnTrace( CNP_IF_DETAIL, CnpTraceEvalRoleUnreach, "[CNP] Declaring node %u unreachable after " "evaluating role of interface on network %u.\n", node->Id, network->Id );
CnpDeclareNodeUnreachable(node);
} else if (node->CurrentInterface != NULL && CnpIsNodeUnreachable(node)) {
//
// This node may now be reachable.
//
CnTrace( CNP_IF_DETAIL, CnpTraceEvalRoleReach, "[CNP] Declaring node %u reachable after " "evaluating role of interface on network %u.\n", node->Id, network->Id ); CnpDeclareNodeReachable(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); BOOLEAN localDisconn = CnpIsNetworkLocalDisconn(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 ( !localDisconn && 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) && !CnpIsNetworkLocalDisconn(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 (CnpIsNodeUnreachable(node)) { CnTrace( CNP_IF_DETAIL, CnpTraceOnlinePendingIfReach, "[CNP] Declaring node %u reachable after " "registering interface on network %u.\n", node->Id, network->Id ); CnpDeclareNodeReachable(node); } }
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; BOOLEAN localDisconn;
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); localDisconn = CnpIsNetworkLocalDisconn(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 ( !localDisconn && 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
|