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

1523 lines
46 KiB
C

/*++
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 numBits;
UINT nextBits;
UINT matchBits;
UINT bitsLeft;
UINT distPos;
UINT nextChild;
UINT i, j;
#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]);
do {
// 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;
}
}
}
while (TRUE);
// 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 numBits;
UINT nextBits;
UINT matchBits;
UINT bitsLeft;
UINT distPos;
UINT nextChild;
UINT i, j;
#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]);
do {
// 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);
}
}
while (TRUE);
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;
STrieNode *pNextNode;
STrieNode *pOtherNode;
Dest *pCurrDest;
Dest *pBestDest;
Route *pPrevRoute;
Route *pCurrRoute;
ULONG addrBits;
ULONG tempBits;
UINT numBits;
UINT nextBits;
UINT matchBits;
UINT bitsLeft;
UINT distPos;
UINT nextChild;
UINT bestMetric;
UINT i, j;
#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]);
do {
// 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);
}
}
while (TRUE);
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]);
do {
// 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);
}
while (TRUE);
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;
STrieNode *pOtherNode;
Route *pPrevRoute;
Route *pCurrRoute;
ULONG addrBits;
ULONG tempBits;
UINT numLevels;
UINT numBits;
UINT nextBits;
UINT matchBits;
UINT bitsLeft;
UINT distPos;
UINT nextChild;
UINT i, j;
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 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]);
do {
// 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);
}
while (TRUE);
}
// We have no routes to return at this point
routeToReturn = FALSE;
// Set direction to print route in context
nextChild = LCHILD;
do {
// 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 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 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 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;
}
}
while (TRUE);
}
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 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 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 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;
if (!(pCurrRoute = pDest->firstRoute)) {
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