|
|
/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
strie.c
Abstract:
This module contains routines that manipulate an S-trie data stucture, that forms the slow path in a fast IP route lookup implementation.
Author:
Chaitanya Kodeboyina (chaitk) 26-Nov-1997
Revision History:
--*/
#include "precomp.h"
#include "strie.h"
UINT CALLCONV InitSTrie(IN STrie * pSTrie, IN ULONG maxMemory) /*++
Routine Description:
Initializes an S-trie. This should be done prior to any other trie operations.
Arguments:
pSTrie - Pointer to the trie to be initialized maxMemory - Limit on memory taken by the S-Trie
Return Value:
TRIE_SUCCESS
--*/ { // Zero all the memory for the trie header
RtlZeroMemory(pSTrie, sizeof(STrie));
// Set a limit on the memory for trie/nodes
pSTrie->availMemory = maxMemory;
return TRIE_SUCCESS; }
UINT CALLCONV InsertIntoSTrie(IN STrie * pSTrie, IN Route * pIncRoute, IN ULONG matchFlags, OUT Route ** ppInsRoute, OUT Dest ** ppOldBestDest, OUT Dest ** ppNewBestDest, OUT Route ** ppOldBestRoute ) /*++
Routine Description:
Inserts a route corresponding to an address prefix into a S-trie, and fills in best dest for addresses that match this prefix both before and after insertion of route.
Arguments:
pSTrie - Pointer to trie to insert into pIncRoute - Pointer to the incoming route matchFlags - Flags to direct route matching ppInsRoute - Pointer to the route inserted ppOldBestDest - Best dest before insertion ppNewBestDest - Best dest after insertion ppOldBestRoute - Best route before insertion
Return Value:
TRIE_SUCCESS or ERROR_TRIE_* --*/ { STrieNode *pNewNode; STrieNode *pPrevNode; STrieNode *pCurrNode; STrieNode *pOthNode; Dest *pCurrDest; Dest *pNewDest; Dest *pBestDest; Route *pNewRoute; Route *pPrevRoute; Route *pCurrRoute; ULONG addrBits; ULONG tempBits; UINT nextBits; UINT matchBits; UINT bitsLeft; UINT distPos; UINT nextChild; #if DBG
// Make sure the trie is initialized
if (!pSTrie) { Fatal("Insert Route: STrie not initialized", ERROR_TRIE_NOT_INITED); } #endif
// Make sure input route is valid
if (NULL_ROUTE(pIncRoute)) { Error("Insert Route: NULL or invalid route", ERROR_TRIE_BAD_PARAM); } if (LEN(pIncRoute) > ADDRSIZE) { Error("Insert Route: Invalid mask length", ERROR_TRIE_BAD_PARAM); } // Use addr bits to index the trie
addrBits = RtlConvertEndianLong(DEST(pIncRoute)); bitsLeft = LEN(pIncRoute);
// Make sure addr and mask agree
if (ShowMostSigNBits(addrBits, bitsLeft) != addrBits) { Error("Insert Route: Addr & mask don't agree", ERROR_TRIE_BAD_PARAM); } TRY_BLOCK { // Start going down the search trie
// Initialize any new allocations
pNewNode = NULL; pOthNode = NULL; pNewDest = NULL; pNewRoute = NULL;
// Initialize other loop variables
pBestDest = NULL;
nextChild = 0; pPrevNode = STRUCT_OF(STrieNode, &pSTrie->trieRoot, child[0]);
for (;;) { // Start this loop by advancing to the next child
pCurrNode = pPrevNode->child[nextChild];
if (pCurrNode == NULL) { // Case 1: Found a NULL - insert now
// Make a copy of the incoming route
NewRouteInSTrie(pSTrie, pNewRoute, pIncRoute);
// Allocate a dest with the new route
NewDestInSTrie(pSTrie, pNewRoute, pNewDest);
// New node with bits left unmatched
NewSTrieNode(pSTrie, pNewNode, bitsLeft, addrBits, pNewDest);
// Stick it as the correct child of node
pPrevNode->child[nextChild] = pNewNode;
break; } // Number of bits to match in this trie node
nextBits = pCurrNode->numBits;
matchBits = (nextBits > bitsLeft) ? bitsLeft : nextBits;
// Adjust next node bits for dist posn check
// Get distinguishing postion for bit patterns
distPos = PickDistPosition(pCurrNode->keyBits, addrBits, matchBits, &tempBits);
if (distPos == nextBits) { // Completely matches next node
if (distPos == bitsLeft) { // We have exhausted all incoming bits
if (!NULL_DEST(pCurrNode->dest)) { // Case 2: This trie node has a route
// Insert in sorted order of metric
pCurrDest = pCurrNode->dest;
// Give a ptr to the old best route
CopyRoutePtr(ppOldBestRoute, pCurrDest->firstRoute);
pPrevRoute = NULL; pCurrRoute = pCurrDest->firstRoute;
// Search for an adequate match (IF, NHop)
do { // Use the flags to control matching
if ((((matchFlags & MATCH_INTF) == 0) || (IF(pCurrRoute) == IF(pIncRoute))) && (((matchFlags & MATCH_NHOP) == 0) || (NHOP(pCurrRoute) == NHOP(pIncRoute)))) break;
pPrevRoute = pCurrRoute; pCurrRoute = NEXT(pPrevRoute); } while (!NULL_ROUTE(pCurrRoute));
if (NULL_ROUTE(pCurrRoute)) { // Case 2.1: No matching route
// Create a new copy of route
NewRouteInSTrie(pSTrie, pNewRoute, pIncRoute); } else { // Case 2.2: A Matching Route
// Has the metric changed ?
if (METRIC(pCurrRoute) != METRIC(pIncRoute)) { // Remove route from current position
if (!NULL_ROUTE(pPrevRoute)) { // Remove it from middle of list
NEXT(pPrevRoute) = NEXT(pCurrRoute); } else { // Remove from beginning of list
pCurrDest->firstRoute = NEXT(pCurrRoute); } } // Keep the new/updated route for later
pNewRoute = pCurrRoute; }
if (NULL_ROUTE(pCurrRoute) || (METRIC(pCurrRoute) != METRIC(pIncRoute))) { // Update metric for new / changing route
METRIC(pNewRoute) = METRIC(pIncRoute);
// Traverse list looking for new position
pPrevRoute = NULL; pCurrRoute = pCurrDest->firstRoute;
while (!NULL_ROUTE(pCurrRoute)) { if (METRIC(pCurrRoute) > METRIC(pIncRoute)) break;
pPrevRoute = pCurrRoute; pCurrRoute = NEXT(pPrevRoute); }
// Insert at the new proper position
NEXT(pNewRoute) = pCurrRoute;
if (!NULL_ROUTE(pPrevRoute)) { // Insert in the middle of list
NEXT(pPrevRoute) = pNewRoute; } else { // Insert at beginning of list
pCurrDest->firstRoute = pNewRoute; } } // Give a ptr to newly inserted route
CopyRoutePtr(ppInsRoute, pNewRoute);
// Give a ptr to the old best dest
CopyDestPtr(ppOldBestDest, pCurrDest);
// Give a ptr to the new best dest
CopyDestPtr(ppNewBestDest, pCurrDest);
// Update the best routes cache on node
CacheBestRoutesInDest(pCurrDest);
return TRIE_SUCCESS; } else { // Case 3: This node was a marker
// Create a new route & attach it
// Create a new copy of this route
NewRouteInSTrie(pSTrie, pNewRoute, pIncRoute);
// Allocate a dest with the new route
NewDestInSTrie(pSTrie, pNewRoute, pNewDest);
// And attach dest to the marker node
pCurrNode->dest = pNewDest; }
break; } else { // Case 4: We still have bits left here
// Go down for more specific match
// Update node with best dest so far
if (!NULL_DEST(pCurrNode->dest)) { pBestDest = pCurrNode->dest; } // Discard used bits for this iteration
addrBits <<= matchBits; bitsLeft -= matchBits;
// Prepare node for the next iteration
pPrevNode = pCurrNode;
// Bit 1 gives direction to search next
nextChild = PickMostSigNBits(addrBits, 1); } } else { if (distPos == bitsLeft) { // Case 5: The route falls on this branch
// Insert a new node in the same branch
// Make a copy of the new route
NewRouteInSTrie(pSTrie, pNewRoute, pIncRoute);
// Allocate a dest with the new route
NewDestInSTrie(pSTrie, pNewRoute, pNewDest);
// New node with bits left unmatched
NewSTrieNode(pSTrie, pNewNode, distPos, ShowMostSigNBits(addrBits, distPos), pNewDest);
pPrevNode->child[nextChild] = pNewNode;
// Adjust the next node - numbits etc
pCurrNode->keyBits <<= distPos, pCurrNode->numBits -= distPos;
// Stick next node in the correct child
nextChild = PickMostSigNBits(pCurrNode->keyBits, 1);
pNewNode->child[nextChild] = pCurrNode;
break; } else { // Case 6: The route fragments the path
// Create a new branch with two nodes
// First make a copy of the new route
NewRouteInSTrie(pSTrie, pNewRoute, pIncRoute);
// Allocate a dest with the new route
NewDestInSTrie(pSTrie, pNewRoute, pNewDest);
// Branch node with non distinguishing bits
NewSTrieNode(pSTrie, pOthNode, distPos, ShowMostSigNBits(addrBits, distPos), NULL);
// A Leaf node with the distinguishing bits
bitsLeft -= distPos; addrBits <<= distPos;
NewSTrieNode(pSTrie, pNewNode, bitsLeft, addrBits, pNewDest);
// Stick new branch node into the trie
pPrevNode->child[nextChild] = pOthNode;
// Set the children of the branch node
// Adjust the next node - numbits etc
pCurrNode->keyBits <<= distPos, pCurrNode->numBits -= distPos;
// Stick next node in the correct child
nextChild = PickMostSigNBits(pCurrNode->keyBits, 1);
pOthNode->child[nextChild] = pCurrNode;
// Stick new leaf node as the other child
pOthNode->child[1 - nextChild] = pNewNode;
break; } } } // Give a ptr to the inserted route
CopyRoutePtr(ppInsRoute, pNewRoute);
// Give a ptr to the old best dest
CopyDestPtr(ppOldBestDest, pBestDest);
// Give a ptr to the old best route
if (!NULL_DEST(pBestDest)) { CopyRoutePtr(ppOldBestRoute, pBestDest->firstRoute); } // Give a ptr to the new best dest
CopyDestPtr(ppNewBestDest, pNewDest);
// Route is the only route on dest
if (pNewDest->maxBestRoutes > 0) { pNewDest->numBestRoutes = 1; pNewDest->bestRoutes[0] = pNewRoute; }
return TRIE_SUCCESS; } ERR_BLOCK { // Not enough RESOURCES to add a new route
// Free the memory for the new route alloc
if (pNewRoute) { FreeRouteInSTrie(pSTrie, pNewRoute); } // Free memory for the dest on the new node
if (pNewDest) { FreeDestInSTrie(pSTrie, pNewDest); } // Free the memory for the new tnode alloc
if (pNewNode) { FreeSTrieNode(pSTrie, pNewNode); } // Free memory for any other new tnode alloc
if (pOthNode) { FreeSTrieNode(pSTrie, pOthNode); } } END_BLOCK }
UINT CALLCONV DeleteFromSTrie(IN STrie * pSTrie, IN Route * pIncRoute, IN ULONG matchFlags, OUT Route ** ppDelRoute, OUT Dest ** ppOldBestDest, OUT Dest ** ppNewBestDest, OUT Route ** ppOldBestRoute ) /*++
Routine Description:
Deletes a route corresponding to an address prefix into a S-trie, and fills in best dest for addresses that match this prefix both before and after deletion of route.
Arguments:
pSTrie - Pointer to trie to delete from pIncRoute - Pointer to the incoming route matchFlags - Flags to direct route matching ppDelRoute - Pointer to the route deleted ppOldBestDest - Best dest before deletion ppNewBestDest - Best dest after deletion ppOldBestRoute - Best route before deletion
Return Value:
TRIE_SUCCESS or ERROR_TRIE_* --*/ { STrieNode *pPrevNode; STrieNode *pCurrNode; STrieNode *pNextNode; STrieNode *pOtherNode; Dest *pBestDest; Dest *pCurrDest; Route *pPrevRoute; Route *pCurrRoute; ULONG addrBits; ULONG tempBits; UINT nextBits; UINT matchBits; UINT bitsLeft; UINT distPos; UINT nextChild; #if DBG
if (!pSTrie) { Fatal("Delete Route: STrie not initialized", ERROR_TRIE_NOT_INITED); } #endif
// Make sure input route is valid
if (NULL_ROUTE(pIncRoute)) { Error("Delete Route: NULL or invalid route", ERROR_TRIE_BAD_PARAM); } if (LEN(pIncRoute) > ADDRSIZE) { Error("Delete Route: Invalid mask length", ERROR_TRIE_BAD_PARAM); } // Use addr bits to index the trie
addrBits = RtlConvertEndianLong(DEST(pIncRoute)); bitsLeft = LEN(pIncRoute);
// Make sure addr and mask agree
if (ShowMostSigNBits(addrBits, bitsLeft) != addrBits) { Error("Delete Route: Addr & mask don't agree", ERROR_TRIE_BAD_PARAM); } // Start going down the search trie
pBestDest = NULL;
nextChild = 0; pPrevNode = STRUCT_OF(STrieNode, &pSTrie->trieRoot, child[0]);
for (;;) { // Start this loop by advancing to the next child
pCurrNode = pPrevNode->child[nextChild];
if (pCurrNode == NULL) { // Case 1: Found a NULL, end search
// Route not found, return an error
Error("Delete Route #0: Route not found", ERROR_TRIE_NO_ROUTES); } // Number of bits to match in this trie node
nextBits = pCurrNode->numBits;
matchBits = (nextBits > bitsLeft) ? bitsLeft : nextBits;
// Adjust next node bits for dist posn check
// Get distinguishing postion for bit patterns
distPos = PickDistPosition(pCurrNode->keyBits, addrBits, matchBits, &tempBits);
if (distPos == nextBits) { // Completely matches next node
if (distPos == bitsLeft) { // We have exhausted all incoming bits
// End search, see if we found a route
if (!NULL_DEST(pCurrNode->dest)) { pCurrDest = pCurrNode->dest;
// This node starts a valid route list
// Give a ptr to the old best dest
CopyDestPtr(ppOldBestDest, pCurrDest);
// Give a ptr to the old best route
CopyRoutePtr(ppOldBestRoute, pCurrDest->firstRoute);
// Give a ptr to the new best dest
CopyDestPtr(ppNewBestDest, pCurrDest);
// Match the rest by walking the list
// sorted increasing order of metric
pPrevRoute = NULL; pCurrRoute = pCurrDest->firstRoute;
do { // Use the flags to control matching
// N.B. Note that certain clients are not allowed
// to delete local routes.
if ((((matchFlags & MATCH_INTF) == 0) || (IF(pCurrRoute) == IF(pIncRoute))) && (((matchFlags & MATCH_NHOP) == 0) || (NHOP(pCurrRoute) == NHOP(pIncRoute))) && (((matchFlags & MATCH_EXCLUDE_LOCAL) == 0) || (PROTO(pCurrRoute) != IRE_PROTO_LOCAL))) { // Case 2: Found an adequate match
//* Do the actual deletion here *
if (!NULL_ROUTE(pPrevRoute)) { // Delete from middle of the list
NEXT(pPrevRoute) = NEXT(pCurrRoute); } else { // Delete from beginning of list
pCurrDest->firstRoute = NEXT(pCurrRoute); }
break; } pPrevRoute = pCurrRoute; pCurrRoute = NEXT(pPrevRoute); } while (!NULL_ROUTE(pCurrRoute));
if (NULL_ROUTE(pCurrRoute)) { // Route not found, return an error
Error("Delete Route #1: Route not found", ERROR_TRIE_NO_ROUTES); } // Give a ptr to newly deleted route
CopyRoutePtr(ppDelRoute, pCurrRoute);
// Did the list become empty after deletion ?
if (NULL_ROUTE(pCurrDest->firstRoute)) { // List is empty, so garbage collection
// Give a ptr to the new best dest
CopyDestPtr(ppNewBestDest, pBestDest);
// Free destination as all routes gone
FreeDestInSTrie(pSTrie, pCurrNode->dest);
if (pCurrNode->child[0] && pCurrNode->child[1]) { // Case 3: Both children are non NULL
// Just remove route from the node
// Route already removed from node
} else if (pCurrNode->child[0] || pCurrNode->child[1]) { // Case 4: One of the children is not NULL
// Pull up lonely child in place of node
// Pick the correct child to pull up
if (pCurrNode->child[0]) pNextNode = pCurrNode->child[0]; else pNextNode = pCurrNode->child[1];
// Child node bears bits of deleted node
pNextNode->keyBits >>= pCurrNode->numBits; pNextNode->keyBits |= pCurrNode->keyBits; pNextNode->numBits += pCurrNode->numBits;
pPrevNode->child[nextChild] = pNextNode;
// Destroy trie node marked for deletion
FreeSTrieNode(pSTrie, pCurrNode); } else { // Node to be deleted has no children
if (&pPrevNode->child[nextChild] == &pSTrie->trieRoot) { // Case 5: Root node is being deleted
// Detach node from trie root & delete
// Just detach by removing the pointer
pPrevNode->child[nextChild] = NULL;
// Destroy trie node marked for deletion
FreeSTrieNode(pSTrie, pCurrNode); } else { if (!NULL_DEST(pPrevNode->dest)) { // Case 6: Parent has a route on it
// Detach child from parent & delete
// Just detach by removing the pointer
pPrevNode->child[nextChild] = NULL;
// Destroy trie node marked for deletion
FreeSTrieNode(pSTrie, pCurrNode); } else { // Case 7: Parent has no route on it
// Pull up other child of parent node
pOtherNode = pPrevNode->child[1 - nextChild];
// Make sure no 1-way branching
Assert(pOtherNode != NULL);
// Parent node bears bits of sibling node
pPrevNode->keyBits |= (pOtherNode->keyBits >> pPrevNode->numBits); pPrevNode->numBits += pOtherNode->numBits;
// Move the route up too - move content
pPrevNode->dest = pOtherNode->dest;
pPrevNode->child[0] = pOtherNode->child[0]; pPrevNode->child[1] = pOtherNode->child[1];
FreeSTrieNode(pSTrie, pCurrNode); FreeSTrieNode(pSTrie, pOtherNode); } } } } else { // We still have some routes on the dest
// Update the best routes cache on node
CacheBestRoutesInDest(pCurrDest); }
// Consider route as deleted at this point
// FreeRouteInSTrie(pSTrie, pCurrRoute);
break; } else { // Case 7: This node was a marker
// No Route to delete in this node
Error("Delete Route #2: Route not found", ERROR_TRIE_NO_ROUTES); } } else { // Case 8: We still have bits left here
// Go down for more specific match
// Update best value of route so far
if (!NULL_DEST(pCurrNode->dest)) { pBestDest = pCurrNode->dest; } // Discard used bits for this iteration
addrBits <<= matchBits; bitsLeft -= matchBits;
// Prepare node for the next iteration
pPrevNode = pCurrNode;
// Bit 1 gives direction to search next
nextChild = PickMostSigNBits(addrBits, 1); } } else { // Case 9: Did not match the next node
// Route not found, fill next best
Error("Delete Route #3: Route not found", ERROR_TRIE_NO_ROUTES); } }
return TRIE_SUCCESS; }
UINT CALLCONV SearchRouteInSTrie(IN STrie * pSTrie, IN ULONG routeDest, IN ULONG routeMask, IN ULONG routeNHop, IN PVOID routeOutIF, IN ULONG matchFlags, OUT Route ** ppOutRoute) /*++
Routine Description:
Search for a specific route in an S-trie
Arguments:
pSTrie - Pointer to the S-trie to search routeDest - Dest of route being looked up routeMask - Mask of route being looked up routeNHop - NHop of route being looked up routeOutIF - Outgoing IF for this route matchFlags - Flags to direct route matching ppOutRoute - To return the best route match
Return Value:
TRIE_SUCCESS or ERROR_TRIE_*
--*/ { STrieNode *pPrevNode; STrieNode *pCurrNode; Dest *pCurrDest; Dest *pBestDest; Route *pPrevRoute; Route *pCurrRoute; ULONG addrBits; ULONG tempBits; UINT nextBits; UINT matchBits; UINT bitsLeft; UINT distPos; UINT nextChild; #if DBG
if (!pSTrie) { Fatal("Search Route: STrie not initialized", ERROR_TRIE_NOT_INITED); } #endif
*ppOutRoute = NULL;
// Use addr bits to index the trie
addrBits = RtlConvertEndianLong(routeDest);
//* Assume an contiguous IP mask *
tempBits = RtlConvertEndianLong(routeMask);
bitsLeft = 0; while (tempBits != 0) { bitsLeft++; tempBits <<= 1; }
// Make sure addr and mask agree
if (ShowMostSigNBits(addrBits, bitsLeft) != addrBits) { Error("Search Route: Addr & mask don't agree", ERROR_TRIE_BAD_PARAM); } // Start going down the search trie
pBestDest = NULL;
nextChild = 0; pPrevNode = STRUCT_OF(STrieNode, &pSTrie->trieRoot, child[0]);
for (;;) { // Start this loop by advancing to the next child
pCurrNode = pPrevNode->child[nextChild];
if (pCurrNode == NULL) { // Case 1: Found a NULL, end search
// Route not found, fill next best
// Give a copy of the next best route
if (!NULL_DEST(pBestDest)) { CopyRoutePtr(ppOutRoute, pBestDest->firstRoute); } Error("Search Route #0: Route not found", ERROR_TRIE_NO_ROUTES); } // Number of bits to match in this trie node
nextBits = pCurrNode->numBits;
matchBits = (nextBits > bitsLeft) ? bitsLeft : nextBits;
// Adjust next node bits for dist posn check
// Get distinguishing postion for bit patterns
distPos = PickDistPosition(pCurrNode->keyBits, addrBits, matchBits, &tempBits);
if (distPos == nextBits) { // Completely matches next node
if (distPos == bitsLeft) { // We have exhausted all incoming bits
// End search, see if we found a route
if (!NULL_DEST(pCurrNode->dest)) { // This node starts a valid route list
pCurrDest = pCurrNode->dest;
// Match the rest by walking the list
// sorted increasing order of metric
pPrevRoute = NULL; pCurrRoute = pCurrDest->firstRoute;
do { // Use the flags to control matching
if ((((matchFlags & MATCH_INTF) == 0) || (IF(pCurrRoute) == routeOutIF)) && (((matchFlags & MATCH_NHOP) == 0) || (NHOP(pCurrRoute) == routeNHop))) { // Found adequately matched route
// Just copy the route and return
CopyRoutePtr(ppOutRoute, pCurrRoute); return TRIE_SUCCESS; } pPrevRoute = pCurrRoute; pCurrRoute = NEXT(pPrevRoute); } while (!NULL_ROUTE(pCurrRoute));
// Give a copy of the old route, or best route
// for that prefix in case of no exact match
if (NULL_ROUTE(pCurrRoute)) { CopyRoutePtr(ppOutRoute, pCurrDest->firstRoute);
Error("Search Route #1: Route not found", ERROR_TRIE_NO_ROUTES); } break; } else { // Case 7: This node was a marker
// No Route present in this node
// Give a copy of the next best route
if (!NULL_DEST(pBestDest)) { CopyRoutePtr(ppOutRoute, pBestDest->firstRoute); } Error("Search Route #2: Route not found", ERROR_TRIE_NO_ROUTES); } } else { // Case 8: We still have bits left here
// Go down for more specific match
// Update node with best dest so far
if (!NULL_DEST(pCurrNode->dest)) { pBestDest = pCurrNode->dest; } // Discard used bits for this iteration
addrBits <<= matchBits; bitsLeft -= matchBits;
// Prepare node for the next iteration
pPrevNode = pCurrNode;
// Bit 1 gives direction to search next
nextChild = PickMostSigNBits(addrBits, 1); } } else { // Case 9: Did not match the next node
// Route not found, fill the next best
// Give a copy of the next best route
if (!NULL_DEST(pBestDest)) { CopyRoutePtr(ppOutRoute, pBestDest->firstRoute); } Error("Search Route #3: Route not found", ERROR_TRIE_NO_ROUTES); } } return TRIE_SUCCESS; }
Dest * CALLCONV SearchAddrInSTrie(IN STrie * pSTrie, IN ULONG Addr) /*++
Routine Description:
Search for an address in an S-trie
Arguments:
pSTrie - Pointer to the trie to search Addr - Pointer to addr being queried
Return Value: Return best route match for this address
--*/ { STrieNode *pCurrNode; Dest *pBestDest; UINT nextChild; ULONG addrBits; ULONG keyMask; ULONG keyBits;
#if DBG
if (!pSTrie) { Fatal("Search Addr: STrie not initialized", ERROR_TRIE_NOT_INITED); } #endif
addrBits = RtlConvertEndianLong(Addr);
// Start going down the search trie
pBestDest = NULL;
nextChild = 0; pCurrNode = STRUCT_OF(STrieNode, &pSTrie->trieRoot, child[0]);
for (;;) { // Start this loop by advancing to next child
pCurrNode = pCurrNode->child[nextChild];
if (pCurrNode == NULL) { // Case 1: Found a NULL, end search
break; } // Mask of bits to use in this trie node
keyMask = MaskBits(pCurrNode->numBits);
// Value of non-masked bits to match now
keyBits = pCurrNode->keyBits;
// Try to match the bits with above mask
if ((addrBits & keyMask) != keyBits) { // Case 2: Failed to match this node
break; } // Update best value of route so far
if (!NULL_DEST(pCurrNode->dest)) { pBestDest = pCurrNode->dest; } // Go down for more specific match
addrBits <<= pCurrNode->numBits;
// Bit 1 gives direction to search next
nextChild = PickMostSigNBits(addrBits, 1); } return pBestDest; }
UINT CALLCONV IterateOverSTrie(IN STrie * pSTrie, IN STrieCtxt * pCtxt, OUT Route ** ppRoute, OUT Dest** ppDest) /*++
Routine Description:
Get the next route in the S-Trie
Arguments:
pSTrie - Pointer to trie to iterate over pCtxt - Pointer to iterator context ppRoute - To return the next S-trie route ppDest - To return destinations instead of routes
Return Value: TRIE_SUCCESS or ERROR_TRIE_*
--*/ { STrieNode *nodeInLevel[MAXLEVEL + 1]; STrieNode *pPrevNode; STrieNode *pCurrNode; STrieNode *pNextNode; Route *pCurrRoute; ULONG addrBits; ULONG tempBits; UINT numLevels; UINT nextBits; UINT matchBits; UINT bitsLeft; UINT distPos; UINT nextChild; BOOLEAN routeToReturn; Dest *pCurrDest;
#if DBG
if (!pSTrie) { Fatal("Iterate Over STrie: STrie not initialized", ERROR_TRIE_NOT_INITED); } #endif
// Check if the context is a valid one
if (NULL_ROUTE(pCtxt->pCRoute)) { // NULL Context Case -- First Time
// Do we have any routes at all ?
if (pSTrie->trieRoot == NULL) { return (UINT) ERROR_TRIE_NO_ROUTES; } // Start from the very beginning
// Fill in actual context
pCtxt->currAddr = 0; pCtxt->currALen = 0;
pCurrDest = pSTrie->trieRoot->dest; pCtxt->pCRoute = pCurrDest ? pCurrDest->firstRoute : NULL;
// Fill generated context
numLevels = 1; nodeInLevel[0] = pSTrie->trieRoot; } else { // Non NULL Context -- Generate path
// of current route from trie's root
// Use addr bits to index the trie
addrBits = RtlConvertEndianLong(pCtxt->currAddr);
bitsLeft = pCtxt->currALen;
#if DBG
// Make sure addr and mask agree
if (ShowMostSigNBits(addrBits, bitsLeft) != addrBits) { Fatal("Search Route: Addr & mask don't agree", ERROR_TRIE_BAD_PARAM); } #endif
// Start going down the search trie
numLevels = 0;
nextChild = 0; pPrevNode = STRUCT_OF(STrieNode, &pSTrie->trieRoot, child[0]);
for (;;) { // Start this loop by advancing to the next child
pCurrNode = pPrevNode->child[nextChild];
// Push pointer to this trie node into the stack
nodeInLevel[numLevels++] = pCurrNode;
// Valid context always matches all nodes exactly
Assert(pCurrNode != NULL);
// Get Number of bits to match in this trie node
nextBits = pCurrNode->numBits;
matchBits = (nextBits > bitsLeft) ? bitsLeft : nextBits;
// Adjust next node bits for dist posn check
// Get distinguishing postion for bit patterns
distPos = PickDistPosition(pCurrNode->keyBits, addrBits, matchBits, &tempBits);
// Valid context always matches all nodes exactly
Assert(distPos == nextBits);
if (distPos == bitsLeft) { // We have exhausted all incoming bits
// We should have found a route (list)
pCurrDest = pCurrNode->dest; #if DBG
// This node starts a valid route list
Assert(pCurrDest);
// Try matching the route in context
pCurrRoute = pCurrDest->firstRoute;
do { if (pCurrRoute == pCtxt->pCRoute) { break; } pCurrRoute = NEXT(pCurrRoute); } while (!NULL_ROUTE(pCurrRoute));
// We should find a definite match
Assert(!NULL_ROUTE(pCurrRoute)); #endif // DBG
// We have the full path from root to
// current node in the stack of nodes
break; } // We still have key bits left here
// Go down for more specific match
// Discard used bits for this iteration
addrBits <<= matchBits; bitsLeft -= matchBits;
// Prepare node for the next iteration
pPrevNode = pCurrNode;
// Bit 1 gives direction to search next
nextChild = PickMostSigNBits(addrBits, 1); } } // We have no routes to return at this point
routeToReturn = FALSE;
// Set direction to print route in context
nextChild = LCHILD;
for (;;) { // Get pointer to the current node & direction
pCurrNode = nodeInLevel[numLevels - 1];
// Return route its first time on top of stack
if (nextChild == LCHILD) { pCurrRoute = pCtxt->pCRoute; if (!NULL_ROUTE(pCurrRoute)) { // We have a valid route to return
routeToReturn = TRUE; if (ppDest) { CopyDestPtr(ppDest, pCurrDest); } else { CopyRoutePtr(ppRoute, pCurrRoute); // Got a valid next route - return
pCtxt->pCRoute = NEXT(pCurrRoute); if (!NULL_ROUTE(NEXT(pCurrRoute))) { // Update the context and return
pCtxt->currAddr = DEST(pCtxt->pCRoute); pCtxt->currALen = LEN(pCtxt->pCRoute); return (UINT) ERROR_TRIE_NOT_EMPTY; } } // Search for the valid next route
} } // Update the context to reflect an access
switch (nextChild) { case LCHILD: // Push left child if not NULL
pNextNode = pCurrNode->child[0];
if (pNextNode != NULL) { // Push next level on the stack - Go Left
nodeInLevel[numLevels++] = pNextNode; nextChild = LCHILD; pCtxt->pCRoute = pNextNode->dest ? pNextNode->dest->firstRoute : NULL;
// Return if we have a route to send back
// & we also have located the next route
if ((routeToReturn) && !NULL_DEST(pNextNode->dest)) { // Update the context and return
pCtxt->currAddr = DEST(pCtxt->pCRoute); pCtxt->currALen = LEN(pCtxt->pCRoute);
return (UINT) ERROR_TRIE_NOT_EMPTY; } continue; } case RCHILD: // Push right child if not NULL
pNextNode = pCurrNode->child[1];
if (pNextNode != NULL) { // Push next level on the stack - Go Left
nodeInLevel[numLevels++] = pNextNode; nextChild = LCHILD; pCtxt->pCRoute = pNextNode->dest ? pNextNode->dest->firstRoute : NULL;
// Return if we have a route to send back
// & we also have located the next route
if ((routeToReturn) && !NULL_DEST(pNextNode->dest)) { // Update the context and return
pCtxt->currAddr = DEST(pCtxt->pCRoute); pCtxt->currALen = LEN(pCtxt->pCRoute);
return (UINT) ERROR_TRIE_NOT_EMPTY; } continue; } case PARENT: // Pop node & calculate new direction
// Are we done iterating ?
if (--numLevels == 0) { return TRIE_SUCCESS; } pPrevNode = nodeInLevel[numLevels - 1];
if (pPrevNode->child[0] == pCurrNode) { nextChild = RCHILD; } else { nextChild = PARENT; }
continue; } } }
UINT CALLCONV IsSTrieIteratorValid(IN STrie * pSTrie, IN STrieCtxt * pCtxt) /*++
Routine Description:
Make sure that iterator's context is valid
Arguments:
pSTrie - Pointer to trie to iterate over pCtxt - Pointer to iterator context
Return Value: TRIE_SUCCESS or ERROR_TRIE_*
--*/ { Route *pCurrRoute; ULONG addrMask; ULONG maskBits;
if (NULL_ROUTE(pCtxt->pCRoute)) { // NULL Context Case
if (pSTrie->trieRoot != NULL) { return TRIE_SUCCESS; } return (UINT) ERROR_TRIE_NO_ROUTES; } else { // Non NULL Context
// Make sure current route is valid
// Search for a node with dest, len
// Generate mask from address length
maskBits = MaskBits(pCtxt->currALen);
// Convert endian - search needs it
addrMask = RtlConvertEndianLong(maskBits);
if (SearchRouteInSTrie(pSTrie, pCtxt->currAddr, addrMask, 0, NULL, MATCH_NONE, &pCurrRoute) != TRIE_SUCCESS) { return (UINT) ERROR_TRIE_BAD_PARAM; } // Search for the route in context
while (!NULL_ROUTE(pCurrRoute)) { if (pCurrRoute == pCtxt->pCRoute) { return TRIE_SUCCESS; } pCurrRoute = NEXT(pCurrRoute); }
return (UINT) ERROR_TRIE_BAD_PARAM; } }
UINT CALLCONV CleanupSTrie(IN STrie * pSTrie) /*++
Routine Description:
Deletes an S-trie if it is empty
Arguments:
ppSTrie - Ptr to the S-trie
Return Value:
TRIE_SUCCESS or ERROR_TRIE_* --*/ { // Zero the memory for the trie header
RtlZeroMemory(pSTrie, sizeof(STrie));
return TRIE_SUCCESS; }
VOID CALLCONV CacheBestRoutesInDest(IN Dest * pDest) /*++
Routine Description:
Updates a destination's best-routes cache
Arguments:
pDest - Ptr to the destination
Return Value:
none. --*/ { Route* pCurrRoute; UINT bestMetric, i;
pCurrRoute = pDest->firstRoute; if (!pCurrRoute) { return; }
// Get metric of the current best route, and store as many routes with the
// same metric as possible
bestMetric = METRIC(pCurrRoute);
for (i = 0; i < pDest->maxBestRoutes; i++) { if (NULL_ROUTE(pCurrRoute) || (METRIC(pCurrRoute) != bestMetric)) { break; } pDest->bestRoutes[i] = pCurrRoute; pCurrRoute = NEXT(pCurrRoute); }
pDest->numBestRoutes = (USHORT) i; }
#if DBG
VOID CALLCONV PrintSTrie(IN STrie * pSTrie, IN UINT printFlags) /*++
Routine Description:
Print an S-Trie
Arguments:
pSTrie - Pointer to the S-Trie printFlags - Information to print
Return Value:
None --*/ { if (pSTrie == NULL) { Print("%s", "Uninitialized STrie\n\n"); return; } if ((printFlags & SUMM) == SUMM) { Print("\n\n/***Slow-Trie------------------------------------------------"); Print("\n/***---------------------------------------------------------\n"); } if (printFlags & POOL) { Print("Available Memory: %10lu\n\n", pSTrie->availMemory); } if (printFlags & STAT) { Print("Statistics:\n\n");
Print("Total Number of Nodes : %d\n", pSTrie->numNodes); Print("Total Number of Routes: %d\n", pSTrie->numRoutes); Print("Total Number of Dests : %d\n", pSTrie->numDests); } if (printFlags & TRIE) { if (pSTrie->trieRoot == NULL) { Print("\nEmpty STrie\n"); } else { PrintSTrieNode(pSTrie->trieRoot); } } if ((printFlags & SUMM) == SUMM) { Print("\n---------------------------------------------------------***/\n"); Print("---------------------------------------------------------***/\n\n"); } }
VOID CALLCONV PrintSTrieNode(IN STrieNode * pSTrieNode) /*++
Routine Description:
Print an S-trie node
Arguments:
pSTrieNode - Pointer to the S-trie node
Return Value:
None --*/ { if (pSTrieNode == NULL) { return; } Print("\n--------------------------------------------------------\n"); Print("Child @ %08x", pSTrieNode); Print("\n--------------------------------------------------------\n"); Print("Key: Num of Bits : %8d, Value of Bits: %08x\n", pSTrieNode->numBits, pSTrieNode->keyBits);
if (NULL_DEST(pSTrieNode->dest)) { Print("NULL Dest\n"); } else { PrintDest(pSTrieNode->dest); }
Print("Children: On the left %08x, On the right %08x\n", pSTrieNode->child[0], pSTrieNode->child[1]); Print("\n--------------------------------------------------------\n"); Print("\n\n");
PrintSTrieNode(pSTrieNode->child[0]); PrintSTrieNode(pSTrieNode->child[1]); }
#endif // DBG
|