3 Copyright (c) 2005 - 2016, Intel Corporation. All rights reserved.<BR>
4 SPDX-License-Identifier: BSD-2-Clause-Patent
11 Allocate a route entry then initialize it with the Dest/Netmask
14 @param[in] Dest The destination network
15 @param[in] Netmask The destination network mask
16 @param[in] GateWay The nexthop address
18 @return NULL if failed to allocate memory, otherwise the newly created
29 IP4_ROUTE_ENTRY
*RtEntry
;
31 RtEntry
= AllocatePool (sizeof (IP4_ROUTE_ENTRY
));
33 if (RtEntry
== NULL
) {
37 InitializeListHead (&RtEntry
->Link
);
41 RtEntry
->Netmask
= Netmask
;
42 RtEntry
->NextHop
= GateWay
;
49 Free the route table entry. It is reference counted.
51 @param RtEntry The route entry to free.
56 IN IP4_ROUTE_ENTRY
*RtEntry
59 ASSERT (RtEntry
->RefCnt
> 0);
61 if (--RtEntry
->RefCnt
== 0) {
67 Allocate and initialize an IP4 route cache entry.
69 @param[in] Dst The destination address
70 @param[in] Src The source address
71 @param[in] GateWay The next hop address
72 @param[in] Tag The tag from the caller. This marks all the cache
73 entries spawned from one route table entry.
75 @return NULL if failed to allocate memory for the cache, other point
76 to the created route cache entry.
79 IP4_ROUTE_CACHE_ENTRY
*
80 Ip4CreateRouteCacheEntry (
87 IP4_ROUTE_CACHE_ENTRY
*RtCacheEntry
;
89 RtCacheEntry
= AllocatePool (sizeof (IP4_ROUTE_CACHE_ENTRY
));
91 if (RtCacheEntry
== NULL
) {
95 InitializeListHead (&RtCacheEntry
->Link
);
97 RtCacheEntry
->RefCnt
= 1;
98 RtCacheEntry
->Dest
= Dst
;
99 RtCacheEntry
->Src
= Src
;
100 RtCacheEntry
->NextHop
= GateWay
;
101 RtCacheEntry
->Tag
= Tag
;
107 Free the route cache entry. It is reference counted.
109 @param RtCacheEntry The route cache entry to free.
113 Ip4FreeRouteCacheEntry (
114 IN IP4_ROUTE_CACHE_ENTRY
*RtCacheEntry
117 ASSERT (RtCacheEntry
->RefCnt
> 0);
119 if (--RtCacheEntry
->RefCnt
== 0) {
120 FreePool (RtCacheEntry
);
125 Initialize an empty route cache table.
127 @param[in, out] RtCache The route cache table to initialize.
132 IN OUT IP4_ROUTE_CACHE
*RtCache
137 for (Index
= 0; Index
< IP4_ROUTE_CACHE_HASH_VALUE
; Index
++) {
138 InitializeListHead (&(RtCache
->CacheBucket
[Index
]));
143 Clean up a route cache, that is free all the route cache
144 entries enqueued in the cache.
146 @param[in] RtCache The route cache table to clean up
151 IN IP4_ROUTE_CACHE
*RtCache
156 IP4_ROUTE_CACHE_ENTRY
*RtCacheEntry
;
159 for (Index
= 0; Index
< IP4_ROUTE_CACHE_HASH_VALUE
; Index
++) {
160 NET_LIST_FOR_EACH_SAFE (Entry
, Next
, &(RtCache
->CacheBucket
[Index
])) {
161 RtCacheEntry
= NET_LIST_USER_STRUCT (Entry
, IP4_ROUTE_CACHE_ENTRY
, Link
);
163 RemoveEntryList (Entry
);
164 Ip4FreeRouteCacheEntry (RtCacheEntry
);
170 Create an empty route table, includes its internal route cache
172 @return NULL if failed to allocate memory for the route table, otherwise
173 the point to newly created route table.
177 Ip4CreateRouteTable (
181 IP4_ROUTE_TABLE
*RtTable
;
184 RtTable
= AllocatePool (sizeof (IP4_ROUTE_TABLE
));
186 if (RtTable
== NULL
) {
191 RtTable
->TotalNum
= 0;
193 for (Index
= 0; Index
<= IP4_MASK_MAX
; Index
++) {
194 InitializeListHead (&(RtTable
->RouteArea
[Index
]));
197 RtTable
->Next
= NULL
;
199 Ip4InitRouteCache (&RtTable
->Cache
);
204 Free the route table and its associated route cache. Route
205 table is reference counted.
207 @param[in] RtTable The route table to free.
212 IN IP4_ROUTE_TABLE
*RtTable
217 IP4_ROUTE_ENTRY
*RtEntry
;
220 ASSERT (RtTable
->RefCnt
> 0);
222 if (--RtTable
->RefCnt
> 0) {
227 // Free all the route table entry and its route cache.
229 for (Index
= 0; Index
<= IP4_MASK_MAX
; Index
++) {
230 NET_LIST_FOR_EACH_SAFE (Entry
, Next
, &(RtTable
->RouteArea
[Index
])) {
231 RtEntry
= NET_LIST_USER_STRUCT (Entry
, IP4_ROUTE_ENTRY
, Link
);
233 RemoveEntryList (Entry
);
234 Ip4FreeRouteEntry (RtEntry
);
238 Ip4CleanRouteCache (&RtTable
->Cache
);
244 Remove all the cache entries bearing the Tag. When a route cache
245 entry is created, it is tagged with the address of route entry
246 from which it is spawned. When a route entry is deleted, the cache
247 entries spawned from it are also deleted.
249 @param RtCache Route cache to remove the entries from
250 @param Tag The Tag of the entries to remove
255 IN OUT IP4_ROUTE_CACHE
*RtCache
,
261 IP4_ROUTE_CACHE_ENTRY
*RtCacheEntry
;
264 for (Index
= 0; Index
< IP4_ROUTE_CACHE_HASH_VALUE
; Index
++) {
265 NET_LIST_FOR_EACH_SAFE (Entry
, Next
, &RtCache
->CacheBucket
[Index
]) {
266 RtCacheEntry
= NET_LIST_USER_STRUCT (Entry
, IP4_ROUTE_CACHE_ENTRY
, Link
);
268 if (RtCacheEntry
->Tag
== Tag
) {
269 RemoveEntryList (Entry
);
270 Ip4FreeRouteCacheEntry (RtCacheEntry
);
277 Add a route entry to the route table. All the IP4_ADDRs are in
280 @param[in, out] RtTable Route table to add route to
281 @param[in] Dest The destination of the network
282 @param[in] Netmask The netmask of the destination
283 @param[in] Gateway The next hop address
285 @retval EFI_ACCESS_DENIED The same route already exists
286 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the entry
287 @retval EFI_SUCCESS The route is added successfully.
292 IN OUT IP4_ROUTE_TABLE
*RtTable
,
300 IP4_ROUTE_ENTRY
*RtEntry
;
303 // All the route entries with the same netmask length are
304 // linke to the same route area
306 Head
= &(RtTable
->RouteArea
[NetGetMaskLength (Netmask
)]);
309 // First check whether the route exists
311 NET_LIST_FOR_EACH (Entry
, Head
) {
312 RtEntry
= NET_LIST_USER_STRUCT (Entry
, IP4_ROUTE_ENTRY
, Link
);
314 if (IP4_NET_EQUAL (RtEntry
->Dest
, Dest
, Netmask
) && (RtEntry
->NextHop
== Gateway
)) {
315 return EFI_ACCESS_DENIED
;
320 // Create a route entry and insert it to the route area.
322 RtEntry
= Ip4CreateRouteEntry (Dest
, Netmask
, Gateway
);
324 if (RtEntry
== NULL
) {
325 return EFI_OUT_OF_RESOURCES
;
328 if (Gateway
== IP4_ALLZERO_ADDRESS
) {
329 RtEntry
->Flag
= IP4_DIRECT_ROUTE
;
332 InsertHeadList (Head
, &RtEntry
->Link
);
339 Remove a route entry and all the route caches spawn from it.
341 @param RtTable The route table to remove the route from
342 @param Dest The destination network
343 @param Netmask The netmask of the Dest
344 @param Gateway The next hop address
346 @retval EFI_SUCCESS The route entry is successfully removed
347 @retval EFI_NOT_FOUND There is no route entry in the table with that
353 IN OUT IP4_ROUTE_TABLE
*RtTable
,
362 IP4_ROUTE_ENTRY
*RtEntry
;
364 Head
= &(RtTable
->RouteArea
[NetGetMaskLength (Netmask
)]);
366 NET_LIST_FOR_EACH_SAFE (Entry
, Next
, Head
) {
367 RtEntry
= NET_LIST_USER_STRUCT (Entry
, IP4_ROUTE_ENTRY
, Link
);
369 if (IP4_NET_EQUAL (RtEntry
->Dest
, Dest
, Netmask
) && (RtEntry
->NextHop
== Gateway
)) {
370 Ip4PurgeRouteCache (&RtTable
->Cache
, (UINTN
)RtEntry
);
371 RemoveEntryList (Entry
);
372 Ip4FreeRouteEntry (RtEntry
);
379 return EFI_NOT_FOUND
;
383 Find a route cache with the dst and src. This is used by ICMP
384 redirect message process. All kinds of redirect is treated as
385 host redirect according to RFC1122. So, only route cache entries
386 are modified according to the ICMP redirect message.
388 @param[in] RtTable The route table to search the cache for
389 @param[in] Dest The destination address
390 @param[in] Src The source address
392 @return NULL if no route entry to the (Dest, Src). Otherwise the point
393 to the correct route cache entry.
396 IP4_ROUTE_CACHE_ENTRY
*
398 IN IP4_ROUTE_TABLE
*RtTable
,
404 IP4_ROUTE_CACHE_ENTRY
*RtCacheEntry
;
407 Index
= IP4_ROUTE_CACHE_HASH (Dest
, Src
);
409 NET_LIST_FOR_EACH (Entry
, &RtTable
->Cache
.CacheBucket
[Index
]) {
410 RtCacheEntry
= NET_LIST_USER_STRUCT (Entry
, IP4_ROUTE_CACHE_ENTRY
, Link
);
412 if ((RtCacheEntry
->Dest
== Dest
) && (RtCacheEntry
->Src
== Src
)) {
413 NET_GET_REF (RtCacheEntry
);
422 Search the route table for a most specific match to the Dst. It searches
423 from the longest route area (mask length == 32) to the shortest route area
424 (default routes). In each route area, it will first search the instance's
425 route table, then the default route table. This is required by the following
427 1. IP search the route table for a most specific match
428 2. The local route entries have precedence over the default route entry.
430 @param[in] RtTable The route table to search from
431 @param[in] Dst The destination address to search
433 @return NULL if no route matches the Dst, otherwise the point to the
434 most specific route to the Dst.
439 IN IP4_ROUTE_TABLE
*RtTable
,
444 IP4_ROUTE_ENTRY
*RtEntry
;
445 IP4_ROUTE_TABLE
*Table
;
450 for (Index
= IP4_MASK_MAX
; Index
>= 0; Index
--) {
451 for (Table
= RtTable
; Table
!= NULL
; Table
= Table
->Next
) {
452 NET_LIST_FOR_EACH (Entry
, &Table
->RouteArea
[Index
]) {
453 RtEntry
= NET_LIST_USER_STRUCT (Entry
, IP4_ROUTE_ENTRY
, Link
);
455 if (IP4_NET_EQUAL (RtEntry
->Dest
, Dst
, RtEntry
->Netmask
)) {
456 NET_GET_REF (RtEntry
);
467 Search the route table to route the packet. Return/create a route
468 cache if there is a route to the destination.
470 @param[in] RtTable The route table to search from
471 @param[in] Dest The destination address to search for
472 @param[in] Src The source address to search for
473 @param[in] SubnetMask The subnet mask of the Src address, this field is
474 used to check if the station is using /32 subnet.
475 @param[in] AlwaysTryDestAddr Always try to use the dest address as next hop even
476 though we can't find a matching route entry. This
477 field is only valid when using /32 subnet.
479 @return NULL if failed to route packet, otherwise a route cache
480 entry that can be used to route packet.
483 IP4_ROUTE_CACHE_ENTRY
*
485 IN IP4_ROUTE_TABLE
*RtTable
,
488 IN IP4_ADDR SubnetMask
,
489 IN BOOLEAN AlwaysTryDestAddr
495 IP4_ROUTE_CACHE_ENTRY
*RtCacheEntry
;
496 IP4_ROUTE_CACHE_ENTRY
*Cache
;
497 IP4_ROUTE_ENTRY
*RtEntry
;
501 ASSERT (RtTable
!= NULL
);
503 Head
= &RtTable
->Cache
.CacheBucket
[IP4_ROUTE_CACHE_HASH (Dest
, Src
)];
504 RtCacheEntry
= Ip4FindRouteCache (RtTable
, Dest
, Src
);
507 // If found, promote the cache entry to the head of the hash bucket. LRU
509 if (RtCacheEntry
!= NULL
) {
510 RemoveEntryList (&RtCacheEntry
->Link
);
511 InsertHeadList (Head
, &RtCacheEntry
->Link
);
516 // Search the route table for the most specific route
518 RtEntry
= Ip4FindRouteEntry (RtTable
, Dest
);
520 if (RtEntry
== NULL
) {
521 if (SubnetMask
!= IP4_ALLONE_ADDRESS
) {
523 } else if (!AlwaysTryDestAddr
) {
529 // Found a route to the Dest, if it is a direct route, the packet
530 // will be sent directly to the destination, such as for connected
531 // network. Otherwise, it is an indirect route, the packet will be
532 // sent to the next hop router.
534 // When using /32 subnet mask, the packet will always be sent to the direct
535 // destination first, if we can't find a matching route cache.
537 if ((SubnetMask
== IP4_ALLONE_ADDRESS
) || ((RtEntry
->Flag
& IP4_DIRECT_ROUTE
) != 0)) {
540 NextHop
= RtEntry
->NextHop
;
543 if (RtEntry
!= NULL
) {
544 Ip4FreeRouteEntry (RtEntry
);
548 // Create a route cache entry, and tag it as spawned from this route entry
549 // For /32 subnet mask, the default route in RtEntry will be used if failed
550 // to send the packet to driect destination address.
552 RtCacheEntry
= Ip4CreateRouteCacheEntry (Dest
, Src
, NextHop
, (UINTN
)RtEntry
);
554 if (RtCacheEntry
== NULL
) {
558 InsertHeadList (Head
, &RtCacheEntry
->Link
);
559 NET_GET_REF (RtCacheEntry
);
562 // Each bucket of route cache can contain at most 64 entries.
563 // Remove the entries at the tail of the bucket. These entries
564 // are likely to be used least.
567 NET_LIST_FOR_EACH_SAFE (Entry
, Next
, Head
) {
568 if (++Count
< IP4_ROUTE_CACHE_MAX
) {
572 Cache
= NET_LIST_USER_STRUCT (Entry
, IP4_ROUTE_CACHE_ENTRY
, Link
);
574 RemoveEntryList (Entry
);
575 Ip4FreeRouteCacheEntry (Cache
);
582 Build a EFI_IP4_ROUTE_TABLE to be returned to the caller of
583 GetModeData. The EFI_IP4_ROUTE_TABLE is clumsy to use in the
584 internal operation of the IP4 driver.
586 @param[in] IpInstance The IP4 child that requests the route table.
588 @retval EFI_SUCCESS The route table is successfully build
589 @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the route table.
593 Ip4BuildEfiRouteTable (
594 IN IP4_PROTOCOL
*IpInstance
598 IP4_ROUTE_TABLE
*RtTable
;
599 IP4_ROUTE_ENTRY
*RtEntry
;
600 EFI_IP4_ROUTE_TABLE
*Table
;
604 RtTable
= IpInstance
->RouteTable
;
606 if (IpInstance
->EfiRouteTable
!= NULL
) {
607 FreePool (IpInstance
->EfiRouteTable
);
609 IpInstance
->EfiRouteTable
= NULL
;
610 IpInstance
->EfiRouteCount
= 0;
613 Count
= RtTable
->TotalNum
;
615 if (RtTable
->Next
!= NULL
) {
616 Count
+= RtTable
->Next
->TotalNum
;
623 Table
= AllocatePool (sizeof (EFI_IP4_ROUTE_TABLE
) * Count
);
626 return EFI_OUT_OF_RESOURCES
;
630 // Copy the route entry to EFI route table. Keep the order of
631 // route entry copied from most specific to default route. That
632 // is, interlevel the route entry from the instance's route area
633 // and those from the default route table's route area.
637 for (Index
= IP4_MASK_MAX
; Index
>= 0; Index
--) {
638 for (RtTable
= IpInstance
->RouteTable
; RtTable
!= NULL
; RtTable
= RtTable
->Next
) {
639 NET_LIST_FOR_EACH (Entry
, &(RtTable
->RouteArea
[Index
])) {
640 RtEntry
= NET_LIST_USER_STRUCT (Entry
, IP4_ROUTE_ENTRY
, Link
);
642 EFI_IP4 (Table
[Count
].SubnetAddress
) = HTONL (RtEntry
->Dest
& RtEntry
->Netmask
);
643 EFI_IP4 (Table
[Count
].SubnetMask
) = HTONL (RtEntry
->Netmask
);
644 EFI_IP4 (Table
[Count
].GatewayAddress
) = HTONL (RtEntry
->NextHop
);
651 IpInstance
->EfiRouteTable
= Table
;
652 IpInstance
->EfiRouteCount
= Count
;