/*++ Copyright (c) 1996-2001 Microsoft Corporation Module Name: mcast.c Abstract: Implements the Node Manager's network multicast management routines. The multicast configuration is stored separately for each network in three different places: 1. Cluster database: The cluster database is the persistent configuration. It does not include the key, which is secret, short-lived, and never written to stable storage. The cluster database is updated on each GUM update with the new state. 2. Network data structure: The network data structure contains what amounts to a cache of the cluster database. So, for instance, the address in the network data structure should match the address in the database (provided there has been at least one multicast config/refresh so that the cache is "primed"). The network also contains non- persistent data, such as the current key and timers. For example, both the database and network store the lease expiration time, but only the network contains the timer that ticks down. 3. ClusNet: ClusNet stores in kernel nonpaged pool only the most basic configuration for each network needed to send and receive multicast traffic. This includes the address and the key (and the brand). Actually, in order to accomodate transitions, ClusNet also stores the previous . The address stored by ClusNet may differ from that in the network data structure and the cluster database. The reason is that ClusNet has no notion of, for instance, disabling multicast. The service implements disabling by sending a 0.0.0.0 multicast address to ClusNet. The cluster database is only modified in the multicast config GUM update (except for private property changes from down-level nodes, but that is an edge case). The network data structure and ClusNet can be modified either in the GUM update or in a refresh. Refresh happens during join or by non-leaders when a network suddenly becomes multicast-ready (e.g. new network created, number of nodes increases to three, etc.). Refresh only reads the config from the database. It does not seek leases, etc., unless it becomes NM leader during refresh and must generate a new multicast key. Author: David Dion (daviddio) 15-Mar-2001 Revision History: --*/ #include "nmp.h" #include ///////////////////////////////////////////////////////////////////////////// // // Constants // ///////////////////////////////////////////////////////////////////////////// // // Lease status. // typedef enum { NmMcastLeaseValid = 0, NmMcastLeaseNeedsRenewal, NmMcastLeaseExpired } NM_MCAST_LEASE_STATUS, *PNM_MCAST_LEASE_STATUS; #define CLUSREG_NAME_CLUSTER_DISABLE_MULTICAST L"MulticastClusterDisabled" #define CLUSREG_NAME_NET_MULTICAST_ADDRESS L"MulticastAddress" #define CLUSREG_NAME_NET_DISABLE_MULTICAST L"MulticastDisabled" #define CLUSREG_NAME_NET_MULTICAST_KEY_SALT L"MulticastSalt" #define CLUSREG_NAME_NET_MCAST_LEASE_OBTAINED L"MulticastLeaseObtained" #define CLUSREG_NAME_NET_MCAST_LEASE_EXPIRES L"MulticastLeaseExpires" #define CLUSREG_NAME_NET_MCAST_REQUEST_ID L"MulticastRequestId" #define CLUSREG_NAME_NET_MCAST_SERVER_ADDRESS L"MulticastLeaseServer" #define CLUSREG_NAME_NET_MCAST_CONFIG_TYPE L"MulticastConfigType" #define CLUSREG_NAME_NET_MCAST_RANGE_LOWER L"MulticastAddressRangeLower" #define CLUSREG_NAME_NET_MCAST_RANGE_UPPER L"MulticastAddressRangeUpper" #define NMP_MCAST_DISABLED_DEFAULT 0 // NOT disabled #define NMP_SINGLE_SOURCE_SCOPE_ADDRESS 0x000000E8 // (232.*.*.*) #define NMP_SINGLE_SOURCE_SCOPE_MASK 0x000000FF // (255.0.0.0) #define NMP_LOCAL_SCOPE_ADDRESS 0x0000FFEF // (239.255.*.*) #define NMP_LOCAL_SCOPE_MASK 0x0000FFFF // (255.255.*.*) #define NMP_ORG_SCOPE_ADDRESS 0x0000C0EF // (239.192.*.*) #define NMP_ORG_SCOPE_MASK 0x0000FCFF // (255.63.*.*) #define NMP_MCAST_DEFAULT_RANGE_LOWER 0x0000FFEF // (239.255.0.0) #define NMP_MCAST_DEFAULT_RANGE_UPPER 0xFFFEFFEF // (239.255.254.255) #define NMP_MCAST_LEASE_RENEWAL_THRESHOLD 300 // 5 minutes #define NMP_MCAST_LEASE_RENEWAL_WINDOW 1800 // 30 minutes #define NMP_MADCAP_REQUERY_PERDIOD 3600 * 24 // 1 day #define NMP_MCAST_CONFIG_STABILITY_DELAY 5 * 1000 // 5 seconds #define NMP_MCAST_REFRESH_RENEW_DELAY 5 * 60 * 1000 // 5 minutes // // Minimum cluster node count in which to run multicast // #define NMP_MCAST_MIN_CLUSTER_NODE_COUNT 3 // // MADCAP lease request/response buffer sizes. These sizes are based on // IPv4 addresses. // #define NMP_MADCAP_REQUEST_BUFFER_SIZE \ (ROUND_UP_COUNT(sizeof(MCAST_LEASE_REQUEST),TYPE_ALIGNMENT(DWORD)) + \ sizeof(DWORD)) #define NMP_MADCAP_REQUEST_ADDR_OFFSET \ (ROUND_UP_COUNT(sizeof(MCAST_LEASE_REQUEST),TYPE_ALIGNMENT(DWORD))) #define NMP_MADCAP_RESPONSE_BUFFER_SIZE \ (ROUND_UP_COUNT(sizeof(MCAST_LEASE_RESPONSE),TYPE_ALIGNMENT(DWORD)) + \ sizeof(DWORD)) #define NMP_MADCAP_RESPONSE_ADDR_OFFSET \ (ROUND_UP_COUNT(sizeof(MCAST_LEASE_RESPONSE),TYPE_ALIGNMENT(DWORD))) // // Avoid trying to free a global NM string. // #define NMP_GLOBAL_STRING(_string) \ (((_string) == NmpNullMulticastAddress) || \ ((_string) == NmpNullString)) // // Conditions in which we release an address. // #define NmpNeedRelease(_Address, _Server, _RequestId, _Expires) \ (((_Address) != NULL) && \ (NmpMulticastValidateAddress(_Address)) && \ ((_Server) != NULL) && \ ((_RequestId)->ClientUID != NULL) && \ ((_RequestId)->ClientUIDLength != 0) && \ ((_Expires) != 0)) // // Convert IPv4 addr DWORD into four arguments for a printf/log routine. // #define NmpIpAddrPrintArgs(_ip) \ ((_ip >> 0 ) & 0xff), \ ((_ip >> 8 ) & 0xff), \ ((_ip >> 16) & 0xff), \ ((_ip >> 24) & 0xff) ///////////////////////////////////////////////////////////////////////////// // // Data // ///////////////////////////////////////////////////////////////////////////// LPWSTR NmpNullMulticastAddress = L"0.0.0.0"; BOOLEAN NmpMadcapClientInitialized = FALSE; BOOLEAN NmpIsNT5NodeInCluster = FALSE; BOOLEAN NmpMulticastIsNotEnoughNodes = FALSE; BOOLEAN NmpMulticastRunInitialConfig = FALSE; // MADCAP lease release node. typedef struct _NM_NETWORK_MADCAP_ADDRESS_RELEASE { LIST_ENTRY Linkage; LPWSTR McastAddress; LPWSTR ServerAddress; MCAST_CLIENT_UID RequestId; } NM_NETWORK_MADCAP_ADDRESS_RELEASE, *PNM_NETWORK_MADCAP_ADDRESS_RELEASE; // Data structure for GUM update typedef struct _NM_NETWORK_MULTICAST_UPDATE { DWORD Disabled; DWORD AddressOffset; DWORD EncryptedMulticastKeyOffset; DWORD EncryptedMulticastKeyLength; DWORD SaltOffset; DWORD SaltLength; DWORD MACOffset; DWORD MACLength; time_t LeaseObtained; time_t LeaseExpires; DWORD LeaseRequestIdOffset; DWORD LeaseRequestIdLength; DWORD LeaseServerOffset; NM_MCAST_CONFIG ConfigType; } NM_NETWORK_MULTICAST_UPDATE, *PNM_NETWORK_MULTICAST_UPDATE; // Data structure for multicast parameters, converted to and from the // GUM update data structure typedef struct _NM_NETWORK_MULTICAST_PARAMETERS { DWORD Disabled; LPWSTR Address; PVOID Key; DWORD KeyLength; DWORD MulticastKeyExpires; time_t LeaseObtained; time_t LeaseExpires; MCAST_CLIENT_UID LeaseRequestId; LPWSTR LeaseServer; NM_MCAST_CONFIG ConfigType; } NM_NETWORK_MULTICAST_PARAMETERS, *PNM_NETWORK_MULTICAST_PARAMETERS; // Data structure for multicast property validation typedef struct _NM_NETWORK_MULTICAST_INFO { LPWSTR MulticastAddress; DWORD MulticastDisable; PVOID MulticastSalt; DWORD MulticastLeaseObtained; DWORD MulticastLeaseExpires; PVOID MulticastLeaseRequestId; LPWSTR MulticastLeaseServer; DWORD MulticastConfigType; LPWSTR MulticastAddressRangeLower; LPWSTR MulticastAddressRangeUpper; } NM_NETWORK_MULTICAST_INFO, *PNM_NETWORK_MULTICAST_INFO; RESUTIL_PROPERTY_ITEM NmpNetworkMulticastProperties[] = { { CLUSREG_NAME_NET_MULTICAST_ADDRESS, NULL, CLUSPROP_FORMAT_SZ, 0, 0, 0, 0, // no flags - multicast address is writeable FIELD_OFFSET(NM_NETWORK_MULTICAST_INFO, MulticastAddress) }, { CLUSREG_NAME_NET_DISABLE_MULTICAST, NULL, CLUSPROP_FORMAT_DWORD, NMP_MCAST_DISABLED_DEFAULT, 0, 0xFFFFFFFF, 0, // no flags - disable is writeable FIELD_OFFSET(NM_NETWORK_MULTICAST_INFO, MulticastDisable) }, { CLUSREG_NAME_NET_MULTICAST_KEY_SALT, NULL, CLUSPROP_FORMAT_BINARY, 0, 0, 0, RESUTIL_PROPITEM_READ_ONLY, FIELD_OFFSET(NM_NETWORK_MULTICAST_INFO, MulticastSalt) }, { CLUSREG_NAME_NET_MCAST_LEASE_OBTAINED, NULL, CLUSPROP_FORMAT_DWORD, 0, 0, MAXLONG, RESUTIL_PROPITEM_READ_ONLY, FIELD_OFFSET(NM_NETWORK_MULTICAST_INFO, MulticastLeaseObtained) }, { CLUSREG_NAME_NET_MCAST_LEASE_EXPIRES, NULL, CLUSPROP_FORMAT_DWORD, 0, 0, MAXLONG, RESUTIL_PROPITEM_READ_ONLY, FIELD_OFFSET(NM_NETWORK_MULTICAST_INFO, MulticastLeaseExpires) }, { CLUSREG_NAME_NET_MCAST_REQUEST_ID, NULL, CLUSPROP_FORMAT_BINARY, 0, 0, 0, RESUTIL_PROPITEM_READ_ONLY, FIELD_OFFSET(NM_NETWORK_MULTICAST_INFO, MulticastLeaseRequestId) }, { CLUSREG_NAME_NET_MCAST_SERVER_ADDRESS, NULL, CLUSPROP_FORMAT_SZ, 0, 0, 0, RESUTIL_PROPITEM_READ_ONLY, FIELD_OFFSET(NM_NETWORK_MULTICAST_INFO, MulticastLeaseServer) }, { CLUSREG_NAME_NET_MCAST_CONFIG_TYPE, NULL, CLUSPROP_FORMAT_DWORD, NmMcastConfigManual, NmMcastConfigManual, NmMcastConfigAuto, RESUTIL_PROPITEM_READ_ONLY, FIELD_OFFSET(NM_NETWORK_MULTICAST_INFO, MulticastConfigType) }, { CLUSREG_NAME_NET_MCAST_RANGE_LOWER, NULL, CLUSPROP_FORMAT_SZ, 0, 0, 0, 0, // no flags - multicast address range is writeable FIELD_OFFSET(NM_NETWORK_MULTICAST_INFO, MulticastAddressRangeLower) }, { CLUSREG_NAME_NET_MCAST_RANGE_UPPER, NULL, CLUSPROP_FORMAT_SZ, 0, 0, 0, 0, // no flags - multicast address range is writeable FIELD_OFFSET(NM_NETWORK_MULTICAST_INFO, MulticastAddressRangeUpper) }, { 0 } }; // // Cluster registry API function pointers. Need a separate collection // of function pointers for multicast because nobody else (e.g. FM, NM) // fills in DmLocalXXX. // CLUSTER_REG_APIS NmpMcastClusterRegApis = { (PFNCLRTLCREATEKEY) DmRtlCreateKey, (PFNCLRTLOPENKEY) DmRtlOpenKey, (PFNCLRTLCLOSEKEY) DmCloseKey, (PFNCLRTLSETVALUE) DmSetValue, (PFNCLRTLQUERYVALUE) DmQueryValue, (PFNCLRTLENUMVALUE) DmEnumValue, (PFNCLRTLDELETEVALUE) DmDeleteValue, (PFNCLRTLLOCALCREATEKEY) DmLocalCreateKey, (PFNCLRTLLOCALSETVALUE) DmLocalSetValue, (PFNCLRTLLOCALDELETEVALUE) DmLocalDeleteValue }; // // Restricted ranges: we cannot choose a multicast address out of these // ranges, even if an administrator defines a selection range that // overlaps with a restricted range. // // Note, however, that if an administrator manually configures an // address, we accept it without question. // struct _NM_MCAST_RESTRICTED_RANGE { DWORD Lower; DWORD Upper; LPWSTR Description; } NmpMulticastRestrictedRange[] = { // single-source scope { NMP_SINGLE_SOURCE_SCOPE_ADDRESS, NMP_SINGLE_SOURCE_SCOPE_ADDRESS | ~NMP_SINGLE_SOURCE_SCOPE_MASK, L"Single-Source IP Multicast Address Range" }, // upper /24 of admin local scope { (NMP_LOCAL_SCOPE_ADDRESS | ~NMP_LOCAL_SCOPE_MASK) & 0x00FFFFFF, NMP_LOCAL_SCOPE_ADDRESS | ~NMP_LOCAL_SCOPE_MASK, L"Administrative Local Scope Relative Assignment Range" }, // upper /24 of admin organizational scope { (NMP_ORG_SCOPE_ADDRESS | ~NMP_ORG_SCOPE_MASK) & 0x00FFFFFF, NMP_ORG_SCOPE_ADDRESS | ~NMP_ORG_SCOPE_MASK, L"Administrative Organizational Scope Relative Assignment Range" } }; DWORD NmpMulticastRestrictedRangeCount = sizeof(NmpMulticastRestrictedRange) / sizeof(struct _NM_MCAST_RESTRICTED_RANGE); // // Range intervals: intervals in the IPv4 class D address space // from which we can choose an address. // typedef struct _NM_MCAST_RANGE_INTERVAL { LIST_ENTRY Linkage; DWORD hlLower; DWORD hlUpper; } NM_MCAST_RANGE_INTERVAL, *PNM_MCAST_RANGE_INTERVAL; #define NmpMulticastRangeIntervalSize(_interval) \ ((_interval)->hlUpper - (_interval)->hlLower + 1) ///////////////////////////////////////////////////////////////////////////// // // Internal prototypes // ///////////////////////////////////////////////////////////////////////////// DWORD NmpScheduleNetworkMadcapWorker( PNM_NETWORK Network ); DWORD NmpReconfigureMulticast( IN PNM_NETWORK Network ); DWORD NmpRegenerateMulticastKey( IN OUT PNM_NETWORK Network ); ///////////////////////////////////////////////////////////////////////////// // // Initialization & cleanup routines // ///////////////////////////////////////////////////////////////////////////// VOID NmpMulticastInitialize( VOID ) /*++ Routine Description: Initialize multicast readiness variables. --*/ { // // Figure out if this is a mixed NT5/NT5.1 cluster. // if (CLUSTER_GET_MAJOR_VERSION(CsClusterHighestVersion) == 3) { ClRtlLogPrint(LOG_NOISE, "[NM] Enabling mixed NT5/NT5.1 operation.\n" ); NmpIsNT5NodeInCluster = TRUE; } else { ClRtlLogPrint(LOG_NOISE, "[NM] Disabling mixed NT5/NT5.1 operation.\n" ); NmpIsNT5NodeInCluster = FALSE; } // // Figure out if there are enough nodes in this cluster // to run multicast. // if (NmpNodeCount < NMP_MCAST_MIN_CLUSTER_NODE_COUNT) { NmpMulticastIsNotEnoughNodes = TRUE; } else { NmpMulticastIsNotEnoughNodes = FALSE; } return; } // NmpMulticastInitialize DWORD NmpMulticastCleanup( VOID ) /*++ Notes: Called with NM lock held. --*/ { // // Cleanup the MADCAP client. // if (NmpMadcapClientInitialized) { McastApiCleanup(); NmpMadcapClientInitialized = FALSE; } return(ERROR_SUCCESS); } // NmpMulticastCleanup ///////////////////////////////////////////////////////////////////////////// // // Internal routines. // ///////////////////////////////////////////////////////////////////////////// #if CLUSTER_BETA LPWSTR NmpCreateLogString( IN PVOID Source, IN DWORD SourceLength ) { PWCHAR displayBuf, bufp; PCHAR chp; DWORD x, i; displayBuf = LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, SourceLength * ( 7 * sizeof(WCHAR) ) ); if (displayBuf == NULL) { ClRtlLogPrint(LOG_NOISE, "[NM] Failed to allocate display buffer of size %1!u!.\n", SourceLength * sizeof(WCHAR) ); goto error_exit; } bufp = displayBuf; chp = (PCHAR) Source; for (i = 0; i < SourceLength; i++) { x = (DWORD) (*chp); x &= 0xff; wsprintf(bufp, L"%02x ", x); chp++; bufp = &displayBuf[wcslen(displayBuf)]; } error_exit: return(displayBuf); } // NmpCreateLogString #endif // CLUSTER_BETA BOOLEAN NmpIsClusterMulticastReady( IN BOOLEAN CheckNodeCount, IN BOOLEAN Verbose ) /*++ Routine Description: Determines from the cluster version and the NM up node set whether multicast should be run in this cluster. Criteria: no nodes with version below Whistler at least three nodes configured (at this point, we're not worried about how many nodes are actually running) Arguments: CheckNodeCount - indicates whether number of nodes configured in cluster should be considered Verbose - indicates whether to write results to the cluster log Return value: TRUE if multicast should be run. Notes: Called and returns with NM lock held. --*/ { LPWSTR reason = NULL; // // First check for the lowest version. // if (NmpIsNT5NodeInCluster) { reason = L"there is at least one NT5 node configured " L"in the cluster membership"; } // // Count the nodes. // else if (CheckNodeCount && NmpMulticastIsNotEnoughNodes) { reason = L"there are not enough nodes configured " L"in the cluster membership"; } if (Verbose && reason != NULL) { ClRtlLogPrint(LOG_NOISE, "[NM] Multicast is not justified for this " "cluster because %1!ws!.\n", reason ); } return((BOOLEAN)(reason == NULL)); } // NmpIsClusterMulticastReady BOOLEAN NmpMulticastValidateAddress( IN LPWSTR McastAddress ) /*++ Routine Description: Determines whether McastAddress is a valid multicast address. Notes: IPv4 specific. --*/ { DWORD status; DWORD address; status = ClRtlTcpipStringToAddress(McastAddress, &address); if (status == ERROR_SUCCESS) { address = ntohl(address); if (IN_CLASSD(address)) { return(TRUE); } } return(FALSE); } // NmpMulticastValidateAddress VOID NmpFreeNetworkMulticastInfo( IN PNM_NETWORK_MULTICAST_INFO McastInfo ) { NM_MIDL_FREE_OBJECT_FIELD(McastInfo, MulticastAddress); NM_MIDL_FREE_OBJECT_FIELD(McastInfo, MulticastSalt); NM_MIDL_FREE_OBJECT_FIELD(McastInfo, MulticastLeaseRequestId); NM_MIDL_FREE_OBJECT_FIELD(McastInfo, MulticastLeaseServer); NM_MIDL_FREE_OBJECT_FIELD(McastInfo, MulticastAddressRangeLower); NM_MIDL_FREE_OBJECT_FIELD(McastInfo, MulticastAddressRangeUpper); return; } // NmpFreeNetworkMulticastInfo DWORD NmpStoreString( IN LPWSTR Source, IN OUT LPWSTR * Dest, IN OUT OPTIONAL DWORD * DestLength ) { DWORD sourceSize; DWORD destLength; if (Source != NULL) { sourceSize = NM_WCSLEN(Source); } else { sourceSize = 0; } if (DestLength == NULL) { destLength = 0; } else { destLength = *DestLength; } if (*Dest != NULL && ((destLength < sourceSize) || (Source == NULL))) { MIDL_user_free(*Dest); *Dest = NULL; } if (*Dest == NULL) { if (sourceSize > 0) { *Dest = MIDL_user_allocate(sourceSize); if (*Dest == NULL) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to allocate buffer of size %1!u! " "for source string %2!ws!.\n", sourceSize, Source ); return(ERROR_NOT_ENOUGH_MEMORY); } } if (DestLength != NULL) { *DestLength = sourceSize; } } if (sourceSize > 0) { RtlCopyMemory(*Dest, Source, sourceSize); } return(ERROR_SUCCESS); } // NmpStoreString DWORD NmpStoreRequestId( IN LPMCAST_CLIENT_UID Source, IN OUT LPMCAST_CLIENT_UID Dest ) { DWORD status; DWORD len; len = Source->ClientUIDLength; if (Source->ClientUID == NULL) { len = 0; } if (Dest->ClientUID != NULL && (Dest->ClientUIDLength < Source->ClientUIDLength || len == 0)) { MIDL_user_free(Dest->ClientUID); Dest->ClientUID = NULL; Dest->ClientUIDLength = 0; } if (Dest->ClientUID == NULL && len > 0) { Dest->ClientUID = MIDL_user_allocate(len); if (Dest->ClientUID == NULL) { status = ERROR_NOT_ENOUGH_MEMORY; goto error_exit; } } if (len > 0) { RtlCopyMemory( Dest->ClientUID, Source->ClientUID, len ); } Dest->ClientUIDLength = len; status = ERROR_SUCCESS; error_exit: return(status); } // NmpStoreRequestId VOID NmpStartNetworkMulticastAddressRenewTimer( PNM_NETWORK Network, DWORD Timeout ) /*++ Notes: Must be called with NM lock held. --*/ { LPCWSTR networkId = OmObjectId(Network); LPCWSTR networkName = OmObjectName(Network); if (Network->McastAddressRenewTimer != Timeout) { Network->McastAddressRenewTimer = Timeout; ClRtlLogPrint(LOG_NOISE, "[NM] %1!ws! multicast address lease renew " "timer (%2!u!ms) for network %3!ws! (%4!ws!)\n", ((Timeout == 0) ? L"Cleared" : L"Started"), Network->McastAddressRenewTimer, networkId, networkName ); } return; } // NmpStartNetworkMulticastAddressRenewTimer VOID NmpStartNetworkMulticastAddressReconfigureTimer( PNM_NETWORK Network, DWORD Timeout ) /*++ Notes: Must be called with NM lock held. --*/ { LPCWSTR networkId = OmObjectId(Network); LPCWSTR networkName = OmObjectName(Network); if (Network->McastAddressReconfigureRetryTimer != Timeout) { Network->McastAddressReconfigureRetryTimer = Timeout; ClRtlLogPrint(LOG_NOISE, "[NM] %1!ws! multicast address reconfigure " "timer (%2!u!ms) for network %3!ws! (%4!ws!)\n", ((Timeout == 0) ? L"Cleared" : L"Started"), Network->McastAddressReconfigureRetryTimer, networkId, networkName ); } return; } // NmpStartNetworkMulticastAddressReconfigureTimer VOID NmpStartNetworkMulticastKeyRegenerateTimer( PNM_NETWORK Network, DWORD Timeout ) /*++ Notes: Must be called with NM lock held. --*/ { LPCWSTR networkId = OmObjectId(Network); LPCWSTR networkName = OmObjectName(Network); if (Network->McastKeyRegenerateTimer != Timeout) { Network->McastKeyRegenerateTimer = Timeout; ClRtlLogPrint(LOG_NOISE, "[NM] %1!ws! multicast key regenerate " "timer (%2!u!ms) for network %3!ws! (%4!ws!)\n", ((Timeout == 0) ? L"Cleared" : L"Started"), Network->McastKeyRegenerateTimer, networkId, networkName ); } return; } // NmpStartNetworkMulticastKeyRegenerateTimer VOID NmpMulticastFreeParameters( IN PNM_NETWORK_MULTICAST_PARAMETERS Parameters ) { if (Parameters == NULL) { return; } if (Parameters->Address != NULL) { if (!NMP_GLOBAL_STRING(Parameters->Address)) { MIDL_user_free(Parameters->Address); } Parameters->Address = NULL; } if (Parameters->Key != NULL) { RtlSecureZeroMemory(Parameters->Key, Parameters->KeyLength); } NM_MIDL_FREE_OBJECT_FIELD(Parameters, Key); Parameters->KeyLength = 0; NM_MIDL_FREE_OBJECT_FIELD(Parameters, LeaseRequestId.ClientUID); Parameters->LeaseRequestId.ClientUIDLength = 0; if (Parameters->LeaseServer != NULL) { if (!NMP_GLOBAL_STRING(Parameters->LeaseServer)) { MIDL_user_free(Parameters->LeaseServer); } Parameters->LeaseServer = NULL; } return; } // NmpMulticastFreeParameters DWORD NmpMulticastCreateParameters( IN DWORD Disabled, IN LPWSTR Address, IN PVOID Key, IN DWORD KeyLength, IN time_t LeaseObtained, IN time_t LeaseExpires, IN LPMCAST_CLIENT_UID LeaseRequestId, IN LPWSTR LeaseServer, IN NM_MCAST_CONFIG ConfigType, OUT PNM_NETWORK_MULTICAST_PARAMETERS Parameters ) { DWORD status; RtlZeroMemory(Parameters, sizeof(*Parameters)); // disabled Parameters->Disabled = Disabled; // address if (Address != NULL) { status = NmpStoreString(Address, &(Parameters->Address), NULL); if (status != ERROR_SUCCESS) { goto error_exit; } } // key if (Key != NULL) { Parameters->Key = MIDL_user_allocate(KeyLength); if (Parameters->Key == NULL) { status = ERROR_NOT_ENOUGH_MEMORY; goto error_exit; } RtlCopyMemory(Parameters->Key, Key, KeyLength); Parameters->KeyLength = KeyLength; } Parameters->LeaseObtained = LeaseObtained; Parameters->LeaseExpires = LeaseExpires; // lease request id if (LeaseRequestId != NULL && LeaseRequestId->ClientUID != NULL) { status = NmpStoreRequestId(LeaseRequestId, &Parameters->LeaseRequestId); if (status != ERROR_SUCCESS) { goto error_exit; } } // lease server address if (LeaseServer != NULL) { status = NmpStoreString(LeaseServer, &(Parameters->LeaseServer), NULL); if (status != ERROR_SUCCESS) { goto error_exit; } } // config type Parameters->ConfigType = ConfigType; // multicast key expiration Parameters->MulticastKeyExpires = 0; return(ERROR_SUCCESS); error_exit: NmpMulticastFreeParameters(Parameters); return(status); } // NmpMulticastCreateParameters DWORD NmpMulticastCreateParametersFromUpdate( IN PNM_NETWORK Network, IN PNM_NETWORK_MULTICAST_UPDATE Update, IN BOOLEAN GenerateKey, OUT PNM_NETWORK_MULTICAST_PARAMETERS Parameters ) /*++ Routine Description: Converts a data structure received in a GUM update into a locally allocated parameters data structure. The base Parameters data structure must be allocated by the caller, though the fields are allocated in this routine. --*/ { DWORD status; MCAST_CLIENT_UID requestId; LPWSTR NetworkId; PVOID EncryptionKey = NULL; DWORD EncryptionKeyLength; PBYTE MulticastKey = NULL; DWORD MulticastKeyLength; requestId.ClientUID = ((Update->LeaseRequestIdOffset == 0) ? NULL : (LPBYTE)((PUCHAR)Update + Update->LeaseRequestIdOffset)); requestId.ClientUIDLength = Update->LeaseRequestIdLength; if (Update->EncryptedMulticastKeyOffset != 0) { // // Decrypted multicast key // NetworkId = (LPWSTR) OmObjectId(Network); status = NmpDeriveClusterKey(NetworkId, NM_WCSLEN(NetworkId), &EncryptionKey, &EncryptionKeyLength ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] NmpMulticastCreateParametersFromUpdate: Failed to " "derive cluster key for " "network %1!ws!, status %2!u!.\n", NetworkId, status ); // Non-fatal error. Proceed with NULL key. This node // will not be able to send or receive multicast on // this network. MulticastKey = NULL; MulticastKeyLength = 0; status = ERROR_SUCCESS; } else { status = NmpVerifyMACAndDecryptData( NmCryptServiceProvider, NMP_ENCRYPT_ALGORITHM, NMP_KEY_LENGTH, (PBYTE) ((PUCHAR)Update + Update->MACOffset), Update->MACLength, NMP_MAC_DATA_LENGTH_EXPECTED, (PBYTE) ((PUCHAR)Update + Update->EncryptedMulticastKeyOffset), Update->EncryptedMulticastKeyLength, EncryptionKey, EncryptionKeyLength, (PBYTE) ((PUCHAR)Update + Update->SaltOffset), Update->SaltLength, &MulticastKey, &MulticastKeyLength ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] NmpMulticastCreateParametersFromUpdate: " "Failed to verify MAC or decrypt data for " "network %1!ws!, status %2!u!.\n", NetworkId, status ); // Non-fatal error. Proceed with NULL key. This node // will not be able to send or receive multicast on // this network. MulticastKey = NULL; MulticastKeyLength = 0; status = ERROR_SUCCESS; } } // // A new key is always generated before a multicast config // GUM update (unless multicast is disabled, in which case // the EncryptedMulticastKey will be NULL). Set the key // expiration to the default. // Parameters->MulticastKeyExpires = NM_NET_MULTICAST_KEY_REGEN_TIMEOUT; } status = NmpMulticastCreateParameters( Update->Disabled, ((Update->AddressOffset == 0) ? NULL : (LPWSTR)((PUCHAR)Update + Update->AddressOffset)), MulticastKey, MulticastKeyLength, Update->LeaseObtained, Update->LeaseExpires, &requestId, ((Update->LeaseServerOffset == 0) ? NULL : (LPWSTR)((PUCHAR)Update + Update->LeaseServerOffset)), Update->ConfigType, Parameters ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] NmpMulticastCreateParametersFromUpdate: " "Failed to create parameters for " "network %1!ws!, status %2!u!.\n", NetworkId, status ); } if (EncryptionKey != NULL) { RtlSecureZeroMemory(EncryptionKey, EncryptionKeyLength); LocalFree(EncryptionKey); } if (MulticastKey != NULL) { RtlSecureZeroMemory(MulticastKey, MulticastKeyLength); LocalFree(MulticastKey); } if (status != ERROR_SUCCESS) { NmpMulticastFreeParameters(Parameters); } return(status); } // NmpMulticastCreateParametersFromUpdate DWORD NmpMulticastCreateUpdateFromParameters( IN PNM_NETWORK Network, IN PNM_NETWORK_MULTICAST_PARAMETERS Parameters, OUT PNM_NETWORK_MULTICAST_UPDATE * Update, OUT DWORD * UpdateSize ) { DWORD updateSize; PNM_NETWORK_MULTICAST_UPDATE update; DWORD address = 0; DWORD encryptedMulticastKey = 0; DWORD salt = 0; DWORD mac = 0; LPWSTR NetworkId; PVOID EncryptionKey = NULL; DWORD EncryptionKeyLength; PBYTE Salt = NULL; PBYTE EncryptedMulticastKey = NULL; DWORD EncryptedMulticastKeyLength = 0; PBYTE MAC = NULL; DWORD MACLength; DWORD status = ERROR_SUCCESS; DWORD requestId = 0; DWORD leaseServer = 0; // // Calculate the size of the update data buffer. // updateSize = sizeof(*update); // address if (Parameters->Address != NULL) { updateSize = ROUND_UP_COUNT(updateSize, TYPE_ALIGNMENT(LPWSTR)); address = updateSize; updateSize += NM_WCSLEN(Parameters->Address); } if (Parameters->Key != NULL) { NetworkId = (LPWSTR) OmObjectId(Network); status = NmpDeriveClusterKey( NetworkId, NM_WCSLEN(NetworkId), &EncryptionKey, &EncryptionKeyLength ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_CRITICAL, "[NM] NmpMulticastCreateUpdateFromParameters: Failed to " "derive cluster key for " "network %1!ws!, status %2!u!.\n", NetworkId, status ); goto error_exit; } MACLength = NMP_MAC_DATA_LENGTH_EXPECTED; status = NmpEncryptDataAndCreateMAC( NmCryptServiceProvider, NMP_ENCRYPT_ALGORITHM, NMP_KEY_LENGTH, Parameters->Key, // Data Parameters->KeyLength, // Data length EncryptionKey, EncryptionKeyLength, TRUE, // Create salt &Salt, NMP_SALT_BUFFER_LEN, &EncryptedMulticastKey, &EncryptedMulticastKeyLength, &MAC, &MACLength ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_CRITICAL, "[NM] NmpMulticastCreateUpdateFromParameters: Failed to " "encrypt data or generate MAC for " "network %1!ws!, status %2!u!.\n", NetworkId, status ); goto error_exit; } // encrypted multicast key updateSize = ROUND_UP_COUNT(updateSize, TYPE_ALIGNMENT(PVOID)); encryptedMulticastKey = updateSize; updateSize += EncryptedMulticastKeyLength; // salt updateSize = ROUND_UP_COUNT(updateSize, TYPE_ALIGNMENT(PVOID)); salt = updateSize; updateSize += NMP_SALT_BUFFER_LEN; // mac updateSize = ROUND_UP_COUNT(updateSize, TYPE_ALIGNMENT(PVOID)); mac = updateSize; updateSize += MACLength; } // request id if (Parameters->LeaseRequestId.ClientUID != NULL) { updateSize = ROUND_UP_COUNT(updateSize, TYPE_ALIGNMENT(LPBYTE)); requestId = updateSize; updateSize += Parameters->LeaseRequestId.ClientUIDLength; } // lease server if (Parameters->LeaseServer != NULL) { updateSize = ROUND_UP_COUNT(updateSize, TYPE_ALIGNMENT(LPWSTR)); leaseServer = updateSize; updateSize += NM_WCSLEN(Parameters->LeaseServer); } // // Allocate the update buffer. // update = MIDL_user_allocate(updateSize); if (update == NULL) { status = ERROR_NOT_ENOUGH_MEMORY; ClRtlLogPrint(LOG_CRITICAL, "[NM] NmpMulticastCreateUpdateFromParameters: Failed to " "allocate %1!u! bytes.\n", updateSize ); goto error_exit; } // // Fill in the update buffer. // update->Disabled = Parameters->Disabled; update->AddressOffset = address; if (address != 0) { RtlCopyMemory( (PUCHAR)update + address, Parameters->Address, NM_WCSLEN(Parameters->Address) ); } // encrypted multicast key update->EncryptedMulticastKeyOffset = encryptedMulticastKey; update->EncryptedMulticastKeyLength = EncryptedMulticastKeyLength; if (encryptedMulticastKey != 0) { RtlCopyMemory( (PUCHAR)update + encryptedMulticastKey, EncryptedMulticastKey, EncryptedMulticastKeyLength ); } // salt update->SaltOffset = salt; update->SaltLength = NMP_SALT_BUFFER_LEN; if (salt != 0) { RtlCopyMemory( (PUCHAR)update + salt, Salt, NMP_SALT_BUFFER_LEN ); } // mac update->MACOffset = mac; update->MACLength = MACLength; if (mac != 0) { RtlCopyMemory( (PUCHAR)update + mac, MAC, MACLength ); } update->LeaseObtained = Parameters->LeaseObtained; update->LeaseExpires = Parameters->LeaseExpires; update->LeaseRequestIdOffset = requestId; update->LeaseRequestIdLength = Parameters->LeaseRequestId.ClientUIDLength; if (requestId != 0) { RtlCopyMemory( (PUCHAR)update + requestId, Parameters->LeaseRequestId.ClientUID, Parameters->LeaseRequestId.ClientUIDLength ); } update->LeaseServerOffset = leaseServer; if (leaseServer != 0) { RtlCopyMemory( (PUCHAR)update + leaseServer, Parameters->LeaseServer, NM_WCSLEN(Parameters->LeaseServer) ); } update->ConfigType = Parameters->ConfigType; *Update = update; *UpdateSize = updateSize; error_exit: if (EncryptionKey != NULL) { RtlSecureZeroMemory(EncryptionKey, EncryptionKeyLength); LocalFree(EncryptionKey); } if (EncryptedMulticastKey != NULL) { LocalFree(EncryptedMulticastKey); } if (Salt != NULL) { LocalFree(Salt); } if (MAC != NULL) { LocalFree(MAC); } return(status); } // NmpMulticastCreateUpdateFromParameters VOID NmpFreeMulticastAddressRelease( IN PNM_NETWORK_MADCAP_ADDRESS_RELEASE Release ) { if (Release == NULL) { return; } if (Release->McastAddress != NULL && !NMP_GLOBAL_STRING(Release->McastAddress)) { MIDL_user_free(Release->McastAddress); Release->McastAddress = NULL; } if (Release->ServerAddress != NULL && !NMP_GLOBAL_STRING(Release->ServerAddress)) { MIDL_user_free(Release->ServerAddress); Release->ServerAddress = NULL; } if (Release->RequestId.ClientUID != NULL) { MIDL_user_free(Release->RequestId.ClientUID); Release->RequestId.ClientUID = NULL; Release->RequestId.ClientUIDLength = 0; } LocalFree(Release); return; } // NmpFreeMulticastAddressRelease DWORD NmpCreateMulticastAddressRelease( IN LPWSTR McastAddress, IN LPWSTR ServerAddress, IN LPMCAST_CLIENT_UID RequestId, OUT PNM_NETWORK_MADCAP_ADDRESS_RELEASE * Release ) /*++ Routine Description: Allocate and initialize an entry for an address release list. --*/ { DWORD status; PNM_NETWORK_MADCAP_ADDRESS_RELEASE release = NULL; release = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, sizeof(*release)); if (release == NULL) { status = ERROR_NOT_ENOUGH_MEMORY; goto error_exit; } status = NmpStoreString(McastAddress, &(release->McastAddress), NULL); if (status != ERROR_SUCCESS) { goto error_exit; } status = NmpStoreString(ServerAddress, &(release->ServerAddress), NULL); if (status != ERROR_SUCCESS) { goto error_exit; } status = NmpStoreRequestId(RequestId, &(release->RequestId)); if (status != ERROR_SUCCESS) { goto error_exit; } *Release = release; return(ERROR_SUCCESS); error_exit: NmpFreeMulticastAddressRelease(release); return(status); } // NmpCreateMulticastAddressRelease VOID NmpInitiateMulticastAddressRelease( IN PNM_NETWORK Network, IN PNM_NETWORK_MADCAP_ADDRESS_RELEASE Release ) /*++ Routine Description: Stores an entry for the network multicast address release list on the list and schedules the release. Notes: Called and returns with NM lock held. --*/ { InsertTailList(&(Network->McastAddressReleaseList), &(Release->Linkage)); NmpScheduleMulticastAddressRelease(Network); return; } // NmpInitiateMulticastAddressRelease DWORD NmpQueryMulticastAddress( IN PNM_NETWORK Network, IN HDMKEY NetworkKey, IN OUT HDMKEY * NetworkParametersKey, IN OUT LPWSTR * McastAddr, IN OUT ULONG * McastAddrLength ) { DWORD status; LPCWSTR networkId = OmObjectId(Network); HDMKEY netParamKey = NULL; BOOLEAN openedNetParamKey = FALSE; DWORD size = 0; if (Network == NULL || NetworkKey == NULL) { status = ERROR_INVALID_PARAMETER; goto error_exit; } #if CLUSTER_BETA ClRtlLogPrint(LOG_NOISE, "[NM] Querying multicast address for " "network %1!ws! from cluster database.\n", networkId ); #endif // CLUSTER_BETA // // Open the network parameters key, if necessary. // netParamKey = *NetworkParametersKey; if (netParamKey == NULL) { netParamKey = DmOpenKey( NetworkKey, CLUSREG_KEYNAME_PARAMETERS, MAXIMUM_ALLOWED ); if (netParamKey == NULL) { status = GetLastError(); ClRtlLogPrint(LOG_NOISE, "[NM] Failed to find Parameters key " "for network %1!ws!, status %2!u!. Using default " "multicast parameters.\n", networkId, status ); goto error_exit; } else { openedNetParamKey = TRUE; } } // // Query for the multicast address. // status = NmpQueryString( netParamKey, CLUSREG_NAME_NET_MULTICAST_ADDRESS, REG_SZ, McastAddr, McastAddrLength, &size ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to read multicast address for " "network %1!ws! from cluster database, " "status %2!u!. Using default.\n", networkId, status ); goto error_exit; } *NetworkParametersKey = netParamKey; netParamKey = NULL; #if CLUSTER_BETA ClRtlLogPrint(LOG_NOISE, "[NM] Found multicast address %1!ws! for " "network %2!ws! in cluster database.\n", *McastAddr, networkId ); #endif // CLUSTER_BETA error_exit: if (openedNetParamKey && netParamKey != NULL) { DmCloseKey(netParamKey); netParamKey = NULL; } return(status); } // NmpQueryMulticastAddress DWORD NmpQueryMulticastDisabled( IN PNM_NETWORK Network, IN OUT HDMKEY * ClusterParametersKey, IN OUT HDMKEY * NetworkKey, IN OUT HDMKEY * NetworkParametersKey, OUT DWORD * Disabled ) /*++ Routine Description: Checks whether multicast has been disabled for this network in the cluster database. Both the network paramters key and the cluster parameters key are checked. The order of precedence is as follows: - a value in the network parameters key has top precedence - if no value is found in the network parameters key, a value is checked in the cluster parameters key. - if no value is found in the cluster parameters key, return NMP_MCAST_DISABLED_DEFAULT. If an error is returned, the return value of Disabled is undefined. Notes: Must not be called with NM lock held. --*/ { DWORD status; LPCWSTR networkId = OmObjectId(Network); DWORD type; DWORD disabled; DWORD len = sizeof(disabled); BOOLEAN found = FALSE; HDMKEY clusParamKey = NULL; BOOLEAN openedClusParamKey = FALSE; HDMKEY networkKey = NULL; BOOLEAN openedNetworkKey = FALSE; HDMKEY netParamKey = NULL; BOOLEAN openedNetParamKey = FALSE; // // Open the network key, if necessary. // networkKey = *NetworkKey; if (networkKey == NULL) { networkKey = DmOpenKey( DmNetworksKey, networkId, MAXIMUM_ALLOWED ); if (networkKey == NULL) { status = GetLastError(); ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to open key for network %1!ws!, " "status %2!u!\n", networkId, status ); goto error_exit; } else { openedNetworkKey = TRUE; } } // // Open the network parameters key, if necessary. // netParamKey = *NetworkParametersKey; if (netParamKey == NULL) { netParamKey = DmOpenKey( networkKey, CLUSREG_KEYNAME_PARAMETERS, MAXIMUM_ALLOWED ); if (netParamKey == NULL) { status = GetLastError(); #if CLUSTER_BETA ClRtlLogPrint(LOG_NOISE, "[NM] Failed to find Parameters key " "for network %1!ws!, status %2!u!. Using default " "multicast parameters.\n", networkId, status ); #endif // CLUSTER_BETA } else { openedNetParamKey = TRUE; } } // // If we found a network parameters key, check for the // disabled value. // if (netParamKey != NULL) { status = DmQueryValue( netParamKey, CLUSREG_NAME_NET_DISABLE_MULTICAST, &type, (LPBYTE) &disabled, &len ); if (status == ERROR_SUCCESS) { if (type != REG_DWORD) { ClRtlLogPrint(LOG_NOISE, "[NM] Unexpected type (%1!u!) for network " "%2!ws! %3!ws!, status %4!u!. Checking " "cluster parameters ...\n", type, networkId, CLUSREG_NAME_NET_DISABLE_MULTICAST, status ); } else { found = TRUE; } } } // // If we were unsuccessful at finding a value in the // network parameters key, try under the cluster // parameters key. // if (!found) { // // Open the cluster parameters key, if necessary. // clusParamKey = *NetworkParametersKey; if (clusParamKey == NULL) { clusParamKey = DmOpenKey( DmClusterParametersKey, CLUSREG_KEYNAME_PARAMETERS, KEY_READ ); if (clusParamKey == NULL) { status = GetLastError(); #if CLUSTER_BETA ClRtlLogPrint(LOG_NOISE, "[NM] Failed to find cluster Parameters " "key, status %1!u!.\n", status ); #endif // CLUSTER_BETA } else { openedClusParamKey = TRUE; // // Query the disabled value under the cluster parameters // key. // status = DmQueryValue( clusParamKey, CLUSREG_NAME_CLUSTER_DISABLE_MULTICAST, &type, (LPBYTE) &disabled, &len ); if (status != ERROR_SUCCESS) { #if CLUSTER_BETA ClRtlLogPrint(LOG_NOISE, "[NM] Failed to read cluster " "%1!ws! value, status %2!u!. " "Using default value ...\n", CLUSREG_NAME_CLUSTER_DISABLE_MULTICAST, status ); #endif // CLUSTER_BETA } else if (type != REG_DWORD) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Unexpected type (%1!u!) for cluster " "%2!ws!, status %3!u!. " "Using default value ...\n", type, CLUSREG_NAME_CLUSTER_DISABLE_MULTICAST, status ); } else { found = TRUE; } } } } // // Return what we found. If we didn't find anything, // return the default. // if (found) { *Disabled = disabled; } else { *Disabled = NMP_MCAST_DISABLED_DEFAULT; } *NetworkKey = networkKey; networkKey = NULL; *NetworkParametersKey = netParamKey; netParamKey = NULL; *ClusterParametersKey = clusParamKey; clusParamKey = NULL; // // Even if we didn't find anything, we return success // because we have a default. Note that we return error // if a fundamental operation (such as locating the // network key) failed. // status = ERROR_SUCCESS; error_exit: if (openedClusParamKey && clusParamKey != NULL) { DmCloseKey(clusParamKey); clusParamKey = NULL; } if (openedNetParamKey && netParamKey != NULL) { DmCloseKey(netParamKey); netParamKey = NULL; } if (openedNetworkKey && networkKey != NULL) { DmCloseKey(networkKey); networkKey = NULL; } return(status); } // NmpQueryMulticastDisabled DWORD NmpQueryMulticastConfigType( IN PNM_NETWORK Network, IN HDMKEY NetworkKey, IN OUT HDMKEY * NetworkParametersKey, OUT NM_MCAST_CONFIG * ConfigType ) /*++ Routine Description: Reads the multicast config type from the cluster database. --*/ { LPCWSTR networkId = OmObjectId(Network); HDMKEY netParamKey = NULL; BOOLEAN openedNetParamKey = FALSE; DWORD type; DWORD len = sizeof(*ConfigType); DWORD status; #if CLUSTER_BETA ClRtlLogPrint(LOG_NOISE, "[NM] Querying multicast address config type for " "network %1!ws! from cluster database.\n", networkId ); #endif // CLUSTER_BETA if (Network == NULL || NetworkKey == NULL) { status = ERROR_INVALID_PARAMETER; goto error_exit; } // // Open the network parameters key, if necessary. // netParamKey = *NetworkParametersKey; if (netParamKey == NULL) { netParamKey = DmOpenKey( NetworkKey, CLUSREG_KEYNAME_PARAMETERS, MAXIMUM_ALLOWED ); if (netParamKey == NULL) { status = GetLastError(); ClRtlLogPrint(LOG_NOISE, "[NM] Failed to find Parameters key " "for network %1!ws!, status %2!u!. Using default " "multicast parameters.\n", networkId, status ); goto error_exit; } else { openedNetParamKey = TRUE; } } // // Read the config type. // status = DmQueryValue( netParamKey, CLUSREG_NAME_NET_MCAST_CONFIG_TYPE, &type, (LPBYTE) ConfigType, &len ); if (status == ERROR_SUCCESS) { if (type != REG_DWORD) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Unexpected type (%1!u!) for network " "%2!ws! %3!ws!, status %4!u!. Checking " "cluster parameters ...\n", type, networkId, CLUSREG_NAME_NET_MCAST_CONFIG_TYPE, status ); status = ERROR_DATATYPE_MISMATCH; goto error_exit; } } else { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to query network %1!ws! %2!ws! " "from cluster database, status %3!u!.\n", networkId, CLUSREG_NAME_NET_MCAST_CONFIG_TYPE, status ); goto error_exit; } *NetworkParametersKey = netParamKey; netParamKey = NULL; #if CLUSTER_BETA ClRtlLogPrint(LOG_NOISE, "[NM] Found multicast address config type %1!u! " "for network %2!ws! in cluster database.\n", *ConfigType, networkId ); #endif // CLUSTER_BETA error_exit: if (openedNetParamKey && netParamKey != NULL) { DmCloseKey(netParamKey); netParamKey = NULL; } return(status); } // NmpQueryMulticastConfigType DWORD NmpMulticastNotifyConfigChange( IN PNM_NETWORK Network, IN HDMKEY NetworkKey, IN OUT HDMKEY * NetworkParametersKey, IN PNM_NETWORK_MULTICAST_PARAMETERS Parameters, IN PVOID PropBuffer, IN DWORD PropBufferSize ) /*++ Routine Description: Notify other cluster nodes of the new multicast configuration parameters by initiating a GUM update. If this is a manual update, there may be other properties to distribute in the GUM update. Notes: Cannot be called with NM lock held. --*/ { DWORD status = ERROR_SUCCESS; LPCWSTR networkId = OmObjectId(Network); PNM_NETWORK_MULTICAST_UPDATE update = NULL; DWORD updateSize = 0; ClRtlLogPrint(LOG_NOISE, "[NM] Notifying other nodes of type %1!u! multicast " "reconfiguration for network %2!ws!.\n", Parameters->ConfigType, networkId ); status = NmpMulticastCreateUpdateFromParameters( Network, Parameters, &update, &updateSize ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to build GUM update for " "multicast configuration of network %1!ws!, " "status %2!u!.\n", networkId, status ); goto error_exit; } // // BUGBUG: Disseminate database updates to downlevel // nodes! // // // Send junk if the prop buffer is empty. // if (PropBuffer == NULL || PropBufferSize == 0) { PropBuffer = &updateSize; PropBufferSize = sizeof(updateSize); } // // Send the update. // status = GumSendUpdateEx( GumUpdateMembership, NmUpdateSetNetworkMulticastConfiguration, 4, NM_WCSLEN(networkId), networkId, updateSize, update, PropBufferSize, PropBuffer, sizeof(PropBufferSize), &PropBufferSize ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to send GUM update for " "multicast configuration of network %1!ws!, " "status %2!u!.\n", networkId, status ); goto error_exit; } error_exit: if (update != NULL) { MIDL_user_free(update); update = NULL; } return(status); } // NmpMulticastNotifyConfigChange DWORD NmpWriteMulticastParameters( IN PNM_NETWORK Network, IN HDMKEY NetworkKey, IN HDMKEY NetworkParametersKey, IN HLOCALXSACTION Xaction, IN PNM_NETWORK_MULTICAST_PARAMETERS Parameters ) { DWORD status = ERROR_SUCCESS; LPCWSTR networkId = OmObjectId(Network); LPWSTR failValueName = NULL; CL_ASSERT(NetworkKey != NULL); CL_ASSERT(NetworkParametersKey != NULL); CL_ASSERT(Xaction != NULL); #if CLUSTER_BETA ClRtlLogPrint(LOG_NOISE, "[NM] Writing multicast parameters for " "network %1!ws! to cluster database.\n", networkId ); #endif // CLUSTER_BETA // // Address. // if (Parameters->Address != NULL) { status = DmLocalSetValue( Xaction, NetworkParametersKey, CLUSREG_NAME_NET_MULTICAST_ADDRESS, REG_SZ, (BYTE *) Parameters->Address, NM_WCSLEN(Parameters->Address) ); if (status != ERROR_SUCCESS) { failValueName = CLUSREG_NAME_NET_MULTICAST_ADDRESS; goto error_exit; } } // // Lease server address. // if (Parameters->LeaseServer != NULL) { status = DmLocalSetValue( Xaction, NetworkParametersKey, CLUSREG_NAME_NET_MCAST_SERVER_ADDRESS, REG_SZ, (BYTE *) Parameters->LeaseServer, NM_WCSLEN(Parameters->LeaseServer) ); if (status != ERROR_SUCCESS) { failValueName = CLUSREG_NAME_NET_MCAST_SERVER_ADDRESS; goto error_exit; } } // // Client request id. // if (Parameters->LeaseRequestId.ClientUID != NULL && Parameters->LeaseRequestId.ClientUIDLength > 0) { status = DmLocalSetValue( Xaction, NetworkParametersKey, CLUSREG_NAME_NET_MCAST_REQUEST_ID, REG_BINARY, (BYTE *) Parameters->LeaseRequestId.ClientUID, Parameters->LeaseRequestId.ClientUIDLength ); if (status != ERROR_SUCCESS) { failValueName = CLUSREG_NAME_NET_MCAST_REQUEST_ID; goto error_exit; } } // // Lease obtained. // status = DmLocalSetValue( Xaction, NetworkParametersKey, CLUSREG_NAME_NET_MCAST_LEASE_OBTAINED, REG_DWORD, (BYTE *) &(Parameters->LeaseObtained), sizeof(Parameters->LeaseObtained) ); if (status != ERROR_SUCCESS) { failValueName = CLUSREG_NAME_NET_MCAST_LEASE_OBTAINED; goto error_exit; } // // Lease expires. // status = DmLocalSetValue( Xaction, NetworkParametersKey, CLUSREG_NAME_NET_MCAST_LEASE_EXPIRES, REG_DWORD, (BYTE *) &(Parameters->LeaseExpires), sizeof(Parameters->LeaseExpires) ); if (status != ERROR_SUCCESS) { failValueName = CLUSREG_NAME_NET_MCAST_LEASE_EXPIRES; goto error_exit; } // // Config type. // status = DmLocalSetValue( Xaction, NetworkParametersKey, CLUSREG_NAME_NET_MCAST_CONFIG_TYPE, REG_DWORD, (BYTE *) &(Parameters->ConfigType), sizeof(Parameters->ConfigType) ); if (status != ERROR_SUCCESS) { failValueName = CLUSREG_NAME_NET_MCAST_CONFIG_TYPE; goto error_exit; } error_exit: if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to write %1!ws! value " "for network %2!ws!, status %3!u!.\n", failValueName, networkId, status ); } return(status); } // NmpWriteMulticastParameters DWORD NmpMulticastEnumerateScopes( IN BOOLEAN Requery, OUT PMCAST_SCOPE_ENTRY * ScopeList, OUT DWORD * ScopeCount ) /*++ Routine Description: Call MADCAP API to enumerate multicast scopes. --*/ { DWORD status; PMCAST_SCOPE_ENTRY scopeList = NULL; DWORD scopeListLength; DWORD scopeCount = 0; // // Initialize MADCAP, if not done already. // if (!NmpMadcapClientInitialized) { DWORD madcapVersion = MCAST_API_CURRENT_VERSION; status = McastApiStartup(&madcapVersion); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to initialize MADCAP API, " "status %1!u!.\n", status ); return(status); } NmpMadcapClientInitialized = TRUE; } // // Enumerate the multicast scopes. // scopeList = NULL; scopeListLength = 0; do { PVOID watchdogHandle; // // Set watchdog timer to try to catch bug 400242. Specify // timeout of 5 minutes (in milliseconds). // watchdogHandle = ClRtlSetWatchdogTimer( 5 * 60 * 1000, L"McastEnumerateScopes (Bug 400242)" ); if (watchdogHandle == NULL) { ClRtlLogPrint(LOG_NOISE, "[NM] Failed to set %1!u!ms watchdog timer for " "McastEnumerateScopes.\n", 5 * 60 * 1000 ); } status = McastEnumerateScopes( AF_INET, Requery, scopeList, &scopeListLength, &scopeCount ); // // Cancel watchdog timer. // if (watchdogHandle != NULL) { ClRtlCancelWatchdogTimer(watchdogHandle); } if ( (scopeList == NULL && status == ERROR_SUCCESS) || (status == ERROR_MORE_DATA) ) { if (scopeList != NULL) { LocalFree(scopeList); } scopeList = LocalAlloc(LMEM_FIXED, scopeListLength); if (scopeList == NULL) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to allocate multicast scope list " "of length %1!u!.\n", scopeListLength ); status = ERROR_NOT_ENOUGH_MEMORY; break; } else { // // Call McastEnumerateScopes again with proper // size scopeList buffer. // Requery = FALSE; continue; } } else { // // McastEnumerateScopes failed with an unexpected // error. Bail out. // break; } } while (TRUE); if (status != ERROR_SUCCESS) { if (scopeList != NULL) { LocalFree(scopeList); scopeList = NULL; scopeCount = 0; } } *ScopeList = scopeList; *ScopeCount = scopeCount; return(status); } // NmpMulticastEnumerateScopes _inline DWORD NmpMadcapTimeToNmTime( IN time_t MadcapTime ) /*++ Routine Description: Convert MADCAP (DHCP) time into NM time. MADCAP time is a time_t and in seconds. NM time is a DWORD in milliseconds. Arguments: MadcapTime - MADCAP time Return value: Converted NM time, or MAXULONG if converted NM time would overflow. --*/ { LARGE_INTEGER product, limit; product.QuadPart = (ULONG) MadcapTime; product = RtlExtendedIntegerMultiply(product, 1000); limit.QuadPart = MAXULONG; if (product.QuadPart > limit.QuadPart) { return(MAXULONG); } else { return(product.LowPart); } } // NmpMadcapTimeToNmTime DWORD NmpRandomizeTimeout( IN PNM_NETWORK Network, IN DWORD BaseTimeout, IN DWORD Window, IN DWORD MinTimeout, IN DWORD MaxTimeout, IN BOOLEAN FavorNmLeader ) /*++ Routine Description: General-purpose routine to randomize a timeout value so that timers (probably) do not expire at the same time on multiple nodes. The randomized timeout will fall within Window on either side of BaseTimeout. If possible, Window is extended only above BaseTimeout, but it will be extended below if BaseTimeout + Window > MaxTimeout. If FavorNmLeader is TRUE, the NM leader is assigned the earliest timeout (within Window). Arguments: Network - network BaseTimeout - time when lease should be renewed Window - period after (or before) BaseTimeout during which timeout can be set MaxTimeout - maximum allowable timeout MinTimeout - minimum allowable timeout FavorNmLeader - if TRUE, NM leader is assigned the earliest timeout Return value: Randomized timeout. --*/ { DWORD status; DWORD result = 0; DWORD * pOffset = NULL; DWORD topWindow, bottomWindow, adjustedWindow; DWORD adjustedBase; DWORD offset, interval; if (MinTimeout > MaxTimeout) { result = BaseTimeout; goto error_exit; } if (MaxTimeout == 0) { result = 0; goto error_exit; } // Adjust the base so that it is between the min and max. adjustedBase = BaseTimeout; if (MaxTimeout < BaseTimeout) { adjustedBase = MaxTimeout; } else if (MinTimeout > adjustedBase) { adjustedBase = MinTimeout; } // If the Window is zero, we're done. if (Window == 0) { result = adjustedBase; goto error_exit; } // // Position the window. If necessary, we will extend // below and/or above the adjusted base. // - topWindow: amount window extends above adjusted base // - bottomWindow: amount window extends below adjusted base // if (Window < MaxTimeout - adjustedBase) { // There is enough room above the adjusted base // for the window. topWindow = Window; bottomWindow = 0; adjustedWindow = Window; } else { // Because of the max, the window pushes below // the adjusted base. topWindow = MaxTimeout - adjustedBase; bottomWindow = Window - topWindow; // Make sure the window doesn't extend below // the min. if (bottomWindow > adjustedBase || adjustedBase - bottomWindow < MinTimeout) { // The window extends below the min. Set // the bottom of the window to be the min. bottomWindow = adjustedBase - MinTimeout; } // Adjusted window. adjustedWindow = topWindow - bottomWindow; // Adjust the base to the bottom of the window (and // recall that bottomWindow is an offset relative // to the current adjusted base). adjustedBase -= bottomWindow; } // // Check if the NM leader gets first dibs. // if (FavorNmLeader && NmpLeaderNodeId == NmLocalNodeId) { result = adjustedBase; goto error_exit; } // // Use a random number to choose an offset into the window. // status = NmpCreateRandomNumber(&pOffset, sizeof(*pOffset)); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to create random offset while " "randomizing timeout for network %1!ws!, " "status %2!u!.\n", OmObjectId(Network), status ); // // Default to intervals based on node id // interval = (adjustedWindow > ClusterDefaultMaxNodes) ? (adjustedWindow / ClusterDefaultMaxNodes) : 1; offset = (NmLocalNodeId * interval) % adjustedWindow; } else { offset = (*pOffset) % adjustedWindow; } result = adjustedBase + offset; error_exit: if (pOffset != NULL) { LocalFree(pOffset); pOffset = NULL; } return(result); } // NmpRandomizeTimeout DWORD NmpCalculateLeaseRenewTime( IN PNM_NETWORK Network, IN NM_MCAST_CONFIG ConfigType, IN OUT time_t * LeaseObtained, IN OUT time_t * LeaseExpires ) /*++ Routine Description: Determines when to schedule a lease renewal, based on the lease obtained and expires times and whether the current lease was obtained from a MADCAP server. If the lease was obtained from a MADCAP server, the policy mimics DHCP client renewal behavior. A renewal is scheduled for half the time until the lease expires. However, if the lease half-life is less than the renewal threshold, renew at the lease expiration time. If the address was selected after a MADCAP timeout, we still periodically query to make sure a MADCAP server doesn't suddenly appear on the network. In this case, LeaseExpires and LeaseObtained will be garbage, and we need to fill them in. If the address was configured by an administrator, return 0, indicating that the timer should not be set. Return value: Relative NM ticks from current time that lease renewal should be scheduled. --*/ { time_t currentTime; time_t leaseExpires; time_t leaseObtained; time_t result = 0; time_t window = 0; time_t leaseHalfLife = 0; DWORD dwResult = 0; DWORD dwWindow = 0; currentTime = time(NULL); leaseExpires = *LeaseExpires; leaseObtained = *LeaseObtained; switch (ConfigType) { case NmMcastConfigManual: dwResult = 0; *LeaseObtained = 0; *LeaseExpires = 0; break; case NmMcastConfigMadcap: if (leaseExpires < currentTime) { result = 1; } else if (leaseExpires <= leaseObtained) { result = 1; } else { leaseHalfLife = (leaseExpires - leaseObtained) / 2; if (leaseHalfLife < NMP_MCAST_LEASE_RENEWAL_THRESHOLD) { // The half life is too small. result = leaseExpires - currentTime; if (result == 0) { result = 1; } window = result / 2; } else { // The half life is acceptable. result = leaseHalfLife; window = NMP_MCAST_LEASE_RENEWAL_WINDOW; if (result + window > leaseExpires) { window = leaseExpires - result; } } } break; case NmMcastConfigAuto: result = NMP_MADCAP_REQUERY_PERDIOD; window = NMP_MCAST_LEASE_RENEWAL_WINDOW; // // Return the lease expiration time to be // written into the cluster database. // *LeaseObtained = currentTime; *LeaseExpires = currentTime + NMP_MADCAP_REQUERY_PERDIOD; break; default: CL_ASSERT(FALSE); result = 0; break; } // // Randomize the timeout so that all nodes don't // try to renew at the same time. If the config // type was manual, do not randomize -- leave the // timeout at zero so that it is cleared. // if (ConfigType != NmMcastConfigManual) { dwResult = NmpRandomizeTimeout( Network, NmpMadcapTimeToNmTime(result), NmpMadcapTimeToNmTime(window), 1000, // one second MAXULONG, TRUE ); } return(dwResult); } // NmpCalculateLeaseRenewTime VOID NmpReportMulticastAddressLease( IN PNM_NETWORK Network, IN PNM_NETWORK_MULTICAST_PARAMETERS Parameters, IN LPWSTR OldAddress ) /*++ Routine Description: Write an event log entry, if not repetitive, reporting that a multicast address lease was obtained. The repetitive criteria is that the address changed. --*/ { BOOLEAN writeLogEntry = FALSE; LPCWSTR nodeName; LPCWSTR networkName; if (Parameters->Address == NULL || Parameters->LeaseServer == NULL || Network == NULL || Network->LocalInterface == NULL) { return; } if (OldAddress == NULL || wcscmp(Parameters->Address, OldAddress) != 0) { networkName = OmObjectName(Network); nodeName = OmObjectName(Network->LocalInterface->Node); ClusterLogEvent4( LOG_NOISE, LOG_CURRENT_MODULE, __FILE__, __LINE__, NM_EVENT_OBTAINED_MULTICAST_LEASE, 0, NULL, nodeName, Parameters->Address, networkName, Parameters->LeaseServer ); } return; } // NmpReportMulticastAddressLease VOID NmpReportMulticastAddressChoice( IN PNM_NETWORK Network, IN LPWSTR Address, IN LPWSTR OldAddress ) /*++ Routine Description: Write an event log entry, if not repetitive, reporting that a multicast address was automatically selected for this network. The repetitive criteria is that our previous config type was anything other than automatic selection or the chosen address is different. Notes: Must not be called with NM lock held. --*/ { DWORD status; LPCWSTR networkId = OmObjectId(Network); HDMKEY networkKey = NULL; HDMKEY netParamKey = NULL; NM_MCAST_CONFIG configType; BOOLEAN writeLogEntry = FALSE; LPCWSTR nodeName; LPCWSTR networkName; if (Address == NULL || Network == NULL || Network->LocalInterface == NULL ) { writeLogEntry = FALSE; goto error_exit; } if (OldAddress == NULL || wcscmp(Address, OldAddress) != 0) { writeLogEntry = TRUE; } if (!writeLogEntry) { // // Open the network key. // networkKey = DmOpenKey( DmNetworksKey, networkId, MAXIMUM_ALLOWED ); if (networkKey == NULL) { status = GetLastError(); ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to open key for network %1!ws!, " "status %2!u!\n", networkId, status ); goto error_exit; } status = NmpQueryMulticastConfigType( Network, networkKey, &netParamKey, &configType ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to query multicast config type " "for network %1!ws!, status %2!u!\n", networkId, status ); goto error_exit; } if (configType != NmMcastConfigAuto) { writeLogEntry = TRUE; } } if (writeLogEntry) { networkName = OmObjectName(Network); nodeName = OmObjectName(Network->LocalInterface->Node); CsLogEvent3( LOG_NOISE, NM_EVENT_MULTICAST_ADDRESS_CHOICE, nodeName, Address, networkName ); } error_exit: if (networkKey != NULL) { DmCloseKey(networkKey); networkKey = NULL; } if (netParamKey != NULL) { DmCloseKey(netParamKey); netParamKey = NULL; } return; } // NmpReportMulticastAddressChoice VOID NmpReportMulticastAddressFailure( IN PNM_NETWORK Network, IN DWORD Error ) /*++ Routine Description: Write an event log entry reporting failure to obtain a multicast address for specified network with specified error. --*/ { LPCWSTR nodeName; LPCWSTR networkName; WCHAR errorString[12]; if (Network == NULL || Network->LocalInterface == NULL) { return; } nodeName = OmObjectName(Network->LocalInterface->Node); networkName = OmObjectName(Network); wsprintfW(&(errorString[0]), L"%u", Error); CsLogEvent3( LOG_UNUSUAL, NM_EVENT_MULTICAST_ADDRESS_FAILURE, nodeName, networkName, errorString ); return; } // NmpReportMulticastAddressFailure DWORD NmpGetMulticastAddressSelectionRange( IN PNM_NETWORK Network, IN HDMKEY NetworkKey, IN OUT HDMKEY * NetworkParametersKey, OUT ULONG * RangeLower, OUT ULONG * RangeUpper ) /*++ Routine Description: Queries the cluster database to determine if a selection range has been configured. If both lower and upper bounds of range are valid, returns that range. Otherwise, returns default range. Notes: Must not be called with NM lock held. --*/ { DWORD status; LPCWSTR networkId = OmObjectId(Network); HDMKEY netParamKey = NULL; BOOLEAN openedNetParamKey = FALSE; LPWSTR addr = NULL; DWORD addrLen; DWORD size; DWORD hllower, hlupper; if (Network == NULL || NetworkKey == NULL) { status = ERROR_INVALID_PARAMETER; goto error_exit; } #if CLUSTER_BETA ClRtlLogPrint(LOG_NOISE, "[NM] Querying multicast address selection range " "for network %1!ws! from cluster database.\n", networkId ); #endif // CLUSTER_BETA // // Open the network parameters key, if necessary. // netParamKey = *NetworkParametersKey; if (netParamKey == NULL) { netParamKey = DmOpenKey( NetworkKey, CLUSREG_KEYNAME_PARAMETERS, MAXIMUM_ALLOWED ); if (netParamKey == NULL) { status = GetLastError(); #if CLUSTER_BETA ClRtlLogPrint(LOG_NOISE, "[NM] Failed to find Parameters key " "for network %1!ws!, status %2!u!. " "Using default multicast address range.\n", networkId, status ); #endif // CLUSTER_BETA goto usedefault; } else { openedNetParamKey = TRUE; } } // // Query for the lower bound of the range. // addr = NULL; addrLen = 0; size = 0; status = NmpQueryString( netParamKey, CLUSREG_NAME_NET_MCAST_RANGE_LOWER, REG_SZ, &addr, &addrLen, &size ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_NOISE, "[NM] Failed to read lower bound of " "multicast address selection range for " "network %1!ws! from cluster database, " "status %2!u!. Using default.\n", networkId, status ); goto usedefault; } status = ClRtlTcpipStringToAddress(addr, RangeLower); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_NOISE, "[NM] Failed to convert lower bound of " "multicast address selection range %1!ws! for " "network %2!ws! into TCP/IP address, " "status %3!u!. Using default.\n", addr, networkId, status ); goto usedefault; } hllower = ntohl(*RangeLower); if (!IN_CLASSD(hllower)) { ClRtlLogPrint(LOG_NOISE, "[NM] Lower bound of multicast address " "selection range %1!ws! for network %2!ws! " "is not a class D IPv4 address. " "Using default.\n", addr, networkId ); goto usedefault; } // // Query for the upper bound of the range. // size = 0; status = NmpQueryString( netParamKey, CLUSREG_NAME_NET_MCAST_RANGE_UPPER, REG_SZ, &addr, &addrLen, &size ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_NOISE, "[NM] Failed to read upper bound of " "multicast address selection range for " "network %1!ws! from cluster database, " "status %2!u!. Using default.\n", networkId, status ); goto usedefault; } status = ClRtlTcpipStringToAddress(addr, RangeUpper); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_NOISE, "[NM] Failed to convert upper bound of " "multicast address selection range %1!ws! for " "network %2!ws! into TCP/IP address, " "status %3!u!. Using default.\n", addr, networkId, status ); goto usedefault; } hlupper = ntohl(*RangeUpper); if (!IN_CLASSD(hlupper)) { ClRtlLogPrint(LOG_NOISE, "[NM] Upper bound of multicast address " "selection range %1!ws! for network %2!ws! " "is not a class D IPv4 address. " "Using default.\n", addr, networkId ); goto usedefault; } // // Make sure it's a legitimate range. // if (hllower >= hlupper) { ClRtlLogPrint(LOG_NOISE, "[NM] Multicast address selection range " "[%1!u!.%2!u!.%3!u!.%4!u!, %5!u!.%6!u!.%7!u!.%8!u!] " "for network %2!ws! is not valid. " "Using default.\n", NmpIpAddrPrintArgs(*RangeLower), NmpIpAddrPrintArgs(*RangeUpper), networkId ); goto usedefault; } status = ERROR_SUCCESS; goto error_exit; usedefault: *RangeLower = NMP_MCAST_DEFAULT_RANGE_LOWER; *RangeUpper = NMP_MCAST_DEFAULT_RANGE_UPPER; status = ERROR_SUCCESS; error_exit: if (status == ERROR_SUCCESS) { ClRtlLogPrint(LOG_NOISE, "[NM] Using multicast address selection range " "[%1!u!.%2!u!.%3!u!.%4!u!, %5!u!.%6!u!.%7!u!.%8!u!] " "for network %9!ws! in cluster database.\n", NmpIpAddrPrintArgs(*RangeLower), NmpIpAddrPrintArgs(*RangeUpper), networkId ); *NetworkParametersKey = netParamKey; netParamKey = NULL; } if (openedNetParamKey && netParamKey != NULL) { DmCloseKey(netParamKey); netParamKey = NULL; } if (addr != NULL) { MIDL_user_free(addr); addr = NULL; } return(status); } // NmpGetMulticastAddressSelectionRange DWORD NmpMulticastExcludeRange( IN OUT PLIST_ENTRY SelectionRange, IN DWORD HlLower, IN DWORD HlUpper ) /*++ Routine Description: Exclude range defined by (HlLower, HlUpper) from list of selection intervals in SelectionRange. Arguments: SelectionRange - sorted list of non-overlapping selection intervals HlLower - lower bound of exclusion in host format HlUpper - upper bound of exclusion in host format --*/ { PNM_MCAST_RANGE_INTERVAL interval; PNM_MCAST_RANGE_INTERVAL newInterval; PLIST_ENTRY entry; // Determine if the exclusion overlaps with any interval. for (entry = SelectionRange->Flink; entry != SelectionRange; entry = entry->Flink) { interval = CONTAINING_RECORD( entry, NM_MCAST_RANGE_INTERVAL, Linkage ); if (HlLower < interval->hlLower && HlUpper < interval->hlUpper) { // Exclusion completely misses below interval. // Since list is sorted, there is no possibility // of a matching interval farther down list. break; } else if (HlLower > interval->hlUpper) { // Exclusion completely misses above interval. // There might be matching intervals later // in sorted list. } else if (HlLower <= interval->hlLower && HlUpper >= interval->hlUpper) { // Exclusion completely covers interval. // Remove interval. RemoveEntryList(entry); } else if (HlLower > interval->hlLower && HlUpper < interval->hlUpper) { // Exclusion splits interval. newInterval = LocalAlloc(LMEM_FIXED, sizeof(*newInterval)); if (newInterval == NULL) { return(ERROR_NOT_ENOUGH_MEMORY); } newInterval->hlLower = HlUpper+1; newInterval->hlUpper = interval->hlUpper; interval->hlUpper = HlLower-1; // Insert the new interval after the current interval InsertHeadList(entry, &newInterval->Linkage); // We can skip the new interval because we already // know how it compares to the exclusion. entry = &newInterval->Linkage; continue; } else if (HlLower <= interval->hlLower) { // Exclusion overlaps lower part of interval. Shrink // interval from below. interval->hlLower = HlUpper + 1; } else { // Exclusion overlaps upper part of interval. Shrink // interval from above. interval->hlUpper = HlLower - 1; } } return(ERROR_SUCCESS); } // NmpMulticastExcludeRange BOOLEAN NmpMulticastAddressInRange( IN PLIST_ENTRY SelectionRange, IN LPWSTR McastAddress ) /*++ Routine Description: Determines if McastAddress is in one of range intervals. --*/ { DWORD mcastAddress; PNM_MCAST_RANGE_INTERVAL interval; PLIST_ENTRY entry; // Convert the address from a string into an address. if (ClRtlTcpipStringToAddress( McastAddress, &mcastAddress ) != ERROR_SUCCESS) { return(FALSE); } mcastAddress = ntohl(mcastAddress); // Walk the list of intervals. for (entry = SelectionRange->Flink; entry != SelectionRange; entry = entry->Flink) { interval = CONTAINING_RECORD( entry, NM_MCAST_RANGE_INTERVAL, Linkage ); if (mcastAddress >= interval->hlLower && mcastAddress <= interval->hlUpper) { return(TRUE); } else if (mcastAddress < interval->hlLower) { // Address is below current interval. // Since interval list is sorted in // increasing order, there is no chance // of a match later in list. break; } } return(FALSE); } // NmpMulticastAddressInRange DWORD NmpMulticastAddressRangeSize( IN PLIST_ENTRY SelectionRange ) /*++ Routine Description: Returns size of selection range. --*/ { PNM_MCAST_RANGE_INTERVAL interval; PLIST_ENTRY entry; DWORD size = 0; // Walk the list of intervals. for (entry = SelectionRange->Flink; entry != SelectionRange; entry = entry->Flink) { interval = CONTAINING_RECORD( entry, NM_MCAST_RANGE_INTERVAL, Linkage ); size += NmpMulticastRangeIntervalSize(interval); } return(size); } // NmpMulticastAddressRangeSize DWORD NmpMulticastRangeOffsetToAddress( IN PLIST_ENTRY SelectionRange, IN DWORD Offset ) /*++ Routine Description: Returns the address that is Offset into the SelectionRange. The address is returned in host format. If SelectionRange is empty, returns 0. If Offset falls outside of non-empty range, returns upper or lower boundary of selection range. --*/ { PNM_MCAST_RANGE_INTERVAL interval; PLIST_ENTRY entry; DWORD address = 0; // Walk the list of intervals. for (entry = SelectionRange->Flink; entry != SelectionRange; entry = entry->Flink) { interval = CONTAINING_RECORD( entry, NM_MCAST_RANGE_INTERVAL, Linkage ); address = interval->hlLower; if (address + Offset <= interval->hlUpper) { address = address + Offset; break; } else { address = interval->hlUpper; Offset -= NmpMulticastRangeIntervalSize(interval); } } return(address); } // NmpMulticastRangeOffsetToAddress VOID NmpMulticastFreeSelectionRange( IN PLIST_ENTRY SelectionRange ) { PNM_MCAST_RANGE_INTERVAL interval; PLIST_ENTRY entry; while (!IsListEmpty(SelectionRange)) { entry = RemoveHeadList(SelectionRange); interval = CONTAINING_RECORD( entry, NM_MCAST_RANGE_INTERVAL, Linkage ); LocalFree(interval); } return; } // NmpMulticastFreeSelectionRange DWORD NmpChooseMulticastAddress( IN PNM_NETWORK Network, OUT PNM_NETWORK_MULTICAST_PARAMETERS Parameters ) /*++ Routine Description: Choose a default multicast address and fill in Parameters appropriately. If there is already a valid multicast address in the selection range stored in the cluster database, continue to use it. If there is not already a valid multicast address, choose an address within the multicast address range by hashing on the last few bytes of the network id GUID. Arguments: Network - network address is being chosen for Parameters - configuration parameters with new address --*/ { LPCWSTR networkId = OmObjectId(Network); DWORD status = ERROR_SUCCESS; HDMKEY networkKey = NULL; HDMKEY netParamKey = NULL; PMCAST_SCOPE_ENTRY scopeList = NULL; DWORD scopeCount; LIST_ENTRY selectionRange; PNM_MCAST_RANGE_INTERVAL interval; DWORD index; DWORD hlLower; DWORD hlUpper; DWORD networkAddress; DWORD networkSubnet; UUID networkIdGuid; DWORD rangeSize; DWORD offset; DWORD address; LPWSTR mcastAddress = NULL; DWORD mcastAddressLength = 0; InitializeListHead(&selectionRange); ClRtlLogPrint(LOG_NOISE, "[NM] Choosing multicast address for " "network %1!ws!.\n", networkId ); networkKey = DmOpenKey( DmNetworksKey, networkId, MAXIMUM_ALLOWED ); if (networkKey == NULL) { status = GetLastError(); ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to open key for network %1!ws!, " "status %2!u!\n", networkId, status ); goto error_exit; } // // Build an array of selection intervals. These are intervals // in the IPv4 class D address space from which an address // can be selected. // // Start with the entire range. interval = LocalAlloc(LMEM_FIXED, sizeof(*interval)); if (interval == NULL) { status = ERROR_NOT_ENOUGH_MEMORY; goto error_exit; } InsertHeadList(&selectionRange, &interval->Linkage); // // Get the selection range. // status = NmpGetMulticastAddressSelectionRange( Network, networkKey, &netParamKey, &interval->hlLower, &interval->hlUpper ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to determine multicast " "address selection range for network %1!ws!, " "status %2!u!.\n", networkId, status ); goto error_exit; } interval->hlLower = ntohl(interval->hlLower); interval->hlUpper = ntohl(interval->hlUpper); // // Process exclusions from the multicast address // selection range, starting with well-known exclusions. // for (index = 0; index < NmpMulticastRestrictedRangeCount; index++) { ClRtlLogPrint(LOG_NOISE, "[NM] Excluding %1!ws! " "[%2!u!.%3!u!.%4!u!.%5!u!, %6!u!.%7!u!.%8!u!.%9!u!] " "from multicast address range for network %10!ws!.\n", NmpMulticastRestrictedRange[index].Description, NmpIpAddrPrintArgs(NmpMulticastRestrictedRange[index].Lower), NmpIpAddrPrintArgs(NmpMulticastRestrictedRange[index].Upper), networkId ); // Convert the exclusion to host format. hlLower = ntohl(NmpMulticastRestrictedRange[index].Lower); hlUpper = ntohl(NmpMulticastRestrictedRange[index].Upper); NmpMulticastExcludeRange(&selectionRange, hlLower, hlUpper); // If the selection range is now empty, there is no point // in examining other exclusions. if (IsListEmpty(&selectionRange)) { status = ERROR_INCORRECT_ADDRESS; goto error_exit; } } // // Process multicast scopes as exclusions. Specifically, any // scope whose interface matches this network is excluded // because it is conceivable that machines on the network are // already using addresses in these scopes. // status = ClRtlTcpipStringToAddress( Network->Address, &networkAddress ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to convert network address string " "%1!ws! into an IPv4 address, status %2!u!.\n", Network->Address, status ); goto error_exit; } status = ClRtlTcpipStringToAddress( Network->AddressMask, &networkSubnet ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to convert network address mask string " "%1!ws! into an IPv4 address, status %2!u!.\n", Network->AddressMask, status ); goto error_exit; } // // Query multicast scopes to determine if we should // exclude any addresses from the selection range. // status = NmpMulticastEnumerateScopes( FALSE, // do not force requery &scopeList, &scopeCount ); if (status != ERROR_SUCCESS) { scopeCount = 0; } for (index = 0; index < scopeCount; index++) { if (ClRtlAreTcpipAddressesOnSameSubnet( networkAddress, scopeList[index].ScopeCtx.Interface.IpAddrV4, networkSubnet )) { ClRtlLogPrint(LOG_NOISE, "[NM] Excluding MADCAP scope " "[%1!u!.%2!u!.%3!u!.%4!u!, %5!u!.%6!u!.%7!u!.%8!u!] " "from multicast address selection range for " "network %9!ws!.\n", NmpIpAddrPrintArgs(scopeList[index].ScopeCtx.ScopeID.IpAddrV4), NmpIpAddrPrintArgs(scopeList[index].LastAddr.IpAddrV4), networkId ); hlLower = ntohl(scopeList[index].ScopeCtx.ScopeID.IpAddrV4); hlUpper = ntohl(scopeList[index].LastAddr.IpAddrV4); NmpMulticastExcludeRange(&selectionRange, hlLower, hlUpper); // If the selection range is empty, there is no point // in examining other exclusions. if (IsListEmpty(&selectionRange)) { status = ERROR_INCORRECT_ADDRESS; goto error_exit; } } } // // The range of intervals from which we can select an // address is now constructed. // // Before choosing an address, see if there is already an // old one in the database that matches the selection range. // status = NmpQueryMulticastAddress( Network, networkKey, &netParamKey, &mcastAddress, &mcastAddressLength ); if (status == ERROR_SUCCESS) { // // We found an address. See if it falls in the range. // if (!NmpMulticastAddressInRange(&selectionRange, mcastAddress)) { // // We can't use this address. Free the string. // MIDL_user_free(mcastAddress); mcastAddress = NULL; } } else { mcastAddress = NULL; } if (mcastAddress == NULL) { // // Calculate the size of the selection range. // rangeSize = NmpMulticastAddressRangeSize(&selectionRange); // // Calculate the range offset using the last DWORD of // the network id GUID. // status = UuidFromString((LPWSTR)networkId, &networkIdGuid); if (status == RPC_S_OK) { offset = (*((PDWORD)&(networkIdGuid.Data4[4]))) % rangeSize; } else { offset = 0; } // // Choose an address within the specified range. // address = NmpMulticastRangeOffsetToAddress(&selectionRange, offset); CL_ASSERT(address != 0); CL_ASSERT(IN_CLASSD(address)); address = htonl(address); // // Convert the address to a string. // status = ClRtlTcpipAddressToString(address, &mcastAddress); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to convert selected multicast " "address %1!u!.%2!u!.%3!u!.%4!u! for " "network %5!ws! to a TCP/IP " "address string, status %6!u!.\n", NmpIpAddrPrintArgs(address), networkId, status ); goto error_exit; } } // // Build a parameters data structure for this address. // status = NmpMulticastCreateParameters( 0, // disabled mcastAddress, NULL, // key 0, // key length 0, // lease obtained 0, // lease expires (filled in below) NULL, // request id NmpNullMulticastAddress, // lease server NmMcastConfigAuto, Parameters ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to build multicast parameters " "for network %1!ws! after choosing address, " "status %2!u!.\n", networkId, status ); goto error_exit; } // // Calculate the lease renew time. We don't need // the lease renew time right now, but a side // effect of this routine is to ensure that the // lease end time is set correctly (e.g. for // manual or auto config). // NmpCalculateLeaseRenewTime( Network, NmMcastConfigAuto, &Parameters->LeaseObtained, &Parameters->LeaseExpires ); ClRtlLogPrint(LOG_NOISE, "[NM] Chose multicast address %1!ws! for " "network %2!ws!.\n", Parameters->Address, networkId ); error_exit: // // If the list is empty, then the selection range // is empty, and we could not choose an address. // if (IsListEmpty(&selectionRange)) { CL_ASSERT(status != ERROR_SUCCESS); ClRtlLogPrint(LOG_UNUSUAL, "[NM] Multicast address selection range for " "network %1!ws! is empty. Unable to select " "a multicast address.\n", networkId ); } else { NmpMulticastFreeSelectionRange(&selectionRange); } if (networkKey != NULL) { DmCloseKey(networkKey); networkKey = NULL; } if (netParamKey != NULL) { DmCloseKey(netParamKey); netParamKey = NULL; } if (scopeList != NULL) { LocalFree(scopeList); } if (mcastAddress != NULL) { MIDL_user_free(mcastAddress); mcastAddress = NULL; } return(status); } // NmpChooseMulticastAddress #define NmpMulticastIsScopeMarked(_scope) \ ((_scope)->ScopeCtx.Interface.IpAddrV4 == 0 && \ (_scope)->ScopeCtx.ScopeID.IpAddrV4 == 0 && \ (_scope)->ScopeCtx.ServerID.IpAddrV4 == 0) #define NmpMulticastMarkScope(_scope) \ RtlZeroMemory(&((_scope)->ScopeCtx), sizeof((_scope)->ScopeCtx)) BOOLEAN NmpIsMulticastScopeNetworkValid( IN PIPNG_ADDRESS LocalAddress, IN PIPNG_ADDRESS LocalMask, IN OUT PMCAST_SCOPE_ENTRY Scope, OUT OPTIONAL BOOLEAN * InterfaceMatch ) /*++ Routine Description: Determine if the scope is valid for the network with specified local address and mask. The valid criteria are - the interface must match (same subnet) the network address. - the scope must not be single-source (232.*.*.*), as defined by the IANA If the scope is not valid, mark it so that future consideration is fast. Mark it by zeroing the scope context interface field. Arguments: LocalAddress - local address for network LocalMask - subnet mask for network CurrentScope - scope under consideration InterfaceMatch - indicates whether the scope matched the local network interface Return value: TRUE if the scope matches the network. FALSE otherwise, and the scope is marked if not already. --*/ { if (InterfaceMatch != NULL) { *InterfaceMatch = FALSE; } // // First check if the scope has been marked. // if (NmpMulticastIsScopeMarked(Scope)) { return(FALSE); } // // This scope is not a candidate if it is not on // the correct interface. // if (!ClRtlAreTcpipAddressesOnSameSubnet( Scope->ScopeCtx.Interface.IpAddrV4, LocalAddress->IpAddrV4, LocalMask->IpAddrV4 )) { // // Mark this scope to avoid trying it again. // NmpMulticastMarkScope(Scope); return(FALSE); } // // The local interface matches this scope. // if (InterfaceMatch != NULL) { *InterfaceMatch = TRUE; } // // This scope is not a candidate if it is single-source. // if (ClRtlAreTcpipAddressesOnSameSubnet( Scope->ScopeCtx.Interface.IpAddrV4, NMP_SINGLE_SOURCE_SCOPE_ADDRESS, NMP_SINGLE_SOURCE_SCOPE_MASK )) { // // Mark this scope to avoid trying it again. // NmpMulticastMarkScope(Scope); return(FALSE); } return(TRUE); } // NmpIsMulticastScopeNetworkValid DWORD NmpNetworkAddressOctetMatchCount( IN ULONG LocalAddress, IN ULONG LocalMask, IN ULONG TargetAddress ) /*++ Routine Description: Counts the octets matched in the target address to the local network number. Note: this routine assumes contiguous subnet masks! Arguments: LocalAddress - local IPv4 address LocalMask - local IPv4 subnet mask TargetAddress - target IPv4 address Return value: Count of octets matched. --*/ { ULONG networkNumber; struct in_addr *inNetwork, *inTarget; networkNumber = LocalAddress & LocalMask; inNetwork = (struct in_addr *) &networkNumber; inTarget = (struct in_addr *) &TargetAddress; if (inNetwork->S_un.S_un_b.s_b1 != inTarget->S_un.S_un_b.s_b1) { return(0); } if (inNetwork->S_un.S_un_b.s_b2 != inTarget->S_un.S_un_b.s_b2) { return(1); } if (inNetwork->S_un.S_un_b.s_b3 != inTarget->S_un.S_un_b.s_b3) { return(2); } if (inNetwork->S_un.S_un_b.s_b4 != inTarget->S_un.S_un_b.s_b4) { return(3); } return(4); } // NmpNetworkAddressOctetMatchCount BOOLEAN NmpIsMulticastScopeBetter( IN PIPNG_ADDRESS LocalAddress, IN PIPNG_ADDRESS LocalMask, IN PMCAST_SCOPE_ENTRY CurrentScope, IN PMCAST_SCOPE_ENTRY NewScope ) /*++ Routine Description: Compares the current scope to the new scope according to the scope evaluation criteria. Assumes both scopes match the current network. Arguments: LocalAddress - local address for network LocalMask - subnet mask for network CurrentScope - currently placed scope NewScope - possible better scope Return value: TRUE if NewScope is better than CurrentScope --*/ { BOOL currentLocal, newLocal; DWORD currentCount, newCount; // // If the new scope is an administrative // link-local and the current best is not, // then the new scope wins. // currentLocal = ClRtlAreTcpipAddressesOnSameSubnet( CurrentScope->ScopeCtx.Interface.IpAddrV4, NMP_LOCAL_SCOPE_ADDRESS, NMP_LOCAL_SCOPE_MASK ); newLocal = ClRtlAreTcpipAddressesOnSameSubnet( NewScope->ScopeCtx.Interface.IpAddrV4, NMP_LOCAL_SCOPE_ADDRESS, NMP_LOCAL_SCOPE_MASK ); if (newLocal && !currentLocal) { return(TRUE); } else if (currentLocal && !newLocal) { return(FALSE); } // // If the two scopes come from different servers, we // rank them according to how close we think the server // is. // if (CurrentScope->ScopeCtx.ServerID.IpAddrV4 != NewScope->ScopeCtx.ServerID.IpAddrV4) { // // If the new scope's server is on the same subnet as // the local address and the current scope's server is // not, then the new scope wins. // currentLocal = ClRtlAreTcpipAddressesOnSameSubnet( CurrentScope->ScopeCtx.ServerID.IpAddrV4, LocalAddress->IpAddrV4, LocalMask->IpAddrV4 ); newLocal = ClRtlAreTcpipAddressesOnSameSubnet( NewScope->ScopeCtx.ServerID.IpAddrV4, LocalAddress->IpAddrV4, LocalMask->IpAddrV4 ); if (newLocal && !currentLocal) { return(TRUE); } else if (currentLocal && !newLocal) { return(FALSE); } // // If neither server is on the same subnet and the new scope's // server seems closer than the current scope's server, then // the new scope wins. Note that this is only a heuristic. // if (!newLocal && !currentLocal) { currentCount = NmpNetworkAddressOctetMatchCount( LocalAddress->IpAddrV4, LocalMask->IpAddrV4, CurrentScope->ScopeCtx.ServerID.IpAddrV4 ); newCount = NmpNetworkAddressOctetMatchCount( LocalAddress->IpAddrV4, LocalMask->IpAddrV4, NewScope->ScopeCtx.ServerID.IpAddrV4 ); if (newCount > currentCount) { return(TRUE); } else if (currentCount > newCount) { return(FALSE); } } } // // If the new scope has a larger range than // the current best, the new scope wins. The scope // range is the last address minus the scope ID. // We do not consider exclusions. // currentCount = CurrentScope->LastAddr.IpAddrV4 - CurrentScope->ScopeCtx.ScopeID.IpAddrV4; newCount = NewScope->LastAddr.IpAddrV4 - NewScope->ScopeCtx.ScopeID.IpAddrV4; if (newCount > currentCount) { return(TRUE); } else if (currentCount > newCount) { return(FALSE); } // // If the new scope has a smaller TTL than // the current best, the new scope wins. // if (NewScope->TTL < CurrentScope->TTL) { return(TRUE); } else if (CurrentScope->TTL < NewScope->TTL) { return(FALSE); } // // No condition was found to indicate that the new scope // is better. // return(FALSE); } // NmpIsMulticastScopeBetter DWORD NmpFindMulticastScopes( IN PNM_NETWORK Network, OUT PMCAST_SCOPE_CTX * ScopeCtxList, OUT DWORD * ScopeCtxCount, OUT OPTIONAL BOOLEAN * FoundInterfaceMatch ) /*++ Routine Description: Gets an enumeration of multicast scopes from a MADCAP server and sorts the scopes according to the multicast scope criteria. Allocates and returns an array of scopes that must be freed by the caller. Arguments: Network - network for which scope is sought ScopeList - returned scope list, must be freed by caller. ScopeCount - returned count of scopes in list FoundInterfaceMatch - TRUE if at least one scope in the scope list matches this network's interface Return value: Status of enumeration or allocations. --*/ { LPCWSTR networkId = OmObjectId(Network); DWORD status; PMCAST_SCOPE_ENTRY scopeList = NULL; DWORD scopeCount; DWORD scope; PMCAST_SCOPE_CTX sortedCtxList = NULL; DWORD sortedCount = 0; DWORD sortedScopeCtx; PMCAST_SCOPE_ENTRY nextScope; BOOLEAN currentCorrectInterface = FALSE; BOOLEAN foundInterfaceMatch = FALSE; IPNG_ADDRESS networkAddress; IPNG_ADDRESS networkSubnet; CL_ASSERT(ScopeCtxList != NULL); CL_ASSERT(ScopeCtxCount != NULL); ClRtlLogPrint(LOG_NOISE, "[NM] Finding multicast scopes for " "network %1!ws!.\n", networkId ); status = NmpMulticastEnumerateScopes( TRUE, // force requery &scopeList, &scopeCount ); if (status != ERROR_SUCCESS) { if (status == ERROR_TIMEOUT || status == ERROR_NO_DATA) { ClRtlLogPrint(LOG_NOISE, "[NM] Request to MADCAP server failed while " "enumerating scopes for network %1!ws! " "(status %2!u!). Assuming there are currently " "no MADCAP servers on the network.\n", networkId, status ); goto error_exit; } else { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to enumerate multicast scopes for " "network %1!ws!, status %2!u!.\n", networkId, status ); goto error_exit; } } if (scopeCount == 0) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Zero multicast scopes located in enumeration " "on network %1!ws!.\n", networkId ); goto error_exit; } // // Get the network's address and mask, used to evaluate // scopes. // // Note: this code is IPv4 specific in that it relies on the // IP address fitting into a ULONG. It uses the // IPNG_ADDRESS data structure only to work with the // MADCAP API. // status = ClRtlTcpipStringToAddress( Network->Address, &(networkAddress.IpAddrV4) ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to convert network address string " "%1!ws! into an IPv4 address, status %2!u!.\n", Network->Address, status ); goto error_exit; } status = ClRtlTcpipStringToAddress( Network->AddressMask, &(networkSubnet.IpAddrV4) ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to convert network address mask string " "%1!ws! into an IPv4 address, status %2!u!.\n", Network->AddressMask, status ); goto error_exit; } ClRtlLogPrint( LOG_NOISE, "[NM] Ranking multicast scopes for network " "%1!ws! with address %2!ws! and mask %3!ws!.\n", networkId, Network->Address, Network->AddressMask ); // // Iterate through the scope list to count the valid scopes // for this network. Also remember whether any scope matches // the local network interface. // Note that this test munges the scope entries. // for (scope = 0, sortedCount = 0; scope < scopeCount; scope++) { if (NmpIsMulticastScopeNetworkValid( &networkAddress, &networkSubnet, &(scopeList[scope]), ¤tCorrectInterface )) { sortedCount++; } foundInterfaceMatch = (BOOLEAN)(foundInterfaceMatch || currentCorrectInterface); } // // Exit if no valid scopes were found. // if (sortedCount == 0) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to locate a valid multicast scope " "for network %1!ws!.\n", networkId ); goto error_exit; } // // Allocate a new scope list for the sorted scope contexts. The // scope context is all that is needed for an address lease // request. // sortedCtxList = MIDL_user_allocate(sortedCount * sizeof(MCAST_SCOPE_CTX)); if (sortedCtxList == NULL) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to allocate multicast scope context " "list for %1!u! scopes.\n", sortedCount ); status = ERROR_NOT_ENOUGH_MEMORY; goto error_exit; } // // Rank the enumerated scopes using a variation of // insertion sort. // Note that the scope list returned by the enumeration is munged. // for (sortedScopeCtx = 0; sortedScopeCtx < sortedCount; sortedScopeCtx++) { // // Find the next valid scope in the list returned from // the enumeration. // nextScope = NULL; for (scope = 0; scope < scopeCount; scope++) { if (!NmpMulticastIsScopeMarked(&scopeList[scope])) { nextScope = &scopeList[scope]; break; } } if (nextScope == NULL) { // // There are no more valid scopes for this network. // break; } // // We know that there is at least one valid scope, but we // want the best of the unranked scopes. Compare the scope // we picked from the list to all those remaining. // for (scope++; scope < scopeCount; scope++) { if (!NmpMulticastIsScopeMarked(&scopeList[scope]) && NmpIsMulticastScopeBetter( &networkAddress, &networkSubnet, nextScope, &scopeList[scope] )) { nextScope = &scopeList[scope]; } } ClRtlLogPrint(LOG_NOISE, "[NM] Ranking scope on " "interface %1!u!.%2!u!.%3!u!.%4!u!, " "id %5!u!.%6!u!.%7!u!.%8!u!, " "last address %9!u!.%10!u!.%11!u!.%12!u!, " "from server %13!u!.%14!u!.%15!u!.%16!u!, with " "description %17!ws!, " "in list position %18!u!.\n", NmpIpAddrPrintArgs(nextScope->ScopeCtx.Interface.IpAddrV4), NmpIpAddrPrintArgs(nextScope->ScopeCtx.ScopeID.IpAddrV4), NmpIpAddrPrintArgs(nextScope->LastAddr.IpAddrV4), NmpIpAddrPrintArgs(nextScope->ScopeCtx.ServerID.IpAddrV4), nextScope->ScopeDesc.Buffer, sortedScopeCtx ); // // Copy the scope context into the sorted scope context // list. // sortedCtxList[sortedScopeCtx] = nextScope->ScopeCtx; // // Mark the scope so that it is not used again. // NmpMulticastMarkScope(nextScope); } error_exit: *ScopeCtxList = sortedCtxList; *ScopeCtxCount = sortedCount; if (FoundInterfaceMatch != NULL) { *FoundInterfaceMatch = foundInterfaceMatch; } if (scopeList != NULL) { LocalFree(scopeList); } return(status); } // NmpFindMulticastScopes DWORD NmpGenerateMulticastRequestId( IN OUT LPMCAST_CLIENT_UID RequestId ) /*++ Routine Description: Allocate, if necessary, and generate a client request id data structure. If the buffer described by the input MCAST_CLIENT_UID data structure is too small, it is deallocated. Arguments: RequestId - IN: pointer to MCAST_CLIENT_UID data structure. if ClientUID field is non-NULL, it points to a buffer for the generated ID and ClientUIDLength is the length of that buffer. OUT: filled in MCAST_CLIENT_UID data structure. --*/ { DWORD status; LPBYTE clientUid = NULL; MCAST_CLIENT_UID requestId; DWORD clientUidLength; CL_ASSERT(RequestId != NULL); #if CLUSTER_BETA ClRtlLogPrint(LOG_NOISE, "[NM] Generating MADCAP client request id.\n" ); #endif // CLUSTER_BETA // // Initialize MADCAP, if not done already. // if (!NmpMadcapClientInitialized) { DWORD madcapVersion = MCAST_API_CURRENT_VERSION; status = McastApiStartup(&madcapVersion); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to initialize MADCAP API, " "status %1!u!.\n", status ); goto error_exit; } NmpMadcapClientInitialized = TRUE; } // // Allocate a buffer for the client uid, if necessary. // clientUid = RequestId->ClientUID; clientUidLength = RequestId->ClientUIDLength; if (clientUid != NULL && clientUidLength < MCAST_CLIENT_ID_LEN) { MIDL_user_free(clientUid); clientUid = NULL; clientUidLength = 0; RequestId->ClientUID = NULL; } if (clientUid == NULL) { clientUidLength = MCAST_CLIENT_ID_LEN; clientUid = MIDL_user_allocate(clientUidLength); if (clientUid == NULL) { status = ERROR_NOT_ENOUGH_MEMORY; ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to allocate buffer for multicast " "clientUid.\n" ); goto error_exit; } } // // Obtain a new ID. // requestId.ClientUID = clientUid; requestId.ClientUIDLength = clientUidLength; status = McastGenUID(&requestId); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to obtain multicast address " "request client id, status %1!u!.\n", status ); goto error_exit; } *RequestId = requestId; clientUid = NULL; error_exit: if (clientUid != NULL) { MIDL_user_free(clientUid); clientUid = NULL; } return(status); } // NmpGenerateMulticastRequestId DWORD NmpRequestMulticastAddress( IN PNM_NETWORK Network, IN BOOLEAN Renew, IN PMCAST_SCOPE_CTX ScopeCtx, IN LPMCAST_CLIENT_UID RequestId, IN OUT LPWSTR * McastAddress, IN OUT DWORD * McastAddressLength, IN OUT LPWSTR * ServerAddress, IN OUT DWORD * ServerAddressLength, OUT time_t * LeaseStartTime, OUT time_t * LeaseEndTime, OUT BOOLEAN * NewMcastAddress ) /*++ Routine Description: Renew lease on multicast group address using MADCAP client API. Arguments: Network - network on which address is used ScopeCtx - multicast scope (ignored if Renew) RequestId - client request id McastAddress - IN: address to renew (ignored if !Renew) OUT: resulting address McastAddressLength - length of McastAddress buffer ServerAddress - IN: address of server on which to renew (ignored if !Renew) OUT: address of address where renew occurred ServerAddressLength - length of ServerAddress buffer LeaseStartTime - UTC lease start time in seconds (buffer allocated by caller) LeaseEndTime - UTC lease stop time in seconds (buffer allocated by caller) NewMcastAddress - whether resulting mcast address is new (different than request on renewal and always true on successful request) --*/ { DWORD status; LPCWSTR networkId = OmObjectId(Network); UCHAR requestBuffer[NMP_MADCAP_REQUEST_BUFFER_SIZE]; PMCAST_LEASE_REQUEST request; UCHAR responseBuffer[NMP_MADCAP_RESPONSE_BUFFER_SIZE]; PMCAST_LEASE_RESPONSE response; LPWSTR address = NULL; DWORD addressSize; DWORD requestAddress = 0; ClRtlLogPrint(LOG_NOISE, "[NM] Preparing to send multicast address %1!ws! " "for network %2!ws! to MADCAP server.\n", ((Renew) ? L"renewal" : L"request"), OmObjectId(Network) ); // // Initialize MADCAP, if not done already. // if (!NmpMadcapClientInitialized) { DWORD madcapVersion = MCAST_API_CURRENT_VERSION; status = McastApiStartup(&madcapVersion); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to initialize MADCAP API, " "status %1!u!.\n", status ); goto error_exit; } NmpMadcapClientInitialized = TRUE; } // // Fill in the request. All fields are zero except those // set below. // request = (PMCAST_LEASE_REQUEST) &requestBuffer[0]; RtlZeroMemory(request, sizeof(requestBuffer)); request->MinLeaseDuration = 0; // currently ignored request->MinAddrCount = 1; // currently ignored request->MaxLeaseStartTime = (LONG) time(NULL); // currently ignored request->AddrCount = 1; // // Set the renew parameters. // if (Renew) { request->pAddrBuf = (PBYTE)request + NMP_MADCAP_REQUEST_ADDR_OFFSET; status = ClRtlTcpipStringToAddress( *McastAddress, &requestAddress ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to convert requested address %1!ws! " "into a TCP/IP address, status %2!u!.\n", *McastAddress, status ); goto error_exit; } *((PULONG) request->pAddrBuf) = requestAddress; status = ClRtlTcpipStringToAddress( *ServerAddress, (PULONG) &(request->ServerAddress.IpAddrV4) ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to convert server address %1!ws! " "into a TCP/IP address, status %2!u!.\n", *ServerAddress, status ); goto error_exit; } } // // Set the address count and buffer fields in the response. // response = (PMCAST_LEASE_RESPONSE) &responseBuffer[0]; RtlZeroMemory(response, sizeof(responseBuffer)); response->AddrCount = 1; response->pAddrBuf = (PBYTE)(response) + NMP_MADCAP_RESPONSE_ADDR_OFFSET; // // Renew or request, as indicated. // if (Renew) { status = McastRenewAddress( AF_INET, RequestId, request, response ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to renew multicast address %1!ws! " "with server %2!ws!, status %3!u!.\n", *McastAddress, *ServerAddress, status ); goto error_exit; } } else { ClRtlLogPrint(LOG_NOISE, "[NM] Requesting multicast address on " "Scope ID %1!u!.%2!u!.%3!u!.%4!u!, " "Server ID %5!u!.%6!u!.%7!u!.%8!u!, " "Interface %9!u!.%10!u!.%11!u!.%12!u!, " "for network %13!ws!.\n", NmpIpAddrPrintArgs(ScopeCtx->ScopeID.IpAddrV4), NmpIpAddrPrintArgs(ScopeCtx->ServerID.IpAddrV4), NmpIpAddrPrintArgs(ScopeCtx->Interface.IpAddrV4), networkId ); status = McastRequestAddress( AF_INET, RequestId, ScopeCtx, request, response ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_NOISE, "[NM] Failed to obtain multicast address on " "Scope ID %1!u!.%2!u!.%3!u!.%4!u!, " "Server ID %5!u!.%6!u!.%7!u!.%8!u!, " "Interface %9!u!.%10!u!.%11!u!.%12!u!, " "for network %13!ws!, status %14!u!.\n", NmpIpAddrPrintArgs(ScopeCtx->ScopeID.IpAddrV4), NmpIpAddrPrintArgs(ScopeCtx->ServerID.IpAddrV4), NmpIpAddrPrintArgs(ScopeCtx->Interface.IpAddrV4), networkId, status ); goto error_exit; } } // // Return results through out parameters. // address = NULL; status = ClRtlTcpipAddressToString( response->ServerAddress.IpAddrV4, &address ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to convert server address %1!x! " "into a TCP/IP address string, status %2!u!.\n", response->ServerAddress.IpAddrV4, status ); goto error_exit; } status = NmpStoreString(address, ServerAddress, ServerAddressLength); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to store server address %1!ws! " "in return buffer, status %2!u!.\n", address, status ); goto error_exit; } LocalFree(address); address = NULL; status = ClRtlTcpipAddressToString( *((PULONG) response->pAddrBuf), &address ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to convert leased address %1!x! " "into a TCP/IP address string, status %2!u!.\n", *((PULONG) response->pAddrBuf), status ); goto error_exit; } status = NmpStoreString(address, McastAddress, McastAddressLength); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to store leased address %1!ws! " "in return buffer, status %2!u!.\n", address, status ); goto error_exit; } if (Renew) { if (*((PULONG) response->pAddrBuf) != requestAddress) { *NewMcastAddress = TRUE; } else { *NewMcastAddress = FALSE; } } else { *NewMcastAddress = TRUE; } *LeaseStartTime = response->LeaseStartTime; *LeaseEndTime = response->LeaseEndTime; ClRtlLogPrint(LOG_NOISE, "[NM] Obtained lease on multicast address %1!ws! " "(%2!ws!) from MADCAP server %3!ws! for network %4!ws!.\n", *McastAddress, ((*NewMcastAddress) ? L"new" : L"same"), *ServerAddress, networkId ); #if CLUSTER_BETA ClRtlLogPrint(LOG_NOISE, "[NM] Lease starts at %1!u!, ends at %2!u!, " "duration %3!u!.\n", *LeaseStartTime, *LeaseEndTime, *LeaseEndTime - *LeaseStartTime ); #endif // CLUSTER_BETA error_exit: if (address != NULL) { LocalFree(address); address = NULL; } return(status); } // NmpRequestMulticastAddress NM_MCAST_LEASE_STATUS NmpCalculateLeaseStatus( IN PNM_NETWORK Network, IN time_t LeaseObtained, IN time_t LeaseExpires ) /*++ Routine Description: Calculate lease status based on current time, lease end time, and config type. If config type is auto, we do not use a half-life for the lease. Rely on the compiler's correct code generation for time_t math! Return value: Lease status --*/ { LPCWSTR networkId = OmObjectId(Network); time_t currentTime; time_t renewThreshold; NM_MCAST_LEASE_STATUS status; if (Network->ConfigType == NmMcastConfigManual || LeaseExpires == 0 || LeaseExpires <= LeaseObtained) { // // A lease expiration of 0 means we hold the lease // forever. Most likely, an administrator statically // configured the network with this address. // status = NmMcastLeaseValid; } else { time(¤tTime); if (currentTime > LeaseExpires) { status = NmMcastLeaseExpired; } else { if (Network->ConfigType == NmMcastConfigAuto) { // We chose this address. There is no server // expiring it. Use the chosen expiration time // rather than the half-life. renewThreshold = LeaseExpires; } else { // We got this address from a MADCAP server. // Renew at half-life. renewThreshold = LeaseObtained + ((LeaseExpires - LeaseObtained) / 2); } if (currentTime >= renewThreshold) { status = NmMcastLeaseNeedsRenewal; } else { status = NmMcastLeaseValid; } } } #if CLUSTER_BETA if (Network->ConfigType == NmMcastConfigManual || LeaseExpires == 0 || LeaseExpires <= LeaseObtained) { ClRtlLogPrint(LOG_NOISE, "[NM] Found that multicast address lease for " "network %1!ws! does not expire.\n", networkId ); } else if (status == NmMcastLeaseExpired) { ClRtlLogPrint(LOG_NOISE, "[NM] Found that multicast address lease for " "network %1!ws! expired %2!u! seconds ago.\n", networkId, currentTime - LeaseExpires ); } else { ClRtlLogPrint(LOG_NOISE, "[NM] Found that multicast address lease for " "network %1!ws! expires in %2!u! seconds. With " "lease obtained %3!u! seconds ago, renewal is " "%4!ws!needed.\n", networkId, LeaseExpires - currentTime, currentTime - LeaseObtained, ((status > NmMcastLeaseValid) ? L"" : L"not ") ); } #endif // CLUSTER_BETA return(status); } // NmpCalculateLeaseStatus DWORD NmpQueryMulticastAddressLease( IN PNM_NETWORK Network, IN HDMKEY NetworkKey, IN OUT HDMKEY * NetworkParametersKey, OUT NM_MCAST_LEASE_STATUS * LeaseStatus, OUT time_t * LeaseObtained, OUT time_t * LeaseExpires ) /*++ Routine Description: Query the lease obtained and expires times stored in the cluster database. Return value: Error if lease times not found. Notes: Must not be called with NM lock held. --*/ { DWORD status; LPCWSTR networkId = OmObjectId(Network); HDMKEY netParamKey = NULL; BOOLEAN openedNetParamKey = FALSE; DWORD type; DWORD len; time_t leaseExpires; time_t leaseObtained; #if CLUSTER_BETA ClRtlLogPrint(LOG_NOISE, "[NM] Querying multicast address lease for " "network %1!ws!.\n", networkId ); #endif // CLUSTER_BETA if (Network == NULL || NetworkKey == NULL) { status = ERROR_INVALID_PARAMETER; goto error_exit; } // // Open the network parameters key, if necessary. // netParamKey = *NetworkParametersKey; if (netParamKey == NULL) { netParamKey = DmOpenKey( NetworkKey, CLUSREG_KEYNAME_PARAMETERS, MAXIMUM_ALLOWED ); if (netParamKey == NULL) { status = GetLastError(); ClRtlLogPrint(LOG_NOISE, "[NM] Failed to find Parameters key " "for network %1!ws!, status %2!u!. Using default " "multicast parameters.\n", networkId, status ); goto error_exit; } else { openedNetParamKey = TRUE; } } // // Query the lease obtained and expires value from the // cluster database. // len = sizeof(leaseObtained); status = DmQueryValue( netParamKey, CLUSREG_NAME_NET_MCAST_LEASE_OBTAINED, &type, (LPBYTE) &leaseObtained, &len ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to read multicast lease obtained " " time for network %1!ws! from cluster database, " "status %2!u!.\n", networkId, status ); goto error_exit; } else if (type != REG_DWORD) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Unexpected type (%1!u!) for network " "%2!ws! %3!ws!.\n", type, networkId, CLUSREG_NAME_NET_MCAST_LEASE_OBTAINED ); status = ERROR_DATATYPE_MISMATCH; goto error_exit; } len = sizeof(leaseExpires); status = DmQueryValue( netParamKey, CLUSREG_NAME_NET_MCAST_LEASE_EXPIRES, &type, (LPBYTE) &leaseExpires, &len ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to read multicast lease expiration " " time for network %1!ws! from cluster database, " "status %2!u!.\n", networkId, status ); goto error_exit; } else if (type != REG_DWORD) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Unexpected type (%1!u!) for network " "%2!ws! %3!ws!.\n", type, networkId, CLUSREG_NAME_NET_MCAST_LEASE_EXPIRES ); status = ERROR_DATATYPE_MISMATCH; goto error_exit; } *NetworkParametersKey = netParamKey; netParamKey = NULL; *LeaseStatus = NmpCalculateLeaseStatus( Network, leaseObtained, leaseExpires ); *LeaseObtained = leaseObtained; *LeaseExpires = leaseExpires; error_exit: if (openedNetParamKey && netParamKey != NULL) { DmCloseKey(netParamKey); netParamKey = NULL; } return(status); } // NmpQueryMulticastAddressLease VOID NmpCheckMulticastAddressLease( IN PNM_NETWORK Network, OUT NM_MCAST_LEASE_STATUS * LeaseStatus, OUT time_t * LeaseObtained, OUT time_t * LeaseExpires ) /*++ Routine Description: Check the lease parameters stored in the network object. Determine if a lease renew is required. Notes: Called and returns with NM lock held. --*/ { #if CLUSTER_BETA LPCWSTR networkId = OmObjectId(Network); ClRtlLogPrint(LOG_NOISE, "[NM] Checking multicast address lease for " "network %1!ws!.\n", networkId ); #endif // CLUSTER_BETA // // Determine if we need to renew. // *LeaseStatus = NmpCalculateLeaseStatus( Network, Network->MulticastLeaseObtained, Network->MulticastLeaseExpires ); *LeaseObtained = Network->MulticastLeaseObtained; *LeaseExpires = Network->MulticastLeaseExpires; return; } // NmpCheckMulticastAddressLease DWORD NmpMulticastGetDatabaseLeaseParameters( IN PNM_NETWORK Network, IN OUT HDMKEY * NetworkKey, IN OUT HDMKEY * NetworkParametersKey, OUT OPTIONAL LPMCAST_CLIENT_UID RequestId, OUT OPTIONAL LPWSTR * ServerAddress, OUT OPTIONAL LPWSTR * McastAddress ) /*++ Routine Description: Read parameters needed to renew a lease from the cluster database. Return value: SUCCESS if all parameters were successfully read. Notes: Must not be called with NM lock held. --*/ { DWORD status; LPCWSTR networkId = OmObjectId(Network); HDMKEY networkKey = NULL; BOOLEAN openedNetworkKey = FALSE; HDMKEY netParamKey = NULL; BOOLEAN openedNetParamKey = FALSE; DWORD type; DWORD len; MCAST_CLIENT_UID requestId = { NULL, 0 }; LPWSTR serverAddress = NULL; DWORD serverAddressLength = 0; LPWSTR mcastAddress = NULL; DWORD mcastAddressLength = 0; // // Open the network key, if necessary. // networkKey = *NetworkKey; if (networkKey == NULL) { networkKey = DmOpenKey( DmNetworksKey, networkId, MAXIMUM_ALLOWED ); if (networkKey == NULL) { status = GetLastError(); ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to open key for network %1!ws!, " "status %2!u!\n", networkId, status ); goto error_exit; } openedNetworkKey = TRUE; } // // Open the network parameters key if necessary. // netParamKey = *NetworkParametersKey; if (netParamKey == NULL) { netParamKey = DmOpenKey( networkKey, CLUSREG_KEYNAME_PARAMETERS, MAXIMUM_ALLOWED ); if (netParamKey == NULL) { status = GetLastError(); ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to open Parameters key for " "network %1!ws!, status %2!u!\n", networkId, status ); goto error_exit; } openedNetParamKey = TRUE; } // // Read the client request id. // if (RequestId != NULL) { requestId.ClientUIDLength = MCAST_CLIENT_ID_LEN; requestId.ClientUID = MIDL_user_allocate(requestId.ClientUIDLength); if (requestId.ClientUID == NULL) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to allocate buffer to read " "request id from Parameters database " "key for network %1!ws!.\n", networkId ); status = ERROR_NOT_ENOUGH_MEMORY; goto error_exit; } len = requestId.ClientUIDLength; status = DmQueryValue( netParamKey, CLUSREG_NAME_NET_MCAST_REQUEST_ID, &type, (LPBYTE) requestId.ClientUID, &len ); if (status == ERROR_SUCCESS) { if (type != REG_BINARY) { ClRtlLogPrint(LOG_NOISE, "[NM] Unexpected type (%1!u!) for network " "%2!ws! %3!ws!, status %4!u!.\n", type, networkId, CLUSREG_NAME_NET_MCAST_REQUEST_ID, status ); goto error_exit; } requestId.ClientUIDLength = len; } else { goto error_exit; } } // // Read the address of the server that granted the // current lease. // if (ServerAddress != NULL) { serverAddress = NULL; serverAddressLength = 0; status = NmpQueryString( netParamKey, CLUSREG_NAME_NET_MCAST_SERVER_ADDRESS, REG_SZ, &serverAddress, &serverAddressLength, &len ); if (status != ERROR_SUCCESS) { goto error_exit; } } // // Read the last known multicast address. // if (McastAddress != NULL) { status = NmpQueryMulticastAddress( Network, networkKey, &netParamKey, &mcastAddress, &mcastAddressLength ); if (status != ERROR_SUCCESS) { goto error_exit; } if (!NmpMulticastValidateAddress(mcastAddress)) { MIDL_user_free(mcastAddress); mcastAddress = NULL; mcastAddressLength = 0; goto error_exit; } } // // We found all the parameters. // *NetworkKey = networkKey; networkKey = NULL; *NetworkParametersKey = netParamKey; netParamKey = NULL; if (RequestId != NULL) { *RequestId = requestId; requestId.ClientUID = NULL; requestId.ClientUIDLength = 0; } if (ServerAddress != NULL) { *ServerAddress = serverAddress; serverAddress = NULL; } if (McastAddress != NULL) { *McastAddress = mcastAddress; mcastAddress = NULL; } status = ERROR_SUCCESS; error_exit: if (requestId.ClientUID != NULL) { MIDL_user_free(requestId.ClientUID); requestId.ClientUID = NULL; requestId.ClientUIDLength = 0; } if (serverAddress != NULL) { MIDL_user_free(serverAddress); serverAddress = NULL; } if (mcastAddress != NULL) { MIDL_user_free(mcastAddress); mcastAddress = NULL; } if (openedNetworkKey && networkKey != NULL) { DmCloseKey(networkKey); networkKey = NULL; } if (openedNetParamKey && netParamKey != NULL) { DmCloseKey(netParamKey); netParamKey = NULL; } return(status); } // NmpMulticastGetDatabaseLeaseParameters DWORD NmpMulticastGetNetworkLeaseParameters( IN PNM_NETWORK Network, OUT LPMCAST_CLIENT_UID RequestId, OUT LPWSTR * ServerAddress, OUT LPWSTR * McastAddress ) /*++ Routine Description: Read parameters needed to renew a lease from the network object data structure. Return value: SUCCESS if all parameters were successfully read. Notes: Must be called with NM lock held. --*/ { DWORD status; LPCWSTR networkId = OmObjectId(Network); MCAST_CLIENT_UID requestId = { NULL, 0 }; LPWSTR serverAddress = NULL; DWORD serverAddressLength = 0; LPWSTR mcastAddress = NULL; DWORD mcastAddressLength = 0; if (Network->MulticastAddress == NULL || Network->MulticastLeaseServer == NULL || Network->MulticastLeaseRequestId.ClientUID == NULL) { ClRtlLogPrint(LOG_NOISE, "[NM] Failed to locate multicast lease " "parameter in network object %1!ws!.\n", networkId ); status = ERROR_NOT_FOUND; goto error_exit; } status = NmpStoreString( Network->MulticastAddress, &mcastAddress, NULL ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to copy multicast address %1!ws! " "from network object %2!ws!, status %3!u!.\n", Network->MulticastAddress, networkId, status ); goto error_exit; } status = NmpStoreString( Network->MulticastLeaseServer, &serverAddress, NULL ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to copy lease server address %1!ws! " "from network object %2!ws!, status %3!u!.\n", Network->MulticastLeaseServer, networkId, status ); goto error_exit; } status = NmpStoreRequestId( &(Network->MulticastLeaseRequestId), &requestId ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to copy lease request id " "from network object %1!ws!, status %2!u!.\n", networkId, status ); goto error_exit; } *RequestId = requestId; requestId.ClientUID = NULL; requestId.ClientUIDLength = 0; *ServerAddress = serverAddress; serverAddress = NULL; *McastAddress = mcastAddress; mcastAddress = NULL; status = ERROR_SUCCESS; error_exit: if (requestId.ClientUID != NULL) { MIDL_user_free(requestId.ClientUID); RtlZeroMemory(&requestId, sizeof(requestId)); } if (mcastAddress != NULL) { MIDL_user_free(mcastAddress); mcastAddress = NULL; } if (serverAddress != NULL) { MIDL_user_free(serverAddress); serverAddress = NULL; } return(status); } // NmpMulticastGetNetworkLeaseParameters DWORD NmpMulticastNeedRetryRenew( IN PNM_NETWORK Network, OUT time_t * DeferRetry ) /*++ Routine Description: Called after a MADCAP timeout, determines whether a new MADCAP request should be sent after a delay. Specifically, a retry after delay is in order when the current address was obtained from a MADCAP server that might simply be temporarily unresponsive. The default is to not retry. Arguments: Network - network DeferRetry - OUT: seconds to defer until retrying MADCAP query, or zero if should not retry --*/ { DWORD status; LPCWSTR networkId = OmObjectId(Network); HDMKEY networkKey = NULL; HDMKEY netParamKey = NULL; NM_MCAST_CONFIG configType; NM_MCAST_LEASE_STATUS leaseStatus; time_t leaseObtained; time_t leaseExpires; time_t currentTime; time_t halfhalfLife; *DeferRetry = 0; // // Open the network key. // networkKey = DmOpenKey( DmNetworksKey, networkId, MAXIMUM_ALLOWED ); if (networkKey == NULL) { status = GetLastError(); ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to open key for network %1!ws!, " "status %2!u!\n", networkId, status ); goto error_exit; } status = NmpQueryMulticastConfigType( Network, networkKey, &netParamKey, &configType ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to query multicast config type " "for network %1!ws!, status %2!u!\n", networkId, status ); goto error_exit; } if (configType != NmMcastConfigMadcap) { goto error_exit; } status = NmpQueryMulticastAddressLease( Network, networkKey, &netParamKey, &leaseStatus, &leaseObtained, &leaseExpires ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to query multicast lease expiration " "time for network %1!ws!, status %2!u!\n", networkId, status ); goto error_exit; } // // Check if the lease has expired. // if (leaseStatus == NmMcastLeaseExpired) { goto error_exit; } // // Check if we are within the threshold of expiration. // currentTime = time(NULL); if (leaseExpires <= currentTime || leaseExpires - currentTime < NMP_MCAST_LEASE_RENEWAL_THRESHOLD) { goto error_exit; } // // Calculate half the time until expiration. // halfhalfLife = currentTime + ((leaseExpires - currentTime) / 2); if (leaseExpires - halfhalfLife < NMP_MCAST_LEASE_RENEWAL_THRESHOLD) { *DeferRetry = NMP_MCAST_LEASE_RENEWAL_THRESHOLD; } else { *DeferRetry = halfhalfLife - currentTime; } status = ERROR_SUCCESS; error_exit: if (networkKey != NULL) { DmCloseKey(networkKey); networkKey = NULL; } if (netParamKey != NULL) { DmCloseKey(netParamKey); netParamKey = NULL; } return(status); } // NmpMulticastNeedRetryRenew DWORD NmpGetMulticastAddress( IN PNM_NETWORK Network, IN OUT LPWSTR * McastAddress, IN OUT LPWSTR * ServerAddress, IN OUT LPMCAST_CLIENT_UID RequestId, OUT PNM_NETWORK_MULTICAST_PARAMETERS Parameters ) /*++ Routine Description: Try to obtain a multicast address lease. If the address, server, and request id are non-NULL, first try to renew. If unsuccessful in renewing, try a new lease. Return lease parameters through Parameters. Free McastAddress, ServerAddress, and RequestId if new values are returned through Parameters. Notes: Must not be called with NM lock held. --*/ { DWORD status = ERROR_SUCCESS; LPCWSTR networkId = OmObjectId(Network); BOOLEAN renew = FALSE; BOOLEAN madcapTimeout = FALSE; BOOLEAN newMcastAddress = FALSE; NM_MCAST_CONFIG configType = NmMcastConfigAuto; PMCAST_SCOPE_CTX scopeCtxList = NULL; DWORD scopeCtxCount; DWORD scopeCtx; BOOLEAN interfaceMatch = FALSE; DWORD mcastAddressLength = 0; LPWSTR serverAddress = NULL; DWORD serverAddressLength = 0; MCAST_CLIENT_UID requestId = {NULL, 0}; time_t leaseStartTime; time_t leaseEndTime; DWORD len; // // First try to renew, but only if the proper parameters are // supplied. // renew = (BOOLEAN)(*McastAddress != NULL && *ServerAddress != NULL && RequestId->ClientUID != NULL && NmpMulticastValidateAddress(*McastAddress) && lstrcmpW(*ServerAddress, NmpNullMulticastAddress) != 0 ); if (renew) { mcastAddressLength = NM_WCSLEN(*McastAddress); serverAddressLength = NM_WCSLEN(*ServerAddress); status = NmpRequestMulticastAddress( Network, TRUE, NULL, RequestId, McastAddress, &mcastAddressLength, ServerAddress, &serverAddressLength, &leaseStartTime, &leaseEndTime, &newMcastAddress ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to renew multicast address " "for network %1!ws!, status %2!u!. Attempting " "a fresh request ...\n", networkId, status ); } } // // Try a fresh request if we had no lease to renew or the // renewal failed. // if (!renew || status != ERROR_SUCCESS) { // // Get the multicast scopes that match this network. // status = NmpFindMulticastScopes( Network, &scopeCtxList, &scopeCtxCount, &interfaceMatch ); if (status != ERROR_SUCCESS || scopeCtxCount == 0) { if (status == ERROR_TIMEOUT) { ClRtlLogPrint(LOG_NOISE, "[NM] Attempt to contact MADCAP server timed " "out while enumerating multicast scopes " "(status %1!u!). Selecting default multicast " "address for network %2!ws! ...\n", status, networkId ); // // Set the MADCAP timeout flag to TRUE, even // if we already contacted a MADCAP server for // renewal (but was denied). The theory is that // that MADCAP server is no longer serving this // network if it failed to respond to an enumerate // scopes request. // madcapTimeout = TRUE; goto error_exit; } else if (interfaceMatch) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to find viable multicast scope " "for network %1!ws! (status %2!u!), but cannot " "choose a default address since a MADCAP server " "was detected on this network.\n", networkId, status ); madcapTimeout = FALSE; goto error_exit; } else { ClRtlLogPrint(LOG_NOISE, "[NM] MADCAP server reported no viable multicast " "scopes on interface for network %1!ws!. " "Selecting default multicast address ... \n", networkId ); // // Treat this situation as a MADCAP timeout, // because there are likely no MADCAP servers // for this network. // madcapTimeout = TRUE; goto error_exit; } } CL_ASSERT(scopeCtxList != NULL && scopeCtxCount > 0); // // The scope context list is sorted by preference. Start // at the beginning of the list and request an address // lease in each scope until one is granted. Generate a // new request id for each request. // for (scopeCtx = 0; scopeCtx < scopeCtxCount; scopeCtx++) { // // Generate a client request id. // status = NmpGenerateMulticastRequestId(RequestId); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to generate multicast client " "request ID for network %1!ws!, status %2!u!.\n", networkId, status ); goto error_exit; } // // Request a lease. // mcastAddressLength = (*McastAddress == NULL) ? 0 : NM_WCSLEN(*McastAddress); serverAddressLength = (*ServerAddress == NULL) ? 0 : NM_WCSLEN(*ServerAddress); status = NmpRequestMulticastAddress( Network, FALSE, &scopeCtxList[scopeCtx], RequestId, McastAddress, &mcastAddressLength, ServerAddress, &serverAddressLength, &leaseStartTime, &leaseEndTime, &newMcastAddress ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] MADCAP server %1!u!.%2!u!.%3!u!.%4!u! " "was unable to provide a multicast address " "in Scope ID %5!u!.%6!u!.%7!u!.%8!u! " "for network %9!ws!, status %10!u!.\n", NmpIpAddrPrintArgs(scopeCtxList[scopeCtx].ServerID.IpAddrV4), NmpIpAddrPrintArgs(scopeCtxList[scopeCtx].ScopeID.IpAddrV4), networkId, status ); } else { // // No need to try additional scopes. // break; } } } if (status == ERROR_SUCCESS) { // // Madcap config succeeded. // configType = NmMcastConfigMadcap; madcapTimeout = FALSE; // // Save lease renewal parameters. // requestId = *RequestId; serverAddress = *ServerAddress; // // Fill in the parameters data structure. // status = NmpMulticastCreateParameters( 0, // disabled *McastAddress, NULL, // key 0, // key length leaseStartTime, leaseEndTime, &requestId, serverAddress, configType, Parameters ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to create multicast parameters " "data structure for network %1!ws!, " "status %2!u!.\n", networkId, status ); goto error_exit; } } error_exit: if (scopeCtxList != NULL) { MIDL_user_free(scopeCtxList); scopeCtxList = NULL; } if (madcapTimeout) { status = ERROR_TIMEOUT; } return(status); } // NmpGetMulticastAddress DWORD NmpMulticastSetNullAddressParameters( IN PNM_NETWORK Network, OUT PNM_NETWORK_MULTICAST_PARAMETERS Parameters ) /*++ Routine Description: Called after failure to process multicast parameters. Changes only address field in parameters to turn off multicast in clusnet. --*/ { LPCWSTR networkId = OmObjectId(Network); ClRtlLogPrint(LOG_NOISE, "[NM] Setting NULL multicast address (%1!ws!) " "for network %2!ws!.\n", NmpNullMulticastAddress, networkId ); if (Parameters->Address != NULL) { MIDL_user_free(Parameters->Address); } Parameters->Address = NmpNullMulticastAddress; return(ERROR_SUCCESS); } // NmpMulticastSetNullAddressParameters DWORD NmpMulticastSetNoAddressParameters( IN PNM_NETWORK Network, OUT PNM_NETWORK_MULTICAST_PARAMETERS Parameters ) /*++ Routine Description: Called after failure to obtain a multicast address. Fills in parameters data structure to reflect failure and to establish retry. --*/ { NmpMulticastSetNullAddressParameters(Network, Parameters); Parameters->ConfigType = NmMcastConfigAuto; NmpCalculateLeaseRenewTime( Network, Parameters->ConfigType, &Parameters->LeaseObtained, &Parameters->LeaseExpires ); return(ERROR_SUCCESS); } // NmpMulticastSetNoAddressParameters DWORD NmpRenewMulticastAddressLease( IN PNM_NETWORK Network ) /*++ Routine Description: Renew a multicast address lease, as determined by lease parameters stored in the cluster database. Notes: Called with NM lock held and must return with NM lock held. --*/ { DWORD status; LPCWSTR networkId = OmObjectId(Network); HDMKEY networkKey = NULL; HDMKEY netParamKey = NULL; BOOLEAN lockAcquired = TRUE; MCAST_CLIENT_UID requestId = { NULL, 0 }; LPWSTR serverAddress = NULL; DWORD serverAddressLength = 0; LPWSTR mcastAddress = NULL; DWORD mcastAddressLength = 0; LPWSTR oldMcastAddress = NULL; NM_NETWORK_MULTICAST_PARAMETERS parameters; time_t deferRetry = 0; BOOLEAN localInterface = FALSE; RtlZeroMemory(¶meters, sizeof(parameters)); localInterface = (BOOLEAN)(Network->LocalInterface != NULL); if (localInterface) { ClRtlLogPrint(LOG_NOISE, "[NM] Renewing multicast address lease for " "network %1!ws!.\n", networkId ); } else { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Attempting to renew multicast address " "lease for network %1!ws! despite the lack of " "a local interface.\n", networkId ); } // // Get the lease parameters from the network object. // status = NmpMulticastGetNetworkLeaseParameters( Network, &requestId, &serverAddress, &mcastAddress ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_NOISE, "[NM] Failed to find multicast lease " "parameters in network object %1!ws!, " "status %2!u!.\n", networkId, status ); } // // Release the NM lock. // NmpReleaseLock(); lockAcquired = FALSE; // // Check if we found the parameters we need. If not, // try the cluster database. // if (status != ERROR_SUCCESS) { status = NmpMulticastGetDatabaseLeaseParameters( Network, &networkKey, &netParamKey, &requestId, &serverAddress, &mcastAddress ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to find multicast lease " "parameters for network %1!ws! in " "cluster database, status %2!u!.\n", networkId, status ); } } // // Remember the old multicast address. // if (mcastAddress != NULL) { status = NmpStoreString(mcastAddress, &oldMcastAddress, NULL); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to copy current multicast " "address (%1!ws!) for network %2!ws! " "during lease renewal, status %3!u!.\n", mcastAddress, networkId, status ); // // Not a fatal error. Only affects event-log // decision. // oldMcastAddress = NULL; } } // // Get an address either by renewing a current // lease or obtaining a new lease. // status = NmpGetMulticastAddress( Network, &mcastAddress, &serverAddress, &requestId, ¶meters ); if (status != ERROR_SUCCESS) { if (status == ERROR_TIMEOUT) { // // The MADCAP server, if it exists, is currently not // responding. // status = NmpMulticastNeedRetryRenew( Network, &deferRetry ); if (status != ERROR_SUCCESS || deferRetry == 0) { // // Choose an address, but only if there is a // local interface on this network. Otherwise, // we cannot assume that the MADCAP server is // unresponsive because we may have no way to // contact it. // if (!localInterface) { status = ERROR_CLUSTER_NETINTERFACE_NOT_FOUND; ClRtlLogPrint(LOG_UNUSUAL, "[NM] Cannot choose a multicast address " "for network %1!ws! because this node " "has no local interface.\n", networkId ); goto error_exit; } status = NmpChooseMulticastAddress( Network, ¶meters ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to choose a default multicast " "address for network %1!ws!, status %2!u!.\n", networkId, status ); goto error_exit; } else { NmpReportMulticastAddressChoice( Network, parameters.Address, oldMcastAddress ); } } else { // // Set the renew timer once we reacquire the // network lock. // } } } else { NmpReportMulticastAddressLease( Network, ¶meters, oldMcastAddress ); } if (deferRetry == 0) { if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to obtain a multicast " "address for network %1!ws! during " "lease renewal, status %2!u!.\n", networkId, status ); NmpReportMulticastAddressFailure(Network, status); NmpMulticastSetNoAddressParameters(Network, ¶meters); } // // Create new multicast key. // status = NmpCreateRandomNumber(&(parameters.Key), MulticastKeyLen ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to create random number " "for network %1!ws!, status %2!u!.\n", networkId, status ); goto error_exit; } parameters.KeyLength = MulticastKeyLen; // // Disseminate the new multicast parameters. // status = NmpMulticastNotifyConfigChange( Network, networkKey, &netParamKey, ¶meters, NULL, 0 ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to disseminate multicast " "configuration for network %1!ws! during " "lease renewal, status %2!u!.\n", networkId, status ); goto error_exit; } } error_exit: if (lockAcquired && (networkKey != NULL || netParamKey != NULL)) { NmpReleaseLock(); lockAcquired = FALSE; } if (networkKey != NULL) { DmCloseKey(networkKey); networkKey = NULL; } if (netParamKey != NULL) { DmCloseKey(netParamKey); netParamKey = NULL; } if (requestId.ClientUID != NULL) { MIDL_user_free(requestId.ClientUID); RtlZeroMemory(&requestId, sizeof(requestId)); } if (mcastAddress != NULL) { MIDL_user_free(mcastAddress); mcastAddress = NULL; } if (serverAddress != NULL) { MIDL_user_free(serverAddress); serverAddress = NULL; } if (oldMcastAddress != NULL) { MIDL_user_free(oldMcastAddress); oldMcastAddress = NULL; } NmpMulticastFreeParameters(¶meters); if (!lockAcquired) { NmpAcquireLock(); lockAcquired = TRUE; } if (deferRetry != 0) { // // Now that the lock is held, start the timer to // renew again. // NmpStartNetworkMulticastAddressRenewTimer( Network, NmpMadcapTimeToNmTime(deferRetry) ); status = ERROR_SUCCESS; } return(status); } // NmpRenewMulticastAddressLease DWORD NmpReleaseMulticastAddress( IN PNM_NETWORK Network ) /*++ Routine Description: Contacts MADCAP server to release a multicast address that was previously obtained in a lease. If multiple addresses need to be released, reschedules MADCAP worker thread. Notes: Called and must return with NM lock held. --*/ { DWORD status; LPCWSTR networkId = OmObjectId(Network); BOOLEAN lockAcquired = TRUE; PNM_NETWORK_MADCAP_ADDRESS_RELEASE releaseInfo = NULL; PLIST_ENTRY entry; UCHAR requestBuffer[NMP_MADCAP_REQUEST_BUFFER_SIZE]; PMCAST_LEASE_REQUEST request; // // Pop a lease data structure off the release list. // if (IsListEmpty(&(Network->McastAddressReleaseList))) { return(ERROR_SUCCESS); } entry = RemoveHeadList(&(Network->McastAddressReleaseList)); releaseInfo = CONTAINING_RECORD( entry, NM_NETWORK_MADCAP_ADDRESS_RELEASE, Linkage ); // // Release the network lock. // NmpReleaseLock(); lockAcquired = FALSE; ClRtlLogPrint(LOG_NOISE, "[NM] Releasing multicast address %1!ws! for " "network %2!ws!.\n", releaseInfo->McastAddress, networkId ); // // Initialize MADCAP, if not done already. // if (!NmpMadcapClientInitialized) { DWORD madcapVersion = MCAST_API_CURRENT_VERSION; status = McastApiStartup(&madcapVersion); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to initialize MADCAP API, " "status %1!u!.\n", status ); goto error_exit; } NmpMadcapClientInitialized = TRUE; } // // Build the MADCAP request structure. // request = (PMCAST_LEASE_REQUEST) &requestBuffer[0]; RtlZeroMemory(request, sizeof(requestBuffer)); request->MinLeaseDuration = 0; // currently ignored request->MinAddrCount = 1; // currently ignored request->MaxLeaseStartTime = (LONG) time(NULL); // currently ignored request->AddrCount = 1; request->pAddrBuf = (PBYTE)request + NMP_MADCAP_REQUEST_ADDR_OFFSET; status = ClRtlTcpipStringToAddress( releaseInfo->McastAddress, ((PULONG) request->pAddrBuf) ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to convert requested address %1!ws! " "into a TCP/IP address, status %2!u!.\n", releaseInfo->McastAddress, status ); goto error_exit; } status = ClRtlTcpipStringToAddress( releaseInfo->ServerAddress, (PULONG) &(request->ServerAddress.IpAddrV4) ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to convert server address %1!ws! " "into a TCP/IP address, status %2!u!.\n", releaseInfo->ServerAddress, status ); goto error_exit; } // // Call MADCAP to release the address. // status = McastReleaseAddress( AF_INET, &releaseInfo->RequestId, request ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to release multicast address %1!ws! " "through MADCAP server %2!ws!, status %3!u!.\n", releaseInfo->McastAddress, releaseInfo->ServerAddress, status ); goto error_exit; } ClRtlLogPrint(LOG_NOISE, "[NM] Successfully released multicast address " "%1!ws! for network %2!ws!.\n", releaseInfo->McastAddress, networkId ); error_exit: NmpFreeMulticastAddressRelease(releaseInfo); if (!lockAcquired) { NmpAcquireLock(); lockAcquired = TRUE; } if (!IsListEmpty(&(Network->McastAddressReleaseList))) { NmpScheduleMulticastAddressRelease(Network); } return(status); } // NmpReleaseMulticastAddress DWORD NmpProcessMulticastConfiguration( IN PNM_NETWORK Network, IN PNM_NETWORK_MULTICAST_PARAMETERS Parameters, OUT PNM_NETWORK_MULTICAST_PARAMETERS UndoParameters ) /*++ Routine Description: Processes configuration changes and calls clusnet if appropriate. If multicast is disabled, the address and key may be NULL. In this case, choose defaults to send to clusnet, but do not commit the changes in the local network object. Arguments: Network - network to process Parameters - parameters with which to configure Network. If successful, Parameters data structure is cleared. UndoParameters - If successful, former multicast parameters of Network. Must be freed by caller. Notes: Called and returns with NM lock held. --*/ { DWORD status = ERROR_SUCCESS; LPWSTR networkId = (LPWSTR) OmObjectId(Network); BOOLEAN callClusnet = FALSE; LPWSTR mcastAddress = NULL; DWORD brand; PVOID tdiMcastAddress = NULL; DWORD tdiMcastAddressLength = 0; UUID networkIdGuid; BOOLEAN mcastAddrChange = FALSE; BOOLEAN mcastKeyChange = FALSE; PVOID EncryptedMulticastKey = NULL; DWORD EncryptedMulticastKeyLength = 0; PVOID CurrentMulticastKey = NULL; DWORD CurrentMulticastKeyLength = 0; #if CLUSTER_BETA ClRtlLogPrint(LOG_NOISE, "[NM] Processing multicast configuration parameters " "for network %1!ws!.\n", networkId /* , ((Parameters->Address != NULL) ? Parameters->Address : L"") */ ); #endif // CLUSTER_BETA // // Zero the undo parameters so that freeing them is not // destructive. // RtlZeroMemory(UndoParameters, sizeof(*UndoParameters)); // // First determine if we need to reconfigure clusnet. // if (Parameters->Address != NULL) { if (Network->MulticastAddress == NULL || wcscmp(Network->MulticastAddress, Parameters->Address) != 0) { // The multicast address in the config parameters is // different from the one in memory. mcastAddrChange = TRUE; } mcastAddress = Parameters->Address; } else { mcastAddress = NmpNullMulticastAddress; } if (Parameters->Key != NULL) { // // Unprotect the current key to see if it has changed. // if (Network->EncryptedMulticastKey != NULL) { status = NmpUnprotectData( Network->EncryptedMulticastKey, Network->EncryptedMulticastKeyLength, &CurrentMulticastKey, &CurrentMulticastKeyLength ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to decrypt multicast key " "for network %1!ws! while processing new " "parameters, status %2!u!. " "Assuming key has changed.\n", networkId, status ); // Non-fatal error. Assume the key has changed. // The check below will find CurrentMulticastKey // still NULL, and mcastKeyChange will be set true. } } if (CurrentMulticastKey == NULL || (CurrentMulticastKeyLength != Parameters->KeyLength || RtlCompareMemory( CurrentMulticastKey, Parameters->Key, Parameters->KeyLength ) != Parameters->KeyLength )) { // // The key in the config parameters is different // from the key in memory. // mcastKeyChange = TRUE; } } if (!Parameters->Disabled && (!NmpIsNetworkMulticastEnabled(Network))) { // Multicast is now enabled. Call clusnet with the new address. callClusnet = TRUE; } if (Parameters->Disabled && (NmpIsNetworkMulticastEnabled(Network))) { // Multicast is now disabled. Call clusnet with NULL address // regardless of which address was specified in the // parameters. mcastAddress = NmpNullMulticastAddress; callClusnet = TRUE; } if (!Parameters->Disabled && (mcastAddrChange || mcastKeyChange )) { // The multicast address, and/or key changed and // multicast is enabled. callClusnet = TRUE; } if (callClusnet) { // // If this network does not have a local interface, do not // plumb the configuration into clusnet or commit changes. // However, the error status must be success so that we // don't fail a GUM update. // if (Network->LocalInterface == NULL) { ClRtlLogPrint(LOG_NOISE, "[NM] Not configuring cluster network driver with " "multicast parameters because network %1!ws! " "has no local interface.\n", networkId ); status = ERROR_SUCCESS; callClusnet = FALSE; } } if (callClusnet) { // // If this network is not yet registered, do not plumb // the configuration into clusnet or commit changes. // However, the error status must be success so that we // don't fail a GUM update. // if (!NmpIsNetworkRegistered(Network)) { ClRtlLogPrint(LOG_NOISE, "[NM] Not configuring cluster network driver with " "multicast parameters because network %1!ws! " "is not registered.\n", networkId ); status = ERROR_SUCCESS; callClusnet = FALSE; } } // // Finalize the address and brand parameters for // clusnet. The new configuration will reflect the current // parameters block except for the address, which is stored // in temporary mcastAddress variable. mcastAddress points // either to the address in the parameters block or // the NULL multicast address if we are disabling. // if (callClusnet) { // // We cannot hand a NULL key to clusnet with a // valid address. It is okay to store the new address, // but make sure we do not try to send with no key // (allowing sending with no key could open a security // vulnerability). // if (mcastAddress != NmpNullMulticastAddress && Parameters->Key == NULL) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Configuring valid multicast address " "with invalid key is not allowed. Zeroing " "multicast address for clusnet configuration " "of network %1!ws!.\n", networkId ); mcastAddress = NmpNullMulticastAddress; } // // Build a TDI address from the address string. // status = ClRtlBuildTcpipTdiAddress( mcastAddress, Network->LocalInterface->ClusnetEndpoint, &tdiMcastAddress, &tdiMcastAddressLength ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to build TCP/IP TDI multicast address " "%1!ws! port %2!ws! for network %3!ws!, " "status %4!u!.\n", mcastAddress, Network->LocalInterface->ClusnetEndpoint, networkId, status ); // // Non-fatal error. A node should not get banished // from the cluster for this. Skip the call to // clusnet. // callClusnet = FALSE; status = ERROR_SUCCESS; } // // Use the lower bytes of the network GUID for the // brand. // status = UuidFromString(networkId, &networkIdGuid); if (status == RPC_S_OK) { brand = *((PDWORD)&(networkIdGuid.Data4[4])); } else { brand = 0; } } // // Plumb the new configuration into clusnet. // if (callClusnet) { ClRtlLogPrint(LOG_NOISE, "[NM] Configuring cluster network driver with " "multicast parameters for network %1!ws!.\n", networkId ); #ifdef MULTICAST_DEBUG NmpDbgPrintData(L"NmpProcessMulticastConfiguration(): before ClusnetConfigureMulticast()", Parameters->Key, Parameters->KeyLength ); #endif status = ClusnetConfigureMulticast( NmClusnetHandle, Network->ShortId, brand, tdiMcastAddress, tdiMcastAddressLength, Parameters->Key, Parameters->KeyLength ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to configure multicast parameters " "for network %1!ws!, status %2!u!.\n", networkId, status ); goto error_exit; } else { if (!Parameters->Disabled) { Network->Flags |= NM_FLAG_NET_MULTICAST_ENABLED; } else { Network->Flags &= ~NM_FLAG_NET_MULTICAST_ENABLED; } } } // // Commit the changes to the network object. // The old state of the network object will be stored in // the undo parameters, in case we need to undo this change. // The new state of the network object will reflect the // paramters block, including the address (even if we // disabled). // UndoParameters->Address = Network->MulticastAddress; Network->MulticastAddress = Parameters->Address; UndoParameters->ConfigType = Network->ConfigType; Network->ConfigType = Parameters->ConfigType; UndoParameters->LeaseObtained = Network->MulticastLeaseObtained; Network->MulticastLeaseObtained = Parameters->LeaseObtained; UndoParameters->LeaseExpires = Network->MulticastLeaseExpires; Network->MulticastLeaseExpires = Parameters->LeaseExpires; UndoParameters->LeaseRequestId = Network->MulticastLeaseRequestId; Network->MulticastLeaseRequestId = Parameters->LeaseRequestId; UndoParameters->LeaseServer = Network->MulticastLeaseServer; Network->MulticastLeaseServer = Parameters->LeaseServer; if (Parameters->Key != NULL && Parameters->KeyLength != 0) { status = NmpProtectData(Parameters->Key, Parameters->KeyLength, &EncryptedMulticastKey, &EncryptedMulticastKeyLength ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to encrypt multicast key " "for network %1!ws! while processing new " "parameters, status %2!u!.\n", networkId, status ); // Non-fatal error. Next update we will assume // the key has changed. In the meantime, if // somebody asks us for the key, we will return // NULL. } } UndoParameters->Key = Network->EncryptedMulticastKey; Network->EncryptedMulticastKey = EncryptedMulticastKey; UndoParameters->KeyLength = Network->EncryptedMulticastKeyLength; Network->EncryptedMulticastKeyLength = EncryptedMulticastKeyLength; UndoParameters->MulticastKeyExpires = Network->MulticastKeyExpires; Network->MulticastKeyExpires = Parameters->MulticastKeyExpires; // // Zero the parameters structure so that the memory now // pointed to by the network object is not freed. // RtlZeroMemory(Parameters, sizeof(*Parameters)); error_exit: if (CurrentMulticastKey != NULL) { RtlSecureZeroMemory(CurrentMulticastKey, CurrentMulticastKeyLength); LocalFree(CurrentMulticastKey); } if (tdiMcastAddress != NULL) { LocalFree(tdiMcastAddress); tdiMcastAddress = NULL; } return(status); } // NmpProcessMulticastConfiguration VOID NmpNetworkMadcapWorker( 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. --*/ { DWORD status; PNM_NETWORK network = (PNM_NETWORK) WorkItem->Context; LPCWSTR networkId = OmObjectId(network); BOOLEAN rescheduled = FALSE; NmpAcquireLock(); ClRtlLogPrint(LOG_NOISE, "[NM] Worker thread processing MADCAP client requests " "for network %1!ws!.\n", networkId ); if ((NmpState >= NmStateOnlinePending) && !NM_DELETE_PENDING(network)) { while (TRUE) { if (!(network->Flags & NM_NET_MADCAP_WORK_FLAGS)) { // // No more work to do - break out of loop. // break; } // // Reconfigure multicast if needed. // if (network->Flags & NM_FLAG_NET_RECONFIGURE_MCAST) { network->Flags &= ~NM_FLAG_NET_RECONFIGURE_MCAST; status = NmpReconfigureMulticast(network); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to reconfigure multicast " "for network %1!ws!, status %2!u!.\n", networkId, status ); } } // // Renew an address lease if needed. // if (network->Flags & NM_FLAG_NET_RENEW_MCAST_ADDRESS) { network->Flags &= ~NM_FLAG_NET_RENEW_MCAST_ADDRESS; status = NmpRenewMulticastAddressLease(network); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to renew multicast address " "lease for network %1!ws!, status %2!u!.\n", networkId, status ); } } // // Release an address lease if needed. // if (network->Flags & NM_FLAG_NET_RELEASE_MCAST_ADDRESS) { network->Flags &= ~NM_FLAG_NET_RELEASE_MCAST_ADDRESS; status = NmpReleaseMulticastAddress(network); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to release multicast address " "lease for network %1!ws!, status %2!u!.\n", networkId, status ); } } // // Regenerate multicast key if needed. // if (network->Flags & NM_FLAG_NET_REGENERATE_MCAST_KEY) { network->Flags &= ~NM_FLAG_NET_REGENERATE_MCAST_KEY; status = NmpRegenerateMulticastKey(network); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to regenerate multicast key" "for network %1!ws!, status %2!u!.\n", networkId, status ); } } if (!(network->Flags & NM_NET_MADCAP_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 MADCAP work to do for network %1!ws!. " "Rescheduling worker thread.\n", networkId ); status = NmpScheduleNetworkMadcapWorker(network); if (status == ERROR_SUCCESS) { rescheduled = TRUE; break; } } } else { network->Flags &= ~NM_NET_MADCAP_WORK_FLAGS; } if (!rescheduled) { network->Flags &= ~NM_FLAG_NET_MADCAP_WORKER_RUNNING; } ClRtlLogPrint(LOG_NOISE, "[NM] Worker thread finished processing MADCAP client " "requests for network %1!ws!.\n", networkId ); NmpLockedLeaveApi(); NmpReleaseLock(); OmDereferenceObject(network); return; } // NmpNetworkMadcapWorker DWORD NmpScheduleNetworkMadcapWorker( PNM_NETWORK Network ) /*++ Routine Description: Schedule a worker thread to execute madcap client requests for 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->MadcapWorkItem), NmpNetworkMadcapWorker, (PVOID) Network ); status = ClRtlPostItemWorkQueue( CsDelayedWorkQueue, &(Network->MadcapWorkItem), 0, 0 ); if (status == ERROR_SUCCESS) { ClRtlLogPrint(LOG_NOISE, "[NM] Scheduled worker thread to execute MADCAP " "client requests for network %1!ws!.\n", networkId ); NmpActiveThreadCount++; Network->Flags |= NM_FLAG_NET_MADCAP_WORKER_RUNNING; OmReferenceObject(Network); } else { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to schedule worker thread to execute " "MADCAP client requests for network " "%1!ws!, status %2!u!\n", networkId, status ); } return(status); } // NmpScheduleNetworkMadcapWorker VOID NmpShareMulticastAddressLease( IN PNM_NETWORK Network, IN BOOLEAN Refresh ) /*++ Routine description: Called after a multicast configuration change, sets a timer to renew the multicast address lease for this network, if one exists. If this call is made from a refresh, it is not the result of a global update and it may be out of sync with other nodes. For instance, if this network was just enabled for cluster use, the NM leader node may be trying to obtain the multicast configuration at the same time this node is refreshing it. Consequently, delay a minimum amount of time before renewing a multicast address if this is a refresh. Arguments: Network - multicast network Refresh - TRUE if this call is made during a refresh Notes: Called and returns with NM lock held. --*/ { DWORD status; LPCWSTR networkId = OmObjectId(Network); DWORD disabled; BOOLEAN delay = TRUE; time_t leaseObtained; time_t leaseExpires; DWORD leaseTimer = 0; NM_MCAST_LEASE_STATUS leaseStatus = NmMcastLeaseValid; if (NmpIsNetworkMulticastEnabled(Network)) { #if CLUSTER_BETA ClRtlLogPrint(LOG_NOISE, "[NM] Sharing ownership of multicast lease " "for network %1!ws!.\n", networkId ); #endif // CLUSTER_BETA NmpCheckMulticastAddressLease( Network, &leaseStatus, &leaseObtained, &leaseExpires ); if (leaseStatus != NmMcastLeaseValid) { if (Refresh) { leaseTimer = NMP_MCAST_REFRESH_RENEW_DELAY; } else { delay = FALSE; } } else { leaseTimer = NmpCalculateLeaseRenewTime( Network, Network->ConfigType, &leaseObtained, &leaseExpires ); if (Refresh && (leaseTimer < NMP_MCAST_REFRESH_RENEW_DELAY)) { leaseTimer = NMP_MCAST_REFRESH_RENEW_DELAY; } } } else { #if CLUSTER_BETA ClRtlLogPrint(LOG_NOISE, "[NM] No need to share multicast lease renewal " "responsibility for network %1!ws! because " "multicast has been disabled.\n", networkId ); #endif // CLUSTER_BETA // // Clear the timer, if it is set. // leaseTimer = 0; } if (delay) { NmpStartNetworkMulticastAddressRenewTimer(Network, leaseTimer); } else { NmpScheduleMulticastAddressRenewal(Network); } return; } // NmpShareMulticastAddressLease VOID NmpShareMulticastKeyRegeneration( IN PNM_NETWORK Network, IN BOOLEAN Refresh ) /*++ Routine description: Called after a multicast configuration change, sets a timer to regenerate the multicast key for this network, if one exists. If this call is made from a refresh, it is not the result of a global update and it may be out of sync with other nodes. For instance, if this network was just enabled for cluster use, the NM leader node may be trying to obtain the multicast configuration at the same time this node is refreshing it. Consequently, delay a minimum amount of time before regenerating a new key if this is a refresh. Arguments: Network - multicast network Refresh - TRUE if this call is made during a refresh Notes: Called and returns with NM lock held. --*/ { DWORD regenTimeout; DWORD baseTimeout, minTimeout; if (NmpIsNetworkMulticastEnabled(Network)) { #if CLUSTER_BETA ClRtlLogPrint(LOG_NOISE, "[NM] Sharing ownership of multicast key " "regeneration for network %1!ws!.\n", networkId ); #endif // CLUSTER_BETA if (Network->EncryptedMulticastKey != NULL) { // Set the key regeneration timer according to the // parameters or the default. baseTimeout = (Network->MulticastKeyExpires != 0) ? Network->MulticastKeyExpires : NM_NET_MULTICAST_KEY_REGEN_TIMEOUT; minTimeout = (Refresh) ? NMP_MCAST_REFRESH_RENEW_DELAY : 1000; regenTimeout = NmpRandomizeTimeout( Network, baseTimeout, NM_NET_MULTICAST_KEY_REGEN_TIMEOUT_WINDOW, minTimeout, MAXULONG, TRUE ); } else { // Clear the key regeneration timer because there is // no key. regenTimeout = 0; } } else { #if CLUSTER_BETA ClRtlLogPrint(LOG_NOISE, "[NM] No need to share multicast key regeneration " "responsibility for network %1!ws! because " "multicast has been disabled.\n", networkId ); #endif // CLUSTER_BETA // // Clear the timer, if it is set. // regenTimeout = 0; } NmpStartNetworkMulticastKeyRegenerateTimer(Network, regenTimeout); return; } DWORD NmpMulticastFormManualConfigParameters( IN PNM_NETWORK Network, IN HDMKEY NetworkKey, IN HDMKEY NetworkParametersKey, IN BOOLEAN DisableConfig, IN DWORD Disabled, IN BOOLEAN McastAddressConfig, IN LPWSTR McastAddress, OUT BOOLEAN * NeedUpdate, OUT PNM_NETWORK_MULTICAST_PARAMETERS Parameters ) /*++ Routine Description: Using parameters provided and those already configured, form a parameters structure to reflect a manual configuration. Arguments: Network - network being configured NetworkKey - network key in cluster database NetworkParametersKey - network parameters key in cluster database DisableConfig - whether the disabled value was set Disabled - if DisableConfig, the value that was set McastAddressConfig - whether the multicast address value was set McastAddress - if McastAddressConfig, the value that was set NeedUpdate - indicates whether an update is needed, i.e. whether anything changed Parameters - parameter structure, allocated by caller, to fill in --*/ { DWORD status; LPCWSTR networkId = OmObjectId(Network); HDMKEY networkKey = NULL; HDMKEY netParamKey = NULL; HDMKEY clusParamKey = NULL; BOOLEAN lockAcquired = FALSE; DWORD regDisabled; BOOLEAN disabledChange = FALSE; BOOLEAN mcastAddressDefault = FALSE; BOOLEAN mcastAddressChange = FALSE; BOOLEAN getAddress = FALSE; DWORD len; BOOLEAN localInterface = FALSE; LPWSTR mcastAddress = NULL; LPWSTR serverAddress = NULL; MCAST_CLIENT_UID requestId = {NULL, 0}; PNM_NETWORK_MADCAP_ADDRESS_RELEASE release = NULL; // // Validate incoming parameters. // // Any nonzero disabled value is set to 1 for simplification. // if (DisableConfig) { if (Disabled != 0) { Disabled = 1; } } // // Non-valid and NULL multicast addresses signify // revert-to-default. // if (McastAddressConfig && (McastAddress == NULL || !NmpMulticastValidateAddress(McastAddress))) { mcastAddressDefault = TRUE; McastAddress = NULL; } // // Base decisions on the current status of the network // object and cluster database. Before acquiring the NM lock, // determine whether we are currently disabled by querying the // cluster database. // status = NmpQueryMulticastDisabled( Network, &clusParamKey, &networkKey, &netParamKey, ®Disabled ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to determine whether multicast " "is disabled for network %1!ws!, status %2!u!.\n", networkId, status ); goto error_exit; } else { // // Registry keys are no longer needed. // if (clusParamKey != NULL) { DmCloseKey(clusParamKey); clusParamKey = NULL; } if (netParamKey != NULL) { DmCloseKey(netParamKey); netParamKey = NULL; } if (networkKey != NULL) { DmCloseKey(networkKey); networkKey = NULL; } } NmpAcquireLock(); lockAcquired = TRUE; // // See if anything changed. // if (DisableConfig) { if (Disabled != regDisabled) { disabledChange = TRUE; } } if (McastAddressConfig) { if (mcastAddressDefault) { mcastAddressChange = TRUE; } else { if (Network->MulticastAddress != NULL) { if (lstrcmpW(Network->MulticastAddress, McastAddress) != 0) { mcastAddressChange = TRUE; } } else { mcastAddressChange = TRUE; } } } if (!disabledChange && !mcastAddressChange) { *NeedUpdate = FALSE; status = ERROR_SUCCESS; #if CLUSTER_BETA ClRtlLogPrint(LOG_NOISE, "[NM] Private property update to network %1!ws! " "contains no multicast changes.\n", networkId ); #endif // CLUSTER_BETA goto error_exit; } // // Initialize the parameters from the network object. // status = NmpMulticastCreateParameters( regDisabled, Network->MulticastAddress, NULL, // key 0, // key length Network->MulticastLeaseObtained, Network->MulticastLeaseExpires, &Network->MulticastLeaseRequestId, Network->MulticastLeaseServer, NmMcastConfigManual, Parameters ); if (status != ERROR_SUCCESS) { goto error_exit; } localInterface = (BOOLEAN)(Network->LocalInterface != NULL); NmpReleaseLock(); lockAcquired = FALSE; if (mcastAddressChange) { // // Figure out what address to use. // if (!mcastAddressDefault) { // // An address was dictated. // // If we currently have a leased address, release it. // if (NmpNeedRelease( Parameters->Address, Parameters->LeaseServer, &(Parameters->LeaseRequestId), Parameters->LeaseExpires )) { status = NmpCreateMulticastAddressRelease( Parameters->Address, Parameters->LeaseServer, &(Parameters->LeaseRequestId), &release ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to create multicast address " "release for address %1!ws! on network %2!ws! " "during manual configuration, status %3!u!.\n", ((Parameters->Address != NULL) ? Parameters->Address : L""), networkId, status ); goto error_exit; } } // // Store the new address in the parameters data structure. // status = NmpStoreString( McastAddress, &Parameters->Address, NULL ); if (status != ERROR_SUCCESS) { goto error_exit; } Parameters->ConfigType = NmMcastConfigManual; Parameters->LeaseObtained = 0; Parameters->LeaseExpires = 0; // // Clear out the lease server. // len = (Parameters->LeaseServer != NULL) ? NM_WCSLEN(Parameters->LeaseServer) : 0; status = NmpStoreString( NmpNullMulticastAddress, &Parameters->LeaseServer, &len ); if (status != ERROR_SUCCESS) { goto error_exit; } } else { // // Need to find an address elsewhere. // getAddress = TRUE; } } // // We also may need to renew the lease if we are moving from // disabled to enabled and an address was not specified, but // only if we don't already have a lease that doesn't expire. // if (disabledChange && !Disabled) { Parameters->Disabled = 0; if (!mcastAddressChange) { // // An address was not set. All we currently have is // what's in the network object (and copied to the // parameters block). // if (Parameters->Address != NULL && NmpMulticastValidateAddress(Parameters->Address)) { // // We already have a valid multicast address, but // the lease may need to be renewed. // if (Parameters->LeaseExpires != 0) { getAddress = TRUE; } else { Parameters->ConfigType = NmMcastConfigManual; } } else { // // We have no valid multicast address. Get one. // getAddress = TRUE; } } } // // We don't bother renewing the lease if we are disabling. // if (Disabled) { getAddress = FALSE; Parameters->Disabled = Disabled; // // If we currently have a leased address that we haven't // already decided to release, release it. // if (release == NULL && NmpNeedRelease( Parameters->Address, Parameters->LeaseServer, &(Parameters->LeaseRequestId), Parameters->LeaseExpires )) { status = NmpCreateMulticastAddressRelease( Parameters->Address, Parameters->LeaseServer, &(Parameters->LeaseRequestId), &release ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to create multicast address " "release for address %1!ws! on network %2!ws! " "during manual configuration, status %3!u!.\n", ((Parameters->Address != NULL) ? Parameters->Address : L""), networkId, status ); goto error_exit; } // // Since we are releasing the address, there is not // much point in saving it. If we re-enable multicast // in the future, we will request a fresh lease. // len = (Parameters->LeaseServer != NULL) ? NM_WCSLEN(Parameters->LeaseServer) : 0; status = NmpStoreString( NmpNullMulticastAddress, &Parameters->LeaseServer, &len ); if (status != ERROR_SUCCESS) { goto error_exit; } len = (Parameters->Address != NULL) ? NM_WCSLEN(Parameters->Address) : 0; status = NmpStoreString( NmpNullMulticastAddress, &Parameters->Address, &len ); if (status != ERROR_SUCCESS) { goto error_exit; } // requestId is initialized to be blank status = NmpStoreRequestId( &requestId, &Parameters->LeaseRequestId ); if (status != ERROR_SUCCESS) { goto error_exit; } // // Remember that this had been a MADCAP address. // Parameters->ConfigType = NmMcastConfigMadcap; } else if (!(mcastAddressChange && !mcastAddressDefault)) { // // If no address is being set, we may keep the former // address in the database even though it is not being // used. We also need to remember the way we got that // address in case it is ever used again. If we fail // to determine the previous configuration, we need // to set it to manual so that we don't lose a manual // configuration. // status = NmpQueryMulticastConfigType( Network, NetworkKey, &NetworkParametersKey, &Parameters->ConfigType ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to query multicast address " "config type for network %1!ws! during " "manual configuration, status %2!u!.\n", networkId, status ); Parameters->ConfigType = NmMcastConfigManual; } } } // // Synchronously get a new address. // if (getAddress) { // // Create temporary strings for proposed address, lease // server, and request id. // status = NmpStoreString(Parameters->Address, &mcastAddress, NULL); if (status != ERROR_SUCCESS) { goto error_exit; } status = NmpStoreString(Parameters->LeaseServer, &serverAddress, NULL); if (status != ERROR_SUCCESS) { goto error_exit; } status = NmpStoreRequestId(&Parameters->LeaseRequestId, &requestId); if (status != ERROR_SUCCESS) { goto error_exit; } // // Get the address. // status = NmpGetMulticastAddress( Network, &mcastAddress, &serverAddress, &requestId, Parameters ); if (status != ERROR_SUCCESS) { if (status == ERROR_TIMEOUT) { // // MADCAP server is not responding. Choose an // address, but only if there is a local // interface on this network. Otherwise, we // cannot assume that the MADCAP server is // unresponsive because we may have no way to // contact it. // if (!localInterface) { status = ERROR_CLUSTER_NETINTERFACE_NOT_FOUND; ClRtlLogPrint(LOG_UNUSUAL, "[NM] Cannot choose a multicast address " "for network %1!ws! because this node " "has no local interface.\n", networkId ); } else { status = NmpChooseMulticastAddress( Network, Parameters ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to choose a default multicast " "address for network %1!ws!, status %2!u!.\n", networkId, status ); } else { NmpReportMulticastAddressChoice( Network, Parameters->Address, NULL ); } } } } else { NmpReportMulticastAddressLease( Network, Parameters, NULL ); } if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to get multicast address for " "network %1!ws! during manual configuration, " "status %2!u!.\n", networkId, status ); NmpReportMulticastAddressFailure(Network, status); NmpMulticastSetNoAddressParameters(Network, Parameters); } } if (((DisableConfig && !Disabled) || (!DisableConfig && !regDisabled)) && (status == ERROR_SUCCESS)) { // // Create new multicast key. // status = NmpCreateRandomNumber(&(Parameters->Key), MulticastKeyLen ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to create random number " "for network %1!ws!, status %2!u!.\n", networkId, status ); goto error_exit; } Parameters->KeyLength = MulticastKeyLen; } *NeedUpdate = TRUE; // // Check if we have an address to release. // if (release != NULL) { NmpAcquireLock(); NmpInitiateMulticastAddressRelease(Network, release); release = NULL; NmpReleaseLock(); } error_exit: if (lockAcquired) { NmpReleaseLock(); lockAcquired = FALSE; } if (requestId.ClientUID != NULL) { MIDL_user_free(requestId.ClientUID); RtlZeroMemory(&requestId, sizeof(requestId)); } if (mcastAddress != NULL && !NMP_GLOBAL_STRING(mcastAddress)) { MIDL_user_free(mcastAddress); mcastAddress = NULL; } if (serverAddress != NULL && !NMP_GLOBAL_STRING(serverAddress)) { MIDL_user_free(serverAddress); serverAddress = NULL; } if (release != NULL) { NmpFreeMulticastAddressRelease(release); } if (clusParamKey != NULL) { DmCloseKey(clusParamKey); clusParamKey = NULL; } if (netParamKey != NULL) { DmCloseKey(netParamKey); netParamKey = NULL; } if (networkKey != NULL) { DmCloseKey(networkKey); networkKey = NULL; } return(status); } // NmpMulticastFormManualConfigParameters DWORD NmpReconfigureMulticast( IN PNM_NETWORK Network ) /*++ Routine Description: Create the multicast configuration for this network for the cluster. This includes the following: - Check the address lease and renew if necessary. - Generate a new multicast key. The address lease is checked first. If the lease needs to be renewed, schedule a worker thread to do it asynchronously. Notes: Called and returns with NM lock held. --*/ { DWORD status; LPWSTR networkId = (LPWSTR) OmObjectId(Network); HDMKEY networkKey = NULL; HDMKEY netParamKey = NULL; HDMKEY clusParamKey = NULL; BOOLEAN lockAcquired = TRUE; NM_NETWORK_MULTICAST_PARAMETERS params = { 0 }; NM_MCAST_LEASE_STATUS leaseStatus = NmMcastLeaseValid; DWORD mcastAddressLength = 0; ClRtlLogPrint(LOG_NOISE, "[NM] Reconfiguring multicast for network %1!ws!.\n", networkId ); // // Clear reconfiguration timer and work flag. // This timer is set in NmpMulticastCheckReconfigure. It is not // necessary to call NmpReconfigureMulticast() twice. // Network->Flags &= ~NM_FLAG_NET_RECONFIGURE_MCAST; Network->McastAddressReconfigureRetryTimer = 0; NmpReleaseLock(); lockAcquired = FALSE; // // Check if multicast is disabled. This has the side-effect, // on success, of opening at least the network key, and // possibly the network parameters key (if it exists) and // the cluster parameters key. // status = NmpQueryMulticastDisabled( Network, &clusParamKey, &networkKey, &netParamKey, ¶ms.Disabled ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to determine whether multicast " "is disabled for network %1!ws!, status %2!u!.\n", networkId, status ); goto error_exit; } // // Read the address from the database. It may have // been configured manually, and we do not want to // lose it. // status = NmpQueryMulticastAddress( Network, networkKey, &netParamKey, ¶ms.Address, &mcastAddressLength ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to read multicast address " "for network %1!ws! from cluster " "database, status %2!u!.\n", networkId, status ); } // // Only proceed with lease renewal if multicast is // not disabled. // if (!params.Disabled) { // // Check the address lease. // status = NmpQueryMulticastAddressLease( Network, networkKey, &netParamKey, &leaseStatus, ¶ms.LeaseObtained, ¶ms.LeaseExpires ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to determine multicast address " "lease status for network %1!ws!, status %2!u!.\n", networkId, status ); if (params.Address == NULL) { // We did not find an address. Assume we // should obtain an address automatically. params.LeaseObtained = time(NULL); params.LeaseExpires = time(NULL); leaseStatus = NmMcastLeaseExpired; } else { // We found an address but not any lease // parameters. Assume that the address // was manually configured. params.ConfigType = NmMcastConfigManual; params.LeaseObtained = 0; params.LeaseExpires = 0; leaseStatus = NmMcastLeaseValid; } } // // If we think we have a valid lease, check first // how we got it. If the address was selected // rather than obtained via MADCAP, go through // the MADCAP query process again. // if (leaseStatus == NmMcastLeaseValid) { status = NmpQueryMulticastConfigType( Network, networkKey, &netParamKey, ¶ms.ConfigType ); if (status != ERROR_SUCCESS) { // // Since we already have an address, stick // with whatever information we deduced // from the lease expiration. // ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to determine the type of the " "multicast address for network %1!ws!, " "status %2!u!. Assuming manual configuration.\n", networkId, status ); } else if (params.ConfigType == NmMcastConfigAuto) { leaseStatus = NmMcastLeaseNeedsRenewal; } } // // If we need to renew the lease, we may block // indefinitely due to the madcap API. Schedule // the renewal and defer configuration to when // it completes. // if (leaseStatus != NmMcastLeaseValid) { NmpAcquireLock(); NmpScheduleMulticastAddressRenewal(Network); NmpReleaseLock(); status = ERROR_IO_PENDING; goto error_exit; } else { // // Ensure that the lease expiration is set correctly // (a side effect of calculating the lease renew time). // We don't actually set the lease renew timer // here. Instead, we wait for the notification // that the new parameters have been disseminated // to all cluster nodes. // NmpCalculateLeaseRenewTime( Network, params.ConfigType, ¶ms.LeaseObtained, ¶ms.LeaseExpires ); } // // Create new multicast key // status = NmpCreateRandomNumber(&(params.Key), MulticastKeyLen ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to create random number " "for network %1!ws!, status %2!u!.\n", networkId, status ); goto error_exit; } ClRtlLogPrint(LOG_NOISE, "[NM] Successfully created multicast key " "for network %1!ws!.\n", networkId ); params.KeyLength = MulticastKeyLen; } // // Registry keys are no longer needed. // if (clusParamKey != NULL) { DmCloseKey(clusParamKey); clusParamKey = NULL; } if (netParamKey != NULL) { DmCloseKey(netParamKey); netParamKey = NULL; } if (networkKey != NULL) { DmCloseKey(networkKey); networkKey = NULL; } // // Disseminate the configuration. // status = NmpMulticastNotifyConfigChange( Network, networkKey, &netParamKey, ¶ms, NULL, 0 ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to disseminate multicast " "configuration for network %1!ws!, " "status %2!u!.\n", networkId, status ); goto error_exit; } error_exit: if (clusParamKey != NULL || netParamKey != NULL || networkKey != NULL) { if (lockAcquired) { NmpReleaseLock(); lockAcquired = FALSE; } if (clusParamKey != NULL) { DmCloseKey(clusParamKey); clusParamKey = NULL; } if (netParamKey != NULL) { DmCloseKey(netParamKey); netParamKey = NULL; } if (networkKey != NULL) { DmCloseKey(networkKey); networkKey = NULL; } } NmpMulticastFreeParameters(¶ms); if (!lockAcquired) { NmpAcquireLock(); lockAcquired = TRUE; } return(status); } // NmpReconfigureMulticast VOID NmpScheduleMulticastReconfiguration( IN PNM_NETWORK Network ) /*++ Routine Description: Schedules a worker thread to reconfigure multicast for a network. Note that we do not use the network worker thread because the madcap API is unfamiliar and therefore unpredictable. Arguments: A pointer to the network to renew. 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 (!NmpIsNetworkMadcapWorkerRunning(Network)) { status = NmpScheduleNetworkMadcapWorker(Network); } if (status == ERROR_SUCCESS) { // // We succeeded in scheduling a worker thread. Stop the // retry timer and set the registration work flag. // Network->McastAddressReconfigureRetryTimer = 0; Network->Flags |= NM_FLAG_NET_RECONFIGURE_MCAST; } else { // // We failed to schedule a worker thread. Set the retry // timer to expire on the next tick, so we can try again. // Network->McastAddressReconfigureRetryTimer = 1; } return; } // NmpScheduleMulticastReconfiguration VOID NmpMulticastCheckReconfigure( IN PNM_NETWORK Network ) /*++ Routine Description: Checks whether the NM leader has a local interface on this network. If not, sets the reconfigure timer such that other nodes will have a chance to reconfigure multicast for this network, but if several minutes pass and it doesn't happen, this node can do it. This timer is cleared in NmpUpdateSetNetworkMulticastConfiguration and NmpReconfigureMulticast. Arguments: Network - network to be reconfigured Return value: None Notes: Called and returns with NM lock held. --*/ { DWORD timeout; if (Network->LocalInterface == NULL) { // This node has no local interface. It should // not bother trying to reconfigure multicast. return; } if (NmpLeaderNodeId != NmLocalNodeId && (NmpGetInterfaceForNodeAndNetworkById( NmpLeaderNodeId, Network->ShortId ) != NULL) ) { // We are not the leader, and the leader node has an // interface on this network. It is responsibile for // reconfiguring multicast. If it fails, there must // be a good reason. // // If we are the leader, we should not be in this // routine at all, but we will retry the reconfigure // nonetheless. return; } // We will set a timer. Randomize the timeout. timeout = NmpRandomizeTimeout( Network, NM_NET_MULTICAST_RECONFIGURE_TIMEOUT, // Base NM_NET_MULTICAST_RECONFIGURE_TIMEOUT, // Window NM_NET_MULTICAST_RECONFIGURE_TIMEOUT, // Minimum MAXULONG, // Maximum TRUE ); NmpStartNetworkMulticastAddressReconfigureTimer( Network, timeout ); return; } // NmpMulticastCheckReconfigure DWORD NmpStartMulticastInternal( IN PNM_NETWORK Network, IN NM_START_MULTICAST_MODE Mode ) /*++ Routine Description: Start multicast on the specified network after performing network-specific checks (currently only that the network is enabled for cluster use). Reconfigure multicast if the caller is forming the cluster or the NM leader. Refresh the multicast configuration from the cluster database otherwise. Arguments: Network - network on which to start multicast. Mode - indicates caller mode Notes: Must be called with NM lock held. --*/ { // // Do not run multicast config on this network if it // is restricted from cluster use. // if (Network->Role != ClusterNetworkRoleNone) { ClRtlLogPrint(LOG_NOISE, "[NM] Starting multicast for cluster network %1!ws!.\n", OmObjectId(Network) ); if (Mode == NmStartMulticastForm || NmpLeaderNodeId == NmLocalNodeId) { NmpScheduleMulticastReconfiguration(Network); } else { if (Mode != NmStartMulticastDynamic) { NmpScheduleMulticastRefresh(Network); } else { // // Non NM leader node and Mode is NmStartMulticastDynamic: // Set timer to reconfigure multicast for the network, // in case NM leader goes down before issuing // GUM update NmpUpdateSetNetworkMulticastConfiguration. // This timer is cleared in // NmpUpdateSetNetworkMulticastConfiguration. // NmpMulticastCheckReconfigure(Network); } } } return(ERROR_SUCCESS); } // NmpStartMulticastInternal ///////////////////////////////////////////////////////////////////////////// // // Routines exported within NM. // ///////////////////////////////////////////////////////////////////////////// VOID NmpMulticastProcessClusterVersionChange( VOID ) /*++ Routine Description: Called when the cluster version changes. Updates global variables to track whether this is a mixed-mode cluster, and starts or stops multicast if necessary. Notes: Called and returns with NM lock held. --*/ { BOOLEAN startMcast = FALSE; BOOLEAN stop = FALSE; // // Figure out if there is a node in this cluster whose // version reveals that it doesn't speak multicast. // if (CLUSTER_GET_MAJOR_VERSION(CsClusterHighestVersion) < 4) { if (NmpIsNT5NodeInCluster == FALSE) { ClRtlLogPrint(LOG_NOISE, "[NM] Disabling multicast in mixed-mode cluster.\n" ); NmpIsNT5NodeInCluster = TRUE; stop = TRUE; } } else { if (NmpIsNT5NodeInCluster == TRUE) { ClRtlLogPrint(LOG_NOISE, "[NM] Enabling multicast after upgrade from " "mixed-mode cluster.\n" ); NmpIsNT5NodeInCluster = FALSE; startMcast = TRUE; } } if (NmpNodeCount < NMP_MCAST_MIN_CLUSTER_NODE_COUNT) { if (NmpMulticastIsNotEnoughNodes == FALSE) { ClRtlLogPrint(LOG_NOISE, "[NM] There are no longer the minimum number of " "nodes configured in the cluster membership to " "justify multicast.\n" ); NmpMulticastIsNotEnoughNodes = TRUE; stop = TRUE; } } else { if (NmpMulticastIsNotEnoughNodes == TRUE) { ClRtlLogPrint(LOG_NOISE, "[NM] The cluster is configured with enough " "nodes to justify multicast.\n" ); NmpMulticastIsNotEnoughNodes = FALSE; startMcast = TRUE; } } if (stop) { // // Stop multicast, since we are no longer // multicast-ready. // NmpStopMulticast(NULL); // // Don't bother checking whether we are now // multicast-ready. // startMcast = FALSE; } // // Start multicast if this is the NM leader node // and one of the multicast conditions changed. // if ((startMcast) && (NmpLeaderNodeId == NmLocalNodeId) && (!NmpMulticastIsNotEnoughNodes) && (!NmpIsNT5NodeInCluster)) { NmpStartMulticast(NULL, NmStartMulticastDynamic); } return; } // NmpMulticastProcessClusterVersionChange VOID NmpScheduleMulticastAddressRenewal( PNM_NETWORK Network ) /*++ Routine Description: Schedules a worker thread to renew the multicast address lease for a network. Note that we do not use the network worker thread because the madcap API is unfamiliar and therefore unpredictable. Arguments: A pointer to the network to renew. 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 (!NmpIsNetworkMadcapWorkerRunning(Network)) { status = NmpScheduleNetworkMadcapWorker(Network); } if (status == ERROR_SUCCESS) { // // We succeeded in scheduling a worker thread. Stop the // retry timer and set the registration work flag. // Network->McastAddressRenewTimer = 0; Network->Flags |= NM_FLAG_NET_RENEW_MCAST_ADDRESS; } else { // // We failed to schedule a worker thread. Set the retry // timer to expire on the next tick, so we can try again. // Network->McastAddressRenewTimer = 1; } return; } // NmpScheduleMulticastAddressRenewal VOID NmpScheduleMulticastKeyRegeneration( PNM_NETWORK Network ) /*++ Routine Description: Schedules a worker thread to regenerate the multicast key for a network. Note that we do not use the network worker thread because the madcap API is unfamiliar and therefore unpredictable. Arguments: A pointer to the network. 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 (!NmpIsNetworkMadcapWorkerRunning(Network)) { status = NmpScheduleNetworkMadcapWorker(Network); } if (status == ERROR_SUCCESS) { // // We succeeded in scheduling a worker thread. Stop the // retry timer and set the registration work flag. // Network->McastKeyRegenerateTimer = 0; Network->Flags |= NM_FLAG_NET_REGENERATE_MCAST_KEY; } else { // // We failed to schedule a worker thread. Set the retry // timer to expire on the next tick, so we can try again. // Network->McastKeyRegenerateTimer = 1; } return; } // NmpScheduleMulticastKeyRegeneration VOID NmpScheduleMulticastAddressRelease( PNM_NETWORK Network ) /*++ Routine Description: Schedules a worker thread to renew the multicast address lease for a network. Note that we do not use the network worker thread because the madcap API is unfamiliar and therefore unpredictable. Arguments: A pointer to the network to renew. 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 (!NmpIsNetworkMadcapWorkerRunning(Network)) { status = NmpScheduleNetworkMadcapWorker(Network); } if (status == ERROR_SUCCESS) { // // We succeeded in scheduling a worker thread. Stop the // retry timer and set the registration work flag. // Network->McastAddressReleaseRetryTimer = 0; Network->Flags |= NM_FLAG_NET_RELEASE_MCAST_ADDRESS; } else { // // We failed to schedule a worker thread. Set the retry // timer to expire on the next tick, so we can try again. // Network->McastAddressReleaseRetryTimer = 1; } return; } // NmpScheduleMulticastAddressRelease VOID NmpScheduleMulticastRefresh( IN PNM_NETWORK Network ) /*++ Routine Description: Schedules a worker thread to refresh the multicast configuration for a network from the cluster database. The regular network worker thread (as opposed to the MADCAP worker thread) is used because refreshing does not call out of the cluster with the MADCAP API. Arguments: A pointer to the network to refresh. 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->McastAddressRefreshRetryTimer = 0; Network->Flags |= NM_FLAG_NET_REFRESH_MCAST; } else { // // We failed to schedule a worker thread. Set the retry // timer to expire on the next tick, so we can try again. // Network->McastAddressRefreshRetryTimer = 1; } return; } // NmpScheduleMulticastRefresh VOID NmpFreeMulticastAddressReleaseList( IN PNM_NETWORK Network ) /*++ Routine Description: Free all release data structures on network list. Notes: Assume that the Network object will not be accessed by any other threads during this call. --*/ { PNM_NETWORK_MADCAP_ADDRESS_RELEASE releaseInfo = NULL; PLIST_ENTRY entry; while (!IsListEmpty(&(Network->McastAddressReleaseList))) { // // Simply free the memory -- don't try to release the // leases. // entry = RemoveHeadList(&(Network->McastAddressReleaseList)); releaseInfo = CONTAINING_RECORD( entry, NM_NETWORK_MADCAP_ADDRESS_RELEASE, Linkage ); NmpFreeMulticastAddressRelease(releaseInfo); } return; } // NmpFreeMulticastAddressReleaseList DWORD NmpMulticastManualConfigChange( IN PNM_NETWORK Network, IN HDMKEY NetworkKey, IN HDMKEY NetworkParametersKey, IN PVOID InBuffer, IN DWORD InBufferSize, OUT BOOLEAN * SetProperties ) /*++ Routine Description: Called by node that receives a clusapi request to set multicast parameters for a network. This routine is a no-op in mixed-mode clusters. Notes: Must not be called with NM lock held. --*/ { DWORD status; LPCWSTR networkId = OmObjectId(Network); BOOLEAN disableConfig = FALSE; BOOLEAN addrConfig = FALSE; DWORD disabled; NM_NETWORK_MULTICAST_PARAMETERS params; LPWSTR mcastAddress = NULL; BOOLEAN needUpdate = FALSE; if (!NmpIsClusterMulticastReady(TRUE, TRUE)) { *SetProperties = TRUE; return(ERROR_SUCCESS); } #if CLUSTER_BETA ClRtlLogPrint(LOG_NOISE, "[NM] Examining update to private properties " "for network %1!ws!.\n", networkId ); #endif // CLUSTER_BETA RtlZeroMemory(¶ms, sizeof(params)); // // Cannot proceed if either registry key is NULL. // if (NetworkKey == NULL || NetworkParametersKey == NULL) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Ignoring possible multicast changes in " "private properties update to network %1!ws! " "because registry keys are missing.\n", networkId ); status = ERROR_INVALID_PARAMETER; goto error_exit; } // // If a writeable multicast parameter is among those properties // being set, we may need to take action before the update is // disseminated. // // Check whether multicast is being disabled for this network. // status = ClRtlFindDwordProperty( InBuffer, InBufferSize, CLUSREG_NAME_NET_DISABLE_MULTICAST, &disabled ); if (status == ERROR_SUCCESS) { disableConfig = TRUE; } else { disabled = NMP_MCAST_DISABLED_DEFAULT; } // // Check whether a multicast address is being set for this // network. // status = ClRtlFindSzProperty( InBuffer, InBufferSize, CLUSREG_NAME_NET_MULTICAST_ADDRESS, &mcastAddress ); if (status == ERROR_SUCCESS) { addrConfig = TRUE; } if (disableConfig || addrConfig) { // // Multicast parameters are being written. // ClRtlLogPrint(LOG_NOISE, "[NM] Processing manual update to multicast " "configuration for network %1!ws!.\n", networkId ); status = NmpMulticastFormManualConfigParameters( Network, NetworkKey, NetworkParametersKey, disableConfig, disabled, addrConfig, mcastAddress, &needUpdate, ¶ms ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to determine multicast " "configuration parameters for network " "%1!ws! during manual configuration, " "status %2!u!.\n", networkId, status ); goto error_exit; } // // Notify other nodes of the config change. // if (needUpdate) { status = NmpMulticastNotifyConfigChange( Network, NetworkKey, &NetworkParametersKey, ¶ms, InBuffer, InBufferSize ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to disseminate multicast " "configuration for network %1!ws! during " "manual configuration, status %2!u!.\n", networkId, status ); goto error_exit; } // // The properties have been disseminated. There is // no need to set them again (in fact, if we changed // one of the multicast properties, it could be // overwritten). // *SetProperties = FALSE; } } if (!needUpdate) { // // No multicast properties are affected. Set them // in the cluster database normally. // *SetProperties = TRUE; status = ERROR_SUCCESS; } error_exit: if (mcastAddress != NULL) { LocalFree(mcastAddress); mcastAddress = NULL; } NmpMulticastFreeParameters(¶ms); // // If multicast config failed, default to setting properties. // if (status != ERROR_SUCCESS) { *SetProperties = TRUE; } return(status); } // NmpMulticastManualConfigChange DWORD NmpUpdateSetNetworkMulticastConfiguration( IN BOOL SourceNode, IN LPWSTR NetworkId, IN PVOID UpdateBuffer, IN PVOID PropBuffer, IN LPDWORD PropBufferSize ) /*++ Routine Description: Global update routine for multicast configuration. Starts a local transaction. Commits property buffer to local database. Commits multicast configuration to local database, possibly overwriting properties from buffer. Configures multicast parameters. Commits transaction. Backs out multicast configuration changes if needed. Starts lease renew timer if needed. Arguments: SourceNode - whether this node is source of update. NetworkId - affected network Update - new multicast configuration PropBuffer - other properties to set in local transaction. may be absent. PropBufferSize - size of property buffer. Return value: SUCCESS if properties or configuration could not be committed. Error not necessarily returned if multicast config failed. --*/ { DWORD status; PNM_NETWORK network = NULL; PNM_NETWORK_MULTICAST_UPDATE update = UpdateBuffer; NM_NETWORK_MULTICAST_PARAMETERS params = { 0 }; NM_NETWORK_MULTICAST_PARAMETERS undoParams = { 0 }; HLOCALXSACTION xaction = NULL; HDMKEY networkKey = NULL; HDMKEY netParamKey = NULL; DWORD createDisposition; BOOLEAN lockAcquired = FALSE; DWORD leaseRenewTime; 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 multicast configuration " "for network %1!ws!.\n", NetworkId ); // // 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; } // // Convert the update into a parameters data structure. // status = NmpMulticastCreateParametersFromUpdate( network, update, (BOOLEAN)(update->Disabled == 0), ¶ms ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_CRITICAL, "[NM] Failed to convert update parameters to " "multicast parameters for network %1!ws!, " "status %2!u!.\n", NetworkId, status ); 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; } // // Open or create the network's parameters key. // netParamKey = DmLocalCreateKey( xaction, networkKey, CLUSREG_KEYNAME_PARAMETERS, 0, // registry options MAXIMUM_ALLOWED, NULL, &createDisposition ); if (netParamKey == NULL) { status = GetLastError(); ClRtlLogPrint(LOG_CRITICAL, "[NM] Failed to open/create Parameters database " "key for network %1!ws!, status %2!u!.\n", NetworkId, status ); goto error_exit; } NmpAcquireLock(); lockAcquired = TRUE; // // If the multicast configuration for this network is // currently being refreshed (occurs outside of a GUM // update), then the properties we are about to write // to the database might make that refresh obsolete or // even inconsistent. Abort it now. // if (network->Flags & NM_FLAG_NET_REFRESH_MCAST_RUNNING) { ClRtlLogPrint(LOG_NOISE, "[NM] Aborting multicast configuration refresh " "in progress on network %1!ws! with GUM update.\n", NetworkId ); network->Flags |= NM_FLAG_NET_REFRESH_MCAST_ABORTING; } // // Cancel any pending multicast configuration refreshes. // They would be redundant after this GUM update. // network->Flags &= ~NM_FLAG_NET_REFRESH_MCAST; network->McastAddressRefreshRetryTimer = 0; // // Clear reconfiguration timer and work flag. // This timer is set in NmpMulticastCheckReconfigure. // It is not necessary to call NmpReconfigureMulticast() once // this GUM update is executed. // network->Flags &= ~NM_FLAG_NET_RECONFIGURE_MCAST; network->McastAddressReconfigureRetryTimer = 0; // // Cancel pending multicast key regeneration, if scheduled. // It would be redundant after this update. // network->Flags &= ~NM_FLAG_NET_REGENERATE_MCAST_KEY; // // If we were given a property buffer, then this update was // caused by a manual configuration (setting of private // properties). Write those properties first, knowing that // they may get overwritten later when we write multicast // parameters. // if (*PropBufferSize > sizeof(DWORD)) { status = ClRtlSetPrivatePropertyList( xaction, netParamKey, &NmpMcastClusterRegApis, PropBuffer, *PropBufferSize ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_CRITICAL, "[NM] Failed to set private properties for " "network %1!ws! during a multicast configuration " "update, status %2!u!.\n", NetworkId, status ); goto error_exit; } } // // Write the multicast configuration. // status = NmpWriteMulticastParameters( network, networkKey, netParamKey, xaction, ¶ms ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_CRITICAL, "[NM] Failed to write multicast configuration for " "network %1!ws!, status %2!u!.\n", NetworkId, status ); goto error_exit; } // // Process the multicast configuration, including storing new // parameters in the network object and plumbing them into // clusnet. // status = NmpProcessMulticastConfiguration(network, ¶ms, &undoParams); if (status == ERROR_SUCCESS) { // // Share responsibility for lease renewal and key // regeneration. // NmpShareMulticastAddressLease(network, FALSE); NmpShareMulticastKeyRegeneration(network, FALSE); } else { // // We should not be sharing the responsibility for renewing // this lease or regenerating the key, especially since the // data in the network object may no longer be accurate. // Clear the timers, if they are set, as well as any work flags. // NmpStartNetworkMulticastAddressRenewTimer(network, 0); network->Flags &= ~NM_FLAG_NET_RENEW_MCAST_ADDRESS; NmpStartNetworkMulticastKeyRegenerateTimer(network, 0); network->Flags &= ~NM_FLAG_NET_REGENERATE_MCAST_KEY; ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to process multicast configuration for " "network %1!ws!, status %2!u!. Attempting null " "multicast configuration.\n", NetworkId, status ); NmpMulticastSetNullAddressParameters(network, ¶ms); status = NmpProcessMulticastConfiguration( network, ¶ms, &undoParams ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_CRITICAL, "[NM] Failed to process multicast configuration for " "network %1!ws!, status %2!u!.\n", NetworkId, status ); goto error_exit; } } error_exit: if (lockAcquired) { NmpLockedLeaveApi(); NmpReleaseLock(); } else { NmpLeaveApi(); } // // Close the network parameters key, which was obtained with // DmLocalCreateKey, before committing/aborting the transaction. // if (netParamKey != NULL) { DmCloseKey(netParamKey); netParamKey = NULL; } if (xaction != NULL) { // // Complete the transaction - this must be done after releasing // the NM lock. // if (status == ERROR_SUCCESS) { DmCommitLocalUpdate(xaction); } else { DmAbortLocalUpdate(xaction); } } NmpMulticastFreeParameters(¶ms); if (undoParams.Key != NULL) { // // undoParams.Key is set to Network->EncryptedMulticastKey in // NmpProcessMulticastConfiguration, which should be freed by // LocalFree(), as it was allocated by NmpProtectData(). // While NmpMulticastFreeParameters() frees undoParams.Key by // MIDL_user_free(). // LocalFree(undoParams.Key); undoParams.Key = NULL; } NmpMulticastFreeParameters(&undoParams); if (networkKey != NULL) { DmCloseKey(networkKey); networkKey = NULL; } if (network != NULL) { OmDereferenceObject(network); } return(status); } // NmpUpdateSetNetworkMulticastConfiguration DWORD NmpRegenerateMulticastKey( IN OUT PNM_NETWORK Network ) /*++ Routine Description: Regenerate multicast key for Network, and issue GUM update to propagate this new multicast key to all cluster nodes. Parameters: Network - [OUT] A pointer to network object. New multicast key and key length are stored in Network->EncryptedMulticastKey and Network->EncryptedMulticastKeyLength. Notes: Called and returned with NM lock held. Lock is released during execution. --*/ { DWORD status = ERROR_SUCCESS; LPWSTR networkId = (LPWSTR) OmObjectId(Network); HDMKEY networkKey = NULL; HDMKEY netParamKey = NULL; BOOL LockAcquired = TRUE; PNM_NETWORK_MULTICAST_PARAMETERS params = NULL; ClRtlLogPrint(LOG_NOISE, "[NM] Regenerating multicast key for network %1!ws!.\n", networkId ); params = MIDL_user_allocate(sizeof(NM_NETWORK_MULTICAST_PARAMETERS)); if (params == NULL) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to allocate %1!u! bytes.\n", sizeof(NM_NETWORK_MULTICAST_PARAMETERS) ); status = ERROR_NOT_ENOUGH_MEMORY; goto error_exit; } ZeroMemory(params, sizeof(NM_NETWORK_MULTICAST_PARAMETERS)); params->Disabled = (NmpIsNetworkMulticastEnabled(Network) ? 0 : 1); if (!(params->Disabled)) { status = NmpMulticastCreateParameters( params->Disabled, Network->MulticastAddress, NULL, // key 0, // keylength Network->MulticastLeaseObtained, Network->MulticastLeaseExpires, &Network->MulticastLeaseRequestId, Network->MulticastLeaseServer, Network->ConfigType, params ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to create multicast parameters " "data structure for network %1!ws!, " "status %2!u!.\n", networkId, status ); goto error_exit; } // // Create new multicast key // status = NmpCreateRandomNumber(&(params->Key), MulticastKeyLen ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to create random number " "for network %1!ws!, status %2!u!.\n", networkId, status ); goto error_exit; } params->KeyLength = MulticastKeyLen; NmpReleaseLock(); LockAcquired = FALSE; // // Disseminate the configuration. // status = NmpMulticastNotifyConfigChange( Network, networkKey, &netParamKey, params, NULL, 0 ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to disseminate multicast " "configuration for network %1!ws!, " "status %2!u!.\n", networkId, status ); goto error_exit; } } // if (!params.Disabled) status = ERROR_SUCCESS; error_exit: if (netParamKey != NULL || networkKey != NULL) { if (netParamKey != NULL) { DmCloseKey(netParamKey); netParamKey = NULL; } if (networkKey != NULL) { DmCloseKey(networkKey); networkKey = NULL; } } if (LockAcquired == FALSE) { NmpAcquireLock(); LockAcquired = TRUE; } NmpMulticastFreeParameters(params); if (params != NULL) { MIDL_user_free(params); } return(status); } // NmpRegenerateMulticastKey DWORD NmpGetMulticastKey( IN OUT PNM_NETWORK_MULTICAST_PARAMETERS Params, IN PNM_NETWORK Network ) /*++ Routine Description: Get multicast key for Network from NM leader. Parameters: Params - [IN OUT] If this node successfully gets multicast key from NM leader, it stores multicast key and key length in Params->Key and Params->KeyLength. It does not store multicast key in Network->EncryptedMulticastKey. Network - [IN] A pointer to the network object. Return value: ERROR_SUCCESS: Successfully gets encrypted multicast key from NM leader, verifies MAC, and decrypts multicast key. Or NM_FLAG_NET_REFRESH_MCAST_ABORTING is set. ERROR_CLUSTER_INVALID_NODE: This node becomes NM leader. Win32 error code: otherwise. Notes: Called and returned with the NM lock held. Lock released and reacquired during execution. --*/ { LPWSTR NetworkId = (LPWSTR) OmObjectId(Network); DWORD status = ERROR_SUCCESS; PNM_NETWORK_MULTICASTKEY networkMulticastKey = NULL; PVOID EncryptionKey = NULL; DWORD EncryptionKeyLength; PVOID MulticastKey = NULL; DWORD MulticastKeyLength; CL_NODE_ID NmpLeaderNodeIdSaved; BOOL Continue = TRUE; while (Continue) { Continue = FALSE; if (NmpLeaderNodeId == NmLocalNodeId) { // // This node becomes NM leader. // status = ERROR_CLUSTER_INVALID_NODE; goto error_exit; } NmpLeaderNodeIdSaved = NmpLeaderNodeId; if (Network->Flags & NM_FLAG_NET_REFRESH_MCAST_ABORTING) { status = ERROR_SUCCESS; goto error_exit; } // // Get multicast key from NM leader // NmpReleaseLock(); ClRtlLogPrint(LOG_NOISE, "[NM] Getting multicast key for " "network %1!ws! from NM leader (node %2!u!).\n", NetworkId, NmpLeaderNodeId ); status = NmpGetMulticastKeyFromNMLeader(NmpLeaderNodeId, NmLocalNodeIdString, NetworkId, &networkMulticastKey ); NmpAcquireLock(); if (Network->Flags & NM_FLAG_NET_REFRESH_MCAST_ABORTING) { status = ERROR_SUCCESS; goto error_exit; } if (status == ERROR_SUCCESS) { if (networkMulticastKey->EncryptedMulticastKey != NULL) { // // set Params->Key, Params->KeyLength, and // Params->MulticastKeyExpires. // status = NmpDeriveClusterKey(NetworkId, NM_WCSLEN(NetworkId), &EncryptionKey, &EncryptionKeyLength ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to derive cluster key for " "network %1!ws!, status %2!u!.\n", NetworkId, status ); goto error_exit; } status = NmpVerifyMACAndDecryptData( NmCryptServiceProvider, NMP_ENCRYPT_ALGORITHM, NMP_KEY_LENGTH, networkMulticastKey->MAC, networkMulticastKey->MACLength, NMP_MAC_DATA_LENGTH_EXPECTED, networkMulticastKey->EncryptedMulticastKey, networkMulticastKey->EncryptedMulticastKeyLength, EncryptionKey, EncryptionKeyLength, networkMulticastKey->Salt, networkMulticastKey->SaltLength, (PBYTE *) &MulticastKey, &MulticastKeyLength ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to verify MAC or decrypt multicast key for " "network %1!ws!, status %2!u!.\n", NetworkId, status ); goto error_exit; } if (EncryptionKey != NULL) { RtlSecureZeroMemory(EncryptionKey, EncryptionKeyLength); LocalFree(EncryptionKey); EncryptionKey = NULL; } if (Params->Key != NULL) { RtlSecureZeroMemory(Params->Key, Params->KeyLength); LocalFree(Params->Key); } Params->Key = LocalAlloc(0, MulticastKeyLength); if (Params->Key == NULL) { status = ERROR_NOT_ENOUGH_MEMORY; ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to allocate %1!u! bytes.\n", MulticastKeyLength ); goto error_exit; } CopyMemory(Params->Key, MulticastKey, MulticastKeyLength); Params->KeyLength = MulticastKeyLength; Params->MulticastKeyExpires = networkMulticastKey->MulticastKeyExpires; } // if (networkMulticastKey->EncryptedMulticastKey != NULL) else { ClRtlLogPrint(LOG_UNUSUAL, "[NM] NM leader (node %1!u!) returned a NULL multicast key " "for network %2!ws!.\n", NmpLeaderNodeId, NetworkId ); } } // if (status == ERROR_SUCCESS) else { // // Failed to get multicast key from NM leader. // ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to get multicast key for " "network %1!ws! from " "NM leader (node %2!u!), status %3!u!.\n", NetworkId, NmpLeaderNodeId, status ); if (NmpLeaderNodeIdSaved != NmpLeaderNodeId) { // // Leader node changed. // ClRtlLogPrint(LOG_NOISE, "[NM] NM leader node has changed. " "Getting multicast key for " " network %1!ws! from new NM leader (node %2!u!).\n", NetworkId, NmpLeaderNodeId ); Continue = TRUE; } } } // while error_exit: if (EncryptionKey != NULL) { RtlSecureZeroMemory(EncryptionKey, EncryptionKeyLength); LocalFree(EncryptionKey); } if (MulticastKey != NULL) { // // MulticastKey is allocated using HeapAlloc() in NmpVerifyMACAndDecryptData(). // RtlSecureZeroMemory(MulticastKey, MulticastKeyLength); HeapFree(GetProcessHeap(), 0, MulticastKey); } NmpFreeNetworkMulticastKey(networkMulticastKey); return (status); } // NmpGetMulticastKey() DWORD NmpRefreshMulticastConfiguration( IN PNM_NETWORK Network ) /*++ Routine Description: NmpRefreshMulticastConfiguration enables multicast on the specified Network according to parameters in the cluster database. This routine processes the multicast configuration from the database, but it does not run in a GUM update. If a GUM update occurs during this routine, a flag is set indicating that the routine should be aborted. If this routine fails in a way that indicates that the network multicast needs to be reconfigured, then a timer is set (to allow the leader to reconfigure first). Notes: Called and returns with the NM lock held, but releases during execution. --*/ { LPWSTR networkId = (LPWSTR) OmObjectId(Network); DWORD status; BOOLEAN lockAcquired = TRUE; BOOLEAN clearRunningFlag = FALSE; HDMKEY networkKey = NULL; HDMKEY netParamKey = NULL; HDMKEY clusParamKey = NULL; NM_NETWORK_MULTICAST_PARAMETERS params = { 0 }; NM_NETWORK_MULTICAST_PARAMETERS undoParams = { 0 }; DWORD mcastAddrLength = 0; NM_MCAST_LEASE_STATUS leaseStatus; NM_MCAST_CONFIG configType; DWORD disabled; BOOLEAN tryReconfigure = FALSE; if (Network->Flags & (NM_FLAG_NET_REFRESH_MCAST_RUNNING | NM_FLAG_NET_REFRESH_MCAST_ABORTING)) { ClRtlLogPrint(LOG_NOISE, "[NM] Multicast configuration refresh for " "network %1!ws! already in progress.\n", networkId ); status = ERROR_SUCCESS; goto error_exit; } else { ClRtlLogPrint(LOG_NOISE, "[NM] Refreshing multicast configuration for network %1!ws!.\n", networkId ); Network->Flags |= NM_FLAG_NET_REFRESH_MCAST_RUNNING; clearRunningFlag = TRUE; } NmpReleaseLock(); lockAcquired = FALSE; // // Check if multicast is disabled. This has the side-effect, // on success, of opening at least the network key, and // possibly the network parameters key (if it exists) and // the cluster parameters key. // status = NmpQueryMulticastDisabled( Network, &clusParamKey, &networkKey, &netParamKey, ¶ms.Disabled ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to determine whether multicast " "is disabled for network %1!ws!, status %2!u!.\n", networkId, status ); goto error_exit; } if (params.Disabled > 0) { ClRtlLogPrint(LOG_NOISE, "[NM] Multicast is disabled for network %1!ws!.\n", networkId ); } // // Determine what type of configuration this is. // status = NmpQueryMulticastConfigType( Network, networkKey, &netParamKey, ¶ms.ConfigType ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to determine configuration type " "for network %1!ws!, status %2!u!.\n", networkId, status ); tryReconfigure = TRUE; goto error_exit; } // // Read the multicast address. // status = NmpQueryMulticastAddress( Network, networkKey, &netParamKey, ¶ms.Address, &mcastAddrLength ); if ( (status == ERROR_SUCCESS && !NmpMulticastValidateAddress(params.Address)) || (status != ERROR_SUCCESS) ) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to get valid multicast address " "for network %1!ws! from cluster database, " "status %2!u!, address %3!ws!.\n", networkId, status, ((params.Address != NULL) ? params.Address : L"") ); tryReconfigure = TRUE; goto error_exit; } // // Get the lease parameters. // status = NmpQueryMulticastAddressLease( Network, networkKey, &netParamKey, &leaseStatus, ¶ms.LeaseObtained, ¶ms.LeaseExpires ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to get multicast address lease " "expiration for network %1!ws!, status %2!u!.\n", networkId, status ); // // Not fatal. // params.LeaseObtained = 0; params.LeaseExpires = 0; status = ERROR_SUCCESS; } // // Remember parameters we will need later. // disabled = params.Disabled; configType = params.ConfigType; // // No longer need registry key handles. // if (clusParamKey != NULL) { DmCloseKey(clusParamKey); clusParamKey = NULL; } if (netParamKey != NULL) { DmCloseKey(netParamKey); netParamKey = NULL; } if (networkKey != NULL) { DmCloseKey(networkKey); networkKey = NULL; } // // Process the configuration changes, but only if // there hasn't been a multicast configuration GUM // update since we read from the database. The GUM // update will set the aborting flag. // NmpAcquireLock(); lockAcquired = TRUE; if (Network->Flags & NM_FLAG_NET_REFRESH_MCAST_ABORTING) { ClRtlLogPrint(LOG_NOISE, "[NM] Multicast configuration refresh for " "network %1!ws! trumped by global update.\n", networkId ); // // Clear the aborting flag. We clear the running // flag as we exit. // Network->Flags &= ~NM_FLAG_NET_REFRESH_MCAST_ABORTING; status = ERROR_SUCCESS; goto error_exit; } if (!params.Disabled) { status = NmpGetMulticastKey(¶ms, Network); if (status== ERROR_SUCCESS) { if (Network->Flags & NM_FLAG_NET_REFRESH_MCAST_ABORTING) { ClRtlLogPrint(LOG_NOISE, "[NM] Multicast configuration refresh for " "network %1!ws! trumped by global update.\n", networkId ); // // Clear the aborting flag. We clear the running // flag as we exit. // Network->Flags &= ~NM_FLAG_NET_REFRESH_MCAST_ABORTING; goto error_exit; } } else { if (NmpLeaderNodeId == NmLocalNodeId) { // // NM leader died while this node was trying to get multicast key // from it. And this node becomes new NM leader. It is this node's // respondibility to create multicast key and disseminate to all // cluster nodes. // ClRtlLogPrint(LOG_NOISE, "[NM] New NM leader. Create and disseminate multicast key " "for network %1!ws!.\n", networkId ); if (params.Key != NULL) { RtlSecureZeroMemory(params.Key, params.KeyLength); LocalFree(params.Key); } status = NmpCreateRandomNumber(&(params.Key), MulticastKeyLen ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to create random number " "for network %1!ws!, status %2!u!.\n", networkId, status ); goto error_exit; } params.KeyLength = MulticastKeyLen; NmpReleaseLock(); lockAcquired = FALSE; // // Disseminate the configuration. // status = NmpMulticastNotifyConfigChange( Network, networkKey, &netParamKey, ¶ms, NULL, 0 ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to disseminate multicast " "configuration for network %1!ws!, " "status %2!u!.\n", networkId, status ); } goto error_exit; } // if (NmpLeaderNodeId == NmLocalNodeId) else { goto error_exit; } } } status = NmpProcessMulticastConfiguration( Network, ¶ms, &undoParams ); // // Check the lease renew parameters if this was not a // manual configuration. // if (!disabled && configType != NmMcastConfigManual) { NmpShareMulticastAddressLease(Network, TRUE); } // // Share responsibility for key regeneration. // NmpShareMulticastKeyRegeneration(Network, TRUE); error_exit: if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to refresh multicast configuration " "for network %1!ws!, status %2!u!.\n", networkId, status ); } else { ClRtlLogPrint(LOG_NOISE, "[NM] Multicast configuration refresh for " "network %1!ws! was successful.\n", networkId ); } if (clusParamKey != NULL || netParamKey != NULL || networkKey != NULL) { if (lockAcquired) { NmpReleaseLock(); lockAcquired = FALSE; } if (clusParamKey != NULL) { DmCloseKey(clusParamKey); clusParamKey = NULL; } if (netParamKey != NULL) { DmCloseKey(netParamKey); netParamKey = NULL; } if (networkKey != NULL) { DmCloseKey(networkKey); networkKey = NULL; } } NmpMulticastFreeParameters(¶ms); NmpMulticastFreeParameters(&undoParams); if (!lockAcquired) { NmpAcquireLock(); lockAcquired = TRUE; } if (tryReconfigure) { NmpMulticastCheckReconfigure(Network); } if (clearRunningFlag) { Network->Flags &= ~NM_FLAG_NET_REFRESH_MCAST_RUNNING; } return(status); } // NmpRefreshMulticastConfiguration DWORD NmpMulticastValidatePrivateProperties( IN PNM_NETWORK Network, IN HDMKEY NetworkKey, IN PVOID InBuffer, IN DWORD InBufferSize ) /*++ Routine Description: Called when a manual update to the private properties of a network is detected. Only called on the node that receives the clusapi clusctl request. Verifies that no read-only properties are being set. Determines whether the multicast configuration of the network will need to be refreshed after the update. This routine is a no-op in a mixed-mode cluster. --*/ { DWORD status; LPCWSTR networkId = OmObjectId(Network); NM_NETWORK_MULTICAST_INFO mcastInfo; // // Enforce property-validation regardless of number of // nodes in cluster. // if (!NmpIsClusterMulticastReady(FALSE, FALSE)) { return(ERROR_SUCCESS); } // // Don't allow any read-only properties to be set. // RtlZeroMemory(&mcastInfo, sizeof(mcastInfo)); status = ClRtlVerifyPropertyTable( NmpNetworkMulticastProperties, NULL, // Reserved TRUE, // Allow unknowns InBuffer, InBufferSize, (LPBYTE) &mcastInfo ); if ( status != ERROR_SUCCESS ) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Error verifying private properties for " "network %1!ws!, status %2!u!.\n", networkId, status ); goto error_exit; } error_exit: NmpFreeNetworkMulticastInfo(&mcastInfo); return(status); } // NmpMulticastValidatePrivateProperties DWORD NmpStartMulticast( IN OPTIONAL PNM_NETWORK Network, IN NM_START_MULTICAST_MODE Mode ) /*++ Routine Description: Start multicast on a network or all networks after performing cluster-wide checks. Arguments: Network - network on which to start multicast. If NULL, start multicast on all networks. Mode - indicates caller mode Notes: Must be called with NM lock held. --*/ { PLIST_ENTRY entry; PNM_NETWORK network; if (!NmpMulticastRunInitialConfig) { if (Mode == NmStartMulticastDynamic) { // // Defer until initial configuration. // ClRtlLogPrint(LOG_NOISE, "[NM] Deferring dynamic multicast start until " "initial configuration.\n" ); return(ERROR_SUCCESS); } else { NmpMulticastRunInitialConfig = TRUE; } } if (!NmpIsClusterMulticastReady(TRUE, TRUE)) { return(ERROR_SUCCESS); } if (Network == NULL) { ClRtlLogPrint(LOG_NOISE, "[NM] Starting multicast for all cluster networks.\n" ); for (entry = NmpNetworkList.Flink; entry != &NmpNetworkList; entry = entry->Flink) { network = CONTAINING_RECORD(entry, NM_NETWORK, Linkage); NmpStartMulticastInternal(network, Mode); } } else { NmpStartMulticastInternal(Network, Mode); } return(ERROR_SUCCESS); } // NmpStartMulticast DWORD NmpStopMulticast( IN OPTIONAL PNM_NETWORK Network ) /*++ Routine Description: Stop multicast on the local node by configuring clusnet with a NULL address. This routine should be called from a GUM update or another barrier. Routine Description: Network - network on which to stop multicast. If NULL, stop multicast on all networks. Notes: Must be called with NM lock held. --*/ { DWORD status = ERROR_SUCCESS; PLIST_ENTRY entry; PNM_NETWORK network; LPWSTR networkId; DWORD disabled; NM_NETWORK_MULTICAST_PARAMETERS params = { 0 }; NM_NETWORK_MULTICAST_PARAMETERS undoParams = { 0 }; if (Network == NULL) { ClRtlLogPrint(LOG_NOISE, "[NM] Stopping multicast for all cluster networks.\n" ); for (entry = NmpNetworkList.Flink; entry != &NmpNetworkList; entry = entry->Flink) { network = CONTAINING_RECORD(entry, NM_NETWORK, Linkage); status = NmpStopMulticast(network); } } else { networkId = (LPWSTR) OmObjectId(Network); disabled = (NmpIsNetworkMulticastEnabled(Network) ? 0 : 1); // // Check if telling clusnet to stop multicast would // be redundant. // if (disabled != 0 || Network->MulticastAddress == NULL || !wcscmp(Network->MulticastAddress, NmpNullMulticastAddress)) { ClRtlLogPrint(LOG_NOISE, "[NM] Not necessary to stop multicast for " "cluster network %1!ws! (disabled = %2!u!, " "multicast address = %3!ws!).\n", networkId, disabled, ((Network->MulticastAddress == NULL) ? L"" : Network->MulticastAddress) ); status = ERROR_SUCCESS; } else { ClRtlLogPrint(LOG_NOISE, "[NM] Stopping multicast for cluster network %1!ws!.\n", networkId ); // // Create parameters from the current state of the network. // However, don't use any lease info, since we are stopping // multicast and will not be renewing. // status = NmpMulticastCreateParameters( disabled, NULL, // blank address initially NULL, // MulticastKey, 0, // MulticastKeyLength, 0, // lease obtained 0, // lease expires NULL, // lease request id NULL, // lease server NmMcastConfigManual, // doesn't matter ¶ms ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to create multicast configuration " "parameter block for network %1!ws!, status %2!u!, " "while stopping multicast.\n", networkId, status ); goto error_exit; } // // Nullify the address. // NmpMulticastSetNullAddressParameters(Network, ¶ms); // // Send the parameters to clusnet. // status = NmpProcessMulticastConfiguration( Network, ¶ms, &undoParams ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to set null multicast " "configuration for network %1!ws! " "while stopping multicast, status %2!u!.\n", networkId, status ); goto error_exit; } } // // Cancel the lease renew timer, if set. // NmpStartNetworkMulticastAddressRenewTimer(Network, 0); // // Clear the retry timers, if set. // Network->McastAddressReconfigureRetryTimer = 0; Network->McastAddressRefreshRetryTimer = 0; // // Clear multicast configuration work flags. Note // that this is best effort -- we do not attempt // to prevent race conditions where a multicast // configuration operation may already be in // progress, since such conditions would not // affect the integrity of the cluster. // Network->Flags &= ~NM_FLAG_NET_RENEW_MCAST_ADDRESS; Network->Flags &= ~NM_FLAG_NET_RECONFIGURE_MCAST; Network->Flags &= ~NM_FLAG_NET_REFRESH_MCAST; } error_exit: if (status != ERROR_SUCCESS && Network != NULL) { ClRtlLogPrint(LOG_NOISE, "[NM] Failed to stop multicast for " "network %1!ws!, status %2!u!.\n", OmObjectId(Network), status ); } NmpMulticastFreeParameters(¶ms); NmpMulticastFreeParameters(&undoParams); return(status); } // NmpStopMulticast