/*++ Copyright (c) 1996 Microsoft Corporation Module Name: nmpnp.c Abstract: Network Plug 'N Play and interface state event handling for the Node Manager. Author: Mike Massa (mikemas) Revision History: 2/23/98 Created. --*/ #include "nmp.h" // // Private Types // typedef struct { LIST_ENTRY Linkage; CLUSNET_EVENT_TYPE Type; DWORD Context1; DWORD Context2; } NM_PNP_EVENT, *PNM_PNP_EVENT; // // Private Data // PCRITICAL_SECTION NmpPnpLock = NULL; BOOLEAN NmpPnpEnabled = FALSE; BOOLEAN NmpPnpChangeOccurred = FALSE; BOOLEAN NmpPnpInitialized = FALSE; PCLRTL_BUFFER_POOL NmpPnpEventPool = NULL; PNM_PNP_EVENT NmpPnpShutdownEvent = NULL; PCL_QUEUE NmpPnpEventQueue = NULL; HANDLE NmpPnpWorkerThreadHandle = NULL; LPWSTR NmpPnpAddressString = NULL; // // Private Prototypes // DWORD NmpPnpWorkerThread( LPVOID Context ); // // Routines // DWORD NmpInitializePnp( VOID ) { DWORD status; HANDLE handle; DWORD threadId; DWORD maxAddressStringLength; // // Create the PnP lock // NmpPnpLock = LocalAlloc(LMEM_FIXED, sizeof(CRITICAL_SECTION)); if (NmpPnpLock == NULL) { ClRtlLogPrint(LOG_CRITICAL, "[NM] Unable to allocate PnP lock.\n"); return(ERROR_NOT_ENOUGH_MEMORY); } InitializeCriticalSection(NmpPnpLock); NmpPnpInitialized = TRUE; // // Allocate a buffer pool for PnP event contexts // NmpPnpEventPool = ClRtlCreateBufferPool( sizeof(NM_PNP_EVENT), 5, CLRTL_MAX_POOL_BUFFERS, NULL, NULL ); if (NmpPnpEventPool == NULL) { status = ERROR_NOT_ENOUGH_MEMORY; ClRtlLogPrint(LOG_CRITICAL, "[NM] Failed to allocate PnP event pool.\n" ); goto error_exit; } // // Pre-allocate the shutdown event // NmpPnpShutdownEvent = ClRtlAllocateBuffer(NmpPnpEventPool); if (NmpPnpShutdownEvent == NULL) { status = ERROR_NOT_ENOUGH_MEMORY; ClRtlLogPrint(LOG_CRITICAL, "[NM] Failed to allocate PnP shutdown event.\n" ); goto error_exit; } NmpPnpShutdownEvent->Type = ClusnetEventNone; // // Allocate the PnP event queue // NmpPnpEventQueue = LocalAlloc(LMEM_FIXED, sizeof(CL_QUEUE)); if (NmpPnpEventQueue == NULL) { status = ERROR_NOT_ENOUGH_MEMORY; ClRtlLogPrint(LOG_CRITICAL, "[NM] Failed to allocate PnP event queue.\n" ); goto error_exit; } ClRtlInitializeQueue(NmpPnpEventQueue); ClRtlQueryTcpipInformation(&maxAddressStringLength, NULL, NULL); NmpPnpAddressString = LocalAlloc( LMEM_FIXED, (maxAddressStringLength + 1) * sizeof(WCHAR) ); if (NmpPnpAddressString == NULL) { status = ERROR_NOT_ENOUGH_MEMORY; ClRtlLogPrint(LOG_CRITICAL, "[NM] Failed to allocate PnP address buffer.\n" ); goto error_exit; } // // Create PnP worker thread // NmpPnpWorkerThreadHandle = CreateThread( NULL, 0, NmpPnpWorkerThread, NULL, 0, &threadId ); if (NmpPnpWorkerThreadHandle == NULL) { status = GetLastError(); ClRtlLogPrint(LOG_CRITICAL, "[NM] Failed to create PnP worker thread, status %1!u!.\n", status ); goto error_exit; } status = ERROR_SUCCESS; error_exit: return(status); } // NmpInitializePnp VOID NmpShutdownPnp( VOID ) { if (!NmpPnpInitialized) { return; } if (NmpPnpWorkerThreadHandle != NULL) { // // Post shutdown event to queue // ClRtlInsertTailQueue( NmpPnpEventQueue, &(NmpPnpShutdownEvent->Linkage) ); // // Wait for the worker thread to terminate // WaitForSingleObject(NmpPnpWorkerThreadHandle, INFINITE); CloseHandle(NmpPnpWorkerThreadHandle); NmpPnpWorkerThreadHandle = NULL; } return; } // NmpShutdownPnp VOID NmpCleanupPnp( VOID ) { if (!NmpPnpInitialized) { return; } if (NmpPnpEventQueue != NULL) { LIST_ENTRY eventList; PLIST_ENTRY entry; PNM_PNP_EVENT event; ClRtlRundownQueue(NmpPnpEventQueue, &eventList); for ( entry = eventList.Flink; entry != &eventList; ) { event = CONTAINING_RECORD(entry, NM_PNP_EVENT, Linkage); if (event == NmpPnpShutdownEvent) { NmpPnpShutdownEvent = NULL; } entry = entry->Flink; ClRtlFreeBuffer(event); } LocalFree(NmpPnpEventQueue); NmpPnpEventQueue = NULL; } if (NmpPnpEventPool != NULL) { if (NmpPnpShutdownEvent != NULL) { ClRtlFreeBuffer(NmpPnpShutdownEvent); NmpPnpShutdownEvent = NULL; } ClRtlDestroyBufferPool(NmpPnpEventPool); NmpPnpEventPool = NULL; } if (NmpPnpAddressString != NULL) { LocalFree(NmpPnpAddressString); NmpPnpAddressString = NULL; } DeleteCriticalSection(NmpPnpLock); NmpPnpLock = NULL; NmpPnpInitialized = FALSE; return; } // NmpCleanupPnp VOID NmpWatchForPnpEvents( VOID ) { EnterCriticalSection(NmpPnpLock); NmpPnpChangeOccurred = FALSE; LeaveCriticalSection(NmpPnpLock); return; } // NmpWatchForPnpEvents DWORD NmpEnablePnpEvents( VOID ) { DWORD status; EnterCriticalSection(NmpPnpLock); if (NmpPnpChangeOccurred) { status = ERROR_CLUSTER_SYSTEM_CONFIG_CHANGED; } else { NmpPnpEnabled = TRUE; status = ERROR_SUCCESS; } LeaveCriticalSection(NmpPnpLock); return(status); } // NmpEnablePnpEvents VOID NmPostPnpEvent( IN CLUSNET_EVENT_TYPE EventType, IN DWORD Context1, IN DWORD Context2 ) { PNM_PNP_EVENT event; event = ClRtlAllocateBuffer(NmpPnpEventPool); if (event != NULL) { event->Type = EventType; event->Context1 = Context1; event->Context2 = Context2; ClRtlInsertTailQueue(NmpPnpEventQueue, &(event->Linkage)); } else { ClRtlLogPrint(LOG_CRITICAL, "[NM] Failed to allocate PnP event.\n" ); } return; } // NmPostPnpEvent VOID NmpProcessPnpAddAddressEvent( IN LPWSTR Address ) { DWORD status; PNM_NETWORK_ENUM networkEnum; PNM_INTERFACE_ENUM2 interfaceEnum; DWORD matchedNetworkCount; DWORD newNetworkCount; DWORD retryCount = 0; // // We will retry this operation a few times in case multiple nodes // race to create a network that has just been powered up. // do { networkEnum = NULL; interfaceEnum = NULL; matchedNetworkCount = 0; newNetworkCount = 0; NmpAcquireLock(); // // Obtain the network and interface definitions. // status = NmpEnumNetworkObjects(&networkEnum); if (status == ERROR_SUCCESS) { status = NmpEnumInterfaceObjects(&interfaceEnum); NmpReleaseLock(); if (status == ERROR_SUCCESS) { // // Run the network configuration engine. This will // update the cluster database. // status = NmpConfigureNetworks( NULL, NmLocalNodeIdString, NmLocalNodeName, &networkEnum, &interfaceEnum, NmpClusnetEndpoint, &matchedNetworkCount, &newNetworkCount, TRUE // RenameConnectoids ); if (status == ERROR_SUCCESS) { ClRtlLogPrint(LOG_NOISE, "[NM] Matched %1!u! networks, created %2!u! new " "networks.\n", matchedNetworkCount, newNetworkCount ); } else { ClRtlLogPrint(LOG_CRITICAL, "[NM] Failed to configure networks & interfaces, " "attempt #%1!u!, status %2!u!.\n", (retryCount + 1), status ); } if (interfaceEnum != NULL) { ClNetFreeInterfaceEnum(interfaceEnum); } } else { ClRtlLogPrint( LOG_UNUSUAL, "[NM] Failed to obtain current interface configuration, " "status %1!u!\n", status ); } if (networkEnum != NULL) { ClNetFreeNetworkEnum(networkEnum); } } else { NmpReleaseLock(); ClRtlLogPrint( LOG_UNUSUAL, "[NM] Failed to obtain current network configuration, " "status %1!u!\n", status ); } } while ((status != ERROR_SUCCESS) && (++retryCount <= 3)); return; } // NmpProcessPnpAddAddressEvent VOID NmpProcessPnpDelAddressEvent( IN LPWSTR Address ) { PLIST_ENTRY entry; PNM_INTERFACE netInterface; BOOLEAN networkDeleted; // // Check if this address corresponds to an interface for // the local node. // NmpAcquireLock(); for (entry = NmLocalNode->InterfaceList.Flink; entry != &(NmLocalNode->InterfaceList); entry = entry->Flink ) { netInterface = CONTAINING_RECORD( entry, NM_INTERFACE, NodeLinkage ); if (lstrcmpW( Address, netInterface->Address ) == 0 ) { // // Delete the interface from the cluster. // NmpGlobalDeleteInterface( (LPWSTR) OmObjectId(netInterface), &networkDeleted ); break; } } if (entry == &(NmLocalNode->InterfaceList)) { ClRtlLogPrint(LOG_NOISE, "[NM] Deleted address does not correspond to a cluster " "interface.\n" ); } NmpReleaseLock(); return; } // NmpProcessPnpDelAddressEvent DWORD NmpPnpWorkerThread( LPVOID Context ) { PLIST_ENTRY entry; PNM_PNP_EVENT event; BOOL again = TRUE; DWORD status; while (again) { entry = ClRtlRemoveHeadQueue(NmpPnpEventQueue); if (entry == NULL) { // // Time to exit // NmpPnpShutdownEvent = NULL; break; } event = CONTAINING_RECORD(entry, NM_PNP_EVENT, Linkage); if (event->Type == ClusnetEventNone) { // // Time to exit // again = FALSE; NmpPnpShutdownEvent = NULL; } else if (event->Type == ClusnetEventNetInterfaceUp) { NmpReportLocalInterfaceStateEvent( event->Context1, event->Context2, ClusterNetInterfaceUp ); } else if (event->Type == ClusnetEventNetInterfaceUnreachable) { NmpReportLocalInterfaceStateEvent( event->Context1, event->Context2, ClusterNetInterfaceUnreachable ); } else if (event->Type == ClusnetEventNetInterfaceFailed) { NmpReportLocalInterfaceStateEvent( event->Context1, event->Context2, ClusterNetInterfaceFailed ); } else if ( (event->Type == ClusnetEventAddAddress) || (event->Type == ClusnetEventDelAddress) ) { // // This is a PnP event. // EnterCriticalSection(NmpPnpLock); if (NmpPnpEnabled) { LeaveCriticalSection(NmpPnpLock); status = ClRtlTcpipAddressToString( event->Context1, &NmpPnpAddressString ); if (status == ERROR_SUCCESS) { if (event->Type == ClusnetEventAddAddress) { ClRtlLogPrint(LOG_NOISE, "[NM] Processing PnP add event for address " "%1!ws!.\n", NmpPnpAddressString ); NmpProcessPnpAddAddressEvent(NmpPnpAddressString); } else { ClRtlLogPrint(LOG_NOISE, "[NM] Processing PnP delete event for address " "%1!ws!.\n", NmpPnpAddressString ); NmpProcessPnpDelAddressEvent(NmpPnpAddressString); } } else { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to convert PnP address %1!x! to string, " "status %2!u!.\n", event->Context1, status ); } } else { // // We are not ready to handle PnP events yet. // Note that something changed. This will cause the join/form // process to eventually abort. // NmpPnpChangeOccurred = TRUE; LeaveCriticalSection(NmpPnpLock); ClRtlLogPrint( LOG_NOISE, "[NM] Discarding Pnp notification - handling not " "enabled.\n" ); } } else { ClRtlLogPrint(LOG_NOISE, "[NM] Received unknown PnP event type 0x%1!x!.\n", event->Type ); } ClRtlFreeBuffer(event); } ClRtlLogPrint(LOG_NOISE, "[NM] Pnp worker thread terminating.\n"); return(ERROR_SUCCESS); } // NmpPnpWorkerThread DWORD NmpConfigureNetworks( IN RPC_BINDING_HANDLE JoinSponsorBinding, IN LPWSTR LocalNodeId, IN LPWSTR LocalNodeName, IN PNM_NETWORK_ENUM * NetworkEnum, IN PNM_INTERFACE_ENUM2 * InterfaceEnum, IN LPWSTR DefaultEndpoint, IN OUT LPDWORD MatchedNetworkCount, IN OUT LPDWORD NewNetworkCount, IN BOOL RenameConnectoids ) /*++ Notes: Must not be called with the NM lock held. RenameConnectoids is TRUE if connectoid names are to be aligned with cluster network names. If FALSE, rename the cluster network names to be like the connectoid names. --*/ { DWORD status; CLNET_CONFIG_LISTS lists; PLIST_ENTRY listEntry; PCLNET_CONFIG_ENTRY configEntry; BOOLEAN networkDeleted; WCHAR errorString[12]; DWORD eventCode = 0; DWORD defaultRole = CL_DEFAULT_NETWORK_ROLE; *MatchedNetworkCount = 0; *NewNetworkCount = 0; ClNetInitializeConfigLists(&lists); // // Convert the enums to a list // status = ClNetConvertEnumsToConfigList( NetworkEnum, InterfaceEnum, LocalNodeId, &(lists.InputConfigList), TRUE ); if (status != ERROR_SUCCESS) { return(status); } // // Read the default network role from the database. // (VOID) DmQueryDword( DmClusterParametersKey, CLUSREG_NAME_CLUS_DEFAULT_NETWORK_ROLE, &defaultRole, &defaultRole ); // // Run the configuration engine. Existing net names take // precedence over connectoid when joining, otherwise change // the net name to align with the changed connectoid name. // ClRtlLogPrint(LOG_NOISE, "[NM] Running network configuration engine.\n" ); status = ClNetConfigureNetworks( LocalNodeId, LocalNodeName, NmpClusnetEndpoint, defaultRole, RenameConnectoids, &lists, MatchedNetworkCount, NewNetworkCount ); if (status != ERROR_SUCCESS) { goto error_exit; } ClRtlLogPrint(LOG_NOISE, "[NM] Processing network configuration changes.\n" ); // // Process the output - The order is important! // while (!IsListEmpty(&(lists.DeletedInterfaceList))) { listEntry = RemoveHeadList(&(lists.DeletedInterfaceList)); configEntry = CONTAINING_RECORD( listEntry, CLNET_CONFIG_ENTRY, Linkage ); status = NmpDeleteInterface( JoinSponsorBinding, configEntry->InterfaceInfo.Id, configEntry->InterfaceInfo.NetworkId, &networkDeleted ); ClNetFreeConfigEntry(configEntry); if (status != ERROR_SUCCESS) { goto error_exit; } } while (!IsListEmpty(&(lists.UpdatedInterfaceList))) { listEntry = RemoveHeadList(&(lists.UpdatedInterfaceList)); configEntry = CONTAINING_RECORD( listEntry, CLNET_CONFIG_ENTRY, Linkage ); status = NmpSetInterfaceInfo( JoinSponsorBinding, &(configEntry->InterfaceInfo) ); if (status == ERROR_SUCCESS && configEntry->UpdateNetworkName) { CL_ASSERT(JoinSponsorBinding == NULL); // // Note: this function must not be called with the NM lock // held. // status = NmpSetNetworkName( &(configEntry->NetworkInfo) ); } ClNetFreeConfigEntry(configEntry); if (status != ERROR_SUCCESS) { goto error_exit; } } while (!IsListEmpty(&(lists.CreatedInterfaceList))) { listEntry = RemoveHeadList(&(lists.CreatedInterfaceList)); configEntry = CONTAINING_RECORD( listEntry, CLNET_CONFIG_ENTRY, Linkage ); status = NmpCreateInterface( JoinSponsorBinding, &(configEntry->InterfaceInfo) ); ClNetFreeConfigEntry(configEntry); if (status != ERROR_SUCCESS) { goto error_exit; } } while (!IsListEmpty(&(lists.CreatedNetworkList))) { listEntry = RemoveHeadList(&(lists.CreatedNetworkList)); configEntry = CONTAINING_RECORD( listEntry, CLNET_CONFIG_ENTRY, Linkage ); status = NmpCreateNetwork( JoinSponsorBinding, &(configEntry->NetworkInfo), &(configEntry->InterfaceInfo) ); ClNetFreeConfigEntry(configEntry); if (status != ERROR_SUCCESS) { goto error_exit; } } error_exit: if (eventCode != 0) { wsprintfW(&(errorString[0]), L"%u", status); CsLogEvent1(LOG_CRITICAL, eventCode, errorString); } ClNetFreeConfigLists(&lists); return(status); } // NmpConfigureNetworks