windows-nt/Source/XPSP1/NT/net/rras/ip/rtrmgr/mbound.c

6065 lines
155 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
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 <math.h> // 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; dwScopeIndex<MAX_SCOPES; dwScopeIndex++)
{
if ( !g_scopeEntry[dwScopeIndex].ipGroupAddress )
{
return &g_scopeEntry[ dwScopeIndex ];
}
}
return NULL;
}
PSCOPE_ENTRY
FindScope(
IN IPV4_ADDRESS ipGroupAddress,
IN IPV4_ADDRESS ipGroupMask
)
/*++
Called by:
AssertScope(), RmGetBoundary()
Locks:
Assumes caller holds write lock on BOUNDARY_TABLE.
--*/
{
PLIST_ENTRY pleNode;
for (pleNode = g_MasterScopeList.Flink;
pleNode isnot &g_MasterScopeList;
pleNode = pleNode->Flink)
{
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 <ipFirst,ipLast> 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; i<BOUNDARY_HASH_TABLE_SIZE; i++) {
for (pleNode = g_bbScopeTable[i].leInterfaceList.Flink;
pleNode isnot &g_bbScopeTable[i].leInterfaceList;
pleNode = pleNode->Flink) {
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<ulNumScopes; i++)
{
// Copy group address, and mask
dwLen = 2 * sizeof(IPV4_ADDRESS);
CopyMemory(&pScopes[i].ipGroupAddress, pBuffer, dwLen);
pBuffer += dwLen;
// Get flags
CopyMemory(&dwFlags, pBuffer, sizeof(DWORD));
pBuffer += sizeof(DWORD);
pScopes[i].bDivisible = dwFlags;
CopyMemory(&dwNumNames, pBuffer, sizeof(DWORD));
pBuffer += sizeof(DWORD);
pScopes[i].ulNumInterfaces = 0; // this value is ignored
pScopes[i].ulNumNames = 0;
InitializeListHead( &pScopes[i].leNameList );
for (j=0; j<dwNumNames; j++)
{
// Set language name
CopyMemory(&dwLanguage, pBuffer, sizeof(dwLanguage));
pBuffer += sizeof(dwLanguage);
// Get scope name length
CopyMemory(&dwLen, pBuffer, sizeof(DWORD));
pBuffer += sizeof(DWORD);
if (dwLen > 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; i<dwNumScopes; i++)
{
while (!IsListEmpty(&pScopes[i].leNameList))
{
DeleteScopeName( pScopes[i].leNameList.Flink );
}
}
FREE(pScopes);
}
DWORD
SetScopeInfo(
PRTR_INFO_BLOCK_HEADER pInfoHdr
)
/*++
Routine Description:
Sets the scope info associated with the router.
First we add the scopes present in the scope info. Then we
enumerate the scopes and delete those that we don't find in the
scope info.
Locks:
BOUNDARY_TABLE for writing
Called by:
InitRouter() in init.c
SetGlobalInfo() in iprtrmgr.c
--*/
{
DWORD dwResult = NO_ERROR;
DWORD dwNumScopes, i, j;
PRTR_TOC_ENTRY pToc;
SCOPE_ENTRY *pScopes;
BOOL bFound;
SCOPE_ENTRY *pScope;
BYTE *pBuffer;
LIST_ENTRY leOldIfRanges, leNewIfRanges;
PSCOPE_NAME_ENTRY pName;
PLIST_ENTRY pleNode;
Trace0( MCAST, "ENTERED SetScopeInfo" );
pToc = GetPointerToTocEntry(IP_MCAST_BOUNDARY_INFO, pInfoHdr);
if (pToc is NULL) {
// No TOC means no change
Trace0( MCAST, "LEFT SetScopeInfo" );
return NO_ERROR;
}
//
// 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.
//
dwResult = ConvertTableToRanges(&leOldIfRanges);
if (dwResult isnot NO_ERROR) {
return dwResult;
}
if (pToc->InfoSize is 0)
{
StopMZAP();
// delete all scopes
ENTER_WRITER(BOUNDARY_TABLE);
{
for (i=0; i<MAX_SCOPES; i++)
DeleteScope(&g_scopeEntry[i]);
}
EXIT_LOCK(BOUNDARY_TABLE);
// Inform MGM of deltas
dwResult = ConvertTableToRanges(&leNewIfRanges);
if (dwResult isnot NO_ERROR)
{
return dwResult;
}
ProcessRangeDeltas(&leOldIfRanges, &leNewIfRanges);
Trace0( MCAST, "LEFT SetScopeInfo" );
return NO_ERROR;
}
pBuffer = (PBYTE)GetInfoFromTocEntry(pInfoHdr, pToc);
if (pBuffer is NULL)
{
return ERROR_INSUFFICIENT_BUFFER;
}
// Scope count is stored in first DWORD
dwNumScopes = *((PDWORD) pBuffer);
pBuffer += sizeof(DWORD);
ParseScopeInfo(pBuffer, dwNumScopes, &pScopes);
ENTER_WRITER(BOUNDARY_TABLE);
{
//
// Add all the new scopes
//
for (i=0; i<dwNumScopes; i++)
{
dwResult = AssertScope( pScopes[i].ipGroupAddress,
pScopes[i].ipGroupMask,
&pScope );
if (!pScope)
{
Trace2( MCAST,
"Bad scope prefix %d.%d.%d.%d/%d.%d.%d.%d",
PRINT_IPADDR(pScopes[i].ipGroupAddress),
PRINT_IPADDR(pScopes[i].ipGroupMask) );
continue;
}
pScope->bDivisible = 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; i<MAX_SCOPES; i++)
{
pScope = &g_scopeEntry[i];
if (pScope->ipGroupAddress == 0)
continue; // not active
bFound = FALSE;
for (j=0; j<dwNumScopes; j++)
{
if (pScopes[j].ipGroupAddress == pScope->ipGroupAddress
&& 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; i<dwNumBoundaries; i++)
{
dwResult = AssertScope( pBoundaries[i].dwGroupAddress,
pBoundaries[i].dwGroupMask,
&pScope );
if (pScope)
{
dwResult = AssertBoundaryEntry( pBoundaryIf,
pScope,
&pBoundary);
}
}
//
// Now enumerate the boundaries, deleting the boundaries that are
// not in the new list.
//
for (pleNode = pBoundaryIf->leBoundaryList.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; j<dwNumBoundaries; j++)
{
if (pBoundaries[j].dwGroupAddress == pScope->ipGroupAddress
&& 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; i<BOUNDARY_HASH_TABLE_SIZE; i++)
InitializeListHead(&g_bbScopeTable[i].leInterfaceList);
InitializeListHead(&g_MasterInterfaceList);
InitializeListHead(&g_MasterScopeList);
ZeroMemory( g_scopeEntry, MAX_SCOPES * sizeof(SCOPE_ENTRY) );
}
EXIT_LOCK(BOUNDARY_TABLE);
}
//////////////////////////////////////////////////////////////////////////////
// START OF MZAP ROUTINES
//////////////////////////////////////////////////////////////////////////////
// Notes on MZAP write lock dependencies:
// ZAM_CACHE - no dependencies
// MZAP_TIMER - no dependencies
// ZBR_LIST - lock BOUNDARY_ENTRY and MZAP_TIMER before ZBR_LIST
// ZLE_LIST - lock MZAP_TIMER before ZLE_LIST
#define TOP_OF_SCOPE(pScope) \
((pScope)->ipGroupAddress | ~(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 <packon.h>
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 <packoff.h>
//////////////////////////////////////////////////////////////////////////////
// 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; i<mh->byNameCount; 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; ulIdx<picb->dwNumAddresses; 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;
}
}