/*++ 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; iFlink ) { network = CONTAINING_RECORD(entry, NM_NETWORK, InternalLinkage); if (NmpIsNetworkForInternalUse(network)) { for (i=0; iPriority != 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; iInternalLinkage) ); 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 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; iNetworkCount; 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; iStateWorkVector[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