reactos/sdk/lib/drivers/ip/network/router.c

521 lines
14 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS TCP/IP protocol driver
* FILE: network/router.c
* PURPOSE: IP routing subsystem
* PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
* NOTES:
* This file holds authoritative routing information.
* Information queries on the route table should be handled here.
* This information should always override the route cache info.
* REVISIONS:
* CSH 01/08-2000 Created
*/
#include "precomp.h"
LIST_ENTRY FIBListHead;
KSPIN_LOCK FIBLock;
void RouterDumpRoutes() {
PLIST_ENTRY CurrentEntry;
PLIST_ENTRY NextEntry;
PFIB_ENTRY Current;
PNEIGHBOR_CACHE_ENTRY NCE;
TI_DbgPrint(DEBUG_ROUTER,("Dumping Routes\n"));
CurrentEntry = FIBListHead.Flink;
while (CurrentEntry != &FIBListHead) {
NextEntry = CurrentEntry->Flink;
Current = CONTAINING_RECORD(CurrentEntry, FIB_ENTRY, ListEntry);
NCE = Current->Router;
TI_DbgPrint(DEBUG_ROUTER,("Examining FIBE %x\n", Current));
TI_DbgPrint(DEBUG_ROUTER,("... NetworkAddress %s\n", A2S(&Current->NetworkAddress)));
TI_DbgPrint(DEBUG_ROUTER,("... NCE->Address . %s\n", A2S(&NCE->Address)));
CurrentEntry = NextEntry;
}
TI_DbgPrint(DEBUG_ROUTER,("Dumping Routes ... Done\n"));
}
VOID FreeFIB(
PVOID Object)
/*
* FUNCTION: Frees an forward information base object
* ARGUMENTS:
* Object = Pointer to an forward information base structure
*/
{
ExFreePoolWithTag(Object, FIB_TAG);
}
VOID DestroyFIBE(
PFIB_ENTRY FIBE)
/*
* FUNCTION: Destroys an forward information base entry
* ARGUMENTS:
* FIBE = Pointer to FIB entry
* NOTES:
* The forward information base lock must be held when called
*/
{
TI_DbgPrint(DEBUG_ROUTER, ("Called. FIBE (0x%X).\n", FIBE));
/* Unlink the FIB entry from the list */
RemoveEntryList(&FIBE->ListEntry);
/* And free the FIB entry */
FreeFIB(FIBE);
}
VOID DestroyFIBEs(
VOID)
/*
* FUNCTION: Destroys all forward information base entries
* NOTES:
* The forward information base lock must be held when called
*/
{
PLIST_ENTRY CurrentEntry;
PLIST_ENTRY NextEntry;
PFIB_ENTRY Current;
/* Search the list and remove every FIB entry we find */
CurrentEntry = FIBListHead.Flink;
while (CurrentEntry != &FIBListHead) {
NextEntry = CurrentEntry->Flink;
Current = CONTAINING_RECORD(CurrentEntry, FIB_ENTRY, ListEntry);
/* Destroy the FIB entry */
DestroyFIBE(Current);
CurrentEntry = NextEntry;
}
}
UINT CountFIBs(PIP_INTERFACE IF) {
UINT FibCount = 0;
PLIST_ENTRY CurrentEntry;
PLIST_ENTRY NextEntry;
PFIB_ENTRY Current;
CurrentEntry = FIBListHead.Flink;
while (CurrentEntry != &FIBListHead) {
NextEntry = CurrentEntry->Flink;
Current = CONTAINING_RECORD(CurrentEntry, FIB_ENTRY, ListEntry);
if (Current->Router->Interface == IF)
FibCount++;
CurrentEntry = NextEntry;
}
return FibCount;
}
UINT CopyFIBs( PIP_INTERFACE IF, PFIB_ENTRY Target ) {
UINT FibCount = 0;
PLIST_ENTRY CurrentEntry;
PLIST_ENTRY NextEntry;
PFIB_ENTRY Current;
CurrentEntry = FIBListHead.Flink;
while (CurrentEntry != &FIBListHead) {
NextEntry = CurrentEntry->Flink;
Current = CONTAINING_RECORD(CurrentEntry, FIB_ENTRY, ListEntry);
if (Current->Router->Interface == IF)
{
Target[FibCount] = *Current;
FibCount++;
}
CurrentEntry = NextEntry;
}
return FibCount;
}
UINT CommonPrefixLength(
PIP_ADDRESS Address1,
PIP_ADDRESS Address2)
/*
* FUNCTION: Computes the length of the longest prefix common to two addresses
* ARGUMENTS:
* Address1 = Pointer to first address
* Address2 = Pointer to second address
* NOTES:
* The two addresses must be of the same type
* RETURNS:
* Length of longest common prefix
*/
{
PUCHAR Addr1, Addr2;
UINT Size;
UINT i, j;
UINT Bitmask;
TI_DbgPrint(DEBUG_ROUTER, ("Called. Address1 (0x%X) Address2 (0x%X).\n", Address1, Address2));
/*TI_DbgPrint(DEBUG_ROUTER, ("Target (%s) \n", A2S(Address1)));*/
/*TI_DbgPrint(DEBUG_ROUTER, ("Adapter (%s).\n", A2S(Address2)));*/
if (Address1->Type == IP_ADDRESS_V4)
Size = sizeof(IPv4_RAW_ADDRESS);
else
Size = sizeof(IPv6_RAW_ADDRESS);
Addr1 = (PUCHAR)&Address1->Address.IPv4Address;
Addr2 = (PUCHAR)&Address2->Address.IPv4Address;
/* Find first non-matching byte */
for (i = 0; i < Size && Addr1[i] == Addr2[i]; i++);
if( i == Size ) return 8 * i;
/* Find first non-matching bit */
Bitmask = 0x80;
for (j = 0; (Addr1[i] & Bitmask) == (Addr2[i] & Bitmask); j++)
Bitmask >>= 1;
TI_DbgPrint(DEBUG_ROUTER, ("Returning %d\n", 8 * i + j));
return 8 * i + j;
}
PFIB_ENTRY RouterAddRoute(
PIP_ADDRESS NetworkAddress,
PIP_ADDRESS Netmask,
PNEIGHBOR_CACHE_ENTRY Router,
UINT Metric)
/*
* FUNCTION: Adds a route to the Forward Information Base (FIB)
* ARGUMENTS:
* NetworkAddress = Pointer to address of network
* Netmask = Pointer to netmask of network
* Router = Pointer to NCE of router to use
* Metric = Cost of this route
* RETURNS:
* Pointer to FIB entry if the route was added, NULL if not
* NOTES:
* The FIB entry references the NetworkAddress, Netmask and
* the NCE of the router. The caller is responsible for providing
* these references
*/
{
PFIB_ENTRY FIBE;
TI_DbgPrint(DEBUG_ROUTER, ("Called. NetworkAddress (0x%X) Netmask (0x%X) "
"Router (0x%X) Metric (%d).\n", NetworkAddress, Netmask, Router, Metric));
TI_DbgPrint(DEBUG_ROUTER, ("NetworkAddress (%s) Netmask (%s) Router (%s).\n",
A2S(NetworkAddress),
A2S(Netmask),
A2S(&Router->Address)));
FIBE = ExAllocatePoolWithTag(NonPagedPool, sizeof(FIB_ENTRY), FIB_TAG);
if (!FIBE) {
TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
return NULL;
}
RtlCopyMemory( &FIBE->NetworkAddress, NetworkAddress,
sizeof(FIBE->NetworkAddress) );
RtlCopyMemory( &FIBE->Netmask, Netmask,
sizeof(FIBE->Netmask) );
FIBE->Router = Router;
FIBE->Metric = Metric;
/* Add FIB to the forward information base */
TcpipInterlockedInsertTailList(&FIBListHead, &FIBE->ListEntry, &FIBLock);
return FIBE;
}
PNEIGHBOR_CACHE_ENTRY RouterGetRoute(PIP_ADDRESS Destination)
/*
* FUNCTION: Finds a router to use to get to Destination
* ARGUMENTS:
* Destination = Pointer to destination address (NULL means don't care)
* RETURNS:
* Pointer to NCE for router, NULL if none was found
* NOTES:
* If found the NCE is referenced
*/
{
KIRQL OldIrql;
PLIST_ENTRY CurrentEntry;
PLIST_ENTRY NextEntry;
PFIB_ENTRY Current;
UCHAR State;
UINT Length, BestLength = 0, MaskLength;
PNEIGHBOR_CACHE_ENTRY NCE, BestNCE = NULL;
TI_DbgPrint(DEBUG_ROUTER, ("Called. Destination (0x%X)\n", Destination));
TI_DbgPrint(DEBUG_ROUTER, ("Destination (%s)\n", A2S(Destination)));
TcpipAcquireSpinLock(&FIBLock, &OldIrql);
CurrentEntry = FIBListHead.Flink;
while (CurrentEntry != &FIBListHead) {
NextEntry = CurrentEntry->Flink;
Current = CONTAINING_RECORD(CurrentEntry, FIB_ENTRY, ListEntry);
NCE = Current->Router;
State = NCE->State;
Length = CommonPrefixLength(Destination, &Current->NetworkAddress);
MaskLength = AddrCountPrefixBits(&Current->Netmask);
TI_DbgPrint(DEBUG_ROUTER,("This-Route: %s (Sharing %d bits)\n",
A2S(&NCE->Address), Length));
if(Length >= MaskLength && (Length > BestLength || !BestNCE) &&
((!(State & NUD_STALE) && !(State & NUD_INCOMPLETE)) || !BestNCE)) {
/* This seems to be a better router */
BestNCE = NCE;
BestLength = Length;
TI_DbgPrint(DEBUG_ROUTER,("Route selected\n"));
}
CurrentEntry = NextEntry;
}
TcpipReleaseSpinLock(&FIBLock, OldIrql);
if( BestNCE ) {
TI_DbgPrint(DEBUG_ROUTER,("Routing to %s\n", A2S(&BestNCE->Address)));
} else {
TI_DbgPrint(DEBUG_ROUTER,("Packet won't be routed\n"));
}
return BestNCE;
}
PNEIGHBOR_CACHE_ENTRY RouteGetRouteToDestination(PIP_ADDRESS Destination)
/*
* FUNCTION: Locates an RCN describing a route to a destination address
* ARGUMENTS:
* Destination = Pointer to destination address to find route to
* RCN = Address of pointer to an RCN
* RETURNS:
* Status of operation
* NOTES:
* The RCN is referenced for the caller. The caller is responsible
* for dereferencing it after use
*/
{
PNEIGHBOR_CACHE_ENTRY NCE = NULL;
PIP_INTERFACE Interface;
TI_DbgPrint(DEBUG_RCACHE, ("Called. Destination (0x%X)\n", Destination));
TI_DbgPrint(DEBUG_RCACHE, ("Destination (%s)\n", A2S(Destination)));
#if 0
TI_DbgPrint(MIN_TRACE, ("Displaying tree (before).\n"));
PrintTree(RouteCache);
#endif
/* Check if the destination is on-link */
Interface = FindOnLinkInterface(Destination);
if (Interface) {
/* The destination address is on-link. Check our neighbor cache */
NCE = NBFindOrCreateNeighbor(Interface, Destination, FALSE);
} else {
/* Destination is not on any subnets we're on. Find a router to use */
NCE = RouterGetRoute(Destination);
}
if( NCE )
TI_DbgPrint(DEBUG_ROUTER,("Interface->MTU: %d\n", NCE->Interface->MTU));
return NCE;
}
VOID RouterRemoveRoutesForInterface(PIP_INTERFACE Interface)
{
KIRQL OldIrql;
PLIST_ENTRY CurrentEntry;
PLIST_ENTRY NextEntry;
PFIB_ENTRY Current;
TcpipAcquireSpinLock(&FIBLock, &OldIrql);
CurrentEntry = FIBListHead.Flink;
while (CurrentEntry != &FIBListHead) {
NextEntry = CurrentEntry->Flink;
Current = CONTAINING_RECORD(CurrentEntry, FIB_ENTRY, ListEntry);
if (Interface == Current->Router->Interface)
DestroyFIBE(Current);
CurrentEntry = NextEntry;
}
TcpipReleaseSpinLock(&FIBLock, OldIrql);
}
NTSTATUS RouterRemoveRoute(PIP_ADDRESS Target, PIP_ADDRESS Router)
/*
* FUNCTION: Removes a route from the Forward Information Base (FIB)
* ARGUMENTS:
* Target: The machine or network targeted by the route
* Router: The router used to pass the packet to the destination
*
* Searches the FIB and removes a route matching the indicated parameters.
*/
{
KIRQL OldIrql;
PLIST_ENTRY CurrentEntry;
PLIST_ENTRY NextEntry;
PFIB_ENTRY Current;
BOOLEAN Found = FALSE;
PNEIGHBOR_CACHE_ENTRY NCE;
TI_DbgPrint(DEBUG_ROUTER, ("Called\n"));
TI_DbgPrint(DEBUG_ROUTER, ("Deleting Route From: %s\n", A2S(Router)));
TI_DbgPrint(DEBUG_ROUTER, (" To: %s\n", A2S(Target)));
TcpipAcquireSpinLock(&FIBLock, &OldIrql);
RouterDumpRoutes();
CurrentEntry = FIBListHead.Flink;
while (CurrentEntry != &FIBListHead) {
NextEntry = CurrentEntry->Flink;
Current = CONTAINING_RECORD(CurrentEntry, FIB_ENTRY, ListEntry);
NCE = Current->Router;
if( AddrIsEqual( &Current->NetworkAddress, Target ) &&
AddrIsEqual( &NCE->Address, Router ) ) {
Found = TRUE;
break;
}
Current = NULL;
CurrentEntry = NextEntry;
}
if( Found ) {
TI_DbgPrint(DEBUG_ROUTER, ("Deleting route\n"));
DestroyFIBE( Current );
}
RouterDumpRoutes();
TcpipReleaseSpinLock(&FIBLock, OldIrql);
TI_DbgPrint(DEBUG_ROUTER, ("Leaving\n"));
return Found ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
}
PFIB_ENTRY RouterCreateRoute(
PIP_ADDRESS NetworkAddress,
PIP_ADDRESS Netmask,
PIP_ADDRESS RouterAddress,
PIP_INTERFACE Interface,
UINT Metric)
/*
* FUNCTION: Creates a route with IPv4 addresses as parameters
* ARGUMENTS:
* NetworkAddress = Address of network
* Netmask = Netmask of network
* RouterAddress = Address of router to use
* NTE = Pointer to NTE to use
* Metric = Cost of this route
* RETURNS:
* Pointer to FIB entry if the route was created, NULL if not.
* The FIB entry references the NTE. The caller is responsible
* for providing this reference
*/
{
KIRQL OldIrql;
PLIST_ENTRY CurrentEntry;
PLIST_ENTRY NextEntry;
PFIB_ENTRY Current;
PNEIGHBOR_CACHE_ENTRY NCE;
TcpipAcquireSpinLock(&FIBLock, &OldIrql);
CurrentEntry = FIBListHead.Flink;
while (CurrentEntry != &FIBListHead) {
NextEntry = CurrentEntry->Flink;
Current = CONTAINING_RECORD(CurrentEntry, FIB_ENTRY, ListEntry);
NCE = Current->Router;
if(AddrIsEqual(NetworkAddress, &Current->NetworkAddress) &&
AddrIsEqual(Netmask, &Current->Netmask) &&
NCE->Interface == Interface)
{
TI_DbgPrint(DEBUG_ROUTER,("Attempting to add duplicate route to %s\n", A2S(NetworkAddress)));
TcpipReleaseSpinLock(&FIBLock, OldIrql);
return NULL;
}
CurrentEntry = NextEntry;
}
TcpipReleaseSpinLock(&FIBLock, OldIrql);
/* The NCE references RouterAddress. The NCE is referenced for us */
NCE = NBFindOrCreateNeighbor(Interface, RouterAddress, TRUE);
if (!NCE) {
/* Not enough free resources */
return NULL;
}
return RouterAddRoute(NetworkAddress, Netmask, NCE, Metric);
}
NTSTATUS RouterStartup(
VOID)
/*
* FUNCTION: Initializes the routing subsystem
* RETURNS:
* Status of operation
*/
{
TI_DbgPrint(DEBUG_ROUTER, ("Called.\n"));
/* Initialize the Forward Information Base */
InitializeListHead(&FIBListHead);
TcpipInitializeSpinLock(&FIBLock);
return STATUS_SUCCESS;
}
NTSTATUS RouterShutdown(
VOID)
/*
* FUNCTION: Shuts down the routing subsystem
* RETURNS:
* Status of operation
*/
{
KIRQL OldIrql;
TI_DbgPrint(DEBUG_ROUTER, ("Called.\n"));
/* Clear Forward Information Base */
TcpipAcquireSpinLock(&FIBLock, &OldIrql);
DestroyFIBEs();
TcpipReleaseSpinLock(&FIBLock, OldIrql);
return STATUS_SUCCESS;
}
/* EOF */