mirror of https://github.com/tongzx/nt5src
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.
8441 lines
233 KiB
8441 lines
233 KiB
/*++
|
|
|
|
Copyright (c) 1996-1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
network.c
|
|
|
|
Abstract:
|
|
|
|
Implements the Node Manager's network management routines.
|
|
|
|
Author:
|
|
|
|
Mike Massa (mikemas) 7-Nov-1996
|
|
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
|
|
#include "nmp.h"
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Data
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
ULONG NmpNextNetworkShortId = 0;
|
|
LIST_ENTRY NmpNetworkList = {NULL, NULL};
|
|
LIST_ENTRY NmpInternalNetworkList = {NULL, NULL};
|
|
LIST_ENTRY NmpDeletedNetworkList = {NULL, NULL};
|
|
DWORD NmpNetworkCount = 0;
|
|
DWORD NmpInternalNetworkCount = 0;
|
|
DWORD NmpClientNetworkCount = 0;
|
|
BOOLEAN NmpIsConnectivityReportWorkerRunning = FALSE;
|
|
BOOLEAN NmpNeedConnectivityReport = FALSE;
|
|
CLRTL_WORK_ITEM NmpConnectivityReportWorkItem;
|
|
|
|
|
|
RESUTIL_PROPERTY_ITEM
|
|
NmpNetworkProperties[] =
|
|
{
|
|
{
|
|
L"Id", NULL, CLUSPROP_FORMAT_SZ,
|
|
0, 0, 0,
|
|
0,
|
|
FIELD_OFFSET(NM_NETWORK_INFO, Id)
|
|
},
|
|
{
|
|
CLUSREG_NAME_NET_NAME, NULL, CLUSPROP_FORMAT_SZ,
|
|
0, 0, 0,
|
|
0,
|
|
FIELD_OFFSET(NM_NETWORK_INFO, Name)
|
|
},
|
|
{
|
|
CLUSREG_NAME_NET_DESC, NULL, CLUSPROP_FORMAT_SZ,
|
|
(DWORD_PTR) NmpNullString, 0, 0,
|
|
0,
|
|
FIELD_OFFSET(NM_NETWORK_INFO, Description)
|
|
},
|
|
{
|
|
CLUSREG_NAME_NET_ROLE, NULL, CLUSPROP_FORMAT_DWORD,
|
|
ClusterNetworkRoleClientAccess,
|
|
ClusterNetworkRoleNone,
|
|
ClusterNetworkRoleInternalAndClient,
|
|
0,
|
|
FIELD_OFFSET(NM_NETWORK_INFO, Role)
|
|
},
|
|
{
|
|
CLUSREG_NAME_NET_PRIORITY, NULL, CLUSPROP_FORMAT_DWORD,
|
|
0, 0, 0xFFFFFFFF,
|
|
0,
|
|
FIELD_OFFSET(NM_NETWORK_INFO, Priority)
|
|
},
|
|
{
|
|
CLUSREG_NAME_NET_TRANSPORT, NULL, CLUSPROP_FORMAT_SZ,
|
|
0, 0, 0,
|
|
0,
|
|
FIELD_OFFSET(NM_NETWORK_INFO, Transport)
|
|
},
|
|
{
|
|
CLUSREG_NAME_NET_ADDRESS, NULL, CLUSPROP_FORMAT_SZ,
|
|
0, 0, 0,
|
|
0,
|
|
FIELD_OFFSET(NM_NETWORK_INFO, Address)
|
|
},
|
|
{
|
|
CLUSREG_NAME_NET_ADDRESS_MASK, NULL, CLUSPROP_FORMAT_SZ,
|
|
0, 0, 0,
|
|
0,
|
|
FIELD_OFFSET(NM_NETWORK_INFO, AddressMask)
|
|
},
|
|
{
|
|
0
|
|
}
|
|
};
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Initialization & cleanup routines
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
DWORD
|
|
NmpInitializeNetworks(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initializes network resources.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
A Win32 status value.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD status;
|
|
OM_OBJECT_TYPE_INITIALIZE networkTypeInitializer;
|
|
|
|
|
|
ClRtlLogPrint(LOG_NOISE,"[NM] Initializing networks.\n");
|
|
|
|
//
|
|
// Create the network object type
|
|
//
|
|
ZeroMemory(&networkTypeInitializer, sizeof(OM_OBJECT_TYPE_INITIALIZE));
|
|
networkTypeInitializer.ObjectSize = sizeof(NM_NETWORK);
|
|
networkTypeInitializer.Signature = NM_NETWORK_SIG;
|
|
networkTypeInitializer.Name = L"Network";
|
|
networkTypeInitializer.DeleteObjectMethod = &NmpDestroyNetworkObject;
|
|
|
|
status = OmCreateType(ObjectTypeNetwork, &networkTypeInitializer);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
WCHAR errorString[12];
|
|
wsprintfW(&(errorString[0]), L"%u", status);
|
|
CsLogEvent1(LOG_CRITICAL, CS_EVENT_ALLOCATION_FAILURE, errorString);
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Unable to create network object type, status %1!u!\n",
|
|
status
|
|
);
|
|
return(status);
|
|
}
|
|
|
|
return(status);
|
|
|
|
} // NmpInitializeNetworks
|
|
|
|
|
|
VOID
|
|
NmpCleanupNetworks(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Destroys all existing network resources.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PNM_NETWORK network;
|
|
PLIST_ENTRY entry;
|
|
DWORD status;
|
|
|
|
|
|
ClRtlLogPrint(LOG_NOISE,"[NM] Network cleanup starting...\n");
|
|
|
|
//
|
|
// Now clean up all the network objects.
|
|
//
|
|
NmpAcquireLock();
|
|
|
|
while (!IsListEmpty(&NmpNetworkList)) {
|
|
|
|
entry = NmpNetworkList.Flink;
|
|
|
|
network = CONTAINING_RECORD(entry, NM_NETWORK, Linkage);
|
|
|
|
CL_ASSERT(NM_OM_INSERTED(network));
|
|
|
|
NmpDeleteNetworkObject(network, FALSE);
|
|
}
|
|
|
|
NmpCleanupMulticast();
|
|
|
|
NmpReleaseLock();
|
|
|
|
ClRtlLogPrint(LOG_NOISE,"[NM] Network cleanup complete\n");
|
|
|
|
return;
|
|
|
|
} // NmpCleanupNetworks
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Top-level routines called during network configuration
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
DWORD
|
|
NmpCreateNetwork(
|
|
IN RPC_BINDING_HANDLE JoinSponsorBinding,
|
|
IN PNM_NETWORK_INFO NetworkInfo,
|
|
IN PNM_INTERFACE_INFO2 InterfaceInfo
|
|
)
|
|
/*++
|
|
|
|
Notes:
|
|
|
|
Must not be called with NM lock held.
|
|
|
|
--*/
|
|
{
|
|
DWORD status;
|
|
|
|
|
|
if (JoinSponsorBinding != NULL) {
|
|
//
|
|
// We are joining a cluster. Ask the sponsor to add the definition
|
|
// to the cluster database. The sponsor will also prompt all active
|
|
// nodes to instantiate a corresponding object. The object will be
|
|
// instantiated locally later in the join process.
|
|
//
|
|
status = NmRpcCreateNetwork2(
|
|
JoinSponsorBinding,
|
|
NmpJoinSequence,
|
|
NmLocalNodeIdString,
|
|
NetworkInfo,
|
|
InterfaceInfo
|
|
);
|
|
}
|
|
else if (NmpState == NmStateOnlinePending) {
|
|
HLOCALXSACTION xaction;
|
|
|
|
//
|
|
// We are forming a cluster. Add the definitions to the database.
|
|
// The corresponding object will be created later in
|
|
// the form process.
|
|
//
|
|
|
|
//
|
|
// Start a transaction - this must be done before acquiring the
|
|
// NM lock.
|
|
//
|
|
xaction = DmBeginLocalUpdate();
|
|
|
|
if (xaction == NULL) {
|
|
status = GetLastError();
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Failed to start a transaction, status %1!u!\n",
|
|
status
|
|
);
|
|
return(status);
|
|
}
|
|
|
|
status = NmpCreateNetworkDefinition(NetworkInfo, xaction);
|
|
|
|
if (status == ERROR_SUCCESS) {
|
|
status = NmpCreateInterfaceDefinition(InterfaceInfo, xaction);
|
|
}
|
|
|
|
//
|
|
// Complete the transaction - this must be done after releasing
|
|
// the NM lock.
|
|
//
|
|
if (status == ERROR_SUCCESS) {
|
|
DmCommitLocalUpdate(xaction);
|
|
}
|
|
else {
|
|
DmAbortLocalUpdate(xaction);
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// We are online. This is a PnP update.
|
|
//
|
|
NmpAcquireLock();
|
|
|
|
status = NmpGlobalCreateNetwork(NetworkInfo, InterfaceInfo);
|
|
|
|
NmpReleaseLock();
|
|
}
|
|
|
|
return(status);
|
|
|
|
} // NmpCreateNetwork
|
|
|
|
DWORD
|
|
NmpSetNetworkName(
|
|
IN PNM_NETWORK_INFO NetworkInfo
|
|
)
|
|
/*++
|
|
|
|
Notes:
|
|
|
|
Must not be called with NM lock held.
|
|
|
|
--*/
|
|
{
|
|
DWORD status;
|
|
|
|
|
|
if (NmpState == NmStateOnlinePending) {
|
|
HLOCALXSACTION xaction;
|
|
|
|
//
|
|
// We are forming a cluster. The local connectoid name has
|
|
// precedence. Fix the cluster network name stored in the
|
|
// cluster database.
|
|
//
|
|
|
|
//
|
|
// Start a transaction - this must be done before acquiring the
|
|
// NM lock.
|
|
//
|
|
xaction = DmBeginLocalUpdate();
|
|
|
|
if (xaction == NULL) {
|
|
status = GetLastError();
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Failed to start a transaction, status %1!u!\n",
|
|
status
|
|
);
|
|
return(status);
|
|
}
|
|
|
|
status = NmpSetNetworkNameDefinition(NetworkInfo, xaction);
|
|
|
|
//
|
|
// Complete the transaction - this must be done after releasing
|
|
// the NM lock.
|
|
//
|
|
if (status == ERROR_SUCCESS) {
|
|
DmCommitLocalUpdate(xaction);
|
|
}
|
|
else {
|
|
DmAbortLocalUpdate(xaction);
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// We are online. This is either a PnP update or we were called
|
|
// back to indicate that a local connectoid name changed.
|
|
// Issue a global update to set the cluster network name accordingly.
|
|
//
|
|
status = NmpGlobalSetNetworkName( NetworkInfo );
|
|
}
|
|
|
|
return(status);
|
|
|
|
} // NmpSetNetworkName
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Remote procedures called by joining nodes.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
error_status_t
|
|
s_NmRpcCreateNetwork(
|
|
IN handle_t IDL_handle,
|
|
IN DWORD JoinSequence, OPTIONAL
|
|
IN LPWSTR JoinerNodeId, OPTIONAL
|
|
IN PNM_NETWORK_INFO NetworkInfo,
|
|
IN PNM_INTERFACE_INFO InterfaceInfo1
|
|
)
|
|
{
|
|
DWORD status;
|
|
NM_INTERFACE_INFO2 interfaceInfo2;
|
|
|
|
|
|
//
|
|
// Translate and call the V2.0 procedure. The NetIndex isn't used in this call.
|
|
//
|
|
CopyMemory(&interfaceInfo2, InterfaceInfo1, sizeof(NM_INTERFACE_INFO));
|
|
interfaceInfo2.AdapterId = NmpUnknownString;
|
|
interfaceInfo2.NetIndex = NmInvalidInterfaceNetIndex;
|
|
|
|
status = s_NmRpcCreateNetwork2(
|
|
IDL_handle,
|
|
JoinSequence,
|
|
JoinerNodeId,
|
|
NetworkInfo,
|
|
&interfaceInfo2
|
|
);
|
|
|
|
return(status);
|
|
|
|
} // s_NmRpcCreateNetwork
|
|
|
|
|
|
error_status_t
|
|
s_NmRpcCreateNetwork2(
|
|
IN handle_t IDL_handle,
|
|
IN DWORD JoinSequence, OPTIONAL
|
|
IN LPWSTR JoinerNodeId, OPTIONAL
|
|
IN PNM_NETWORK_INFO NetworkInfo,
|
|
IN PNM_INTERFACE_INFO2 InterfaceInfo
|
|
)
|
|
{
|
|
DWORD status = ERROR_SUCCESS;
|
|
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NMJOIN] Received request to create new network %1!ws! for "
|
|
"joining node.\n",
|
|
NetworkInfo->Id
|
|
);
|
|
|
|
NmpAcquireLock();
|
|
|
|
if (NmpLockedEnterApi(NmStateOnline)) {
|
|
PNM_NODE joinerNode = NULL;
|
|
|
|
if (lstrcmpW(JoinerNodeId, NmpInvalidJoinerIdString) != 0) {
|
|
joinerNode = OmReferenceObjectById(
|
|
ObjectTypeNode,
|
|
JoinerNodeId
|
|
);
|
|
|
|
if (joinerNode != NULL) {
|
|
if ( (JoinSequence == NmpJoinSequence) &&
|
|
(NmpJoinerNodeId == joinerNode->NodeId) &&
|
|
(NmpSponsorNodeId == NmLocalNodeId) &&
|
|
!NmpJoinAbortPending
|
|
)
|
|
{
|
|
CL_ASSERT(joinerNode->State == ClusterNodeJoining);
|
|
CL_ASSERT(NmpJoinerUp == FALSE);
|
|
CL_ASSERT(NmpJoinTimer != 0);
|
|
|
|
//
|
|
// Suspend the join timer while we are working on
|
|
// behalf of the joiner. This precludes an abort
|
|
// from occuring as well.
|
|
//
|
|
NmpJoinTimer = 0;
|
|
}
|
|
else {
|
|
status = ERROR_CLUSTER_JOIN_ABORTED;
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NMJOIN] CreateNetwork call for joining node %1!ws! "
|
|
"failed because the join was aborted.\n",
|
|
JoinerNodeId
|
|
);
|
|
}
|
|
}
|
|
else {
|
|
status = ERROR_CLUSTER_NODE_NOT_MEMBER;
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NMJOIN] CreateNetwork call for joining node %1!ws! "
|
|
"failed because the node is not a member of the "
|
|
"cluster.\n",
|
|
JoinerNodeId
|
|
);
|
|
}
|
|
}
|
|
|
|
if (status == ERROR_SUCCESS) {
|
|
|
|
status = NmpGlobalCreateNetwork(NetworkInfo, InterfaceInfo);
|
|
|
|
if (joinerNode != NULL) {
|
|
//
|
|
// Verify that the join is still in progress
|
|
//
|
|
if ( (JoinSequence == NmpJoinSequence) &&
|
|
(NmpJoinerNodeId == joinerNode->NodeId)
|
|
)
|
|
{
|
|
CL_ASSERT(joinerNode->State == ClusterNodeJoining);
|
|
CL_ASSERT(NmpJoinerUp == FALSE);
|
|
CL_ASSERT(NmpSponsorNodeId == NmLocalNodeId);
|
|
CL_ASSERT(NmpJoinTimer == 0);
|
|
CL_ASSERT(NmpJoinAbortPending == FALSE);
|
|
|
|
if (status == ERROR_SUCCESS) {
|
|
//
|
|
// Restart the join timer.
|
|
//
|
|
NmpJoinTimer = NM_JOIN_TIMEOUT;
|
|
}
|
|
else {
|
|
//
|
|
// Abort the join
|
|
//
|
|
NmpJoinAbort(status, joinerNode);
|
|
}
|
|
}
|
|
else {
|
|
status = ERROR_CLUSTER_JOIN_ABORTED;
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NMJOIN] CreateNetwork call for joining node %1!ws! "
|
|
"failed because the join was aborted.\n",
|
|
JoinerNodeId
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (joinerNode != NULL) {
|
|
OmDereferenceObject(joinerNode);
|
|
}
|
|
|
|
NmpLockedLeaveApi();
|
|
}
|
|
else {
|
|
status = ERROR_NODE_NOT_AVAILABLE;
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NMJOIN] Not in valid state to process CreateNetwork request.\n"
|
|
);
|
|
}
|
|
|
|
NmpReleaseLock();
|
|
|
|
return(status);
|
|
|
|
} // s_NmRpcCreateNetwork2
|
|
|
|
|
|
error_status_t
|
|
s_NmRpcSetNetworkName(
|
|
IN handle_t IDL_handle,
|
|
IN DWORD JoinSequence, OPTIONAL
|
|
IN LPWSTR JoinerNodeId, OPTIONAL
|
|
IN PNM_NETWORK_INFO NetworkInfo
|
|
)
|
|
{
|
|
DWORD status = ERROR_SUCCESS;
|
|
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NMJOIN] Received request to set name of network %1!ws! from "
|
|
"joining node %2!ws!.\n",
|
|
NetworkInfo->Id,
|
|
JoinerNodeId
|
|
);
|
|
|
|
NmpAcquireLock();
|
|
|
|
if (NmpLockedEnterApi(NmStateOnline)) {
|
|
PNM_NODE joinerNode = NULL;
|
|
|
|
if (lstrcmpW(JoinerNodeId, NmpInvalidJoinerIdString) != 0) {
|
|
joinerNode = OmReferenceObjectById(
|
|
ObjectTypeNode,
|
|
JoinerNodeId
|
|
);
|
|
|
|
if (joinerNode != NULL) {
|
|
if ( (JoinSequence == NmpJoinSequence) &&
|
|
(NmpJoinerNodeId == joinerNode->NodeId) &&
|
|
(NmpSponsorNodeId == NmLocalNodeId) &&
|
|
!NmpJoinAbortPending
|
|
)
|
|
{
|
|
CL_ASSERT(joinerNode->State == ClusterNodeJoining);
|
|
CL_ASSERT(NmpJoinerUp == FALSE);
|
|
CL_ASSERT(NmpJoinTimer != 0);
|
|
|
|
//
|
|
// Suspend the join timer while we are working on
|
|
// behalf of the joiner. This precludes an abort
|
|
// from occuring as well.
|
|
//
|
|
NmpJoinTimer = 0;
|
|
}
|
|
else {
|
|
status = ERROR_CLUSTER_JOIN_ABORTED;
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NMJOIN] SetNetworkName call for joining node "
|
|
"%1!ws! failed because the join was aborted.\n",
|
|
JoinerNodeId
|
|
);
|
|
}
|
|
}
|
|
else {
|
|
status = ERROR_CLUSTER_NODE_NOT_MEMBER;
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NMJOIN] SetNetworkName call for joining node %1!ws! "
|
|
"failed because the node is not a member of the cluster.\n",
|
|
JoinerNodeId
|
|
);
|
|
}
|
|
}
|
|
|
|
if (status == ERROR_SUCCESS) {
|
|
|
|
status = NmpGlobalSetNetworkName( NetworkInfo );
|
|
|
|
if (joinerNode != NULL) {
|
|
//
|
|
// Verify that the join is still in progress
|
|
//
|
|
if ( (JoinSequence == NmpJoinSequence) &&
|
|
(NmpJoinerNodeId == joinerNode->NodeId)
|
|
)
|
|
{
|
|
CL_ASSERT(joinerNode->State == ClusterNodeJoining);
|
|
CL_ASSERT(NmpJoinerUp == FALSE);
|
|
CL_ASSERT(NmpSponsorNodeId == NmLocalNodeId);
|
|
CL_ASSERT(NmpJoinTimer == 0);
|
|
CL_ASSERT(NmpJoinAbortPending == FALSE);
|
|
|
|
if (status == ERROR_SUCCESS) {
|
|
//
|
|
// Restart the join timer.
|
|
//
|
|
NmpJoinTimer = NM_JOIN_TIMEOUT;
|
|
}
|
|
else {
|
|
//
|
|
// Abort the join
|
|
//
|
|
NmpJoinAbort(status, joinerNode);
|
|
}
|
|
}
|
|
else {
|
|
status = ERROR_CLUSTER_JOIN_ABORTED;
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NMJOIN] SetNetworkName call for joining node "
|
|
"%1!ws! failed because the join was aborted.\n",
|
|
JoinerNodeId
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (joinerNode != NULL) {
|
|
OmDereferenceObject(joinerNode);
|
|
}
|
|
|
|
NmpLockedLeaveApi();
|
|
}
|
|
else {
|
|
status = ERROR_NODE_NOT_AVAILABLE;
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NMJOIN] Not in valid state to process SetNetworkName request.\n"
|
|
);
|
|
}
|
|
|
|
NmpReleaseLock();
|
|
|
|
return(status);
|
|
|
|
} // s_NmRpcSetNetworkName
|
|
|
|
error_status_t
|
|
s_NmRpcEnumNetworkDefinitions(
|
|
IN handle_t IDL_handle,
|
|
IN DWORD JoinSequence, OPTIONAL
|
|
IN LPWSTR JoinerNodeId, OPTIONAL
|
|
OUT PNM_NETWORK_ENUM * NetworkEnum
|
|
)
|
|
{
|
|
DWORD status = ERROR_SUCCESS;
|
|
PNM_NODE joinerNode = NULL;
|
|
|
|
|
|
NmpAcquireLock();
|
|
|
|
if (NmpLockedEnterApi(NmStateOnline)) {
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NMJOIN] Supplying network information to joining node.\n"
|
|
);
|
|
|
|
if (lstrcmpW(JoinerNodeId, NmpInvalidJoinerIdString) != 0) {
|
|
joinerNode = OmReferenceObjectById(
|
|
ObjectTypeNode,
|
|
JoinerNodeId
|
|
);
|
|
|
|
if (joinerNode != NULL) {
|
|
if ( (JoinSequence == NmpJoinSequence) &&
|
|
(NmpJoinerNodeId == joinerNode->NodeId) &&
|
|
(NmpSponsorNodeId == NmLocalNodeId) &&
|
|
!NmpJoinAbortPending
|
|
)
|
|
{
|
|
CL_ASSERT(joinerNode->State == ClusterNodeJoining);
|
|
CL_ASSERT(NmpJoinerUp == FALSE);
|
|
CL_ASSERT(NmpJoinTimer != 0);
|
|
|
|
//
|
|
// Suspend the join timer while we are working on
|
|
// behalf of the joiner. This precludes an abort
|
|
// from occuring as well.
|
|
//
|
|
NmpJoinTimer = 0;
|
|
}
|
|
else {
|
|
status = ERROR_CLUSTER_JOIN_ABORTED;
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NMJOIN] EnumNetworkDefinitions call for joining "
|
|
"node %1!ws! failed because the join was aborted.\n",
|
|
JoinerNodeId
|
|
);
|
|
}
|
|
}
|
|
else {
|
|
status = ERROR_CLUSTER_NODE_NOT_MEMBER;
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NMJOIN] EnumNetworkDefinitions call for joining "
|
|
"node %1!ws! failed because the node is not a member "
|
|
"of the cluster.\n",
|
|
JoinerNodeId
|
|
);
|
|
}
|
|
}
|
|
|
|
if (status == ERROR_SUCCESS) {
|
|
status = NmpEnumNetworkObjects(NetworkEnum);
|
|
|
|
if (joinerNode != NULL) {
|
|
if (status == ERROR_SUCCESS) {
|
|
//
|
|
// Restart the join timer.
|
|
//
|
|
NmpJoinTimer = NM_JOIN_TIMEOUT;
|
|
|
|
}
|
|
else {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NMJOIN] Failed to enumerate network definitions, "
|
|
"status %1!u!.\n",
|
|
status
|
|
);
|
|
|
|
//
|
|
// Abort the join
|
|
//
|
|
NmpJoinAbort(status, joinerNode);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (joinerNode != NULL) {
|
|
OmDereferenceObject(joinerNode);
|
|
}
|
|
|
|
NmpLockedLeaveApi();
|
|
}
|
|
else {
|
|
status = ERROR_NODE_NOT_AVAILABLE;
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NMJOIN] Not in valid state to process EnumNetworkDefinitions "
|
|
"request.\n"
|
|
);
|
|
}
|
|
|
|
NmpReleaseLock();
|
|
|
|
return(status);
|
|
|
|
} // s_NmRpcEnumNetworkDefinitions
|
|
|
|
|
|
error_status_t
|
|
s_NmRpcEnumNetworkAndInterfaceStates(
|
|
IN handle_t IDL_handle,
|
|
IN DWORD JoinSequence,
|
|
IN LPWSTR JoinerNodeId,
|
|
OUT PNM_NETWORK_STATE_ENUM * NetworkStateEnum,
|
|
OUT PNM_INTERFACE_STATE_ENUM * InterfaceStateEnum
|
|
)
|
|
{
|
|
DWORD status = ERROR_SUCCESS;
|
|
|
|
|
|
NmpAcquireLock();
|
|
|
|
if (NmpLockedEnterApi(NmStateOnline)) {
|
|
PNM_NODE joinerNode = OmReferenceObjectById(
|
|
ObjectTypeNode,
|
|
JoinerNodeId
|
|
);
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NMJOIN] Supplying network and interface state information "
|
|
"to joining node.\n"
|
|
);
|
|
|
|
if (joinerNode != NULL) {
|
|
if ( (JoinSequence != NmpJoinSequence) ||
|
|
(NmpJoinerNodeId != joinerNode->NodeId) ||
|
|
(NmpSponsorNodeId != NmLocalNodeId) ||
|
|
NmpJoinAbortPending
|
|
)
|
|
{
|
|
status = ERROR_CLUSTER_JOIN_ABORTED;
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NMJOIN] EnumNetworkAndInterfaceStates call for "
|
|
"joining node %1!ws! failed because the join was "
|
|
"aborted.\n",
|
|
JoinerNodeId
|
|
);
|
|
}
|
|
else {
|
|
CL_ASSERT(joinerNode->State == ClusterNodeJoining);
|
|
CL_ASSERT(NmpJoinerUp);
|
|
CL_ASSERT(NmpJoinTimer == 0);
|
|
}
|
|
}
|
|
else {
|
|
status = ERROR_CLUSTER_NODE_NOT_MEMBER;
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NMJOIN] EnumNetworkAndInterfaceStates call for joining "
|
|
"node %1!ws! failed because the node is not a member of "
|
|
"the cluster.\n",
|
|
JoinerNodeId
|
|
);
|
|
}
|
|
|
|
if (status == ERROR_SUCCESS) {
|
|
|
|
status = NmpEnumNetworkObjectStates(NetworkStateEnum);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NMJOIN] EnumNetworkAndInterfaceStates failed, "
|
|
"status %1!u!.\n",
|
|
status
|
|
);
|
|
|
|
//
|
|
// Abort the join
|
|
//
|
|
NmpJoinAbort(status, joinerNode);
|
|
}
|
|
|
|
status = NmpEnumInterfaceObjectStates(InterfaceStateEnum);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NMJOIN] EnumNetworkAndInterfaceStates failed, "
|
|
"status %1!u!.\n",
|
|
status
|
|
);
|
|
|
|
//
|
|
// Abort the join
|
|
//
|
|
NmpJoinAbort(status, joinerNode);
|
|
|
|
NmpFreeNetworkStateEnum(*NetworkStateEnum);
|
|
*NetworkStateEnum = NULL;
|
|
}
|
|
}
|
|
|
|
OmDereferenceObject(joinerNode);
|
|
|
|
NmpLockedLeaveApi();
|
|
}
|
|
else {
|
|
status = ERROR_NODE_NOT_AVAILABLE;
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NMJOIN] Not in valid state to process "
|
|
"EnumNetworkAndInterfaceStates request.\n"
|
|
);
|
|
}
|
|
|
|
NmpReleaseLock();
|
|
|
|
return(status);
|
|
|
|
} // s_NmRpcEnumNetworkAndInterfaceStates
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Routines used to make global configuration changes when the node
|
|
// is online.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
DWORD
|
|
NmpGlobalCreateNetwork(
|
|
IN PNM_NETWORK_INFO NetworkInfo,
|
|
IN PNM_INTERFACE_INFO2 InterfaceInfo
|
|
)
|
|
/*++
|
|
|
|
Notes:
|
|
|
|
Called with the NmpLock held.
|
|
|
|
--*/
|
|
{
|
|
DWORD status = ERROR_SUCCESS;
|
|
DWORD networkPropertiesSize;
|
|
PVOID networkProperties;
|
|
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Issuing global update to create network %1!ws! and "
|
|
"interface %2!ws!.\n",
|
|
NetworkInfo->Id,
|
|
InterfaceInfo->Id
|
|
);
|
|
|
|
//
|
|
// Marshall the info structures into property lists.
|
|
//
|
|
status = NmpMarshallObjectInfo(
|
|
NmpNetworkProperties,
|
|
NetworkInfo,
|
|
&networkProperties,
|
|
&networkPropertiesSize
|
|
);
|
|
|
|
if (status == ERROR_SUCCESS) {
|
|
DWORD interfacePropertiesSize;
|
|
PVOID interfaceProperties;
|
|
|
|
status = NmpMarshallObjectInfo(
|
|
NmpInterfaceProperties,
|
|
InterfaceInfo,
|
|
&interfaceProperties,
|
|
&interfacePropertiesSize
|
|
);
|
|
|
|
if (status == ERROR_SUCCESS) {
|
|
NmpReleaseLock();
|
|
|
|
//
|
|
// Issue a global update to create the network
|
|
//
|
|
status = GumSendUpdateEx(
|
|
GumUpdateMembership,
|
|
NmUpdateCreateNetwork,
|
|
4,
|
|
networkPropertiesSize,
|
|
networkProperties,
|
|
sizeof(networkPropertiesSize),
|
|
&networkPropertiesSize,
|
|
interfacePropertiesSize,
|
|
interfaceProperties,
|
|
sizeof(interfacePropertiesSize),
|
|
&interfacePropertiesSize
|
|
);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Global update to create network %1!ws! failed, "
|
|
"status %2!u!.\n",
|
|
NetworkInfo->Id,
|
|
status
|
|
);
|
|
}
|
|
|
|
NmpAcquireLock();
|
|
|
|
MIDL_user_free(interfaceProperties);
|
|
}
|
|
else {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Failed to marshall properties for new interface "
|
|
"%1!ws!, status %2!u!\n",
|
|
InterfaceInfo->Id,
|
|
status
|
|
);
|
|
}
|
|
|
|
MIDL_user_free(networkProperties);
|
|
}
|
|
else {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Failed to marshall properties for new network %1!ws!, "
|
|
"status %2!u!\n",
|
|
NetworkInfo->Id,
|
|
status
|
|
);
|
|
}
|
|
|
|
return(status);
|
|
|
|
} // NmpGlobalCreateNetwork
|
|
|
|
|
|
DWORD
|
|
NmpGlobalSetNetworkName(
|
|
IN PNM_NETWORK_INFO NetworkInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Changes the name of a network defined for the cluster.
|
|
|
|
Arguments:
|
|
|
|
NetworkInfo - A pointer to info about the network to be modified.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if the routine succeeds.
|
|
A Win32 error code otherwise.
|
|
|
|
Notes:
|
|
|
|
Must not be called with NM lock held.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD status = ERROR_SUCCESS;
|
|
|
|
if (status == ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Processing request to set name for network %1!ws! "
|
|
"to '%2!ws!'.\n",
|
|
NetworkInfo->Id,
|
|
NetworkInfo->Name
|
|
);
|
|
|
|
//
|
|
// Issue a global update
|
|
//
|
|
status = GumSendUpdateEx(
|
|
GumUpdateMembership,
|
|
NmUpdateSetNetworkName,
|
|
2,
|
|
NM_WCSLEN(NetworkInfo->Id),
|
|
NetworkInfo->Id,
|
|
NM_WCSLEN( NetworkInfo->Name ),
|
|
NetworkInfo->Name
|
|
);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Global update to set name of network %1!ws! "
|
|
"failed, status %2!u!.\n",
|
|
NetworkInfo->Id,
|
|
status
|
|
);
|
|
}
|
|
}
|
|
else {
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NM] New name parameter supplied for network %1!ws! is invalid\n",
|
|
NetworkInfo->Id
|
|
);
|
|
}
|
|
|
|
return(status);
|
|
|
|
} // NmpGlobalSetNetworkName
|
|
|
|
|
|
DWORD
|
|
NmpGlobalSetNetworkAndInterfaceStates(
|
|
PNM_NETWORK Network,
|
|
CLUSTER_NETWORK_STATE NewNetworkState
|
|
)
|
|
/*++
|
|
|
|
Notes:
|
|
|
|
Called with NmpLock held and the Network referenced.
|
|
|
|
--*/
|
|
{
|
|
DWORD status;
|
|
DWORD i;
|
|
LPCWSTR networkId = OmObjectId(Network);
|
|
DWORD entryCount = Network->ConnectivityVector->EntryCount;
|
|
DWORD vectorSize = sizeof(NM_STATE_ENTRY) * entryCount;
|
|
PNM_STATE_ENTRY ifStateVector;
|
|
|
|
|
|
ifStateVector = LocalAlloc(LMEM_FIXED, vectorSize);
|
|
|
|
if (ifStateVector != NULL ) {
|
|
|
|
for (i=0; i< entryCount; i++) {
|
|
ifStateVector[i] = Network->StateWorkVector[i].State;
|
|
}
|
|
|
|
if (NmpState == NmStateOnline) {
|
|
//
|
|
// Issue a global state update for this network.
|
|
//
|
|
NmpReleaseLock();
|
|
|
|
status = GumSendUpdateEx(
|
|
GumUpdateMembership,
|
|
NmUpdateSetNetworkAndInterfaceStates,
|
|
4,
|
|
NM_WCSLEN(networkId),
|
|
networkId,
|
|
sizeof(NewNetworkState),
|
|
&NewNetworkState,
|
|
vectorSize,
|
|
ifStateVector,
|
|
sizeof(entryCount),
|
|
&entryCount
|
|
);
|
|
|
|
NmpAcquireLock();
|
|
}
|
|
else {
|
|
CL_ASSERT(NmpState == NmStateOnlinePending);
|
|
//
|
|
// We're still in the form process. Bypass GUM.
|
|
//
|
|
NmpSetNetworkAndInterfaceStates(
|
|
Network,
|
|
NewNetworkState,
|
|
ifStateVector,
|
|
entryCount
|
|
);
|
|
|
|
status = ERROR_SUCCESS;
|
|
}
|
|
|
|
LocalFree(ifStateVector);
|
|
}
|
|
else {
|
|
status = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
return(status);
|
|
|
|
} // NmpGlobalSetNetworkAndInterfaceStates
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Routines called by other cluster service components
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
CLUSTER_NETWORK_STATE
|
|
NmGetNetworkState(
|
|
IN PNM_NETWORK Network
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
Notes:
|
|
|
|
Because the caller must have a reference on the object and the
|
|
call is so simple, there is no reason to put the call through the
|
|
EnterApi/LeaveApi dance.
|
|
|
|
--*/
|
|
{
|
|
CLUSTER_NETWORK_STATE state;
|
|
|
|
|
|
NmpAcquireLock();
|
|
|
|
state = Network->State;
|
|
|
|
NmpReleaseLock();
|
|
|
|
return(state);
|
|
|
|
} // NmGetNetworkState
|
|
|
|
|
|
DWORD
|
|
NmSetNetworkName(
|
|
IN PNM_NETWORK Network,
|
|
IN LPCWSTR Name
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Changes the name of a network defined for the cluster.
|
|
|
|
Arguments:
|
|
|
|
Network - A pointer to the object for the network to be modified.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if the routine succeeds.
|
|
A Win32 error code otherwise.
|
|
|
|
Notes:
|
|
|
|
The network object must be referenced by the caller.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD status = ERROR_SUCCESS;
|
|
|
|
|
|
if (NmpEnterApi(NmStateOnline)) {
|
|
LPCWSTR networkId = OmObjectId(Network);
|
|
DWORD nameLength;
|
|
|
|
|
|
//
|
|
// Validate the name
|
|
//
|
|
try {
|
|
nameLength = lstrlenW(Name);
|
|
|
|
if (nameLength == 0) {
|
|
status = ERROR_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER) {
|
|
status = ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (status == ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Processing request to set name for network %1!ws! "
|
|
"to %2!ws!.\n",
|
|
networkId,
|
|
Name
|
|
);
|
|
|
|
//
|
|
// Issue a global update
|
|
//
|
|
status = GumSendUpdateEx(
|
|
GumUpdateMembership,
|
|
NmUpdateSetNetworkName,
|
|
2,
|
|
NM_WCSLEN(networkId),
|
|
networkId,
|
|
(nameLength + 1) * sizeof(WCHAR),
|
|
Name
|
|
);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Global update to set name of network %1!ws! "
|
|
"failed, status %2!u!.\n",
|
|
networkId,
|
|
status
|
|
);
|
|
}
|
|
}
|
|
else {
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NM] New name parameter supplied for network %1!ws! "
|
|
"is invalid\n",
|
|
networkId
|
|
);
|
|
}
|
|
|
|
NmpLeaveApi();
|
|
}
|
|
else {
|
|
status = ERROR_NODE_NOT_AVAILABLE;
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Not in valid state to process SetNetworkName request.\n"
|
|
);
|
|
}
|
|
|
|
return(status);
|
|
|
|
} // NmSetNetworkName
|
|
|
|
|
|
DWORD
|
|
NmSetNetworkPriorityOrder(
|
|
IN DWORD NetworkCount,
|
|
IN LPWSTR * NetworkIdList
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sets the priority ordering of internal networks.
|
|
|
|
Arguments:
|
|
|
|
NetworkCount - Contains the count of items in NetworkIdList.
|
|
|
|
NetworkIdList - A pointer to an array of pointers to unicode strings.
|
|
Each string contains the ID of one internal network.
|
|
The array is sorted in priority order. The highest
|
|
priority network is listed first in the array.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if the routine is successful.
|
|
|
|
A Win32 error code othewise.
|
|
|
|
--*/
|
|
{
|
|
DWORD status = ERROR_SUCCESS;
|
|
|
|
|
|
if (NetworkCount == 0) {
|
|
return(ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Received request to set network priority order.\n"
|
|
);
|
|
|
|
if (NmpEnterApi(NmStateOnline)) {
|
|
DWORD i;
|
|
DWORD multiSzLength = 0;
|
|
PVOID multiSz = NULL;
|
|
|
|
//
|
|
// Marshall the network ID list into a MULTI_SZ.
|
|
//
|
|
for (i=0; i< NetworkCount; i++) {
|
|
multiSzLength += NM_WCSLEN(NetworkIdList[i]);
|
|
}
|
|
|
|
multiSzLength += sizeof(UNICODE_NULL);
|
|
|
|
multiSz = MIDL_user_allocate(multiSzLength);
|
|
|
|
if (multiSz != NULL) {
|
|
LPWSTR tmp = multiSz;
|
|
|
|
for (i=0; i< NetworkCount; i++) {
|
|
lstrcpyW(tmp, NetworkIdList[i]);
|
|
tmp += lstrlenW(NetworkIdList[i]) + 1;
|
|
}
|
|
|
|
*tmp = UNICODE_NULL;
|
|
|
|
//
|
|
// Issue a global update
|
|
//
|
|
status = GumSendUpdateEx(
|
|
GumUpdateMembership,
|
|
NmUpdateSetNetworkPriorityOrder,
|
|
1,
|
|
multiSzLength,
|
|
multiSz
|
|
);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Global update to reprioritize networks failed, "
|
|
"status %1!u!.\n",
|
|
status
|
|
);
|
|
}
|
|
|
|
MIDL_user_free(multiSz);
|
|
}
|
|
else {
|
|
status = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
NmpLeaveApi();
|
|
}
|
|
else {
|
|
status = ERROR_NODE_NOT_AVAILABLE;
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Not in valid state to process a request to set the "
|
|
"network priority order.\n"
|
|
);
|
|
}
|
|
|
|
return(status);
|
|
|
|
} // NmSetNetworkPriorityOrder
|
|
|
|
|
|
DWORD
|
|
NmEnumInternalNetworks(
|
|
OUT LPDWORD NetworkCount,
|
|
OUT PNM_NETWORK * NetworkList[]
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns a prioritized list of networks that are eligible to
|
|
carry internal communication.
|
|
|
|
Arguments:
|
|
|
|
NetworkCount - On output, contains the number of items in NetworkList.
|
|
|
|
NetworkList - On output, points to an array of pointers to network
|
|
objects. The highest priority network is first in the
|
|
array. Each pointer in the array must be dereferenced
|
|
by the caller. The storage for the array must be
|
|
deallocated by the caller.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if the routine is successful.
|
|
|
|
A Win32 error code othewise.
|
|
|
|
--*/
|
|
{
|
|
DWORD status;
|
|
|
|
|
|
NmpAcquireLock();
|
|
|
|
if (NmpLockedEnterApi(NmStateOnline)) {
|
|
|
|
status = NmpEnumInternalNetworks(NetworkCount, NetworkList);
|
|
|
|
NmpLockedLeaveApi();
|
|
}
|
|
else {
|
|
status = ERROR_NODE_NOT_AVAILABLE;
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Not in valid state to process EnumInternalNetworks "
|
|
"request.\n"
|
|
);
|
|
}
|
|
|
|
NmpReleaseLock();
|
|
|
|
return(status);
|
|
|
|
} // NmEnumInternalNetworks
|
|
|
|
|
|
DWORD
|
|
NmpEnumInternalNetworks(
|
|
OUT LPDWORD NetworkCount,
|
|
OUT PNM_NETWORK * NetworkList[]
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns a prioritized list of networks that are eligible to
|
|
carry internal communication.
|
|
|
|
Arguments:
|
|
|
|
NetworkCount - On output, contains the number of items in NetworkList.
|
|
|
|
NetworkList - On output, points to an array of pointers to network
|
|
objects. The highest priority network is first in the
|
|
array. Each pointer in the array must be dereferenced
|
|
by the caller. The storage for the array must be
|
|
deallocated by the caller.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if the routine is successful.
|
|
|
|
A Win32 error code othewise.
|
|
|
|
Notes:
|
|
|
|
Called with NM Lock held.
|
|
|
|
--*/
|
|
{
|
|
DWORD status = ERROR_SUCCESS;
|
|
|
|
|
|
if (NmpInternalNetworkCount > 0) {
|
|
PNM_NETWORK * networkList = LocalAlloc(
|
|
LMEM_FIXED,
|
|
( sizeof(PNM_NETWORK) *
|
|
NmpInternalNetworkCount)
|
|
);
|
|
|
|
if (networkList != NULL) {
|
|
PNM_NETWORK network;
|
|
PLIST_ENTRY entry;
|
|
DWORD networkCount = 0;
|
|
|
|
//
|
|
// The internal network list is sorted in priority order.
|
|
// The highest priority network is at the head of the list.
|
|
//
|
|
for (entry = NmpInternalNetworkList.Flink;
|
|
entry != &NmpInternalNetworkList;
|
|
entry = entry->Flink
|
|
)
|
|
{
|
|
network = CONTAINING_RECORD(
|
|
entry,
|
|
NM_NETWORK,
|
|
InternalLinkage
|
|
);
|
|
|
|
CL_ASSERT(NmpIsNetworkForInternalUse(network));
|
|
OmReferenceObject(network);
|
|
networkList[networkCount++] = network;
|
|
}
|
|
|
|
CL_ASSERT(networkCount == NmpInternalNetworkCount);
|
|
*NetworkCount = networkCount;
|
|
*NetworkList = networkList;
|
|
}
|
|
else {
|
|
status = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
}
|
|
else {
|
|
*NetworkCount = 0;
|
|
*NetworkList = NULL;
|
|
}
|
|
|
|
return(status);
|
|
|
|
} // NmpEnumInternalNetworks
|
|
|
|
|
|
DWORD
|
|
NmEnumNetworkInterfaces(
|
|
IN PNM_NETWORK Network,
|
|
OUT LPDWORD InterfaceCount,
|
|
OUT PNM_INTERFACE * InterfaceList[]
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns the list of interfaces associated with a specified network.
|
|
|
|
Arguments:
|
|
|
|
Network - A pointer to the network object for which to enumerate
|
|
interfaces.
|
|
|
|
InterfaceCount - On output, contains the number of items in InterfaceList.
|
|
|
|
InterfaceList - On output, points to an array of pointers to interface
|
|
objects. Each pointer in the array must be dereferenced
|
|
by the caller. The storage for the array must be
|
|
deallocated by the caller.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if the routine is successful.
|
|
|
|
A Win32 error code othewise.
|
|
|
|
--*/
|
|
{
|
|
DWORD status = ERROR_SUCCESS;
|
|
|
|
|
|
NmpAcquireLock();
|
|
|
|
if (NmpLockedEnterApi(NmStateOnline)) {
|
|
if (Network->InterfaceCount > 0) {
|
|
PNM_INTERFACE * interfaceList = LocalAlloc(
|
|
LMEM_FIXED,
|
|
( sizeof(PNM_INTERFACE) *
|
|
Network->InterfaceCount)
|
|
);
|
|
|
|
if (interfaceList != NULL) {
|
|
PNM_INTERFACE netInterface;
|
|
PLIST_ENTRY entry;
|
|
DWORD i;
|
|
|
|
for (entry = Network->InterfaceList.Flink, i=0;
|
|
entry != &(Network->InterfaceList);
|
|
entry = entry->Flink, i++
|
|
)
|
|
{
|
|
netInterface = CONTAINING_RECORD(
|
|
entry,
|
|
NM_INTERFACE,
|
|
NetworkLinkage
|
|
);
|
|
|
|
OmReferenceObject(netInterface);
|
|
interfaceList[i] = netInterface;
|
|
}
|
|
|
|
*InterfaceCount = Network->InterfaceCount;
|
|
*InterfaceList = interfaceList;
|
|
}
|
|
else {
|
|
status = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
}
|
|
else {
|
|
*InterfaceCount = 0;
|
|
*InterfaceList = NULL;
|
|
}
|
|
|
|
NmpLockedLeaveApi();
|
|
}
|
|
else {
|
|
status = ERROR_NODE_NOT_AVAILABLE;
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NM] Not in valid state to process EnumNetworkInterfaces "
|
|
"request.\n"
|
|
);
|
|
}
|
|
|
|
NmpReleaseLock();
|
|
|
|
return(status);
|
|
|
|
} // NmEnumNetworkInterfaces
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Handlers for global updates
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
DWORD
|
|
NmpUpdateCreateNetwork(
|
|
IN BOOL IsSourceNode,
|
|
IN PVOID NetworkPropertyList,
|
|
IN LPDWORD NetworkPropertyListSize,
|
|
IN PVOID InterfacePropertyList,
|
|
IN LPDWORD InterfacePropertyListSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Global update handler for creating a new network. The network
|
|
definition is read from the cluster database, and a corresponding
|
|
object is instantiated. The cluster transport is also updated if
|
|
necessary.
|
|
|
|
Arguments:
|
|
|
|
IsSourceNode - Set to TRUE if this node is the source of the update.
|
|
Set to FALSE otherwise.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if the routine succeeds.
|
|
A Win32 error code otherwise.
|
|
|
|
Notes:
|
|
|
|
This routine must not be called with NM lock held.
|
|
|
|
--*/
|
|
{
|
|
DWORD status;
|
|
NM_NETWORK_INFO networkInfo;
|
|
NM_INTERFACE_INFO2 interfaceInfo;
|
|
PNM_NETWORK network = NULL;
|
|
PNM_INTERFACE netInterface = NULL;
|
|
HLOCALXSACTION xaction = NULL;
|
|
BOOLEAN isInternalNetwork = FALSE;
|
|
BOOLEAN isLockAcquired = FALSE;
|
|
CL_NODE_ID joinerNodeId;
|
|
|
|
|
|
if (!NmpEnterApi(NmStateOnline)) {
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Not in valid state to process CreateNetwork update.\n"
|
|
);
|
|
return(ERROR_NODE_NOT_AVAILABLE);
|
|
}
|
|
|
|
//
|
|
// Unmarshall the property lists.
|
|
//
|
|
ZeroMemory(&networkInfo, sizeof(networkInfo));
|
|
ZeroMemory(&interfaceInfo, sizeof(interfaceInfo));
|
|
|
|
status = ClRtlVerifyPropertyTable(
|
|
NmpNetworkProperties,
|
|
NULL, // Reserved
|
|
FALSE, // Don't allow unknowns
|
|
NetworkPropertyList,
|
|
*NetworkPropertyListSize,
|
|
(LPBYTE) &networkInfo
|
|
);
|
|
|
|
if ( status != ERROR_SUCCESS ) {
|
|
ClRtlLogPrint( LOG_CRITICAL,
|
|
"[NM] Failed to unmarshall properties for new network, "
|
|
"status %1!u!.\n",
|
|
status
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
status = ClRtlVerifyPropertyTable(
|
|
NmpInterfaceProperties,
|
|
NULL, // Reserved
|
|
FALSE, // Don't allow unknowns
|
|
InterfacePropertyList,
|
|
*InterfacePropertyListSize,
|
|
(LPBYTE) &interfaceInfo
|
|
);
|
|
|
|
if ( status != ERROR_SUCCESS ) {
|
|
ClRtlLogPrint( LOG_CRITICAL,
|
|
"[NM] Failed to unmarshall properties for new interface, "
|
|
"status %1!u!.\n",
|
|
status
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Received update to create network %1!ws! & interface %2!ws!.\n",
|
|
networkInfo.Id,
|
|
interfaceInfo.Id
|
|
);
|
|
|
|
//
|
|
// Start a transaction - this must be done before acquiring the NM lock.
|
|
//
|
|
xaction = DmBeginLocalUpdate();
|
|
|
|
if (xaction == NULL) {
|
|
status = GetLastError();
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Failed to begin a transaction, status %1!u!\n",
|
|
status
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
NmpAcquireLock(); isLockAcquired = TRUE;
|
|
|
|
//
|
|
// Fix up the network's priority, if needed.
|
|
//
|
|
if (networkInfo.Role & ClusterNetworkRoleInternalUse) {
|
|
CL_ASSERT(networkInfo.Priority == 0xFFFFFFFF);
|
|
|
|
//
|
|
// The network's priority is one greater than that of the lowest
|
|
// priority network already in the internal network list.
|
|
//
|
|
if (IsListEmpty(&NmpInternalNetworkList)) {
|
|
networkInfo.Priority = 1;
|
|
}
|
|
else {
|
|
PNM_NETWORK network = CONTAINING_RECORD(
|
|
NmpInternalNetworkList.Blink,
|
|
NM_NETWORK,
|
|
InternalLinkage
|
|
);
|
|
|
|
CL_ASSERT(network->Priority != 0);
|
|
CL_ASSERT(network->Priority != 0xFFFFFFFF);
|
|
|
|
networkInfo.Priority = network->Priority + 1;
|
|
}
|
|
|
|
isInternalNetwork = TRUE;
|
|
}
|
|
|
|
//
|
|
// Update the database.
|
|
//
|
|
status = NmpCreateNetworkDefinition(&networkInfo, xaction);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
goto error_exit;
|
|
}
|
|
|
|
status = NmpCreateInterfaceDefinition(&interfaceInfo, xaction);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
goto error_exit;
|
|
}
|
|
|
|
joinerNodeId = NmpJoinerNodeId;
|
|
|
|
NmpReleaseLock(); isLockAcquired = FALSE;
|
|
|
|
network = NmpCreateNetworkObject(&networkInfo);
|
|
|
|
if (network == NULL) {
|
|
status = GetLastError();
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Failed to create object for network %1!ws!, "
|
|
"status %2!u!.\n",
|
|
networkInfo.Id,
|
|
status
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
netInterface = NmpCreateInterfaceObject(
|
|
&interfaceInfo,
|
|
TRUE // Do retry on failure
|
|
);
|
|
|
|
#ifdef CLUSTER_TESTPOINT
|
|
TESTPT(TpFailNmCreateNetwork) {
|
|
NmpAcquireLock();
|
|
NmpDeleteInterfaceObject(netInterface, FALSE); netInterface = NULL;
|
|
NmpReleaseLock();
|
|
SetLastError(999999);
|
|
}
|
|
#endif
|
|
|
|
NmpAcquireLock(); isLockAcquired = TRUE;
|
|
|
|
if (netInterface == NULL) {
|
|
status = GetLastError();
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Failed to create object for interface %1!ws!, "
|
|
"status %2!u!.\n",
|
|
interfaceInfo.Id,
|
|
status
|
|
);
|
|
|
|
NmpDeleteNetworkObject(network, FALSE);
|
|
OmDereferenceObject(network);
|
|
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// If a node happens to be joining right now, flag the fact that
|
|
// it is now out of synch with the cluster config.
|
|
//
|
|
if ( ( (joinerNodeId != ClusterInvalidNodeId) &&
|
|
(netInterface->Node->NodeId != joinerNodeId)
|
|
) ||
|
|
( (NmpJoinerNodeId != ClusterInvalidNodeId) &&
|
|
(netInterface->Node->NodeId != NmpJoinerNodeId)
|
|
)
|
|
)
|
|
{
|
|
NmpJoinerOutOfSynch = TRUE;
|
|
}
|
|
|
|
ClusterEvent(CLUSTER_EVENT_NETWORK_ADDED, network);
|
|
ClusterEvent(CLUSTER_EVENT_NETINTERFACE_ADDED, netInterface);
|
|
|
|
if (isInternalNetwork) {
|
|
NmpIssueClusterPropertyChangeEvent();
|
|
}
|
|
|
|
OmDereferenceObject(netInterface);
|
|
OmDereferenceObject(network);
|
|
|
|
CL_ASSERT(status == ERROR_SUCCESS);
|
|
|
|
error_exit:
|
|
|
|
if (isLockAcquired) {
|
|
NmpLockedLeaveApi();
|
|
NmpReleaseLock();
|
|
}
|
|
else {
|
|
NmpLeaveApi();
|
|
}
|
|
|
|
if (xaction != NULL) {
|
|
//
|
|
// Complete the transaction - this must be done after releasing
|
|
// the NM lock.
|
|
//
|
|
if (status == ERROR_SUCCESS) {
|
|
DmCommitLocalUpdate(xaction);
|
|
}
|
|
else {
|
|
DmAbortLocalUpdate(xaction);
|
|
}
|
|
}
|
|
|
|
ClNetFreeNetworkInfo(&networkInfo);
|
|
ClNetFreeInterfaceInfo(&interfaceInfo);
|
|
|
|
return(status);
|
|
|
|
} // NmpUpdateCreateNetwork
|
|
|
|
|
|
DWORD
|
|
NmpUpdateSetNetworkName(
|
|
IN BOOL IsSourceNode,
|
|
IN LPWSTR NetworkId,
|
|
IN LPWSTR NewNetworkName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Global update handler for setting the name of a network.
|
|
|
|
Arguments:
|
|
|
|
IsSourceNode - Set to TRUE if this node is the source of the update.
|
|
Set to FALSE otherwise.
|
|
|
|
NetworkId - A pointer to a unicode string containing the ID of the
|
|
network to update.
|
|
|
|
NewNetworkName - A pointer to a unicode string containing the new network name.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if the routine succeeds.
|
|
A Win32 error code otherwise.
|
|
|
|
Notes:
|
|
|
|
This routine must not be called with NM lock held.
|
|
|
|
--*/
|
|
{
|
|
DWORD status;
|
|
DWORD i;
|
|
PLIST_ENTRY entry;
|
|
HLOCALXSACTION xaction = NULL;
|
|
HDMKEY networkKey;
|
|
HDMKEY netInterfaceKey;
|
|
PNM_NETWORK network = NULL;
|
|
PNM_INTERFACE netInterface;
|
|
LPCWSTR netInterfaceId;
|
|
LPCWSTR netInterfaceName;
|
|
LPCWSTR networkName;
|
|
LPCWSTR nodeName;
|
|
LPWSTR oldNetworkName = NULL;
|
|
LPWSTR * oldNameVector = NULL;
|
|
LPWSTR * newNameVector = NULL;
|
|
BOOLEAN isLockAcquired = FALSE;
|
|
DWORD interfaceCount;
|
|
|
|
|
|
if (!NmpEnterApi(NmStateOnline)) {
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Not in valid state to process SetNetworkName update.\n"
|
|
);
|
|
return(ERROR_NODE_NOT_AVAILABLE);
|
|
}
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Received update to set the name for network %1!ws! "
|
|
"to '%2!ws!'.\n",
|
|
NetworkId,
|
|
NewNetworkName
|
|
);
|
|
|
|
//
|
|
// Find the network's object
|
|
//
|
|
network = OmReferenceObjectById(ObjectTypeNetwork, NetworkId);
|
|
|
|
if (network == NULL) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Unable to find network %1!ws!.\n",
|
|
NetworkId
|
|
);
|
|
status = ERROR_CLUSTER_NETWORK_NOT_FOUND;
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Start a transaction - this must be done before acquiring the NM lock.
|
|
//
|
|
xaction = DmBeginLocalUpdate();
|
|
|
|
if (xaction == NULL) {
|
|
status = GetLastError();
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Failed to begin a transaction, status %1!u!\n",
|
|
status
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
NmpAcquireLock(); isLockAcquired = TRUE;
|
|
|
|
//
|
|
// compare the names. If the same, return success
|
|
//
|
|
if ( _wcsicmp( OmObjectName( network ), NewNetworkName ) == 0 ) {
|
|
ClRtlLogPrint(LOG_NOISE, "[NM] Network name does not need changing.\n");
|
|
|
|
status = ERROR_SUCCESS;
|
|
goto error_exit;
|
|
}
|
|
|
|
networkName = OmObjectName(network);
|
|
|
|
oldNetworkName = LocalAlloc(LMEM_FIXED, NM_WCSLEN(networkName));
|
|
|
|
if (oldNetworkName == NULL) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Failed to allocate memory for network %1!ws! name change!\n",
|
|
NetworkId
|
|
);
|
|
status = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto error_exit;
|
|
}
|
|
|
|
wcscpy(oldNetworkName, networkName);
|
|
|
|
//
|
|
// Update the database.
|
|
//
|
|
// This processing can always be undone on error.
|
|
//
|
|
networkKey = DmOpenKey(DmNetworksKey, NetworkId, KEY_WRITE);
|
|
|
|
if (networkKey == NULL) {
|
|
status = GetLastError();
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Failed to open database key for network %1!ws!, "
|
|
"status %2!u!\n",
|
|
NetworkId,
|
|
status
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
status = DmLocalSetValue(
|
|
xaction,
|
|
networkKey,
|
|
CLUSREG_NAME_NET_NAME,
|
|
REG_SZ,
|
|
(CONST BYTE *) NewNetworkName,
|
|
NM_WCSLEN(NewNetworkName)
|
|
);
|
|
|
|
DmCloseKey(networkKey);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Set of name value failed for network %1!ws!, "
|
|
"status %2!u!.\n",
|
|
NetworkId,
|
|
status
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Update the names of all of the interfaces on this network
|
|
//
|
|
interfaceCount = network->InterfaceCount;
|
|
|
|
oldNameVector = LocalAlloc(
|
|
LMEM_FIXED | LMEM_ZEROINIT,
|
|
interfaceCount * sizeof(LPWSTR)
|
|
);
|
|
|
|
newNameVector = LocalAlloc(
|
|
LMEM_FIXED | LMEM_ZEROINIT,
|
|
interfaceCount * sizeof(LPWSTR)
|
|
);
|
|
|
|
if ((oldNameVector == NULL) || (newNameVector == NULL)) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Failed to allocate memory for net interface name change.\n"
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
for (entry = network->InterfaceList.Flink, i = 0;
|
|
entry != &(network->InterfaceList);
|
|
entry = entry->Flink, i++
|
|
)
|
|
{
|
|
netInterface = CONTAINING_RECORD(entry, NM_INTERFACE, NetworkLinkage);
|
|
netInterfaceId = OmObjectId(netInterface);
|
|
netInterfaceName = OmObjectName(netInterface);
|
|
nodeName = OmObjectName(netInterface->Node);
|
|
|
|
oldNameVector[i] = LocalAlloc(
|
|
LMEM_FIXED,
|
|
NM_WCSLEN(netInterfaceName)
|
|
);
|
|
|
|
newNameVector[i] = ClNetMakeInterfaceName(
|
|
NULL,
|
|
(LPWSTR) nodeName,
|
|
NewNetworkName
|
|
);
|
|
|
|
if ((oldNameVector[i] == NULL) || (newNameVector[i] == NULL)) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Failed to allocate memory for net interface name "
|
|
"change.\n"
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
wcscpy(oldNameVector[i], netInterfaceName);
|
|
|
|
netInterfaceKey = DmOpenKey(
|
|
DmNetInterfacesKey,
|
|
netInterfaceId,
|
|
KEY_WRITE
|
|
);
|
|
|
|
if (netInterfaceKey == NULL) {
|
|
status = GetLastError();
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Failed to open database key for net interface "
|
|
"%1!ws!, status %2!u!\n",
|
|
netInterfaceId,
|
|
status
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
status = DmLocalSetValue(
|
|
xaction,
|
|
netInterfaceKey,
|
|
CLUSREG_NAME_NETIFACE_NAME,
|
|
REG_SZ,
|
|
(CONST BYTE *) newNameVector[i],
|
|
NM_WCSLEN(newNameVector[i])
|
|
);
|
|
|
|
DmCloseKey(netInterfaceKey);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Set of name value failed for net interface %1!ws!, "
|
|
"status %2!u!.\n",
|
|
netInterfaceId,
|
|
status
|
|
);
|
|
goto error_exit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update the in-memory objects.
|
|
//
|
|
// This processing may not be undoable on error.
|
|
//
|
|
|
|
//
|
|
// Update name of the network
|
|
//
|
|
status = OmSetObjectName(network, NewNetworkName);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Failed to change name for network %1!ws!, status %2!u!\n",
|
|
NetworkId,
|
|
status
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Update the names of all of the interfaces on the network.
|
|
//
|
|
for (entry = network->InterfaceList.Flink, i = 0;
|
|
entry != &(network->InterfaceList);
|
|
entry = entry->Flink, i++
|
|
)
|
|
{
|
|
netInterface = CONTAINING_RECORD(entry, NM_INTERFACE, NetworkLinkage);
|
|
netInterfaceId = OmObjectId(netInterface);
|
|
|
|
status = OmSetObjectName(netInterface, newNameVector[i]);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
//
|
|
// Try to undo what has already been done. If we fail, we must
|
|
// commit suicide to preserve consistency.
|
|
//
|
|
DWORD j;
|
|
PLIST_ENTRY entry2;
|
|
DWORD undoStatus;
|
|
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Failed to change name for net interface %1!ws!, "
|
|
"status %2!u!\n",
|
|
netInterfaceId,
|
|
status
|
|
);
|
|
|
|
//
|
|
// Undo the update of the network name
|
|
//
|
|
undoStatus = OmSetObjectName(network, oldNetworkName);
|
|
|
|
if (undoStatus != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Failed to undo change of name for network %1!ws!, "
|
|
"status %2!u!\n",
|
|
NetworkId,
|
|
undoStatus
|
|
);
|
|
CsInconsistencyHalt(undoStatus);
|
|
}
|
|
|
|
//
|
|
// Undo update of network interface names
|
|
//
|
|
for (j = 0, entry2 = network->InterfaceList.Flink;
|
|
j < i;
|
|
j++, entry2 = entry2->Flink
|
|
)
|
|
{
|
|
netInterface = CONTAINING_RECORD(
|
|
entry2,
|
|
NM_INTERFACE,
|
|
NetworkLinkage
|
|
);
|
|
|
|
netInterfaceId = OmObjectId(netInterface);
|
|
|
|
undoStatus = OmSetObjectName(netInterface, oldNameVector[i]);
|
|
|
|
if (undoStatus != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Failed to undo change of name for net "
|
|
"interface %1!ws!, status %2!u!\n",
|
|
netInterfaceId,
|
|
undoStatus
|
|
);
|
|
CsInconsistencyHalt(undoStatus);
|
|
}
|
|
}
|
|
|
|
goto error_exit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set the corresponding connectoid object name if necessary.
|
|
//
|
|
if (network->LocalInterface != NULL) {
|
|
INetConnection * connectoid;
|
|
LPWSTR connectoidName;
|
|
DWORD tempStatus;
|
|
|
|
connectoid = ClRtlFindConnectoidByGuid(
|
|
network->LocalInterface->AdapterId
|
|
);
|
|
|
|
if (connectoid != NULL) {
|
|
connectoidName = ClRtlGetConnectoidName(connectoid);
|
|
|
|
if (connectoidName != NULL) {
|
|
if (lstrcmpW(connectoidName, NewNetworkName) != 0) {
|
|
tempStatus = ClRtlSetConnectoidName(
|
|
connectoid,
|
|
NewNetworkName
|
|
);
|
|
|
|
if (tempStatus != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NM] Failed to set name of Network Connection "
|
|
"Object for interface on cluster network %1!ws! "
|
|
"(%2!ws!), status %3!u!\n",
|
|
oldNetworkName,
|
|
NetworkId,
|
|
tempStatus
|
|
);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
tempStatus = GetLastError();
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NM] Failed to query name of Network Connection Object "
|
|
"for interface on cluster network %1!ws! (%2!ws!), "
|
|
"status %3!u!\n",
|
|
oldNetworkName,
|
|
NetworkId,
|
|
tempStatus
|
|
);
|
|
}
|
|
|
|
INetConnection_Release( connectoid );
|
|
}
|
|
else {
|
|
tempStatus = GetLastError();
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NM] Failed to find Network Connection Object for "
|
|
"interface on cluster network %1!ws! (%2!ws!), "
|
|
"status %3!u!\n",
|
|
oldNetworkName,
|
|
NetworkId,
|
|
tempStatus
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Issue property change events.
|
|
//
|
|
ClusterEvent(CLUSTER_EVENT_NETWORK_PROPERTY_CHANGE, network);
|
|
|
|
for (entry = network->InterfaceList.Flink;
|
|
entry != &(network->InterfaceList);
|
|
entry = entry->Flink
|
|
)
|
|
{
|
|
netInterface = CONTAINING_RECORD(entry, NM_INTERFACE, NetworkLinkage);
|
|
|
|
ClusterEvent(
|
|
CLUSTER_EVENT_NETINTERFACE_PROPERTY_CHANGE,
|
|
netInterface
|
|
);
|
|
}
|
|
|
|
CL_ASSERT(status == ERROR_SUCCESS);
|
|
|
|
error_exit:
|
|
|
|
if (isLockAcquired) {
|
|
NmpLockedLeaveApi();
|
|
NmpReleaseLock();
|
|
}
|
|
else {
|
|
NmpLeaveApi();
|
|
}
|
|
|
|
if (xaction != NULL) {
|
|
//
|
|
// Complete the transaction - this must be done after releasing
|
|
// the NM lock.
|
|
//
|
|
if (status == ERROR_SUCCESS) {
|
|
DmCommitLocalUpdate(xaction);
|
|
}
|
|
else {
|
|
DmAbortLocalUpdate(xaction);
|
|
}
|
|
}
|
|
|
|
if (network != NULL) {
|
|
OmDereferenceObject(network);
|
|
|
|
if (oldNetworkName != NULL) {
|
|
LocalFree(oldNetworkName);
|
|
}
|
|
|
|
if (oldNameVector != NULL) {
|
|
for (i=0; i < interfaceCount; i++) {
|
|
if (oldNameVector[i] == NULL) {
|
|
break;
|
|
}
|
|
|
|
LocalFree(oldNameVector[i]);
|
|
}
|
|
|
|
LocalFree(oldNameVector);
|
|
}
|
|
|
|
if (newNameVector != NULL) {
|
|
for (i=0; i < interfaceCount; i++) {
|
|
if (newNameVector[i] == NULL) {
|
|
break;
|
|
}
|
|
|
|
LocalFree(newNameVector[i]);
|
|
}
|
|
|
|
LocalFree(newNameVector);
|
|
}
|
|
}
|
|
|
|
return(status);
|
|
|
|
} // NmpUpdateSetNetworkName
|
|
|
|
|
|
DWORD
|
|
NmpUpdateSetNetworkPriorityOrder(
|
|
IN BOOL IsSourceNode,
|
|
IN LPCWSTR NetworkIdList
|
|
)
|
|
{
|
|
DWORD status = ERROR_SUCCESS;
|
|
PNM_NETWORK network;
|
|
PLIST_ENTRY entry;
|
|
DWORD matchCount=0;
|
|
DWORD networkCount=0;
|
|
PNM_NETWORK * networkList=NULL;
|
|
DWORD i;
|
|
DWORD multiSzLength;
|
|
LPCWSTR networkId;
|
|
HLOCALXSACTION xaction = NULL;
|
|
BOOLEAN isLockAcquired = FALSE;
|
|
|
|
|
|
if (!NmpEnterApi(NmStateOnline)) {
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Not in valid state to process SetNetworkPriorityOrder "
|
|
"update.\n"
|
|
);
|
|
return(ERROR_NODE_NOT_AVAILABLE);
|
|
}
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Received update to set network priority order.\n"
|
|
);
|
|
|
|
//
|
|
// Unmarshall the MULTI_SZ
|
|
//
|
|
try {
|
|
multiSzLength = ClRtlMultiSzLength(NetworkIdList);
|
|
|
|
for (i=0; ; i++) {
|
|
networkId = ClRtlMultiSzEnum(
|
|
NetworkIdList,
|
|
multiSzLength,
|
|
i
|
|
);
|
|
|
|
if (networkId == NULL) {
|
|
break;
|
|
}
|
|
|
|
networkCount++;
|
|
}
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER) {
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NM] Hit exception while parsing network ID list for "
|
|
"priority change\n"
|
|
);
|
|
status = ERROR_INVALID_PARAMETER;
|
|
goto error_exit;
|
|
}
|
|
|
|
if (networkCount == 0) {
|
|
status = ERROR_INVALID_PARAMETER;
|
|
goto error_exit;
|
|
}
|
|
|
|
networkList = LocalAlloc(
|
|
LMEM_ZEROINIT| LMEM_FIXED,
|
|
networkCount * sizeof(PNM_NETWORK)
|
|
);
|
|
|
|
if (networkList == NULL) {
|
|
status = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Start a transaction - this must be done before acquiring the NM lock.
|
|
//
|
|
xaction = DmBeginLocalUpdate();
|
|
|
|
if (xaction == NULL) {
|
|
status = GetLastError();
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Failed to start a transaction, status %1!u!\n",
|
|
status
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
NmpAcquireLock(); isLockAcquired = TRUE;
|
|
|
|
if (NmpJoinerNodeId != ClusterInvalidNodeId) {
|
|
status = ERROR_CLUSTER_JOIN_IN_PROGRESS;
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Cannot set network priority order because a node is "
|
|
"joining the cluster.\n"
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
for (i=0; i<networkCount; i++) {
|
|
networkId = ClRtlMultiSzEnum(
|
|
NetworkIdList,
|
|
multiSzLength,
|
|
i
|
|
);
|
|
|
|
CL_ASSERT(networkId != NULL);
|
|
|
|
networkList[i] = OmReferenceObjectById(
|
|
ObjectTypeNetwork,
|
|
networkId
|
|
);
|
|
|
|
if (networkList[i] == NULL) {
|
|
goto error_exit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Verify that all of the networks specified are internal, and
|
|
// that all of the internal networks are specified.
|
|
//
|
|
if (networkCount != NmpInternalNetworkCount) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Supplied network count %1!u! doesn't match internal "
|
|
"network count %2!u!\n",
|
|
networkCount,
|
|
NmpInternalNetworkCount
|
|
);
|
|
status = ERROR_INVALID_PARAMETER;
|
|
goto error_exit;
|
|
}
|
|
|
|
for (entry = NmpInternalNetworkList.Flink, matchCount = 0;
|
|
entry != &NmpInternalNetworkList;
|
|
entry = entry->Flink
|
|
)
|
|
{
|
|
network = CONTAINING_RECORD(entry, NM_NETWORK, InternalLinkage);
|
|
|
|
if (NmpIsNetworkForInternalUse(network)) {
|
|
for (i=0; i<networkCount; i++) {
|
|
if (network == networkList[i]) {
|
|
matchCount++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == networkCount) {
|
|
//
|
|
// This network is not in the list.
|
|
//
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Internal use network %1!ws! is not in priority "
|
|
"list\n",
|
|
(LPWSTR) OmObjectId(network)
|
|
);
|
|
status = ERROR_INVALID_PARAMETER;
|
|
goto error_exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (matchCount != networkCount) {
|
|
//
|
|
// Some of the specified networks are not internal use.
|
|
//
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Some non-internal use networks are in priority list\n"
|
|
);
|
|
status = ERROR_INVALID_PARAMETER;
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// The list is kosher. Set the priorities.
|
|
//
|
|
status = NmpSetNetworkPriorityOrder(networkCount, networkList, xaction);
|
|
|
|
error_exit:
|
|
|
|
if (isLockAcquired) {
|
|
NmpLockedLeaveApi();
|
|
NmpReleaseLock();
|
|
}
|
|
else {
|
|
NmpLeaveApi();
|
|
}
|
|
|
|
if (xaction != NULL) {
|
|
//
|
|
// Complete the transaction - this must be done after releasing
|
|
// the NM lock.
|
|
//
|
|
if (status == ERROR_SUCCESS) {
|
|
DmCommitLocalUpdate(xaction);
|
|
}
|
|
else {
|
|
DmAbortLocalUpdate(xaction);
|
|
}
|
|
}
|
|
|
|
if (networkList != NULL) {
|
|
for (i=0; i<networkCount; i++) {
|
|
if (networkList[i] != NULL) {
|
|
OmDereferenceObject(networkList[i]);
|
|
}
|
|
}
|
|
|
|
LocalFree(networkList);
|
|
}
|
|
|
|
return(status);
|
|
|
|
} // NmpUpdateSetNetworkPriorityOrder
|
|
|
|
|
|
DWORD
|
|
NmpSetNetworkPriorityOrder(
|
|
IN DWORD NetworkCount,
|
|
IN PNM_NETWORK * NetworkList,
|
|
IN HLOCALXSACTION Xaction
|
|
)
|
|
/*++
|
|
|
|
Notes:
|
|
|
|
Called with NM Lock held.
|
|
|
|
--*/
|
|
{
|
|
DWORD status = ERROR_SUCCESS;
|
|
PNM_NETWORK network;
|
|
DWORD i;
|
|
LPCWSTR networkId;
|
|
HDMKEY networkKey;
|
|
DWORD priority;
|
|
|
|
|
|
//
|
|
// Update the database first.
|
|
//
|
|
for (i=0, priority = 1; i<NetworkCount; i++, priority++) {
|
|
network = NetworkList[i];
|
|
networkId = OmObjectId(network);
|
|
|
|
CL_ASSERT(NmpIsNetworkForInternalUse(network));
|
|
|
|
if (network->Priority != priority) {
|
|
networkKey = DmOpenKey(DmNetworksKey, networkId, KEY_WRITE);
|
|
|
|
if (networkKey == NULL) {
|
|
status = GetLastError();
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Failed to open database key for network %1!ws!, "
|
|
"status %2!u!\n",
|
|
networkId,
|
|
status
|
|
);
|
|
return(status);
|
|
}
|
|
|
|
status = DmLocalSetValue(
|
|
Xaction,
|
|
networkKey,
|
|
CLUSREG_NAME_NET_PRIORITY,
|
|
REG_DWORD,
|
|
(CONST BYTE *) &priority,
|
|
sizeof(priority)
|
|
);
|
|
|
|
DmCloseKey(networkKey);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Set of priority value failed for network %1!ws!, "
|
|
"status %2!u!.\n",
|
|
networkId,
|
|
status
|
|
);
|
|
return(status);
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef CLUSTER_TESTPOINT
|
|
TESTPT(TpFailNmSetNetworkPriorityOrder) {
|
|
status = 999999;
|
|
return(status);
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Update the in-memory representation
|
|
//
|
|
InitializeListHead(&NmpInternalNetworkList);
|
|
|
|
for (i=0, priority = 1; i<NetworkCount; i++, priority++) {
|
|
network = NetworkList[i];
|
|
networkId = OmObjectId(network);
|
|
|
|
InsertTailList(
|
|
&NmpInternalNetworkList,
|
|
&(network->InternalLinkage)
|
|
);
|
|
|
|
if (network->Priority != priority) {
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Set priority for network %1!ws! to %2!u!.\n",
|
|
networkId,
|
|
priority
|
|
);
|
|
|
|
network->Priority = priority;
|
|
|
|
//
|
|
// If the local node is attached to this network, set its
|
|
// priority in the cluster transport
|
|
//
|
|
if (NmpIsNetworkRegistered(network)) {
|
|
status = ClusnetSetNetworkPriority(
|
|
NmClusnetHandle,
|
|
network->ShortId,
|
|
network->Priority
|
|
);
|
|
|
|
#ifdef CLUSTER_TESTPOINT
|
|
TESTPT(TpFailNmSetNetworkPriorityOrder2) {
|
|
status = 999999;
|
|
}
|
|
#endif
|
|
if (status != ERROR_SUCCESS) {
|
|
//
|
|
// We can't easily abort here. We must either continue
|
|
// or commit suicide. We choose to continue and log an
|
|
// event.
|
|
//
|
|
WCHAR string[16];
|
|
|
|
wsprintfW(&(string[0]), L"%u", status);
|
|
|
|
CsLogEvent2(
|
|
LOG_UNUSUAL,
|
|
NM_EVENT_SET_NETWORK_PRIORITY_FAILED,
|
|
OmObjectName(network),
|
|
string
|
|
);
|
|
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Cluster transport failed to set priority for "
|
|
"network %1!ws!, status %2!u!\n",
|
|
networkId,
|
|
status
|
|
);
|
|
|
|
status = ERROR_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
CL_ASSERT(status == ERROR_SUCCESS);
|
|
|
|
//
|
|
// Issue a cluster property change event.
|
|
//
|
|
NmpIssueClusterPropertyChangeEvent();
|
|
|
|
return(ERROR_SUCCESS);
|
|
|
|
} // NmpSetNetworkPriorityOrder
|
|
|
|
|
|
DWORD
|
|
NmpUpdateSetNetworkCommonProperties(
|
|
IN BOOL IsSourceNode,
|
|
IN LPWSTR NetworkId,
|
|
IN UCHAR * PropertyList,
|
|
IN LPDWORD PropertyListLength
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Global update handler for setting the common properties of a network.
|
|
|
|
Arguments:
|
|
|
|
IsSourceNode - Set to TRUE if this node is the source of the update.
|
|
Set to FALSE otherwise.
|
|
|
|
NetworkId - A pointer to a unicode string containing the ID of the
|
|
network to update.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if the routine succeeds.
|
|
A Win32 error code otherwise.
|
|
|
|
--*/
|
|
{
|
|
DWORD status = ERROR_SUCCESS;
|
|
NM_NETWORK_INFO networkInfo;
|
|
PNM_NETWORK network = NULL;
|
|
HLOCALXSACTION xaction = NULL;
|
|
HDMKEY networkKey = NULL;
|
|
DWORD descSize = 0;
|
|
LPWSTR descBuffer = NULL;
|
|
BOOLEAN propertyChanged = FALSE;
|
|
BOOLEAN isLockAcquired = FALSE;
|
|
|
|
|
|
if (!NmpEnterApi(NmStateOnline)) {
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Not in valid state to process SetNetworkCommonProperties "
|
|
"update.\n"
|
|
);
|
|
return(ERROR_NODE_NOT_AVAILABLE);
|
|
}
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Received update to set common properties for network %1!ws!.\n",
|
|
NetworkId
|
|
);
|
|
|
|
ZeroMemory(&networkInfo, sizeof(NM_NETWORK_INFO));
|
|
|
|
//
|
|
// Find the network's object
|
|
//
|
|
network = OmReferenceObjectById(ObjectTypeNetwork, NetworkId);
|
|
|
|
if (network == NULL) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Unable to find network %1!ws!.\n",
|
|
NetworkId
|
|
);
|
|
status = ERROR_CLUSTER_NETWORK_NOT_FOUND;
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Open the network's database key
|
|
//
|
|
networkKey = DmOpenKey(DmNetworksKey, NetworkId, KEY_WRITE);
|
|
|
|
if (networkKey == NULL) {
|
|
status = GetLastError();
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Failed to open database key for network %1!ws!, "
|
|
"status %2!u!\n",
|
|
NetworkId,
|
|
status
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Start a transaction - this must be done before acquiring the NM lock.
|
|
//
|
|
xaction = DmBeginLocalUpdate();
|
|
|
|
if (xaction == NULL) {
|
|
status = GetLastError();
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Failed to begin a transaction, status %1!u!\n",
|
|
status
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
NmpAcquireLock(); isLockAcquired = TRUE;
|
|
|
|
if (NmpJoinerNodeId != ClusterInvalidNodeId) {
|
|
status = ERROR_CLUSTER_JOIN_IN_PROGRESS;
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Cannot set network common properties because a node is "
|
|
"joining the cluster.\n"
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Validate the property list and convert it to a network params struct.
|
|
//
|
|
status = NmpNetworkValidateCommonProperties(
|
|
network,
|
|
PropertyList,
|
|
*PropertyListLength,
|
|
&networkInfo
|
|
);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Property list validation failed, status %1!u!.\n",
|
|
status
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
CL_ASSERT(network->Priority == networkInfo.Priority);
|
|
CL_ASSERT(wcscmp(NetworkId, networkInfo.Id) == 0);
|
|
CL_ASSERT(wcscmp(OmObjectName(network), networkInfo.Name) == 0);
|
|
CL_ASSERT(wcscmp(network->Transport, networkInfo.Transport) == 0);
|
|
CL_ASSERT(wcscmp(network->Address, networkInfo.Address) == 0);
|
|
CL_ASSERT(wcscmp(network->AddressMask, networkInfo.AddressMask) == 0);
|
|
|
|
//
|
|
// Check if the network's description has changed.
|
|
//
|
|
if (wcscmp(network->Description, networkInfo.Description) != 0) {
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Setting description for network %1!ws! to %2!ws!.\n",
|
|
NetworkId,
|
|
networkInfo.Description
|
|
);
|
|
|
|
//
|
|
// Allocate a buffer for the description string
|
|
//
|
|
descSize = NM_WCSLEN(networkInfo.Description);
|
|
|
|
descBuffer = MIDL_user_allocate(descSize);
|
|
|
|
if (descBuffer == NULL) {
|
|
status = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto error_exit;
|
|
}
|
|
|
|
RtlMoveMemory(descBuffer, networkInfo.Description, descSize);
|
|
|
|
//
|
|
// Update the database
|
|
//
|
|
status = DmLocalSetValue(
|
|
xaction,
|
|
networkKey,
|
|
CLUSREG_NAME_NET_DESC,
|
|
REG_SZ,
|
|
(CONST BYTE *) networkInfo.Description,
|
|
descSize
|
|
);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Set of description value failed for network %1!ws!, "
|
|
"status %2!u!.\n",
|
|
NetworkId,
|
|
status
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Updating the network object is deferred until the transaction
|
|
// commits.
|
|
//
|
|
|
|
propertyChanged = TRUE;
|
|
}
|
|
|
|
#ifdef CLUSTER_TESTPOINT
|
|
TESTPT(TpFailNmSetNetworkCommonProperties) {
|
|
status = 999999;
|
|
goto error_exit;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Check if the network's role has changed.
|
|
//
|
|
//
|
|
// NOTE: This operation is not guaranteed to be reversible, so it must
|
|
// be the last thing we do in this routine. If it succeeds, the
|
|
// update must be committed.
|
|
//
|
|
if ( network->Role != ((CLUSTER_NETWORK_ROLE) networkInfo.Role) ) {
|
|
status = NmpSetNetworkRole(
|
|
network,
|
|
networkInfo.Role,
|
|
xaction,
|
|
networkKey
|
|
);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
goto error_exit;
|
|
}
|
|
|
|
propertyChanged = TRUE;
|
|
}
|
|
|
|
if (propertyChanged) {
|
|
//
|
|
// Commit the updates to the network object in memory
|
|
//
|
|
if (descBuffer != NULL) {
|
|
MIDL_user_free(network->Description);
|
|
network->Description = descBuffer;
|
|
descBuffer = NULL;
|
|
}
|
|
|
|
//
|
|
// Issue a network property change event.
|
|
//
|
|
if (propertyChanged && (status == ERROR_SUCCESS)) {
|
|
ClusterEvent(CLUSTER_EVENT_NETWORK_PROPERTY_CHANGE, network);
|
|
}
|
|
}
|
|
|
|
CL_ASSERT(status == ERROR_SUCCESS);
|
|
|
|
error_exit:
|
|
|
|
if (isLockAcquired) {
|
|
NmpLockedLeaveApi();
|
|
NmpReleaseLock();
|
|
}
|
|
else {
|
|
NmpLeaveApi();
|
|
}
|
|
|
|
if (xaction != NULL) {
|
|
//
|
|
// Complete the transaction - this must be done after releasing
|
|
// the NM lock.
|
|
//
|
|
if (propertyChanged && (status == ERROR_SUCCESS)) {
|
|
DmCommitLocalUpdate(xaction);
|
|
}
|
|
else {
|
|
DmAbortLocalUpdate(xaction);
|
|
}
|
|
}
|
|
|
|
ClNetFreeNetworkInfo(&networkInfo);
|
|
|
|
if (descBuffer != NULL) {
|
|
MIDL_user_free(descBuffer);
|
|
}
|
|
|
|
if (networkKey != NULL) {
|
|
DmCloseKey(networkKey);
|
|
}
|
|
|
|
if (network != NULL) {
|
|
OmDereferenceObject(network);
|
|
}
|
|
|
|
return(status);
|
|
|
|
} // NmpUpdateSetNetworkCommonProperties
|
|
|
|
|
|
DWORD
|
|
NmpUpdateSetNetworkAndInterfaceStates(
|
|
IN BOOL IsSourceNode,
|
|
IN LPWSTR NetworkId,
|
|
IN CLUSTER_NETWORK_STATE * NewNetworkState,
|
|
IN PNM_STATE_ENTRY InterfaceStateVector,
|
|
IN LPDWORD InterfaceStateVectorSize
|
|
)
|
|
{
|
|
DWORD status = ERROR_SUCCESS;
|
|
PNM_NETWORK network;
|
|
|
|
|
|
NmpAcquireLock();
|
|
|
|
if (NmpLockedEnterApi(NmStateOnline)) {
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Received update to set state for network %1!ws!.\n",
|
|
NetworkId
|
|
);
|
|
|
|
//
|
|
// Find the network's object
|
|
//
|
|
network = OmReferenceObjectById(ObjectTypeNetwork, NetworkId);
|
|
|
|
if (network != NULL) {
|
|
NmpSetNetworkAndInterfaceStates(
|
|
network,
|
|
*NewNetworkState,
|
|
InterfaceStateVector,
|
|
*InterfaceStateVectorSize
|
|
);
|
|
|
|
OmDereferenceObject(network);
|
|
}
|
|
else {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Unable to find network %1!ws!.\n",
|
|
NetworkId
|
|
);
|
|
status = ERROR_CLUSTER_NETWORK_NOT_FOUND;
|
|
}
|
|
}
|
|
else {
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Not in valid state to process SetNetworkAndInterfaceStates "
|
|
"update.\n"
|
|
);
|
|
status = ERROR_NODE_NOT_AVAILABLE;
|
|
}
|
|
|
|
NmpLockedLeaveApi();
|
|
|
|
NmpReleaseLock();
|
|
|
|
return(status);
|
|
|
|
} // NmpUpdateSetNetworkAndInterfaceStates
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Helper routines for updates
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
DWORD
|
|
NmpSetNetworkRole(
|
|
PNM_NETWORK Network,
|
|
CLUSTER_NETWORK_ROLE NewRole,
|
|
HLOCALXSACTION Xaction,
|
|
HDMKEY NetworkKey
|
|
)
|
|
/*++
|
|
|
|
Called with the NmpLock acquired.
|
|
|
|
This operation is not guaranteed to be reversible, so this
|
|
function is coded such that it either succeeds and makes the update
|
|
or it fails and no update is made.
|
|
|
|
--*/
|
|
{
|
|
DWORD status = ERROR_SUCCESS;
|
|
CLUSTER_NETWORK_ROLE oldRole = Network->Role;
|
|
DWORD dwordNewRole = (DWORD) NewRole;
|
|
LPCWSTR networkId = OmObjectId(Network);
|
|
DWORD oldPriority = Network->Priority;
|
|
DWORD newPriority = oldPriority;
|
|
BOOLEAN internalNetworkListChanged = FALSE;
|
|
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Changing role for network %1!ws! to %2!u!\n",
|
|
networkId,
|
|
NewRole
|
|
);
|
|
|
|
CL_ASSERT(NewRole != oldRole);
|
|
CL_ASSERT(
|
|
NmpValidateNetworkRoleChange(Network, NewRole) == ERROR_SUCCESS
|
|
);
|
|
|
|
//
|
|
// First, make any registry updates since these can be
|
|
// aborted by the caller.
|
|
//
|
|
|
|
//
|
|
// Update the role property.
|
|
//
|
|
status = DmLocalSetValue(
|
|
Xaction,
|
|
NetworkKey,
|
|
CLUSREG_NAME_NET_ROLE,
|
|
REG_DWORD,
|
|
(CONST BYTE *) &dwordNewRole,
|
|
sizeof(DWORD)
|
|
);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Set of role value failed for network %1!ws!, "
|
|
"status %2!u!.\n",
|
|
networkId,
|
|
status
|
|
);
|
|
return(status);
|
|
}
|
|
|
|
//
|
|
// Update the priority property, if needed.
|
|
//
|
|
if (oldRole & ClusterNetworkRoleInternalUse) {
|
|
if (!(NewRole & ClusterNetworkRoleInternalUse)) {
|
|
//
|
|
// This network is no longer used for internal communication.
|
|
// Invalidate its priority value.
|
|
//
|
|
newPriority = 0xFFFFFFFF;
|
|
internalNetworkListChanged = TRUE;
|
|
}
|
|
}
|
|
else if (NewRole & ClusterNetworkRoleInternalUse) {
|
|
//
|
|
// This network is now used for internal communication.
|
|
// The network's priority is one greater than that of the lowest
|
|
// (numerically highest) priority network already in the list.
|
|
//
|
|
if (IsListEmpty(&NmpInternalNetworkList)) {
|
|
newPriority = 1;
|
|
}
|
|
else {
|
|
PNM_NETWORK network = CONTAINING_RECORD(
|
|
NmpInternalNetworkList.Blink,
|
|
NM_NETWORK,
|
|
InternalLinkage
|
|
);
|
|
|
|
CL_ASSERT(network->Priority != 0);
|
|
CL_ASSERT(network->Priority != 0xFFFFFFFF);
|
|
|
|
newPriority = network->Priority + 1;
|
|
}
|
|
|
|
internalNetworkListChanged = TRUE;
|
|
}
|
|
|
|
if (newPriority != oldPriority) {
|
|
status = DmLocalSetValue(
|
|
Xaction,
|
|
NetworkKey,
|
|
CLUSREG_NAME_NET_PRIORITY,
|
|
REG_DWORD,
|
|
(CONST BYTE *) &newPriority,
|
|
sizeof(newPriority)
|
|
);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Failed to set priority value for network %1!ws!, "
|
|
"status %2!u!.\n",
|
|
networkId,
|
|
status
|
|
);
|
|
return(status);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update the network object. Some of the subsequent subroutine calls
|
|
// depend on this.
|
|
//
|
|
Network->Role = NewRole;
|
|
Network->Priority = newPriority;
|
|
|
|
//
|
|
// Do other housekeeping based on the change.
|
|
//
|
|
// Note that the housekeeping work is coded such that none of it needs
|
|
// to be reversed if an error occurs.
|
|
//
|
|
if (NewRole == ClusterNetworkRoleNone) {
|
|
PLIST_ENTRY entry;
|
|
PNM_INTERFACE netInterface;
|
|
|
|
//
|
|
// Case 1: This network is no longer used for clustering.
|
|
//
|
|
if (NmpIsNetworkRegistered(Network)) {
|
|
//
|
|
// Delete the network from the cluster transport.
|
|
// This will delete all of its interfaces as well.
|
|
//
|
|
NmpDeregisterNetwork(Network);
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] No longer hearbeating on network %1!ws!.\n",
|
|
networkId
|
|
);
|
|
}
|
|
|
|
//
|
|
// Invalidate the connectivity data for all interfaces on
|
|
// the network.
|
|
//
|
|
for ( entry = Network->InterfaceList.Flink;
|
|
entry != &(Network->InterfaceList);
|
|
entry = entry->Flink
|
|
)
|
|
{
|
|
netInterface = CONTAINING_RECORD(
|
|
entry,
|
|
NM_INTERFACE,
|
|
NetworkLinkage
|
|
);
|
|
|
|
NmpSetInterfaceConnectivityData(
|
|
Network,
|
|
netInterface->NetIndex,
|
|
ClusterNetInterfaceUnavailable
|
|
);
|
|
}
|
|
|
|
//
|
|
// Clean up tracking data.
|
|
//
|
|
if (oldRole & ClusterNetworkRoleInternalUse) {
|
|
RemoveEntryList(&(Network->InternalLinkage));
|
|
CL_ASSERT(NmpInternalNetworkCount > 0);
|
|
NmpInternalNetworkCount--;
|
|
}
|
|
|
|
if (oldRole & ClusterNetworkRoleClientAccess) {
|
|
CL_ASSERT(NmpClientNetworkCount > 0);
|
|
NmpClientNetworkCount--;
|
|
}
|
|
|
|
//
|
|
// Use the NT5 state logic.
|
|
//
|
|
if (NmpLeaderNodeId == NmLocalNodeId) {
|
|
//
|
|
// Schedule an immediate state update.
|
|
//
|
|
NmpScheduleNetworkStateRecalc(Network);
|
|
}
|
|
}
|
|
else if (oldRole == ClusterNetworkRoleNone) {
|
|
//
|
|
// Case 2: This network is now used for clustering.
|
|
//
|
|
if (Network->LocalInterface != NULL) {
|
|
//
|
|
// Register this network with the cluster transport.
|
|
//
|
|
// Note that this action will trigger a connectivity report,
|
|
// which will in turn trigger a state update under the NT5 logic.
|
|
//
|
|
status = NmpRegisterNetwork(
|
|
Network,
|
|
TRUE // Do retry on failure
|
|
);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
goto error_exit;
|
|
}
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Now heartbeating on network %1!ws!.\n",
|
|
networkId
|
|
);
|
|
}
|
|
else if (NmpLeaderNodeId == NmLocalNodeId) {
|
|
//
|
|
// Schedule a delayed state update in case none of the other
|
|
// nodes attached to this network are up right now.
|
|
//
|
|
NmpStartNetworkStateRecalcTimer(
|
|
Network,
|
|
NM_NET_STATE_RECALC_TIMEOUT
|
|
);
|
|
}
|
|
|
|
//
|
|
// Update tracking data.
|
|
//
|
|
if (NewRole & ClusterNetworkRoleInternalUse) {
|
|
InsertTailList(
|
|
&NmpInternalNetworkList,
|
|
&(Network->InternalLinkage)
|
|
);
|
|
NmpInternalNetworkCount++;
|
|
}
|
|
|
|
if (NewRole & ClusterNetworkRoleClientAccess) {
|
|
NmpClientNetworkCount++;
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// Case 3: We are using the network in a different way now.
|
|
// Note that the network is already registered with
|
|
// the cluster transport and will remain so. As a result,
|
|
// there is no need to perform a state update.
|
|
//
|
|
|
|
//
|
|
// First, examine the former and new use of the
|
|
// network for internal communication, and make any
|
|
// necessary updates to the cluster transport.
|
|
//
|
|
if (oldRole & ClusterNetworkRoleInternalUse) {
|
|
if (!(NewRole & ClusterNetworkRoleInternalUse)) {
|
|
//
|
|
// This network is no longer used for internal communication.
|
|
// It is used for client access.
|
|
//
|
|
CL_ASSERT(NewRole & ClusterNetworkRoleClientAccess);
|
|
|
|
if (NmpIsNetworkRegistered(Network)) {
|
|
//
|
|
// Restrict the network to heartbeats only.
|
|
//
|
|
status = ClusnetSetNetworkRestriction(
|
|
NmClusnetHandle,
|
|
Network->ShortId,
|
|
TRUE, // Network is restricted
|
|
0
|
|
);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Failed to restrict network %1!ws!, "
|
|
"status %2!u!.\n",
|
|
networkId,
|
|
status
|
|
);
|
|
goto error_exit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update tracking data
|
|
//
|
|
RemoveEntryList(&(Network->InternalLinkage));
|
|
CL_ASSERT(NmpInternalNetworkCount > 0);
|
|
NmpInternalNetworkCount--;
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// The network is now used for internal communication.
|
|
//
|
|
CL_ASSERT(NewRole & ClusterNetworkRoleInternalUse);
|
|
|
|
if (NmpIsNetworkRegistered(Network)) {
|
|
//
|
|
// Clear the restriction and set the priority.
|
|
//
|
|
status = ClusnetSetNetworkRestriction(
|
|
NmClusnetHandle,
|
|
Network->ShortId,
|
|
FALSE, // Network is not restricted
|
|
newPriority
|
|
);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Failed to unrestrict network %1!ws!, "
|
|
"status %2!u!.\n",
|
|
networkId,
|
|
status
|
|
);
|
|
goto error_exit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update the tracking data
|
|
//
|
|
InsertTailList(
|
|
&NmpInternalNetworkList,
|
|
&(Network->InternalLinkage)
|
|
);
|
|
NmpInternalNetworkCount++;
|
|
}
|
|
|
|
//
|
|
// Now update the remaining tracking data based on former and
|
|
// current use of the network for client access.
|
|
//
|
|
|
|
if (oldRole & ClusterNetworkRoleClientAccess) {
|
|
if (!NewRole & ClusterNetworkRoleClientAccess) {
|
|
//
|
|
// This network is no longer used for client access.
|
|
//
|
|
CL_ASSERT(NmpClientNetworkCount > 0);
|
|
NmpClientNetworkCount--;
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// This network is now used for client access.
|
|
//
|
|
CL_ASSERT(NewRole & ClusterNetworkRoleClientAccess);
|
|
NmpClientNetworkCount++;
|
|
}
|
|
}
|
|
|
|
if (internalNetworkListChanged) {
|
|
NmpIssueClusterPropertyChangeEvent();
|
|
}
|
|
|
|
return(ERROR_SUCCESS);
|
|
|
|
error_exit:
|
|
|
|
//
|
|
// Undo the updates to the network object.
|
|
//
|
|
Network->Role = oldRole;
|
|
Network->Priority = oldPriority;
|
|
|
|
return(status);
|
|
|
|
} // NmpSetNetworkRole
|
|
|
|
|
|
VOID
|
|
NmpSetNetworkAndInterfaceStates(
|
|
IN PNM_NETWORK Network,
|
|
IN CLUSTER_NETWORK_STATE NewNetworkState,
|
|
IN PNM_STATE_ENTRY InterfaceStateVector,
|
|
IN DWORD VectorSize
|
|
)
|
|
/*++
|
|
|
|
Notes:
|
|
|
|
Called with NmpLock held.
|
|
|
|
Because NM_STATE_ENTRY is an unsigned type, while
|
|
CLUSTER_NETINTERFACE_STATE is a signed type, and
|
|
ClusterNetInterfaceStateUnknown is defined to be -1, we cannot cast
|
|
from NM_STATE_ENTRY to CLUSTER_NETINTERFACE_STATE and preserve the
|
|
value of ClusterNetInterfaceStateUnknown.
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY entry;
|
|
PNM_INTERFACE netInterface;
|
|
PNM_NODE node;
|
|
DWORD logLevel, logCode;
|
|
DWORD ifNetIndex;
|
|
LPCWSTR netInterfaceId;
|
|
LPCWSTR nodeName;
|
|
LPCWSTR networkName = OmObjectName(Network);
|
|
LPCWSTR networkId = OmObjectId(Network);
|
|
|
|
|
|
//
|
|
// Examine each interface on this network to see if its state
|
|
// has changed.
|
|
//
|
|
for ( entry = Network->InterfaceList.Flink;
|
|
entry != &(Network->InterfaceList);
|
|
entry = entry->Flink
|
|
)
|
|
{
|
|
netInterface = CONTAINING_RECORD(
|
|
entry,
|
|
NM_INTERFACE,
|
|
NetworkLinkage
|
|
);
|
|
|
|
ifNetIndex = netInterface->NetIndex;
|
|
netInterfaceId = OmObjectId(netInterface);
|
|
node = netInterface->Node;
|
|
nodeName = OmObjectName(node);
|
|
|
|
if ( (ifNetIndex < VectorSize) &&
|
|
(InterfaceStateVector[ifNetIndex] !=
|
|
(NM_STATE_ENTRY) netInterface->State
|
|
)
|
|
)
|
|
{
|
|
BOOLEAN logEvent = FALSE;
|
|
CLUSTER_EVENT eventCode = 0;
|
|
NM_STATE_ENTRY newState = InterfaceStateVector[ifNetIndex];
|
|
|
|
|
|
if (newState == (NM_STATE_ENTRY) ClusterNetInterfaceUnavailable) {
|
|
//
|
|
// Either the node has gone down or the network has been
|
|
// disabled.
|
|
//
|
|
netInterface->State = ClusterNetInterfaceUnavailable;
|
|
eventCode = CLUSTER_EVENT_NETINTERFACE_UNAVAILABLE;
|
|
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NM] Interface %1!ws! is unavailable (node: %2!ws!, "
|
|
"network: %3!ws!).\n",
|
|
netInterfaceId,
|
|
nodeName,
|
|
networkName
|
|
);
|
|
}
|
|
else if (newState == (NM_STATE_ENTRY) ClusterNetInterfaceUp) {
|
|
netInterface->State = ClusterNetInterfaceUp;
|
|
eventCode = CLUSTER_EVENT_NETINTERFACE_UP;
|
|
logCode = NM_EVENT_CLUSTER_NETINTERFACE_UP;
|
|
logLevel = LOG_NOISE;
|
|
logEvent = TRUE;
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Interface %1!ws! is up (node: %2!ws!, "
|
|
"network: %3!ws!).\n",
|
|
netInterfaceId,
|
|
nodeName,
|
|
networkName
|
|
);
|
|
}
|
|
else if ( newState ==
|
|
(NM_STATE_ENTRY) ClusterNetInterfaceUnreachable
|
|
)
|
|
{
|
|
netInterface->State = ClusterNetInterfaceUnreachable;
|
|
eventCode = CLUSTER_EVENT_NETINTERFACE_UNREACHABLE;
|
|
logCode = NM_EVENT_CLUSTER_NETINTERFACE_UNREACHABLE;
|
|
logLevel = LOG_UNUSUAL;
|
|
logEvent = TRUE;
|
|
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NM] Interface %1!ws! is unreachable (node: %2!ws!, "
|
|
"network: %3!ws!).\n",
|
|
netInterfaceId,
|
|
nodeName,
|
|
networkName
|
|
);
|
|
}
|
|
else if (newState == (NM_STATE_ENTRY) ClusterNetInterfaceFailed) {
|
|
netInterface->State = ClusterNetInterfaceFailed;
|
|
eventCode = CLUSTER_EVENT_NETINTERFACE_FAILED;
|
|
logCode = NM_EVENT_CLUSTER_NETINTERFACE_FAILED;
|
|
logLevel = LOG_UNUSUAL;
|
|
logEvent = TRUE;
|
|
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NM] Interface %1!ws! failed (node: %2!ws!, "
|
|
"network: %3!ws!).\n",
|
|
netInterfaceId,
|
|
nodeName,
|
|
networkName
|
|
);
|
|
}
|
|
else if ( newState ==
|
|
(NM_STATE_ENTRY) ClusterNetInterfaceStateUnknown
|
|
)
|
|
{
|
|
//
|
|
// This case can happen if a create update races with a
|
|
// state update. This will be the new interface. Just
|
|
// ignore it. Another state update will arrive shortly.
|
|
//
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NM] State for interface %1!ws! is unknown "
|
|
"(node: %2!ws!, network: %3!ws!).\n",
|
|
netInterfaceId,
|
|
nodeName,
|
|
networkName
|
|
);
|
|
}
|
|
else {
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NM] UpdateInterfaceState: Invalid state %1!u! "
|
|
"specified for interface %2!ws!\n",
|
|
newState,
|
|
netInterfaceId
|
|
);
|
|
CL_ASSERT(FALSE);
|
|
}
|
|
|
|
if (eventCode != 0) {
|
|
ClusterEvent(eventCode, netInterface);
|
|
}
|
|
|
|
if (logEvent && (NmpLeaderNodeId == NmLocalNodeId)) {
|
|
CsLogEvent2(
|
|
logLevel,
|
|
logCode,
|
|
nodeName,
|
|
networkName
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Network->State != NewNetworkState) {
|
|
BOOLEAN logEvent = FALSE;
|
|
CLUSTER_EVENT eventCode = 0;
|
|
|
|
|
|
if (NewNetworkState == ClusterNetworkUnavailable) {
|
|
Network->State = ClusterNetworkUnavailable;
|
|
eventCode = CLUSTER_EVENT_NETWORK_UNAVAILABLE;
|
|
}
|
|
else if (NewNetworkState == ClusterNetworkUp) {
|
|
Network->State = ClusterNetworkUp;
|
|
eventCode = CLUSTER_EVENT_NETWORK_UP;
|
|
logCode = NM_EVENT_CLUSTER_NETWORK_UP;
|
|
logLevel = LOG_NOISE;
|
|
logEvent = TRUE;
|
|
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NM] Network %1!ws! (%2!ws!) is up.\n",
|
|
networkId,
|
|
networkName
|
|
);
|
|
}
|
|
else if (NewNetworkState == ClusterNetworkDown) {
|
|
Network->State = ClusterNetworkDown;
|
|
eventCode = CLUSTER_EVENT_NETWORK_DOWN;
|
|
logCode = NM_EVENT_CLUSTER_NETWORK_DOWN;
|
|
logLevel = LOG_UNUSUAL;
|
|
logEvent = TRUE;
|
|
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NM] Network %1!ws! (%2!ws!) is down.\n",
|
|
networkId,
|
|
networkName
|
|
);
|
|
}
|
|
else if (NewNetworkState == ClusterNetworkPartitioned) {
|
|
Network->State = ClusterNetworkPartitioned;
|
|
eventCode = CLUSTER_EVENT_NETWORK_PARTITIONED;
|
|
logCode = NM_EVENT_CLUSTER_NETWORK_PARTITIONED;
|
|
logLevel = LOG_UNUSUAL;
|
|
logEvent = TRUE;
|
|
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NM] Network %1!ws! (%2!ws!) is partitioned.\n",
|
|
networkId,
|
|
networkName
|
|
);
|
|
}
|
|
else {
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NM] Invalid state %1!u! for network %2!ws!\n",
|
|
Network->State,
|
|
networkId
|
|
);
|
|
CL_ASSERT(FALSE);
|
|
}
|
|
|
|
if (eventCode != 0) {
|
|
ClusterEvent(eventCode, Network);
|
|
}
|
|
|
|
if (logEvent && (NmpLeaderNodeId == NmLocalNodeId)) {
|
|
CsLogEvent1(
|
|
logLevel,
|
|
logCode,
|
|
networkName
|
|
);
|
|
}
|
|
}
|
|
|
|
return;
|
|
|
|
} // NmpSetNetworkAndInterfaceStates
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Network state management routines
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
VOID
|
|
NmpRecomputeNT5NetworkAndInterfaceStates(
|
|
VOID
|
|
)
|
|
{
|
|
PNM_NETWORK network;
|
|
PLIST_ENTRY entry;
|
|
|
|
|
|
for (entry = NmpNetworkList.Flink;
|
|
entry != &NmpNetworkList;
|
|
entry = entry->Flink
|
|
)
|
|
{
|
|
network = CONTAINING_RECORD(
|
|
entry,
|
|
NM_NETWORK,
|
|
Linkage
|
|
);
|
|
|
|
NmpStartNetworkStateRecalcTimer(
|
|
network,
|
|
NM_NET_STATE_RECALC_TIMEOUT_AFTER_REGROUP
|
|
);
|
|
}
|
|
|
|
return;
|
|
|
|
} // NmpRecomputeNT5NetworkAndInterfaceStates
|
|
|
|
|
|
BOOLEAN
|
|
NmpComputeNetworkAndInterfaceStates(
|
|
IN PNM_NETWORK Network,
|
|
IN BOOLEAN IsolateFailure,
|
|
OUT CLUSTER_NETWORK_STATE * NewNetworkState
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Computes the state of a network and all of its interfaces based on
|
|
connectivity reports from each constituent interface. Attempts
|
|
to distinguish between failures of individual interfaces and
|
|
failure of an entire network.
|
|
|
|
Arguments:
|
|
|
|
Network - A pointer to the network object to be processed.
|
|
|
|
IsolateFailure - Triggers failure isolation analysis if set to true.
|
|
|
|
NewNetworkState - A pointer to a variable that, upon return, contains
|
|
the new state of the network.
|
|
|
|
Return Value:
|
|
|
|
TRUE if either the state of the network or the state of at least one
|
|
of its constituent interfaces changed. FALSE otherwise.
|
|
|
|
Notes:
|
|
|
|
Called with NmpLock held and the network object referenced.
|
|
|
|
--*/
|
|
{
|
|
DWORD numIfUnavailable = 0;
|
|
DWORD numIfFailed = 0;
|
|
DWORD numIfUnreachable = 0;
|
|
DWORD numIfUp = 0;
|
|
DWORD numIfReachable = 0;
|
|
PNM_CONNECTIVITY_MATRIX matrix = Network->ConnectivityMatrix;
|
|
PNM_CONNECTIVITY_MATRIX matrixEntry;
|
|
PNM_STATE_WORK_VECTOR workVector = Network->StateWorkVector;
|
|
DWORD entryCount =
|
|
Network->ConnectivityVector->EntryCount;
|
|
DWORD reporter, ifNetIndex;
|
|
BOOLEAN stateChanged = FALSE;
|
|
LPCWSTR networkId = OmObjectId(Network);
|
|
LPCWSTR netInterfaceId;
|
|
PLIST_ENTRY entry;
|
|
PNM_INTERFACE netInterface;
|
|
NM_STATE_ENTRY state;
|
|
BOOLEAN selfDeclaredFailure = FALSE;
|
|
DWORD interfaceFailureTimeout = 0;
|
|
BOOLEAN abortComputation = FALSE;
|
|
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Beginning phase 1 of state computation for network %1!ws!.\n",
|
|
networkId
|
|
);
|
|
|
|
//
|
|
// Phase 1 - Compute the state of each interface from the data
|
|
// in the connectivity matrix.
|
|
//
|
|
for ( entry = Network->InterfaceList.Flink;
|
|
entry != &(Network->InterfaceList);
|
|
entry = entry->Flink
|
|
)
|
|
{
|
|
netInterface = CONTAINING_RECORD(
|
|
entry,
|
|
NM_INTERFACE,
|
|
NetworkLinkage
|
|
);
|
|
|
|
netInterfaceId = OmObjectId(netInterface);
|
|
ifNetIndex = netInterface->NetIndex;
|
|
workVector[ifNetIndex].ReachableCount = 0;
|
|
|
|
if (NmpIsNetworkEnabledForUse(Network)) {
|
|
//
|
|
// First, check what the interface thinks its own state is
|
|
//
|
|
matrixEntry = NM_GET_CONNECTIVITY_MATRIX_ENTRY(
|
|
matrix,
|
|
ifNetIndex,
|
|
ifNetIndex,
|
|
entryCount
|
|
);
|
|
|
|
if ( *matrixEntry ==
|
|
(NM_STATE_ENTRY) ClusterNetInterfaceStateUnknown
|
|
)
|
|
{
|
|
//
|
|
// This case should never happen.
|
|
//
|
|
// An existing interface cannot think its own state is
|
|
// unknown. The reflexive entry is always initialized to
|
|
// Unavailable whenever an interface is created.
|
|
//
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] No data for interface %1!u! (%2!ws!) on network "
|
|
"%3!ws!\n",
|
|
ifNetIndex,
|
|
netInterfaceId,
|
|
networkId
|
|
);
|
|
|
|
CL_ASSERT(
|
|
*matrixEntry !=
|
|
(NM_STATE_ENTRY) ClusterNetInterfaceStateUnknown
|
|
);
|
|
|
|
state = ClusterNetInterfaceUnavailable;
|
|
numIfUnavailable++;
|
|
}
|
|
else if ( *matrixEntry ==
|
|
(NM_STATE_ENTRY) ClusterNetInterfaceUnavailable
|
|
)
|
|
{
|
|
if (NM_NODE_UP(netInterface->Node)) {
|
|
//
|
|
// The node is up, but its connectivity report has
|
|
// not been received yet. This case may happen while a
|
|
// node is joining. It can also happen if this node has
|
|
// just become the new leader.
|
|
//
|
|
// Abort the state computation.
|
|
//
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Data is not yet valid for interface %1!u! "
|
|
"(%2!ws!) on network %3!ws!.\n",
|
|
ifNetIndex,
|
|
netInterfaceId,
|
|
networkId
|
|
);
|
|
|
|
abortComputation = TRUE;
|
|
break;
|
|
}
|
|
else {
|
|
//
|
|
// The owning node is down or joining.
|
|
// The interface is in the unavailable state.
|
|
//
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Node is down for interface %1!u! (%2!ws!) on "
|
|
"network %3!ws!\n",
|
|
ifNetIndex,
|
|
netInterfaceId,
|
|
networkId
|
|
);
|
|
|
|
state = ClusterNetInterfaceUnavailable;
|
|
numIfUnavailable++;
|
|
}
|
|
}
|
|
else if ( *matrixEntry ==
|
|
(NM_STATE_ENTRY) ClusterNetInterfaceFailed
|
|
)
|
|
{
|
|
//
|
|
// The node declared its own interface as failed.
|
|
// The interface is in the failed state.
|
|
//
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Interface %1!u! (%2!ws!) has failed on network "
|
|
"%3!ws!\n",
|
|
ifNetIndex,
|
|
netInterfaceId,
|
|
networkId
|
|
);
|
|
|
|
state = ClusterNetInterfaceFailed;
|
|
numIfFailed++;
|
|
if (netInterface->State == ClusterNetInterfaceUp) {
|
|
selfDeclaredFailure = TRUE;
|
|
}
|
|
}
|
|
else {
|
|
CL_ASSERT(
|
|
*matrixEntry == (NM_STATE_ENTRY) ClusterNetInterfaceUp
|
|
);
|
|
//
|
|
// The owning node thinks its interface is Up.
|
|
//
|
|
// If there are no other operational interfaces on the
|
|
// network, then the interface is in the Up state.
|
|
//
|
|
// If there are other operational interfaces on the
|
|
// network, but all of their reports are not yet valid,
|
|
// then we defer calculating a new state for the interface.
|
|
//
|
|
// If there are other operational interfaces on the network,
|
|
// and all of their reports are valid, then if at least one
|
|
// of the operational interfaces reports that the interface
|
|
// is unreachable, then then the interface is in the
|
|
// Unreachable state. If all of the other operational
|
|
// interfaces report that the interface is reachable, then
|
|
// the interface is in the Up state.
|
|
//
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Examining connectivity data for interface %1!u! "
|
|
"(%2!ws!) on network %2!ws!.\n",
|
|
ifNetIndex,
|
|
netInterfaceId,
|
|
networkId
|
|
);
|
|
|
|
//
|
|
// Assume that the interface is Up until proven otherwise.
|
|
//
|
|
state = ClusterNetInterfaceUp;
|
|
|
|
//
|
|
// Examine the reports from other interfaces -
|
|
// i.e. scan the matrix column - to see if any node with
|
|
// an operational interface reports this interface to
|
|
// be unreachable.
|
|
//
|
|
for (reporter=0; reporter<entryCount; reporter++) {
|
|
|
|
if (reporter == ifNetIndex) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// First, see if the reporting interface is operational
|
|
// by checking what the repoerter thinks of its own
|
|
// interface.
|
|
//
|
|
matrixEntry = NM_GET_CONNECTIVITY_MATRIX_ENTRY(
|
|
matrix,
|
|
reporter,
|
|
reporter,
|
|
entryCount
|
|
);
|
|
|
|
if (*matrixEntry == ClusterNetInterfaceUp) {
|
|
PNM_CONNECTIVITY_MATRIX matrixEntry2;
|
|
|
|
//
|
|
// Both the reporter and the reportee believe that
|
|
// their respective interfaces are operational.
|
|
// Check if they agree on the state of their
|
|
// connectivity before going any further.
|
|
// ClusNet guarantees that eventually they will
|
|
// agree.
|
|
//
|
|
matrixEntry = NM_GET_CONNECTIVITY_MATRIX_ENTRY(
|
|
matrix,
|
|
reporter,
|
|
ifNetIndex,
|
|
entryCount
|
|
);
|
|
|
|
matrixEntry2 = NM_GET_CONNECTIVITY_MATRIX_ENTRY(
|
|
matrix,
|
|
ifNetIndex,
|
|
reporter,
|
|
entryCount
|
|
);
|
|
|
|
if (*matrixEntry == *matrixEntry2) {
|
|
//
|
|
// The two interfaces agree on the state of
|
|
// their connectivity. Check what they agree on.
|
|
//
|
|
if (*matrixEntry == ClusterNetInterfaceUp) {
|
|
//
|
|
// The interface is reported to be up.
|
|
//
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Interface %1!u! reports interface "
|
|
"%2!u! is up on network %3!ws!\n",
|
|
reporter,
|
|
ifNetIndex,
|
|
networkId
|
|
);
|
|
|
|
workVector[ifNetIndex].ReachableCount++;
|
|
}
|
|
else if ( *matrixEntry ==
|
|
ClusterNetInterfaceUnreachable
|
|
)
|
|
{
|
|
//
|
|
// The interface is reported to be
|
|
// unreachable.
|
|
//
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Interface %1!u! reports interface "
|
|
"%2!u! is unreachable on network %3!ws!\n",
|
|
reporter,
|
|
ifNetIndex,
|
|
networkId
|
|
);
|
|
|
|
state = ClusterNetInterfaceUnreachable;
|
|
|
|
//
|
|
// If this interface is already in failed state do fault isolation immediately.
|
|
//
|
|
if(workVector[ifNetIndex].State == ClusterNetInterfaceFailed)
|
|
IsolateFailure = TRUE;
|
|
}
|
|
else {
|
|
CL_ASSERT(
|
|
*matrixEntry != ClusterNetInterfaceFailed
|
|
);
|
|
//
|
|
// The interface report is not valid yet.
|
|
// Abort the computation.
|
|
//
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Report from interface %1!u! for "
|
|
"interface %2!u! is not valid yet on "
|
|
"network %3!ws!.\n",
|
|
reporter,
|
|
ifNetIndex,
|
|
*matrixEntry,
|
|
networkId
|
|
);
|
|
abortComputation = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// The two interfaces do not yet agree on
|
|
// their connectivity. Abort the computation.
|
|
//
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Interface %1!u! and interface %2!u! do "
|
|
"not agree on their connectivity on network "
|
|
"%3!ws!\n",
|
|
reporter,
|
|
ifNetIndex,
|
|
networkId
|
|
);
|
|
abortComputation = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// The reporter does not think its own interface is
|
|
// operational.
|
|
//
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] The report from interface %1!u! is not "
|
|
"valid on network %2!ws!.\n",
|
|
reporter,
|
|
networkId
|
|
);
|
|
}
|
|
} // end of connectivity matrix scan loop
|
|
|
|
if (abortComputation) {
|
|
//
|
|
// Abort Phase 1 computation.
|
|
//
|
|
break;
|
|
}
|
|
|
|
if (state == ClusterNetInterfaceUp) {
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Interface %1!u! (%2!ws!) is up on network "
|
|
"%3!ws!.\n",
|
|
ifNetIndex,
|
|
netInterfaceId,
|
|
networkId
|
|
);
|
|
numIfUp++;
|
|
}
|
|
else {
|
|
CL_ASSERT(state == ClusterNetInterfaceUnreachable);
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Interface %1!u! (%2!ws!) is unreachable on "
|
|
"network %3!ws!\n",
|
|
ifNetIndex,
|
|
netInterfaceId,
|
|
networkId
|
|
);
|
|
numIfUnreachable++;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// The network is disabled. It is in the Unavailable state.
|
|
//
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Interface %1!u! (%2!ws!) is unavailable because "
|
|
"network %3!ws! is disabled. \n",
|
|
ifNetIndex,
|
|
netInterfaceId,
|
|
networkId
|
|
);
|
|
state = ClusterNetInterfaceUnavailable;
|
|
numIfUnavailable++;
|
|
}
|
|
|
|
workVector[ifNetIndex].State = state;
|
|
|
|
//
|
|
// Keep track of how many interfaces on the network are
|
|
// reachable by at least one peer.
|
|
//
|
|
if ( (state == ClusterNetInterfaceUp) ||
|
|
(workVector[ifNetIndex].ReachableCount > 0)
|
|
) {
|
|
numIfReachable++;
|
|
}
|
|
|
|
if (netInterface->State != state) {
|
|
stateChanged = TRUE;
|
|
}
|
|
|
|
} // end of phase one interface loop
|
|
|
|
if (!abortComputation && !IsolateFailure && selfDeclaredFailure) {
|
|
|
|
interfaceFailureTimeout =
|
|
NmpGetNetworkInterfaceFailureTimerValue(networkId);
|
|
|
|
if (interfaceFailureTimeout > 0) {
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Delaying state computation for network %1!ws! "
|
|
"for %2!u! ms to damp self-declared failure event.\n",
|
|
networkId, interfaceFailureTimeout
|
|
);
|
|
|
|
NmpStartNetworkFailureIsolationTimer(
|
|
Network,
|
|
interfaceFailureTimeout
|
|
);
|
|
|
|
abortComputation = TRUE;
|
|
}
|
|
}
|
|
|
|
if (abortComputation) {
|
|
|
|
if (interfaceFailureTimeout == 0) {
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Aborting state computation for network %1!ws! "
|
|
" until connectivity data is valid.\n",
|
|
networkId
|
|
);
|
|
}
|
|
|
|
//
|
|
// Undo any changes we made to the work vector.
|
|
//
|
|
for ( entry = Network->InterfaceList.Flink;
|
|
entry != &(Network->InterfaceList);
|
|
entry = entry->Flink
|
|
)
|
|
{
|
|
netInterface = CONTAINING_RECORD(
|
|
entry,
|
|
NM_INTERFACE,
|
|
NetworkLinkage
|
|
);
|
|
|
|
ifNetIndex = netInterface->NetIndex;
|
|
workVector[ifNetIndex].State = (NM_STATE_ENTRY)
|
|
netInterface->State;
|
|
}
|
|
|
|
*NewNetworkState = Network->State;
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Phase 2
|
|
//
|
|
// Try to determine the scope of any failures and recompute the
|
|
// interface states. There are two cases in which we can isolate
|
|
// failures.
|
|
//
|
|
// Case 1: Three or more interfaces are operational. At least two
|
|
// interfaces can communicate with a peer. One or more
|
|
// interfaces cannot communicate with any peer.
|
|
// Those that cannot communicate at all have failed.
|
|
//
|
|
// Case 2: Exactly two interfaces are operational and they cannot
|
|
// communicate with one another. If one interface can
|
|
// communicate with an external host while the other
|
|
// cannot communicate with any external host, then the one
|
|
// that cannot communicate has failed.
|
|
//
|
|
// In any other situation, we do nothing.
|
|
//
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Completed phase 1 of state computation for network "
|
|
"%1!ws!.\n",
|
|
networkId
|
|
);
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Unavailable=%1!u!, Failed = %2!u!, Unreachable=%3!u!, "
|
|
"Reachable=%4!u!, Up=%5!u! on network %6!ws! \n",
|
|
numIfUnavailable,
|
|
numIfFailed,
|
|
numIfUnreachable,
|
|
numIfReachable,
|
|
numIfUp,
|
|
networkId
|
|
);
|
|
|
|
if (numIfUnreachable > 0) {
|
|
//
|
|
// Some interfaces are unreachable.
|
|
//
|
|
if ( ((numIfUp + numIfUnreachable) >= 3) && (numIfReachable >= 2) ) {
|
|
if (IsolateFailure) {
|
|
//
|
|
// Case 1.
|
|
//
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Trying to determine scope of connectivity failure "
|
|
"for >3 interfaces on network %1!ws!.\n",
|
|
networkId
|
|
);
|
|
|
|
//
|
|
// Any operational interface that cannot communicate with at
|
|
// least one other operational interface has failed.
|
|
//
|
|
for ( entry = Network->InterfaceList.Flink;
|
|
entry != &(Network->InterfaceList);
|
|
entry = entry->Flink
|
|
)
|
|
{
|
|
netInterface = CONTAINING_RECORD(
|
|
entry,
|
|
NM_INTERFACE,
|
|
NetworkLinkage
|
|
);
|
|
|
|
ifNetIndex = netInterface->NetIndex;
|
|
netInterfaceId = OmObjectId(netInterface);
|
|
|
|
if ( ( workVector[ifNetIndex].State ==
|
|
ClusterNetInterfaceUnreachable
|
|
)
|
|
&&
|
|
(workVector[ifNetIndex].ReachableCount == 0)
|
|
)
|
|
{
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Interface %1!u! (%2!ws!) has failed on "
|
|
"network %3!ws!\n",
|
|
ifNetIndex,
|
|
netInterfaceId,
|
|
networkId
|
|
);
|
|
workVector[ifNetIndex].State =
|
|
ClusterNetInterfaceFailed;
|
|
numIfUnreachable--;
|
|
numIfFailed++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If any interface, whose state is still unreachable, can see
|
|
// all other reachable interfaces, change its state to up.
|
|
//
|
|
for ( entry = Network->InterfaceList.Flink;
|
|
entry != &(Network->InterfaceList);
|
|
entry = entry->Flink
|
|
)
|
|
{
|
|
netInterface = CONTAINING_RECORD(
|
|
entry,
|
|
NM_INTERFACE,
|
|
NetworkLinkage
|
|
);
|
|
|
|
ifNetIndex = netInterface->NetIndex;
|
|
|
|
if ( ( workVector[ifNetIndex].State ==
|
|
ClusterNetInterfaceUnreachable
|
|
)
|
|
&&
|
|
( workVector[ifNetIndex].ReachableCount ==
|
|
(numIfUp + numIfUnreachable - 1)
|
|
)
|
|
)
|
|
{
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Interface %1!u! (%2!ws!) is up on network "
|
|
"%3!ws!\n",
|
|
ifNetIndex,
|
|
netInterfaceId,
|
|
networkId
|
|
);
|
|
workVector[ifNetIndex].State = ClusterNetInterfaceUp;
|
|
numIfUnreachable--;
|
|
numIfUp++;
|
|
}
|
|
}
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Connectivity failure scope determination complete "
|
|
"for network %1!ws!.\n",
|
|
networkId
|
|
);
|
|
|
|
}
|
|
else {
|
|
//
|
|
// Schedule a failure isolation calculation to run later.
|
|
// Declaring a failure can affect cluster resources, so we
|
|
// don't want to do it unless we are sure. Delaying for a
|
|
// while reduces the risk of a false positive.
|
|
//
|
|
NmpStartNetworkFailureIsolationTimer(Network,
|
|
NM_NET_STATE_FAILURE_ISOLATION_TIMEOUT);
|
|
|
|
}
|
|
}
|
|
else if ((numIfUnreachable == 2) && (numIfUp == 0)) {
|
|
if (IsolateFailure) {
|
|
//
|
|
// Case 2.
|
|
//
|
|
PNM_INTERFACE interface1 = NULL;
|
|
BOOLEAN interface1HasConnectivity;
|
|
LPCWSTR interfaceId1;
|
|
PNM_INTERFACE interface2 = NULL;
|
|
BOOLEAN interface2HasConnectivity;
|
|
LPCWSTR interfaceId2;
|
|
DWORD status;
|
|
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Trying to determine scope of connectivity failure "
|
|
"for 2 interfaces on network %1!ws!.\n",
|
|
networkId
|
|
);
|
|
|
|
for ( entry = Network->InterfaceList.Flink;
|
|
entry != &(Network->InterfaceList);
|
|
entry = entry->Flink
|
|
)
|
|
{
|
|
netInterface = CONTAINING_RECORD(
|
|
entry,
|
|
NM_INTERFACE,
|
|
NetworkLinkage
|
|
);
|
|
|
|
ifNetIndex = netInterface->NetIndex;
|
|
|
|
if ( workVector[ifNetIndex].State ==
|
|
ClusterNetInterfaceUnreachable
|
|
)
|
|
{
|
|
if (interface1 == NULL) {
|
|
interface1 = netInterface;
|
|
interfaceId1 = OmObjectId(interface1);
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Unreachable interface 1 = %1!ws! on "
|
|
"network %2!ws!\n",
|
|
interfaceId1,
|
|
networkId
|
|
);
|
|
}
|
|
else {
|
|
interface2 = netInterface;
|
|
interfaceId2 = OmObjectId(interface2);
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Unreachable interface 2 = %1!ws! on "
|
|
"network %2!ws!\n",
|
|
interfaceId2,
|
|
networkId
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// NmpTestInterfaceConnectivity releases and reacquires
|
|
// the NmpLock. We must reference the interface objects
|
|
// to ensure that they are still valid upon return from
|
|
// that routine.
|
|
//
|
|
OmReferenceObject(interface1);
|
|
OmReferenceObject(interface2);
|
|
|
|
status = NmpTestInterfaceConnectivity(
|
|
interface1,
|
|
&interface1HasConnectivity,
|
|
interface2,
|
|
&interface2HasConnectivity
|
|
);
|
|
|
|
if (status == ERROR_SUCCESS) {
|
|
if ( interface1HasConnectivity &&
|
|
!interface2HasConnectivity
|
|
)
|
|
{
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Interface %1!u! (%2!ws!) has Failed on "
|
|
"network %3!ws!\n",
|
|
interface2->NetIndex,
|
|
interfaceId2,
|
|
networkId
|
|
);
|
|
|
|
workVector[interface2->NetIndex].State =
|
|
ClusterNetInterfaceFailed;
|
|
numIfUnreachable--;
|
|
numIfFailed++;
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Interface %1!u! (%2!ws!) is Up on network "
|
|
"%3!ws!\n",
|
|
interface1->NetIndex,
|
|
interfaceId1,
|
|
networkId
|
|
);
|
|
|
|
workVector[interface1->NetIndex].State =
|
|
ClusterNetInterfaceUp;
|
|
numIfUnreachable--;
|
|
numIfUp++;
|
|
}
|
|
else if ( !interface1HasConnectivity &&
|
|
interface2HasConnectivity
|
|
)
|
|
{
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Interface %1!u! (%2!ws!) has Failed on "
|
|
"network %3!ws!\n",
|
|
interface1->NetIndex,
|
|
interfaceId1,
|
|
networkId
|
|
);
|
|
|
|
workVector[interface1->NetIndex].State =
|
|
ClusterNetInterfaceFailed;
|
|
numIfUnreachable--;
|
|
numIfFailed++;
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Interface %1!u! (%2!ws!) is Up on network "
|
|
"%3!ws!\n",
|
|
interface2->NetIndex,
|
|
interfaceId2,
|
|
networkId
|
|
);
|
|
|
|
workVector[interface2->NetIndex].State =
|
|
ClusterNetInterfaceUp;
|
|
numIfUnreachable--;
|
|
numIfUp++;
|
|
}
|
|
else {
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NM] Network %1!ws! state is indeterminate, Scheduling"
|
|
" Failure Isolation poll\n",
|
|
networkId
|
|
);
|
|
NmpStartNetworkFailureIsolationTimer(Network,
|
|
NmpGetIsolationPollTimerValue());
|
|
}
|
|
}
|
|
else {
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NM] Error in Interface Connectivity test for Network %1!ws!"
|
|
", Scheduling Failure Isolation poll\n",
|
|
networkId
|
|
);
|
|
NmpStartNetworkFailureIsolationTimer(Network,
|
|
NmpGetIsolationPollTimerValue());
|
|
}
|
|
|
|
OmDereferenceObject(interface1);
|
|
OmDereferenceObject(interface2);
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Connectivity failure scope determination complete "
|
|
"for network %1!ws!.\n",
|
|
networkId
|
|
);
|
|
}
|
|
else {
|
|
//
|
|
// Schedule a failure isolation calculation to run later.
|
|
// Declaring a failure can affect cluster resources, so we
|
|
// don't want to do it unless we are sure. Delaying for a
|
|
// while reduces the risk of a false positive.
|
|
//
|
|
NmpStartNetworkFailureIsolationTimer(Network,
|
|
NM_NET_STATE_FAILURE_ISOLATION_TIMEOUT);
|
|
}
|
|
}
|
|
else {
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Cannot determine scope of connectivity failure on "
|
|
"network %1!ws!.\n",
|
|
networkId
|
|
);
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// No unreachable interfaces. Disable the failure isolation timer,
|
|
// if it is running.
|
|
//
|
|
Network->FailureIsolationTimer = 0;
|
|
Network->Flags &= ~NM_FLAG_NET_ISOLATE_FAILURE;
|
|
}
|
|
|
|
//
|
|
// Phase 3 - Compute the new network state.
|
|
//
|
|
if (numIfUnavailable == Network->InterfaceCount) {
|
|
//
|
|
// All interfaces are unavailable.
|
|
//
|
|
*NewNetworkState = ClusterNetworkUnavailable;
|
|
}
|
|
else if (numIfUnreachable > 0) {
|
|
//
|
|
// Some operational interfaces have experienced
|
|
// a loss of connectivity, but the fault could not be
|
|
// isolated to them.
|
|
//
|
|
if (numIfReachable > 0) {
|
|
CL_ASSERT(numIfReachable >= 2);
|
|
//
|
|
// At least two interfaces can still communicate
|
|
// with each other, so the network is not completely down.
|
|
//
|
|
*NewNetworkState = ClusterNetworkPartitioned;
|
|
}
|
|
else {
|
|
CL_ASSERT(numIfUp == 0);
|
|
//
|
|
// None of the operational interfaces can communicate
|
|
//
|
|
*NewNetworkState = ClusterNetworkDown;
|
|
}
|
|
}
|
|
else if (numIfUp > 0) {
|
|
//
|
|
// Some interfaces are Up, all others are Failed or Unavailable
|
|
//
|
|
*NewNetworkState = ClusterNetworkUp;
|
|
}
|
|
else {
|
|
//
|
|
// Some interfaces are Unavailable, rest are Failed.
|
|
//
|
|
*NewNetworkState = ClusterNetworkDown;
|
|
}
|
|
|
|
if (Network->State != *NewNetworkState) {
|
|
stateChanged = TRUE;
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Network %1!ws! is now in state %2!u!\n",
|
|
networkId,
|
|
*NewNetworkState
|
|
);
|
|
}
|
|
|
|
return(stateChanged);
|
|
|
|
} // NmpComputeNetworkAndInterfaceStates
|
|
|
|
|
|
DWORD
|
|
NmpGetIsolationPollTimerValue(
|
|
VOID
|
|
)
|
|
/*--
|
|
* Reads the IsolationPollTimerValue Parameter from the registry if it's there
|
|
* else returns default value.
|
|
*/
|
|
{
|
|
|
|
DWORD value;
|
|
DWORD type;
|
|
DWORD len = sizeof(value);
|
|
DWORD status;
|
|
|
|
// Release NM Lock to avoid deadlock with DM lock
|
|
NmpReleaseLock();
|
|
|
|
status = DmQueryValue(
|
|
DmClusterParametersKey,
|
|
L"IsolationPollTimerValue",
|
|
&type,
|
|
(LPBYTE)&value,
|
|
&len
|
|
);
|
|
|
|
NmpAcquireLock();
|
|
if((status != ERROR_SUCCESS) || (type != REG_DWORD) ||
|
|
(value < 10) || (value > 600000)) {
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NM] Failed to read IsolationPollTimerValue or value out of range,"
|
|
"status %1!u! using default %2!u! ms\n",
|
|
status,
|
|
NM_NET_STATE_FAILURE_ISOLATION_POLL
|
|
);
|
|
return NM_NET_STATE_FAILURE_ISOLATION_POLL;
|
|
}
|
|
else {
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] IsolationPollTimerValue = %1!u!\n",
|
|
value
|
|
);
|
|
return value;
|
|
}
|
|
}
|
|
|
|
DWORD
|
|
NmpGetNetworkInterfaceFailureTimerValue(
|
|
IN LPCWSTR NetworkId
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reads InterfaceFailure timer value from registry.
|
|
If a value is located in the network key, it is used.
|
|
Otherwise the cluster parameters key is checked.
|
|
If no value is present, returns default.
|
|
|
|
Arguments:
|
|
|
|
NetworkId - id of network whose timer value to determine
|
|
|
|
Return value:
|
|
|
|
InterfaceFailure timer value
|
|
|
|
Notes:
|
|
|
|
Called with NM lock held (from NmpComputeNetworkAndInterfaceStates).
|
|
|
|
This routine queries the cluster database; hence, it drops the
|
|
NM lock. Since NmpComputeNetworkAndInterfaceStates is always called
|
|
in the context of the NmpWorkerThread, the Network is always
|
|
referenced during execution of this routine.
|
|
|
|
--*/
|
|
{
|
|
HDMKEY networkKey, paramKey;
|
|
DWORD status;
|
|
DWORD type;
|
|
DWORD value = NM_NET_STATE_INTERFACE_FAILURE_TIMEOUT;
|
|
DWORD len = sizeof(value);
|
|
BOOLEAN found = FALSE;
|
|
|
|
//
|
|
// To avoid deadlock, the DM lock must be acquired before the
|
|
// NM lock. Hence, release the NM lock prior to querying the
|
|
// cluster database.
|
|
//
|
|
NmpReleaseLock();
|
|
|
|
//
|
|
// First check the network key
|
|
//
|
|
networkKey = DmOpenKey(DmNetworksKey, NetworkId, KEY_READ);
|
|
if (networkKey == NULL) {
|
|
status = GetLastError();
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NM] Failed to open key for network %1!ws!, "
|
|
"status %1!u!\n",
|
|
NetworkId, status
|
|
);
|
|
}
|
|
else {
|
|
paramKey = DmOpenKey(networkKey, L"Parameters", KEY_READ);
|
|
if (paramKey == NULL) {
|
|
status = GetLastError();
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Failed to find Parameters key "
|
|
"for network %1!ws!, status %2!u!. Checking "
|
|
"cluster parameters ...\n",
|
|
NetworkId, status
|
|
);
|
|
}
|
|
else {
|
|
status = DmQueryValue(
|
|
paramKey,
|
|
L"InterfaceFailureTimeout",
|
|
&type,
|
|
(LPBYTE) &value,
|
|
&len
|
|
);
|
|
if (status != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Failed to read InterfaceFailureTimeout "
|
|
"for network %1!ws!, status %2!u!. Checking "
|
|
"cluster parameters ...\n",
|
|
NetworkId, status
|
|
);
|
|
}
|
|
else if (type != REG_DWORD) {
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Unexpected type (%1!u!) for network "
|
|
"%2!ws! InterfaceFailureTimeout, status %3!u!. "
|
|
"Checking cluster parameters ...\n",
|
|
type, NetworkId, status
|
|
);
|
|
}
|
|
else {
|
|
found = TRUE;
|
|
}
|
|
|
|
DmCloseKey(paramKey);
|
|
}
|
|
|
|
DmCloseKey(networkKey);
|
|
}
|
|
|
|
|
|
//
|
|
// If no value was found under the network key, check the
|
|
// cluster parameters key.
|
|
//
|
|
if (!found) {
|
|
|
|
paramKey = DmOpenKey(DmClusterParametersKey, L"Parameters", KEY_READ);
|
|
if (paramKey == NULL) {
|
|
status = GetLastError();
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Failed to find cluster Parameters key, status %1!u!.\n",
|
|
status
|
|
);
|
|
}
|
|
else {
|
|
status = DmQueryValue(
|
|
paramKey,
|
|
L"InterfaceFailureTimeout",
|
|
&type,
|
|
(LPBYTE) &value,
|
|
&len
|
|
);
|
|
if (status != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Failed to read cluster "
|
|
"InterfaceFailureTimeout, status %1!u!. "
|
|
"Using default value ...\n",
|
|
status
|
|
);
|
|
value = NM_NET_STATE_INTERFACE_FAILURE_TIMEOUT;
|
|
}
|
|
else if (type != REG_DWORD) {
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Unexpected type (%1!u!) for cluster "
|
|
"InterfaceFailureTimeout, status %2!u!. "
|
|
"Using default value ...\n",
|
|
type, status
|
|
);
|
|
value = NM_NET_STATE_INTERFACE_FAILURE_TIMEOUT;
|
|
}
|
|
|
|
DmCloseKey(paramKey);
|
|
}
|
|
}
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Using InterfaceFailureTimeout of %1!u! ms "
|
|
"for network %2!ws!.\n",
|
|
value, NetworkId
|
|
);
|
|
|
|
//
|
|
// Reacquire NM lock.
|
|
//
|
|
NmpAcquireLock();
|
|
|
|
return(value);
|
|
}
|
|
|
|
VOID
|
|
NmpConnectivityReportWorker(
|
|
IN PCLRTL_WORK_ITEM WorkItem,
|
|
IN DWORD Status,
|
|
IN DWORD BytesTransferred,
|
|
IN ULONG_PTR IoContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Worker routine for deferred operations on network objects.
|
|
Invoked to process items placed in the cluster delayed work queue.
|
|
|
|
Arguments:
|
|
|
|
WorkItem - Ignored.
|
|
|
|
Status - Ignored.
|
|
|
|
BytesTransferred - Ignored.
|
|
|
|
IoContext - Ignored.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Notes:
|
|
|
|
This routine is run in an asynchronous worker thread.
|
|
The NmpActiveThreadCount was incremented when the thread was
|
|
scheduled.
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN rescheduled = FALSE;
|
|
|
|
|
|
NmpAcquireLock();
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Connectivity report worker thread running.\n"
|
|
);
|
|
|
|
CL_ASSERT(NmpIsConnectivityReportWorkerRunning == TRUE);
|
|
CL_ASSERT(NmpNeedConnectivityReport == TRUE);
|
|
|
|
if (NmpState >= NmStateOnlinePending) {
|
|
PNM_NETWORK network;
|
|
LPCWSTR networkId;
|
|
PLIST_ENTRY entry;
|
|
DWORD status;
|
|
|
|
while(TRUE) {
|
|
|
|
NmpNeedConnectivityReport = FALSE;
|
|
|
|
for (entry = NmpNetworkList.Flink;
|
|
entry != &NmpNetworkList;
|
|
entry = entry->Flink
|
|
)
|
|
{
|
|
network = CONTAINING_RECORD(entry, NM_NETWORK, Linkage);
|
|
|
|
if (!NM_DELETE_PENDING(network)) {
|
|
networkId = OmObjectId(network);
|
|
|
|
//
|
|
// Deliver an InterfaceFailed event for the local interface
|
|
// if needed.
|
|
//
|
|
if (network->Flags & NM_FLAG_NET_REPORT_LOCAL_IF_FAILED) {
|
|
network->Flags &= ~NM_FLAG_NET_REPORT_LOCAL_IF_FAILED;
|
|
|
|
if (NmpIsNetworkRegistered(network)) {
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Processing local interface failed "
|
|
" event for network %1!ws!.\n",
|
|
networkId
|
|
);
|
|
|
|
NmpProcessLocalInterfaceStateEvent(
|
|
network->LocalInterface,
|
|
ClusterNetInterfaceFailed
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Deliver an InterfaceUp event for the local interface
|
|
// if needed.
|
|
//
|
|
if (network->Flags & NM_FLAG_NET_REPORT_LOCAL_IF_UP) {
|
|
network->Flags &= ~NM_FLAG_NET_REPORT_LOCAL_IF_UP;
|
|
|
|
if (NmpIsNetworkRegistered(network)) {
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Processing local interface up event "
|
|
"for network %1!ws!.\n",
|
|
networkId
|
|
);
|
|
|
|
NmpProcessLocalInterfaceStateEvent(
|
|
network->LocalInterface,
|
|
ClusterNetInterfaceUp
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Send a connectivity report if needed.
|
|
//
|
|
if (network->Flags & NM_FLAG_NET_REPORT_CONNECTIVITY) {
|
|
CL_NODE_ID leaderNodeId = NmpLeaderNodeId;
|
|
|
|
network->Flags &= ~NM_FLAG_NET_REPORT_CONNECTIVITY;
|
|
|
|
//
|
|
// Report our connectivity to the leader.
|
|
//
|
|
status = NmpReportNetworkConnectivity(network);
|
|
|
|
if (status == ERROR_SUCCESS) {
|
|
//
|
|
// Clear the report retry count.
|
|
//
|
|
network->ConnectivityReportRetryCount = 0;
|
|
}
|
|
else {
|
|
if (NmpLeaderNodeId == leaderNodeId) {
|
|
if (network->ConnectivityReportRetryCount++ <
|
|
NM_CONNECTIVITY_REPORT_RETRY_LIMIT
|
|
)
|
|
{
|
|
//
|
|
// Try again in 500ms.
|
|
//
|
|
network->ConnectivityReportTimer = 500;
|
|
}
|
|
else {
|
|
//
|
|
// We have failed to communicate with
|
|
// the leader for too long. Mutiny.
|
|
//
|
|
NmpAdviseNodeFailure(
|
|
NmpIdArray[NmpLeaderNodeId],
|
|
status
|
|
);
|
|
|
|
network->ConnectivityReportRetryCount = 0;
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// New leader, clear the retry count. We
|
|
// already scheduled another connectivity
|
|
// report in the node down handling.
|
|
//
|
|
network->ConnectivityReportRetryCount = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} // end network for loop
|
|
|
|
if (NmpNeedConnectivityReport == FALSE) {
|
|
//
|
|
// No more work to do - break out of loop.
|
|
//
|
|
break;
|
|
}
|
|
|
|
//
|
|
// More work to do. Resubmit the work item. We do this instead
|
|
// of looping so we don't hog the worker thread. If
|
|
// rescheduling fails, we will loop again in this thread.
|
|
//
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] More connectivity reports to send. Rescheduling "
|
|
"worker thread.\n"
|
|
);
|
|
|
|
status = NmpScheduleConnectivityReportWorker();
|
|
|
|
if (status == ERROR_SUCCESS) {
|
|
rescheduled = TRUE;
|
|
break;
|
|
}
|
|
} // end while(TRUE)
|
|
}
|
|
|
|
if (!rescheduled) {
|
|
NmpIsConnectivityReportWorkerRunning = FALSE;
|
|
}
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Connectivity report worker thread finished.\n"
|
|
);
|
|
|
|
//
|
|
// Decrement active thread reference count.
|
|
//
|
|
NmpLockedLeaveApi();
|
|
|
|
NmpReleaseLock();
|
|
|
|
return;
|
|
|
|
} // NmpConnectivityReportWorker
|
|
|
|
|
|
VOID
|
|
NmpNetworkWorker(
|
|
IN PCLRTL_WORK_ITEM WorkItem,
|
|
IN DWORD Status,
|
|
IN DWORD BytesTransferred,
|
|
IN ULONG_PTR IoContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Worker routine for deferred operations on network objects.
|
|
Invoked to process items placed in the cluster delayed work queue.
|
|
|
|
Arguments:
|
|
|
|
WorkItem - A pointer to a work item structure that identifies the
|
|
network for which to perform work.
|
|
|
|
Status - Ignored.
|
|
|
|
BytesTransferred - Ignored.
|
|
|
|
IoContext - Ignored.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Notes:
|
|
|
|
This routine is run in an asynchronous worker thread.
|
|
The NmpActiveThreadCount was incremented when the thread was
|
|
scheduled. The network object was also referenced.
|
|
|
|
--*/
|
|
{
|
|
PNM_NETWORK network = (PNM_NETWORK) WorkItem->Context;
|
|
LPCWSTR networkId = OmObjectId(network);
|
|
BOOLEAN rescheduled = FALSE;
|
|
|
|
|
|
NmpAcquireLock();
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Worker thread processing network %1!ws!.\n",
|
|
networkId
|
|
);
|
|
|
|
if ((NmpState >= NmStateOnlinePending) && !NM_DELETE_PENDING(network)) {
|
|
DWORD status;
|
|
|
|
while(TRUE) {
|
|
CL_ASSERT(network->Flags & NM_FLAG_NET_WORKER_RUNNING);
|
|
|
|
//
|
|
// Register the network if needed.
|
|
//
|
|
if (network->Flags & NM_FLAG_NET_NEED_TO_REGISTER) {
|
|
network->Flags &= ~NM_FLAG_NET_NEED_TO_REGISTER;
|
|
|
|
if (network->LocalInterface != NULL) {
|
|
(VOID) NmpRegisterNetwork(
|
|
network,
|
|
TRUE // Do retry on failure
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Isolate a network failure if needed.
|
|
//
|
|
if (network->Flags & NM_FLAG_NET_ISOLATE_FAILURE) {
|
|
|
|
BOOLEAN stateChanged;
|
|
CLUSTER_NETWORK_STATE newNetworkState;
|
|
|
|
network->Flags &= ~NM_FLAG_NET_ISOLATE_FAILURE;
|
|
|
|
//
|
|
// Turn off the state recalc timer and flag, since we will
|
|
// do a recalc here.
|
|
//
|
|
network->Flags &= ~NM_FLAG_NET_RECALC_STATE;
|
|
network->StateRecalcTimer = 0;
|
|
|
|
CL_ASSERT(NmpLeaderNodeId == NmLocalNodeId);
|
|
|
|
//
|
|
// Recompute the interface and network states
|
|
// with failure isolation enabled.
|
|
//
|
|
stateChanged = NmpComputeNetworkAndInterfaceStates(
|
|
network,
|
|
TRUE,
|
|
&newNetworkState
|
|
);
|
|
|
|
if (stateChanged) {
|
|
//
|
|
// Broadcast the new network and interface states to
|
|
// all nodes
|
|
//
|
|
status = NmpGlobalSetNetworkAndInterfaceStates(
|
|
network,
|
|
newNetworkState
|
|
);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
//
|
|
// Try again in 1 second.
|
|
//
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Global update failed for network %1!ws!, "
|
|
"status %2!u! - restarting failure isolation "
|
|
"timer.\n",
|
|
networkId,
|
|
status
|
|
);
|
|
|
|
network->FailureIsolationTimer = 1000;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Recalculate the network and interface states if needed.
|
|
//
|
|
if (network->Flags & NM_FLAG_NET_RECALC_STATE) {
|
|
BOOLEAN stateChanged;
|
|
CLUSTER_NETWORK_STATE newNetworkState;
|
|
|
|
network->Flags &= ~NM_FLAG_NET_RECALC_STATE;
|
|
|
|
CL_ASSERT(NmpLeaderNodeId == NmLocalNodeId);
|
|
|
|
//
|
|
// Recompute the interface and network states
|
|
// with failure isolation disabled. It will be
|
|
// enabled if needed.
|
|
//
|
|
stateChanged = NmpComputeNetworkAndInterfaceStates(
|
|
network,
|
|
FALSE,
|
|
&newNetworkState
|
|
);
|
|
|
|
if (stateChanged) {
|
|
//
|
|
// Broadcast the new network and interface states to
|
|
// all nodes
|
|
//
|
|
status = NmpGlobalSetNetworkAndInterfaceStates(
|
|
network,
|
|
newNetworkState
|
|
);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
//
|
|
// Try again in 500ms.
|
|
//
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] NetworkStateUpdateWorker failed issue "
|
|
"global update for network %1!ws!, status "
|
|
"%2!u! - restarting update timer.\n",
|
|
networkId,
|
|
status
|
|
);
|
|
|
|
network->StateRecalcTimer = 500;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!(network->Flags & NM_NET_WORK_FLAGS)) {
|
|
//
|
|
// No more work to do - break out of loop.
|
|
//
|
|
break;
|
|
}
|
|
|
|
//
|
|
// More work to do. Resubmit the work item. We do this instead
|
|
// of looping so we don't hog the worker thread. If
|
|
// rescheduling fails, we will loop again in this thread.
|
|
//
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] More work to do for network %1!ws!. Rescheduling "
|
|
"worker thread.\n",
|
|
networkId
|
|
);
|
|
|
|
status = NmpScheduleNetworkWorker(network);
|
|
|
|
if (status == ERROR_SUCCESS) {
|
|
rescheduled = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
network->Flags &= ~NM_NET_WORK_FLAGS;
|
|
}
|
|
|
|
if (!rescheduled) {
|
|
network->Flags &= ~NM_FLAG_NET_WORKER_RUNNING;
|
|
}
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Worker thread finished processing network %1!ws!.\n",
|
|
networkId
|
|
);
|
|
|
|
NmpLockedLeaveApi();
|
|
|
|
NmpReleaseLock();
|
|
|
|
OmDereferenceObject(network);
|
|
|
|
return;
|
|
|
|
} // NmpNetworkWorker
|
|
|
|
|
|
VOID
|
|
NmpNetworkTimerTick(
|
|
IN DWORD MsTickInterval
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called by NM periodic timer to decrement network timers.
|
|
|
|
Arguments:
|
|
|
|
MsTickInterval - The number of milliseconds that have passed since
|
|
the last timer tick.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Notes:
|
|
|
|
Called with NmpLock held.
|
|
|
|
--*/
|
|
{
|
|
if (NmpLockedEnterApi(NmStateOnlinePending)) {
|
|
PLIST_ENTRY entry;
|
|
PNM_NETWORK network;
|
|
|
|
|
|
//
|
|
// Walk thru the list of networks and decrement any running timers.
|
|
//
|
|
for ( entry = NmpNetworkList.Flink;
|
|
entry != &NmpNetworkList;
|
|
entry = entry->Flink
|
|
)
|
|
{
|
|
network = CONTAINING_RECORD(entry, NM_NETWORK, Linkage);
|
|
|
|
//
|
|
// Network registration retry timer.
|
|
//
|
|
if (network->RegistrationRetryTimer != 0) {
|
|
if (network->RegistrationRetryTimer > MsTickInterval) {
|
|
network->RegistrationRetryTimer -= MsTickInterval;
|
|
}
|
|
else {
|
|
//
|
|
// The timer has expired. Schedule a worker thread
|
|
// to register the network.
|
|
//
|
|
LPCWSTR networkId = OmObjectId(network);
|
|
LPCWSTR networkName = OmObjectName(network);
|
|
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Registration retry timer expired for "
|
|
"network %1!ws! (%2!ws!).\n",
|
|
networkId,
|
|
networkName
|
|
);
|
|
|
|
NmpScheduleNetworkRegistration(network);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Connectivity report generation timer.
|
|
//
|
|
if (network->ConnectivityReportTimer != 0) {
|
|
if (network->ConnectivityReportTimer > MsTickInterval) {
|
|
network->ConnectivityReportTimer -= MsTickInterval;
|
|
}
|
|
else {
|
|
//
|
|
// The timer has expired. Schedule a worker thread
|
|
// to deliver a connectivity report.
|
|
//
|
|
LPCWSTR networkId = OmObjectId(network);
|
|
LPCWSTR networkName = OmObjectName(network);
|
|
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Connectivity report timer expired for "
|
|
"network %1!ws! (%2!ws!).\n",
|
|
networkId,
|
|
networkName
|
|
);
|
|
|
|
NmpScheduleNetworkConnectivityReport(network);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Network state recalculation timer.
|
|
//
|
|
if (network->StateRecalcTimer != 0) {
|
|
if (network->StateRecalcTimer > MsTickInterval) {
|
|
network->StateRecalcTimer -= MsTickInterval;
|
|
}
|
|
else {
|
|
//
|
|
// The timer has expired. Schedule a worker thread
|
|
// to recalculate the state of the network.
|
|
//
|
|
LPCWSTR networkId = OmObjectId(network);
|
|
LPCWSTR networkName = OmObjectName(network);
|
|
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] State recalculation timer expired for "
|
|
"network %1!ws! (%2!ws!).\n",
|
|
networkId,
|
|
networkName
|
|
);
|
|
|
|
NmpScheduleNetworkStateRecalc(network);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Network multicast address renewal timer.
|
|
//
|
|
if (network->McastAddressRenewTimer != 0) {
|
|
if (network->McastAddressRenewTimer > MsTickInterval) {
|
|
network->McastAddressRenewTimer -= MsTickInterval;
|
|
}
|
|
else {
|
|
//
|
|
// The timer has expired. Schedule a worker thread
|
|
// to renew the network's multicast address.
|
|
//
|
|
LPCWSTR networkId = OmObjectId(network);
|
|
LPCWSTR networkName = OmObjectName(network);
|
|
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Multicast address lease renewal timer "
|
|
"expired for network %1!ws! (%2!ws!).\n",
|
|
networkId,
|
|
networkName
|
|
);
|
|
|
|
NmpScheduleMulticastAddressRenewal(network);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Network multicast address release timer.
|
|
//
|
|
if (network->McastAddressReleaseRetryTimer != 0) {
|
|
if (network->McastAddressReleaseRetryTimer > MsTickInterval) {
|
|
network->McastAddressReleaseRetryTimer -= MsTickInterval;
|
|
}
|
|
else {
|
|
//
|
|
// The timer has expired. Schedule a worker thread
|
|
// to release the network's multicast address.
|
|
//
|
|
LPCWSTR networkId = OmObjectId(network);
|
|
LPCWSTR networkName = OmObjectName(network);
|
|
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Multicast address release timer "
|
|
"expired for network %1!ws! (%2!ws!).\n",
|
|
networkId,
|
|
networkName
|
|
);
|
|
|
|
NmpScheduleMulticastAddressRelease(network);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Network failure isolation timer.
|
|
//
|
|
if (network->FailureIsolationTimer != 0) {
|
|
if (network->FailureIsolationTimer > MsTickInterval) {
|
|
network->FailureIsolationTimer -= MsTickInterval;
|
|
}
|
|
else {
|
|
//
|
|
// The timer has expired. Schedule a worker thread
|
|
// to perform failure isolation on the network.
|
|
//
|
|
DWORD status = ERROR_SUCCESS;
|
|
LPCWSTR networkId = OmObjectId(network);
|
|
LPCWSTR networkName = OmObjectName(network);
|
|
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Failure isolation timer expired for network "
|
|
"%1!ws! (%2!ws!).\n",
|
|
networkId,
|
|
networkName
|
|
);
|
|
|
|
if (!NmpIsNetworkWorkerRunning(network)) {
|
|
status = NmpScheduleNetworkWorker(network);
|
|
}
|
|
|
|
//
|
|
// Zero out the timer if we succeeded in scheduling a
|
|
// worker thread. If we failed, leave the timer value
|
|
// non-zero and we'll try again on the next tick.
|
|
//
|
|
if (status == ERROR_SUCCESS) {
|
|
network->FailureIsolationTimer = 0;
|
|
network->Flags |= NM_FLAG_NET_ISOLATE_FAILURE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
NmpLockedLeaveApi();
|
|
}
|
|
|
|
return;
|
|
|
|
} // NmpNetworkTimerTick
|
|
|
|
|
|
VOID
|
|
NmpStartNetworkConnectivityReportTimer(
|
|
PNM_NETWORK Network
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Starts the connectivity report timer for a network. Connectivity
|
|
reports are delayed in order to aggregate events when a failure
|
|
occurs that affects multiple nodes.
|
|
|
|
Arguments:
|
|
|
|
Network - A pointer to the network for which to start the timer.
|
|
|
|
Return Value
|
|
|
|
None.
|
|
|
|
Notes:
|
|
|
|
Called with NM lock held.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Check if the timer is already running.
|
|
//
|
|
if (Network->ConnectivityReportTimer == 0) {
|
|
//
|
|
// Check how many nodes are attached to this network.
|
|
//
|
|
if (Network->InterfaceCount <= 2) {
|
|
//
|
|
// There is no point in waiting to aggregate reports when
|
|
// only two nodes are connected to the network.
|
|
// Just schedule a worker thread to deliver the report.
|
|
//
|
|
NmpScheduleNetworkConnectivityReport(Network);
|
|
}
|
|
else {
|
|
//
|
|
// More than two nodes are connected to this network.
|
|
// Start the timer.
|
|
//
|
|
LPCWSTR networkId = OmObjectId(Network);
|
|
LPCWSTR networkName = OmObjectName(Network);
|
|
|
|
Network->ConnectivityReportTimer =
|
|
NM_NET_CONNECTIVITY_REPORT_TIMEOUT;
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Started connectivity report timer (%1!u!ms) for "
|
|
"network %2!ws! (%3!ws!)\n",
|
|
Network->ConnectivityReportTimer,
|
|
networkId,
|
|
networkName
|
|
);
|
|
}
|
|
}
|
|
|
|
return;
|
|
|
|
} // NmpStartNetworkConnectivityReportTimer
|
|
|
|
|
|
VOID
|
|
NmpStartNetworkStateRecalcTimer(
|
|
PNM_NETWORK Network,
|
|
DWORD Timeout
|
|
)
|
|
{
|
|
LPCWSTR networkId = OmObjectId(Network);
|
|
LPCWSTR networkName = OmObjectName(Network);
|
|
|
|
if (Network->StateRecalcTimer == 0) {
|
|
Network->StateRecalcTimer = Timeout;
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Started state recalculation timer (%1!u!ms) for "
|
|
"network %2!ws! (%3!ws!)\n",
|
|
Network->StateRecalcTimer,
|
|
networkId,
|
|
networkName
|
|
);
|
|
}
|
|
|
|
return;
|
|
|
|
} // NmpStartNetworkStateRecalcTimer
|
|
|
|
|
|
VOID
|
|
NmpStartNetworkFailureIsolationTimer(
|
|
PNM_NETWORK Network,
|
|
DWORD Timeout
|
|
)
|
|
{
|
|
|
|
if (Network->FailureIsolationTimer == 0) {
|
|
Network->FailureIsolationTimer = Timeout;
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Started failure isolation timer (%1!u!ms) for "
|
|
"network %2!ws! (%3!ws!)\n",
|
|
Network->FailureIsolationTimer,
|
|
OmObjectId(Network),
|
|
OmObjectName(Network)
|
|
);
|
|
}
|
|
|
|
return;
|
|
|
|
} // NmpStartNetworkFailureIsolationTimer
|
|
|
|
|
|
VOID
|
|
NmpStartNetworkRegistrationRetryTimer(
|
|
PNM_NETWORK Network
|
|
)
|
|
{
|
|
if (Network->RegistrationRetryTimer == 0) {
|
|
if (Network->RegistrationRetryTimeout == 0) {
|
|
Network->RegistrationRetryTimeout =
|
|
NM_NET_MIN_REGISTRATION_RETRY_TIMEOUT;
|
|
}
|
|
else {
|
|
//
|
|
// Exponential backoff
|
|
//
|
|
Network->RegistrationRetryTimeout *= 2;
|
|
|
|
if ( Network->RegistrationRetryTimeout >
|
|
NM_NET_MAX_REGISTRATION_RETRY_TIMEOUT
|
|
)
|
|
{
|
|
Network->RegistrationRetryTimeout =
|
|
NM_NET_MAX_REGISTRATION_RETRY_TIMEOUT;
|
|
}
|
|
}
|
|
|
|
Network->RegistrationRetryTimer = Network->RegistrationRetryTimeout;
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Started registration retry timer (%1!u!ms) for "
|
|
"network %2!ws! (%3!ws!)\n",
|
|
Network->RegistrationRetryTimer,
|
|
OmObjectId(Network),
|
|
OmObjectName(Network)
|
|
);
|
|
}
|
|
|
|
return;
|
|
|
|
} // NmpStartNetworkRegistrationRetryTimer
|
|
|
|
|
|
VOID
|
|
NmpScheduleNetworkConnectivityReport(
|
|
PNM_NETWORK Network
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Schedules a worker thread to deliver a connectivity report to
|
|
the leader node for the specified network. Called when the
|
|
ConnectivityReport timer expires for a network. Also called
|
|
directly in some cases.
|
|
|
|
Arguments:
|
|
|
|
A pointer to the network object for which to generate a report.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Notes:
|
|
|
|
This routine is called with the NM lock held.
|
|
|
|
--*/
|
|
{
|
|
DWORD status = ERROR_SUCCESS;
|
|
|
|
//
|
|
// Check if a worker thread is already scheduled.
|
|
//
|
|
if (!NmpIsConnectivityReportWorkerRunning) {
|
|
status = NmpScheduleConnectivityReportWorker();
|
|
}
|
|
|
|
if (status == ERROR_SUCCESS) {
|
|
//
|
|
// We succeeded in scheduling a worker thread. Stop the
|
|
// ConnectivityReport timer and set the work flag to generate
|
|
// a report.
|
|
//
|
|
Network->ConnectivityReportTimer = 0;
|
|
Network->Flags |= NM_FLAG_NET_REPORT_CONNECTIVITY;
|
|
NmpNeedConnectivityReport = TRUE;
|
|
}
|
|
else {
|
|
//
|
|
// We failed to schedule a worker thread. Set the
|
|
// ConnecivityReport timer to expire on the next tick, so we
|
|
// can try again.
|
|
//
|
|
Network->ConnectivityReportTimer = 1;
|
|
}
|
|
|
|
return;
|
|
|
|
} // NmpScheduleNetworkConnectivityReport
|
|
|
|
|
|
VOID
|
|
NmpScheduleNetworkStateRecalc(
|
|
PNM_NETWORK Network
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Schedules a worker thread to recalculate the state of the
|
|
specified network and all of the network's interface. A network
|
|
state recalculation can be triggered by the arrival of a connectivity
|
|
report, the joining/death of a node, or a network role change.
|
|
|
|
Arguments:
|
|
|
|
A pointer to the network object whose state is to be recalculated.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Notes:
|
|
|
|
This routine is called with the NM lock held.
|
|
|
|
--*/
|
|
{
|
|
DWORD status = ERROR_SUCCESS;
|
|
|
|
//
|
|
// Check if a worker thread is already scheduled to
|
|
// service this network.
|
|
//
|
|
if (!NmpIsNetworkWorkerRunning(Network)) {
|
|
status = NmpScheduleNetworkWorker(Network);
|
|
}
|
|
|
|
if (status == ERROR_SUCCESS) {
|
|
//
|
|
// We succeeded in scheduling a worker thread. Stop the
|
|
// StateRecalc timer and set the state recalculation work flag.
|
|
//
|
|
Network->StateRecalcTimer = 0;
|
|
Network->Flags |= NM_FLAG_NET_RECALC_STATE;
|
|
}
|
|
else {
|
|
//
|
|
// We failed to schedule a worker thread. Set the StateRecalc
|
|
// timer to expire on the next tick, so we can try again.
|
|
//
|
|
Network->ConnectivityReportTimer = 1;
|
|
}
|
|
|
|
return;
|
|
|
|
} // NmpScheduleNetworkStateRecalc
|
|
|
|
|
|
VOID
|
|
NmpScheduleNetworkRegistration(
|
|
PNM_NETWORK Network
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Schedules a worker thread to register a network with the cluster
|
|
transport.
|
|
|
|
Arguments:
|
|
|
|
A pointer to the network to register.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Notes:
|
|
|
|
This routine is called with the NM lock held.
|
|
|
|
--*/
|
|
{
|
|
DWORD status = ERROR_SUCCESS;
|
|
|
|
//
|
|
// Check if a worker thread is already scheduled to
|
|
// service this network.
|
|
//
|
|
if (!NmpIsNetworkWorkerRunning(Network)) {
|
|
status = NmpScheduleNetworkWorker(Network);
|
|
}
|
|
|
|
if (status == ERROR_SUCCESS) {
|
|
//
|
|
// We succeeded in scheduling a worker thread. Stop the
|
|
// retry timer and set the registration work flag.
|
|
//
|
|
Network->RegistrationRetryTimer = 0;
|
|
Network->Flags |= NM_FLAG_NET_NEED_TO_REGISTER;
|
|
}
|
|
else {
|
|
//
|
|
// We failed to schedule a worker thread. Set the retry
|
|
// timer to expire on the next tick, so we can try again.
|
|
//
|
|
Network->RegistrationRetryTimer = 1;
|
|
}
|
|
|
|
return;
|
|
|
|
} // NmpScheduleNetworkRegistration
|
|
|
|
|
|
DWORD
|
|
NmpScheduleConnectivityReportWorker(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Schedule a worker thread to deliver network connectivity reports.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
A Win32 status code.
|
|
|
|
Notes:
|
|
|
|
Called with the NM global lock held.
|
|
|
|
--*/
|
|
{
|
|
DWORD status;
|
|
|
|
|
|
ClRtlInitializeWorkItem(
|
|
&NmpConnectivityReportWorkItem,
|
|
NmpConnectivityReportWorker,
|
|
NULL
|
|
);
|
|
|
|
status = ClRtlPostItemWorkQueue(
|
|
CsDelayedWorkQueue,
|
|
&NmpConnectivityReportWorkItem,
|
|
0,
|
|
0
|
|
);
|
|
|
|
if (status == ERROR_SUCCESS) {
|
|
NmpActiveThreadCount++;
|
|
NmpIsConnectivityReportWorkerRunning = TRUE;
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Scheduled network connectivity report worker thread.\n"
|
|
);
|
|
}
|
|
else {
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NM] Failed to schedule network connectivity report worker "
|
|
"thread, status %1!u!\n",
|
|
status
|
|
);
|
|
}
|
|
|
|
return(status);
|
|
|
|
} // NmpScheduleConnectivityReportWorker
|
|
|
|
|
|
DWORD
|
|
NmpScheduleNetworkWorker(
|
|
PNM_NETWORK Network
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Schedule a worker thread to service this network
|
|
|
|
Arguments:
|
|
|
|
Network - Pointer to the network for which to schedule a worker thread.
|
|
|
|
Return Value:
|
|
|
|
A Win32 status code.
|
|
|
|
Notes:
|
|
|
|
Called with the NM global lock held.
|
|
|
|
--*/
|
|
{
|
|
DWORD status;
|
|
LPCWSTR networkId = OmObjectId(Network);
|
|
|
|
|
|
ClRtlInitializeWorkItem(
|
|
&(Network->WorkItem),
|
|
NmpNetworkWorker,
|
|
(PVOID) Network
|
|
);
|
|
|
|
status = ClRtlPostItemWorkQueue(
|
|
CsDelayedWorkQueue,
|
|
&(Network->WorkItem),
|
|
0,
|
|
0
|
|
);
|
|
|
|
if (status == ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Scheduled worker thread to service network %1!ws!.\n",
|
|
networkId
|
|
);
|
|
|
|
NmpActiveThreadCount++;
|
|
Network->Flags |= NM_FLAG_NET_WORKER_RUNNING;
|
|
OmReferenceObject(Network);
|
|
}
|
|
else {
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NM] Failed to schedule worker thread to service network "
|
|
"%1!ws!, status %2!u!\n",
|
|
networkId,
|
|
status
|
|
);
|
|
}
|
|
|
|
return(status);
|
|
|
|
} // NmpScheduleNetworkWorker
|
|
|
|
|
|
DWORD
|
|
NmpReportNetworkConnectivity(
|
|
IN PNM_NETWORK Network
|
|
)
|
|
/*+
|
|
|
|
Notes:
|
|
|
|
Called with the NmpLock held.
|
|
May be called by asynchronous worker threads.
|
|
|
|
--*/
|
|
{
|
|
DWORD status = ERROR_SUCCESS;
|
|
LPCWSTR networkId = OmObjectId(Network);
|
|
|
|
|
|
//
|
|
// Since this routine is called by asynchronous worker threads,
|
|
// check if the report is still valid.
|
|
//
|
|
if (NmpIsNetworkRegistered(Network)) {
|
|
PNM_CONNECTIVITY_VECTOR vector = Network->ConnectivityVector;
|
|
PNM_INTERFACE localInterface = Network->LocalInterface;
|
|
|
|
//
|
|
// Record the information in our local data structures.
|
|
//
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NM] Updating local connectivity info for network %1!ws!.\n",
|
|
networkId
|
|
);
|
|
|
|
NmpProcessInterfaceConnectivityReport(
|
|
localInterface,
|
|
vector
|
|
);
|
|
|
|
if (NmpLeaderNodeId != NmLocalNodeId) {
|
|
//
|
|
// Send the report to the leader via RPC.
|
|
//
|
|
PNM_CONNECTIVITY_VECTOR tmpVector;
|
|
DWORD vectorSize;
|
|
LPCWSTR localInterfaceId =
|
|
OmObjectId(localInterface);
|
|
|
|
//
|
|
// Allocate a temporary connectivity vector, since the
|
|
// one in the network object can be resized during the
|
|
// RPC call.
|
|
//
|
|
vectorSize = sizeof(NM_CONNECTIVITY_VECTOR) +
|
|
((vector->EntryCount - 1) * sizeof(NM_STATE_ENTRY));
|
|
|
|
tmpVector = LocalAlloc(LMEM_FIXED, vectorSize);
|
|
|
|
if (tmpVector != NULL) {
|
|
CopyMemory(tmpVector, vector, vectorSize);
|
|
|
|
OmReferenceObject(Network);
|
|
OmReferenceObject(localInterface);
|
|
|
|
if (NM_NODE_UP(NmLocalNode) && (NmpState == NmStateOnline)) {
|
|
//
|
|
// This node is fully operational. Send the report
|
|
// directly to the leader.
|
|
//
|
|
PNM_NODE node = NmpIdArray[NmpLeaderNodeId];
|
|
RPC_BINDING_HANDLE rpcBinding = node->ReportRpcBinding;
|
|
|
|
OmReferenceObject(node);
|
|
|
|
status = NmpReportInterfaceConnectivity(
|
|
rpcBinding,
|
|
(LPWSTR) localInterfaceId,
|
|
tmpVector,
|
|
(LPWSTR) networkId
|
|
);
|
|
|
|
OmDereferenceObject(node);
|
|
}
|
|
else if (CsJoinSponsorBinding != NULL) {
|
|
//
|
|
// This node is joining. Forward the report to the
|
|
// sponsor.
|
|
//
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NM] Reporting connectivity to sponsor for "
|
|
"network %1!ws!.\n",
|
|
networkId
|
|
);
|
|
|
|
NmpReleaseLock();
|
|
|
|
status = NmRpcReportJoinerInterfaceConnectivity(
|
|
CsJoinSponsorBinding,
|
|
NmpJoinSequence,
|
|
NmLocalNodeIdString,
|
|
(LPWSTR) localInterfaceId,
|
|
tmpVector
|
|
);
|
|
|
|
NmpAcquireLock();
|
|
}
|
|
else {
|
|
//
|
|
// This node must be shutting down
|
|
//
|
|
CL_ASSERT(NmpState == NmStateOfflinePending);
|
|
status = ERROR_SUCCESS;
|
|
}
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Failed to report connectivity for network "
|
|
"%1!ws!, status %2!u!.\n",
|
|
networkId,
|
|
status
|
|
);
|
|
}
|
|
|
|
LocalFree(tmpVector);
|
|
|
|
OmDereferenceObject(localInterface);
|
|
OmDereferenceObject(Network);
|
|
}
|
|
else {
|
|
status = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
}
|
|
}
|
|
|
|
return(status);
|
|
|
|
} // NmpReportNetworkConnectivity
|
|
|
|
|
|
VOID
|
|
NmpUpdateNetworkConnectivityForDownNode(
|
|
PNM_NODE Node
|
|
)
|
|
/*++
|
|
|
|
Notes:
|
|
|
|
Called with NmpLock held.
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY entry;
|
|
PNM_NETWORK network;
|
|
LPCWSTR networkId;
|
|
PNM_INTERFACE netInterface;
|
|
DWORD entryCount;
|
|
DWORD i;
|
|
PNM_CONNECTIVITY_MATRIX matrixEntry;
|
|
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Cleaning up network and interface states for dead node %1!u!\n",
|
|
Node->NodeId
|
|
);
|
|
|
|
//
|
|
// Walk the dead node's interface list and clean up the network and
|
|
// interface states.
|
|
//
|
|
for (entry = Node->InterfaceList.Flink;
|
|
entry != &(Node->InterfaceList);
|
|
entry = entry->Flink
|
|
)
|
|
{
|
|
netInterface = CONTAINING_RECORD(
|
|
entry,
|
|
NM_INTERFACE,
|
|
NodeLinkage
|
|
);
|
|
|
|
network = netInterface->Network;
|
|
networkId = OmObjectId(network);
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Cleaning up state of network %1!ws!\n",
|
|
networkId
|
|
);
|
|
|
|
//
|
|
// Invalidate the connectivity data for this interface.
|
|
//
|
|
NmpSetInterfaceConnectivityData(
|
|
network,
|
|
netInterface->NetIndex,
|
|
ClusterNetInterfaceUnavailable
|
|
);
|
|
|
|
//
|
|
// If the local node is attached to the network, schedule a
|
|
// connectivity report to the new leader.
|
|
//
|
|
if (NmpIsNetworkRegistered(network)) {
|
|
NmpScheduleNetworkConnectivityReport(network);
|
|
}
|
|
|
|
//
|
|
// If the local node is the (possibly new) leader, schedule
|
|
// a state update. We explicitly enable this timer here in case
|
|
// there are no active nodes attached to the network.
|
|
//
|
|
if (NmpLeaderNodeId == NmLocalNodeId) {
|
|
NmpStartNetworkStateRecalcTimer(
|
|
network,
|
|
NM_NET_STATE_RECALC_TIMEOUT_AFTER_REGROUP
|
|
);
|
|
}
|
|
}
|
|
|
|
return;
|
|
|
|
} // NmpUpdateNetworkConnectivityForDownNode
|
|
|
|
|
|
VOID
|
|
NmpFreeNetworkStateEnum(
|
|
PNM_NETWORK_STATE_ENUM NetworkStateEnum
|
|
)
|
|
{
|
|
PNM_NETWORK_STATE_INFO networkStateInfo;
|
|
DWORD i;
|
|
|
|
|
|
for (i=0; i<NetworkStateEnum->NetworkCount; i++) {
|
|
networkStateInfo = &(NetworkStateEnum->NetworkList[i]);
|
|
|
|
if (networkStateInfo->Id != NULL) {
|
|
MIDL_user_free(networkStateInfo->Id);
|
|
}
|
|
}
|
|
|
|
MIDL_user_free(NetworkStateEnum);
|
|
|
|
return;
|
|
|
|
} // NmpFreeNetworkStateEnum
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Database management routines
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
DWORD
|
|
NmpCreateNetworkDefinition(
|
|
IN PNM_NETWORK_INFO NetworkInfo,
|
|
IN HLOCALXSACTION Xaction
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates a new network definition in the cluster database.
|
|
|
|
Arguments:
|
|
|
|
NetworkInfo - A pointer to the information structure describing the
|
|
network to create.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
A Win32 error code otherwise.
|
|
|
|
--*/
|
|
{
|
|
DWORD status;
|
|
HDMKEY networkKey = NULL;
|
|
DWORD valueLength;
|
|
DWORD disposition;
|
|
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Creating database entry for network %1!ws!\n",
|
|
NetworkInfo->Id
|
|
);
|
|
|
|
networkKey = DmLocalCreateKey(
|
|
Xaction,
|
|
DmNetworksKey,
|
|
NetworkInfo->Id,
|
|
0,
|
|
KEY_WRITE,
|
|
NULL,
|
|
&disposition
|
|
);
|
|
|
|
if (networkKey == NULL) {
|
|
status = GetLastError();
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Failed to create network key, status %1!u!\n",
|
|
status
|
|
);
|
|
return(status);
|
|
}
|
|
|
|
CL_ASSERT(disposition == REG_CREATED_NEW_KEY);
|
|
|
|
//
|
|
// Write the name value for this network
|
|
//
|
|
valueLength = (wcslen(NetworkInfo->Name) + 1) * sizeof(WCHAR);
|
|
|
|
status = DmLocalSetValue(
|
|
Xaction,
|
|
networkKey,
|
|
CLUSREG_NAME_NET_NAME,
|
|
REG_SZ,
|
|
(CONST BYTE *) NetworkInfo->Name,
|
|
valueLength
|
|
);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Set of network name value failed, status %1!u!.\n",
|
|
status
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Write the description value for this network
|
|
//
|
|
valueLength = (wcslen(NetworkInfo->Description) + 1) * sizeof(WCHAR);
|
|
|
|
status = DmLocalSetValue(
|
|
Xaction,
|
|
networkKey,
|
|
CLUSREG_NAME_NET_DESC,
|
|
REG_SZ,
|
|
(CONST BYTE *) NetworkInfo->Description,
|
|
valueLength
|
|
);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Set of network description value failed, status %1!u!.\n",
|
|
status
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Write the role value for this network
|
|
//
|
|
status = DmLocalSetValue(
|
|
Xaction,
|
|
networkKey,
|
|
CLUSREG_NAME_NET_ROLE,
|
|
REG_DWORD,
|
|
(CONST BYTE *) &(NetworkInfo->Role),
|
|
sizeof(DWORD)
|
|
);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Set of network role value failed, status %1!u!.\n",
|
|
status
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Write the priority value for this network
|
|
//
|
|
status = DmLocalSetValue(
|
|
Xaction,
|
|
networkKey,
|
|
CLUSREG_NAME_NET_PRIORITY,
|
|
REG_DWORD,
|
|
(CONST BYTE *) &(NetworkInfo->Priority),
|
|
sizeof(DWORD)
|
|
);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Set of network priority value failed, status %1!u!.\n",
|
|
status
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Write the transport value for this network
|
|
//
|
|
valueLength = (wcslen(NetworkInfo->Transport) + 1) * sizeof(WCHAR);
|
|
|
|
status = DmLocalSetValue(
|
|
Xaction,
|
|
networkKey,
|
|
CLUSREG_NAME_NET_TRANSPORT,
|
|
REG_SZ,
|
|
(CONST BYTE *) NetworkInfo->Transport,
|
|
valueLength
|
|
);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Set of network transport value failed, status %1!u!.\n",
|
|
status
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Write the address value for this network
|
|
//
|
|
valueLength = (wcslen(NetworkInfo->Address) + 1) * sizeof(WCHAR);
|
|
|
|
status = DmLocalSetValue(
|
|
Xaction,
|
|
networkKey,
|
|
CLUSREG_NAME_NET_ADDRESS,
|
|
REG_SZ,
|
|
(CONST BYTE *) NetworkInfo->Address,
|
|
valueLength
|
|
);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Set of network address value failed, status %1!u!.\n",
|
|
status
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Write the address mask value for this network
|
|
//
|
|
valueLength = (wcslen(NetworkInfo->AddressMask) + 1) * sizeof(WCHAR);
|
|
|
|
status = DmLocalSetValue(
|
|
Xaction,
|
|
networkKey,
|
|
CLUSREG_NAME_NET_ADDRESS_MASK,
|
|
REG_SZ,
|
|
(CONST BYTE *) NetworkInfo->AddressMask,
|
|
valueLength
|
|
);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Set of network address mask value failed, status %1!u!.\n",
|
|
status
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
CL_ASSERT(status == ERROR_SUCCESS);
|
|
|
|
error_exit:
|
|
|
|
if (networkKey != NULL) {
|
|
DmCloseKey(networkKey);
|
|
}
|
|
|
|
return(status);
|
|
|
|
} // NmpCreateNetworkDefinition
|
|
|
|
|
|
DWORD
|
|
NmpSetNetworkNameDefinition(
|
|
IN PNM_NETWORK_INFO NetworkInfo,
|
|
IN HLOCALXSACTION Xaction
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Changes the network name in the local database
|
|
|
|
Arguments:
|
|
|
|
NetworkInfo - A pointer to the information structure describing the
|
|
network to create.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
A Win32 error code otherwise.
|
|
|
|
--*/
|
|
{
|
|
DWORD status;
|
|
HDMKEY networkKey = NULL;
|
|
DWORD disposition;
|
|
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Changing network name database entry for network %1!ws!\n",
|
|
NetworkInfo->Id
|
|
);
|
|
|
|
//
|
|
// Open the network's key.
|
|
//
|
|
networkKey = DmOpenKey(DmNetworksKey, NetworkInfo->Id, KEY_WRITE);
|
|
|
|
if (networkKey == NULL) {
|
|
status = GetLastError();
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Failed to open network key, status %1!u!\n",
|
|
status
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Write the name value for this network
|
|
//
|
|
|
|
status = DmLocalSetValue(
|
|
Xaction,
|
|
networkKey,
|
|
CLUSREG_NAME_NET_NAME,
|
|
REG_SZ,
|
|
(CONST BYTE *) NetworkInfo->Name,
|
|
NM_WCSLEN( NetworkInfo->Name )
|
|
);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Set of network name value failed, status %1!u!.\n",
|
|
status
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
CL_ASSERT(status == ERROR_SUCCESS);
|
|
|
|
error_exit:
|
|
|
|
if (networkKey != NULL) {
|
|
DmCloseKey(networkKey);
|
|
}
|
|
|
|
return(status);
|
|
|
|
} // NmpSetNetworkNameDefinition
|
|
|
|
|
|
DWORD
|
|
NmpGetNetworkDefinition(
|
|
IN LPWSTR NetworkId,
|
|
OUT PNM_NETWORK_INFO NetworkInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reads information about a defined cluster network from the cluster
|
|
database and fills in a structure describing it.
|
|
|
|
Arguments:
|
|
|
|
NetworkId - A pointer to a unicode string containing the ID of the
|
|
network to query.
|
|
|
|
NetworkInfo - A pointer to the network info structure to fill in.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if the routine succeeds.
|
|
A Win32 error code otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD status;
|
|
HDMKEY networkKey = NULL;
|
|
DWORD valueLength, valueSize;
|
|
DWORD i;
|
|
|
|
|
|
ZeroMemory(NetworkInfo, sizeof(NM_NETWORK_INFO));
|
|
|
|
//
|
|
// Open the network's key.
|
|
//
|
|
networkKey = DmOpenKey(DmNetworksKey, NetworkId, KEY_READ);
|
|
|
|
if (networkKey == NULL) {
|
|
status = GetLastError();
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Failed to open network key, status %1!u!\n",
|
|
status
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Copy the ID value.
|
|
//
|
|
NetworkInfo->Id = MIDL_user_allocate(NM_WCSLEN(NetworkId));
|
|
|
|
if (NetworkInfo->Id == NULL) {
|
|
status = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto error_exit;
|
|
}
|
|
|
|
wcscpy(NetworkInfo->Id, NetworkId);
|
|
|
|
//
|
|
// Read the network's name.
|
|
//
|
|
valueLength = 0;
|
|
|
|
status = NmpQueryString(
|
|
networkKey,
|
|
CLUSREG_NAME_NET_NAME,
|
|
REG_SZ,
|
|
&(NetworkInfo->Name),
|
|
&valueLength,
|
|
&valueSize
|
|
);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Query of name value failed for network %1!ws!, "
|
|
"status %2!u!.\n",
|
|
NetworkId,
|
|
status
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Read the description value.
|
|
//
|
|
valueLength = 0;
|
|
|
|
status = NmpQueryString(
|
|
networkKey,
|
|
CLUSREG_NAME_NET_DESC,
|
|
REG_SZ,
|
|
&(NetworkInfo->Description),
|
|
&valueLength,
|
|
&valueSize
|
|
);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Query of description value failed for network %1!ws!, "
|
|
"status %2!u!.\n",
|
|
NetworkId,
|
|
status
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Read the role value.
|
|
//
|
|
status = DmQueryDword(
|
|
networkKey,
|
|
CLUSREG_NAME_NET_ROLE,
|
|
&(NetworkInfo->Role),
|
|
NULL
|
|
);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Query of role value failed for network %1!ws!, "
|
|
"status %2!u!.\n",
|
|
NetworkId,
|
|
status
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Read the priority value.
|
|
//
|
|
status = DmQueryDword(
|
|
networkKey,
|
|
CLUSREG_NAME_NET_PRIORITY,
|
|
&(NetworkInfo->Priority),
|
|
NULL
|
|
);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Query of priority value failed for network %1!ws!, "
|
|
"status %2!u!.\n",
|
|
NetworkId,
|
|
status
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Read the address value.
|
|
//
|
|
valueLength = 0;
|
|
|
|
status = NmpQueryString(
|
|
networkKey,
|
|
CLUSREG_NAME_NET_ADDRESS,
|
|
REG_SZ,
|
|
&(NetworkInfo->Address),
|
|
&valueLength,
|
|
&valueSize
|
|
);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Query of address value failed for network %1!ws!, "
|
|
"status %2!u!.\n",
|
|
NetworkId,
|
|
status
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Read the address mask.
|
|
//
|
|
valueLength = 0;
|
|
|
|
status = NmpQueryString(
|
|
networkKey,
|
|
CLUSREG_NAME_NET_ADDRESS_MASK,
|
|
REG_SZ,
|
|
&(NetworkInfo->AddressMask),
|
|
&valueLength,
|
|
&valueSize
|
|
);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Query of address mask value failed for network %1!ws!, "
|
|
"status %2!u!.\n",
|
|
NetworkId,
|
|
status
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Read the transport name.
|
|
//
|
|
valueLength = 0;
|
|
|
|
status = NmpQueryString(
|
|
networkKey,
|
|
CLUSREG_NAME_NET_TRANSPORT,
|
|
REG_SZ,
|
|
&(NetworkInfo->Transport),
|
|
&valueLength,
|
|
&valueSize
|
|
);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Query of transport value failed for network %1!ws!, "
|
|
"status %2!u!.\n",
|
|
NetworkId,
|
|
status
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
CL_ASSERT(status == ERROR_SUCCESS);
|
|
|
|
error_exit:
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
ClNetFreeNetworkInfo(NetworkInfo);
|
|
}
|
|
|
|
if (networkKey != NULL) {
|
|
DmCloseKey(networkKey);
|
|
}
|
|
|
|
return(status);
|
|
|
|
} // NmpGetNetworkDefinition
|
|
|
|
|
|
DWORD
|
|
NmpEnumNetworkDefinitions(
|
|
OUT PNM_NETWORK_ENUM * NetworkEnum
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reads information about defined cluster networks from the cluster
|
|
database. and builds an enumeration structure to hold the information.
|
|
|
|
Arguments:
|
|
|
|
NetworkEnum - A pointer to the variable into which to place a pointer to
|
|
the allocated network enumeration.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if the routine succeeds.
|
|
A Win32 error code otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD status;
|
|
PNM_NETWORK_ENUM networkEnum = NULL;
|
|
PNM_NETWORK_INFO networkInfo;
|
|
WCHAR networkId[CS_NETWORK_ID_LENGTH + 1];
|
|
DWORD i;
|
|
DWORD valueLength;
|
|
DWORD numNetworks;
|
|
DWORD ignored;
|
|
FILETIME fileTime;
|
|
|
|
|
|
*NetworkEnum = NULL;
|
|
|
|
//
|
|
// First count the number of networks.
|
|
//
|
|
status = DmQueryInfoKey(
|
|
DmNetworksKey,
|
|
&numNetworks,
|
|
&ignored, // MaxSubKeyLen
|
|
&ignored, // Values
|
|
&ignored, // MaxValueNameLen
|
|
&ignored, // MaxValueLen
|
|
&ignored, // lpcbSecurityDescriptor
|
|
&fileTime
|
|
);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Failed to query Networks key information, status %1!u!\n",
|
|
status
|
|
);
|
|
return(status);
|
|
}
|
|
|
|
if (numNetworks == 0) {
|
|
valueLength = sizeof(NM_NETWORK_ENUM);
|
|
|
|
}
|
|
else {
|
|
valueLength = sizeof(NM_NETWORK_ENUM) +
|
|
(sizeof(NM_NETWORK_INFO) * (numNetworks-1));
|
|
}
|
|
|
|
valueLength = sizeof(NM_NETWORK_ENUM) +
|
|
(sizeof(NM_NETWORK_INFO) * (numNetworks-1));
|
|
|
|
networkEnum = MIDL_user_allocate(valueLength);
|
|
|
|
if (networkEnum == NULL) {
|
|
ClRtlLogPrint(LOG_CRITICAL, "[NM] Failed to allocate memory.\n");
|
|
return(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
|
|
ZeroMemory(networkEnum, valueLength);
|
|
|
|
for (i=0; i < numNetworks; i++) {
|
|
networkInfo = &(networkEnum->NetworkList[i]);
|
|
|
|
valueLength = sizeof(networkId);
|
|
|
|
status = DmEnumKey(
|
|
DmNetworksKey,
|
|
i,
|
|
&(networkId[0]),
|
|
&valueLength,
|
|
NULL
|
|
);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Failed to enumerate network key, status %1!u!\n",
|
|
status
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
status = NmpGetNetworkDefinition(networkId, networkInfo);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
goto error_exit;
|
|
}
|
|
|
|
networkEnum->NetworkCount++;
|
|
}
|
|
|
|
*NetworkEnum = networkEnum;
|
|
|
|
return(ERROR_SUCCESS);
|
|
|
|
|
|
error_exit:
|
|
|
|
if (networkEnum != NULL) {
|
|
ClNetFreeNetworkEnum(networkEnum);
|
|
}
|
|
|
|
return(status);
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Object management routines
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
DWORD
|
|
NmpCreateNetworkObjects(
|
|
IN PNM_NETWORK_ENUM NetworkEnum
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Processes a network information enumeration and creates network objects.
|
|
|
|
Arguments:
|
|
|
|
NetworkEnum - A pointer to a network information enumeration structure.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if the routine completes successfully.
|
|
A Win32 error code otherwise.
|
|
|
|
--*/
|
|
{
|
|
DWORD status = ERROR_SUCCESS;
|
|
PNM_NETWORK_INFO networkInfo;
|
|
PNM_NETWORK network;
|
|
DWORD i;
|
|
|
|
|
|
for (i=0; i < NetworkEnum->NetworkCount; i++) {
|
|
networkInfo = &(NetworkEnum->NetworkList[i]);
|
|
|
|
network = NmpCreateNetworkObject(networkInfo);
|
|
|
|
if (network == NULL) {
|
|
status = GetLastError();
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Failed to create network %1!ws!, status %2!u!.\n",
|
|
networkInfo->Id,
|
|
status
|
|
);
|
|
break;
|
|
}
|
|
else {
|
|
OmDereferenceObject(network);
|
|
}
|
|
}
|
|
|
|
return(status);
|
|
|
|
} // NmpCreateNetworkObjects
|
|
|
|
|
|
PNM_NETWORK
|
|
NmpCreateNetworkObject(
|
|
IN PNM_NETWORK_INFO NetworkInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Instantiates a cluster network object.
|
|
|
|
Arguments:
|
|
|
|
NetworkInfo - A pointer to a structure describing the network to create.
|
|
|
|
Return Value:
|
|
|
|
A pointer to the new network object on success.
|
|
NULL on failure.
|
|
|
|
--*/
|
|
{
|
|
DWORD status;
|
|
PNM_NETWORK network = NULL;
|
|
BOOL created = FALSE;
|
|
DWORD i;
|
|
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Creating object for network %1!ws! (%2!ws!).\n",
|
|
NetworkInfo->Id,
|
|
NetworkInfo->Name
|
|
);
|
|
|
|
//
|
|
// Make sure that an object with the same name doesn't already exist.
|
|
//
|
|
network = OmReferenceObjectById(ObjectTypeNetwork, NetworkInfo->Id);
|
|
|
|
if (network != NULL) {
|
|
OmDereferenceObject(network);
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] A network object named %1!ws! already exists. Cannot "
|
|
"create a new network with the same name.\n",
|
|
NetworkInfo->Id
|
|
);
|
|
SetLastError(ERROR_OBJECT_ALREADY_EXISTS);
|
|
return(NULL);
|
|
}
|
|
|
|
//
|
|
// Ensure that the IP (sub)network is unique in the cluster. Two
|
|
// nodes can race to create a new network in some cases.
|
|
//
|
|
network = NmpReferenceNetworkByAddress(
|
|
NetworkInfo->Address
|
|
);
|
|
|
|
if (network != NULL) {
|
|
OmDereferenceObject(network);
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] A network object already exists for IP network %1!ws!. "
|
|
"Cannot create a new network with the same address.\n",
|
|
NetworkInfo->Address
|
|
);
|
|
SetLastError(ERROR_OBJECT_ALREADY_EXISTS);
|
|
return(NULL);
|
|
}
|
|
|
|
//
|
|
// Create a network object.
|
|
//
|
|
network = OmCreateObject(
|
|
ObjectTypeNetwork,
|
|
NetworkInfo->Id,
|
|
NetworkInfo->Name,
|
|
&created
|
|
);
|
|
|
|
if (network == NULL) {
|
|
status = GetLastError();
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Failed to create object for network %1!ws! (%2!ws!), status %3!u!\n",
|
|
NetworkInfo->Id,
|
|
NetworkInfo->Name,
|
|
status
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
CL_ASSERT(created == TRUE);
|
|
|
|
//
|
|
// Initialize the network object
|
|
//
|
|
ZeroMemory(network, sizeof(NM_NETWORK));
|
|
|
|
network->ShortId = InterlockedIncrement(&NmpNextNetworkShortId);
|
|
network->State = ClusterNetworkUnavailable;
|
|
network->Role = NetworkInfo->Role;
|
|
network->Priority = NetworkInfo->Priority;
|
|
network->Description = NetworkInfo->Description;
|
|
NetworkInfo->Description = NULL;
|
|
network->Transport = NetworkInfo->Transport;
|
|
NetworkInfo->Transport = NULL;
|
|
network->Address = NetworkInfo->Address;
|
|
NetworkInfo->Address = NULL;
|
|
network->AddressMask = NetworkInfo->AddressMask;
|
|
NetworkInfo->AddressMask = NULL;
|
|
InitializeListHead(&(network->InterfaceList));
|
|
InitializeListHead(&(network->McastAddressReleaseList));
|
|
|
|
//
|
|
// Allocate an initial connectivity vector.
|
|
// Note that we get one vector entry as part of
|
|
// the NM_CONNECTIVITY_VECTOR structure.
|
|
//
|
|
#define NM_INITIAL_VECTOR_SIZE 2
|
|
|
|
network->ConnectivityVector = LocalAlloc(
|
|
LMEM_FIXED,
|
|
( sizeof(NM_CONNECTIVITY_VECTOR) +
|
|
( ((NM_INITIAL_VECTOR_SIZE) - 1) *
|
|
sizeof(NM_STATE_ENTRY)
|
|
)
|
|
));
|
|
|
|
if (network->ConnectivityVector == NULL) {
|
|
status = ERROR_NOT_ENOUGH_MEMORY;
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Failed to allocate memory for connectivity vector\n"
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
network->ConnectivityVector->EntryCount = NM_INITIAL_VECTOR_SIZE;
|
|
|
|
FillMemory(
|
|
&(network->ConnectivityVector->Data[0]),
|
|
NM_INITIAL_VECTOR_SIZE * sizeof(NM_STATE_ENTRY),
|
|
(UCHAR) ClusterNetInterfaceStateUnknown
|
|
);
|
|
|
|
//
|
|
// Allocate a state work vector
|
|
//
|
|
network->StateWorkVector = LocalAlloc(
|
|
LMEM_FIXED,
|
|
(NM_INITIAL_VECTOR_SIZE) *
|
|
sizeof(NM_STATE_WORK_ENTRY)
|
|
);
|
|
|
|
if (network->StateWorkVector == NULL) {
|
|
status = ERROR_NOT_ENOUGH_MEMORY;
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Failed to allocate memory for state work vector\n"
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Initialize the state work vector
|
|
//
|
|
for (i=0; i<NM_INITIAL_VECTOR_SIZE; i++) {
|
|
network->StateWorkVector[i].State =
|
|
(NM_STATE_ENTRY) ClusterNetInterfaceStateUnknown;
|
|
}
|
|
|
|
//
|
|
// Put a reference on the object for the caller.
|
|
//
|
|
OmReferenceObject(network);
|
|
|
|
NmpAcquireLock();
|
|
|
|
//
|
|
// Allocate the corresponding connectivity matrix
|
|
//
|
|
network->ConnectivityMatrix =
|
|
LocalAlloc(
|
|
LMEM_FIXED,
|
|
NM_SIZEOF_CONNECTIVITY_MATRIX(NM_INITIAL_VECTOR_SIZE)
|
|
);
|
|
|
|
if (network->ConnectivityMatrix == NULL) {
|
|
status = ERROR_NOT_ENOUGH_MEMORY;
|
|
NmpReleaseLock();
|
|
OmDereferenceObject(network);
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Failed to allocate memory for connectivity matrix\n"
|
|
);
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Initialize the matrix
|
|
//
|
|
FillMemory(
|
|
network->ConnectivityMatrix,
|
|
NM_SIZEOF_CONNECTIVITY_MATRIX(NM_INITIAL_VECTOR_SIZE),
|
|
(UCHAR) ClusterNetInterfaceStateUnknown
|
|
);
|
|
|
|
//
|
|
// Make the network object available.
|
|
//
|
|
InsertTailList(&NmpNetworkList, &(network->Linkage));
|
|
NmpNetworkCount++;
|
|
|
|
if (NmpIsNetworkForInternalUse(network)) {
|
|
NmpInsertInternalNetwork(network);
|
|
NmpInternalNetworkCount++;
|
|
}
|
|
|
|
if (NmpIsNetworkForClientAccess(network)) {
|
|
NmpClientNetworkCount++;
|
|
}
|
|
|
|
network->Flags |= NM_FLAG_OM_INSERTED;
|
|
OmInsertObject(network);
|
|
|
|
NmpReleaseLock();
|
|
|
|
return(network);
|
|
|
|
error_exit:
|
|
|
|
if (network != NULL) {
|
|
NmpAcquireLock();
|
|
NmpDeleteNetworkObject(network, FALSE);
|
|
NmpReleaseLock();
|
|
}
|
|
|
|
SetLastError(status);
|
|
|
|
return(NULL);
|
|
|
|
} // NmpCreateNetworkObject
|
|
|
|
|
|
|
|
DWORD
|
|
NmpGetNetworkObjectInfo(
|
|
IN PNM_NETWORK Network,
|
|
OUT PNM_NETWORK_INFO NetworkInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reads information about a defined cluster network from the
|
|
network object and fills in a structure describing it.
|
|
|
|
Arguments:
|
|
|
|
Network - A pointer to the network object to query.
|
|
|
|
NetworkInfo - A pointer to the structure to fill in with network
|
|
information.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if the routine succeeds.
|
|
A Win32 error code otherwise.
|
|
|
|
Notes:
|
|
|
|
Called with NmpLock held.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD status = ERROR_NOT_ENOUGH_MEMORY;
|
|
LPWSTR tmpString = NULL;
|
|
LPWSTR networkId = (LPWSTR) OmObjectId(Network);
|
|
LPWSTR networkName = (LPWSTR) OmObjectName(Network);
|
|
|
|
|
|
ZeroMemory(NetworkInfo, sizeof(NM_NETWORK_INFO));
|
|
|
|
tmpString = MIDL_user_allocate(NM_WCSLEN(networkId));
|
|
if (tmpString == NULL) {
|
|
goto error_exit;
|
|
}
|
|
wcscpy(tmpString, networkId);
|
|
NetworkInfo->Id = tmpString;
|
|
|
|
tmpString = MIDL_user_allocate(NM_WCSLEN(networkName));
|
|
if (tmpString == NULL) {
|
|
goto error_exit;
|
|
}
|
|
wcscpy(tmpString, networkName);
|
|
NetworkInfo->Name = tmpString;
|
|
|
|
tmpString = MIDL_user_allocate(NM_WCSLEN(Network->Description));
|
|
if (tmpString == NULL) {
|
|
goto error_exit;
|
|
}
|
|
wcscpy(tmpString, Network->Description);
|
|
NetworkInfo->Description = tmpString;
|
|
|
|
NetworkInfo->Role = Network->Role;
|
|
NetworkInfo->Priority = Network->Priority;
|
|
|
|
tmpString = MIDL_user_allocate(NM_WCSLEN(Network->Transport));
|
|
if (tmpString == NULL) {
|
|
goto error_exit;
|
|
}
|
|
wcscpy(tmpString, Network->Transport);
|
|
NetworkInfo->Transport = tmpString;
|
|
|
|
tmpString = MIDL_user_allocate(NM_WCSLEN(Network->Address));
|
|
if (tmpString == NULL) {
|
|
goto error_exit;
|
|
}
|
|
wcscpy(tmpString, Network->Address);
|
|
NetworkInfo->Address = tmpString;
|
|
|
|
tmpString = MIDL_user_allocate(NM_WCSLEN(Network->AddressMask));
|
|
if (tmpString == NULL) {
|
|
goto error_exit;
|
|
}
|
|
wcscpy(tmpString, Network->AddressMask);
|
|
NetworkInfo->AddressMask = tmpString;
|
|
|
|
return(ERROR_SUCCESS);
|
|
|
|
error_exit:
|
|
|
|
ClNetFreeNetworkInfo(NetworkInfo);
|
|
|
|
return(status);
|
|
|
|
} // NmpGetNetworkObjectInfo
|
|
|
|
|
|
VOID
|
|
NmpDeleteNetworkObject(
|
|
IN PNM_NETWORK Network,
|
|
IN BOOLEAN IssueEvent
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Deletes a cluster network object.
|
|
|
|
Arguments:
|
|
|
|
Network - A pointer to the network object to delete.
|
|
|
|
IssueEvent - TRUE if a NETWORK_DELETED event should be issued when this
|
|
object is created. FALSE otherwise.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Notes:
|
|
|
|
Called with NM global lock held.
|
|
|
|
--*/
|
|
{
|
|
DWORD status;
|
|
PLIST_ENTRY entry;
|
|
LPWSTR networkId = (LPWSTR) OmObjectId(Network);
|
|
BOOLEAN wasInternalNetwork = FALSE;
|
|
|
|
|
|
if (NM_DELETE_PENDING(Network)) {
|
|
CL_ASSERT(!NM_OM_INSERTED(Network));
|
|
return;
|
|
}
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Deleting object for network %1!ws!.\n",
|
|
networkId
|
|
);
|
|
|
|
CL_ASSERT(IsListEmpty(&(Network->InterfaceList)));
|
|
|
|
Network->Flags |= NM_FLAG_DELETE_PENDING;
|
|
|
|
//
|
|
// Remove from the object lists
|
|
//
|
|
if (NM_OM_INSERTED(Network)) {
|
|
status = OmRemoveObject(Network);
|
|
CL_ASSERT(status == ERROR_SUCCESS);
|
|
|
|
Network->Flags &= ~NM_FLAG_OM_INSERTED;
|
|
|
|
RemoveEntryList(&(Network->Linkage));
|
|
CL_ASSERT(NmpNetworkCount > 0);
|
|
NmpNetworkCount--;
|
|
|
|
if (NmpIsNetworkForInternalUse(Network)) {
|
|
RemoveEntryList(&(Network->InternalLinkage));
|
|
CL_ASSERT(NmpInternalNetworkCount > 0);
|
|
NmpInternalNetworkCount--;
|
|
wasInternalNetwork = TRUE;
|
|
}
|
|
|
|
if (NmpIsNetworkForClientAccess(Network)) {
|
|
CL_ASSERT(NmpClientNetworkCount > 0);
|
|
NmpClientNetworkCount--;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Place the object on the deleted list
|
|
//
|
|
#if DBG
|
|
{
|
|
PLIST_ENTRY entry;
|
|
|
|
for ( entry = NmpDeletedNetworkList.Flink;
|
|
entry != &NmpDeletedNetworkList;
|
|
entry = entry->Flink
|
|
)
|
|
{
|
|
if (entry == &(Network->Linkage)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
CL_ASSERT(entry != &(Network->Linkage));
|
|
}
|
|
#endif DBG
|
|
|
|
InsertTailList(&NmpDeletedNetworkList, &(Network->Linkage));
|
|
|
|
if (NmpIsNetworkEnabledForUse(Network)) {
|
|
//
|
|
// Deregister the network from the cluster transport
|
|
//
|
|
NmpDeregisterNetwork(Network);
|
|
}
|
|
|
|
//
|
|
// Issue an event if needed
|
|
//
|
|
if (IssueEvent) {
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Issuing network deleted event for network %1!ws!.\n",
|
|
networkId
|
|
);
|
|
|
|
ClusterEvent(CLUSTER_EVENT_NETWORK_DELETED, Network);
|
|
|
|
//
|
|
// Issue a cluster property change event if this network was
|
|
// used for internal communication. The network priority list
|
|
// was changed.
|
|
//
|
|
if (wasInternalNetwork) {
|
|
NmpIssueClusterPropertyChangeEvent();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Remove the initial reference so the object can be destroyed.
|
|
//
|
|
OmDereferenceObject(Network);
|
|
|
|
return;
|
|
|
|
} // NmpDeleteNetworkObject
|
|
|
|
|
|
BOOL
|
|
NmpDestroyNetworkObject(
|
|
PNM_NETWORK Network
|
|
)
|
|
{
|
|
DWORD status;
|
|
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] destroying object for network %1!ws!\n",
|
|
OmObjectId(Network)
|
|
);
|
|
|
|
CL_ASSERT(NM_DELETE_PENDING(Network));
|
|
CL_ASSERT(!NM_OM_INSERTED(Network));
|
|
CL_ASSERT(Network->InterfaceCount == 0);
|
|
|
|
//
|
|
// Remove the network from the deleted list
|
|
//
|
|
#if DBG
|
|
{
|
|
PLIST_ENTRY entry;
|
|
|
|
for ( entry = NmpDeletedNetworkList.Flink;
|
|
entry != &NmpDeletedNetworkList;
|
|
entry = entry->Flink
|
|
)
|
|
{
|
|
if (entry == &(Network->Linkage)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
CL_ASSERT(entry == &(Network->Linkage));
|
|
}
|
|
#endif DBG
|
|
|
|
RemoveEntryList(&(Network->Linkage));
|
|
|
|
NM_FREE_OBJECT_FIELD(Network, Description);
|
|
NM_FREE_OBJECT_FIELD(Network, Transport);
|
|
NM_FREE_OBJECT_FIELD(Network, Address);
|
|
NM_FREE_OBJECT_FIELD(Network, AddressMask);
|
|
|
|
if (Network->ConnectivityVector != NULL) {
|
|
LocalFree(Network->ConnectivityVector);
|
|
Network->ConnectivityVector = NULL;
|
|
}
|
|
|
|
if (Network->StateWorkVector != NULL) {
|
|
LocalFree(Network->StateWorkVector);
|
|
Network->StateWorkVector = NULL;
|
|
}
|
|
|
|
if (Network->ConnectivityMatrix != NULL) {
|
|
LocalFree(Network->ConnectivityMatrix);
|
|
Network->ConnectivityMatrix = NULL;
|
|
}
|
|
|
|
NM_MIDL_FREE_OBJECT_FIELD(Network, MulticastAddress);
|
|
NM_MIDL_FREE_OBJECT_FIELD(Network, MulticastKey);
|
|
NM_MIDL_FREE_OBJECT_FIELD(Network, MulticastKeySalt);
|
|
NM_MIDL_FREE_OBJECT_FIELD(Network, MulticastLeaseServer);
|
|
NM_MIDL_FREE_OBJECT_FIELD(Network, MulticastLeaseRequestId.ClientUID);
|
|
|
|
NmpFreeMulticastAddressReleaseList(Network);
|
|
|
|
return(TRUE);
|
|
|
|
} // NmpDestroyNetworkObject
|
|
|
|
|
|
DWORD
|
|
NmpEnumNetworkObjects(
|
|
OUT PNM_NETWORK_ENUM * NetworkEnum
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reads information about defined cluster networks from the cluster
|
|
objects and builds an enumeration structure to hold the information.
|
|
|
|
Arguments:
|
|
|
|
NetworkEnum - A pointer to the variable into which to place a pointer to
|
|
the allocated network enumeration.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if the routine succeeds.
|
|
A Win32 error code otherwise.
|
|
|
|
Notes:
|
|
|
|
Called with the NmpLock held.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD status = ERROR_SUCCESS;
|
|
PNM_NETWORK_ENUM networkEnum = NULL;
|
|
DWORD i;
|
|
DWORD valueLength;
|
|
PLIST_ENTRY entry;
|
|
PNM_NETWORK network;
|
|
|
|
|
|
*NetworkEnum = NULL;
|
|
|
|
if (NmpNetworkCount == 0) {
|
|
valueLength = sizeof(NM_NETWORK_ENUM);
|
|
|
|
}
|
|
else {
|
|
valueLength = sizeof(NM_NETWORK_ENUM) +
|
|
(sizeof(NM_NETWORK_INFO) * (NmpNetworkCount - 1));
|
|
}
|
|
|
|
networkEnum = MIDL_user_allocate(valueLength);
|
|
|
|
if (networkEnum == NULL) {
|
|
return(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
|
|
ZeroMemory(networkEnum, valueLength);
|
|
|
|
for (entry = NmpNetworkList.Flink, i=0;
|
|
entry != &NmpNetworkList;
|
|
entry = entry->Flink, i++
|
|
)
|
|
{
|
|
network = CONTAINING_RECORD(entry, NM_NETWORK, Linkage);
|
|
|
|
status = NmpGetNetworkObjectInfo(
|
|
network,
|
|
&(networkEnum->NetworkList[i])
|
|
);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
ClNetFreeNetworkEnum(networkEnum);
|
|
return(status);
|
|
}
|
|
}
|
|
|
|
networkEnum->NetworkCount = NmpNetworkCount;
|
|
*NetworkEnum = networkEnum;
|
|
networkEnum = NULL;
|
|
|
|
return(ERROR_SUCCESS);
|
|
|
|
} // NmpEnumNetworkObjects
|
|
|
|
|
|
DWORD
|
|
NmpEnumNetworkObjectStates(
|
|
OUT PNM_NETWORK_STATE_ENUM * NetworkStateEnum
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reads state information for all defined cluster networks
|
|
and fills in an enumeration structure.
|
|
|
|
Arguments:
|
|
|
|
NetworkStateEnum - A pointer to the variable into which to place a
|
|
pointer to the allocated interface enumeration.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if the routine succeeds.
|
|
A Win32 error code otherwise.
|
|
|
|
Notes:
|
|
|
|
Called with the NmpLock held.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD status = ERROR_SUCCESS;
|
|
PNM_NETWORK_STATE_ENUM networkStateEnum = NULL;
|
|
PNM_NETWORK_STATE_INFO networkStateInfo;
|
|
DWORD i;
|
|
DWORD valueLength;
|
|
PLIST_ENTRY entry;
|
|
PNM_NETWORK network;
|
|
LPWSTR networkId;
|
|
|
|
|
|
*NetworkStateEnum = NULL;
|
|
|
|
if (NmpNetworkCount == 0) {
|
|
valueLength = sizeof(NM_NETWORK_STATE_ENUM);
|
|
}
|
|
else {
|
|
valueLength =
|
|
sizeof(NM_NETWORK_STATE_ENUM) +
|
|
(sizeof(NM_NETWORK_STATE_INFO) * (NmpNetworkCount - 1));
|
|
}
|
|
|
|
networkStateEnum = MIDL_user_allocate(valueLength);
|
|
|
|
if (networkStateEnum == NULL) {
|
|
return(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
|
|
ZeroMemory(networkStateEnum, valueLength);
|
|
|
|
for (entry = NmpNetworkList.Flink, i=0;
|
|
entry != &NmpNetworkList;
|
|
entry = entry->Flink, i++
|
|
)
|
|
{
|
|
network = CONTAINING_RECORD(entry, NM_NETWORK, Linkage);
|
|
networkId = (LPWSTR) OmObjectId(network);
|
|
networkStateInfo = &(networkStateEnum->NetworkList[i]);
|
|
|
|
networkStateInfo->State = network->State;
|
|
|
|
networkStateInfo->Id = MIDL_user_allocate(NM_WCSLEN(networkId));
|
|
|
|
if (networkStateInfo->Id == NULL) {
|
|
NmpFreeNetworkStateEnum(networkStateEnum);
|
|
return(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
|
|
lstrcpyW(networkStateInfo->Id, networkId);
|
|
}
|
|
|
|
networkStateEnum->NetworkCount = NmpNetworkCount;
|
|
*NetworkStateEnum = networkStateEnum;
|
|
|
|
return(ERROR_SUCCESS);
|
|
|
|
} // NmpEnumNetworkObjectStates
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Miscellaneous routines
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
DWORD
|
|
NmpRegisterNetwork(
|
|
IN PNM_NETWORK Network,
|
|
IN BOOLEAN RetryOnFailure
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Registers a network and the associated interfaces with the
|
|
cluster transport and brings the network online.
|
|
|
|
Arguments:
|
|
|
|
Network - A pointer to the network to register.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
A Win32 error code otherwise.
|
|
|
|
Notes:
|
|
|
|
Called with the NmpLock held.
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY entry;
|
|
PNM_INTERFACE netInterface;
|
|
DWORD status = ERROR_SUCCESS;
|
|
DWORD tempStatus;
|
|
PVOID tdiAddress = NULL;
|
|
ULONG tdiAddressLength = 0;
|
|
LPWSTR networkId = (LPWSTR) OmObjectId(Network);
|
|
PVOID tdiAddressInfo = NULL;
|
|
ULONG tdiAddressInfoLength = 0;
|
|
DWORD responseLength;
|
|
PNM_INTERFACE localInterface = Network->LocalInterface;
|
|
BOOLEAN restricted = FALSE;
|
|
BOOLEAN registered = FALSE;
|
|
|
|
|
|
if (Network->LocalInterface != NULL) {
|
|
if (!NmpIsNetworkRegistered(Network)) {
|
|
//
|
|
// Register the network
|
|
//
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Registering network %1!ws! (%2!ws!) with cluster "
|
|
"transport.\n",
|
|
networkId,
|
|
OmObjectName(Network)
|
|
);
|
|
|
|
if (!NmpIsNetworkForInternalUse(Network)) {
|
|
restricted = TRUE;
|
|
}
|
|
|
|
status = ClusnetRegisterNetwork(
|
|
NmClusnetHandle,
|
|
Network->ShortId,
|
|
Network->Priority,
|
|
restricted
|
|
);
|
|
|
|
if (status == ERROR_SUCCESS) {
|
|
registered = TRUE;
|
|
|
|
//
|
|
// Bring the network online.
|
|
//
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Bringing network %1!ws! online.\n",
|
|
networkId
|
|
);
|
|
|
|
status = ClRtlBuildTcpipTdiAddress(
|
|
localInterface->Address,
|
|
localInterface->ClusnetEndpoint,
|
|
&tdiAddress,
|
|
&tdiAddressLength
|
|
);
|
|
|
|
if (status == ERROR_SUCCESS) {
|
|
ClRtlQueryTcpipInformation(
|
|
NULL,
|
|
NULL,
|
|
&tdiAddressInfoLength
|
|
);
|
|
|
|
tdiAddressInfo = LocalAlloc(
|
|
LMEM_FIXED,
|
|
tdiAddressInfoLength
|
|
);
|
|
|
|
if (tdiAddressInfo != NULL) {
|
|
responseLength = tdiAddressInfoLength;
|
|
|
|
status = ClusnetOnlineNetwork(
|
|
NmClusnetHandle,
|
|
Network->ShortId,
|
|
L"\\Device\\Udp",
|
|
tdiAddress,
|
|
tdiAddressLength,
|
|
localInterface->AdapterId,
|
|
tdiAddressInfo,
|
|
&responseLength
|
|
);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Cluster transport failed to bring "
|
|
"network %1!ws! online, status %2!u!.\n",
|
|
networkId,
|
|
status
|
|
);
|
|
}
|
|
else {
|
|
CL_ASSERT(responseLength == tdiAddressInfoLength);
|
|
}
|
|
|
|
LocalFree(tdiAddressInfo);
|
|
}
|
|
else {
|
|
status = ERROR_NOT_ENOUGH_MEMORY;
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NM] Failed to allocate memory to register "
|
|
"network %1!ws! with cluster transport.\n",
|
|
networkId
|
|
);
|
|
}
|
|
|
|
LocalFree(tdiAddress);
|
|
}
|
|
else {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Failed to build address to register "
|
|
"network %1!ws! withh cluster transport, "
|
|
"status %2!u!.\n",
|
|
networkId,
|
|
status
|
|
);
|
|
}
|
|
}
|
|
else {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[NM] Failed to register network %1!ws! with cluster "
|
|
"transport, status %2!u!.\n",
|
|
networkId,
|
|
status
|
|
);
|
|
}
|
|
|
|
if (status == ERROR_SUCCESS) {
|
|
Network->Flags |= NM_FLAG_NET_REGISTERED;
|
|
Network->RegistrationRetryTimeout = 0;
|
|
}
|
|
else {
|
|
WCHAR string[16];
|
|
|
|
wsprintfW(&(string[0]), L"%u", status);
|
|
|
|
CsLogEvent2(
|
|
LOG_UNUSUAL,
|
|
NM_EVENT_REGISTER_NETWORK_FAILED,
|
|
OmObjectName(Network),
|
|
string
|
|
);
|
|
|
|
if (registered) {
|
|
NmpDeregisterNetwork(Network);
|
|
}
|
|
|
|
//
|
|
// Retry if the error is transient.
|
|
//
|
|
if ( RetryOnFailure &&
|
|
( (status == ERROR_INVALID_NETNAME) ||
|
|
(status == ERROR_NOT_ENOUGH_MEMORY) ||
|
|
(status == ERROR_NO_SYSTEM_RESOURCES)
|
|
)
|
|
)
|
|
{
|
|
NmpStartNetworkRegistrationRetryTimer(Network);
|
|
|
|
status = ERROR_SUCCESS;
|
|
}
|
|
|
|
return(status);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Register the network's interfaces.
|
|
//
|
|
for (entry = Network->InterfaceList.Flink;
|
|
entry != &(Network->InterfaceList);
|
|
entry = entry->Flink
|
|
)
|
|
{
|
|
netInterface = CONTAINING_RECORD(
|
|
entry,
|
|
NM_INTERFACE,
|
|
NetworkLinkage
|
|
);
|
|
|
|
if (!NmpIsInterfaceRegistered(netInterface)) {
|
|
tempStatus = NmpRegisterInterface(
|
|
netInterface,
|
|
RetryOnFailure
|
|
);
|
|
|
|
if (tempStatus != ERROR_SUCCESS) {
|
|
status = tempStatus;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
return(status);
|
|
|
|
} // NmpRegisterNetwork
|
|
|
|
|
|
VOID
|
|
NmpDeregisterNetwork(
|
|
IN PNM_NETWORK Network
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Deregisters a network and the associated interfaces from the
|
|
cluster transport.
|
|
|
|
Arguments:
|
|
|
|
Network - A pointer to the network to deregister.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Notes:
|
|
|
|
Called with the NmpLock held.
|
|
|
|
--*/
|
|
{
|
|
DWORD status;
|
|
PNM_INTERFACE netInterface;
|
|
PLIST_ENTRY entry;
|
|
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Deregistering network %1!ws! (%2!ws!) from cluster transport.\n",
|
|
OmObjectId(Network),
|
|
OmObjectName(Network)
|
|
);
|
|
|
|
status = ClusnetDeregisterNetwork(
|
|
NmClusnetHandle,
|
|
Network->ShortId
|
|
);
|
|
|
|
CL_ASSERT(
|
|
(status == ERROR_SUCCESS) ||
|
|
(status == ERROR_CLUSTER_NETWORK_NOT_FOUND)
|
|
);
|
|
|
|
//
|
|
// Mark all of the network's interfaces as deregistered.
|
|
//
|
|
for (entry = Network->InterfaceList.Flink;
|
|
entry != &(Network->InterfaceList);
|
|
entry = entry->Flink
|
|
)
|
|
{
|
|
netInterface = CONTAINING_RECORD(entry, NM_INTERFACE, NetworkLinkage);
|
|
|
|
netInterface->Flags &= ~NM_FLAG_IF_REGISTERED;
|
|
}
|
|
|
|
//
|
|
// Mark the network as deregistered
|
|
//
|
|
Network->Flags &= ~NM_FLAG_NET_REGISTERED;
|
|
|
|
return;
|
|
|
|
} // NmpDeregisterNetwork
|
|
|
|
|
|
VOID
|
|
NmpInsertInternalNetwork(
|
|
PNM_NETWORK Network
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Inserts a network into internal networks list based on its priority.
|
|
|
|
Arguments:
|
|
|
|
Network - A pointer to the network object to be inserted.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Notes:
|
|
|
|
Called with the NmpLock held.
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY entry;
|
|
PNM_NETWORK network;
|
|
|
|
|
|
//
|
|
// Maintain internal networks in highest to lowest
|
|
// (numerically lowest to highest) priority order.
|
|
//
|
|
for (entry = NmpInternalNetworkList.Flink;
|
|
entry != &NmpInternalNetworkList;
|
|
entry = entry->Flink
|
|
)
|
|
{
|
|
network = CONTAINING_RECORD(entry, NM_NETWORK, InternalLinkage);
|
|
|
|
if (Network->Priority < network->Priority) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Insert the network in front of this entry.
|
|
//
|
|
InsertTailList(entry, &(Network->InternalLinkage));
|
|
|
|
return;
|
|
|
|
} // NmpInsertNetwork
|
|
|
|
|
|
DWORD
|
|
NmpValidateNetworkRoleChange(
|
|
PNM_NETWORK Network,
|
|
CLUSTER_NETWORK_ROLE NewRole
|
|
)
|
|
{
|
|
if ( !(NewRole & ClusterNetworkRoleInternalUse) &&
|
|
NmpIsNetworkForInternalUse(Network)
|
|
)
|
|
{
|
|
//
|
|
// This change eliminates an internal network. This is only
|
|
// legal if we would still have at least one internal network
|
|
// between all active nodes.
|
|
//
|
|
if ((NmpInternalNetworkCount < 2) || !NmpVerifyConnectivity(Network)) {
|
|
return(ERROR_CLUSTER_LAST_INTERNAL_NETWORK);
|
|
}
|
|
}
|
|
|
|
if ( ( !(NewRole & ClusterNetworkRoleClientAccess) )
|
|
&&
|
|
NmpIsNetworkForClientAccess(Network)
|
|
)
|
|
{
|
|
BOOL hasDependents;
|
|
|
|
//
|
|
// This change eliminates a public network. This is only
|
|
// legal if there are no dependencies (IP address resources) on
|
|
// the network.
|
|
//
|
|
NmpReleaseLock();
|
|
|
|
hasDependents = FmCheckNetworkDependency(OmObjectId(Network));
|
|
|
|
NmpAcquireLock();
|
|
|
|
if (hasDependents) {
|
|
return(ERROR_CLUSTER_NETWORK_HAS_DEPENDENTS);
|
|
}
|
|
}
|
|
|
|
return(ERROR_SUCCESS);
|
|
|
|
} // NmpValidateNetworkRoleChange
|
|
|
|
|
|
BOOLEAN
|
|
NmpVerifyNodeConnectivity(
|
|
PNM_NODE Node1,
|
|
PNM_NODE Node2,
|
|
PNM_NETWORK ExcludedNetwork
|
|
)
|
|
{
|
|
PLIST_ENTRY ifEntry1, ifEntry2;
|
|
PNM_NETWORK network;
|
|
PNM_INTERFACE interface1, interface2;
|
|
|
|
|
|
for (ifEntry1 = Node1->InterfaceList.Flink;
|
|
ifEntry1 != &(Node1->InterfaceList);
|
|
ifEntry1 = ifEntry1->Flink
|
|
)
|
|
{
|
|
interface1 = CONTAINING_RECORD(
|
|
ifEntry1,
|
|
NM_INTERFACE,
|
|
NodeLinkage
|
|
);
|
|
|
|
network = interface1->Network;
|
|
|
|
if ( (network != ExcludedNetwork) &&
|
|
NmpIsNetworkForInternalUse(network)
|
|
)
|
|
{
|
|
for (ifEntry2 = Node2->InterfaceList.Flink;
|
|
ifEntry2 != &(Node2->InterfaceList);
|
|
ifEntry2 = ifEntry2->Flink
|
|
)
|
|
{
|
|
interface2 = CONTAINING_RECORD(
|
|
ifEntry2,
|
|
NM_INTERFACE,
|
|
NodeLinkage
|
|
);
|
|
|
|
if (interface2->Network == interface1->Network) {
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] nodes %1!u! & %2!u! are connected over "
|
|
"network %3!ws!\n",
|
|
Node1->NodeId,
|
|
Node2->NodeId,
|
|
OmObjectId(interface1->Network)
|
|
);
|
|
return(TRUE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Nodes %1!u! & %2!u! are not connected over any internal "
|
|
"networks\n",
|
|
Node1->NodeId,
|
|
Node2->NodeId
|
|
);
|
|
|
|
return(FALSE);
|
|
|
|
} // NmpVerifyNodeConnectivity
|
|
|
|
|
|
BOOLEAN
|
|
NmpVerifyConnectivity(
|
|
PNM_NETWORK ExcludedNetwork
|
|
)
|
|
{
|
|
PLIST_ENTRY node1Entry, node2Entry;
|
|
PNM_NODE node1, node2;
|
|
|
|
|
|
ClRtlLogPrint(LOG_NOISE, "[NM] Verifying connectivity\n");
|
|
|
|
for (node1Entry = NmpNodeList.Flink;
|
|
node1Entry != &NmpNodeList;
|
|
node1Entry = node1Entry->Flink
|
|
)
|
|
{
|
|
node1 = CONTAINING_RECORD(
|
|
node1Entry,
|
|
NM_NODE,
|
|
Linkage
|
|
);
|
|
|
|
if (NM_NODE_UP(node1)) {
|
|
for (node2Entry = node1->Linkage.Flink;
|
|
node2Entry != &NmpNodeList;
|
|
node2Entry = node2Entry->Flink
|
|
)
|
|
{
|
|
node2 = CONTAINING_RECORD(
|
|
node2Entry,
|
|
NM_NODE,
|
|
Linkage
|
|
);
|
|
|
|
if (NM_NODE_UP(node2)) {
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[NM] Verifying nodes %1!u! & %2!u! are connected\n",
|
|
node1->NodeId,
|
|
node2->NodeId
|
|
);
|
|
|
|
if (!NmpVerifyNodeConnectivity(
|
|
node1,
|
|
node2,
|
|
ExcludedNetwork
|
|
)
|
|
)
|
|
{
|
|
return(FALSE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return(TRUE);
|
|
|
|
} // NmpVerifyConnectivity
|
|
|
|
|
|
VOID
|
|
NmpIssueClusterPropertyChangeEvent(
|
|
VOID
|
|
)
|
|
{
|
|
DWORD status;
|
|
DWORD valueLength = 0;
|
|
DWORD valueSize = 0;
|
|
PWCHAR clusterName = NULL;
|
|
|
|
|
|
//
|
|
// The notification API expects a
|
|
// cluster name to be associated with this event.
|
|
//
|
|
status = NmpQueryString(
|
|
DmClusterParametersKey,
|
|
CLUSREG_NAME_CLUS_NAME,
|
|
REG_SZ,
|
|
&clusterName,
|
|
&valueLength,
|
|
&valueSize
|
|
);
|
|
|
|
if (status == ERROR_SUCCESS) {
|
|
ClusterEventEx(
|
|
CLUSTER_EVENT_PROPERTY_CHANGE,
|
|
EP_CONTEXT_VALID | EP_FREE_CONTEXT,
|
|
clusterName
|
|
);
|
|
|
|
//
|
|
// clusterName will be freed by the event processing code.
|
|
//
|
|
}
|
|
else {
|
|
ClRtlLogPrint(LOG_WARNING,
|
|
"[NM] Failed to issue cluster property change event, "
|
|
"status %1!u!.\n",
|
|
status
|
|
);
|
|
}
|
|
|
|
return;
|
|
|
|
} // NmpIssueClusterPropertyChangeEvent
|
|
|
|
|
|
DWORD
|
|
NmpMarshallObjectInfo(
|
|
IN const PRESUTIL_PROPERTY_ITEM PropertyTable,
|
|
IN PVOID ObjectInfo,
|
|
OUT PVOID * PropertyList,
|
|
OUT LPDWORD PropertyListSize
|
|
)
|
|
{
|
|
DWORD status;
|
|
PVOID propertyList = NULL;
|
|
DWORD propertyListSize = 0;
|
|
DWORD bytesReturned = 0;
|
|
DWORD bytesRequired = 0;
|
|
|
|
|
|
status = ClRtlPropertyListFromParameterBlock(
|
|
PropertyTable,
|
|
NULL,
|
|
&propertyListSize,
|
|
(LPBYTE) ObjectInfo,
|
|
&bytesReturned,
|
|
&bytesRequired
|
|
);
|
|
|
|
if (status != ERROR_MORE_DATA) {
|
|
CL_ASSERT(status != ERROR_SUCCESS);
|
|
return(status);
|
|
}
|
|
|
|
CL_ASSERT(bytesRequired > 0);
|
|
|
|
propertyList = MIDL_user_allocate(bytesRequired);
|
|
|
|
if (propertyList == NULL) {
|
|
return(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
|
|
propertyListSize = bytesRequired;
|
|
|
|
status = ClRtlPropertyListFromParameterBlock(
|
|
PropertyTable,
|
|
propertyList,
|
|
&propertyListSize,
|
|
(LPBYTE) ObjectInfo,
|
|
&bytesReturned,
|
|
&bytesRequired
|
|
);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
CL_ASSERT(status != ERROR_MORE_DATA);
|
|
MIDL_user_free(propertyList);
|
|
}
|
|
else {
|
|
CL_ASSERT(bytesReturned == propertyListSize);
|
|
*PropertyList = propertyList;
|
|
*PropertyListSize = bytesReturned;
|
|
}
|
|
|
|
return(status);
|
|
|
|
} // NmpMarshallObjectInfo
|
|
|
|
|
|
VOID
|
|
NmpReferenceNetwork(
|
|
PNM_NETWORK Network
|
|
)
|
|
{
|
|
OmReferenceObject(Network);
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
NmpDereferenceNetwork(
|
|
PNM_NETWORK Network
|
|
)
|
|
{
|
|
OmDereferenceObject(Network);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
PNM_NETWORK
|
|
NmpReferenceNetworkByAddress(
|
|
LPWSTR NetworkAddress
|
|
)
|
|
/*++
|
|
|
|
Notes:
|
|
|
|
Called with NM lock held.
|
|
|
|
--*/
|
|
{
|
|
PNM_NETWORK network;
|
|
PLIST_ENTRY entry;
|
|
|
|
|
|
for ( entry = NmpNetworkList.Flink;
|
|
entry != &NmpNetworkList;
|
|
entry = entry->Flink
|
|
)
|
|
{
|
|
network = CONTAINING_RECORD(entry, NM_NETWORK, Linkage);
|
|
|
|
if (lstrcmpW(network->Address, NetworkAddress) == 0) {
|
|
NmpReferenceNetwork(network);
|
|
|
|
return(network);
|
|
}
|
|
}
|
|
|
|
return(NULL);
|
|
|
|
} // NmpReferenceNetworkByAddress
|
|
|
|
|
|
BOOLEAN
|
|
NmpCheckForNetwork(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Checks whether at least one network on this node configured for MSCS
|
|
has media sense.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
TRUE if a viable network is found. FALSE otherwise.
|
|
|
|
Notes:
|
|
|
|
Called with and returns with no locks held.
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY entry;
|
|
PNM_NETWORK network;
|
|
BOOLEAN haveNetwork = FALSE;
|
|
|
|
NmpAcquireLock();
|
|
|
|
for (entry = NmpNetworkList.Flink;
|
|
entry != &NmpNetworkList;
|
|
entry = entry->Flink
|
|
)
|
|
{
|
|
network = CONTAINING_RECORD(
|
|
entry,
|
|
NM_NETWORK,
|
|
Linkage
|
|
);
|
|
|
|
// if a network's local interface is disabled, it is not
|
|
// considered a viable network. in this case the
|
|
// LocalInterface field is NULL.
|
|
if (network->LocalInterface != NULL) {
|
|
if (NmpVerifyLocalInterfaceConnected(network->LocalInterface)) {
|
|
|
|
haveNetwork = TRUE;
|
|
break;
|
|
|
|
} else {
|
|
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[NM] Network adapter %1!ws! with address %2!ws! "
|
|
"reported not connected.\n",
|
|
network->LocalInterface->AdapterId,
|
|
network->Address
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
NmpReleaseLock();
|
|
|
|
if (!haveNetwork) {
|
|
SetLastError(ERROR_NETWORK_NOT_AVAILABLE);
|
|
}
|
|
|
|
return(haveNetwork);
|
|
|
|
} // NmpCheckForNetwork
|
|
|
|
|