/*++ Copyright (c) 1998 Microsoft Corporation Module Name: mbound.c Abstract: This module implements routines associated with administratively- scoped boundaries (i.e. group-prefix boundaries). An IPv4 Local Scope boundary implicitly exists (no state needed) whenever any other boundary exists. The IPv4 Local Scope implicitly exists whenever any other scope exists. Author: dthaler@microsoft.com 4-20-98 Revision History: --*/ #include "allinc.h" #include "mbound.h" #include // for floor() #pragma hdrstop #ifdef DEBUG #define INLINE #else #define INLINE __inline #endif #define MZAP_DEFAULT_BIT 0x80 #define MAX_SCOPES 10 SCOPE_ENTRY g_scopeEntry[MAX_SCOPES]; SCOPE_ENTRY g_LocalScope; #define BOUNDARY_HASH_TABLE_SIZE 57 #define BOUNDARY_HASH(X) ((X) % BOUNDARY_HASH_TABLE_SIZE) BOUNDARY_BUCKET g_bbScopeTable[BOUNDARY_HASH_TABLE_SIZE]; #define ROWSTATUS_ACTIVE 1 #define MIN_SCOPE_ADDR 0xef000000 #define MAX_SCOPE_ADDR (0xefff0000 - 1) #define IPV4_LOCAL_SCOPE_LANG MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US) #define IPV4_LOCAL_SCOPE_NAME SN_L"IPv4 Local Scope" #define IPV4_LOCAL_SCOPE_ADDR htonl(0xefff0000) #define IPV4_LOCAL_SCOPE_MASK htonl(0xffff0000) #define IN_IPV4_LOCAL_SCOPE(x) \ (((x) & IPV4_LOCAL_SCOPE_MASK) == IPV4_LOCAL_SCOPE_ADDR) LIST_ENTRY g_MasterInterfaceList; LIST_ENTRY g_MasterScopeList; #define MALLOC(dwSize) HeapAlloc(IPRouterHeap, 0, dwSize) #define FREE(x) HeapFree(IPRouterHeap, 0, x) #define MIN(x,y) (((x)<(y))?(x):(y)) // Forward static declarations DWORD AssertBoundaryEntry( BOUNDARY_IF *pBoundaryIf, SCOPE_ENTRY *pScope, PBOUNDARY_ENTRY *ppBoundary ); VOID MzapInitScope( PSCOPE_ENTRY pScope ); DWORD MzapInitBIf( PBOUNDARY_IF pBIf ); VOID MzapUninitBIf( PBOUNDARY_IF pBIf ); DWORD MzapActivateBIf( PBOUNDARY_IF pBIf ); // // Functions to manipulate scopes // PSCOPE_ENTRY NewScope() { DWORD dwScopeIndex; // Find an unused scope index for (dwScopeIndex=0; dwScopeIndexFlink) { SCOPE_ENTRY *pScope = CONTAINING_RECORD(pleNode, SCOPE_ENTRY, leScopeLink); if (pScope->ipGroupAddress == ipGroupAddress && pScope->ipGroupMask == ipGroupMask) return pScope; } return NULL; } PBYTE GetLangName( IN LANGID idLanguage ) { char b1[8], b2[8]; static char buff[80]; LCID lcid = MAKELCID(idLanguage, SORT_DEFAULT); GetLocaleInfo(lcid, LOCALE_SISO639LANGNAME, b1, sizeof(b1)); GetLocaleInfo(lcid, LOCALE_SISO3166CTRYNAME, b2, sizeof(b2)); if (_stricmp(b1, b2)) sprintf(buff, "%s-%s", b1, b2); else strcpy(buff, b1); return buff; } PSCOPE_NAME_ENTRY GetScopeNameByLangID( IN PSCOPE_ENTRY pScope, IN LANGID idLanguage ) /*++ Called by: AssertScopeName() --*/ { PLIST_ENTRY pleNode; PSCOPE_NAME_ENTRY pName; for (pleNode = pScope->leNameList.Flink; pleNode isnot &pScope->leNameList; pleNode = pleNode->Flink) { pName = CONTAINING_RECORD(pleNode, SCOPE_NAME_ENTRY, leNameLink); if (idLanguage == pName->idLanguage) return pName; } return NULL; } PSCOPE_NAME_ENTRY GetScopeNameByLangName( IN PSCOPE_ENTRY pScope, IN PBYTE pLangName ) /*++ Called by: CheckForScopeNameMismatch() --*/ { PLIST_ENTRY pleNode; PSCOPE_NAME_ENTRY pName; for (pleNode = pScope->leNameList.Flink; pleNode isnot &pScope->leNameList; pleNode = pleNode->Flink) { pName = CONTAINING_RECORD(pleNode, SCOPE_NAME_ENTRY, leNameLink); if (!strcmp(pLangName, GetLangName(pName->idLanguage))) return pName; } return NULL; } VOID MakePrefixStringW( OUT PWCHAR pwcPrefixStr, IN IPV4_ADDRESS ipAddr, IN IPV4_ADDRESS ipMask ) { swprintf( pwcPrefixStr, L"%d.%d.%d.%d/%d", PRINT_IPADDR(ipAddr), MaskToMaskLen(ipMask) ); } // Global buffers used to create messages WCHAR g_AddrBuf1[20]; WCHAR g_AddrBuf2[20]; WCHAR g_AddrBuf3[20]; WCHAR g_AddrBuf4[20]; VOID MakeAddressStringW( OUT PWCHAR pwcAddressStr, IN IPV4_ADDRESS ipAddr ) { swprintf( pwcAddressStr, L"%d.%d.%d.%d", PRINT_IPADDR(ipAddr) ); } SCOPE_NAME GetDefaultName( IN PSCOPE_ENTRY pScope ) /*++ Called by: RmGetNextScope() Various other functions for use in Trace() calls --*/ { PLIST_ENTRY pleNode; PSCOPE_NAME_ENTRY pName; static SCOPE_NAME_BUFFER snScopeNameBuffer; SCOPE_NAME pFirst = NULL; for (pleNode = pScope->leNameList.Flink; pleNode isnot &pScope->leNameList; pleNode = pleNode->Flink) { pName = CONTAINING_RECORD(pleNode, SCOPE_NAME_ENTRY, leNameLink); if (pName->bDefault) return pName->snScopeName; if (!pFirst) pFirst = pName->snScopeName; } // If any names were specified, just pick the first one. if (pFirst) return pFirst; MakePrefixStringW( snScopeNameBuffer, pScope->ipGroupAddress, pScope->ipGroupMask ); return snScopeNameBuffer; } VOID DeleteScopeName( IN PLIST_ENTRY pleNode ) { PSCOPE_NAME_ENTRY pName = CONTAINING_RECORD( pleNode, SCOPE_NAME_ENTRY, leNameLink ); RemoveEntryList(pleNode); if (pName->snScopeName) FREE(pName->snScopeName); FREE( pName ); } DWORD AssertScopeName( IN PSCOPE_ENTRY pScope, IN LANGID idLanguage, IN SCOPE_NAME snScopeName // unicode string to duplicate ) /*++ Arguments: pScope - scope entry to modify idLanguage - language ID of new name snScopeName - new name to use Called by: MzapInitLocalScope(), AddScope(), ParseScopeInfo(), SetScopeInfo(), SNMPSetScope() --*/ { SCOPE_NAME_BUFFER snScopeNameBuffer; PSCOPE_NAME_ENTRY pName; pName = GetScopeNameByLangID(pScope, idLanguage); // // See if the name is already correct. // if (pName && snScopeName && !sn_strcmp( snScopeName, pName->snScopeName )) { return NO_ERROR; } // // Create a scope name if we weren't given one // if ( snScopeName is NULL || snScopeName[0] is '\0' ) { MakePrefixStringW( snScopeNameBuffer, pScope->ipGroupAddress, pScope->ipGroupMask ); snScopeName = snScopeNameBuffer; } // Add a name entry if needed if (!pName) { pName = (PSCOPE_NAME_ENTRY)MALLOC( sizeof(SCOPE_NAME_ENTRY) ); if (!pName) { return ERROR_NOT_ENOUGH_MEMORY; } pName->bDefault = FALSE; pName->snScopeName = NULL; pName->idLanguage = idLanguage; InsertTailList( &pScope->leNameList, &pName->leNameLink ); pScope->ulNumNames++; } // // Free the old name and save the new one // if (pName->snScopeName) { FREE( pName->snScopeName ); } pName->snScopeName = (SCOPE_NAME)MALLOC( (sn_strlen(snScopeName)+1) * SNCHARSIZE ); if (pName->snScopeName == NULL) { DWORD dwErr = GetLastError(); Trace3( ANY, "Error %d allocating %d bytes for scope name %s", dwErr, sn_strlen(snScopeName)+1, snScopeName ); return dwErr; } sn_strcpy(pName->snScopeName, snScopeName); Trace4(MCAST, "Updated scope name for \"%s\": %ls (%d.%d.%d.%d/%d)", GetLangName(idLanguage), snScopeName, PRINT_IPADDR(pScope->ipGroupAddress), MaskToMaskLen(pScope->ipGroupMask) ); return NO_ERROR; } VOID MzapInitLocalScope() /*++ Called by: ActivateMZAP() --*/ { PSCOPE_ENTRY pScope = &g_LocalScope; pScope->ipGroupAddress = IPV4_LOCAL_SCOPE_ADDR; pScope->ipGroupMask = IPV4_LOCAL_SCOPE_MASK; InitializeListHead( &pScope->leNameList ); pScope->ulNumNames = 0; MzapInitScope(pScope); AssertScopeName( pScope, IPV4_LOCAL_SCOPE_LANG, IPV4_LOCAL_SCOPE_NAME ); } DWORD AddScope( IN IPV4_ADDRESS ipGroupAddress, IN IPV4_ADDRESS ipGroupMask, OUT PSCOPE_ENTRY *pScopeEntry ) /*++ Routine Description: Add a named scope. Arguments: IN ipGroupAddress - first address in the scope to add IN ipGroupMask - mask associated with the address OUT pScope - scope entry added Called by: AssertScope() Locks: Assumes caller holds write lock on BOUNDARY_TABLE Returns: NO_ERROR ERROR_NOT_ENOUGH_MEMORY ERROR_INVALID_PARAMETER --*/ { SCOPE_ENTRY *pScope; PLIST_ENTRY pleNode; // See if any bits are set in the address but not the mask if (ipGroupAddress & ~ipGroupMask) return ERROR_INVALID_PARAMETER; // Make sure the address is a valid one if (ntohl(ipGroupAddress) < MIN_SCOPE_ADDR || ntohl(ipGroupAddress) > MAX_SCOPE_ADDR) return ERROR_INVALID_PARAMETER; // Make sure we have space for this entry if ((pScope = NewScope()) == NULL) return ERROR_NOT_ENOUGH_MEMORY; pScope->ipGroupAddress = ipGroupAddress; pScope->ipGroupMask = ipGroupMask; InitializeListHead( &pScope->leNameList ); pScope->ulNumNames = 0; #if 0 { SCOPE_NAME_BUFFER snScopeNameBuffer; // Create a scope name if we weren't given one if ( snScopeName is NULL || snScopeName[0] is '\0' ) { MakePrefixStringW( snScopeNameBuffer, pScope->ipGroupAddress, pScope->ipGroupMask ); snScopeName = snScopeNameBuffer; } AssertScopeName( pScope, idLanguage, snScopeName ); } #endif MzapInitScope(pScope); // // Add it to the master scope list // // Search for entry after the new one for (pleNode = g_MasterScopeList.Flink; pleNode isnot &g_MasterScopeList; pleNode = pleNode->Flink) { SCOPE_ENTRY *pPrevScope = CONTAINING_RECORD(pleNode, SCOPE_ENTRY, leScopeLink); IPV4_ADDRESS ipAddress = pPrevScope->ipGroupAddress; IPV4_ADDRESS ipMask = pPrevScope->ipGroupMask; if (ipAddress > pScope->ipGroupAddress || (ipAddress==pScope->ipGroupAddress && ipMask>pScope->ipGroupMask)) break; } InsertTailList( pleNode, &pScope->leScopeLink ); *pScopeEntry = pScope; #if 0 Trace4(MCAST, "AddScope: added %s %ls (%d.%d.%d.%d/%d)", GetLangName( idLanguage ), snScopeName, PRINT_IPADDR(ipGroupAddress), MaskToMaskLen(ipGroupMask) ); #endif Trace2(MCAST, "AddScope: added (%d.%d.%d.%d/%d)", PRINT_IPADDR(ipGroupAddress), MaskToMaskLen(ipGroupMask) ); return NO_ERROR; } DWORD AssertScope( IN IPV4_ADDRESS ipGroupAddress, IN IPV4_ADDRESS ipGroupMask, OUT PSCOPE_ENTRY *ppScopeEntry ) /*++ Arguments: ipGroupAddress - address part of the scope prefix ipGroupMask - mask part of the scope prefix ppScopeEntry - scope entry to return to caller Locks: Assumes caller holds write lock on BOUNDARY_TABLE. Called by: SetScopeInfo() SNMPAddBoundaryToInterface() Returns: NO_ERROR - success whatever AddScope() returns --*/ { DWORD dwResult = NO_ERROR; *ppScopeEntry = FindScope(ipGroupAddress, ipGroupMask); if (! *ppScopeEntry) { dwResult = AddScope(ipGroupAddress, ipGroupMask, ppScopeEntry); } return dwResult; } DWORD DeleteScope( IN PSCOPE_ENTRY pScope ) /*++ Routine Description: Remove all information about a given boundary. Called by: SetScopeInfo(), SNMPDeleteBoundaryFromInterface() Locks: Assumes caller holds write lock on BOUNDARY_TABLE Returns: NO_ERROR --*/ { Trace2( MCAST, "ENTERED DeleteScope: %d.%d.%d.%d/%d", PRINT_IPADDR(pScope->ipGroupAddress), MaskToMaskLen(pScope->ipGroupMask) ); if (pScope->ipGroupAddress == 0) { Trace0( MCAST, "LEFT DeleteScope" ); return NO_ERROR; // already deleted } if (pScope->ulNumInterfaces > 0) { // // Walk all interfaces looking for references. It doesn't matter // whether this is inefficient, since it occurs extremely rarely, // if ever, and we don't care whether it takes a couple of seconds // to do. // DWORD dwBucketIdx; PLIST_ENTRY pleNode, pleNext; for (dwBucketIdx = 0; dwBucketIdx < BOUNDARY_HASH_TABLE_SIZE && pScope->ulNumInterfaces > 0; dwBucketIdx++) { for (pleNode = g_bbScopeTable[dwBucketIdx].leInterfaceList.Flink; pleNode isnot & g_bbScopeTable[dwBucketIdx].leInterfaceList; pleNode = pleNext) { BOUNDARY_ENTRY *pBoundary = CONTAINING_RECORD(pleNode, BOUNDARY_ENTRY, leBoundaryLink); // Save pointer to next node, since we may delete the current one pleNext = pleNode->Flink; if (pBoundary->pScope == pScope) { // Delete boundary RemoveEntryList(&(pBoundary->leBoundaryLink)); pScope->ulNumInterfaces--; FREE(pBoundary); } } } } // Do the actual scope deletion RemoveEntryList(&(pScope->leScopeLink)); pScope->ipGroupAddress = 0; pScope->ipGroupMask = 0xFFFFFFFF; while (! IsListEmpty(&pScope->leNameList) ) { DeleteScopeName(pScope->leNameList.Flink); pScope->ulNumNames--; } Trace0( MCAST, "LEFT DeleteScope" ); return NO_ERROR; } // // Routines to manipulate BOUNDARY_IF structures // BOUNDARY_IF * FindBIfEntry( IN DWORD dwIfIndex ) /*++ Locks: Assumes caller holds at least a read lock on BOUNDARY_TABLE Called by: AssertBIfEntry(), RmHasBoundary(), BindBoundaryInterface() Returns: pointer to BOUNDARY_IF entry, if found NULL, if not found --*/ { PLIST_ENTRY pleNode; BOUNDARY_IF *pIf; DWORD dwBucketIdx = BOUNDARY_HASH(dwIfIndex); for (pleNode = g_bbScopeTable[dwBucketIdx].leInterfaceList.Flink; pleNode isnot & g_bbScopeTable[dwBucketIdx].leInterfaceList; pleNode = pleNode->Flink) { pIf = CONTAINING_RECORD(pleNode, BOUNDARY_IF, leBoundaryIfLink); if (pIf->dwIfIndex == dwIfIndex) return pIf; } return NULL; } BOUNDARY_IF * FindBIfEntryBySocket( IN SOCKET sMzapSocket ) { register PLIST_ENTRY pleNode; register DWORD dwBucketIdx; BOUNDARY_IF *pIf; for (dwBucketIdx = 0; dwBucketIdx < BOUNDARY_HASH_TABLE_SIZE; dwBucketIdx++) { for (pleNode = g_bbScopeTable[dwBucketIdx].leInterfaceList.Flink; pleNode isnot & g_bbScopeTable[dwBucketIdx].leInterfaceList; pleNode = pleNode->Flink) { pIf = CONTAINING_RECORD(pleNode, BOUNDARY_IF, leBoundaryIfLink); if (pIf->sMzapSocket == sMzapSocket) return pIf; } } return NULL; } DWORD AddBIfEntry( IN DWORD dwIfIndex, OUT PBOUNDARY_IF *ppBoundaryIf, IN BOOL bIsOperational ) /*++ Locks: Assumes caller holds a write lock on BOUNDARY_TABLE Called by: AssertBIfEntry() Returns: NO_ERROR on success ERROR_NOT_ENOUGH_MEMORY --*/ { PLIST_ENTRY pleNode; DWORD dwBucketIdx, dwErr = NO_ERROR; BOUNDARY_IF *pBoundaryIf; Trace1(MCAST, "AddBIfEntry %x", dwIfIndex); dwBucketIdx = BOUNDARY_HASH(dwIfIndex); pBoundaryIf = MALLOC( sizeof(BOUNDARY_IF) ); if (!pBoundaryIf) return ERROR_NOT_ENOUGH_MEMORY; pBoundaryIf->dwIfIndex = dwIfIndex; InitializeListHead(&pBoundaryIf->leBoundaryList); MzapInitBIf(pBoundaryIf); if (bIsOperational) { dwErr = MzapActivateBIf(pBoundaryIf); } // find entry in bucket's list to insert before for (pleNode = g_bbScopeTable[dwBucketIdx].leInterfaceList.Flink; pleNode isnot &g_bbScopeTable[dwBucketIdx].leInterfaceList; pleNode = pleNode->Flink) { BOUNDARY_IF *pPrevIf = CONTAINING_RECORD(pleNode, BOUNDARY_IF, leBoundaryIfLink); if (pPrevIf->dwIfIndex > dwIfIndex) break; } InsertTailList( pleNode, &(pBoundaryIf->leBoundaryIfLink)); // find entry in master list to insert before for (pleNode = g_MasterInterfaceList.Flink; pleNode isnot &g_MasterInterfaceList; pleNode = pleNode->Flink) { BOUNDARY_IF *pPrevIf = CONTAINING_RECORD(pleNode, BOUNDARY_IF, leBoundaryIfLink); if (pPrevIf->dwIfIndex > dwIfIndex) break; } InsertTailList( pleNode, &(pBoundaryIf->leBoundaryIfMasterLink)); *ppBoundaryIf = pBoundaryIf; return dwErr; } DWORD AssertBIfEntry( IN DWORD dwIfIndex, OUT PBOUNDARY_IF *ppBoundaryIf, IN BOOL bIsOperational ) /*++ Locks: Assumes caller holds a write lock on BOUNDARY_TABLE Called by: SetBoundaryInfo(), SNMPAddBoundaryToInterface() --*/ { if ((*ppBoundaryIf = FindBIfEntry(dwIfIndex)) != NULL) return NO_ERROR; return AddBIfEntry(dwIfIndex, ppBoundaryIf, bIsOperational); } // // Routines to manipulate BOUNDARY_ENTRY structures // BOUNDARY_ENTRY * FindBoundaryEntry( BOUNDARY_IF *pBoundaryIf, SCOPE_ENTRY *pScope ) /*++ Locks: Assumes caller already holds at least a read lock on BOUNDARY_TABLE Called by: AssertBoundaryEntry() Returns: pointer to BOUNDARY_ENTRY, if found NULL, if not found --*/ { PLIST_ENTRY pleNode; for (pleNode = pBoundaryIf->leBoundaryList.Flink; pleNode isnot &(pBoundaryIf->leBoundaryList); pleNode = pleNode->Flink) { BOUNDARY_ENTRY *pBoundary = CONTAINING_RECORD(pleNode, BOUNDARY_ENTRY, leBoundaryLink); if (pScope == &g_LocalScope || pScope == pBoundary->pScope) return pBoundary; } return NULL; } DWORD AddBoundaryEntry( BOUNDARY_IF *pBoundaryIf, SCOPE_ENTRY *pScope, PBOUNDARY_ENTRY *ppBoundary ) /*++ Called by: AssertBoundaryEntry() Locks: Assumes caller holds a write lock on BOUNDARY_TABLE Returns: NO_ERROR on success ERROR_NOT_ENOUGH_MEMORY --*/ { PLIST_ENTRY pleNode; Trace3(MCAST, "AddBoundaryEntry: If %x Scope %d.%d.%d.%d/%d", pBoundaryIf->dwIfIndex, PRINT_IPADDR(pScope->ipGroupAddress), MaskToMaskLen(pScope->ipGroupMask) ); if ((*ppBoundary = MALLOC( sizeof(BOUNDARY_ENTRY) )) == NULL) return ERROR_NOT_ENOUGH_MEMORY; (*ppBoundary)->pScope = pScope; // Search for entry after the new one for (pleNode = pBoundaryIf->leBoundaryList.Flink; pleNode isnot &pBoundaryIf->leBoundaryList; pleNode = pleNode->Flink) { BOUNDARY_ENTRY *pPrevRange = CONTAINING_RECORD(pleNode, BOUNDARY_ENTRY, leBoundaryLink); IPV4_ADDRESS ipAddress = pPrevRange->pScope->ipGroupAddress; IPV4_ADDRESS ipMask = pPrevRange->pScope->ipGroupMask; if (ipAddress > pScope->ipGroupAddress || (ipAddress==pScope->ipGroupAddress && ipMask>pScope->ipGroupMask)) break; } InsertTailList( pleNode, &((*ppBoundary)->leBoundaryLink)); pScope->ulNumInterfaces++; return NO_ERROR; } DWORD AssertBoundaryEntry( BOUNDARY_IF *pBoundaryIf, SCOPE_ENTRY *pScope, PBOUNDARY_ENTRY *ppBoundary ) /*++ Called by: SetBoundaryInfo() Locks: Assumes caller holds a write lock on BOUNDARY_TABLE Returns: NO_ERROR on success ERROR_NOT_ENOUGH_MEMORY --*/ { if ((*ppBoundary = FindBoundaryEntry(pBoundaryIf, pScope)) != NULL) return NO_ERROR; return AddBoundaryEntry(pBoundaryIf, pScope, ppBoundary); } // // Functions to manipulate boundaries // VOID DeleteBoundaryFromInterface(pBoundary, pBoundaryIf) BOUNDARY_ENTRY *pBoundary; BOUNDARY_IF *pBoundaryIf; /*++ Called by: SetBoundaryInfo(), SNMPDeleteBoundaryFromInterface() --*/ { Trace3(MCAST, "DeleteBoundaryFromInterface: If %x Scope %d.%d.%d.%d/%d", pBoundaryIf->dwIfIndex, PRINT_IPADDR(pBoundary->pScope->ipGroupAddress), MaskToMaskLen(pBoundary->pScope->ipGroupMask) ); RemoveEntryList(&(pBoundary->leBoundaryLink)); pBoundary->pScope->ulNumInterfaces--; FREE(pBoundary); // // If there are no boundaries left, delete the pBoundaryIf. // if (IsListEmpty( &pBoundaryIf->leBoundaryList )) { // Remove the BoundaryIf MzapUninitBIf( pBoundaryIf ); RemoveEntryList( &(pBoundaryIf->leBoundaryIfLink)); RemoveEntryList( &(pBoundaryIf->leBoundaryIfMasterLink)); FREE(pBoundaryIf); } } // // Routines to process range information, which is what MGM deals with. // It's much more efficient to pass range deltas to MGM than to pass // prefixes, or original info, since overlapping boundaries might exist. // DWORD AssertRange( IN OUT PLIST_ENTRY pHead, IN IPV4_ADDRESS ipFirst, IN IPV4_ADDRESS ipLast ) /*++ Called by: ConvertIfTableToRanges() Locks: none --*/ { PLIST_ENTRY pleLast; RANGE_ENTRY *pRange; Trace2(MCAST, "AssertRange: (%d.%d.%d.%d - %d.%d.%d.%d)", PRINT_IPADDR(ipFirst), PRINT_IPADDR(ipLast)); // // Since we're calling this in order, the new // range may only overlap with the last range, if any. // pleLast = pHead->Blink; if (pleLast isnot pHead) { RANGE_ENTRY *pPrevRange = CONTAINING_RECORD(pleLast, RANGE_ENTRY, leRangeLink); // See if it aggregates if (ntohl(ipFirst) <= ntohl(pPrevRange->ipLast) + 1) { if (ntohl(ipLast) > ntohl(pPrevRange->ipLast)) pPrevRange->ipLast = ipLast; return NO_ERROR; } } // // Ok, no overlap, so add a new range // pRange = MALLOC( sizeof(RANGE_ENTRY) ); if (pRange == NULL) { return ERROR_NOT_ENOUGH_MEMORY; } pRange->ipFirst = ipFirst; pRange->ipLast = ipLast; InsertTailList(pHead, &pRange->leRangeLink); return NO_ERROR; } VOID ConvertIfTableToRanges( IN DWORD dwIfIndex, OUT PLIST_ENTRY pHead ) /*++ Routine Description: Go through the list of boundaries on a given interface, and compose an ordered list of non-overlapping ranges. Called by: ConvertTableToRanges() SetBoundaryInfo(), SNMPAddBoundaryToInterface(), SNMPDeleteBoundaryFromInterface() Locks: Assumes caller holds read lock on BOUNDARY_TABLE --*/ { PLIST_ENTRY pleNode; IPV4_ADDRESS ipLastAddress; BOUNDARY_IF *pBoundaryIf; BOUNDARY_ENTRY *pBoundary; Trace1( MCAST, "ENTERED ConvertIfTableToRanges: If=%x", dwIfIndex ); InitializeListHead(pHead); pBoundaryIf = FindBIfEntry(dwIfIndex); if (pBoundaryIf) { for (pleNode = pBoundaryIf->leBoundaryList.Flink; pleNode isnot &pBoundaryIf->leBoundaryList; pleNode = pleNode->Flink) { pBoundary = CONTAINING_RECORD(pleNode, BOUNDARY_ENTRY, leBoundaryLink); ipLastAddress = pBoundary->pScope->ipGroupAddress | ~pBoundary->pScope->ipGroupMask; AssertRange(pHead, pBoundary->pScope->ipGroupAddress, ipLastAddress); } // Finally, we also have one for the IPv4 Local Scope if ( !IsListEmpty( &pBoundaryIf->leBoundaryList ) ) { AssertRange(pHead, IPV4_LOCAL_SCOPE_ADDR, IPV4_LOCAL_SCOPE_ADDR | ~IPV4_LOCAL_SCOPE_MASK); } } Trace0( MCAST, "LEFT ConvertIfTableToRanges" ); } DWORD ConvertTableToRanges( OUT PLIST_ENTRY pIfHead ) /*++ Routine description: Calculate the list of blocked ranges on all interfaces. Locks: BOUNDARY_TABLE for reading --*/ { DWORD i, dwErr = NO_ERROR; PLIST_ENTRY pleNode; BOUNDARY_IF *pBoundaryIf, *pRangeIf; InitializeListHead(pIfHead); ENTER_READER(BOUNDARY_TABLE); { // For each interface with boundaries... for (i=0; iFlink) { pBoundaryIf = CONTAINING_RECORD(pleNode, BOUNDARY_IF, leBoundaryIfLink); // Add a node to the if range list pRangeIf = MALLOC( sizeof(BOUNDARY_IF) ); if (pRangeIf is NULL) { dwErr = ERROR_NOT_ENOUGH_MEMORY; break; } pRangeIf->dwIfIndex = pBoundaryIf->dwIfIndex; InsertTailList(pIfHead, &pRangeIf->leBoundaryIfLink); // Compose the range list for this interface ConvertIfTableToRanges(pBoundaryIf->dwIfIndex, &pRangeIf->leBoundaryList); } } } EXIT_LOCK(BOUNDARY_TABLE); return dwErr; } VOID GetRange( IN PLIST_ENTRY pleNode, IN PLIST_ENTRY pHead, OUT PRANGE_ENTRY *ppRange, OUT IPV4_ADDRESS *phipFirst, OUT IPV4_ADDRESS *phipLast ) { if (pleNode isnot pHead) { (*ppRange) = CONTAINING_RECORD(pleNode, RANGE_ENTRY, leRangeLink); *phipFirst = ntohl((*ppRange)->ipFirst); *phipLast = ntohl((*ppRange)->ipLast); } else { (*ppRange) = NULL; *phipFirst = *phipLast = 0xFFFFFFFF; } } VOID GetRangeIf( IN PLIST_ENTRY pleNode, IN PLIST_ENTRY pHead, OUT PBOUNDARY_IF *ppRangeIf, OUT ULONG *pulIfIndex ) { if (pleNode isnot pHead) { (*ppRangeIf) = CONTAINING_RECORD(pleNode, BOUNDARY_IF, leBoundaryIfLink); *pulIfIndex = (*ppRangeIf)->dwIfIndex; } else { (*ppRangeIf) = NULL; *pulIfIndex = 0xFFFFFFFF; } } VOID FreeRangeList( IN PLIST_ENTRY pHead ) /*++ Routine description: Free up space from a range list Called by: ProcessIfRangeDeltas() Locks: none --*/ { RANGE_ENTRY *pRange; PLIST_ENTRY pleNode; for (pleNode = pHead->Flink; pleNode isnot pHead; pleNode = pHead->Flink) { pRange = CONTAINING_RECORD(pleNode, RANGE_ENTRY, leRangeLink); RemoveEntryList(&pRange->leRangeLink); FREE(pRange); } } VOID FreeIfRangeLists( IN PLIST_ENTRY pHead ) /*++ Routine description: Free all entries in the list, as well as the list of ranges off each entry. Called by: ProcessRangeDeltas() Locks: none --*/ { BOUNDARY_IF *pRangeIf; PLIST_ENTRY pleNode; for (pleNode = pHead->Flink; pleNode isnot pHead; pleNode = pHead->Flink) { pRangeIf = CONTAINING_RECORD(pleNode, BOUNDARY_IF, leBoundaryIfLink); RemoveEntryList(&pRangeIf->leBoundaryIfLink); FreeRangeList(&pRangeIf->leBoundaryList); FREE(pRangeIf); } } // // Check if the interface // is the RAS Server Interface in which case, the // callback should be invoked for all clients connected // and the NEXT HOP address should be set to the client // address. Otherwise zero should be fine as NHOP // VOID BlockGroups( IN IPV4_ADDRESS ipFirst, IN IPV4_ADDRESS ipLast, IN DWORD dwIfIndex ) { IPV4_ADDRESS ipNextHop; PICB picb; PLIST_ENTRY pleNode; ENTER_READER(ICB_LIST); do { // Look up the type of this interface picb = InterfaceLookupByIfIndex(dwIfIndex); if (picb==NULL) break; // If interface is not an NBMA interface, just block on the interface // Currently, the only NBMA-like interface is the "internal" interface if (picb->ritType isnot ROUTER_IF_TYPE_INTERNAL) { Trace3( MCAST, "Blocking [%d.%d.%d.%d-%d.%d.%d.%d] on if %x", PRINT_IPADDR(ipFirst), PRINT_IPADDR(ipLast), dwIfIndex ); g_pfnMgmBlockGroups(ipFirst, ipLast, dwIfIndex, 0); break; } // For NBMA interfaces, need to block on each next hop // to enumerate all next hops on the internal interface, // we have to walk the PICB list looking for entries with // an ifIndex of -1. for (pleNode = ICBList.Flink; pleNode isnot &ICBList; pleNode = pleNode->Flink) { picb = CONTAINING_RECORD(pleNode, ICB, leIfLink); if (picb->ritType isnot ROUTER_IF_TYPE_CLIENT) continue; Trace4( MCAST, "Blocking [%d.%d.%d.%d-%d.%d.%d.%d] on if %x nh %d.%d.%d.%d", PRINT_IPADDR(ipFirst), PRINT_IPADDR(ipLast), dwIfIndex, PRINT_IPADDR(picb->dwRemoteAddress) ); g_pfnMgmBlockGroups( ipFirst, ipLast, dwIfIndex, picb->dwRemoteAddress ); } } while(0); EXIT_LOCK(ICB_LIST); } // // Check if the interface // is the RAS Server Interface in which case, the // callback should be invoked for all clients connected // and the NEXT HOP address should be set to the client // address. Otherwise zero should be fine as NHOP // VOID UnblockGroups( IN IPV4_ADDRESS ipFirst, IN IPV4_ADDRESS ipLast, IN DWORD dwIfIndex ) { IPV4_ADDRESS ipNextHop; PICB picb; PLIST_ENTRY pleNode; ENTER_READER(ICB_LIST); do { // Look up the type of this interface picb = InterfaceLookupByIfIndex(dwIfIndex); if (picb == NULL ) break; // If interface is not an NBMA interface, just block on the interface // Currently, the only NBMA-like interface is the "internal" interface if (picb->ritType isnot ROUTER_IF_TYPE_INTERNAL) { Trace3( MCAST, "Unblocking [%d.%d.%d.%d-%d.%d.%d.%d] on if %x", PRINT_IPADDR(ipFirst), PRINT_IPADDR(ipLast), dwIfIndex ); g_pfnMgmUnBlockGroups(ipFirst, ipLast, dwIfIndex, 0); break; } // For NBMA interfaces, need to block on each next hop // to enumerate all next hops on the internal interface, // we have to walk the PICB list looking for entries with // an ifIndex of -1. for (pleNode = ICBList.Flink; pleNode isnot &ICBList; pleNode = pleNode->Flink) { picb = CONTAINING_RECORD(pleNode, ICB, leIfLink); if (picb->ritType isnot ROUTER_IF_TYPE_CLIENT) continue; Trace4( MCAST, "Unblocking [%d.%d.%d.%d-%d.%d.%d.%d] on if %x nh %d.%d.%d.%d", PRINT_IPADDR(ipFirst), PRINT_IPADDR(ipLast), dwIfIndex, PRINT_IPADDR(picb->dwRemoteAddress) ); g_pfnMgmUnBlockGroups( ipFirst, ipLast, dwIfIndex, picb->dwRemoteAddress ); } } while(0); EXIT_LOCK(ICB_LIST); } VOID ProcessIfRangeDeltas( IN DWORD dwIfIndex, IN PLIST_ENTRY pOldHead, IN PLIST_ENTRY pNewHead ) /*++ Routine Description: Go through the previous and current lists of ranges, and inform MGM of any differences found. Called by: SetBoundaryInfo(), SNMPAddBoundaryToInterface(), SNMPDeleteBoundaryFromInterface() Locks: none --*/ { PLIST_ENTRY pleOld = pOldHead->Flink, pleNew = pNewHead->Flink; RANGE_ENTRY *pOld, *pNew; IPV4_ADDRESS hipOldFirst, hipOldLast, hipNewFirst, hipNewLast; IPV4_ADDRESS hipLast; // Get ranges in host order fields GetRange(pleOld, pOldHead, &pOld, &hipOldFirst, &hipOldLast); GetRange(pleNew, pNewHead, &pNew, &hipNewFirst, &hipNewLast); // Loop until we hit the end of both lists while (pOld || pNew) { // See if there's a new range to block if (pNew && hipNewFirst < hipOldFirst) { hipLast = MIN(hipNewLast, hipOldFirst-1); BlockGroups(pNew->ipFirst, htonl(hipLast), dwIfIndex); hipNewFirst = hipOldFirst; pNew->ipFirst = htonl(hipNewFirst); if (hipNewFirst > hipNewLast) { // advance new pleNew = pleNew->Flink; GetRange(pleNew, pNewHead, &pNew, &hipNewFirst, &hipNewLast); } } // See if there's an old range to unblock if (pOld && hipOldFirst < hipNewFirst) { hipLast = MIN(hipOldLast, hipNewFirst-1); UnblockGroups(pOld->ipFirst, htonl(hipLast), dwIfIndex); hipOldFirst = hipNewFirst; pOld->ipFirst = htonl(hipOldFirst); if (hipOldFirst > hipOldLast) { // advance old pleOld = pleOld->Flink; GetRange(pleOld, pOldHead, &pOld, &hipOldFirst, &hipOldLast); } } // See if there's an unchanged range to skip if (pOld && pNew && hipOldFirst == hipNewFirst) { hipLast = MIN(hipOldLast, hipNewLast); hipOldFirst = hipLast+1; pOld->ipFirst = htonl(hipOldFirst); if (hipOldFirst > hipOldLast) { // advance old pleOld = pleOld->Flink; GetRange(pleOld, pOldHead, &pOld, &hipOldFirst, &hipOldLast); } hipNewFirst = hipLast+1; pNew->ipFirst = htonl(hipNewFirst); if (hipNewFirst > hipNewLast) { // advance new pleNew = pleNew->Flink; GetRange(pleNew, pNewHead, &pNew, &hipNewFirst, &hipNewLast); } } } FreeRangeList(pOldHead); FreeRangeList(pNewHead); } VOID ProcessRangeDeltas( IN PLIST_ENTRY pOldIfHead, IN PLIST_ENTRY pNewIfHead ) { PLIST_ENTRY pleOldIf = pOldIfHead->Flink, pleNewIf = pNewIfHead->Flink; BOUNDARY_IF *pOldIf, *pNewIf; ULONG ulOldIfIndex, ulNewIfIndex; LIST_ENTRY emptyList; GetRangeIf(pleOldIf, pOldIfHead, &pOldIf, &ulOldIfIndex); GetRangeIf(pleNewIf, pNewIfHead, &pNewIf, &ulNewIfIndex); InitializeListHead(&emptyList); // Loop until we hit the end of both lists while (pOldIf || pNewIf) { // See if there's a new interface without old boundaries if (pNewIf && ulNewIfIndex < ulOldIfIndex) { // process it ProcessIfRangeDeltas(ulNewIfIndex, &emptyList, &pNewIf->leBoundaryList); // advance new pleNewIf = pleNewIf->Flink; GetRangeIf(pleNewIf, pNewIfHead, &pNewIf, &ulNewIfIndex); } // See if there's an old interface without new boundaries if (pOldIf && ulOldIfIndex < ulNewIfIndex) { // process it ProcessIfRangeDeltas(ulOldIfIndex, &pOldIf->leBoundaryList, &emptyList); // advance old pleOldIf = pleOldIf->Flink; GetRangeIf(pleOldIf, pOldIfHead, &pOldIf, &ulOldIfIndex); } // See if there's an ifindex to change if (pOldIf && pNewIf && ulOldIfIndex == ulNewIfIndex) { // process it ProcessIfRangeDeltas(ulOldIfIndex, &pOldIf->leBoundaryList, &pNewIf->leBoundaryList); // advance old pleOldIf = pleOldIf->Flink; GetRangeIf(pleOldIf, pOldIfHead, &pOldIf, &ulOldIfIndex); // advance new pleNewIf = pleNewIf->Flink; GetRangeIf(pleNewIf, pNewIfHead, &pNewIf, &ulNewIfIndex); } } FreeIfRangeLists(pOldIfHead); FreeIfRangeLists(pNewIfHead); } VOID ParseScopeInfo( IN PBYTE pBuffer, IN ULONG ulNumScopes, OUT PSCOPE_ENTRY *ppScopes ) /*++ Description: Routines to parse registry info into a pre-allocated array. Space for names will be dynamically allocated by this function, and it is the caller's responsibility to free them. Called by: SetScopeInfo() --*/ { DWORD i, j, dwLen, dwNumNames, dwLanguage, dwFlags; SCOPE_NAME_BUFFER pScopeName; PSCOPE_ENTRY pScopes; *ppScopes = pScopes = MALLOC( ulNumScopes * sizeof(SCOPE_ENTRY) ); for (i=0; i MAX_SCOPE_NAME_LEN) { Trace2(MCAST, "ERROR %d-char scope name in registry, truncated to %d", dwLen, MAX_SCOPE_NAME_LEN); dwLen = MAX_SCOPE_NAME_LEN; } // Set scope name wcsncpy(pScopeName, (SCOPE_NAME)pBuffer, dwLen); pScopeName[ dwLen ] = '\0'; pBuffer += dwLen * SNCHARSIZE; AssertScopeName( &pScopes[i], (LANGID)dwLanguage, pScopeName ); } } } VOID FreeScopeInfo( PSCOPE_ENTRY pScopes, DWORD dwNumScopes ) { PLIST_ENTRY pleNode; DWORD i; for (i=0; iInfoSize is 0) { StopMZAP(); // delete all scopes ENTER_WRITER(BOUNDARY_TABLE); { for (i=0; ibDivisible = pScopes[i].bDivisible; for (pleNode = pScopes[i].leNameList.Flink; pleNode isnot &pScopes[i].leNameList; pleNode = pleNode->Flink) { pName = CONTAINING_RECORD(pleNode, SCOPE_NAME_ENTRY, leNameLink); AssertScopeName( pScope, pName->idLanguage, pName->snScopeName ); } } // // Now enumerate the scopes, deleting the scopes that are not in the // new list. // for (i=0; iipGroupAddress == 0) continue; // not active bFound = FALSE; for (j=0; jipGroupAddress && pScopes[j].ipGroupMask == pScope->ipGroupMask ) { bFound = TRUE; break; } } if (!bFound) DeleteScope(pScope); } } EXIT_LOCK(BOUNDARY_TABLE); // Free scopes and names FreeScopeInfo(pScopes, dwNumScopes); dwResult = ConvertTableToRanges(&leNewIfRanges); if (dwResult isnot NO_ERROR) { return dwResult; } ProcessRangeDeltas(&leOldIfRanges, &leNewIfRanges); Trace0( MCAST, "LEFT SetScopeInfo" ); return NO_ERROR; } DWORD GetScopeInfo( IN OUT PRTR_TOC_ENTRY pToc, IN OUT PDWORD pdwTocIndex, IN OUT PBYTE pBuffer, IN PRTR_INFO_BLOCK_HEADER pInfoHdr, IN OUT PDWORD pdwBufferSize ) /*++ Routine Description: Called to get a copy of the scope information to write into the registry. Locks: BOUNDARY_TABLE for reading Arguments: pToc Space to fill in the TOC entry (may be NULL) pdwTocIndex Pointer to TOC index to be incremented if TOC written pBuffer Pointer to buffer into which info is to be written pInfoHdr Pointer to info block header for offset computation pdwBufferSize [IN] Size of the buffer pointed to by pBuffer [OUT] Size of data copied out, or size of buffer needed Called by: GetGlobalConfiguration() in info.c Return Value: NO_ERROR Buffer of size *pdwBufferSize was copied out ERROR_INSUFFICIENT_BUFFER The buffer was too small to copy out the info The size of buffer needed is in *pdwBufferSize --*/ { DWORD i, dwSizeReqd, dwNumScopes, dwLen, dwNumNames, dwLanguage, dwFlags; PLIST_ENTRY pleNode, pleNode2; PSCOPE_ENTRY pScope; PSCOPE_NAME_ENTRY pName; dwSizeReqd = sizeof(DWORD); dwNumScopes = 0; ENTER_READER(BOUNDARY_TABLE); { // // Calculate size required // for (pleNode = g_MasterScopeList.Flink; pleNode isnot &g_MasterScopeList; pleNode = pleNode->Flink) { pScope = CONTAINING_RECORD(pleNode, SCOPE_ENTRY, leScopeLink); if ( !pScope->ipGroupAddress ) continue; // not active dwSizeReqd += 2*sizeof(IPV4_ADDRESS) + 2*sizeof(DWORD); for (pleNode2 = pScope->leNameList.Flink; pleNode2 isnot &pScope->leNameList; pleNode2 = pleNode2->Flink) { pName = CONTAINING_RECORD( pleNode2, SCOPE_NAME_ENTRY, leNameLink ); dwSizeReqd += (DWORD)(2 * sizeof(DWORD) + sn_strlen(pName->snScopeName) * SNCHARSIZE); } dwNumScopes++; } if (dwNumScopes) { dwSizeReqd += sizeof(DWORD); // space for scope count } // // Increment TOC index by number of TOC entries needed // if (pdwTocIndex && dwSizeReqd>0) (*pdwTocIndex)++; if (dwSizeReqd > *pdwBufferSize) { *pdwBufferSize = dwSizeReqd; EXIT_LOCK(BOUNDARY_TABLE); return ERROR_INSUFFICIENT_BUFFER; } *pdwBufferSize = dwSizeReqd; if (pToc) { //pToc->InfoVersion = IP_MCAST_BOUNDARY_INFO; pToc->InfoType = IP_MCAST_BOUNDARY_INFO; pToc->Count = 1; // single variable-sized opaque block pToc->InfoSize = dwSizeReqd; pToc->Offset = (ULONG)(pBuffer - (PBYTE) pInfoHdr); } if (pBuffer) { // // Add scope count // CopyMemory(pBuffer, &dwNumScopes, sizeof(DWORD)); pBuffer += sizeof(DWORD); // // Go through and get each scope // for (pleNode = g_MasterScopeList.Flink; pleNode isnot &g_MasterScopeList; pleNode = pleNode->Flink) { pScope = CONTAINING_RECORD(pleNode, SCOPE_ENTRY, leScopeLink); if ( !pScope->ipGroupAddress ) continue; // not active // Copy scope address, and mask dwLen = 2 * sizeof(IPV4_ADDRESS); CopyMemory(pBuffer, &pScope->ipGroupAddress, dwLen); pBuffer += dwLen; // Copy flags dwFlags = pScope->bDivisible; CopyMemory(pBuffer, &dwFlags, sizeof(dwFlags)); pBuffer += sizeof(dwFlags); // Copy # of names CopyMemory(pBuffer, &pScope->ulNumNames, sizeof(DWORD)); pBuffer += sizeof(DWORD); for (pleNode2 = pScope->leNameList.Flink; pleNode2 isnot &pScope->leNameList; pleNode2 = pleNode2->Flink) { pName = CONTAINING_RECORD( pleNode2, SCOPE_NAME_ENTRY, leNameLink ); // Save language dwLanguage = pName->idLanguage; CopyMemory(pBuffer, &dwLanguage, sizeof(dwLanguage)); pBuffer += sizeof(dwLanguage); // Copy scope name (save length in words) dwLen = sn_strlen(pName->snScopeName); CopyMemory(pBuffer, &dwLen, sizeof(DWORD)); pBuffer += sizeof(DWORD); dwLen *= SNCHARSIZE; CopyMemory(pBuffer, pName->snScopeName, dwLen); pBuffer += dwLen; } } } } EXIT_LOCK(BOUNDARY_TABLE); return NO_ERROR; } DWORD SetBoundaryInfo( PICB picb, PRTR_INFO_BLOCK_HEADER pInfoHdr ) /*++ Routine Description: Sets the boundary info associated with an interface. First we add the boundaries present in the boundary info. Then we enumerate the boundaries and delete those that we don't find in the boundary info. Arguments: picb The ICB of the interface Called by: AddInterface() in iprtrmgr.c SetInterfaceInfo() in iprtrmgr.c Locks: BOUNDARY_TABLE for writing --*/ { DWORD dwResult = NO_ERROR, dwNumBoundaries, i, j; PRTR_TOC_ENTRY pToc; PMIB_BOUNDARYROW pBoundaries; BOOL bFound; BOUNDARY_ENTRY *pBoundary; SCOPE_ENTRY *pScope; LIST_ENTRY leOldRanges, leNewRanges, *pleNode, *pleNext; BOUNDARY_IF *pBoundaryIf; Trace1( MCAST, "ENTERED SetBoundaryInfo for If %x", picb->dwIfIndex ); pToc = GetPointerToTocEntry(IP_MCAST_BOUNDARY_INFO, pInfoHdr); if (pToc is NULL) { // No TOC means no change Trace0( MCAST, "LEFT SetBoundaryInfo" ); return NO_ERROR; } dwNumBoundaries = pToc->Count; ENTER_WRITER(BOUNDARY_TABLE); { // // This call wouldn't be needed if we saved this info in the // BOUNDARY_IF structure, but since it should rarely, if ever, // change, we won't worry about it for now. // ConvertIfTableToRanges(picb->dwIfIndex, &leOldRanges); if (pToc->InfoSize is 0) { // Delete all boundaries on this interface pBoundaryIf = FindBIfEntry(picb->dwIfIndex); if (pBoundaryIf) { for (pleNode = pBoundaryIf->leBoundaryList.Flink; pleNode isnot &pBoundaryIf->leBoundaryList; pleNode = pBoundaryIf->leBoundaryList.Flink) { pBoundary = CONTAINING_RECORD(pleNode, BOUNDARY_ENTRY, leBoundaryLink); DeleteBoundaryFromInterface(pBoundary, pBoundaryIf); } } } else { pBoundaries = (PMIB_BOUNDARYROW)GetInfoFromTocEntry(pInfoHdr, pToc); dwResult = AssertBIfEntry(picb->dwIfIndex, &pBoundaryIf, (picb->dwOperationalState is IF_OPER_STATUS_OPERATIONAL)); // Add all the new boundaries for (i=0; ileBoundaryList.Flink; pleNode isnot &pBoundaryIf->leBoundaryList; pleNode = pleNext) { pleNext = pleNode->Flink; pBoundary = CONTAINING_RECORD(pleNode, BOUNDARY_ENTRY, leBoundaryLink); pScope = pBoundary->pScope; bFound = FALSE; for (j=0; jipGroupAddress && pBoundaries[j].dwGroupMask == pScope->ipGroupMask ) { bFound = TRUE; break; } } if (!bFound) DeleteBoundaryFromInterface(pBoundary, pBoundaryIf); } } ConvertIfTableToRanges(picb->dwIfIndex, &leNewRanges); } EXIT_LOCK(BOUNDARY_TABLE); // Inform MGM of deltas ProcessIfRangeDeltas(picb->dwIfIndex, &leOldRanges, &leNewRanges); StartMZAP(); Trace0( MCAST, "LEFT SetBoundaryInfo" ); return NO_ERROR; } DWORD GetMcastLimitInfo( IN PICB picb, OUT PRTR_TOC_ENTRY pToc, IN OUT PDWORD pdwTocIndex, OUT PBYTE pBuffer, IN PRTR_INFO_BLOCK_HEADER pInfoHdr, IN OUT PDWORD pdwBufferSize ) /*++ Routine Description: Called to get a copy of the limit information to write into the registry. Arguments: picb Interface entry pToc Space to fill in the TOC entry (may be NULL) pdwTocIndex Pointer to TOC index to be incremented if TOC written pBuffer Pointer to buffer into which info is to be written pInfoHdr Pointer to info block header for offset computation pdwBufferSize [IN] Size of the buffer pointed to by pBuffer [OUT] Size of data copied out, or size of buffer needed Called by: GetInterfaceConfiguration() in info.c Return Value: NO_ERROR Buffer of size *pdwBufferSize was copied out ERROR_INSUFFICIENT_BUFFER The buffer was too small to copy out the info The size of buffer needed is in *pdwBufferSize --*/ { DWORD i, dwLen, dwSizeReqd, dwNumBoundaries; PLIST_ENTRY pleNode; PMIB_MCAST_LIMIT_ROW pLimit; dwSizeReqd = 0; dwNumBoundaries = 0; if (picb->dwMcastTtl < 2 and picb->dwMcastRateLimit is 0) { // No block needed, since values are default *pdwBufferSize = 0; return NO_ERROR; } if (pdwTocIndex) (*pdwTocIndex)++; if (*pdwBufferSize < sizeof (MIB_MCAST_LIMIT_ROW)) { *pdwBufferSize = sizeof(MIB_MCAST_LIMIT_ROW); return ERROR_INSUFFICIENT_BUFFER; } if (pToc) { //pToc->InfoVersion = IP_MCAST_BOUNDARY_INFO; pToc->InfoSize = sizeof(MIB_MCAST_LIMIT_ROW); pToc->InfoType = IP_MCAST_LIMIT_INFO; pToc->Count = 1; pToc->Offset = (DWORD)(pBuffer - (PBYTE) pInfoHdr); } *pdwBufferSize = sizeof(MIB_MCAST_LIMIT_ROW); pLimit = (PMIB_MCAST_LIMIT_ROW)pBuffer; pLimit->dwTtl = picb->dwMcastTtl; pLimit->dwRateLimit = picb->dwMcastRateLimit; return NO_ERROR; } DWORD GetBoundaryInfo( IN PICB picb, OUT PRTR_TOC_ENTRY pToc, IN OUT PDWORD pdwTocIndex, OUT PBYTE pBuffer, IN PRTR_INFO_BLOCK_HEADER pInfoHdr, IN OUT PDWORD pdwBufferSize ) /*++ Routine Description: Called to get a copy of the boundary information to write into the registry. Locks: BOUNDARY_TABLE for reading Arguments: picb Interface entry pToc Space to fill in the TOC entry (may be NULL) pdwTocIndex Pointer to TOC index to be incremented if TOC written pBuffer Pointer to buffer into which info is to be written pInfoHdr Pointer to info block header for offset computation pdwBufferSize [IN] Size of the buffer pointed to by pBuffer [OUT] Size of data copied out, or size of buffer needed Called by: GetInterfaceConfiguration() in info.c Return Value: NO_ERROR Buffer of size *pdwBufferSize was copied out ERROR_INSUFFICIENT_BUFFER The buffer was too small to copy out the info The size of buffer needed is in *pdwBufferSize --*/ { DWORD i, dwLen, dwSizeReqd, dwNumBoundaries; PLIST_ENTRY pleNode; BOUNDARY_ENTRY *pBoundary; MIB_BOUNDARYROW BoundaryRow; BOUNDARY_IF *pIf; dwSizeReqd = 0; dwNumBoundaries = 0; ENTER_READER(BOUNDARY_TABLE); { pIf = FindBIfEntry(picb->dwIfIndex); if (!pIf) { *pdwBufferSize = 0; EXIT_LOCK(BOUNDARY_TABLE); return NO_ERROR; } // // Calculate size required. We could have stored the count // in the boundary entry, but we expect a pretty small number // of boundaries (1 or 2) so use brute force for now. // for (pleNode = pIf->leBoundaryList.Flink; pleNode isnot &pIf->leBoundaryList; pleNode = pleNode->Flink) { dwNumBoundaries++; } dwSizeReqd += dwNumBoundaries * sizeof(MIB_BOUNDARYROW); // // Increment TOC index by number of TOC entries needed // if (pdwTocIndex && dwSizeReqd>0) (*pdwTocIndex)++; if (dwSizeReqd > *pdwBufferSize) { *pdwBufferSize = dwSizeReqd; EXIT_LOCK(BOUNDARY_TABLE); return ERROR_INSUFFICIENT_BUFFER; } *pdwBufferSize = dwSizeReqd; if (pToc) { //pToc->InfoVersion = sizeof(MIB_BOUNDARYROW); pToc->InfoSize = sizeof(MIB_BOUNDARYROW); pToc->InfoType = IP_MCAST_BOUNDARY_INFO; pToc->Count = dwNumBoundaries; pToc->Offset = (DWORD)(pBuffer - (PBYTE) pInfoHdr); } // Go through and copy each boundary for (pleNode = pIf->leBoundaryList.Flink; pleNode isnot &pIf->leBoundaryList; pleNode = pleNode->Flink) { pBoundary = CONTAINING_RECORD(pleNode, BOUNDARY_ENTRY, leBoundaryLink); BoundaryRow.dwGroupAddress = pBoundary->pScope->ipGroupAddress; BoundaryRow.dwGroupMask = pBoundary->pScope->ipGroupMask; CopyMemory(pBuffer, &BoundaryRow, sizeof(MIB_BOUNDARYROW)); pBuffer += sizeof(MIB_BOUNDARYROW); } } EXIT_LOCK(BOUNDARY_TABLE); return NO_ERROR; } // // Functions used by SNMP // DWORD SNMPDeleteScope( IN IPV4_ADDRESS ipGroupAddress, IN IPV4_ADDRESS ipGroupMask ) /*++ Called by: Locks: BOUNDARY_TABLE for writing. ICB_LIST and then PROTOCOL_CB_LIST for writing (for saving to registry). Returns: ERROR_INVALID_PARAMETER if trying to delete the local scope whatever DeleteScope() returns whatever ProcessSaveGlobalConfigInfo() returns --*/ { DWORD dwErr = NO_ERROR; PSCOPE_ENTRY pScope; BOOL bChanged = FALSE; if ( IN_IPV4_LOCAL_SCOPE(ipGroupAddress) ) { return ERROR_INVALID_PARAMETER; } ENTER_WRITER(BOUNDARY_TABLE); { pScope = FindScope( ipGroupAddress, ipGroupMask ); if (pScope) { dwErr = DeleteScope( pScope ); bChanged = TRUE; } } EXIT_LOCK(BOUNDARY_TABLE); // Resave the scopes to the registry if (dwErr is NO_ERROR && bChanged) { // ProcessSaveGlobalConfigInfo() requires us to have both the // ICB_LIST and the PROTOCOL_CB_LIST locked. ENTER_WRITER(ICB_LIST); ENTER_WRITER(PROTOCOL_CB_LIST); dwErr = ProcessSaveGlobalConfigInfo(); EXIT_LOCK(PROTOCOL_CB_LIST); EXIT_LOCK(ICB_LIST); } return dwErr; } DWORD SNMPSetScope( IN IPV4_ADDRESS ipGroupAddress, IN IPV4_ADDRESS ipGroupMask, IN SCOPE_NAME snScopeName ) /*++ Called by: AccessMcastScope() in access.c Locks: Locks BOUNDARY_TABLE for writing Locks ICB_LIST then PROTOCOL_CB_LIST for writing (for saving to registry) Returns: whatever ProcessSaveGlobalConfigInfo() returns --*/ { DWORD dwErr; PSCOPE_ENTRY pScope; LANGID idLanguage = MAKELANGID(LANG_NEUTRAL, SUBLANG_SYS_DEFAULT); ENTER_WRITER(BOUNDARY_TABLE); { pScope = FindScope( ipGroupAddress, ipGroupMask ); if ( ! pScope ) { dwErr = ERROR_INVALID_PARAMETER; } else { dwErr = AssertScopeName( pScope, idLanguage, snScopeName ); } } EXIT_LOCK(BOUNDARY_TABLE); // Save the scope to the registry if (dwErr is NO_ERROR) { // ProcessSaveGlobalConfigInfo() requires us to have both the // ICB_LIST and the PROTOCOL_CB_LIST locked. ENTER_WRITER(ICB_LIST); ENTER_WRITER(PROTOCOL_CB_LIST); dwErr = ProcessSaveGlobalConfigInfo(); EXIT_LOCK(PROTOCOL_CB_LIST); EXIT_LOCK(ICB_LIST); } return dwErr; } DWORD SNMPAddScope( IN IPV4_ADDRESS ipGroupAddress, IN IPV4_ADDRESS ipGroupMask, IN SCOPE_NAME snScopeName, OUT PSCOPE_ENTRY *ppScope ) /*++ Called by: AccessMcastScope() in access.c Locks: Locks BOUNDARY_TABLE for writing Locks ICB_LIST then PROTOCOL_CB_LIST for writing (for saving to registry) Returns: ERROR_INVALID_PARAMATER if already exists whatever AddScope() returns whatever ProcessSaveGlobalConfigInfo() returns --*/ { DWORD dwErr; PSCOPE_ENTRY pScope; LANGID idLanguage = MAKELANGID(LANG_NEUTRAL, SUBLANG_SYS_DEFAULT); ENTER_WRITER(BOUNDARY_TABLE); { pScope = FindScope( ipGroupAddress, ipGroupMask ); if ( pScope ) { dwErr = ERROR_INVALID_PARAMETER; } else { dwErr = AddScope( ipGroupAddress, ipGroupMask, ppScope ); if (dwErr is NO_ERROR) dwErr = AssertScopeName( *ppScope, idLanguage, snScopeName ); } } EXIT_LOCK(BOUNDARY_TABLE); // Save the scope to the registry if (dwErr is NO_ERROR) { // ProcessSaveGlobalConfigInfo() requires us to have both the // ICB_LIST and the PROTOCOL_CB_LIST locked. ENTER_WRITER(ICB_LIST); ENTER_WRITER(PROTOCOL_CB_LIST); dwErr = ProcessSaveGlobalConfigInfo(); EXIT_LOCK(PROTOCOL_CB_LIST); EXIT_LOCK(ICB_LIST); } return dwErr; } DWORD SNMPAssertScope( IN IPV4_ADDRESS ipGroupAddress, IN IPV4_ADDRESS ipGroupMask, IN PBYTE pScopeName, // string to duplicate OUT PSCOPE_ENTRY *ppScopeEntry, OUT PBOOL pbSaveGlobal ) /*++ Locks: Assumes caller holds write lock on BOUNDARY_TABLE. Called by: SNMPAddBoundaryToInterface() Returns: NO_ERROR - success whatever AddScope() returns --*/ { DWORD dwErr = NO_ERROR; SCOPE_NAME_BUFFER snScopeNameBuffer; LANGID idLanguage; if (pScopeName) { idLanguage = MAKELANGID( LANG_NEUTRAL, SUBLANG_SYS_DEFAULT ); MultiByteToWideChar( CP_UTF8, 0, pScopeName, strlen(pScopeName), snScopeNameBuffer, MAX_SCOPE_NAME_LEN+1 ); } *ppScopeEntry = FindScope(ipGroupAddress, ipGroupMask); if (! *ppScopeEntry) { dwErr = AddScope( ipGroupAddress, ipGroupMask, ppScopeEntry); if (pScopeName and (dwErr is NO_ERROR)) { dwErr = AssertScopeName( *ppScopeEntry, idLanguage, snScopeNameBuffer ); } *pbSaveGlobal = TRUE; } return dwErr; } DWORD SNMPAddBoundaryToInterface( IN DWORD dwIfIndex, IN IPV4_ADDRESS ipGroupAddress, IN IPV4_ADDRESS ipGroupMask ) /*++ Routine Description: Create a boundary if necessary, and add it to a given interface and to the registry. Called by: AccessMcastBoundary() in access.c Locks: BOUNDARY_TABLE for writing ICB_LIST and then PROTOCOL_CB_LIST for writing Returns: NO_ERROR whatever AssertScope() returns whatever AssertBifEntry() returns whatever ProcessSaveInterfaceConfigInfo() --*/ { DWORD dwResult; LIST_ENTRY leOldRanges, leNewRanges; BOOL bSaveGlobal = FALSE, bIsOperational = TRUE; BOUNDARY_ENTRY *pBoundary; BOUNDARY_IF *pBIf; SCOPE_ENTRY *pScope; // // bIsOperational should really be set to TRUE only if // picb->dwOperationalState is IF_OPER_STATUS_OPERATIONAL // // Add the boundary ENTER_WRITER(BOUNDARY_TABLE); { Trace0( MCAST, "SNMPAddBoundaryToInterface: converting old ranges" ); ConvertIfTableToRanges(dwIfIndex, &leOldRanges); dwResult = SNMPAssertScope(ipGroupAddress, ipGroupMask, NULL, &pScope, &bSaveGlobal); if (dwResult == NO_ERROR) { dwResult = AssertBIfEntry(dwIfIndex, &pBIf, bIsOperational); if (dwResult is NO_ERROR) { AssertBoundaryEntry(pBIf, pScope, &pBoundary); } } if (dwResult isnot NO_ERROR) { EXIT_LOCK(BOUNDARY_TABLE); return dwResult; } Trace0( MCAST, "SNMPAddBoundaryToInterface: converting new ranges" ); ConvertIfTableToRanges(dwIfIndex, &leNewRanges); } EXIT_LOCK(BOUNDARY_TABLE); // Inform MGM of deltas ProcessIfRangeDeltas(dwIfIndex, &leOldRanges, &leNewRanges); // Save the boundary to the registry { // ProcessSaveInterfaceConfigInfo() requires us to have both the // ICB_LIST and the PROTOCOL_CB_LIST locked. ENTER_WRITER(ICB_LIST); ENTER_WRITER(PROTOCOL_CB_LIST); if (bSaveGlobal) dwResult = ProcessSaveGlobalConfigInfo(); dwResult = ProcessSaveInterfaceConfigInfo(dwIfIndex); EXIT_LOCK(PROTOCOL_CB_LIST); EXIT_LOCK(ICB_LIST); } return dwResult; } DWORD SNMPDeleteBoundaryFromInterface( IN DWORD dwIfIndex, IN IPV4_ADDRESS ipGroupAddress, IN IPV4_ADDRESS ipGroupMask ) /*++ Routine Description: Remove a boundary from a given interface, and delete the scope entry if it's unnamed and no interfaces remain. Called by: AccessMcastBoundary() in access.c Locks: BOUNDARY_TABLE for writing Returns: NO_ERROR --*/ { LIST_ENTRY leOldRanges, leNewRanges, *pleNode, *pleNext; DWORD dwResult = NO_ERROR; BOOL bSaveGlobal = FALSE; BOUNDARY_IF *pBIf; BOUNDARY_ENTRY *pBoundary; SCOPE_ENTRY *pScope; ENTER_WRITER(BOUNDARY_TABLE); { Trace0( MCAST, "SNMPDeleteBoundaryFromInterface: converting old ranges" ); ConvertIfTableToRanges(dwIfIndex, &leOldRanges); // // We have to do a little more work than just calling // DeleteBoundaryFromInterface(), since we first have to // look up which boundary matches. // pBIf = FindBIfEntry(dwIfIndex); if (pBIf is NULL) { // nothing to do FreeRangeList(&leOldRanges); EXIT_LOCK(BOUNDARY_TABLE); return NO_ERROR; } for (pleNode = pBIf->leBoundaryList.Flink; pleNode isnot &pBIf->leBoundaryList; pleNode = pleNext) { // Save ptr to next node, since we may delete this one pleNext = pleNode->Flink; pBoundary = CONTAINING_RECORD(pleNode, BOUNDARY_ENTRY, leBoundaryLink); pScope = pBoundary->pScope; if (pScope->ipGroupAddress == ipGroupAddress && pScope->ipGroupMask == ipGroupMask) { // Delete boundary from interface DeleteBoundaryFromInterface(pBoundary, pBIf); if (!pScope->ulNumInterfaces && IsListEmpty(&pScope->leNameList)) { DeleteScope(pScope); bSaveGlobal = TRUE; } } } Trace0( MCAST, "SNMPDeleteBoundaryFromInterface: converting new ranges" ); ConvertIfTableToRanges(dwIfIndex, &leNewRanges); } EXIT_LOCK(BOUNDARY_TABLE); // Inform MGM of deltas ProcessIfRangeDeltas(dwIfIndex, &leOldRanges, &leNewRanges); // Resave boundaries to registry { // ProcessSaveInterfaceConfigInfo() requires us to have both the // ICB_LIST and the PROTOCOL_CB_LIST locked. ENTER_WRITER(ICB_LIST); ENTER_WRITER(PROTOCOL_CB_LIST); if (bSaveGlobal) dwResult = ProcessSaveGlobalConfigInfo(); dwResult = ProcessSaveInterfaceConfigInfo(dwIfIndex); EXIT_LOCK(PROTOCOL_CB_LIST); EXIT_LOCK(ICB_LIST); } return dwResult; } // // Functions which can be called from MGM and Routing Protocols // BOOL WINAPI RmHasBoundary( IN DWORD dwIfIndex, IN IPV4_ADDRESS ipGroupAddress ) /*++ Routine Description: Test to see whether a boundary for the given group exists on the indicated interface. Called by: (MGM, Routing Protocols) Locks: BOUNDARY_TABLE for reading Returns: TRUE, if a boundary exists FALSE, if not --*/ { BOUNDARY_IF *pIf; BOUNDARY_ENTRY *pBoundary; PLIST_ENTRY pleNode; BOOL bFound = FALSE; ENTER_READER(BOUNDARY_TABLE); { pIf = FindBIfEntry(dwIfIndex); if (pIf) { // An address in the IPv4 Local Scope has a boundary if // ANY boundary exists. if ( !IsListEmpty( &pIf->leBoundaryList ) && IN_IPV4_LOCAL_SCOPE(ipGroupAddress) ) bFound = TRUE; for (pleNode = pIf->leBoundaryList.Flink; !bFound && pleNode isnot &pIf->leBoundaryList; pleNode = pleNode->Flink) { pBoundary = CONTAINING_RECORD(pleNode, BOUNDARY_ENTRY, leBoundaryLink); if ((ipGroupAddress & pBoundary->pScope->ipGroupMask) == pBoundary->pScope->ipGroupAddress) bFound = TRUE; } } } EXIT_LOCK(BOUNDARY_TABLE); return bFound; } //---------------------------------------------------------------------------- // Boundary enumeration API. // //---------------------------------------------------------------------------- DWORD RmGetBoundary( IN PMIB_IPMCAST_BOUNDARY pimm, IN OUT PDWORD pdwBufferSize, IN OUT PBYTE pbBuffer ) /*++ Called by: AccessMcastBoundary() in access.c Returns: SNMP error code --*/ { DWORD dwErr = NO_ERROR; BOUNDARY_IF *pBIf; BOUNDARY_ENTRY *pBoundary; SCOPE_ENTRY *pScope; PMIB_IPMCAST_BOUNDARY *pOut; Trace1( ENTER, "ENTERED RmGetBoundary: %d", *pdwBufferSize ); if (*pdwBufferSize < sizeof(MIB_IPMCAST_BOUNDARY)) { *pdwBufferSize = sizeof(MIB_IPMCAST_BOUNDARY); return ERROR_INSUFFICIENT_BUFFER; } do { ENTER_READER(BOUNDARY_TABLE); if ((pBIf = FindBIfEntry(pimm->dwIfIndex)) == NULL) { dwErr = ERROR_NOT_FOUND; break; } if ( IN_IPV4_LOCAL_SCOPE(pimm->dwGroupAddress) ) { dwErr = ERROR_NOT_FOUND; break; } else { pScope = FindScope(pimm->dwGroupAddress, pimm->dwGroupMask); if (pScope == NULL) { dwErr = ERROR_NOT_FOUND; break; } if ((pBoundary = FindBoundaryEntry(pBIf, pScope)) == NULL) { dwErr = ERROR_NOT_FOUND; break; } } // Ok, we found it. pimm->dwStatus = ROWSTATUS_ACTIVE; CopyMemory(pbBuffer, pimm, sizeof(MIB_IPMCAST_BOUNDARY)); } while(0); EXIT_LOCK(BOUNDARY_TABLE); Trace1( ENTER, "LEAVING RmGetBoundary %x\n", dwErr ); return dwErr; } //---------------------------------------------------------------------------- // SCOPE enumeration API. // //---------------------------------------------------------------------------- DWORD AddNextScope( IN IPV4_ADDRESS ipAddr, IN IPV4_ADDRESS ipMask, IN SCOPE_NAME snScopeName, IN PMIB_IPMCAST_SCOPE pimmStart, IN OUT PDWORD pdwNumEntries, IN OUT PDWORD pdwBufferSize, IN OUT PBYTE *ppbBuffer) /*++ Arguments: pdwBufferSize: [IN] size of buffer [OUT] extra space left, if NO_ERROR is returned total size needed, if ERROR_INSUFFICIENT_BUFFER --*/ { // // See whether this scope fits the requested criteria // if (ntohl(ipAddr) > ntohl(pimmStart->dwGroupAddress) || ( ipAddr == pimmStart->dwGroupAddress && ntohl(ipMask) >= ntohl(pimmStart->dwGroupMask))) { MIB_IPMCAST_SCOPE imm; // // Make sure enough space is left in the buffer // if (*pdwBufferSize < sizeof(MIB_IPMCAST_SCOPE)) { if (*pdwNumEntries == 0) *pdwBufferSize = sizeof(MIB_IPMCAST_SCOPE); return ERROR_INSUFFICIENT_BUFFER; } // // Copy scope into buffer // imm.dwGroupAddress = ipAddr; imm.dwGroupMask = ipMask; sn_strcpy(imm.snNameBuffer, snScopeName); imm.dwStatus = ROWSTATUS_ACTIVE; CopyMemory(*ppbBuffer, &imm, sizeof(MIB_IPMCAST_SCOPE)); (*ppbBuffer) += sizeof(MIB_IPMCAST_SCOPE); (*pdwBufferSize) -= sizeof(MIB_IPMCAST_SCOPE); (*pdwNumEntries)++; } return NO_ERROR; } DWORD RmGetNextScope( IN PMIB_IPMCAST_SCOPE pimmStart, IN OUT PDWORD pdwBufferSize, IN OUT PBYTE pbBuffer, IN OUT PDWORD pdwNumEntries ) /*++ Locks: BOUNDARY_TABLE for reading Called by: RmGetFirstScope(), AccessMcastScope() in access.c --*/ { DWORD dwErr = NO_ERROR; DWORD dwNumEntries=0, dwBufferSize = *pdwBufferSize; SCOPE_ENTRY *pScope, local; DWORD dwInd; BOOL bHaveScopes = FALSE; PLIST_ENTRY pleNode; Trace1( MCAST, "ENTERED RmGetNextScope: %d", dwBufferSize); // Bump index by 1 pimmStart->dwGroupMask = htonl( ntohl(pimmStart->dwGroupMask) + 1); if (!pimmStart->dwGroupMask) { pimmStart->dwGroupAddress = htonl( ntohl(pimmStart->dwGroupAddress) + 1); } ENTER_READER(BOUNDARY_TABLE); { // Walk master scope list for (pleNode = g_MasterScopeList.Flink; dwNumEntries < *pdwNumEntries && pleNode isnot &g_MasterScopeList; pleNode = pleNode->Flink) { pScope = CONTAINING_RECORD(pleNode, SCOPE_ENTRY, leScopeLink); if ( !pScope->ipGroupAddress ) continue; bHaveScopes = TRUE; dwErr = AddNextScope(pScope->ipGroupAddress, pScope->ipGroupMask, GetDefaultName( pScope ), pimmStart, &dwNumEntries, &dwBufferSize, &pbBuffer); if (dwErr == ERROR_INSUFFICIENT_BUFFER) { *pdwBufferSize = dwBufferSize; return dwErr; } } // // Finally, if we have scopes, then we can also count // one for the IPv4 Local Scope. // if ( dwNumEntries > 0 && dwNumEntries < *pdwNumEntries && bHaveScopes ) { dwErr = AddNextScope( IPV4_LOCAL_SCOPE_ADDR, IPV4_LOCAL_SCOPE_MASK, IPV4_LOCAL_SCOPE_NAME, pimmStart, &dwNumEntries, &dwBufferSize, &pbBuffer ); } if (!dwNumEntries && dwErr==NO_ERROR) dwErr = ERROR_NO_MORE_ITEMS; } EXIT_LOCK(BOUNDARY_TABLE); *pdwBufferSize -= dwBufferSize; *pdwNumEntries = dwNumEntries; Trace1( MCAST, "LEAVING RmGetNextScope %x", dwErr ); return dwErr; } DWORD RmGetScope( IN PMIB_IPMCAST_SCOPE pimm, IN OUT PDWORD pdwBufferSize, IN OUT PBYTE pbBuffer ) /*++ Called by: AccessMcastScope() in access.c Returns: SNMP error code --*/ { DWORD dwErr = NO_ERROR; SCOPE_ENTRY *pScope; PMIB_IPMCAST_SCOPE *pOut; Trace1( ENTER, "ENTERED RmGetScope: %d", *pdwBufferSize ); if (*pdwBufferSize < sizeof(MIB_IPMCAST_SCOPE)) { *pdwBufferSize = sizeof(MIB_IPMCAST_SCOPE); return ERROR_INSUFFICIENT_BUFFER; } pimm->dwStatus = ROWSTATUS_ACTIVE; ENTER_READER(BOUNDARY_TABLE); do { if ( pimm->dwGroupAddress == IPV4_LOCAL_SCOPE_ADDR && pimm->dwGroupMask == IPV4_LOCAL_SCOPE_MASK ) { sn_strcpy( pimm->snNameBuffer, IPV4_LOCAL_SCOPE_NAME ); CopyMemory(pbBuffer, pimm, sizeof(MIB_IPMCAST_SCOPE)); } else { pScope = FindScope(pimm->dwGroupAddress, pimm->dwGroupMask); if (pScope == NULL) { dwErr = ERROR_NOT_FOUND; break; } // Ok, we found it. CopyMemory(pbBuffer, pimm, sizeof(MIB_IPMCAST_SCOPE)); } } while(0); EXIT_LOCK(BOUNDARY_TABLE); Trace1( ENTER, "LEAVING RmGetScope %x\n", dwErr ); return dwErr; } DWORD RmGetFirstScope( IN OUT PDWORD pdwBufferSize, IN OUT PBYTE pbBuffer, IN OUT PDWORD pdwNumEntries ) /*++ Routine description: Get the first scope in lexicographic order. Since Addr=0 is not used, a GetFirst is equivalent to a GetNext with Addr=0. Called by: AccessMcastScope() in access.c --*/ { MIB_IPMCAST_SCOPE imm; imm.dwGroupAddress = imm.dwGroupMask = 0; return RmGetNextScope(&imm, pdwBufferSize, pbBuffer, pdwNumEntries); } //---------------------------------------------------------------------------- // BOUNDARY enumeration API. // //---------------------------------------------------------------------------- DWORD AddNextBoundary( IN DWORD dwIfIndex, IN IPV4_ADDRESS ipAddr, IN IPV4_ADDRESS ipMask, IN PMIB_IPMCAST_BOUNDARY pimmStart, IN OUT PDWORD pdwNumEntries, IN OUT PDWORD pdwBufferSize, IN OUT PBYTE *ppbBuffer) /*++ Arguments: pdwBufferSize: [IN] size of buffer [OUT] extra space left, if NO_ERROR is returned total size needed, if ERROR_INSUFFICIENT_BUFFER --*/ { // // See whether this boundary fits the requested criteria // if (ntohl(ipAddr) > ntohl(pimmStart->dwGroupAddress) || ( ipAddr == pimmStart->dwGroupAddress && ntohl(ipMask) >= ntohl(pimmStart->dwGroupMask))) { MIB_IPMCAST_BOUNDARY imm; // // Make sure enough space is left in the buffer // if (*pdwBufferSize < sizeof(MIB_IPMCAST_BOUNDARY)) { if (*pdwNumEntries == 0) *pdwBufferSize = sizeof(MIB_IPMCAST_BOUNDARY); return ERROR_INSUFFICIENT_BUFFER; } // // Copy boundary into buffer // imm.dwIfIndex = dwIfIndex; imm.dwGroupAddress = ipAddr; imm.dwGroupMask = ipMask; imm.dwStatus = ROWSTATUS_ACTIVE; CopyMemory(*ppbBuffer, &imm, sizeof(MIB_IPMCAST_BOUNDARY)); (*ppbBuffer) += sizeof(MIB_IPMCAST_BOUNDARY); (*pdwBufferSize) -= sizeof(MIB_IPMCAST_BOUNDARY); (*pdwNumEntries)++; } return NO_ERROR; } DWORD RmGetNextBoundary( IN PMIB_IPMCAST_BOUNDARY pimmStart, IN OUT PDWORD pdwBufferSize, IN OUT PBYTE pbBuffer, IN OUT PDWORD pdwNumEntries ) /*++ Locks: BOUNDARY_TABLE for reading Called by: RmGetFirstBoundary(), AccessMcastBoundary() in access.c --*/ { DWORD dwErr = NO_ERROR; PLIST_ENTRY pleIf, pleBound; DWORD dwNumEntries=0, dwBufferSize = *pdwBufferSize; BOUNDARY_ENTRY *pBound, local; Trace1( MCAST, "ENTERED RmGetNextBoundary: %d", dwBufferSize); // Bump index by 1 pimmStart->dwGroupMask = htonl( ntohl(pimmStart->dwGroupMask) + 1); if (!pimmStart->dwGroupMask) { pimmStart->dwGroupAddress = htonl( ntohl(pimmStart->dwGroupAddress) + 1); if (!pimmStart->dwGroupAddress) pimmStart->dwIfIndex++; } ENTER_READER(BOUNDARY_TABLE); { // Walk master BOUNDARY_IF list for (pleIf = g_MasterInterfaceList.Flink; dwErr == NO_ERROR && dwNumEntries < *pdwNumEntries && pleIf isnot &g_MasterInterfaceList; pleIf = pleIf->Flink) { BOUNDARY_IF *pBIf = CONTAINING_RECORD(pleIf, BOUNDARY_IF, leBoundaryIfMasterLink); if (pBIf->dwIfIndex >= pimmStart->dwIfIndex) { // Walk BOUNDARY list for (pleBound = pBIf->leBoundaryList.Flink; dwErr == NO_ERROR && dwNumEntries < *pdwNumEntries && pleBound isnot &pBIf->leBoundaryList; pleBound = pleBound->Flink) { pBound = CONTAINING_RECORD(pleBound, BOUNDARY_ENTRY, leBoundaryLink); dwErr = AddNextBoundary(pBIf->dwIfIndex, pBound->pScope->ipGroupAddress, pBound->pScope->ipGroupMask, pimmStart, &dwNumEntries, &dwBufferSize, &pbBuffer); } // // Finally, if we have boundaries, then we can also count // one for the IPv4 Local Scope. // if (dwErr == NO_ERROR && dwNumEntries < *pdwNumEntries && !IsListEmpty( &pBIf->leBoundaryList ) ) { dwErr = AddNextBoundary(pBIf->dwIfIndex, IPV4_LOCAL_SCOPE_ADDR, IPV4_LOCAL_SCOPE_MASK, pimmStart, &dwNumEntries, &dwBufferSize, &pbBuffer); } if (dwErr == ERROR_INSUFFICIENT_BUFFER) { *pdwBufferSize = dwBufferSize; return dwErr; } } } if (!dwNumEntries && dwErr==NO_ERROR) dwErr = ERROR_NO_MORE_ITEMS; } EXIT_LOCK(BOUNDARY_TABLE); *pdwBufferSize -= dwBufferSize; *pdwNumEntries = dwNumEntries; Trace1( MCAST, "LEAVING RmGetNextBoundary %x\n", dwErr ); return dwErr; } DWORD RmGetFirstBoundary( IN OUT PDWORD pdwBufferSize, IN OUT PBYTE pbBuffer, IN OUT PDWORD pdwNumEntries ) /*++ Routine description: Get the first boundary in lexicographic order. Since IfIndex=0 is not used, a GetFirst is equivalent to a GetNext with IfIndex=0. Called by: AccessMcastBoundary() in access.c --*/ { MIB_IPMCAST_BOUNDARY imm; imm.dwIfIndex = imm.dwGroupAddress = imm.dwGroupMask = 0; return RmGetNextBoundary(&imm, pdwBufferSize, pbBuffer, pdwNumEntries); } void InitializeBoundaryTable() /*++ Locks: BOUNDARY_TABLE for writing --*/ { register int i; ENTER_WRITER(BOUNDARY_TABLE); { for (i=0; iipGroupAddress | ~(pScope)->ipGroupMask) // Convert number of seconds to number of 100ns intervals #define TM_SECONDS(x) ((x)*10000000) // // Define this if/when authentication of MZAP messages is provided // #undef SECURE_MZAP // // Address used as Message Origin for locally-originated messages // IPV4_ADDRESS g_ipMyAddress = INADDR_ANY; IPV4_ADDRESS g_ipMyLocalZoneID = INADDR_ANY; SOCKET g_mzapLocalSocket = INVALID_SOCKET; LIST_ENTRY g_zbrTimerList; LIST_ENTRY g_zleTimerList; BOOL g_bMzapStarted = FALSE; HANDLE g_hMzapSocketEvent = NULL; // For now, originate all ZAMs and ZCMs at the same time. LARGE_INTEGER g_liZamExpiryTime; DWORD UpdateMzapTimer(); #include typedef struct _IPV4_MZAP_HEADER { BYTE byVersion; BYTE byBPType; BYTE byAddressFamily; BYTE byNameCount; IPV4_ADDRESS ipMessageOrigin; IPV4_ADDRESS ipScopeZoneID; IPV4_ADDRESS ipScopeStart; IPV4_ADDRESS ipScopeEnd; BYTE pScopeNameBlock[0]; } IPV4_MZAP_HEADER, *PIPV4_MZAP_HEADER; typedef struct _IPV4_ZAM_HEADER { BYTE bZT; BYTE bZTL; WORD wHoldTime; IPV4_ADDRESS ipAddress[1]; } IPV4_ZAM_HEADER, *PIPV4_ZAM_HEADER; typedef struct _IPV4_ZCM_HEADER { BYTE bZNUM; BYTE bReserved; WORD wHoldTime; IPV4_ADDRESS ipZBR[0]; } IPV4_ZCM_HEADER, *PIPV4_ZCM_HEADER; #include ////////////////////////////////////////////////////////////////////////////// // Functions for ZBR neighbor list and Zone ID maintenance ////////////////////////////////////////////////////////////////////////////// ZBR_ENTRY * FindZBR( IN PSCOPE_ENTRY pScope, IN IPV4_ADDRESS ipAddress ) /*++ Description: Finds a given ZBR in a list. Arguments: IN pscope - scope to find a ZBR associated with IN ipAddress - address of ZBR to find Returns: Pointer to ZBR entry, or NULL if not found Called by: AssertZBR() Locks: Assumes caller holds read lock on BOUNDARY_ENTRY and ZBR_LIST --*/ { PZBR_ENTRY pZbr; PLIST_ENTRY pleNode; for (pleNode = pScope->leZBRList.Flink; pleNode isnot &pScope->leZBRList; pleNode = pleNode->Flink) { pZbr = CONTAINING_RECORD(pleNode, ZBR_ENTRY, leZBRLink); if (pZbr->ipAddress == ipAddress) { return pZbr; } } return NULL; } IPV4_ADDRESS MyScopeZoneID( IN PSCOPE_ENTRY pScope ) /*++ Description: Get the Zone ID which we're inside for a given scope Arguments: IN pScope - scope to get the zone ID for Returns: scope zone address Called by: AddMZAPHeader(), HandleZAM() Locks: Assumes caller holds read lock on BOUNDARY_TABLE so that pScope won't go away. --*/ { PLIST_ENTRY pleNode; IPV4_ADDRESS ipScopeZoneID = g_ipMyAddress; // If first ZBR has a lower IP address than us, use that. pleNode = pScope->leZBRList.Flink; if (pleNode isnot &pScope->leZBRList) { ZBR_ENTRY *pZbr = CONTAINING_RECORD(pleNode, ZBR_ENTRY, leZBRLink); if (ntohl(pZbr->ipAddress) < ntohl(ipScopeZoneID)) ipScopeZoneID = pZbr->ipAddress; } return ipScopeZoneID; } VOID SetZbrExpiryTime( PZBR_ENTRY pZbr, LARGE_INTEGER liExpiryTime ) { PLIST_ENTRY pleNode; pZbr->liExpiryTime = liExpiryTime; for (pleNode = g_zbrTimerList.Flink; pleNode isnot &g_zbrTimerList; pleNode = pleNode->Flink) { ZBR_ENTRY *pPrev = CONTAINING_RECORD(pleNode, ZBR_ENTRY, leTimerLink); if (RtlLargeIntegerGreaterThan(pPrev->liExpiryTime, liExpiryTime)) break; } InsertTailList( pleNode, &pZbr->leTimerLink ); } ZBR_ENTRY * AddZBR( IN PSCOPE_ENTRY pScope, IN IPV4_ADDRESS ipAddress, IN LARGE_INTEGER liExpiryTime ) /*++ Description: Adds ZBR to scope's list. Initializes timer, and updates Zone ID. Arguments: IN pScope - scope to add a boundary router of IN ipAddress - address of the boundary router to add IN liExpiryTime - time at which to expire the boundary router entry Returns: Pointer to new boundary router entry, or NULL on memory alloc error Called by: AssertZBR() Locks: Assumes caller holds write lock on BOUNDARY_ENTRY, MZAP_TIMER, and ZBR_LIST --*/ { PZBR_ENTRY pZbr; PLIST_ENTRY pleNode; // Initialize new ZBR entry pZbr = MALLOC( sizeof(ZBR_ENTRY) ); if (!pZbr) { return NULL; } pZbr->ipAddress = ipAddress; // Add ZBR to list in order of lowest IP address for (pleNode = pScope->leZBRList.Flink; pleNode isnot &pScope->leZBRList; pleNode = pleNode->Flink) { ZBR_ENTRY *pPrev = CONTAINING_RECORD(pleNode, ZBR_ENTRY, leZBRLink); if (ntohl(pPrev->ipAddress) > ntohl(ipAddress)) { break; } } InsertTailList( pleNode, &pZbr->leZBRLink ); // We don't need to update Zone ID since it's recalculated // whenever we need it. // Add ZBR to timer list in order of expiry time SetZbrExpiryTime( pZbr, liExpiryTime ); UpdateMzapTimer(); return pZbr; } ZBR_ENTRY * AssertZBR( IN PSCOPE_ENTRY pScope, IN IPV4_ADDRESS ipAddress, IN WORD wHoldTime ) /*++ Description: Finds ZBR in list, adding it if needed. Resets timer for ZBR. Arguments: IN pScope - scope to find/add a boundary router of IN ipAddress - address of boundary router to find/add IN wHoldTime - hold time in seconds remaining to reset timer to Returns: Pointer to boundary router entry Called by: HandleZAM(), HandleZCM() Locks: Assumes caller holds read lock on BOUNDARY_ENTRY Locks MZAP_TIMER and then ZBR_LIST for writing --*/ { LARGE_INTEGER liCurrentTime, liExpiryTime; ZBR_ENTRY *pZbr; NtQuerySystemTime( &liCurrentTime ); liExpiryTime = RtlLargeIntegerAdd(liCurrentTime, RtlConvertUlongToLargeInteger(TM_SECONDS((ULONG)wHoldTime))); ENTER_WRITER(MZAP_TIMER); ENTER_WRITER(ZBR_LIST); { pZbr = FindZBR( pScope, ipAddress ); if (!pZbr) { pZbr = AddZBR( pScope, ipAddress, liExpiryTime ); } else { RemoveEntryList( &pZbr->leTimerLink ); SetZbrExpiryTime( pZbr, liExpiryTime ); } } EXIT_LOCK(ZBR_LIST); EXIT_LOCK(MZAP_TIMER); return pZbr; } VOID DeleteZBR( IN PZBR_ENTRY pZbr ) /*++ Arguments: IN pZbr - Pointer to boundary router entry to delete Called by: HandleMzapTimer() Locks: Assumes caller has write lock on ZBR_LIST --*/ { // Remove from timer list RemoveEntryList( &pZbr->leTimerLink ); // Remove from ZBR list for the scope RemoveEntryList( &pZbr->leZBRLink ); // We don't need to update the Zone ID, since it's recalculated // whenever we need it } ////////////////////////////////////////////////////////////////////////////// // Functions for pending ZLE store manipulation ////////////////////////////////////////////////////////////////////////////// typedef struct _ZLE_PENDING { LIST_ENTRY leTimerLink; PBYTE pBuffer; ULONG ulBuffLen; LARGE_INTEGER liExpiryTime; } ZLE_PENDING, *PZLE_PENDING; LIST_ENTRY g_leZleList; PZLE_PENDING AddPendingZLE( IN PBYTE pBuffer, IN ULONG ulBuffLen, IN LARGE_INTEGER liExpiryTime ) /*++ Arguments: IN pBuffer - buffer holding ZLE message IN ulBuffLen - size in bytes of buffer passed in IN liExpiryTime - time at which to expire ZLE entry Returns: Pointer to ZLE entry added, or NULL on memory alloc error Called by: HandleZAM() Locks: Assumes caller holds write lock on ZLE_LIST --*/ { PLIST_ENTRY pleNode; PZLE_PENDING pZle; pZle = MALLOC( sizeof(ZLE_PENDING) ); if (!pZle) { return NULL; } pZle->pBuffer = pBuffer; pZle->ulBuffLen = ulBuffLen; pZle->liExpiryTime = liExpiryTime; // Search for entry after the new one for (pleNode = g_leZleList.Flink; pleNode isnot &g_leZleList; pleNode = pleNode->Flink) { PZLE_PENDING pPrev = CONTAINING_RECORD(pleNode,ZLE_PENDING,leTimerLink); if (RtlLargeIntegerGreaterThan(pPrev->liExpiryTime, pZle->liExpiryTime)) { break; } } // Insert into cache InsertTailList( pleNode, &pZle->leTimerLink ); return pZle; } VOID DeletePendingZLE( IN PZLE_PENDING zle ) /*++ Description: Remove all state related to a pending ZLE Arguments: IN zle - pointer to ZLE entry to delete Called by: HandleZLE(), SendZLE() Locks: Assumes caller holds write lock on ZLE_LIST --*/ { RemoveEntryList( &zle->leTimerLink ); // Free up space FREE(zle->pBuffer); FREE(zle); } PZLE_PENDING FindPendingZLE( IN IPV4_MZAP_HEADER *mh ) /*++ Description: Find an entry for a pending ZLE which matches a given MZAP message header Arguments: IN mh - pointer to MZAP message header to locate a matching ZLE entry for Returns: Pointer to matching ZLE entry, if any Called by: HandleZAM(), HandleZLE() Locks: Assumes caller holds read lock on ZLE_LIST --*/ { PLIST_ENTRY pleNode; IPV4_MZAP_HEADER *mh2; for (pleNode = g_leZleList.Flink; pleNode isnot &g_leZleList; pleNode = pleNode->Flink) { PZLE_PENDING zle = CONTAINING_RECORD(pleNode, ZLE_PENDING, leTimerLink); mh2 = (PIPV4_MZAP_HEADER)zle->pBuffer; if (mh->ipScopeZoneID == mh2->ipScopeZoneID && mh->ipScopeStart == mh2->ipScopeStart) { return zle; } } return NULL; } ////////////////////////////////////////////////////////////////////////////// // Functions for ZAM cache manipulation ////////////////////////////////////////////////////////////////////////////// typedef struct _ZAM_ENTRY { LIST_ENTRY leCacheLink; IPV4_ADDRESS ipScopeZoneID; IPV4_ADDRESS ipStartAddress; LARGE_INTEGER liExpiryTime; } ZAM_ENTRY, *PZAM_ENTRY; LIST_ENTRY g_leZamCache; void UpdateZamCache( IN LARGE_INTEGER liCurrentTime ) /*++ Description: Throw any expired entries out of the ZAM cache. Arguments: IN liCurrentTime - current time, to compare vs expiry times of entries Called by: AssertInZamCache() Locks: Assumes caller has write lock on ZAM_CACHE --*/ { PLIST_ENTRY pleNode; PZAM_ENTRY pZam; // Throw out old cache entries while (g_leZamCache.Flink isnot &g_leZamCache) { pleNode = g_leZamCache.Flink; pZam = CONTAINING_RECORD(pleNode, ZAM_ENTRY, leCacheLink); if ( RtlLargeIntegerLessThanOrEqualTo( pZam->liExpiryTime, liCurrentTime ) || RtlLargeIntegerEqualToZero( liCurrentTime )) { Trace6(MCAST, "Evicting %d.%d.%d.%d/%d.%d.%d.%d from ZAM cache with current time %x.%x exp %x.%x", PRINT_IPADDR(pZam->ipScopeZoneID), PRINT_IPADDR(pZam->ipStartAddress), liCurrentTime.HighPart, liCurrentTime.LowPart, pZam->liExpiryTime.HighPart, pZam->liExpiryTime.LowPart); RemoveEntryList( &pZam->leCacheLink ); FREE( pZam ); continue; } // Ok, we've reached one that stays, so we're done break; } } PZAM_ENTRY AddToZamCache( IN IPV4_ADDRESS ipScopeZoneID, IN IPV4_ADDRESS ipStartAddress, IN LARGE_INTEGER liExpiryTime ) /*++ Description: This function takes a ZAM identifier and timeout, and adds it to the ZAM cache. Arguments: IN ipScopeZoneID - scope zone ID to cache IN ipStartAddress - scope start address to cache IN liExpiryTime - time at which to expire the cache entry Returns: Pointer to cache entry, or NULL on memory error Called by: AssertInZamCache() Locks: Assumes caller holds write lock on ZAM_CACHE --*/ { PLIST_ENTRY pleNode; PZAM_ENTRY pZam; // Add entry to cache pZam = MALLOC( sizeof(ZAM_ENTRY) ); if (!pZam) { return NULL; } pZam->ipScopeZoneID = ipScopeZoneID; pZam->ipStartAddress = ipStartAddress; pZam->liExpiryTime = liExpiryTime; // Search for entry after the new one for (pleNode = g_leZamCache.Flink; pleNode isnot &g_leZamCache; pleNode = pleNode->Flink) { PZAM_ENTRY pPrevC = CONTAINING_RECORD(pleNode, ZAM_ENTRY, leCacheLink); if (RtlLargeIntegerGreaterThan(pPrevC->liExpiryTime, pZam->liExpiryTime)) { break; } } // Insert into cache InsertTailList( pleNode, &pZam->leCacheLink ); return pZam; } PZAM_ENTRY FindInZamCache( IN IPV4_ADDRESS ipScopeZoneID, IN IPV4_ADDRESS ipStartAddress ) /*++ Description: See if a given ZAM spec is in the cache. Arguments: IN ipScopeZoneID - scope zone ID to match IN ipStartAddress - scope start address to match Return: Pointer to cache entry, or NULL if not found. Called by: AssertInZamCache() Locks: Assumes caller has read lock on ZAM_CACHE --*/ { PLIST_ENTRY pleNode; // Search for cache entry for (pleNode = g_leZamCache.Flink; pleNode isnot &g_leZamCache; pleNode = pleNode->Flink) { ZAM_ENTRY *pZam = CONTAINING_RECORD(pleNode, ZAM_ENTRY, leCacheLink); if ( ipScopeZoneID is pZam->ipScopeZoneID && ipStartAddress is pZam->ipStartAddress) { return pZam; } } return NULL; } PZAM_ENTRY AssertInZamCache( IN IPV4_ADDRESS ipScopeZoneID, IN IPV4_ADDRESS ipStartAddress, OUT BOOL *pbFound ) /*++ Description: Locate a ZAM spec in the cache, adding it if not already present. Arguments: IN ipScopeZoneID - scope zone ID to match/cache IN ipStartAddress - scope start address to match/cache OUT pbFound - TRUE if found, FALSE if newly cached Called by: HandleZAM() Locks: ZAM_CACHE for writing --*/ { PZAM_ENTRY pZam; LARGE_INTEGER liCurrentTime, liExpiryTime; // Get current time NtQuerySystemTime(&liCurrentTime); ENTER_WRITER(ZAM_CACHE); { UpdateZamCache(liCurrentTime); pZam = FindInZamCache( ipScopeZoneID, ipStartAddress); if (!pZam) { liExpiryTime = RtlLargeIntegerAdd(liCurrentTime, RtlConvertUlongToLargeInteger(TM_SECONDS(ZAM_DUP_TIME))); AddToZamCache( ipScopeZoneID, ipStartAddress, liExpiryTime ); Trace6(MCAST, "Added %d.%d.%d.%d/%d.%d.%d.%d to ZAM cache with current time %x/%x exp %x/%x", PRINT_IPADDR(ipScopeZoneID), PRINT_IPADDR(ipStartAddress), liCurrentTime.HighPart, liCurrentTime.LowPart, liExpiryTime.HighPart, liExpiryTime.LowPart); *pbFound = FALSE; } else { *pbFound = TRUE; } } EXIT_LOCK(ZAM_CACHE); return pZam; } ////////////////////////////////////////////////////////////////////////////// // Functions for message sending ////////////////////////////////////////////////////////////////////////////// DWORD SendMZAPMessageByIndex( IN PBYTE pBuffer, IN ULONG ulBuffLen, IN IPV4_ADDRESS ipGroup, IN DWORD dwIfIndex ) { SOCKADDR_IN sinAddr; DWORD dwErr = NO_ERROR, dwLen; dwErr = McSetMulticastIfByIndex( g_mzapLocalSocket, SOCK_DGRAM, dwIfIndex ); if (dwErr is SOCKET_ERROR) { dwErr = WSAGetLastError(); Trace2( ERR, "SendMZAPMessage: error %d setting oif to IF %x", dwErr, dwIfIndex ); } sinAddr.sin_family = AF_INET; sinAddr.sin_addr.s_addr = ipGroup; sinAddr.sin_port = htons(MZAP_PORT); #ifdef DEBUG_MZAP Trace2( ERR, "SendMZAPMessageByIndex: sending %d bytes on IF %d", ulBuffLen, dwIfIndex ); #endif dwLen = sendto( g_mzapLocalSocket, pBuffer, ulBuffLen, 0, (struct sockaddr*)&sinAddr, sizeof(sinAddr)); #ifdef DEBUG_MZAP Trace1( ERR, "SendMZAPMessageByIndex: sent %d bytes", dwLen); #endif if (dwLen is SOCKET_ERROR ) { dwErr = WSAGetLastError(); Trace1( ERR, "SendMZAPMessage: error %d sending message", dwErr ); } return dwErr; } DWORD SendMZAPMessage( IN PBYTE pBuffer, IN ULONG ulBuffLen, IN IPV4_ADDRESS ipGroup, IN IPV4_ADDRESS ipInterface ) /*++ Called by: HandleZAM() Arguments: IN pBuffer - buffer containing message to send IN ulBuffLen - length of buffer in bytes IN ipGroup - destination address to send message to IN ipInterface - interface to send message out Returns: whatever WSAGetLastError() returns Locks: None --*/ { SOCKADDR_IN sinAddr; DWORD dwErr = NO_ERROR, dwLen; dwErr = McSetMulticastIf( g_mzapLocalSocket, ipInterface ); if (dwErr is SOCKET_ERROR) { dwErr = WSAGetLastError(); Trace2( ERR, "SendMZAPMessage: error %d setting oif to %d.%d.%d.%d", dwErr, PRINT_IPADDR(ipInterface) ); } sinAddr.sin_family = AF_INET; sinAddr.sin_addr.s_addr = ipGroup; sinAddr.sin_port = htons(MZAP_PORT); #ifdef DEBUG_MZAP Trace2( ERR, "SendMZAPMessage: sending %d bytes on %d.%d.%d.%d", ulBuffLen, PRINT_IPADDR(ipInterface)); #endif dwLen = sendto( g_mzapLocalSocket, pBuffer, ulBuffLen, 0, (struct sockaddr*)&sinAddr, sizeof(sinAddr)); #ifdef DEBUG_MZAP Trace1( ERR, "SendMZAPMessage: sent %d bytes", dwLen); #endif if (dwLen is SOCKET_ERROR ) { dwErr = WSAGetLastError(); Trace1( ERR, "SendMZAPMessage: error %d sending message", dwErr ); } return dwErr; } void AddMZAPHeader( IN OUT PBYTE *ppb, // IN: pointer into buffer IN BYTE byPType, // IN: message type IN PSCOPE_ENTRY pScope // IN: scope ) /*++ Description: Compose an MZAP message header in a buffer. Arguments: IN/OUT ppb - buffer to add an MZAP header to IN byPType - message type to fill into header IN pScope - scope to fill into header Called by: SendZAM(), SendZCM() Locks: Assumes caller holds read lock on BOUNDARY_TABLE so pScope won't go away --*/ { PBYTE pb; IPV4_MZAP_HEADER *mh = (PIPV4_MZAP_HEADER)*ppb; BYTE pConfName[257]; ULONG ulConfNameLen, ulConfLangLen; PSCOPE_NAME_ENTRY pName; int iDefault; PLIST_ENTRY pleNode; PBYTE pLangName; // Make sure packing is correct ASSERT((((PBYTE)&mh->ipMessageOrigin) - ((PBYTE)mh)) is 4); mh->byVersion = MZAP_VERSION; mh->byBPType = byPType; if (pScope->bDivisible) { mh->byBPType |= MZAP_BIG_BIT; } mh->byAddressFamily = ADDRFAMILY_IPV4; mh->byNameCount = 0; mh->ipMessageOrigin = g_ipMyAddress; mh->ipScopeZoneID = MyScopeZoneID(pScope); mh->ipScopeStart = pScope->ipGroupAddress; mh->ipScopeEnd = TOP_OF_SCOPE( pScope ); // Append scope name blocks pb = *ppb + sizeof(IPV4_MZAP_HEADER); for (pleNode = pScope->leNameList.Flink; pleNode isnot &pScope->leNameList; pleNode = pleNode->Flink) { pName = CONTAINING_RECORD(pleNode, SCOPE_NAME_ENTRY, leNameLink); iDefault = (pName->bDefault)? MZAP_DEFAULT_BIT : 0; pLangName = GetLangName(pName->idLanguage); ulConfLangLen = strlen(pLangName); ulConfNameLen = WideCharToMultiByte( CP_UTF8, 0, pName->snScopeName, sn_strlen( pName->snScopeName ), pConfName, sizeof(pConfName), NULL, NULL ); *pb++ = (BYTE)iDefault; *pb++ = (BYTE)ulConfLangLen; strncpy( pb, pLangName, ulConfLangLen ); pb += ulConfLangLen; *pb++ = (BYTE)ulConfNameLen; strncpy( pb, pConfName, ulConfNameLen ); pb += ulConfNameLen; mh->byNameCount++; } // Pad to a 4-byte boundary // Note that casting to a ULONG is 64-bit safe, since we only care about // the low-order bits anyway. while (((ULONG_PTR)pb) & 3) { *pb++ = '\0'; } *ppb = pb; } INLINE IPV4_ADDRESS MzapRelativeGroup( IN PSCOPE_ENTRY pScope ) /*++ Description: Returns the Scope-relative group address for MZAP within a given scope. Arguments: IN pScope - scope to find the MZAP group in Returns: Address of the MZAP group in the scope Locks: Assumes caller holds read lock on BOUNDARY_TABLE so pScope doesn't go away --*/ { return htonl(ntohl(TOP_OF_SCOPE(pScope)) - MZAP_RELATIVE_GROUP); } ULONG GetMZAPHeaderSize( IN PSCOPE_ENTRY pScope ) { PLIST_ENTRY pleNode; ULONG ulLen = sizeof(IPV4_MZAP_HEADER); BYTE pConfName[257]; PSCOPE_NAME_ENTRY pName; PBYTE pLangName; ULONG ulConfLangLen, ulConfNameLen; // For each scope name, add size needed to store it for (pleNode = pScope->leNameList.Flink; pleNode isnot &pScope->leNameList; pleNode = pleNode->Flink) { pName = CONTAINING_RECORD(pleNode, SCOPE_NAME_ENTRY, leNameLink); pLangName = GetLangName(pName->idLanguage); ulConfLangLen = strlen(pLangName); WideCharToMultiByte( CP_UTF8, 0, pName->snScopeName, sn_strlen( pName->snScopeName ), pConfName, sizeof(pConfName), NULL, NULL ); ulConfNameLen = strlen( pConfName ); ulLen += 3; // flags, langlen, and namelen ulLen += ulConfLangLen; ulLen += ulConfNameLen; } // Round up to multiple of 4 ulLen = 4 * ((ulLen + 3) / 4); return ulLen; } ULONG GetZAMBuffSize( IN PSCOPE_ENTRY pScope ) { ULONG ulLen = GetMZAPHeaderSize(pScope) + sizeof(IPV4_ZAM_HEADER); #ifdef SECURE_MZAP // Add size of Authentication Block // XXX #endif // return 512; // an unsigned IPv4 ZAM message is at most 284 bytes return ulLen; } DWORD SendZAM( IN PSCOPE_ENTRY pScope ) /*++ Description: Send a ZAM message within a given scope. Locks: Assumes caller holds lock on BOUNDARY_TABLE so pScope doesn't go away --*/ { DWORD dwErr; PBYTE pBuffer, pb; PIPV4_ZAM_HEADER zam; ULONG ulBuffLen; ulBuffLen = GetZAMBuffSize( pScope ); pb = pBuffer = MALLOC( ulBuffLen ); if (!pb) { return ERROR_NOT_ENOUGH_MEMORY; } // Fill in MZAP header AddMZAPHeader(&pb, PTYPE_ZAM, pScope); zam = (PIPV4_ZAM_HEADER)pb; zam->bZT = 0; zam->bZTL = pScope->bZTL; zam->wHoldTime = htons(ZAM_HOLDTIME); zam->ipAddress[0] = g_ipMyLocalZoneID; pb += sizeof(IPV4_ZAM_HEADER); #ifdef SECURE_MZAP // Add optional authentication block here #endif #ifdef DEBUG_MZAP Trace0(ERR, "Originate ZAM inside..."); #endif // Send on an interface which does not have a boundary for the given scope. dwErr = SendMZAPMessage( pBuffer, (DWORD)(pb-pBuffer), MZAP_LOCAL_GROUP, g_ipMyAddress ); FREE( pBuffer ); return dwErr; } ULONG GetZCMBuffSize( IN PSCOPE_ENTRY pScope ) { PLIST_ENTRY pleNode; ULONG ulLen = GetMZAPHeaderSize(pScope) + sizeof(IPV4_ZCM_HEADER); for (pleNode = pScope->leZBRList.Flink; pleNode isnot &pScope->leZBRList; pleNode = pleNode->Flink) { ulLen += sizeof(IPV4_ADDRESS); } return ulLen; } DWORD SendZCM( IN PSCOPE_ENTRY pScope ) /*++ Description: Sends a Zone Convexity Message for a given scope. Locks: Assumes caller has read lock on BOUNDARY_TABLE so pScope won't go away. Locks ZBR_LIST for reading. --*/ { PBYTE pb; PIPV4_ZCM_HEADER zcm; PLIST_ENTRY pleNode; PZBR_ENTRY pZbr; WSABUF wsaZcmBuf; DWORD dwSize, dwErr; ENTER_READER(ZBR_LIST); { dwSize = GetZCMBuffSize(pScope); wsaZcmBuf.len = dwSize; wsaZcmBuf.buf = MALLOC( dwSize ); pb = wsaZcmBuf.buf; if (!pb) { EXIT_LOCK(ZBR_LIST); return GetLastError(); } // Fill in MZAP header AddMZAPHeader(&pb, PTYPE_ZCM, pScope); zcm = (PIPV4_ZCM_HEADER)pb; zcm->bZNUM = 0; zcm->bReserved = 0; zcm->wHoldTime = htons(ZCM_HOLDTIME); // Add all known neighbors for (pleNode = pScope->leZBRList.Flink; pleNode isnot &pScope->leZBRList; pleNode = pleNode->Flink) { pZbr = CONTAINING_RECORD(pleNode, ZBR_ENTRY, leZBRLink); zcm->ipZBR[ zcm->bZNUM++ ] = pZbr->ipAddress; } } EXIT_LOCK(ZBR_LIST); pb += sizeof(IPV4_ZCM_HEADER) + zcm->bZNUM * sizeof(IPV4_ADDRESS); #ifdef DEBUG_MZAP Trace0(ERR, "Sending ZCM..."); #endif dwErr = SendMZAPMessage( wsaZcmBuf.buf, (DWORD)(pb-wsaZcmBuf.buf), MzapRelativeGroup(pScope), g_ipMyAddress ); // Free the buffer FREE( wsaZcmBuf.buf ); return dwErr; } DWORD SendZLE( IN PZLE_PENDING zle ) /*++ Description: Given a buffer holding a ZAM, immediately send a ZLE to the origin. Locks: Assumes caller holds write lock on ZLE_LIST --*/ { DWORD dwErr; PBYTE pBuffer = zle->pBuffer; ULONG ulBuffLen = zle->ulBuffLen; IPV4_MZAP_HEADER *mh = (PIPV4_MZAP_HEADER)pBuffer; IPV4_ADDRESS ipDestAddr = mh->ipScopeEnd - MZAP_RELATIVE_GROUP; // Change PType to ZLE mh->byBPType = (mh->byBPType & MZAP_BIG_BIT) | PTYPE_ZLE; #ifdef DEBUG_MZAP Trace0(ERR, "Sending ZLE..."); #endif // Return to sender dwErr = SendMZAPMessage( pBuffer, ulBuffLen, ipDestAddr, g_ipMyAddress ); // Free up space DeletePendingZLE(zle); return dwErr; } double UniformRandom01() { return ((double)rand()) / RAND_MAX; } VOID SendAllZamsAndZcms() /*++ Locks: BOUNDARY_TABLE for reading --*/ { PLIST_ENTRY pleNode; PSCOPE_ENTRY pScope; double t,x; ULONG Tmin,Trange; BOOL bSent = FALSE; ENTER_READER(BOUNDARY_TABLE); { for (pleNode = g_MasterScopeList.Flink; pleNode isnot &g_MasterScopeList; pleNode = pleNode->Flink) { pScope = CONTAINING_RECORD(pleNode, SCOPE_ENTRY, leScopeLink); // Send ZAM inside SendZAM( pScope ); // Send ZCM inside SendZCM( pScope ); bSent = TRUE; } if (bSent) { SendZCM( &g_LocalScope ); } } EXIT_LOCK(BOUNDARY_TABLE); // Schedule the next time to send them Tmin = ZAM_INTERVAL/2; Trange = ZAM_INTERVAL; x = UniformRandom01(); t = Tmin + x*Trange; g_liZamExpiryTime = RtlLargeIntegerAdd( g_liZamExpiryTime, RtlConvertUlongToLargeInteger(TM_SECONDS((ULONG)floor(t+0.5)))); } ////////////////////////////////////////////////////////////////////////////// // Functions for message processing ////////////////////////////////////////////////////////////////////////////// VOID CheckForScopeNameMismatch( IN PSCOPE_ENTRY pScope, IN IPV4_MZAP_HEADER *mh ) /*++ Locks: Assumes caller holds read lock on BOUNDARY_TABLE so pScope won't go away --*/ { DWORD i, dwMsgNameLen, dwMsgLangLen, dwConfNameLen = 0, dwConfLangLen; DWORD dwMsgNameWLen; BYTE pMsgLang[257], *pb, *pConfLang; SCOPE_NAME snConfName = NULL; SCOPE_NAME_BUFFER snMsgName; PLIST_ENTRY pleNode; PSCOPE_NAME_ENTRY pName; // For each language in the message // If we know that language // If the names are different // Signal a conflict pb = mh->pScopeNameBlock; for (i=0; ibyNameCount; i++) { pb++; // skip flags dwMsgLangLen = *pb++; strncpy(pMsgLang, pb, dwMsgLangLen); pMsgLang[ dwMsgLangLen ] = '\0'; pb += dwMsgLangLen; dwMsgNameLen = *pb++; dwMsgNameWLen = MultiByteToWideChar( CP_UTF8, 0, pb, dwMsgNameLen, snMsgName, MAX_SCOPE_NAME_LEN+1 ); snMsgName[dwMsgNameWLen] = L'\0'; pb += dwMsgNameLen; pName = GetScopeNameByLangName( pScope, pMsgLang ); if (!pName) continue; snConfName = pName->snScopeName; dwConfNameLen = sn_strlen(snConfName); // Check for a name conflict if (dwConfNameLen != dwMsgNameWLen || sn_strncmp(snConfName, snMsgName, dwMsgNameWLen)) { // Display origin and both scope names MakeAddressStringW(g_AddrBuf1, mh->ipMessageOrigin); Trace1( ERR, "ERROR: Scope name conflict with %ls", g_AddrBuf1 ); Trace1( ERR, "ERROR: Our name = %ls", snConfName ); Trace1( ERR, "ERROR: His name = %ls", snMsgName ); RouterLogEventExW( LOGHANDLE, EVENTLOG_ERROR_TYPE, 0, ROUTERLOG_IP_SCOPE_NAME_CONFLICT, L"%S%S%S", g_AddrBuf1, snConfName, snMsgName ); } } } VOID ReportLeakyScope( IN PSCOPE_ENTRY pScope, IN IPV4_MZAP_HEADER *mh, IN IPV4_ZAM_HEADER *zam ) /*++ Called by: HandleZAM(), HandleZLE() Locks: Assumes caller has read lock on BOUNDARY_TABLE so pScope won't go away --*/ { ULONG ulIdx; PWCHAR pwszBuffer, pb; Trace1( ERR, "ERROR: Leak detected in '%ls' scope! One of the following routers is misconfigured:", GetDefaultName( pScope ) ); pb = pwszBuffer = MALLOC( zam->bZT * 20 + 1 ); if (pwszBuffer is NULL) { Trace0( ERR, "ERROR: Couldn't allocate space for rest of message"); return; } // Add origin. swprintf(pb, L" %d.%d.%d.%d", PRINT_IPADDR(mh->ipMessageOrigin )); pb += wcslen(pb); Trace1( ERR, " %d.%d.%d.%d", PRINT_IPADDR(mh->ipMessageOrigin )); // Display addresses of routers in the path list. for (ulIdx=0; ulIdx < zam->bZT; ulIdx++) { swprintf(pb,L" %d.%d.%d.%d", PRINT_IPADDR(zam->ipAddress[ulIdx*2+1])); pb += wcslen(pb); Trace1( ERR, " %d.%d.%d.%d", PRINT_IPADDR(zam->ipAddress[ulIdx*2+1] )); } // write to event log RouterLogEventExW( LOGHANDLE, EVENTLOG_ERROR_TYPE, 0, ROUTERLOG_IP_LEAKY_SCOPE, L"%S%S", GetDefaultName(pScope), pwszBuffer ); FREE( pwszBuffer ); } VOID CheckForScopeRangeMismatch( IN IPV4_MZAP_HEADER *mh ) /*++ Called by: HandleZAM(), HandleZCM() Locks: Assumes caller has read lock on BOUNDARY_TABLE --*/ { PLIST_ENTRY pleNode; PSCOPE_ENTRY pScope; for (pleNode = g_MasterScopeList.Flink; pleNode isnot &g_MasterScopeList; pleNode = pleNode->Flink) { pScope = CONTAINING_RECORD(pleNode, SCOPE_ENTRY, leScopeLink); if (mh->ipScopeStart > TOP_OF_SCOPE(pScope) || mh->ipScopeEnd < pScope->ipGroupAddress) continue; MakeAddressStringW(g_AddrBuf1, mh->ipScopeStart); MakeAddressStringW(g_AddrBuf2, mh->ipScopeEnd); MakeAddressStringW(g_AddrBuf3, pScope->ipGroupAddress); MakeAddressStringW(g_AddrBuf4, TOP_OF_SCOPE(pScope) ); Trace1( ERR, "ERROR: ZAM scope conflicts with configured scope '%ls'!", GetDefaultName(pScope) ); Trace2( ERR, "ERROR: ZAM has: (%ls-%ls)", g_AddrBuf1, g_AddrBuf2 ); Trace2( ERR, "ERROR: Scope is (%ls-%ls)", g_AddrBuf3, g_AddrBuf4 ); RouterLogEventExW( LOGHANDLE, EVENTLOG_ERROR_TYPE, 0, ROUTERLOG_IP_SCOPE_ADDR_CONFLICT, L"%S%S%S%S%S", GetDefaultName(pScope), g_AddrBuf1, g_AddrBuf2, g_AddrBuf3, g_AddrBuf4 ); break; } } BOOL ZamIncludesZoneID( IPV4_ZAM_HEADER *zam, IPV4_ADDRESS ipZoneID ) { ULONG ulIdx; for (ulIdx=0; ulIdx <= ((ULONG)zam->bZT)*2; ulIdx+=2) { if (zam->ipAddress[ulIdx] == ipZoneID) { return TRUE; } } return FALSE; } void HandleZAM( IN PBYTE pBuffer, // IN: Buffer holding ZAM received IN ULONG ulBuffLen, // IN: Length of ZAM message IN PBOUNDARY_IF pInBIf // IN: BIf on which ZAM arrived, or NULL // if it came from "inside" ) /*++ Called by: HandleMZAPSocket() Locks: Assumes caller holds read lock on BOUNDARY_TABLE Locks ZLE_LIST for writing --*/ { PBYTE pb; IPV4_MZAP_HEADER *mh; IPV4_ZAM_HEADER *zam; BOOL bFound, bFromInside = FALSE; PSCOPE_ENTRY pScope, pOverlap; BOUNDARY_ENTRY *pBoundary = NULL; ULONG ulIdx; PBOUNDARY_IF pBIf; mh = (PIPV4_MZAP_HEADER)pBuffer; // Set pb to end of MZAP header pb = pBuffer + sizeof(IPV4_MZAP_HEADER); for (ulIdx=0; ulIdx < mh->byNameCount; ulIdx++) { // skip flags pb ++; // skip language tag len and str pb += (1 + *pb); // skip scope name len and str pb += (1 + *pb); } // Note that casting to a ULONG is safe, since we only care about // the low-order bits anyway. while (((ULONG_PTR)pb) & 3) *pb++ = '\0'; zam = (PIPV4_ZAM_HEADER)pb; { // Find matching scope entry pScope = FindScope( mh->ipScopeStart, ~(mh->ipScopeEnd - mh->ipScopeStart) ); if (pScope) { pBoundary = (pInBIf)? FindBoundaryEntry(pInBIf, pScope) : NULL; if (pBoundary) { // ZAM arrived from "outside" // // If ZAM is for a scope we're inside, but was received over a // boundary, signal a leaky scope warning. // if (mh->ipScopeZoneID == MyScopeZoneID(pScope)) { ReportLeakyScope(pScope, mh, zam); } // If the previous Local Zone ID was given, update our // local copy. if ( zam->ipAddress[ zam->bZT * 2 ] ) { pInBIf->ipOtherLocalZoneID = zam->ipAddress[ zam->bZT * 2 ]; } // // If ZAM was received on an interface with a boundary for the // given scope, drop it. // return; } else { // ZAM arrived from "inside" bFromInside = TRUE; // Make sure we know about the origin as a neighbor AssertZBR(pScope, mh->ipMessageOrigin, zam->wHoldTime); // // If a ZAM was received from within the zone, then the // Zone ID should match. Persistent mismatches are evidence // of a leaky Local Scope. // if (mh->ipScopeZoneID != MyScopeZoneID(pScope)) { // // Display origin and scope info, warn about // possible leaky local scope. // MakeAddressStringW(g_AddrBuf1, mh->ipMessageOrigin); MakeAddressStringW(g_AddrBuf2, mh->ipScopeStart); Trace2( ERR, "WARNING: Possible leaky Local Scope detected between this machine and %ls, boundary exists for %ls.", g_AddrBuf1, g_AddrBuf2 ); RouterLogEventExW( LOGHANDLE, EVENTLOG_WARNING_TYPE, 0, ROUTERLOG_IP_POSSIBLE_LEAKY_SCOPE, L"%S%S", g_AddrBuf1, g_AddrBuf2 ); } // See if scope names don't match CheckForScopeNameMismatch(pScope, mh); } // If last local zone ID is 0, but we know a zone ID, fill it in. if ( ! zam->ipAddress[ zam->bZT * 2 ] ) { if (pBoundary) zam->ipAddress[ zam->bZT*2 ] = pInBIf->ipOtherLocalZoneID; else zam->ipAddress[ zam->bZT*2 ] = MyScopeZoneID(pScope); } } else { // // Check for conflicting address ranges. A scope conflicts // if any locally-configured scope's range overlaps that in the ZAM. // CheckForScopeRangeMismatch(mh); } } // Check ZAM cache. If found, drop new ZAM. AssertInZamCache(mh->ipScopeZoneID, mh->ipScopeStart, &bFound); Trace3(MCAST, "ZAM Cache check for %d.%d.%d.%d, %d.%d.%d.%d is %d", PRINT_IPADDR(mh->ipScopeZoneID), PRINT_IPADDR( mh->ipScopeStart), bFound); if (bFound) { #ifdef SECURE_MZAP // If cached ZAM wasn't authenticated, and this one is, // then go ahead and forward it. XXX #endif return; } // If it's from outside, see if our Local Zone ID is already in // the path list. If so, drop it. if (!bFromInside) { if (ZamIncludesZoneID(zam, g_ipMyLocalZoneID)) return; } // Update Zones travelled, and drop if we've reached the limit zam->bZT++; if (zam->bZT >= zam->bZTL) { PBYTE pBufferDup; ZLE_PENDING *zle; LARGE_INTEGER liCurrentTime, liExpiryTime; double x,c,t; ENTER_WRITER(MZAP_TIMER); ENTER_WRITER(ZLE_LIST); { // See if one is already scheduled if (FindPendingZLE(mh)) { EXIT_LOCK(ZLE_LIST); EXIT_LOCK(MZAP_TIMER); return; } // Schedule a ZLE message x = UniformRandom01(); c = 256.0; t = ZLE_SUPPRESSION_INTERVAL * log(c*x+1) / log(c); // Duplicate the message pBufferDup = MALLOC( ulBuffLen ); if (!pBufferDup) { EXIT_LOCK(ZLE_LIST); EXIT_LOCK(MZAP_TIMER); return; } memcpy(pBufferDup, pBuffer, ulBuffLen); NtQuerySystemTime(&liCurrentTime); liExpiryTime = RtlLargeIntegerAdd(liCurrentTime, RtlConvertUlongToLargeInteger(TM_SECONDS((ULONG)floor(t+0.5)))); zle = AddPendingZLE(pBufferDup, ulBuffLen, liExpiryTime); } EXIT_LOCK(ZLE_LIST); UpdateMzapTimer(); EXIT_LOCK(MZAP_TIMER); return; } // Add our address ulBuffLen += 2*sizeof(IPV4_ADDRESS); zam->ipAddress[ zam->bZT*2 - 1 ] = g_ipMyAddress; // If from outside, inject inside if ( !bFromInside ) { zam->ipAddress[ zam->bZT*2 ] = g_ipMyLocalZoneID; #ifdef DEBUG_MZAP Trace0(ERR, "Relaying ZAM inside..."); #endif SendMZAPMessage( pBuffer, ulBuffLen, MZAP_LOCAL_GROUP, g_ipMyAddress ); } // // Re-originate on all interfaces with boundaries // (skipping the arrival interface, if it has a boundary) // We don't need to hold the lock on the BOUNDARY_TABLE from // the first pass above, since it doesn't matter whether the // boundaries change in between. // ENTER_READER(BOUNDARY_TABLE); { PLIST_ENTRY pleNode; DWORD dwBucketIdx; for (dwBucketIdx = 0; dwBucketIdx < BOUNDARY_HASH_TABLE_SIZE; dwBucketIdx++) { for (pleNode = g_bbScopeTable[dwBucketIdx].leInterfaceList.Flink; pleNode isnot & g_bbScopeTable[dwBucketIdx].leInterfaceList; pleNode = pleNode->Flink) { pBIf = CONTAINING_RECORD( pleNode, BOUNDARY_IF, leBoundaryIfLink ); if ( pBIf == pInBIf ) continue; if (FindBoundaryEntry(pBIf, pScope)) { #ifdef DEBUG_MZAP Trace1(ERR, "NOT relaying ZAM on IF %d due to boundary", pBIf->dwIfIndex ); #endif continue; } // If other local zone ID is already in the path, // skip it. if (pBIf->ipOtherLocalZoneID && ZamIncludesZoneID(zam, pBIf->ipOtherLocalZoneID)) continue; zam->ipAddress[ zam->bZT*2 ] = pBIf->ipOtherLocalZoneID; #ifdef DEBUG_MZAP Trace0(ERR, "Relaying ZAM outside by index..."); #endif SendMZAPMessageByIndex( pBuffer, ulBuffLen, MZAP_LOCAL_GROUP, pBIf->dwIfIndex ); } } } EXIT_LOCK(BOUNDARY_TABLE); } void HandleZCM( IN PBYTE pBuffer, // IN: Buffer holding ZAM received IN ULONG ulBuffLen, // IN: Length of ZAM message IN PBOUNDARY_IF pInBIf // IN: Interface on which the message arrived, // or NULL if from "inside" ) /*++ Called by: HandleMZAPSocket() Locks: BOUNDARY_TABLE for reading --*/ { PBYTE pb; IPV4_MZAP_HEADER *mh = (PIPV4_MZAP_HEADER)pBuffer; IPV4_ZCM_HEADER *zcm; PSCOPE_ENTRY pScope; ULONG i; BOOL bRouteFound; // Set pb to end of MZAP header pb = pBuffer + sizeof(IPV4_MZAP_HEADER); for (i=0; i < mh->byNameCount; i++) { // skip flags pb ++; // skip language tag len and str pb += (1 + *pb); // skip scope name len and str pb += (1 + *pb); } // // Note that casting to a ULONG is safe, since we only care about // the low-order bits anyway. // while (((ULONG_PTR)pb) & 3) *pb++ = '\0'; zcm = (PIPV4_ZCM_HEADER)pb; ENTER_READER(BOUNDARY_TABLE); { // Find matching scope entry if (mh->ipScopeStart == IPV4_LOCAL_SCOPE_ADDR && ~(mh->ipScopeEnd - mh->ipScopeStart) == IPV4_LOCAL_SCOPE_MASK) { pScope = &g_LocalScope; } else { pScope = FindScope( mh->ipScopeStart, ~(mh->ipScopeEnd - mh->ipScopeStart) ); } if (pScope) { PBOUNDARY_IF pBIf; PBOUNDARY_ENTRY pBoundary; pBoundary = (pInBIf)? FindBoundaryEntry(pInBIf, pScope) : NULL; if (pBoundary) { // ZCM arrived from "outside" // // If ZCM was received on an interface with a boundary for the // given scope, drop it. // EXIT_LOCK(BOUNDARY_TABLE); return; } else { // ZCM arrived from "inside" #ifdef HAVE_RTMV2 RTM_NET_ADDRESS naZBR; RTM_DEST_INFO rdi; PRTM_ROUTE_INFO pri; RTM_NEXTHOP_INFO nhi; ULONG ulIdx; #endif // Make sure we know about the origin as a neighbor AssertZBR(pScope, mh->ipMessageOrigin, zcm->wHoldTime); #ifdef HAVE_RTMV2 // // If multicast RIB route to any router address included // is over a boundary for the given scope, signal // non-convexity warning. // pri = HeapAlloc( IPRouterHeap, 0, RTM_SIZE_OF_ROUTE_INFO(g_rtmProfile.MaxNextHopsInRoute) ); if (pri == NULL) { EXIT_LOCK(BOUNDARY_TABLE); return; } for (i = 0; i < zcm->bZNUM; i++) { RTM_IPV4_MAKE_NET_ADDRESS(&naZBR, zcm->ipZBR[i], 32); // Look up route in multicast RIB if ( RtmGetMostSpecificDestination( g_hLocalRoute, &naZBR, RTM_BEST_PROTOCOL, RTM_VIEW_MASK_MCAST, &rdi ) isnot NO_ERROR ) { continue; } // // See if next hop interface has a boundary for the // ZCM group // ASSERT(rdi.ViewInfo[0].ViewId == RTM_VIEW_ID_MCAST); if ( RtmGetRouteInfo( g_hLocalRoute, rdi.ViewInfo[0].Route, pri, NULL ) is NO_ERROR ) { for (ulIdx = 0; ulIdx < pri->NextHopsList.NumNextHops; ulIdx++) { if ( RtmGetNextHopInfo( g_hLocalRoute, pri->NextHopsList.NextHops[ulIdx], &nhi ) is NO_ERROR ) { if ( RmHasBoundary( nhi.InterfaceIndex, MzapRelativeGroup(pScope) )) { MakeAddressStringW(g_AddrBuf1, mh->ipMessageOrigin); Trace2( ERR, "ERROR: non-convex scope zone for '%ls', router %ls", GetDefaultName(pScope), g_AddrBuf1 ); RouterLogEventExW( LOGHANDLE, EVENTLOG_ERROR_TYPE, 0, ROUTERLOG_NONCONVEX_SCOPE_ZONE, L"%S%S", GetDefaultName(pScope), g_AddrBuf1 ); } RtmReleaseNextHopInfo( g_hLocalRoute, &nhi); } } RtmReleaseRouteInfo( g_hLocalRoute, pri ); } RtmReleaseDestInfo(g_hLocalRoute, &rdi); } HeapFree(IPRouterHeap, 0, pri); #endif /* HAVE_RTMV2 */ // See if scope names don't match CheckForScopeNameMismatch(pScope, mh); } } else { // // Check for conflicting address ranges. A scope conflicts // if any locally-configured scope's range overlaps that in the ZAM. // CheckForScopeRangeMismatch(mh); } } EXIT_LOCK(BOUNDARY_TABLE); } void HandleZLE( IN PBYTE pBuffer, IN ULONG ulBuffLen ) /*++ Called by: HandleMZAPSocket() Locks: BOUNDARY_TABLE for reading ZLE_LIST for writing --*/ { PBYTE pb; IPV4_MZAP_HEADER *mh; IPV4_ZAM_HEADER *zam; PSCOPE_ENTRY pScope; ZLE_PENDING *zle; ULONG ulIdx; mh = (PIPV4_MZAP_HEADER)pBuffer; // Set pb to end of MZAP header pb = pBuffer + sizeof(IPV4_MZAP_HEADER); for (ulIdx=0; ulIdx < mh->byNameCount; ulIdx++) { // skip flags pb ++; // skip language tag len and str pb += (1 + *pb); // skip scope name len and ptr pb += (1 + *pb); } // // Note that casting to a ULONG is safe, since we only care about // the low-order bits anyway. // while (((ULONG_PTR)pb) & 3) *pb++ = '\0'; zam = (PIPV4_ZAM_HEADER)pb; ENTER_READER(BOUNDARY_TABLE); { // Find matching scope entry pScope = FindScope( mh->ipScopeStart, ~(mh->ipScopeEnd - mh->ipScopeStart) ); // // ZLE's are multicast. If we are the "Message Origin", signal a // leaky scope warning. Display addresses of routers in the path list. // if (mh->ipMessageOrigin == g_ipMyAddress) { ReportLeakyScope(pScope, mh, zam); EXIT_LOCK(BOUNDARY_TABLE); return; } } EXIT_LOCK(BOUNDARY_TABLE); // Otherwise, abort any pending ZLE which matches the one received ENTER_WRITER(ZLE_LIST); { if ((zle = FindPendingZLE(mh)) isnot NULL) { DeletePendingZLE(zle); } } EXIT_LOCK(ZLE_LIST); } VOID HandleMZAPSocket( PBOUNDARY_IF pBIf, SOCKET s ) /*++ Description: Receive an MZAP message on a socket s, and dispatch it to the appropriate function. Called by: HandleMZAPMessages() Locks: Assumes caller holds read lock on BOUNDARY_TABLE if pBIf is non-NULL --*/ { IPV4_MZAP_HEADER *mh; DWORD dwErr, dwNumBytes, dwFlags, dwAddrLen, dwSizeOfHeader; DWORD dwDataLen; SOCKADDR_IN sinFrom; WSANETWORKEVENTS wsaNetworkEvents; if (s is INVALID_SOCKET) return; if (WSAEnumNetworkEvents( s, NULL, &wsaNetworkEvents) is SOCKET_ERROR) { dwErr = GetLastError(); Trace1(ERR, "HandleMZAPMessages: WSAEnumNetworkEvents() returned %d", dwErr); return; } if (!(wsaNetworkEvents.lNetworkEvents & FD_READ)) { return; } if (wsaNetworkEvents.iErrorCode[FD_READ_BIT] isnot NO_ERROR) { Trace1( ERR, "HandleMZAPMessages: Error %d on FD_READ", wsaNetworkEvents.iErrorCode[FD_READ_BIT] ); return; } // // read the incoming packet. If the buffer isn't big enough, // WSAEMSGSIZE will be returned, and we'll ignore the message. // We don't currently expect this will ever happen. // dwAddrLen = sizeof(sinFrom); dwFlags = 0; dwErr = WSARecvFrom( s, &g_wsaMcRcvBuf, 1, &dwNumBytes, &dwFlags, (SOCKADDR FAR *)&sinFrom, &dwAddrLen, NULL, NULL ); // // check if any error in reading packet // if ((dwErr!=0) || (dwNumBytes==0)) { dwErr = WSAGetLastError(); Trace1( MCAST, "HandleMZAPSocket: Error %d receiving MZAP message", dwErr); // LogErr1(RECVFROM_FAILED, lpszAddr, dwErr); return; } mh = (PIPV4_MZAP_HEADER)g_wsaMcRcvBuf.buf; if (mh->byVersion isnot MZAP_VERSION) return; #ifdef DEBUG_MZAP Trace4( MCAST, "HandleMZAPSocket: received type %x len %d IF %x from %d.%d.%d.%d", mh->byBPType, dwNumBytes, ((pBIf)? pBIf->dwIfIndex : 0), PRINT_IPADDR(mh->ipMessageOrigin) ); #endif switch(mh->byBPType & ~MZAP_BIG_BIT) { case PTYPE_ZAM: HandleZAM(g_wsaMcRcvBuf.buf, dwNumBytes, pBIf); break; case PTYPE_ZLE: HandleZLE(g_wsaMcRcvBuf.buf, dwNumBytes); break; case PTYPE_ZCM: HandleZCM(g_wsaMcRcvBuf.buf, dwNumBytes, pBIf); break; } return; } VOID HandleMZAPMessages() /*++ Called by: WorkerThread() in worker.c Locks: BOUNDARY_TABLE for reading --*/ { DWORD dwBucketIdx; PLIST_ENTRY pleNode; TraceEnter("HandleMZAPMessages"); ENTER_READER(BOUNDARY_TABLE); { // Check local socket HandleMZAPSocket(NULL, g_mzapLocalSocket); // Loop through all BIf entries... for (dwBucketIdx = 0; dwBucketIdx < BOUNDARY_HASH_TABLE_SIZE; dwBucketIdx++) { for (pleNode = g_bbScopeTable[dwBucketIdx].leInterfaceList.Flink; pleNode isnot & g_bbScopeTable[dwBucketIdx].leInterfaceList; pleNode = pleNode->Flink) { PBOUNDARY_IF pBIf = CONTAINING_RECORD( pleNode, BOUNDARY_IF, leBoundaryIfLink ); HandleMZAPSocket(pBIf, pBIf->sMzapSocket); } } } EXIT_LOCK(BOUNDARY_TABLE); TraceLeave("HandleMZAPMessages"); } ////////////////////////////////////////////////////////////////////////////// // Functions for timer events ////////////////////////////////////////////////////////////////////////////// DWORD UpdateMzapTimer() /*++ Called by: AddZBR(), HandleZAM(), HandleMzapTimer() Locks: Assumes caller has write lock on MZAP_TIMER --*/ { DWORD dwErr = NO_ERROR; LARGE_INTEGER liExpiryTime; PLIST_ENTRY pleNode; TraceEnter("UpdateMzapTimer"); // // Expiry time of next ZAM/ZCM advertisement is already in // g_liZamExpiryTime // liExpiryTime = g_liZamExpiryTime; // // Get expiry time of first ZBR // if (!IsListEmpty( &g_zbrTimerList )) { ZBR_ENTRY *pZbr; pleNode = g_zbrTimerList.Flink; pZbr = CONTAINING_RECORD(pleNode, ZBR_ENTRY, leTimerLink); if (RtlLargeIntegerLessThan(pZbr->liExpiryTime, liExpiryTime)) { liExpiryTime = pZbr->liExpiryTime; } } // // Get expiry time of first ZLE // if (!IsListEmpty( &g_zleTimerList )) { ZLE_PENDING *zle; pleNode = g_zleTimerList.Flink; zle = CONTAINING_RECORD(pleNode, ZLE_PENDING, leTimerLink); if (RtlLargeIntegerLessThan(zle->liExpiryTime, liExpiryTime)) { liExpiryTime = zle->liExpiryTime; } } // // Reset the event timer // if (!SetWaitableTimer( g_hMzapTimer, &liExpiryTime, 0, NULL, NULL, FALSE )) { dwErr = GetLastError(); Trace1( ERR, "UpdateMzapTimer: Error %d setting timer", dwErr ); } TraceLeave("UpdateMzapTimer"); return dwErr; } VOID HandleMzapTimer( VOID ) /*++ Description: Process all events which are now due Locks: MZAP_TIMER and then ZBR_LIST for writing --*/ { LARGE_INTEGER liCurrentTime; PLIST_ENTRY pleNode; BOOL bDidSomething; TraceEnter("HandleMzapTimer"); ENTER_WRITER(MZAP_TIMER); do { bDidSomething = FALSE; NtQuerySystemTime(&liCurrentTime); // // Process timing out ZBRs if due // ENTER_WRITER(ZBR_LIST); { for ( pleNode = g_zbrTimerList.Flink; pleNode isnot &g_zbrTimerList; pleNode = g_zbrTimerList.Flink) { ZBR_ENTRY *pZbr; pZbr = CONTAINING_RECORD(pleNode, ZBR_ENTRY, leTimerLink); if (RtlLargeIntegerLessThan(liCurrentTime, pZbr->liExpiryTime)) break; DeleteZBR(pZbr); bDidSomething = TRUE; } } EXIT_LOCK(ZBR_LIST); // // Process sending ZAM/ZCMs if due // if (RtlLargeIntegerGreaterThanOrEqualTo(liCurrentTime, g_liZamExpiryTime)) { SendAllZamsAndZcms(); bDidSomething = TRUE; } // // Process sending ZLEs if due // ENTER_WRITER(ZLE_LIST); { for ( pleNode = g_zleTimerList.Flink; pleNode isnot &g_zleTimerList; pleNode = g_zleTimerList.Flink) { ZLE_PENDING *zle; zle = CONTAINING_RECORD(pleNode, ZLE_PENDING, leTimerLink); if (RtlLargeIntegerLessThan(liCurrentTime, zle->liExpiryTime)) break; SendZLE( zle ); bDidSomething = TRUE; } } EXIT_LOCK(ZLE_LIST); } while (bDidSomething); // Reset the timer UpdateMzapTimer(); EXIT_LOCK(MZAP_TIMER); TraceLeave("HandleMzapTimer"); } ////////////////////////////////////////////////////////////////////////////// DWORD ActivateMZAP() /*++ Called by: StartMZAP(), BindBoundaryInterface() --*/ { DWORD dwErr = NO_ERROR; DWORD dwBucketIdx; PLIST_ENTRY pleNode; BOOL bOption; SOCKADDR_IN sinAddr; TraceEnter("ActivateMZAP"); g_ipMyLocalZoneID = g_ipMyAddress; MzapInitLocalScope(); // Start listening for MZAP messages g_mzapLocalSocket = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); if (g_mzapLocalSocket is INVALID_SOCKET) { dwErr = WSAGetLastError(); Trace1(ERR, "ActivateMZAP: error %d creating socket", dwErr); TraceLeave("ActivateMZAP"); return dwErr; } if (WSAEventSelect( g_mzapLocalSocket, g_hMzapSocketEvent, FD_READ) is SOCKET_ERROR) { dwErr = WSAGetLastError(); Trace1(ERR, "ActivateMZAP: WSAEventSelect failed for local socket, Err=%d", dwErr); closesocket( g_mzapLocalSocket ); g_mzapLocalSocket = INVALID_SOCKET; TraceLeave("ActivateMZAP"); return dwErr; } bOption = TRUE; if(setsockopt(g_mzapLocalSocket, SOL_SOCKET, SO_REUSEADDR, (const char FAR*)&bOption, sizeof(BOOL)) is SOCKET_ERROR) { Trace1(ERR, "ActivateMZAP: Couldn't set reuse option - continuing. Error %d", WSAGetLastError()); } // Bind to INADDR_ANY/MZAP_PORT to get ZLEs sinAddr.sin_family = AF_INET; sinAddr.sin_addr.s_addr = INADDR_ANY; sinAddr.sin_port = htons(MZAP_PORT); if (bind(g_mzapLocalSocket, (struct sockaddr*)&sinAddr, sizeof(sinAddr)) is SOCKET_ERROR) { dwErr = WSAGetLastError(); Trace2(ERR, "ActivateMZAP: error %d binding to port %d", dwErr, MZAP_PORT); TraceLeave("ActivateMZAP"); return dwErr; } // Set TTL to 255 if (McSetMulticastTtl( g_mzapLocalSocket, 255 ) is SOCKET_ERROR) { Trace1(ERR, "ActivateMZAP: Couldn't set TTL. Error %d", WSAGetLastError()); } ENTER_READER(BOUNDARY_TABLE); { // // Join MZAP_RELATIVE_GROUPs locally, to get ZCMs // for (pleNode = g_MasterScopeList.Flink; pleNode isnot &g_MasterScopeList; pleNode = pleNode->Flink) { SCOPE_ENTRY *pScope = CONTAINING_RECORD(pleNode, SCOPE_ENTRY, leScopeLink); if (McJoinGroup( g_mzapLocalSocket, MzapRelativeGroup(pScope), g_ipMyAddress ) is SOCKET_ERROR) { dwErr = WSAGetLastError(); Trace3( ERR, "Error %d joining %d.%d.%d.%d on %d.%d.%d.%d", dwErr, PRINT_IPADDR(MzapRelativeGroup(pScope)), PRINT_IPADDR(g_ipMyAddress) ); EXIT_LOCK(BOUNDARY_TABLE); TraceLeave("ActivateMZAP"); return dwErr; } } // // Join MZAP_LOCAL_GROUP in each local zone we connect to, to get ZAMs // if (McJoinGroup( g_mzapLocalSocket, MZAP_LOCAL_GROUP, g_ipMyAddress ) is SOCKET_ERROR) { dwErr = WSAGetLastError(); Trace3( ERR, "Error %d joining %d.%d.%d.%d on %d.%d.%d.%d", dwErr, PRINT_IPADDR(MZAP_LOCAL_GROUP), PRINT_IPADDR(g_ipMyAddress) ); EXIT_LOCK(BOUNDARY_TABLE); TraceLeave("ActivateMZAP"); return dwErr; } for (dwBucketIdx = 0; dwBucketIdx < BOUNDARY_HASH_TABLE_SIZE; dwBucketIdx++) { for (pleNode = g_bbScopeTable[dwBucketIdx].leInterfaceList.Flink; pleNode isnot & g_bbScopeTable[dwBucketIdx].leInterfaceList; pleNode = pleNode->Flink) { PBOUNDARY_IF pBIf = CONTAINING_RECORD( pleNode, BOUNDARY_IF, leBoundaryIfLink ); if ( pBIf->sMzapSocket is INVALID_SOCKET ) { // Interface is not yet active. The join will be // done at the time BindBoundaryInterface() is called continue; } if (McJoinGroupByIndex( pBIf->sMzapSocket, SOCK_DGRAM, MZAP_LOCAL_GROUP, pBIf->dwIfIndex ) is SOCKET_ERROR) { dwErr = WSAGetLastError(); Trace3( ERR, "Error %d joining %d.%d.%d.%d on IF %x", dwErr, PRINT_IPADDR(MZAP_LOCAL_GROUP), pBIf->dwIfIndex ); EXIT_LOCK(BOUNDARY_TABLE); TraceLeave("ActivateMZAP"); return dwErr; } } } } EXIT_LOCK(BOUNDARY_TABLE); // // Initialize timer used for sending messages // ENTER_WRITER(MZAP_TIMER); { LARGE_INTEGER liCurrentTime, liExpiryTime; NtQuerySystemTime( &liCurrentTime ); g_liZamExpiryTime = RtlLargeIntegerAdd( liCurrentTime, RtlConvertUlongToLargeInteger(TM_SECONDS(ZAM_STARTUP_DELAY)) ); UpdateMzapTimer(); } EXIT_LOCK(MZAP_TIMER); TraceLeave("ActivateMZAP"); return dwErr; } VOID UpdateLowestAddress( PIPV4_ADDRESS pIpAddr, PICB picb ) { ULONG ulIdx; for (ulIdx=0; ulIdxdwNumAddresses; ulIdx++) { if (IS_ROUTABLE(picb->pibBindings[ulIdx].dwAddress) && (!*pIpAddr || ntohl(picb->pibBindings[ulIdx].dwAddress) < ntohl(*pIpAddr))) { *pIpAddr = picb->pibBindings[ulIdx].dwAddress; } } } DWORD MzapActivateBIf( PBOUNDARY_IF pBIf ) /*++ Called by: AddBIfEntry(), BindBoundaryInterface() Locks: Assumes caller holds at least a read lock on BOUNDARY_TABLE --*/ { BOOL bOption; DWORD dwErr = NO_ERROR; pBIf->sMzapSocket = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); if ( pBIf->sMzapSocket is INVALID_SOCKET ) { dwErr = WSAGetLastError(); Trace1(ERR, "StartMZAP: error %d creating socket", dwErr); return dwErr; } if (setsockopt( pBIf->sMzapSocket, SOL_SOCKET, SO_REUSEADDR, (const char FAR*)&bOption, sizeof(BOOL)) is SOCKET_ERROR) { Trace1(ERR, "MzapInitBIf: Couldn't set reuse option - continuing. Error %d", WSAGetLastError()); } #if 1 { struct sockaddr_in sinAddr; // // WORKAROUND FOR BUG #222214: must bind before set TTL will work // sinAddr.sin_family = AF_INET; sinAddr.sin_addr.s_addr = INADDR_ANY; sinAddr.sin_port = htons(MZAP_PORT); if (bind( pBIf->sMzapSocket, (struct sockaddr*)&sinAddr, sizeof(sinAddr) ) is SOCKET_ERROR) { dwErr = WSAGetLastError(); Trace2( ERR, "StartMZAP: error %d binding boundary socket to port %d", dwErr, MZAP_PORT); return dwErr; } } #endif // Set TTL to 255 if (McSetMulticastTtl( pBIf->sMzapSocket, 255) is SOCKET_ERROR) { Trace1(ERR, "StartMZAP: Couldn't set TTL. Error %d", WSAGetLastError()); } if (WSAEventSelect( pBIf->sMzapSocket, g_hMzapSocketEvent, FD_READ) is SOCKET_ERROR) { dwErr = WSAGetLastError(); Trace1(ERR, "StartMZAP: WSAEventSelect failed for local socket, Err=%d", dwErr); closesocket( pBIf->sMzapSocket ); pBIf->sMzapSocket = INVALID_SOCKET; return dwErr; } if (g_bMzapStarted) { if (McJoinGroupByIndex( pBIf->sMzapSocket, SOCK_DGRAM, MZAP_LOCAL_GROUP, pBIf->dwIfIndex ) is SOCKET_ERROR) { dwErr = WSAGetLastError(); Trace3( ERR, "Error %d joining %d.%d.%d.%d on IF %x", dwErr, PRINT_IPADDR(MZAP_LOCAL_GROUP), pBIf->dwIfIndex ); } } return dwErr; } DWORD BindBoundaryInterface( PICB picb ) { DWORD dwErr = NO_ERROR; ULONG ulIdx; BOUNDARY_IF *pBif; TraceEnter("BindBoundaryInterface"); if (!g_bMzapStarted) return NO_ERROR; ENTER_READER(BOUNDARY_TABLE); { pBif = FindBIfEntry(picb->dwIfIndex); if ( ! g_ipMyAddress && ! pBif ) { UpdateLowestAddress(&g_ipMyAddress, picb); if (g_ipMyAddress) dwErr = ActivateMZAP(); } if ( pBif && (pBif->sMzapSocket is INVALID_SOCKET)) { dwErr = MzapActivateBIf(pBif ); } } EXIT_LOCK(BOUNDARY_TABLE); TraceLeave("BindBoundaryInterface"); return dwErr; } DWORD StartMZAP() /*++ Description: Initialize state and start running MZAP() Called by: SetScopeInfo() Locks: ICB_LIST for reading BOUNDARY_TABLE for reading --*/ { DWORD dwErr = NO_ERROR, dwBucketIdx; SOCKADDR_IN sinAddr; ULONG ulIdx; PLIST_ENTRY pleNode; PSCOPE_ENTRY pScope; BOOL bOption; if (g_bMzapStarted) return NO_ERROR; g_bMzapStarted = TRUE; // Initialize local data structures InitializeListHead( &g_leZamCache ); InitializeListHead( &g_leZleList ); InitializeListHead( &g_zbrTimerList ); InitializeListHead( &g_zleTimerList ); // // Set address to lowest routable IP address which has no boundary // configured on it. // ENTER_READER(ICB_LIST); { PICB picb; for (pleNode = ICBList.Flink; pleNode isnot &ICBList; pleNode = pleNode->Flink) { picb = CONTAINING_RECORD(pleNode, ICB, leIfLink); if (FindBIfEntry(picb->dwIfIndex)) continue; UpdateLowestAddress(&g_ipMyAddress, picb); } } EXIT_LOCK(ICB_LIST); if (!g_ipMyAddress) { Trace0(ERR, "StartMZAP: no IP address found in local scope"); return ERROR_NOT_SUPPORTED; } dwErr = ActivateMZAP(); return dwErr; } void StopMZAP() /*++ Called by: SetScopeInfo() --*/ { if (!g_bMzapStarted) return; g_bMzapStarted = FALSE; // Stop timer used for sending messages ENTER_WRITER(MZAP_TIMER); { CancelWaitableTimer(g_hMzapTimer); } EXIT_LOCK(MZAP_TIMER); // Stop listening for MZAP messages if (g_mzapLocalSocket isnot INVALID_SOCKET) { closesocket(g_mzapLocalSocket); g_mzapLocalSocket = INVALID_SOCKET; } // // Free up local data stores // Empty ZAM cache // ENTER_WRITER(ZAM_CACHE); UpdateZamCache(RtlConvertUlongToLargeInteger(0)); EXIT_LOCK(ZAM_CACHE); } VOID MzapInitScope( PSCOPE_ENTRY pScope ) /*++ Description: Initialize MZAP fields of a scope --*/ { pScope->ipMyZoneID = g_ipMyLocalZoneID; InitializeListHead(&pScope->leZBRList); pScope->bZTL = MZAP_DEFAULT_ZTL; pScope->ulNumInterfaces = 0; pScope->bDivisible = FALSE; } DWORD MzapInitBIf( PBOUNDARY_IF pBIf ) /*++ Description: Called when the first boundary is added to an interface, and we need to start up MZAP on it. MZAP may (if we add a boundary while the router is running) or may not (startup time) already be running at this point. Called by: AddBIfEntry() Locks: Assumes caller holds a write lock on BOUNDARY_TABLE --*/ { BOOL bOption; DWORD dwErr = NO_ERROR; pBIf->ipOtherLocalZoneID = 0; pBIf->sMzapSocket = INVALID_SOCKET; return dwErr; } VOID MzapUninitBIf( PBOUNDARY_IF pBIf ) /*++ Called by: --*/ { if ( pBIf->sMzapSocket isnot INVALID_SOCKET ) { closesocket( pBIf->sMzapSocket ); pBIf->sMzapSocket = INVALID_SOCKET; } }