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