/*++ Copyright (c) 1996-2001 Microsoft Corporation Module Name: mcast.c Abstract: Implements the Node Manager's network multicast management routines. Author: David Dion (daviddio) 15-Mar-2001 Revision History: --*/ #include "nmp.h" #include ///////////////////////////////////////////////////////////////////////////// // // Constants // ///////////////////////////////////////////////////////////////////////////// // // Multicast configuration types. // - Manual: administrator configured address // - Madcap: lease obtained from MADCAP server // - Auto: address chosen after no MADCAP server detected // typedef enum { NmMcastConfigManual = 0, NmMcastConfigMadcap, NmMcastConfigAuto } NM_MCAST_CONFIG, *PNM_MCAST_CONFIG; // // Lease status. // typedef enum { NmMcastLeaseValid = 0, NmMcastLeaseNeedsRenewal, NmMcastLeaseExpired } NM_MCAST_LEASE_STATUS, *PNM_MCAST_LEASE_STATUS; #define CLUSREG_NAME_CLUSTER_DISABLE_MULTICAST L"MulticastClusterDisable" #define CLUSREG_NAME_NET_MULTICAST_ADDRESS L"MulticastAddress" #define CLUSREG_NAME_NET_DISABLE_MULTICAST L"MulticastDisable" #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_MANUAL_DEFAULT_WAIT_INTERVAL 60 * 1000 // 2 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))) // // MADCAP lease times are in seconds. NM timers are in milliseconds. // #define NMP_MADCAP_TO_NM_TIME(_mc_time) ((_mc_time) * 1000) // // Force a time_t value into a DWORD. // #define NMP_TIME_TO_DWORD(_tm, _dw) \ (_tm) = ((_tm) < 0) ? 0 : (_tm); \ (_tm) = ((_tm) > MAXLONG) ? MAXLONG : (_tm); \ (_dw) = (DWORD) (_tm) // // 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; // 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 SaltOffset; DWORD SaltLength; 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 Salt; DWORD SaltLength; PVOID Key; DWORD KeyLength; 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 ); ///////////////////////////////////////////////////////////////////////////// // // Initialization & cleanup routines // ///////////////////////////////////////////////////////////////////////////// DWORD NmpCleanupMulticast( VOID ) /*++ Notes: Called with NM lock held. --*/ { // // Cleanup the MADCAP client. // if (NmpMadcapClientInitialized) { McastApiCleanup(); NmpMadcapClientInitialized = FALSE; } return(ERROR_SUCCESS); } // NmpCleanupMulticast ///////////////////////////////////////////////////////////////////////////// // // 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 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 DWORD * DestLength OPTIONAL ) { 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 DWORD NmpGenerateMulticastKey( IN PNM_NETWORK Network, IN OUT PVOID * Key, IN OUT ULONG * KeyLength ) /*++ Routine Description: Obtain a secret key that all nodes in the cluster can independently generate. --*/ { DWORD status; LPCWSTR networkId = OmObjectId(Network); PVOID key = NULL; DWORD keyLength = 0; ClRtlLogPrint(LOG_NOISE, "[NM] Obtaining multicast key for network %1!ws!.\n", networkId ); status = NmpGetClusterKey(key, &keyLength); if (status != ERROR_SUCCESS) { if (status == ERROR_INSUFFICIENT_BUFFER) { key = MIDL_user_allocate(keyLength); if (key == NULL) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to allocate key buffer of " "size %1!u! for network %2!ws!.\n", keyLength, networkId ); return(ERROR_NOT_ENOUGH_MEMORY); } status = NmpGetClusterKey(key, &keyLength); } } if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to obtain multicast key for " "network %1!ws!, status %2!u!.\n", networkId, status ); return(status); } *Key = key; *KeyLength = keyLength; return(ERROR_SUCCESS); } // NmpGenerateMulticastKey VOID NmpMulticastFreeParameters( IN PNM_NETWORK_MULTICAST_PARAMETERS Parameters ) { if (Parameters->Address != NULL) { if (!NMP_GLOBAL_STRING(Parameters->Address)) { MIDL_user_free(Parameters->Address); } Parameters->Address = NULL; } NM_MIDL_FREE_OBJECT_FIELD(Parameters, Salt); Parameters->SaltLength = 0; 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 Salt, IN DWORD SaltLength, 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; } } // salt if (Salt != NULL) { Parameters->Salt = MIDL_user_allocate(SaltLength); if (Parameters->Salt == NULL) { status = ERROR_NOT_ENOUGH_MEMORY; goto error_exit; } RtlCopyMemory(Parameters->Salt, Salt, SaltLength); Parameters->SaltLength = SaltLength; } // 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->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; 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; requestId.ClientUID = ((Update->LeaseRequestIdOffset == 0) ? NULL : (LPBYTE)((PUCHAR)Update + Update->LeaseRequestIdOffset)); requestId.ClientUIDLength = Update->LeaseRequestIdLength; status = NmpMulticastCreateParameters( Update->Disabled, ((Update->AddressOffset == 0) ? NULL : (LPWSTR)((PUCHAR)Update + Update->AddressOffset)), ((Update->SaltOffset == 0) ? NULL : (PVOID)((PUCHAR)Update + Update->SaltOffset)), Update->SaltLength, NULL, 0, Update->LeaseObtained, Update->LeaseExpires, &requestId, ((Update->LeaseServerOffset == 0) ? NULL : (LPWSTR)((PUCHAR)Update + Update->LeaseServerOffset)), Update->ConfigType, Parameters ); if (status == ERROR_SUCCESS && GenerateKey) { status = NmpGenerateMulticastKey( Network, &(Parameters->Key), &(Parameters->KeyLength) ); } 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 salt = 0; 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); } // salt if (Parameters->Salt != NULL) { updateSize = ROUND_UP_COUNT(updateSize, TYPE_ALIGNMENT(PVOID)); salt = updateSize; updateSize += Parameters->SaltLength; } // 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) { return(ERROR_NOT_ENOUGH_MEMORY); } // // 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) ); } update->SaltOffset = salt; update->SaltLength = Parameters->SaltLength; if (salt != 0) { RtlCopyMemory( (PUCHAR)update + salt, Parameters->Salt, Parameters->SaltLength ); } 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; return(ERROR_SUCCESS); } // 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 %2!ws! %3!ws! " "from cluster database, status %4!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 NmpQueryMulticastKeySalt( IN PNM_NETWORK Network, IN HDMKEY NetworkKey, IN OUT HDMKEY * NetworkParametersKey, IN OUT PVOID * Salt, IN OUT ULONG * SaltLength ) /*++ Routine Description: Reads the multicast key salt from the cluster database. Notes: Must not be called with NM lock held. --*/ { LPCWSTR networkId = OmObjectId(Network); HDMKEY netParamKey = NULL; BOOLEAN openedNetParamKey = FALSE; DWORD type; PUCHAR salt = NULL; DWORD saltLength; DWORD status; #if CLUSTER_BETA ClRtlLogPrint(LOG_NOISE, "[NM] Querying multicast salt 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; } } // // Allocate a buffer for the salt, if necessary. // salt = *Salt; saltLength = *SaltLength; if (salt != NULL && saltLength != sizeof(FILETIME)) { MIDL_user_free(salt); salt = NULL; saltLength = 0; *SaltLength = 0; } if (salt == NULL) { saltLength = sizeof(FILETIME); salt = MIDL_user_allocate(saltLength); if (salt == NULL) { status = ERROR_NOT_ENOUGH_MEMORY; ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to allocate buffer for multicast " "salt for network %1!ws!.\n", networkId ); goto error_exit; } } // // Query the salt value from the cluster database. // status = DmQueryValue( netParamKey, CLUSREG_NAME_NET_MULTICAST_KEY_SALT, &type, (LPBYTE) salt, &saltLength ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to read multicast key salt for " "network %1!ws! from cluster database, " "status %2!u!. Using default.\n", networkId, status ); goto error_exit; } else if (type != REG_BINARY) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Unexpected type (%1!u!) for network " "%2!ws! %3!ws!. Using default.\n", type, networkId, CLUSREG_NAME_NET_MULTICAST_KEY_SALT ); status = ERROR_DATATYPE_MISMATCH; goto error_exit; } *NetworkParametersKey = netParamKey; netParamKey = NULL; *Salt = salt; salt = NULL; *SaltLength = saltLength; #if CLUSTER_BETA // // Display the salt. // { LPWSTR displayBuf = NULL; displayBuf = NmpCreateLogString(*Salt, *SaltLength); if (displayBuf != NULL) { ClRtlLogPrint(LOG_NOISE, "[NM] Found multicast key salt (length %1!u!): %2!ws!.\n", saltLength, displayBuf ); LocalFree(displayBuf); } } #endif // CLUSTER_BETA error_exit: if (openedNetParamKey && netParamKey != NULL) { DmCloseKey(netParamKey); netParamKey = NULL; } if (salt != NULL) { MIDL_user_free(salt); salt = NULL; } return(status); } // NmpQueryMulticastKeySalt DWORD NmpGenerateMulticastKeySalt( IN PNM_NETWORK Network, IN OUT PVOID * Salt, IN OUT ULONG * SaltLength ) /*++ Routine Description: Generates a default multicast key salt. Notes: Must not be called with NM lock held. --*/ { LPCWSTR networkId = OmObjectId(Network); HDMKEY netParamKey = NULL; DWORD createDisposition; PUCHAR salt; DWORD saltLength; DWORD status; #if CLUSTER_BETA ClRtlLogPrint(LOG_NOISE, "[NM] Generating multicast key salt for " "network %1!ws!.\n", networkId ); #endif // CLUSTER_BETA // // Allocate a buffer for the salt, if necessary. // salt = *Salt; saltLength = *SaltLength; if (salt != NULL && saltLength != sizeof(FILETIME)) { MIDL_user_free(salt); salt = NULL; saltLength = 0; *SaltLength = 0; } if (salt == NULL) { saltLength = sizeof(FILETIME); salt = MIDL_user_allocate(saltLength); if (salt == NULL) { status = ERROR_NOT_ENOUGH_MEMORY; ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to allocate buffer for multicast " "salt for network %1!ws!.\n", networkId ); goto error_exit; } } // // Create the salt. Use the current time. // GetSystemTimeAsFileTime((LPFILETIME) salt); *Salt = salt; salt = NULL; *SaltLength = saltLength; status = ERROR_SUCCESS; #if CLUSTER_BETA // // Display the salt. // { LPWSTR displayBuf = NULL; displayBuf = NmpCreateLogString(*Salt, *SaltLength); if (displayBuf != NULL) { ClRtlLogPrint(LOG_NOISE, "[NM] Generated multicast key salt (length %1!u!): %2!ws!.\n", saltLength, displayBuf ); LocalFree(displayBuf); } } #endif // CLUSTER_BETA error_exit: if (salt != NULL) { MIDL_user_free(salt); salt = NULL; } return(status); } // NmpGenerateMulticastKeySalt 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; } } // // Salt. // if (Parameters->Salt != NULL) { status = DmLocalSetValue( Xaction, NetworkParametersKey, CLUSREG_NAME_NET_MULTICAST_KEY_SALT, REG_BINARY, (BYTE *) Parameters->Salt, Parameters->SaltLength ); if (status != ERROR_SUCCESS) { failValueName = CLUSREG_NAME_NET_MULTICAST_KEY_SALT; 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 DWORD NmpRandomizeLeaseRenewTime( IN PNM_NETWORK Network, IN DWORD BaseRenewTime, IN DWORD Window ) /*++ Routine Description: Randomizes the lease renew time within Window on either side of BaseRenewTime. Current algorithm favors the NM leader, which gets BaseRenewTime. Other nodes are spread out in the Window according to node id. The nodes are grouped tighter as the number of nodes grows, since the common case is only a few nodes. Arguments: Network - network BaseRenewTime - time when lease should be renewed Window - period on either side of BaseRenewTime during which lease could be renewed Return value: Randomized lease renew time. --*/ { DWORD result = 0; DWORD interval, delta; DWORD index; static USHORT spread[] = {0, 16, 8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15}; if (BaseRenewTime == 0) { result = 0; goto error_exit; } if (Window == 0) { result = BaseRenewTime; goto error_exit; } interval = Window / ClusterDefaultMaxNodes; if (NmpLeaderNodeId == NmLocalNodeId) { result = BaseRenewTime; } else { if (interval == 0) { result = BaseRenewTime + Window; } else { if (NmLocalNodeId > sizeof(spread)/sizeof(spread[0]) - 1) { index = sizeof(spread)/sizeof(spread[0]) - 1; } else { index = NmLocalNodeId; } result = BaseRenewTime + spread[index] * interval; } } error_exit: return(result); } // NmpRandomizeLeaseRenewTime 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: result = 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; } NMP_TIME_TO_DWORD(result, dwResult); NMP_TIME_TO_DWORD(window, dwWindow); dwResult = NmpRandomizeLeaseRenewTime( Network, NMP_MADCAP_TO_NM_TIME(dwResult), NMP_MADCAP_TO_NM_TIME(dwWindow) ); 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) { 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) { 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 = OmObjectName(Network->LocalInterface->Node); LPCWSTR networkName = OmObjectName(Network); WCHAR errorString[12]; 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; MCAST_CLIENT_UID requestId = { NULL, 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, // salt 0, // salt length NULL, // key 0, // key length 0, // lease obtained 0, // lease expires (filled in below) &requestId, 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 VOID NmpChooseBetterMulticastScope( IN PIPNG_ADDRESS LocalAddress, IN PIPNG_ADDRESS LocalMask, IN PMCAST_SCOPE_ENTRY CurrentScope, OUT BOOLEAN * CurrentCorrectInterface, IN OUT PMCAST_SCOPE_ENTRY * BestScope ) /*++ Routine Description: Try to choose a good scope using the following criteria: - the interface must match (same subnet) the network address. - the scope must not be single-source (232.*.*.*), as defined by the IANA - aim for link-local - aim for a scope with a large range, increasing the probability that other clusters that may be on our subnet are assigned different group addresses - aim for the lowest TTL Arguments: LocalAddress - local address for network LocalMask - subnet mask for network CurrentScope - scope under consideration CurrentCorrectInterface - indicates whether current scope is on the same interface as the local network interface BestScope - current best scope (may be NULL) Return value: None. --*/ { BOOL bestLocal, currentLocal; DWORD bestRange, currentRange; *CurrentCorrectInterface = FALSE; // // This scope is not a candidate if it is not on // the correct interface or if it is single-source. // if (!ClRtlAreTcpipAddressesOnSameSubnet( CurrentScope->ScopeCtx.Interface.IpAddrV4, LocalAddress->IpAddrV4, LocalMask->IpAddrV4 )) { return; } *CurrentCorrectInterface = TRUE; if (ClRtlAreTcpipAddressesOnSameSubnet( CurrentScope->ScopeCtx.Interface.IpAddrV4, NMP_SINGLE_SOURCE_SCOPE_ADDRESS, NMP_SINGLE_SOURCE_SCOPE_MASK )) { return; } // // If the current best scope is NULL, then this // is the first match. // if (*BestScope == NULL) { goto use_current; } // // If the current scope is an administrative // link-local and the current best is not, // then the current scope wins. // bestLocal = ClRtlAreTcpipAddressesOnSameSubnet( (*BestScope)->ScopeCtx.Interface.IpAddrV4, NMP_LOCAL_SCOPE_ADDRESS, NMP_LOCAL_SCOPE_MASK ); currentLocal = ClRtlAreTcpipAddressesOnSameSubnet( CurrentScope->ScopeCtx.Interface.IpAddrV4, NMP_LOCAL_SCOPE_ADDRESS, NMP_LOCAL_SCOPE_MASK ); if (currentLocal && !bestLocal) { goto use_current; } else if (bestLocal && !currentLocal) { return; } // // If the current scope has a larger range than // the current best, the current wins. The scope // range is the last address minus the scope ID. // We do not consider exclusions. // bestRange = (*BestScope)->LastAddr.IpAddrV4 - (*BestScope)->ScopeCtx.ScopeID.IpAddrV4; currentRange = CurrentScope->LastAddr.IpAddrV4 - CurrentScope->ScopeCtx.ScopeID.IpAddrV4; if (currentRange > bestRange) { goto use_current; } else if (bestRange > currentRange) { return; } // // If the current scope has a smaller TTL than // the current best, the current wins. // if (CurrentScope->TTL < (*BestScope)->TTL) { goto use_current; } else if ((*BestScope)->TTL < CurrentScope->TTL) { return; } // // Found no reason to replace BestScope. // return; use_current: *BestScope = CurrentScope; return; } // NmpChooseBetterMulticastScope DWORD NmpFindMulticastScope( IN PNM_NETWORK Network, OUT PMCAST_SCOPE_CTX ScopeCtx, OUT BOOLEAN * FoundInterfaceMatch ) { LPCWSTR networkId = OmObjectId(Network); DWORD status; PMCAST_SCOPE_ENTRY scopeList = NULL; DWORD scopeCount; DWORD scope; PMCAST_SCOPE_ENTRY bestScope; BOOLEAN currentCorrectInterface = FALSE; BOOLEAN foundInterfaceMatch = FALSE; IPNG_ADDRESS networkAddress; IPNG_ADDRESS networkSubnet; CL_ASSERT(ScopeCtx != NULL); #if CLUSTER_BETA ClRtlLogPrint(LOG_NOISE, "[NM] Finding multicast scope for " "network %1!ws!.\n", networkId ); #endif // CLUSTER_BETA 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; } // // Try to choose the best scope among those enumerated. // // 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", 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", status ); goto error_exit; } #if CLUSTER_BETA ClRtlLogPrint( LOG_NOISE, "[NM] Trying to choose multicast scope for network " "%1!ws! with address %2!ws! and mask %3!ws!.\n", networkId, Network->Address, Network->AddressMask ); #endif // CLUSTER_BETA bestScope = NULL; for (scope = 0; scope < scopeCount; scope++) { #if CLUSTER_BETA ClRtlLogPrint(LOG_NOISE, "[NM] Examining 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!.\n", NmpIpAddrPrintArgs(scopeList[scope].ScopeCtx.Interface.IpAddrV4), NmpIpAddrPrintArgs(scopeList[scope].ScopeCtx.ScopeID.IpAddrV4), NmpIpAddrPrintArgs(scopeList[scope].LastAddr.IpAddrV4), NmpIpAddrPrintArgs(scopeList[scope].ScopeCtx.ServerID.IpAddrV4), scopeList[scope].ScopeDesc.Buffer ); #endif // CLUSTER_BETA NmpChooseBetterMulticastScope( &networkAddress, &networkSubnet, &(scopeList[scope]), ¤tCorrectInterface, &bestScope ); foundInterfaceMatch = (BOOLEAN)(foundInterfaceMatch || currentCorrectInterface); } if (bestScope == NULL) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to find multicast scope matching " "network id %1!ws!.\n", networkId ); status = ERROR_NOT_FOUND; goto error_exit; } else { ClRtlLogPrint(LOG_NOISE, "[NM] Selecting MADCAP scope [%1!u!.%2!u!.%3!u!.%4!u!, " "%5!u!.%6!u!.%7!u!.%8!u!] from server " "%9!u!.%10!u!.%11!u!.%12!u! on interface " "%13!u!.%14!u!.%15!u!.%16!u! with description " "%17!ws! for network %18!ws!.\n", NmpIpAddrPrintArgs(bestScope->ScopeCtx.ScopeID.IpAddrV4), NmpIpAddrPrintArgs(bestScope->LastAddr.IpAddrV4), NmpIpAddrPrintArgs(bestScope->ScopeCtx.ServerID.IpAddrV4), NmpIpAddrPrintArgs(bestScope->ScopeCtx.Interface.IpAddrV4), bestScope->ScopeDesc.Buffer, networkId ); } RtlCopyMemory(ScopeCtx, &(bestScope->ScopeCtx), sizeof(*ScopeCtx)); error_exit: *FoundInterfaceMatch = foundInterfaceMatch; if (scopeList != NULL) { LocalFree(scopeList); } return(status); } // NmpFindMulticastScope 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 { status = McastRequestAddress( AF_INET, RequestId, ScopeCtx, request, response ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to request multicast address on " "Scope ID %1!x!, Server ID %2!x!, Interface " "%3!x!, status %4!u!.\n", ScopeCtx->ScopeID.IpAddrV4, ScopeCtx->ServerID.IpAddrV4, ScopeCtx->Interface.IpAddrV4, 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 and lease end time. 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 halfLife; NM_MCAST_LEASE_STATUS status; if (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 { halfLife = LeaseObtained + ((LeaseExpires - LeaseObtained) / 2); if (currentTime >= halfLife) { status = NmMcastLeaseNeedsRenewal; } else { status = NmMcastLeaseValid; } } } #if CLUSTER_BETA if (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 LPMCAST_CLIENT_UID RequestId, OPTIONAL OUT LPWSTR * ServerAddress, OPTIONAL OUT LPWSTR * McastAddress OPTIONAL ) /*++ 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 DWORD * 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; time_t result; *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 < 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) { result = leaseExpires - NMP_MCAST_LEASE_RENEWAL_THRESHOLD; } else { result = halfhalfLife - currentTime; } NMP_TIME_TO_DWORD(result, *DeferRetry); 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; LPCWSTR networkId = OmObjectId(Network); BOOLEAN renew = FALSE; BOOLEAN retryFresh = FALSE; BOOLEAN madcapTimeout = FALSE; BOOLEAN newMcastAddress = FALSE; NM_MCAST_CONFIG configType = NmMcastConfigAuto; MCAST_SCOPE_CTX 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; renew = (BOOLEAN)(*McastAddress != NULL && *ServerAddress != NULL && RequestId->ClientUID != NULL && NmpMulticastValidateAddress(*McastAddress) && lstrcmpW(*ServerAddress, NmpNullMulticastAddress) != 0 ); do { if (!renew) { // // Find a scope. // status = NmpFindMulticastScope( Network, &scopeCtx, &interfaceMatch ); if (status != ERROR_SUCCESS) { 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 ); 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!.\n", networkId, status ); goto error_exit; } else { ClRtlLogPrint(LOG_NOISE, "[NM] MADCAP server reported no 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; } } // // 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, renew, ((renew) ? NULL : &scopeCtx), RequestId, McastAddress, &mcastAddressLength, ServerAddress, &serverAddressLength, &leaseStartTime, &leaseEndTime, &newMcastAddress ); if (status != ERROR_SUCCESS) { if (status == ERROR_TIMEOUT) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Attempt to contact MADCAP server timed " "out while requesting a multicast address " "(status %1!u!). Selecting default multicast " "address for network %2!ws! ...\n", status, networkId ); madcapTimeout = TRUE; goto error_exit; } else if (renew && !retryFresh) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to renew multicast address " "for network %1!ws!, status %2!u!. Attempting " "a fresh request ...\n", networkId, status ); retryFresh = TRUE; renew = FALSE; } else { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to request multicast address " "for network %1!ws!, status %2!u!.\n", networkId, status ); goto error_exit; } } else { // // Madcap config succeeded. // configType = NmMcastConfigMadcap; madcapTimeout = FALSE; // // Save lease renewal parameters. // requestId = *RequestId; serverAddress = *ServerAddress; // // Break out of loop. // retryFresh = FALSE; } } while ( retryFresh ); // // Fill in the parameters data structure. // status = NmpMulticastCreateParameters( 0, // disabled *McastAddress, NULL, // salt 0, // salt length 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; } status = ERROR_SUCCESS; error_exit: 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; DWORD deferRetry = 0; BOOLEAN localInterface = FALSE; 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 ); } RtlZeroMemory(¶meters, sizeof(parameters)); // // 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); } // // This may be the first configuration for this // network for this cluster instantiation, so // generate a new salt value. // status = NmpGenerateMulticastKeySalt( Network, ¶meters.Salt, ¶meters.SaltLength ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to generate multicast " "key salt for network %1!ws! during " "lease renewal, status %2!u!.\n", networkId, status ); goto error_exit; } // // 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, NMP_MADCAP_TO_NM_TIME(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, key, and salt 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; BOOLEAN mcastSaltChange = FALSE; #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)); // // If multicast is not disabled, we need valid parameters. // if (!Parameters->Disabled && ((Parameters->Address == NULL) || (Parameters->Key == NULL) || (Parameters->Salt == NULL) )) { status = ERROR_INVALID_PARAMETER; goto error_exit; } // // 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) { if (Network->MulticastKey == NULL || (Network->MulticastKeyLength != Parameters->KeyLength || RtlCompareMemory( Network->MulticastKey, Parameters->Key, Parameters->KeyLength ) != Parameters->KeyLength )) { // The key in the config parameters is different // from the key in memory. mcastKeyChange = TRUE; } } if (Parameters->Salt != NULL) { if (Network->MulticastKeySalt == NULL || (Network->MulticastKeySaltLength != Parameters->SaltLength || RtlCompareMemory( Network->MulticastKeySalt, Parameters->Salt, Parameters->SaltLength ) != Parameters->SaltLength )) { // The salt in the config parameters is different // from the salt in memory. mcastSaltChange = 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 || mcastSaltChange)) { // The multicast address, key, and/or salt changed and // multicast is enabled. callClusnet = TRUE; } // // If this network does not have a local interface, do not // plumb the configuration into clusnet. If this network // does have a local interface, the network must already // be registered. // if (Network->LocalInterface != NULL) { // // Verify that the network is registered. // if (!NmpIsNetworkRegistered(Network)) { status = ERROR_CLUSTER_NETWORK_NOT_FOUND; ClRtlLogPrint(LOG_UNUSUAL, "[NM] Cannot configure multicast parameters " "for unregistered network %1!ws!.\n", networkId ); goto error_exit; } } else { // // Do not call clusnet, but don't fail call. Also, // store new parameters in network object. // callClusnet = FALSE; ClRtlLogPrint(LOG_NOISE, "[NM] Not configuring cluster network driver with " "multicast parameters because network %1!ws! " "has no local interface.\n", networkId ); } // // Plumb the new configuration into 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) { // // 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 ); goto error_exit; } // // 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; } ClRtlLogPrint(LOG_NOISE, "[NM] Configuring cluster network driver with " "multicast parameters for network %1!ws!.\n", networkId ); status = ClusnetConfigureMulticast( NmClusnetHandle, Network->ShortId, brand, tdiMcastAddress, tdiMcastAddressLength, Parameters->Key, Parameters->KeyLength, Parameters->Salt, Parameters->SaltLength ); 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; } } } // // If successful, 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->Key = Network->MulticastKey; Network->MulticastKey = Parameters->Key; UndoParameters->KeyLength = Network->MulticastKeyLength; Network->MulticastKeyLength = Parameters->KeyLength; UndoParameters->Salt = Network->MulticastKeySalt; Network->MulticastKeySalt = Parameters->Salt; UndoParameters->SaltLength = Network->MulticastKeySaltLength; Network->MulticastKeySaltLength = Parameters->SaltLength; 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; // // 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 (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 ); } } 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 NM_MCAST_CONFIG ConfigType ) /*++ Routine description: Called after a non-manual refresh of the multicast configuration, sets a timer to renew the multicast address lease for this network, if one exists. Notes: Called and returns with NM lock held. --*/ { DWORD status; LPCWSTR networkId = OmObjectId(Network); DWORD disabled; BOOLEAN lockAcquired = TRUE; time_t leaseObtained; time_t leaseExpires; DWORD leaseTimer = 0; NM_MCAST_LEASE_STATUS leaseStatus = NmMcastLeaseValid; #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) { NmpScheduleMulticastAddressRenewal(Network); } else { leaseTimer = NmpCalculateLeaseRenewTime( Network, ConfigType, &leaseObtained, &leaseExpires ); NmpStartNetworkMulticastAddressRenewTimer(Network, leaseTimer); } return; } // NmpShareMulticastAddressLease 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); BOOLEAN lockAcquired = FALSE; DWORD netobjDisabled; 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. // NmpAcquireLock(); lockAcquired = TRUE; netobjDisabled = (NmpIsNetworkMulticastEnabled(Network) ? 0 : 1); // // See if anything changed. // if (DisableConfig) { if (Disabled != netobjDisabled) { 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( netobjDisabled, Network->MulticastAddress, NULL, // salt 0, // salt length 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 ); } // 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 we are not disabling multicast, we will need // a new salt value. // if (!Disabled) { status = NmpGenerateMulticastKeySalt( Network, &Parameters->Salt, &Parameters->SaltLength ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to generate multicast " "key salt for network %1!ws! during " "manual configuration, status %2!u!.\n", networkId, status ); goto error_exit; } } *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); } 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 key. - Generate a salt. 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 ); 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 ); } } // // Generate a new multicast salt. // status = NmpGenerateMulticastKeySalt( Network, ¶ms.Salt, ¶ms.SaltLength ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to generate multicast key salt for " "network %1!ws!, status %2!u!.\n", networkId, status ); goto error_exit; } // // 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 DWORD NmpReconfigureClusterMulticast( VOID ) /*++ Routine Description: Configures multicast from scratch for all networks in cluster. --*/ { DWORD status = ERROR_SUCCESS; PNM_NETWORK * networkList; DWORD networkCount; PNM_NETWORK network; PLIST_ENTRY entry; DWORD i = 0; ClRtlLogPrint(LOG_NOISE, "[NM] Reconfiguring multicast on all cluster networks.\n" ); NmpAcquireLock(); networkCount = NmpNetworkCount; networkList = (PNM_NETWORK *) LocalAlloc( LMEM_FIXED, networkCount * sizeof(PNM_NETWORK) ); if (networkList == NULL) { NmpReleaseLock(); return(ERROR_NOT_ENOUGH_MEMORY); } for (entry = NmpNetworkList.Flink; entry != &NmpNetworkList; entry = entry->Flink ) { network = CONTAINING_RECORD( entry, NM_NETWORK, Linkage ); // // Place a pointer to the network in the network list, // and reference the network so it doesn't disappear. // networkList[i] = network; NmpReferenceNetwork(network); i++; CL_ASSERT(i <= networkCount); } NmpReleaseLock(); for (i = 0; i < networkCount; i++) { status = NmpReconfigureMulticast(networkList[i]); if (status != ERROR_SUCCESS) { if (status == ERROR_IO_PENDING) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Deferred multicast reconfiguration " "for network %1!ws! to worker thread.\n", OmObjectId(networkList[i]) ); status = ERROR_SUCCESS; } else { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to reconfigure multicast " "for network %1!ws!, status %2!u!.\n", OmObjectId(networkList[i]), status ); // // Not a de facto fatal error. // status = ERROR_SUCCESS; } } // // Drop the reference that was taken in the // enum routine. // NmpDereferenceNetwork(networkList[i]); } // // Deallocate the network list. // LocalFree(networkList); return(status); } // NmpReconfigureClusterMulticast 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 ///////////////////////////////////////////////////////////////////////////// // // Routines exported within NM. // ///////////////////////////////////////////////////////////////////////////// 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 BOOLEAN NmpIsClusterMulticastReady( IN BOOLEAN CheckNodeCount ) /*++ 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 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 (reason != NULL) { ClRtlLogPrint(LOG_NOISE, "[NM] Multicast is not justified for this " "cluster because %1!ws!.\n", reason ); } return((BOOLEAN)(reason == NULL)); } // NmpIsClusterMulticastReady 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 checkReady = 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; checkReady = 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 configured to justify multicast.\n" ); NmpMulticastIsNotEnoughNodes = FALSE; checkReady = TRUE; } } if (stop) { // // Stop multicast, since we are no longer // multicast-ready. // NmpStopMulticast(NULL); // // Don't bother checking whether we are now // multicast-ready. // checkReady = FALSE; } // // Start multicast if this is the NM leader node // and we have now become multicast-ready. // if (checkReady && NmpLeaderNodeId == NmLocalNodeId && NmpIsClusterMulticastReady(TRUE)) { NmpStartMulticast(NULL); } 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 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 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. Generates a new multicast key salt. Then writes the new salt to the cluster database and sets the lease expiration to zero. 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)) { *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; NM_MCAST_CONFIG configType; 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 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; } // // Save the config type. // configType = params.ConfigType; // // 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 renewal responsibility for this lease. // NmpShareMulticastAddressLease(network, configType); } else { 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); NmpMulticastFreeParameters(&undoParams); if (networkKey != NULL) { DmCloseKey(networkKey); networkKey = NULL; } if (network != NULL) { OmDereferenceObject(network); } return(status); } // NmpUpdateSetNetworkMulticastConfiguration DWORD NmpRefreshMulticastConfiguration( IN PNM_NETWORK Network ) /*++ Routine Description: NmpRefreshMulticastConfiguration enables multicast on the specified Network according to parameters in the cluster database. Notes: Must not be called with the NM lock held. --*/ { LPWSTR networkId = (LPWSTR) OmObjectId(Network); DWORD status; 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; ClRtlLogPrint(LOG_NOISE, "[NM] Configuring multicast for network %1!ws!.\n", networkId ); // // 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 ); 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"") ); goto error_exit; } // // Only re-generate the key if multicast is not disabled. // if (!params.Disabled) { status = NmpGenerateMulticastKey( Network, ¶ms.Key, ¶ms.KeyLength ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to generate multicast key for " "network %1!ws!, status %2!u!.\n", networkId, status ); goto error_exit; } } // // Get the multicast salt. // status = NmpQueryMulticastKeySalt( Network, networkKey, &netParamKey, ¶ms.Salt, ¶ms.SaltLength ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to get multicast key salt for " "network %1!ws!, status %2!u!.\n", networkId, status ); 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; // // Process the configuration changes. // NmpAcquireLock(); status = NmpProcessMulticastConfiguration( Network, ¶ms, &undoParams ); // // Check the lease renew parameters if this was not a // manual configuration. // if (!disabled && configType != NmMcastConfigManual) { NmpShareMulticastAddressLease( Network, configType ); } NmpReleaseLock(); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to process multicast configuration " "for network %1!ws!, status %2!u!.\n", networkId, status ); goto error_exit; } ClRtlLogPrint(LOG_NOISE, "[NM] Multicast configuration for network %1!ws! " "was successful.\n", networkId ); error_exit: 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); return(status); } // NmpRefreshMulticastConfiguration DWORD NmpRefreshClusterMulticastConfiguration( VOID ) /*++ Routine Description: Configures multicast on all networks in cluster. --*/ { DWORD status = ERROR_SUCCESS; PNM_NETWORK * networkList; DWORD networkCount; PNM_NETWORK network; PLIST_ENTRY entry; DWORD i = 0; ClRtlLogPrint(LOG_NOISE, "[NM] Configuring multicast on all cluster networks.\n" ); NmpAcquireLock(); networkCount = NmpNetworkCount; networkList = (PNM_NETWORK *) LocalAlloc( LMEM_FIXED, networkCount * sizeof(PNM_NETWORK) ); if (networkList == NULL) { NmpReleaseLock(); return(ERROR_NOT_ENOUGH_MEMORY); } for (entry = NmpNetworkList.Flink; entry != &NmpNetworkList; entry = entry->Flink ) { network = CONTAINING_RECORD( entry, NM_NETWORK, Linkage ); // // Place a pointer to the network in the network list, // and reference the network so it doesn't disappear. // networkList[i] = network; NmpReferenceNetwork(network); i++; CL_ASSERT(i <= networkCount); } NmpReleaseLock(); for (i = 0; i < networkCount; i++) { status = NmpRefreshMulticastConfiguration(networkList[i]); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to configure multicast " "parameters for network, %1!ws!, " "status %2!u!.\n", OmObjectId(networkList[i]), status ); // // Not a de facto fatal error. // status = ERROR_SUCCESS; } // // Drop the reference that was taken in the // enum routine. // NmpDereferenceNetwork(networkList[i]); } // // Deallocate the network list. // LocalFree(networkList); return(status); } // NmpRefreshClusterMulticastConfiguration 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)) { 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 NmpMulticastRegenerateKey( IN PNM_NETWORK Network ) /*++ Routine Description: Regenerates the network multicast key, and reconfigures ClusNet with new key if it has changed. Arguments: Network - network to regenerate key for, or NULL if key should be regenerated for all Notes: Called and returns with NM lock held. --*/ { DWORD status = ERROR_SUCCESS; PLIST_ENTRY entry; PNM_NETWORK network; LPWSTR networkId; NM_NETWORK_MULTICAST_PARAMETERS params = { 0 }; NM_NETWORK_MULTICAST_PARAMETERS undoParams = { 0 }; if (Network == NULL) { ClRtlLogPrint(LOG_NOISE, "[NM] Regenerating key for all cluster networks.\n" ); for (entry = NmpNetworkList.Flink; entry != &NmpNetworkList; entry = entry->Flink) { network = CONTAINING_RECORD(entry, NM_NETWORK, Linkage); NmpMulticastRegenerateKey(network); } status = ERROR_SUCCESS; } else if (NmpIsNetworkMulticastEnabled(Network)) { networkId = (LPWSTR) OmObjectId(Network); ClRtlLogPrint(LOG_NOISE, "[NM] Regenerating key for cluster network %1!ws!.\n", networkId ); // // Create a parameters structure with the current // configuration. // status = NmpMulticastCreateParameters( 0, // disabled Network->MulticastAddress, Network->MulticastKeySalt, Network->MulticastKeySaltLength, NULL, // leave key blank 0, // leave key length blank Network->MulticastLeaseObtained, Network->MulticastLeaseExpires, &(Network->MulticastLeaseRequestId), Network->MulticastLeaseServer, 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!.\n", networkId, status ); goto error_exit; } // // Generate the new key. // status = NmpGenerateMulticastKey( Network, ¶ms.Key, ¶ms.KeyLength ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to generate multicast " "key for network %1!ws!, status %2!u!.\n", networkId, status ); goto error_exit; } status = NmpProcessMulticastConfiguration( Network, ¶ms, &undoParams ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to process multicast " "configuration for network %1!ws! " "after key regeneration, status %2!u!.\n", networkId, status ); NmpMulticastSetNullAddressParameters(Network, ¶ms); status = NmpProcessMulticastConfiguration( Network, ¶ms, &undoParams ); if (status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_UNUSUAL, "[NM] Failed to set null multicast " "configuration for network %1!ws! " "after key regeneration, status %2!u!.\n", networkId, status ); goto error_exit; } } } error_exit: if (status != ERROR_SUCCESS && Network != NULL) { ClRtlLogPrint(LOG_NOISE, "[NM] Failed to regenerate multicast key for " "network %1!ws!, status %2!u!.\n", OmObjectId(Network), status ); } NmpMulticastFreeParameters(¶ms); NmpMulticastFreeParameters(&undoParams); return(status); } // NmpMulticastRegenerateKey DWORD NmpStartMulticast( IN PNM_NETWORK Network OPTIONAL ) /*++ Routine Description: Start multicast on a network by configuring multicast parameters and sending a GUM update. Deferred to the network worker thread. Arguments: Network - network on which to start multicast. If NULL, start multicast on all networks. Notes: Must be called with NM lock held. --*/ { PLIST_ENTRY entry; PNM_NETWORK network; LPWSTR networkId; CL_ASSERT(NmpLeaderNodeId == NmLocalNodeId); 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); NmpStartMulticast(network); } } else { networkId = (LPWSTR) OmObjectId(Network); ClRtlLogPrint(LOG_NOISE, "[NM] Starting multicast for cluster network %1!ws!.\n", networkId ); NmpScheduleMulticastReconfiguration(Network); } return(ERROR_SUCCESS); } // NmpStartMulticast DWORD NmpStopMulticast( IN PNM_NETWORK Network OPTIONAL ) /*++ 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] Starting 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 Network->MulticastKeySalt, Network->MulticastKeySaltLength, Network->MulticastKey, Network->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 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; } 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