windows-nt/Source/XPSP1/NT/net/tcpip/driver/ipv4/ftrie.c
2020-09-26 16:20:57 +08:00

870 lines
23 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1997-2000 Microsoft Corporation
Module Name:
ftrie.c
Abstract:
This module contains routines that manipulate
an F-trie data stucture, that forms the fast
path in a fast IP route lookup implementation.
Author:
Chaitanya Kodeboyina (chaitk) 26-Nov-1997
Revision History:
--*/
#include "precomp.h"
#include "ftrie.h"
UINT
CALLCONV
InitFTrie(IN FTrie * pFTrie,
IN ULONG levels,
IN ULONG maxMemory)
/*++
Routine Description:
Initialises an F-trie. This should be done prior to
any other trie operations.
Arguments:
pFTrie - Pointer to the trie to be initialized
levels - Bitmap [ 32 bits ] of expanded levels
maxMemory - Limit on memory taken by the F-Trie
For example, levels = 0xF0F0F0F0 [8,16,24,32 bits]
means -> all prefixes are expanded to these levels
and only these trie levels have any dests at all
Num of Levels + 2 memory accesses are needed in the
worst case to get to the dest corresponding to a
prefix - Num of Levels + 1 accesses including the
zero level access, and 1 access to read the dest.
Return Value:
TRIE_SUCCESS or ERROR_TRIE_*
--*/
{
UINT prevLevel;
UINT currLevel;
UINT nBytes, i;
TRY_BLOCK
{
if (levels == 0) {
Error("NewFTrie: No levels specified", ERROR_TRIE_BAD_PARAM);
}
// Zero all the memory for the trie header
RtlZeroMemory(pFTrie, sizeof(FTrie));
// Set a limit on the memory for trie/nodes
pFTrie->availMemory = maxMemory;
// Initialize list of trienodes allocated
InitializeListHead(&pFTrie->listofNodes);
// Initialize root node with a NULL dest
pFTrie->trieRoot = StoreDestPtr(NULL);
// Initialize the number of bits in each level
nBytes = (MAXLEVEL + 1) * sizeof(UINT);
AllocMemory2(pFTrie->bitsInLevel,
nBytes,
pFTrie->availMemory);
RtlZeroMemory(pFTrie->bitsInLevel, nBytes);
// Get the number of index bits at each level
prevLevel = 0;
i = 0;
for (currLevel = 1; currLevel <= MAXLEVEL; currLevel++) {
if (levels & 1) {
pFTrie->bitsInLevel[i++] = currLevel - prevLevel;
prevLevel = currLevel;
}
levels >>= 1;
}
pFTrie->numLevels = i;
// Make sure that the last level is MAXLEVEL
if (pFTrie->bitsInLevel[i] = MAXLEVEL - prevLevel) {
pFTrie->numLevels++;
}
#if DBG
Print("Num of levels: %d\n", pFTrie->numLevels);
Print("Bits In Level:\n");
for (i = 0; i < pFTrie->numLevels; i++) {
Print("\t%d", pFTrie->bitsInLevel[i]);
if (i % 8 == 7)
Print("\n");
}
Print("\n\n");
#endif
// Allocate and Zero all the statistics variables
nBytes = (MAXLEVEL + 1) * sizeof(UINT);
AllocMemory2(pFTrie->numDestsInOrigLevel,
nBytes,
pFTrie->availMemory);
RtlZeroMemory(pFTrie->numDestsInOrigLevel, nBytes);
nBytes = pFTrie->numLevels * sizeof(UINT);
AllocMemory2(pFTrie->numNodesInExpnLevel,
nBytes,
pFTrie->availMemory);
RtlZeroMemory(pFTrie->numNodesInExpnLevel, nBytes);
nBytes = pFTrie->numLevels * sizeof(UINT);
AllocMemory2(pFTrie->numDestsInExpnLevel,
nBytes,
pFTrie->availMemory);
RtlZeroMemory(pFTrie->numDestsInExpnLevel, nBytes);
return TRIE_SUCCESS;
}
ERR_BLOCK
{
// Not enough resources to create an FTrie
CleanupFTrie(pFTrie);
}
END_BLOCK
}
UINT
CALLCONV
InsertIntoFTrie(IN FTrie * pFTrie,
IN Route * pInsRoute,
IN Dest * pInsDest,
IN Dest * pOldDest)
/*++
Routine Description:
Inserts a dest corresponding to an address
prefix into a F-trie. It actually replaces
all pointers to OldDest by that of InsDest.
Arguments:
pFTrie - Pointer to the F-Trie to insert into
pInsRoute - Pointer to best route on new dest
pInsDest - Pointer to the dest being inserted
pOldDest - Pointer to the dest being replaced
Return Value:
TRIE_SUCCESS or ERROR_TRIE_*
--*/
{
FTrieNode **ppCurrNode;
FTrieNode *pCurrNode;
Dest *pBestDest;
Dest *pComDest;
UINT startIndex;
UINT stopIndex;
UINT nextIndex;
UINT shiftIndex;
UINT addrBits;
UINT numBits;
UINT bitsLeft;
UINT i, j;
#if DBG
// Make sure the trie is initialized
if (!pFTrie || !pFTrie->trieRoot) {
Fatal("Insert Dest: FTrie not initialized",
ERROR_TRIE_NOT_INITED);
}
// Make sure input dest is valid
if (NULL_DEST(pInsDest)) {
Fatal("Insert Dest: NULL or invalid dest",
ERROR_TRIE_BAD_PARAM);
}
// Make sure input route is valid
if (NULL_ROUTE(pInsRoute)) {
Fatal("Insert Dest: NULL or invalid route",
ERROR_TRIE_BAD_PARAM);
}
if (LEN(pInsRoute) > ADDRSIZE) {
Fatal("Insert Dest: Invalid mask length",
ERROR_TRIE_BAD_PARAM);
}
#endif
Assert(pInsDest != pOldDest);
// Use addr bits to index the trie
addrBits = RtlConvertEndianLong(DEST(pInsRoute));
bitsLeft = LEN(pInsRoute);
#if DBG
// Make sure addr and mask agree
if (ShowMostSigNBits(addrBits, bitsLeft) != addrBits) {
Fatal("Insert Dest: Addr & mask don't agree",
ERROR_TRIE_BAD_PARAM);
}
#endif
TRY_BLOCK
{
// Special case: Default Prefix
if (LEN(pInsRoute) == 0) {
// Do we have a subtree in the trie's root node ?
if (IsPtrADestPtr(pFTrie->trieRoot)) {
// Make sure you are replacing right dest
Assert(pFTrie->trieRoot == StoreDestPtr(pOldDest));
// Make the root to point to the new default
pFTrie->trieRoot = StoreDestPtr(pInsDest);
} else {
// Make sure you are replacing right dest
Assert(pFTrie->trieRoot->comDest == pOldDest);
// Make new dest the common subtrie dest
pFTrie->trieRoot->comDest = pInsDest;
}
return TRIE_SUCCESS;
}
// Start going down the trie using addr bits
pBestDest = NULL;
ppCurrNode = &pFTrie->trieRoot;
for (i = 0; /* NOTHING */ ; i++) {
pCurrNode = *ppCurrNode;
if (IsPtrADestPtr(pCurrNode)) {
// Creating a new subtree for the current level
// This pointer actually points to a dest node
pComDest = RestoreDestPtr(pCurrNode);
// Create, initialize a new FTrie node (grow it)
NewFTrieNode(pFTrie,
pCurrNode,
pFTrie->bitsInLevel[i],
pComDest);
// Attach it to the FTrie
*ppCurrNode = pCurrNode;
// Update FTrie Statistics
pFTrie->numNodesInExpnLevel[i]++;
}
// Update the best dest seen so far - used later
pComDest = pCurrNode->comDest;
if (pComDest) {
pBestDest = pComDest;
}
// Increment the number of dests in this subtrie
pCurrNode->numDests++;
// Can I pass this level with remaining bits ?
if (bitsLeft <= pFTrie->bitsInLevel[i]) {
break;
}
// Get the next index from the IP addr
numBits = pCurrNode->numBits;
nextIndex = PickMostSigNBits(addrBits, numBits);
ppCurrNode = &pCurrNode->child[nextIndex];
// Throw away the used bits
addrBits <<= numBits;
bitsLeft -= numBits;
}
// Update FTrie stats before expanding
// Update if this isn't a dest change
pFTrie->numDestsInExpnLevel[i]++;
pFTrie->numDestsInOrigLevel[LEN(pInsRoute)]++;
// At this level, expand and add the dest
nextIndex = PickMostSigNBits(addrBits, bitsLeft);
shiftIndex = pFTrie->bitsInLevel[i] - bitsLeft;
startIndex = nextIndex << shiftIndex;
stopIndex = (nextIndex + 1) << shiftIndex;
// Have you seen the old dest already ?
if (pBestDest == pOldDest) {
pOldDest = NULL;
}
// These dests cannot be the same here
Assert(pInsDest != pOldDest);
// Fill the expanded range with the dest
for (i = startIndex; i < stopIndex; i++) {
if (IsPtrADestPtr(pCurrNode->child[i])) {
// A dest pointer - replace with new one
ReplaceDestPtr(StoreDestPtr(pInsDest),
StoreDestPtr(pOldDest),
&pCurrNode->child[i]);
} else {
// Node pointer - update subtree's dest
ReplaceDestPtr(pInsDest,
pOldDest,
&pCurrNode->child[i]->comDest);
}
}
return TRIE_SUCCESS;
}
ERR_BLOCK
{
// Not enough resources - rollback to original state
DeleteFromFTrie(pFTrie, pInsRoute, pInsDest, pOldDest, PARTIAL);
}
END_BLOCK
}
UINT
CALLCONV
DeleteFromFTrie(IN FTrie * pFTrie,
IN Route * pDelRoute,
IN Dest * pDelDest,
IN Dest * pNewDest,
IN BOOLEAN trieState)
/*++
Routine Description:
Deletes a dest corresponding to an address
prefix from a F-trie. It actually replaces
all pointers to DelDest by that of NewDest.
Arguments:
pFTrie - Pointer to the F-Trie to delete from
pDelRoute - Pointer to last route on old dest
pDelDest - Pointer to the dest being deleted
pNewDest - Pointer to the dest replacing above
trieState - NORMAL - deleting from a consistent FTrie
PARTIAL - cleaning up an incomplete insert
Return Value:
TRIE_SUCCESS or ERROR_TRIE_*
--*/
{
FTrieNode **ppCurrNode;
FTrieNode *pCurrNode;
FTrieNode *pPrevNode;
FTrieNode *pNextNode;
Dest *pBestDest;
Dest *pComDest;
UINT startIndex;
UINT stopIndex;
UINT nextIndex;
UINT shiftIndex;
UINT addrBits;
UINT numBits;
UINT bitsLeft;
UINT i, j;
#if DBG
// Make sure the trie is initialized
if (!pFTrie || !pFTrie->trieRoot) {
Fatal("Delete Dest: FTrie not initialized",
ERROR_TRIE_NOT_INITED);
}
// Make sure input dest is valid
if (NULL_DEST(pDelDest)) {
Fatal("Delete Dest: NULL or invalid dest",
ERROR_TRIE_BAD_PARAM);
}
// Make sure input route is valid
if (NULL_ROUTE(pDelRoute)) {
Fatal("Delete Dest: NULL or invalid route",
ERROR_TRIE_BAD_PARAM);
}
if (LEN(pDelRoute) > ADDRSIZE) {
Fatal("Delete Dest: Invalid mask length",
ERROR_TRIE_BAD_PARAM);
}
#endif
// Use addr bits to index the trie
addrBits = RtlConvertEndianLong(DEST(pDelRoute));
bitsLeft = LEN(pDelRoute);
#if DBG
// Make sure addr and mask agree
if (ShowMostSigNBits(addrBits, bitsLeft) != addrBits) {
Fatal("Delete Dest: Addr & mask don't agree",
ERROR_TRIE_BAD_PARAM);
}
#endif
Assert(pDelDest != pNewDest);
// Special case: Default Prefix
if (LEN(pDelRoute) == 0) {
// Do we have a subtree in the trie's root node ?
if (IsPtrADestPtr(pFTrie->trieRoot)) {
// Make sure you are replacing right dest
Assert(pFTrie->trieRoot == StoreDestPtr(pDelDest));
// Make the root to point to the new default
pFTrie->trieRoot = StoreDestPtr(pNewDest);
} else {
// Make sure you are replacing right dest
Assert(pFTrie->trieRoot->comDest == pDelDest);
// Make new dest the common subtrie dest
pFTrie->trieRoot->comDest = pNewDest;
}
return TRIE_SUCCESS;
}
// Start going down the trie using addr bits
pBestDest = NULL;
ppCurrNode = &pFTrie->trieRoot;
pPrevNode = pCurrNode = *ppCurrNode;
for (i = 0; /* NOTHING */ ; i++) {
// We still have bits left, so we go down the trie
// Do we have a valid subtree at the current node
if (IsPtrADestPtr(pCurrNode)) {
// We are cleaning a partial (failed) insert
Assert(trieState == PARTIAL);
// We have cleaned up the trie - return now
return TRIE_SUCCESS;
}
// We have a valid subtree, so we go down the trie
// Update the best dest seen so far - used later
pComDest = pCurrNode->comDest;
if (pComDest) {
pBestDest = pComDest;
}
// Decrement the number of dests in this subtrie
pCurrNode->numDests--;
// Is the number of dests in curr subtree zero ?
if (pCurrNode->numDests == 0) {
#if DBG
int k = 0;
// Just make sure that only one dest exists
for (j = 1; j < (UINT) 1 << pCurrNode->numBits; j++) {
if (pCurrNode->child[j - 1] != pCurrNode->child[j]) {
Assert((pCurrNode->child[j] == StoreDestPtr(NULL)) ||
(pCurrNode->child[j - 1] == StoreDestPtr(NULL)));
k++;
}
}
if (trieState == NORMAL) {
if ((k != 1) && (k != 2)) {
Print("k = %d\n", k);
Assert(FALSE);
}
} else {
if ((k != 0) && (k != 1) && (k != 2)) {
Print("k = %d\n", k);
Assert(FALSE);
}
}
#endif
// Remove link from its parent (if it exists)
if (pPrevNode) {
*ppCurrNode = StoreDestPtr(pCurrNode->comDest);
}
}
// Can I pass this level with remaining bits ?
if (bitsLeft <= pFTrie->bitsInLevel[i]) {
break;
}
// Get the next index from the IP addr
numBits = pCurrNode->numBits;
nextIndex = PickMostSigNBits(addrBits, numBits);
ppCurrNode = &pCurrNode->child[nextIndex];
pNextNode = *ppCurrNode;
// Throw away the used bits
addrBits <<= numBits;
bitsLeft -= numBits;
// Is the number of dests in subtree zero ?
if (pCurrNode->numDests == 0) {
// Deallocate it (shrink FTrie)
FreeFTrieNode(pFTrie, pCurrNode);
// Update FTrie Statistics
pFTrie->numNodesInExpnLevel[i]--;
}
pPrevNode = pCurrNode;
pCurrNode = pNextNode;
}
// Update F-Trie stats before deleting
pFTrie->numDestsInExpnLevel[i]--;
pFTrie->numDestsInOrigLevel[LEN(pDelRoute)]--;
// Is the number of dests in curr subtree zero ?
if (pCurrNode->numDests == 0) {
// Deallocate it (shrink FTrie)
FreeFTrieNode(pFTrie, pCurrNode);
// Update FTrie Statistics
pFTrie->numNodesInExpnLevel[i]--;
} else {
// At this level, expand and add the dest
nextIndex = PickMostSigNBits(addrBits, bitsLeft);
shiftIndex = pFTrie->bitsInLevel[i] - bitsLeft;
startIndex = nextIndex << shiftIndex;
stopIndex = (nextIndex + 1) << shiftIndex;
// Have you seen the new dest already ?
if (pBestDest == pNewDest) {
pNewDest = NULL;
}
// These dests cannot be the same here
Assert(pDelDest != pNewDest);
// Fill the expanded range with the dest
for (i = startIndex; i < stopIndex; i++) {
if (IsPtrADestPtr(pCurrNode->child[i])) {
// A dest pointer - replace with new one
ReplaceDestPtr(StoreDestPtr(pNewDest),
StoreDestPtr(pDelDest),
&pCurrNode->child[i]);
} else {
// Node pointer - update subtree's dest
ReplaceDestPtr(pNewDest,
pDelDest,
&pCurrNode->child[i]->comDest);
}
}
}
return TRIE_SUCCESS;
}
UINT
CALLCONV
SearchDestInFTrie(IN FTrie * pFTrie,
IN Dest * pSerDest,
OUT UINT * pNumPtrs,
OUT Dest ** pStartPtr)
/*++
Routine Description:
Search for a specific dest in an F-trie,
returns the expanded range for the dest
Arguments:
pFTrie - Pointer to the F-trie to search
pSerDest - Pointer to dest being searched
pStartPtr - Start of dest's expanded range
pNumPtrs - Number of pointers in the range
Return Value:
TRIE_SUCCESS or ERROR_TRIE_*
--*/
{
return ERROR_TRIE_BAD_PARAM;
}
Dest *
CALLCONV
SearchAddrInFTrie(IN FTrie * pFTrie,
IN ULONG Addr)
/*++
Routine Description:
Search for an address in an F-trie
Arguments:
pFTrie - Pointer to the trie to search
Addr - Pointer to addr being queried
Return Value:
Return best dest match for this address
--*/
{
FTrieNode *pCurrNode;
Dest *pBestDest;
Dest *pDest;
ULONG addrBits;
UINT numBits;
UINT nextIndex;
#if DBG
if (!pFTrie || !pFTrie->trieRoot) {
Fatal("Searching into an uninitialized FTrie",
ERROR_TRIE_NOT_INITED);
}
#endif
addrBits = RtlConvertEndianLong(Addr);
pBestDest = NULL;
pCurrNode = pFTrie->trieRoot;
do {
// Have we reached the end of this search ?
if (IsPtrADestPtr(pCurrNode)) {
// Get the best matching dest until now
pDest = RestoreDestPtr(pCurrNode);
if (!NULL_DEST(pDest)) {
pBestDest = pDest;
}
return pBestDest;
} else {
// Get the best matching dest until now
pDest = pCurrNode->comDest;
if (!NULL_DEST(pDest)) {
pBestDest = pDest;
}
}
// Number of bits to use in this FTrie level
numBits = pCurrNode->numBits;
// Get the next index from IP address bits
nextIndex = PickMostSigNBits(addrBits, numBits);
// And go down the tree with the new index
pCurrNode = pCurrNode->child[nextIndex];
// Throw away the used bits for this iteration
addrBits <<= numBits;
}
while (TRUE);
}
UINT
CALLCONV
CleanupFTrie(IN FTrie * pFTrie)
/*++
Routine Description:
Deletes an F-trie if it is empty
Arguments:
ppFTrie - Ptr to the F-trie
Return Value:
TRIE_SUCCESS or ERROR_TRIE_*
--*/
{
FTrieNode *pCurrNode;
LIST_ENTRY *p;
// Free all trie nodes and corresponding memory
while (!IsListEmpty(&pFTrie->listofNodes)) {
p = RemoveHeadList(&pFTrie->listofNodes);
pCurrNode = CONTAINING_RECORD(p, FTrieNode, linkage);
FreeFTrieNode(pFTrie, pCurrNode);
}
// Free the memory for the arr of levels
if (pFTrie->bitsInLevel) {
FreeMemory1(pFTrie->bitsInLevel,
(MAXLEVEL + 1) * sizeof(UINT),
pFTrie->availMemory);
}
// Free memory allocated for statistics
if (pFTrie->numDestsInOrigLevel) {
FreeMemory1(pFTrie->numDestsInOrigLevel,
(MAXLEVEL + 1) * sizeof(UINT),
pFTrie->availMemory);
}
if (pFTrie->numNodesInExpnLevel) {
FreeMemory1(pFTrie->numNodesInExpnLevel,
pFTrie->numLevels * sizeof(UINT),
pFTrie->availMemory);
}
if (pFTrie->numDestsInExpnLevel) {
FreeMemory1(pFTrie->numDestsInExpnLevel,
pFTrie->numLevels * sizeof(UINT),
pFTrie->availMemory);
}
// Reset other fields in trie structure
pFTrie->trieRoot = NULL;
pFTrie->numLevels = 0;
return TRIE_SUCCESS;
}
#if DBG
VOID
CALLCONV
PrintFTrie(IN FTrie * pFTrie,
IN UINT printFlags)
/*++
Routine Description:
Print an F-Trie
Arguments:
pFTrie - Pointer to the F-Trie
printFlags - Information to print
Return Value:
None
--*/
{
FTrieNode *pCurrNode;
UINT i;
if (pFTrie == NULL) {
Print("%s", "Uninitialized FTrie\n\n");
return;
}
if ((printFlags & SUMM) == SUMM) {
Print("\n\n/***Fast-Trie------------------------------------------------");
Print("\n/***---------------------------------------------------------\n");
}
if (printFlags & POOL) {
Print("Available Memory: %10lu\n\n", pFTrie->availMemory);
}
if (printFlags & STAT) {
Print("Statistics:\n\n");
Print("Num of levels: %d\n", pFTrie->numLevels);
Print("Bits In Level:\n");
for (i = 0; i < pFTrie->numLevels; i++) {
Print("\t%d", pFTrie->bitsInLevel[i]);
if (i % 8 == 7)
Print("\n");
}
Print("\n\n");
Print("Num of Nodes in Expanded Levels:\n");
for (i = 0; i < pFTrie->numLevels; i++) {
Print("\t%d", pFTrie->numNodesInExpnLevel[i]);
if (i % 8 == 7)
Print("\n");
}
Print("\n\n");
Print("Num of Dests in Original Levels:\n");
for (i = 0; i < MAXLEVEL + 1; i++) {
Print("\t%d", pFTrie->numDestsInOrigLevel[i]);
if (i % 8 == 0)
Print("\n");
}
Print("\n\n");
Print("Num of Dests in Expanded Levels:\n");
for (i = 0; i < pFTrie->numLevels; i++) {
Print("\t%d", pFTrie->numDestsInExpnLevel[i]);
if (i % 8 == 7)
Print("\n");
}
Print("\n\n");
}
if (printFlags & TRIE) {
if (!IsPtrADestPtr(pFTrie->trieRoot)) {
PrintFTrieNode(pFTrie->trieRoot, 0);
} else {
PrintDest(RestoreDestPtr(pFTrie->trieRoot));
}
}
if ((printFlags & SUMM) == SUMM) {
Print("\n---------------------------------------------------------***/\n");
Print("---------------------------------------------------------***/\n\n");
}
}
VOID
CALLCONV
PrintFTrieNode(IN FTrieNode * pFTrieNode,
IN UINT levelNumber)
/*++
Routine Description:
Print an F-Trie node
Arguments:
pFTrieNode - Pointer to the FTrie node
Return Value:
None
--*/
{
FTrieNode *pCurrNode;
UINT numElmts;
UINT i, j;
Print("\n/*-----------------------------------------------------------\n");
Print("Num of bits at level %3d : %d\n", levelNumber, pFTrieNode->numBits);
Print("Number of Subtrie Dests : %d\n", pFTrieNode->numDests);
Print("Common SubTree Dest : ");
PrintDest(pFTrieNode->comDest);
Print("\n");
numElmts = 1 << pFTrieNode->numBits;
pCurrNode = StoreDestPtr(NULL);
Print("Child Ptrs:\n\n");
for (i = 0; i < numElmts; i++) {
if (pFTrieNode->child[i] != pCurrNode) {
pCurrNode = pFTrieNode->child[i];
Print("Child Index: %8lu ", i);
if (IsPtrADestPtr(pCurrNode)) {
PrintDest(RestoreDestPtr(pCurrNode));
} else {
PrintFTrieNode(pCurrNode, levelNumber + 1);
}
}
}
Print("-----------------------------------------------------------*/\n\n");
}
#endif // DBG