/*++ Copyright (c) 1997 Microsoft Corporation Module Name: trie.c Abstract: Contains wrapper routines around the fast & slow IP route lookup schemes Author: Chaitanya Kodeboyina (chaitk) 26-Nov-1997 Revision History: --*/ #include "precomp.h" #include "strie.h" #include "ftrie.h" UINT CreateTrie(IN ULONG levels, IN ULONG flags, IN ULONG maxSTMemory, IN ULONG maxFTMemory, OUT Trie ** ppTrie) /*++ Routine Description: Initializes S-Trie(Slow Trie) and F-Trie(Fast Trie) components in the trie [ wrapper structure ]. The Slow Trie component keeps all the routes, while the Fast Trie keeps only a pointer the destination that holds the list of all routes to the IP address of the destination network and cache of best routes. The flags parameter determines the trie's behavior, and among other things if we are using a fast trie. A fast trie (which is a fast copy of the slow trie) enables faster route lookup, but needs more memory. Arguments: pTrie - Pointer to the trie to be initialized levels - Bitmap of expanded levels in the F-Trie flags - Flags that determine trie's behaviour maxSTMemory - Limit on memory taken by the S-Trie maxFTMemory - Limit on memory taken by the F-Trie Return Value: TRIE_SUCCESS or ERROR_TRIE_* --*/ { Trie *pTrie; UINT nBytes; UINT initStatus; // Allocate memory for the tries nBytes = sizeof(Trie) + sizeof(STrie); if (flags & TFLAG_FAST_TRIE_ENABLED) { nBytes += sizeof(FTrie); } *ppTrie = AllocMemory0(nBytes); if (*ppTrie == NULL) { return ERROR_TRIE_RESOURCES; } pTrie = *ppTrie; // Initialize the behavior flags pTrie->flags = flags; // Initialize the trie pointers pTrie->sTrie = (STrie *) ((UCHAR *) pTrie + sizeof(Trie)); pTrie->fTrie = NULL; if (flags & TFLAG_FAST_TRIE_ENABLED) { pTrie->fTrie = (FTrie *) ((UCHAR *) pTrie + sizeof(Trie) + sizeof(STrie)); } do { // Initialize the Slow Component if ((initStatus = InitSTrie(pTrie->sTrie, maxSTMemory)) != TRIE_SUCCESS) break; // Are we using the fast trie ? if (!(flags & TFLAG_FAST_TRIE_ENABLED)) return TRIE_SUCCESS; // Initialize the Fast Component if ((initStatus = InitFTrie(pTrie->fTrie, levels, maxFTMemory)) != TRIE_SUCCESS) break; return TRIE_SUCCESS; } while (FALSE); // An error occurred - Clean up // Clean up slow component if (CleanupSTrie(pTrie->sTrie) != TRIE_SUCCESS) return initStatus; // Do we have a fast trie ? if (!(pTrie->flags & TFLAG_FAST_TRIE_ENABLED)) return initStatus; // Clean up fast component if (CleanupFTrie(pTrie->fTrie) != TRIE_SUCCESS) return initStatus; // Zero the flags to be safe pTrie->flags = 0; return initStatus; } VOID DestroyTrie(IN Trie * pTrie, OUT UINT * status) /*++ Routine Description: Cleans up a trie if it is empty. Arguments: pTrie - Pointer to the trie status - The Cleanup status Return Value: TRIE_SUCCESS or ERROR_TRIE_* --*/ { // Clean up slow component if ((*status = CleanupSTrie(pTrie->sTrie)) != TRIE_SUCCESS) return; // Do we have a fast trie if (!(pTrie->flags & TFLAG_FAST_TRIE_ENABLED)) return; // Clean up fast component if ((*status = CleanupFTrie(pTrie->fTrie)) != TRIE_SUCCESS) return; // Deallocate the trie memory FreeMemory0(pTrie); } UINT CALLCONV InsertIntoTrie(IN Trie * pTrie, IN Route * pIncRoute, IN ULONG matchFlags, OUT Route ** ppInsRoute, OUT Route ** ppOldBestRoute, OUT Route ** ppNewBestRoute) /*++ Routine Description: Inserts a route corresponding to an address prefix into the slow trie. If this is a new address prefix, then the corresponding dest is added to the FTrie (if it is being used). Arguments: pTrie - Pointer to the Trie to insert into pIncRoute - Pointer to the incoming route matchFlags - Flags to direct route matching ppInsRoute - Pointer to the route inserted ppOldBestRoute - Best route before insertion ppNewBestRoute - Best route after insertion Return Value: TRIE_SUCCESS or ERROR_TRIE_* --*/ { Dest *pOldBestDest; Dest *pNewBestDest; Route *pDelRoute; UINT retVal; *ppOldBestRoute = *ppNewBestRoute = *ppInsRoute = NULL; pOldBestDest = pNewBestDest = NULL; // Insert into the slow trie if ((retVal = InsertIntoSTrie(pTrie->sTrie, pIncRoute, matchFlags, ppInsRoute, &pOldBestDest, &pNewBestDest, ppOldBestRoute)) == TRIE_SUCCESS) { // Insertion successful - return new route *ppNewBestRoute = pNewBestDest->firstRoute; #if _DBG_ Print("\n@ pInsRTE = %08x\n@ pOldBestRTE = %08x\n@ pOldBestDest = %08x\n@ pNewBestDest = %08x\n", *ppInsRoute, *ppOldBestRoute, pOldBestDest, pNewBestDest); #endif // Are we using a fast trie ? if (pTrie->flags & TFLAG_FAST_TRIE_ENABLED) { // Did we have a new destination ? if (pOldBestDest != pNewBestDest) { // Tweak the fast trie if ((InsertIntoFTrie(pTrie->fTrie, *ppInsRoute, pNewBestDest, pOldBestDest)) != TRIE_SUCCESS) { // Not enough memory in F-Trie // Switch back to the S-Trie pTrie->flags &= ~TFLAG_FAST_TRIE_ENABLED; // And clean up the fast trie CleanupFTrie(pTrie->fTrie); return retVal; } } } } return retVal; } UINT CALLCONV DeleteFromTrie(IN Trie * pTrie, IN Route * pIncRoute, IN ULONG matchFlags, OUT Route ** ppDelRoute, OUT Route ** ppOldBestRoute, OUT Route ** ppNewBestRoute) /*++ Routine Description: Deletes a route corresponding to an address prefix into the S-trie. If this is the last route on dest, then dest is freed and it is replaced in the F-Trie by the next best dest. The route deleted is returned to the caller, who is responsible for freeing its memory. Arguments: pTrie - Pointer to trie to delete from pIncRoute - Pointer to the incoming route matchFlags - Flags to direct route matching ppDelRoute - Pointer to the route deleted ppOldBestRoute - Best route before deletion ppNewBestRoute - Best route after deletion Return Value: TRIE_SUCCESS or ERROR_TRIE_* --*/ { Dest *pOldBestDest; Dest *pNewBestDest; UINT retVal; *ppDelRoute = *ppOldBestRoute = *ppNewBestRoute = NULL; pOldBestDest = pNewBestDest = NULL; // Delete from slow trie if ((retVal = DeleteFromSTrie(pTrie->sTrie, pIncRoute, matchFlags, ppDelRoute, &pOldBestDest, &pNewBestDest, ppOldBestRoute)) == TRIE_SUCCESS) { // Deletion successful - return new route *ppNewBestRoute = pNewBestDest ? pNewBestDest->firstRoute : NULL; #if _DBG_ Print("\n@ pDelRTE = %08x\n@ pOldBestRTE = %08x\n@ pOldBestDest = %08x\n@ pNewBestDest = %08x\n", *ppDelRoute, *ppOldBestRoute, pOldBestDest, pNewBestDest); #endif // Are we using a fast trie ? if (pTrie->flags & TFLAG_FAST_TRIE_ENABLED) { // Was deleted route last one on dest ? if (pOldBestDest != pNewBestDest) { // Tweak the fast trie retVal = DeleteFromFTrie(pTrie->fTrie, *ppDelRoute, pOldBestDest, pNewBestDest, NORMAL); // Operation cannot fail Assert(retVal == TRIE_SUCCESS); } } // Reclaim route's memory - in the caller // FreeRouteInSTrie(pTrie->sTrie, *ppDelRoute); } return retVal; } #if DBG Dest * SearchAddrInTrie(IN Trie * pTrie, IN ULONG Addr) /*++ Routine Description: Search for an address in a trie Arguments: pTrie - Pointer to the trie to search Addr - Pointer to addr being queried Return Value: Return best dest match for this address --*/ { Dest *pBestDest1, *pBestDest2; #if _DBG_ // Just pretend that you are searching just one trie if (pTrie->flags & TFLAG_FAST_TRIE_ENABLED) Print("Looking up fast trie for %08x\n", Addr); else Print("Looking up slow trie for %08x\n", Addr); #endif pBestDest1 = SearchAddrInSTrie(pTrie->sTrie, Addr); // Make sure that the S-Trie and F-Trie are consistent if (pTrie->flags & TFLAG_FAST_TRIE_ENABLED) { pBestDest2 = SearchAddrInFTrie(pTrie->fTrie, Addr); Assert(pBestDest1 == pBestDest2); } // Return best dest returned (same by both operations) return pBestDest1; } #else // DBG #define SearchAddrInTrie(_pTrie_, _Addr_) \ (((_pTrie_)->flags & TFLAG_FAST_TRIE_ENABLED) \ ? SearchAddrInFTrie((_pTrie_)->fTrie, _Addr_) \ : SearchAddrInSTrie((_pTrie_)->sTrie, _Addr_)) \ #endif // DBG #if DBG VOID PrintTrie(IN Trie * pTrie, IN UINT flags) /*++ Routine Description: Prints a trie to the console Arguments: pTrie - Pointer to the trie Return Value: None --*/ { // Print the Slow Trie if (flags & SLOW) PrintSTrie(pTrie->sTrie, flags & FULL); // Is fast trie enabled if (!(pTrie->flags & TFLAG_FAST_TRIE_ENABLED)) return; // Print the Fast Trie if (flags & FAST) PrintFTrie(pTrie->fTrie, flags & FULL); } // // Miscellaneous Helper Functions // VOID PrintDest(IN Dest * dest) { Route *route; UINT i; if (NULL_DEST(dest)) { Print("NULL dest\n"); } else { route = dest->firstRoute; Print("Dest: "); PrintIPAddr(&DEST(route)); Print("/ %2d, Metric = %3lu\n", LEN(route), METRIC(route)); Print("Best Routes: \n"); for (i = 0; i < dest->numBestRoutes; i++) { route = dest->bestRoutes[i]; Print("Route %d @ %p: ", i, route); if (NULL_ROUTE(route)) { Print("NULL Route\n"); } else { Print("NHop = "); PrintIPAddr(&NHOP(route)); Print(", IF = %08x\n", IF(route)); } } Print("\n"); } } VOID PrintRoute(IN Route * route) { UINT i; Print("Route: Len = %2d", LEN(route)); Print(", Addr = "); PrintIPAddr(&DEST(route)); Print(", "); Print("NHop = "); PrintIPAddr(&NHOP(route)); Print(", IF = %08x", IF(route)); Print(", Metric = %3lu\n", METRIC(route)); } VOID PrintIPAddr(IN ULONG * addr) { UCHAR *addrBytes = (UCHAR *) addr; UINT i; if (addrBytes) { for (i = 0; i < 4; i++) { Print("%3d.", addrBytes[i]); } Print(" "); } else { Print("NULL Addr "); } } #endif // DBG