/*++ 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