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.
2196 lines
58 KiB
2196 lines
58 KiB
/*++
|
|
|
|
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
|