2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS TCP/IP protocol driver
4 * FILE: network/router.c
5 * PURPOSE: IP routing subsystem
6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
8 * This file holds authoritative routing information.
9 * Information queries on the route table should be handled here.
10 * This information should always override the route cache info.
12 * CSH 01/08-2000 Created
17 LIST_ENTRY FIBListHead
;
20 void RouterDumpRoutes() {
21 PLIST_ENTRY CurrentEntry
;
22 PLIST_ENTRY NextEntry
;
24 PNEIGHBOR_CACHE_ENTRY NCE
;
26 TI_DbgPrint(DEBUG_ROUTER
,("Dumping Routes\n"));
28 CurrentEntry
= FIBListHead
.Flink
;
29 while (CurrentEntry
!= &FIBListHead
) {
30 NextEntry
= CurrentEntry
->Flink
;
31 Current
= CONTAINING_RECORD(CurrentEntry
, FIB_ENTRY
, ListEntry
);
33 NCE
= Current
->Router
;
35 TI_DbgPrint(DEBUG_ROUTER
,("Examining FIBE %x\n", Current
));
36 TI_DbgPrint(DEBUG_ROUTER
,("... NetworkAddress %s\n", A2S(&Current
->NetworkAddress
)));
37 TI_DbgPrint(DEBUG_ROUTER
,("... NCE->Address . %s\n", A2S(&NCE
->Address
)));
39 CurrentEntry
= NextEntry
;
42 TI_DbgPrint(DEBUG_ROUTER
,("Dumping Routes ... Done\n"));
48 * FUNCTION: Frees an forward information base object
50 * Object = Pointer to an forward information base structure
53 ExFreePoolWithTag(Object
, FIB_TAG
);
60 * FUNCTION: Destroys an forward information base entry
62 * FIBE = Pointer to FIB entry
64 * The forward information base lock must be held when called
67 TI_DbgPrint(DEBUG_ROUTER
, ("Called. FIBE (0x%X).\n", FIBE
));
69 /* Unlink the FIB entry from the list */
70 RemoveEntryList(&FIBE
->ListEntry
);
72 /* And free the FIB entry */
80 * FUNCTION: Destroys all forward information base entries
82 * The forward information base lock must be held when called
85 PLIST_ENTRY CurrentEntry
;
86 PLIST_ENTRY NextEntry
;
89 /* Search the list and remove every FIB entry we find */
90 CurrentEntry
= FIBListHead
.Flink
;
91 while (CurrentEntry
!= &FIBListHead
) {
92 NextEntry
= CurrentEntry
->Flink
;
93 Current
= CONTAINING_RECORD(CurrentEntry
, FIB_ENTRY
, ListEntry
);
94 /* Destroy the FIB entry */
96 CurrentEntry
= NextEntry
;
101 UINT
CountFIBs(PIP_INTERFACE IF
) {
103 PLIST_ENTRY CurrentEntry
;
104 PLIST_ENTRY NextEntry
;
107 CurrentEntry
= FIBListHead
.Flink
;
108 while (CurrentEntry
!= &FIBListHead
) {
109 NextEntry
= CurrentEntry
->Flink
;
110 Current
= CONTAINING_RECORD(CurrentEntry
, FIB_ENTRY
, ListEntry
);
111 if (Current
->Router
->Interface
== IF
)
113 CurrentEntry
= NextEntry
;
120 UINT
CopyFIBs( PIP_INTERFACE IF
, PFIB_ENTRY Target
) {
122 PLIST_ENTRY CurrentEntry
;
123 PLIST_ENTRY NextEntry
;
126 CurrentEntry
= FIBListHead
.Flink
;
127 while (CurrentEntry
!= &FIBListHead
) {
128 NextEntry
= CurrentEntry
->Flink
;
129 Current
= CONTAINING_RECORD(CurrentEntry
, FIB_ENTRY
, ListEntry
);
130 if (Current
->Router
->Interface
== IF
)
132 Target
[FibCount
] = *Current
;
135 CurrentEntry
= NextEntry
;
142 UINT
CommonPrefixLength(
143 PIP_ADDRESS Address1
,
144 PIP_ADDRESS Address2
)
146 * FUNCTION: Computes the length of the longest prefix common to two addresses
148 * Address1 = Pointer to first address
149 * Address2 = Pointer to second address
151 * The two addresses must be of the same type
153 * Length of longest common prefix
161 TI_DbgPrint(DEBUG_ROUTER
, ("Called. Address1 (0x%X) Address2 (0x%X).\n", Address1
, Address2
));
163 /*TI_DbgPrint(DEBUG_ROUTER, ("Target (%s) \n", A2S(Address1)));*/
164 /*TI_DbgPrint(DEBUG_ROUTER, ("Adapter (%s).\n", A2S(Address2)));*/
166 if (Address1
->Type
== IP_ADDRESS_V4
)
167 Size
= sizeof(IPv4_RAW_ADDRESS
);
169 Size
= sizeof(IPv6_RAW_ADDRESS
);
171 Addr1
= (PUCHAR
)&Address1
->Address
.IPv4Address
;
172 Addr2
= (PUCHAR
)&Address2
->Address
.IPv4Address
;
174 /* Find first non-matching byte */
175 for (i
= 0; i
< Size
&& Addr1
[i
] == Addr2
[i
]; i
++);
176 if( i
== Size
) return 8 * i
;
178 /* Find first non-matching bit */
180 for (j
= 0; (Addr1
[i
] & Bitmask
) == (Addr2
[i
] & Bitmask
); j
++)
183 TI_DbgPrint(DEBUG_ROUTER
, ("Returning %d\n", 8 * i
+ j
));
189 PFIB_ENTRY
RouterAddRoute(
190 PIP_ADDRESS NetworkAddress
,
192 PNEIGHBOR_CACHE_ENTRY Router
,
195 * FUNCTION: Adds a route to the Forward Information Base (FIB)
197 * NetworkAddress = Pointer to address of network
198 * Netmask = Pointer to netmask of network
199 * Router = Pointer to NCE of router to use
200 * Metric = Cost of this route
202 * Pointer to FIB entry if the route was added, NULL if not
204 * The FIB entry references the NetworkAddress, Netmask and
205 * the NCE of the router. The caller is responsible for providing
211 TI_DbgPrint(DEBUG_ROUTER
, ("Called. NetworkAddress (0x%X) Netmask (0x%X) "
212 "Router (0x%X) Metric (%d).\n", NetworkAddress
, Netmask
, Router
, Metric
));
214 TI_DbgPrint(DEBUG_ROUTER
, ("NetworkAddress (%s) Netmask (%s) Router (%s).\n",
217 A2S(&Router
->Address
)));
219 FIBE
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(FIB_ENTRY
), FIB_TAG
);
221 TI_DbgPrint(MIN_TRACE
, ("Insufficient resources.\n"));
225 RtlCopyMemory( &FIBE
->NetworkAddress
, NetworkAddress
,
226 sizeof(FIBE
->NetworkAddress
) );
227 RtlCopyMemory( &FIBE
->Netmask
, Netmask
,
228 sizeof(FIBE
->Netmask
) );
229 FIBE
->Router
= Router
;
230 FIBE
->Metric
= Metric
;
232 /* Add FIB to the forward information base */
233 TcpipInterlockedInsertTailList(&FIBListHead
, &FIBE
->ListEntry
, &FIBLock
);
239 PNEIGHBOR_CACHE_ENTRY
RouterGetRoute(PIP_ADDRESS Destination
)
241 * FUNCTION: Finds a router to use to get to Destination
243 * Destination = Pointer to destination address (NULL means don't care)
245 * Pointer to NCE for router, NULL if none was found
247 * If found the NCE is referenced
251 PLIST_ENTRY CurrentEntry
;
252 PLIST_ENTRY NextEntry
;
255 UINT Length
, BestLength
= 0, MaskLength
;
256 PNEIGHBOR_CACHE_ENTRY NCE
, BestNCE
= NULL
;
258 TI_DbgPrint(DEBUG_ROUTER
, ("Called. Destination (0x%X)\n", Destination
));
260 TI_DbgPrint(DEBUG_ROUTER
, ("Destination (%s)\n", A2S(Destination
)));
262 TcpipAcquireSpinLock(&FIBLock
, &OldIrql
);
264 CurrentEntry
= FIBListHead
.Flink
;
265 while (CurrentEntry
!= &FIBListHead
) {
266 NextEntry
= CurrentEntry
->Flink
;
267 Current
= CONTAINING_RECORD(CurrentEntry
, FIB_ENTRY
, ListEntry
);
269 NCE
= Current
->Router
;
272 Length
= CommonPrefixLength(Destination
, &Current
->NetworkAddress
);
273 MaskLength
= AddrCountPrefixBits(&Current
->Netmask
);
275 TI_DbgPrint(DEBUG_ROUTER
,("This-Route: %s (Sharing %d bits)\n",
276 A2S(&NCE
->Address
), Length
));
278 if(Length
>= MaskLength
&& (Length
> BestLength
|| !BestNCE
) &&
279 ((!(State
& NUD_STALE
) && !(State
& NUD_INCOMPLETE
)) || !BestNCE
)) {
280 /* This seems to be a better router */
283 TI_DbgPrint(DEBUG_ROUTER
,("Route selected\n"));
286 CurrentEntry
= NextEntry
;
289 TcpipReleaseSpinLock(&FIBLock
, OldIrql
);
292 TI_DbgPrint(DEBUG_ROUTER
,("Routing to %s\n", A2S(&BestNCE
->Address
)));
294 TI_DbgPrint(DEBUG_ROUTER
,("Packet won't be routed\n"));
300 PNEIGHBOR_CACHE_ENTRY
RouteGetRouteToDestination(PIP_ADDRESS Destination
)
302 * FUNCTION: Locates an RCN describing a route to a destination address
304 * Destination = Pointer to destination address to find route to
305 * RCN = Address of pointer to an RCN
307 * Status of operation
309 * The RCN is referenced for the caller. The caller is responsible
310 * for dereferencing it after use
313 PNEIGHBOR_CACHE_ENTRY NCE
= NULL
;
314 PIP_INTERFACE Interface
;
316 TI_DbgPrint(DEBUG_RCACHE
, ("Called. Destination (0x%X)\n", Destination
));
318 TI_DbgPrint(DEBUG_RCACHE
, ("Destination (%s)\n", A2S(Destination
)));
321 TI_DbgPrint(MIN_TRACE
, ("Displaying tree (before).\n"));
322 PrintTree(RouteCache
);
325 /* Check if the destination is on-link */
326 Interface
= FindOnLinkInterface(Destination
);
328 /* The destination address is on-link. Check our neighbor cache */
329 NCE
= NBFindOrCreateNeighbor(Interface
, Destination
, FALSE
);
331 /* Destination is not on any subnets we're on. Find a router to use */
332 NCE
= RouterGetRoute(Destination
);
336 TI_DbgPrint(DEBUG_ROUTER
,("Interface->MTU: %d\n", NCE
->Interface
->MTU
));
341 VOID
RouterRemoveRoutesForInterface(PIP_INTERFACE Interface
)
344 PLIST_ENTRY CurrentEntry
;
345 PLIST_ENTRY NextEntry
;
348 TcpipAcquireSpinLock(&FIBLock
, &OldIrql
);
350 CurrentEntry
= FIBListHead
.Flink
;
351 while (CurrentEntry
!= &FIBListHead
) {
352 NextEntry
= CurrentEntry
->Flink
;
353 Current
= CONTAINING_RECORD(CurrentEntry
, FIB_ENTRY
, ListEntry
);
355 if (Interface
== Current
->Router
->Interface
)
356 DestroyFIBE(Current
);
358 CurrentEntry
= NextEntry
;
361 TcpipReleaseSpinLock(&FIBLock
, OldIrql
);
364 NTSTATUS
RouterRemoveRoute(PIP_ADDRESS Target
, PIP_ADDRESS Router
)
366 * FUNCTION: Removes a route from the Forward Information Base (FIB)
368 * Target: The machine or network targeted by the route
369 * Router: The router used to pass the packet to the destination
371 * Searches the FIB and removes a route matching the indicated parameters.
375 PLIST_ENTRY CurrentEntry
;
376 PLIST_ENTRY NextEntry
;
378 BOOLEAN Found
= FALSE
;
379 PNEIGHBOR_CACHE_ENTRY NCE
;
381 TI_DbgPrint(DEBUG_ROUTER
, ("Called\n"));
382 TI_DbgPrint(DEBUG_ROUTER
, ("Deleting Route From: %s\n", A2S(Router
)));
383 TI_DbgPrint(DEBUG_ROUTER
, (" To: %s\n", A2S(Target
)));
385 TcpipAcquireSpinLock(&FIBLock
, &OldIrql
);
389 CurrentEntry
= FIBListHead
.Flink
;
390 while (CurrentEntry
!= &FIBListHead
) {
391 NextEntry
= CurrentEntry
->Flink
;
392 Current
= CONTAINING_RECORD(CurrentEntry
, FIB_ENTRY
, ListEntry
);
394 NCE
= Current
->Router
;
396 if( AddrIsEqual( &Current
->NetworkAddress
, Target
) &&
397 AddrIsEqual( &NCE
->Address
, Router
) ) {
403 CurrentEntry
= NextEntry
;
407 TI_DbgPrint(DEBUG_ROUTER
, ("Deleting route\n"));
408 DestroyFIBE( Current
);
413 TcpipReleaseSpinLock(&FIBLock
, OldIrql
);
415 TI_DbgPrint(DEBUG_ROUTER
, ("Leaving\n"));
417 return Found
? STATUS_SUCCESS
: STATUS_UNSUCCESSFUL
;
421 PFIB_ENTRY
RouterCreateRoute(
422 PIP_ADDRESS NetworkAddress
,
424 PIP_ADDRESS RouterAddress
,
425 PIP_INTERFACE Interface
,
428 * FUNCTION: Creates a route with IPv4 addresses as parameters
430 * NetworkAddress = Address of network
431 * Netmask = Netmask of network
432 * RouterAddress = Address of router to use
433 * NTE = Pointer to NTE to use
434 * Metric = Cost of this route
436 * Pointer to FIB entry if the route was created, NULL if not.
437 * The FIB entry references the NTE. The caller is responsible
438 * for providing this reference
442 PLIST_ENTRY CurrentEntry
;
443 PLIST_ENTRY NextEntry
;
445 PNEIGHBOR_CACHE_ENTRY NCE
;
447 TcpipAcquireSpinLock(&FIBLock
, &OldIrql
);
449 CurrentEntry
= FIBListHead
.Flink
;
450 while (CurrentEntry
!= &FIBListHead
) {
451 NextEntry
= CurrentEntry
->Flink
;
452 Current
= CONTAINING_RECORD(CurrentEntry
, FIB_ENTRY
, ListEntry
);
454 NCE
= Current
->Router
;
456 if(AddrIsEqual(NetworkAddress
, &Current
->NetworkAddress
) &&
457 AddrIsEqual(Netmask
, &Current
->Netmask
) &&
458 NCE
->Interface
== Interface
)
460 TI_DbgPrint(DEBUG_ROUTER
,("Attempting to add duplicate route to %s\n", A2S(NetworkAddress
)));
461 TcpipReleaseSpinLock(&FIBLock
, OldIrql
);
465 CurrentEntry
= NextEntry
;
468 TcpipReleaseSpinLock(&FIBLock
, OldIrql
);
470 /* The NCE references RouterAddress. The NCE is referenced for us */
471 NCE
= NBFindOrCreateNeighbor(Interface
, RouterAddress
, TRUE
);
474 /* Not enough free resources */
478 return RouterAddRoute(NetworkAddress
, Netmask
, NCE
, Metric
);
482 NTSTATUS
RouterStartup(
485 * FUNCTION: Initializes the routing subsystem
487 * Status of operation
490 TI_DbgPrint(DEBUG_ROUTER
, ("Called.\n"));
492 /* Initialize the Forward Information Base */
493 InitializeListHead(&FIBListHead
);
494 TcpipInitializeSpinLock(&FIBLock
);
496 return STATUS_SUCCESS
;
500 NTSTATUS
RouterShutdown(
503 * FUNCTION: Shuts down the routing subsystem
505 * Status of operation
510 TI_DbgPrint(DEBUG_ROUTER
, ("Called.\n"));
512 /* Clear Forward Information Base */
513 TcpipAcquireSpinLock(&FIBLock
, &OldIrql
);
515 TcpipReleaseSpinLock(&FIBLock
, OldIrql
);
517 return STATUS_SUCCESS
;