Scrubbed more.
[mirror_edk2.git] / MdeModulePkg / Universal / Network / Ip4Dxe / Ip4Route.c
1 /** @file\r
2 \r
3 Copyright (c) 2005 - 2006, Intel Corporation\r
4 All rights reserved. This program and the accompanying materials\r
5 are licensed and made available under the terms and conditions of the BSD License\r
6 which accompanies this distribution.  The full text of the license may be found at\r
7 http://opensource.org/licenses/bsd-license.php\r
8 \r
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
11 \r
12 \r
13 Module Name:\r
14 \r
15   Ip4Route.c\r
16 \r
17 Abstract:\r
18 \r
19 \r
20 **/\r
21 \r
22 #include "Ip4Impl.h"\r
23 \r
24 \r
25 /**\r
26   Allocate a route entry then initialize it with the Dest/Netmaks\r
27   and Gateway.\r
28 \r
29   @param  Dest                  The destination network\r
30   @param  Netmask               The destination network mask\r
31   @param  GateWay               The nexthop address\r
32 \r
33   @return NULL if failed to allocate memeory, otherwise the newly created\r
34           route entry.\r
35 \r
36 **/\r
37 IP4_ROUTE_ENTRY *\r
38 Ip4CreateRouteEntry (\r
39   IN IP4_ADDR               Dest,\r
40   IN IP4_ADDR               Netmask,\r
41   IN IP4_ADDR               GateWay\r
42   )\r
43 {\r
44   IP4_ROUTE_ENTRY           *RtEntry;\r
45 \r
46   RtEntry = AllocatePool (sizeof (IP4_ROUTE_ENTRY));\r
47 \r
48   if (RtEntry == NULL) {\r
49     return NULL;\r
50   }\r
51 \r
52   InitializeListHead (&RtEntry->Link);\r
53 \r
54   RtEntry->RefCnt  = 1;\r
55   RtEntry->Dest    = Dest;\r
56   RtEntry->Netmask = Netmask;\r
57   RtEntry->NextHop = GateWay;\r
58   RtEntry->Flag    = 0;\r
59 \r
60   return RtEntry;\r
61 }\r
62 \r
63 \r
64 /**\r
65   Free the route table entry. It is reference counted.\r
66 \r
67   @param  RtEntry               The route entry to free.\r
68 \r
69   @return NONE\r
70 \r
71 **/\r
72 VOID\r
73 Ip4FreeRouteEntry (\r
74   IN IP4_ROUTE_ENTRY    *RtEntry\r
75   )\r
76 {\r
77   ASSERT (RtEntry->RefCnt > 0);\r
78 \r
79   if (--RtEntry->RefCnt == 0) {\r
80     gBS->FreePool (RtEntry);\r
81   }\r
82 }\r
83 \r
84 \r
85 /**\r
86   Allocate and initialize an IP4 route cache entry.\r
87 \r
88   @param  Dst                   The destination address\r
89   @param  Src                   The source address\r
90   @param  GateWay               The next hop address\r
91   @param  Tag                   The tag from the caller. This marks all the cache\r
92                                 entries spawned from one route table entry.\r
93 \r
94   @return NULL if failed to allocate memory for the cache, other point\r
95           to the created route cache entry.\r
96 \r
97 **/\r
98 IP4_ROUTE_CACHE_ENTRY *\r
99 Ip4CreateRouteCacheEntry (\r
100   IN IP4_ADDR               Dst,\r
101   IN IP4_ADDR               Src,\r
102   IN IP4_ADDR               GateWay,\r
103   IN UINTN                  Tag\r
104   )\r
105 {\r
106   IP4_ROUTE_CACHE_ENTRY     *RtCacheEntry;\r
107 \r
108   RtCacheEntry = AllocatePool (sizeof (IP4_ROUTE_CACHE_ENTRY));\r
109 \r
110   if (RtCacheEntry == NULL) {\r
111     return NULL;\r
112   }\r
113 \r
114   InitializeListHead (&RtCacheEntry->Link);\r
115 \r
116   RtCacheEntry->RefCnt  = 1;\r
117   RtCacheEntry->Dest    = Dst;\r
118   RtCacheEntry->Src     = Src;\r
119   RtCacheEntry->NextHop = GateWay;\r
120   RtCacheEntry->Tag     = Tag;\r
121 \r
122   return RtCacheEntry;\r
123 }\r
124 \r
125 \r
126 /**\r
127   Free the route cache entry. It is reference counted.\r
128 \r
129   @param  RtCacheEntry          The route cache entry to free.\r
130 \r
131   @return None\r
132 \r
133 **/\r
134 VOID\r
135 Ip4FreeRouteCacheEntry (\r
136   IN IP4_ROUTE_CACHE_ENTRY  *RtCacheEntry\r
137   )\r
138 {\r
139   ASSERT (RtCacheEntry->RefCnt > 0);\r
140 \r
141   if (--RtCacheEntry->RefCnt == 0) {\r
142     gBS->FreePool (RtCacheEntry);\r
143   }\r
144 }\r
145 \r
146 \r
147 /**\r
148   Initialize an empty route cache table.\r
149 \r
150   @param  RtCache               The rotue cache table to initialize.\r
151 \r
152   @return NONE\r
153 \r
154 **/\r
155 VOID\r
156 Ip4InitRouteCache (\r
157   IN OUT IP4_ROUTE_CACHE        *RtCache\r
158   )\r
159 {\r
160   UINT32                    Index;\r
161 \r
162   for (Index = 0; Index < IP4_ROUTE_CACHE_HASH; Index++) {\r
163     InitializeListHead (&(RtCache->CacheBucket[Index]));\r
164   }\r
165 }\r
166 \r
167 \r
168 /**\r
169   Clean up a route cache, that is free all the route cache\r
170   entries enqueued in the cache.\r
171 \r
172   @param  RtCache               The route cache table to clean up\r
173 \r
174   @return None\r
175 \r
176 **/\r
177 VOID\r
178 Ip4CleanRouteCache (\r
179   IN IP4_ROUTE_CACHE        *RtCache\r
180   )\r
181 {\r
182   LIST_ENTRY                *Entry;\r
183   LIST_ENTRY                *Next;\r
184   IP4_ROUTE_CACHE_ENTRY     *RtCacheEntry;\r
185   UINT32                    Index;\r
186 \r
187   for (Index = 0; Index < IP4_ROUTE_CACHE_HASH; Index++) {\r
188     NET_LIST_FOR_EACH_SAFE (Entry, Next, &(RtCache->CacheBucket[Index])) {\r
189       RtCacheEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_CACHE_ENTRY, Link);\r
190 \r
191       RemoveEntryList (Entry);\r
192       Ip4FreeRouteCacheEntry (RtCacheEntry);\r
193     }\r
194   }\r
195 }\r
196 \r
197 \r
198 \r
199 /**\r
200   Create an empty route table, includes its internal route cache\r
201 \r
202   @return NULL if failed to allocate memory for the route table, otherwise\r
203           the point to newly created route table.\r
204 \r
205 **/\r
206 IP4_ROUTE_TABLE *\r
207 Ip4CreateRouteTable (\r
208   VOID\r
209   )\r
210 {\r
211   IP4_ROUTE_TABLE           *RtTable;\r
212   UINT32                    Index;\r
213 \r
214   RtTable = AllocatePool (sizeof (IP4_ROUTE_TABLE));\r
215 \r
216   if (RtTable == NULL) {\r
217     return NULL;\r
218   }\r
219 \r
220   RtTable->RefCnt   = 1;\r
221   RtTable->TotalNum = 0;\r
222 \r
223   for (Index = 0; Index < IP4_MASK_NUM; Index++) {\r
224     InitializeListHead (&(RtTable->RouteArea[Index]));\r
225   }\r
226 \r
227   RtTable->Next = NULL;\r
228 \r
229   Ip4InitRouteCache (&RtTable->Cache);\r
230   return RtTable;\r
231 }\r
232 \r
233 \r
234 /**\r
235   Free the route table and its associated route cache. Route\r
236   table is reference counted.\r
237 \r
238   @param  RtTable               The route table to free.\r
239 \r
240   @return None\r
241 \r
242 **/\r
243 VOID\r
244 Ip4FreeRouteTable (\r
245   IN IP4_ROUTE_TABLE        *RtTable\r
246   )\r
247 {\r
248   LIST_ENTRY                *Entry;\r
249   LIST_ENTRY                *Next;\r
250   IP4_ROUTE_ENTRY           *RtEntry;\r
251   UINT32                    Index;\r
252 \r
253   ASSERT (RtTable->RefCnt > 0);\r
254 \r
255   if (--RtTable->RefCnt > 0) {\r
256     return ;\r
257   }\r
258 \r
259   //\r
260   // Free all the route table entry and its route cache.\r
261   //\r
262   for (Index = 0; Index < IP4_MASK_NUM; Index++) {\r
263     NET_LIST_FOR_EACH_SAFE (Entry, Next, &(RtTable->RouteArea[Index])) {\r
264       RtEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_ENTRY, Link);\r
265 \r
266       RemoveEntryList (Entry);\r
267       Ip4FreeRouteEntry (RtEntry);\r
268     }\r
269   }\r
270 \r
271   Ip4CleanRouteCache (&RtTable->Cache);\r
272 \r
273   gBS->FreePool (RtTable);\r
274 }\r
275 \r
276 \r
277 \r
278 /**\r
279   Remove all the cache entries bearing the Tag. When a route cache\r
280   entry is created, it is tagged with the address of route entry\r
281   from which it is spawned. When a route entry is deleted, the cache\r
282   entries spawned from it are also deleted.\r
283 \r
284   @param  RtCache               Route cache to remove the entries from\r
285   @param  Tag                   The Tag of the entries to remove\r
286 \r
287   @return None\r
288 \r
289 **/\r
290 VOID\r
291 Ip4PurgeRouteCache (\r
292   IN OUT IP4_ROUTE_CACHE        *RtCache,\r
293   IN     UINTN                  Tag\r
294   )\r
295 {\r
296   LIST_ENTRY                *Entry;\r
297   LIST_ENTRY                *Next;\r
298   IP4_ROUTE_CACHE_ENTRY     *RtCacheEntry;\r
299   UINT32                    Index;\r
300 \r
301   for (Index = 0; Index < IP4_ROUTE_CACHE_HASH; Index++) {\r
302     NET_LIST_FOR_EACH_SAFE (Entry, Next, &RtCache->CacheBucket[Index]) {\r
303 \r
304       RtCacheEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_CACHE_ENTRY, Link);\r
305 \r
306       if (RtCacheEntry->Tag == Tag) {\r
307         RemoveEntryList (Entry);\r
308         Ip4FreeRouteCacheEntry (RtCacheEntry);\r
309       }\r
310     }\r
311   }\r
312 }\r
313 \r
314 \r
315 /**\r
316   Add a route entry to the route table. All the IP4_ADDRs are in\r
317   host byte order.\r
318 \r
319   @param  RtTable               Route table to add route to\r
320   @param  Dest                  The destination of the network\r
321   @param  Netmask               The netmask of the destination\r
322   @param  Gateway               The next hop address\r
323 \r
324   @retval EFI_ACCESS_DENIED     The same route already exists\r
325   @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory for the entry\r
326   @retval EFI_SUCCESS           The route is added successfully.\r
327 \r
328 **/\r
329 EFI_STATUS\r
330 Ip4AddRoute (\r
331   IN OUT IP4_ROUTE_TABLE        *RtTable,\r
332   IN     IP4_ADDR               Dest,\r
333   IN     IP4_ADDR               Netmask,\r
334   IN     IP4_ADDR               Gateway\r
335   )\r
336 {\r
337   LIST_ENTRY                *Head;\r
338   LIST_ENTRY                *Entry;\r
339   IP4_ROUTE_ENTRY           *RtEntry;\r
340 \r
341   //\r
342   // All the route entries with the same netmask length are\r
343   // linke to the same route area\r
344   //\r
345   Head = &(RtTable->RouteArea[NetGetMaskLength (Netmask)]);\r
346 \r
347   //\r
348   // First check whether the route exists\r
349   //\r
350   NET_LIST_FOR_EACH (Entry, Head) {\r
351     RtEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_ENTRY, Link);\r
352 \r
353     if (IP4_NET_EQUAL (RtEntry->Dest, Dest, Netmask) && (RtEntry->NextHop == Gateway)) {\r
354       return EFI_ACCESS_DENIED;\r
355     }\r
356   }\r
357 \r
358   //\r
359   // Create a route entry and insert it to the route area.\r
360   //\r
361   RtEntry = Ip4CreateRouteEntry (Dest, Netmask, Gateway);\r
362 \r
363   if (RtEntry == NULL) {\r
364     return EFI_OUT_OF_RESOURCES;\r
365   }\r
366 \r
367   if (Gateway == IP4_ALLZERO_ADDRESS) {\r
368     RtEntry->Flag = IP4_DIRECT_ROUTE;\r
369   }\r
370 \r
371   InsertHeadList (Head, &RtEntry->Link);\r
372   RtTable->TotalNum++;\r
373 \r
374   return EFI_SUCCESS;\r
375 }\r
376 \r
377 \r
378 /**\r
379   Remove a route entry and all the route caches spawn from it.\r
380 \r
381   @param  RtTable               The route table to remove the route from\r
382   @param  Dest                  The destination network\r
383   @param  Netmask               The netmask of the Dest\r
384   @param  Gateway               The next hop address\r
385 \r
386   @retval EFI_SUCCESS           The route entry is successfully removed\r
387   @retval EFI_NOT_FOUND         There is no route entry in the table with that\r
388                                 properity.\r
389 \r
390 **/\r
391 EFI_STATUS\r
392 Ip4DelRoute (\r
393   IN OUT IP4_ROUTE_TABLE      *RtTable,\r
394   IN     IP4_ADDR             Dest,\r
395   IN     IP4_ADDR             Netmask,\r
396   IN     IP4_ADDR             Gateway\r
397   )\r
398 {\r
399   LIST_ENTRY                *Head;\r
400   LIST_ENTRY                *Entry;\r
401   LIST_ENTRY                *Next;\r
402   IP4_ROUTE_ENTRY           *RtEntry;\r
403 \r
404   Head = &(RtTable->RouteArea[NetGetMaskLength (Netmask)]);\r
405 \r
406   NET_LIST_FOR_EACH_SAFE (Entry, Next, Head) {\r
407     RtEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_ENTRY, Link);\r
408 \r
409     if (IP4_NET_EQUAL (RtEntry->Dest, Dest, Netmask) && (RtEntry->NextHop == Gateway)) {\r
410       Ip4PurgeRouteCache (&RtTable->Cache, (UINTN) RtEntry);\r
411       RemoveEntryList (Entry);\r
412       Ip4FreeRouteEntry  (RtEntry);\r
413 \r
414       RtTable->TotalNum--;\r
415       return EFI_SUCCESS;\r
416     }\r
417   }\r
418 \r
419   return EFI_NOT_FOUND;\r
420 }\r
421 \r
422 \r
423 /**\r
424   Find a route cache with the dst and src. This is used by ICMP\r
425   redirect messasge process. All kinds of redirect is treated as\r
426   host redirect according to RFC1122. So, only route cache entries\r
427   are modified according to the ICMP redirect message.\r
428 \r
429   @param  RtTable               The route table to search the cache for\r
430   @param  Dest                  The destination address\r
431   @param  Src                   The source address\r
432 \r
433   @return NULL if no route entry to the (Dest, Src). Otherwise the point\r
434           to the correct route cache entry.\r
435 \r
436 **/\r
437 IP4_ROUTE_CACHE_ENTRY *\r
438 Ip4FindRouteCache (\r
439   IN IP4_ROUTE_TABLE        *RtTable,\r
440   IN IP4_ADDR               Dest,\r
441   IN IP4_ADDR               Src\r
442   )\r
443 {\r
444   LIST_ENTRY                *Entry;\r
445   IP4_ROUTE_CACHE_ENTRY     *RtCacheEntry;\r
446   UINT32                    Index;\r
447 \r
448   Index = IP4_ROUTE_CACHE_HASH (Dest, Src);\r
449 \r
450   NET_LIST_FOR_EACH (Entry, &RtTable->Cache.CacheBucket[Index]) {\r
451     RtCacheEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_CACHE_ENTRY, Link);\r
452 \r
453     if ((RtCacheEntry->Dest == Dest) && (RtCacheEntry->Src == Src)) {\r
454       NET_GET_REF (RtCacheEntry);\r
455       return RtCacheEntry;\r
456     }\r
457   }\r
458 \r
459   return NULL;\r
460 }\r
461 \r
462 \r
463 /**\r
464   Search the route table for a most specific match to the Dst. It searches\r
465   from the longest route area (mask length == 32) to the shortest route area\r
466   (default routes). In each route area, it will first search the instance's\r
467   route table, then the default route table. This is required by the following\r
468   requirements:\r
469   1. IP search the route table for a most specific match\r
470   2. The local route entries have precedence over the default route entry.\r
471 \r
472   @param  RtTable               The route table to search from\r
473   @param  Dst                   The destionation address to search\r
474 \r
475   @return NULL if no route matches the Dst, otherwise the point to the\r
476   @return most specific route to the Dst.\r
477 \r
478 **/\r
479 IP4_ROUTE_ENTRY *\r
480 Ip4FindRouteEntry (\r
481   IN IP4_ROUTE_TABLE        *RtTable,\r
482   IN IP4_ADDR               Dst\r
483   )\r
484 {\r
485   LIST_ENTRY                *Entry;\r
486   IP4_ROUTE_ENTRY           *RtEntry;\r
487   IP4_ROUTE_TABLE           *Table;\r
488   INTN                      Index;\r
489 \r
490   RtEntry = NULL;\r
491 \r
492   for (Index = IP4_MASK_NUM - 1; Index >= 0; Index--) {\r
493     for (Table = RtTable; Table != NULL; Table = Table->Next) {\r
494       NET_LIST_FOR_EACH (Entry, &Table->RouteArea[Index]) {\r
495         RtEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_ENTRY, Link);\r
496 \r
497         if (IP4_NET_EQUAL (RtEntry->Dest, Dst, RtEntry->Netmask)) {\r
498           NET_GET_REF (RtEntry);\r
499           return RtEntry;\r
500         }\r
501       }\r
502     }\r
503   }\r
504 \r
505 \r
506   return NULL;\r
507 }\r
508 \r
509 \r
510 /**\r
511   Search the route table to route the packet. Return/create a route\r
512   cache if there is a route to the destination.\r
513 \r
514   @param  RtTable               The route table to search from\r
515   @param  Dest                  The destination address to search for\r
516   @param  Src                   The source address to search for\r
517 \r
518   @return NULL if failed to route packet, otherwise a route cache\r
519           entry that can be used to route packet.\r
520 \r
521 **/\r
522 IP4_ROUTE_CACHE_ENTRY *\r
523 Ip4Route (\r
524   IN IP4_ROUTE_TABLE        *RtTable,\r
525   IN IP4_ADDR               Dest,\r
526   IN IP4_ADDR               Src\r
527   )\r
528 {\r
529   LIST_ENTRY                *Head;\r
530   LIST_ENTRY                *Entry;\r
531   LIST_ENTRY                *Next;\r
532   IP4_ROUTE_CACHE_ENTRY     *RtCacheEntry;\r
533   IP4_ROUTE_CACHE_ENTRY     *Cache;\r
534   IP4_ROUTE_ENTRY           *RtEntry;\r
535   IP4_ADDR                  NextHop;\r
536   UINT32                    Count;\r
537 \r
538   ASSERT (RtTable != NULL);\r
539 \r
540   Head          = &RtTable->Cache.CacheBucket[IP4_ROUTE_CACHE_HASH (Dest, Src)];\r
541   RtCacheEntry  = Ip4FindRouteCache (RtTable, Dest, Src);\r
542 \r
543   //\r
544   // If found, promote the cache entry to the head of the hash bucket. LRU\r
545   //\r
546   if (RtCacheEntry != NULL) {\r
547     RemoveEntryList (&RtCacheEntry->Link);\r
548     InsertHeadList (Head, &RtCacheEntry->Link);\r
549     return RtCacheEntry;\r
550   }\r
551 \r
552   //\r
553   // Search the route table for the most specific route\r
554   //\r
555   RtEntry = Ip4FindRouteEntry (RtTable, Dest);\r
556 \r
557   if (RtEntry == NULL) {\r
558     return NULL;\r
559   }\r
560 \r
561   //\r
562   // Found a route to the Dest, if it is a direct route, the packet\r
563   // will be sent directly to the destination, such as for connected\r
564   // network. Otherwise, it is an indirect route, the packet will be\r
565   // sent to the next hop router.\r
566   //\r
567   if ((RtEntry->Flag & IP4_DIRECT_ROUTE) != 0) {\r
568     NextHop = Dest;\r
569   } else {\r
570     NextHop = RtEntry->NextHop;\r
571   }\r
572 \r
573   Ip4FreeRouteEntry (RtEntry);\r
574 \r
575   //\r
576   // Create a route cache entry, and tag it as spawned from this route entry\r
577   //\r
578   RtCacheEntry = Ip4CreateRouteCacheEntry (Dest, Src, NextHop, (UINTN) RtEntry);\r
579 \r
580   if (RtCacheEntry == NULL) {\r
581     return NULL;\r
582   }\r
583 \r
584   InsertHeadList (Head, &RtCacheEntry->Link);\r
585   NET_GET_REF (RtCacheEntry);\r
586 \r
587   //\r
588   // Each bucket of route cache can contain at most 64 entries.\r
589   // Remove the entries at the tail of the bucket. These entries\r
590   // are likely to be used least.\r
591   //\r
592   Count = 0;\r
593   NET_LIST_FOR_EACH_SAFE (Entry, Next, Head) {\r
594     if (++Count < IP4_ROUTE_CACHE_MAX) {\r
595       continue;\r
596     }\r
597 \r
598     Cache = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_CACHE_ENTRY, Link);\r
599 \r
600     RemoveEntryList (Entry);\r
601     Ip4FreeRouteCacheEntry (Cache);\r
602   }\r
603 \r
604   return RtCacheEntry;\r
605 }\r
606 \r
607 \r
608 /**\r
609   Build a EFI_IP4_ROUTE_TABLE to be returned to the caller of\r
610   GetModeData. The EFI_IP4_ROUTE_TABLE is clumsy to use in the\r
611   internal operation of the IP4 driver.\r
612 \r
613   @param  IpInstance            The IP4 child that requests the route table.\r
614 \r
615   @retval EFI_SUCCESS           The route table is successfully build\r
616   @retval EFI_OUT_OF_RESOURCES  Failed to allocate the memory for the rotue table.\r
617 \r
618 **/\r
619 EFI_STATUS\r
620 Ip4BuildEfiRouteTable (\r
621   IN IP4_PROTOCOL           *IpInstance\r
622   )\r
623 {\r
624   LIST_ENTRY                *Entry;\r
625   IP4_ROUTE_TABLE           *RtTable;\r
626   IP4_ROUTE_ENTRY           *RtEntry;\r
627   EFI_IP4_ROUTE_TABLE       *Table;\r
628   UINT32                    Count;\r
629   INT32                     Index;\r
630 \r
631   RtTable = IpInstance->RouteTable;\r
632 \r
633   if (IpInstance->EfiRouteTable != NULL) {\r
634     gBS->FreePool (IpInstance->EfiRouteTable);\r
635 \r
636     IpInstance->EfiRouteTable = NULL;\r
637     IpInstance->EfiRouteCount = 0;\r
638   }\r
639 \r
640   Count = RtTable->TotalNum;\r
641 \r
642   if (RtTable->Next != NULL) {\r
643     Count += RtTable->Next->TotalNum;\r
644   }\r
645 \r
646   if (Count == 0) {\r
647     return EFI_SUCCESS;\r
648   }\r
649 \r
650   Table = AllocatePool (sizeof (EFI_IP4_ROUTE_TABLE) * Count);\r
651 \r
652   if (Table == NULL) {\r
653     return EFI_OUT_OF_RESOURCES;\r
654   }\r
655 \r
656   //\r
657   // Copy the route entry to EFI route table. Keep the order of\r
658   // route entry copied from most specific to default route. That\r
659   // is, interlevel the route entry from the instance's route area\r
660   // and those from the default route table's route area.\r
661   //\r
662   Count = 0;\r
663 \r
664   for (Index = IP4_MASK_NUM - 1; Index >= 0; Index--) {\r
665     for (RtTable = IpInstance->RouteTable; RtTable != NULL; RtTable = RtTable->Next) {\r
666       NET_LIST_FOR_EACH (Entry, &(RtTable->RouteArea[Index])) {\r
667         RtEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_ENTRY, Link);\r
668 \r
669         EFI_IP4 (Table[Count].SubnetAddress)  = HTONL (RtEntry->Dest & RtEntry->Netmask);\r
670         EFI_IP4 (Table[Count].SubnetMask)     = HTONL (RtEntry->Netmask);\r
671         EFI_IP4 (Table[Count].GatewayAddress) = HTONL (RtEntry->NextHop);\r
672 \r
673         Count++;\r
674       }\r
675     }\r
676   }\r
677 \r
678   IpInstance->EfiRouteTable = Table;\r
679   IpInstance->EfiRouteCount = Count;\r
680   return EFI_SUCCESS;\r
681 }\r