2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS TCP/IP protocol driver
4 * FILE: network/route.c
6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * NOTES: The route cache is implemented as a binary search
8 * tree to obtain fast searches
10 * CSH 01/08-2000 Created
17 /* This RCN is shared by all external nodes. It complicates things,
18 but the memory requirements are reduced by approximately 50%.
19 The RCN is protected by the route cache spin lock */
20 PROUTE_CACHE_NODE ExternalRCN;
21 PROUTE_CACHE_NODE RouteCache;
22 KSPIN_LOCK RouteCacheLock;
23 NPAGED_LOOKASIDE_LIST IPRCNList;
28 PROUTE_CACHE_NODE Node)
30 * FUNCTION: Prints all nodes on tree
32 * Node = Pointer to root node of tree
34 * This function must be called with the route cache lock held.
37 if (IsInternalRCN(Node)) {
38 /* Traverse left subtree */
39 PrintTree(Node->Left);
41 /* Traverse right subtree */
42 PrintTree(Node->Right);
44 /* Finally check the node itself */
45 TI_DbgPrint(MIN_TRACE, ("(Internal) Self,Parent,Left,Right,Data = (%08X, %08X, %08X, %08X, %08X).\n",
46 Node, Node->Parent, Node->Left, Node->Right, (ULONG_PTR)Node->Destination.Address.IPv4Address));
48 TI_DbgPrint(MIN_TRACE, ("(External) Self,Parent,Left,Right = (%08X, %08X, %08X, %08X).\n",
49 Node, Node->Parent, Node->Left, Node->Right));
57 * FUNCTION: Frees an route cache node object
59 * Object = Pointer to an route cache node structure
62 ExFreeToNPagedLookasideList(&IPRCNList, Object);
66 VOID RemoveAboveExternal(VOID)
68 * FUNCTION: Removes the parent node of the selected external node from the route cache tree
70 * This function must be called with the route cache lock held.
71 * ExternalRCN->Parent must be initialized
74 PROUTE_CACHE_NODE Parent;
75 PROUTE_CACHE_NODE Sibling;
77 TI_DbgPrint(DEBUG_RCACHE, ("Called.\n"));
80 TI_DbgPrint(MIN_TRACE, ("Displaying tree (before).\n"));
81 PrintTree(RouteCache);
84 Parent = ExternalRCN->Parent;
85 /* Find sibling of external node */
86 if (ExternalRCN == Parent->Left)
87 Sibling = Parent->Right;
89 Sibling = Parent->Left;
91 /* Replace parent node with sibling of external node */
92 if (Parent != RouteCache) {
93 if (Parent->Parent->Left == Parent)
94 Parent->Parent->Left = Sibling;
96 Parent->Parent->Right = Sibling;
97 /* Give sibling a new parent */
98 Sibling->Parent = Parent->Parent;
100 /* This is the root we're removing */
101 RouteCache = Sibling;
102 Sibling->Parent = NULL;
105 DereferenceObject(Parent);
108 TI_DbgPrint(MIN_TRACE, ("Displaying tree (after).\n"));
109 PrintTree(RouteCache);
114 PROUTE_CACHE_NODE SearchRouteCache(
115 PIP_ADDRESS Destination,
116 PROUTE_CACHE_NODE Node)
118 * FUNCTION: Searches route cache for a RCN for a destination address
120 * Destination = Pointer to destination address (key)
121 * Node = Pointer to start route cache node
123 * This function must be called with the route cache lock held
125 * Pointer to internal node if a matching node was found, or
126 * external node where it should be if none was found
131 TI_DbgPrint(DEBUG_RCACHE, ("Called. Destination (0x%X) Node (0x%X)\n", Destination, Node));
133 /* Is this an external node? */
134 if (IsExternalRCN(Node))
137 /* Is it this node we are looking for? */
138 Value = AddrCompare(Destination, &Node->Destination);
142 /* Traverse down the left subtree if the key is smaller than
143 the key of the node, otherwise traverse the right subtree */
145 Node->Left->Parent = Node;
146 ExternalRCN->Left = (PROUTE_CACHE_NODE)&Node->Left;
147 return SearchRouteCache(Destination, Node->Left);
149 Node->Right->Parent = Node;
150 ExternalRCN->Left = (PROUTE_CACHE_NODE)&Node->Right;
151 return SearchRouteCache(Destination, Node->Right);
156 PROUTE_CACHE_NODE ExpandExternalRCN(VOID)
158 * FUNCTION: Expands an external route cache node
160 * This function must be called with the route cache lock held.
161 * We cheat a little here to save memory. We don't actually allocate memory
162 * for external nodes. We wait until they're turned into internal nodes.
163 * ExternalRCN->Parent must be initialized
164 * ExternalRCN->Left must be a pointer to the correct child link of it's parent
166 * Pointer to new internal node if the external node was expanded, NULL if not
169 PROUTE_CACHE_NODE RCN;
171 TI_DbgPrint(DEBUG_RCACHE, ("Called.\n"));
173 RCN = ExAllocateFromNPagedLookasideList(&IPRCNList);
175 TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
181 if (ExternalRCN->Left)
182 /* Register RCN as a child with it's parent */
183 *(PROUTE_CACHE_NODE*)ExternalRCN->Left = RCN;
185 RCN->Parent = ExternalRCN->Parent;
186 RCN->Left = ExternalRCN;
187 RCN->Right = ExternalRCN;
194 PROUTE_CACHE_NODE *Node1,
195 PROUTE_CACHE_NODE *Node2)
197 * FUNCTION: Swaps two nodes
199 * Node1 = Address of pointer to first node
200 * Node2 = Address of pointer to second node
203 PROUTE_CACHE_NODE Temp;
212 * FUNCTION: Removes a route to a destination
214 * RCN = Pointer to route cache node to remove
216 * Internal version. Route cache lock must be held
218 VOID RemoveRouteToDestination(
219 PROUTE_CACHE_NODE RCN)
221 PROUTE_CACHE_NODE RemNode, Parent, SwapNode;
223 TI_DbgPrint(DEBUG_RCACHE, ("Called. RCN (0x%X).\n", RCN));
225 if (IsExternalRCN(RCN->Left)) {
226 /* Left node is external */
228 RemNode->Parent = RCN;
229 } else if (IsExternalRCN(RCN->Right)) {
230 /* Right node is external */
231 RemNode = RCN->Right;
232 RemNode->Parent = RCN;
234 /* The node has internal children */
236 /* Normally we would replace the item of RCN with the item
237 of the leftmost external node on the right subtree of
238 RCN. This we cannot do here because there may be
239 references directly to that node. Instead we swap pointer
240 values (parent, left and right) of the two nodes */
241 RemNode = RCN->Right;
244 RemNode = RemNode->Left;
245 } while (IsInternalRCN(RemNode));
246 RemNode->Parent = Parent;
248 SwapNode = RemNode->Parent;
250 if (RCN != RouteCache) {
251 /* Set SwapNode to be child of RCN's parent instead of RCN */
252 Parent = RCN->Parent;
253 if (RCN == Parent->Left)
254 Parent->Left = SwapNode;
256 Parent->Right = SwapNode;
258 /* SwapNode is the new cache root */
259 RouteCache = SwapNode;
261 /* Set RCN to be child of SwapNode's parent instead of SwapNode */
262 Parent = SwapNode->Parent;
263 if (SwapNode == Parent->Left)
269 SwapRCN(&SwapNode->Parent, &RCN->Parent);
271 SwapRCN(&SwapNode->Left, &RCN->Left);
272 SwapRCN(&SwapNode->Right, &RCN->Right);
276 /* Dereference NTE and NCE */
277 DereferenceObject(RCN->NTE);
278 DereferenceObject(RCN->NCE);
280 ExternalRCN->Parent = RemNode->Parent;
282 RemoveAboveExternal();
286 VOID InvalidateNTEOnSubtree(
287 PNET_TABLE_ENTRY NTE,
288 PROUTE_CACHE_NODE Node)
290 * FUNCTION: Removes all RCNs with references to an NTE on a subtree
292 * NTE = Pointer to NTE to invalidate
293 * Node = Pointer to RCN to start removing nodes at
295 * This function must be called with the route cache lock held.
298 TI_DbgPrint(DEBUG_RCACHE, ("Called. NTE (0x%X) Node (0x%X).\n", NTE, Node));
300 if (IsInternalRCN(Node)) {
301 /* Traverse left subtree */
302 InvalidateNTEOnSubtree(NTE, Node->Left);
304 /* Traverse right subtree */
305 InvalidateNTEOnSubtree(NTE, Node->Right);
307 /* Finally check the node itself */
308 if (Node->NTE == NTE)
309 RemoveRouteToDestination(Node);
314 VOID InvalidateNCEOnSubtree(
315 PNEIGHBOR_CACHE_ENTRY NCE,
316 PROUTE_CACHE_NODE Node)
318 * FUNCTION: Removes all RCNs with references to an NCE on a subtree
320 * NCE = Pointer to NCE to invalidate
321 * Node = Pointer to RCN to start removing nodes at
323 * This function must be called with the route cache lock held
326 TI_DbgPrint(DEBUG_RCACHE, ("Called. NCE (0x%X) Node (0x%X).\n", NCE, Node));
328 if (IsInternalRCN(Node)) {
329 /* Traverse left subtree */
330 InvalidateNCEOnSubtree(NCE, Node->Left);
332 /* Traverse right subtree */
333 InvalidateNCEOnSubtree(NCE, Node->Right);
335 /* Finally check the node itself */
336 if (Node->NCE == NCE)
337 RemoveRouteToDestination(Node);
343 PROUTE_CACHE_NODE Node)
345 * FUNCTION: Removes a subtree from the tree using recursion
347 * Node = Pointer to RCN to start removing nodes at
349 * This function must be called with the route cache lock held
352 TI_DbgPrint(DEBUG_RCACHE, ("Called. Node (0x%X).\n", Node));
354 if (IsInternalRCN(Node)) {
355 /* Traverse left subtree */
356 RemoveSubtree(Node->Left);
358 /* Traverse right subtree */
359 RemoveSubtree(Node->Right);
361 /* Finally remove the node itself */
363 /* It's an internal node, so dereference NTE and NCE */
364 DereferenceObject(Node->NTE);
365 DereferenceObject(Node->NCE);
368 if (Node->RefCount != 1)
369 TI_DbgPrint(MIN_TRACE, ("RCN at (0x%X) has (%d) references (should be 1).\n", Node, Node->RefCount));
372 /* Remove reference for being alive */
373 DereferenceObject(Node);
378 NTSTATUS RouteStartup(
381 * FUNCTION: Initializes the routing subsystem
383 * Status of operation
386 TI_DbgPrint(DEBUG_RCACHE, ("Called.\n"));
388 ExInitializeNPagedLookasideList(
389 &IPRCNList, /* Lookaside list */
390 NULL, /* Allocate routine */
391 NULL, /* Free routine */
393 sizeof(ROUTE_CACHE_NODE), /* Size of each entry */
394 TAG('I','P','R','C'), /* Tag */
397 /* Initialize the pseudo external route cache node */
398 ExternalRCN = ExAllocateFromNPagedLookasideList(&IPRCNList);
400 TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
401 return STATUS_INSUFFICIENT_RESOURCES;
403 INIT_TAG(ExternalRCN, TAG('R','C','N',' '));
405 ExternalRCN->Free = FreeRCN;
406 ExternalRCN->Parent = NULL;
407 ExternalRCN->Left = NULL;
408 ExternalRCN->Right = NULL;
410 /* Initialize the route cache root */
411 RouteCache = ExternalRCN;
413 KeInitializeSpinLock(&RouteCacheLock);
416 TI_DbgPrint(MIN_TRACE, ("Displaying tree.\n"));
417 PrintTree(RouteCache);
419 return STATUS_SUCCESS;
423 NTSTATUS RouteShutdown(
426 * FUNCTION: Shuts down the routing subsystem
428 * Status of operation
433 TI_DbgPrint(DEBUG_RCACHE, ("Called.\n"));
435 KeAcquireSpinLock(&RouteCacheLock, &OldIrql);
437 TI_DbgPrint(MIN_TRACE, ("Displaying tree.\n"));
438 PrintTree(RouteCache);
440 /* Clear route cache */
441 RemoveSubtree(RouteCache);
443 FreeRCN(ExternalRCN);
445 KeReleaseSpinLock(&RouteCacheLock, OldIrql);
447 ExDeleteNPagedLookasideList(&IPRCNList);
449 return STATUS_SUCCESS;
453 UINT RouteGetRouteToDestination(
454 PIP_ADDRESS Destination,
455 PNET_TABLE_ENTRY NTE,
456 PROUTE_CACHE_NODE *RCN)
458 * FUNCTION: Locates an RCN describing a route to a destination address
460 * Destination = Pointer to destination address to find route to
461 * NTE = Pointer to NTE describing net to send on
462 * (NULL means routing module choose NTE to send on)
463 * RCN = Address of pointer to an RCN
465 * Status of operation
467 * The RCN is referenced for the caller. The caller is responsible
468 * for dereferencing it after use
472 PROUTE_CACHE_NODE RCN2;
473 PNEIGHBOR_CACHE_ENTRY NCE;
474 PIP_INTERFACE Interface;
476 TI_DbgPrint(DEBUG_RCACHE, ("Called. Destination (0x%X) NTE (0x%X).\n",
479 TI_DbgPrint(DEBUG_RCACHE, ("Destination (%s) NTE (%s).\n",
480 A2S(Destination), A2S(NTE->Address)));
482 KeAcquireSpinLock(&RouteCacheLock, &OldIrql);
485 TI_DbgPrint(MIN_TRACE, ("Displaying tree (before).\n"));
486 PrintTree(RouteCache);
489 ExternalRCN->Left = NULL;
490 RCN2 = SearchRouteCache(Destination, RouteCache);
491 if (IsExternalRCN(RCN2)) {
492 /* No route was found in the cache */
494 /* Check if the destination is on-link */
495 Interface = RouterFindOnLinkInterface(Destination, NTE);
498 NTE = RouterFindBestNTE(Interface, Destination);
500 /* We cannot get to the specified destination. Return error */
501 KeReleaseSpinLock(&RouteCacheLock, OldIrql);
502 return IP_NO_ROUTE_TO_DESTINATION;
505 ReferenceObject(NTE);
507 /* The destination address is on-link. Check our neighbor cache */
508 NCE = NBFindOrCreateNeighbor(Interface, Destination);
510 DereferenceObject(NTE);
511 KeReleaseSpinLock(&RouteCacheLock, OldIrql);
512 return IP_NO_RESOURCES;
515 /* Destination is not on any subnets we're on. Find a router to use */
516 NCE = RouterGetRoute(Destination, NTE);
518 /* We cannot get to the specified destination. Return error */
519 KeReleaseSpinLock(&RouteCacheLock, OldIrql);
520 return IP_NO_ROUTE_TO_DESTINATION;
524 /* Add the new route to the route cache */
525 if (RCN2 == RouteCache) {
526 RCN2 = ExpandExternalRCN();
529 RCN2 = ExpandExternalRCN();
531 DereferenceObject(NTE);
532 DereferenceObject(NCE);
533 KeReleaseSpinLock(&RouteCacheLock, OldIrql);
534 return IP_NO_RESOURCES;
538 RCN2->State = RCN_STATE_COMPUTED;
540 RtlCopyMemory(&RCN2->Destination, Destination, sizeof(IP_ADDRESS));
541 RCN2->PathMTU = NCE->Interface->MTU;
544 /* The route cache node references the NTE and the NCE. The
545 NTE was referenced before and NCE is already referenced by
546 RouteGetRoute() or NBFindOrCreateNeighbor() so we don't
547 reference them here */
550 /* Reference the RCN for the user */
551 ReferenceObject(RCN2);
554 TI_DbgPrint(MIN_TRACE, ("Displaying tree (after).\n"));
555 PrintTree(RouteCache);
558 KeReleaseSpinLock(&RouteCacheLock, OldIrql);
566 PROUTE_CACHE_NODE RouteAddRouteToDestination(
567 PIP_ADDRESS Destination,
568 PNET_TABLE_ENTRY NTE,
570 PNEIGHBOR_CACHE_ENTRY NCE)
572 * FUNCTION: Adds a (permanent) route to a destination
574 * Destination = Pointer to destination address
575 * NTE = Pointer to net table entry
576 * IF = Pointer to interface to use
577 * NCE = Pointer to first hop to destination
579 * Pointer to RCN if the route was added, NULL if not.
580 * There can be at most one RCN per destination address / interface pair
584 PROUTE_CACHE_NODE RCN;
586 TI_DbgPrint(DEBUG_RCACHE, ("Called. Destination (0x%X) NTE (0x%X) IF (0x%X) NCE (0x%X).\n",
587 Destination, NTE, IF, NCE));
589 TI_DbgPrint(DEBUG_RCACHE, ("Destination (%s) NTE (%s) NCE (%s).\n",
590 A2S(Destination), A2S(NTE->Address), A2S(NCE->Address)));
592 KeAcquireSpinLock(&RouteCacheLock, &OldIrql);
594 /* Locate an external RCN we can expand */
596 ExternalRCN->Left = NULL;
598 RCN = SearchRouteCache(Destination, RCN);
599 if (IsInternalRCN(RCN)) {
600 ExternalRCN->Left = (PROUTE_CACHE_NODE)&RCN->Right;
601 /* This is an internal node, continue the search to the right */
604 /* This is an external node, we've found an empty spot */
608 /* Expand the external node */
609 if (RCN == RouteCache) {
610 RCN = ExpandExternalRCN();
613 RCN = ExpandExternalRCN();
615 KeReleaseSpinLock(&RouteCacheLock, OldIrql);
619 /* Initialize the newly created internal node */
621 INIT_TAG(RCN, TAG('R','C','N',' '));
623 /* Reference once for beeing alive */
625 RCN->State = RCN_STATE_PERMANENT;
627 RtlCopyMemory(&RCN->Destination, Destination, sizeof(IP_ADDRESS));
628 RCN->PathMTU = IF->MTU;
631 KeReleaseSpinLock(&RouteCacheLock, OldIrql);
633 /* The route cache node references the NTE and the NCE */
634 ReferenceObject(NTE);
636 ReferenceObject(NCE);
639 TI_DbgPrint(MIN_TRACE, ("Displaying tree.\n"));
640 PrintTree(RouteCache);
647 VOID RouteRemoveRouteToDestination(
648 PROUTE_CACHE_NODE RCN)
650 * FUNCTION: Removes a route to a destination
652 * RCN = Pointer to route cache node to remove
657 TI_DbgPrint(DEBUG_RCACHE, ("Called. RCN (0x%X).\n", RCN));
659 KeAcquireSpinLock(&RouteCacheLock, &OldIrql);
661 RemoveRouteToDestination(RCN);
663 KeReleaseSpinLock(&RouteCacheLock, OldIrql);
667 VOID RouteInvalidateNTE(
668 PNET_TABLE_ENTRY NTE)
670 * FUNCTION: Removes all RCNs with references to an NTE
672 * NTE = Pointer to net table entry to invalidate
677 TI_DbgPrint(DEBUG_RCACHE, ("Called. NTE (0x%X).\n", NTE));
679 KeAcquireSpinLock(&RouteCacheLock, &OldIrql);
680 InvalidateNTEOnSubtree(NTE, RouteCache);
681 KeReleaseSpinLock(&RouteCacheLock, OldIrql);
685 VOID RouteInvalidateNCE(
686 PNEIGHBOR_CACHE_ENTRY NCE)
688 * FUNCTION: Removes all RCNs with references to an NCE
690 * NCE = Pointer to neighbor cache entry to invalidate
695 TI_DbgPrint(DEBUG_RCACHE, ("Called. NCE (0x%X).\n", NCE));
697 KeAcquireSpinLock(&RouteCacheLock, &OldIrql);
698 InvalidateNCEOnSubtree(NCE, RouteCache);
699 KeReleaseSpinLock(&RouteCacheLock, OldIrql);