NetworkPkg: Move Network library and drivers from MdeModulePkg to NetworkPkg
[mirror_edk2.git] / NetworkPkg / Library / DxeIpIoLib / DxeIpIoLib.c
1 /** @file\r
2   IpIo Library.\r
3 \r
4 (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>\r
5 Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.<BR>\r
6 SPDX-License-Identifier: BSD-2-Clause-Patent\r
7 **/\r
8 \r
9 #include <Uefi.h>\r
10 \r
11 #include <Protocol/Udp4.h>\r
12 \r
13 #include <Library/IpIoLib.h>\r
14 #include <Library/BaseLib.h>\r
15 #include <Library/DebugLib.h>\r
16 #include <Library/BaseMemoryLib.h>\r
17 #include <Library/UefiBootServicesTableLib.h>\r
18 #include <Library/MemoryAllocationLib.h>\r
19 #include <Library/DpcLib.h>\r
20 \r
21 \r
22 GLOBAL_REMOVE_IF_UNREFERENCED LIST_ENTRY  mActiveIpIoList = {\r
23   &mActiveIpIoList,\r
24   &mActiveIpIoList\r
25 };\r
26 \r
27 GLOBAL_REMOVE_IF_UNREFERENCED EFI_IP4_CONFIG_DATA  mIp4IoDefaultIpConfigData = {\r
28   EFI_IP_PROTO_UDP,\r
29   FALSE,\r
30   TRUE,\r
31   FALSE,\r
32   FALSE,\r
33   FALSE,\r
34   {{0, 0, 0, 0}},\r
35   {{0, 0, 0, 0}},\r
36   0,\r
37   255,\r
38   FALSE,\r
39   FALSE,\r
40   0,\r
41   0\r
42 };\r
43 \r
44 GLOBAL_REMOVE_IF_UNREFERENCED EFI_IP6_CONFIG_DATA  mIp6IoDefaultIpConfigData = {\r
45   EFI_IP_PROTO_UDP,\r
46   FALSE,\r
47   TRUE,\r
48   FALSE,\r
49   {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},\r
50   {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},\r
51   0,\r
52   255,\r
53   0,\r
54   0,\r
55   0\r
56 };\r
57 \r
58 GLOBAL_REMOVE_IF_UNREFERENCED ICMP_ERROR_INFO  mIcmpErrMap[10] = {\r
59   {FALSE, TRUE }, // ICMP_ERR_UNREACH_NET\r
60   {FALSE, TRUE }, // ICMP_ERR_UNREACH_HOST\r
61   {TRUE,  TRUE }, // ICMP_ERR_UNREACH_PROTOCOL\r
62   {TRUE,  TRUE }, // ICMP_ERR_UNREACH_PORT\r
63   {TRUE,  TRUE }, // ICMP_ERR_MSGSIZE\r
64   {FALSE, TRUE }, // ICMP_ERR_UNREACH_SRCFAIL\r
65   {FALSE, TRUE }, // ICMP_ERR_TIMXCEED_INTRANS\r
66   {FALSE, TRUE }, // ICMP_ERR_TIMEXCEED_REASS\r
67   {FALSE, FALSE}, // ICMP_ERR_QUENCH\r
68   {FALSE, TRUE }  // ICMP_ERR_PARAMPROB\r
69 };\r
70 \r
71 GLOBAL_REMOVE_IF_UNREFERENCED ICMP_ERROR_INFO  mIcmp6ErrMap[10] = {\r
72   {FALSE, TRUE}, // ICMP6_ERR_UNREACH_NET\r
73   {FALSE, TRUE}, // ICMP6_ERR_UNREACH_HOST\r
74   {TRUE,  TRUE}, // ICMP6_ERR_UNREACH_PROTOCOL\r
75   {TRUE,  TRUE}, // ICMP6_ERR_UNREACH_PORT\r
76   {TRUE,  TRUE}, // ICMP6_ERR_PACKAGE_TOOBIG\r
77   {FALSE, TRUE}, // ICMP6_ERR_TIMXCEED_HOPLIMIT\r
78   {FALSE, TRUE}, // ICMP6_ERR_TIMXCEED_REASS\r
79   {FALSE, TRUE}, // ICMP6_ERR_PARAMPROB_HEADER\r
80   {FALSE, TRUE}, // ICMP6_ERR_PARAMPROB_NEXHEADER\r
81   {FALSE, TRUE}  // ICMP6_ERR_PARAMPROB_IPV6OPTION\r
82 };\r
83 \r
84 \r
85 /**\r
86   Notify function for IP transmit token.\r
87 \r
88   @param[in]  Context               The context passed in by the event notifier.\r
89 \r
90 **/\r
91 VOID\r
92 EFIAPI\r
93 IpIoTransmitHandlerDpc (\r
94   IN VOID      *Context\r
95   );\r
96 \r
97 \r
98 /**\r
99   Notify function for IP transmit token.\r
100 \r
101   @param[in]  Event                 The event signaled.\r
102   @param[in]  Context               The context passed in by the event notifier.\r
103 \r
104 **/\r
105 VOID\r
106 EFIAPI\r
107 IpIoTransmitHandler (\r
108   IN EFI_EVENT Event,\r
109   IN VOID      *Context\r
110   );\r
111 \r
112 \r
113 /**\r
114   This function create an IP child ,open the IP protocol, and return the opened\r
115   IP protocol as Interface.\r
116 \r
117   @param[in]    ControllerHandle   The controller handle.\r
118   @param[in]    ImageHandle        The image handle.\r
119   @param[in]    ChildHandle        Pointer to the buffer to save the IP child handle.\r
120   @param[in]    IpVersion          The version of the IP protocol to use, either\r
121                                    IPv4 or IPv6.\r
122   @param[out]   Interface          Pointer used to get the IP protocol interface.\r
123 \r
124   @retval       EFI_SUCCESS        The IP child is created and the IP protocol\r
125                                    interface is retrieved.\r
126   @retval       EFI_UNSUPPORTED    Upsupported IpVersion.\r
127   @retval       Others             The required operation failed.\r
128 \r
129 **/\r
130 EFI_STATUS\r
131 IpIoCreateIpChildOpenProtocol (\r
132   IN  EFI_HANDLE  ControllerHandle,\r
133   IN  EFI_HANDLE  ImageHandle,\r
134   IN  EFI_HANDLE  *ChildHandle,\r
135   IN  UINT8       IpVersion,\r
136   OUT VOID        **Interface\r
137   )\r
138 {\r
139   EFI_STATUS  Status;\r
140   EFI_GUID    *ServiceBindingGuid;\r
141   EFI_GUID    *IpProtocolGuid;\r
142 \r
143   if (IpVersion == IP_VERSION_4) {\r
144     ServiceBindingGuid = &gEfiIp4ServiceBindingProtocolGuid;\r
145     IpProtocolGuid     = &gEfiIp4ProtocolGuid;\r
146   } else if (IpVersion == IP_VERSION_6){\r
147     ServiceBindingGuid = &gEfiIp6ServiceBindingProtocolGuid;\r
148     IpProtocolGuid     = &gEfiIp6ProtocolGuid;\r
149   } else {\r
150     return EFI_UNSUPPORTED;\r
151   }\r
152 \r
153   //\r
154   // Create an IP child.\r
155   //\r
156   Status = NetLibCreateServiceChild (\r
157              ControllerHandle,\r
158              ImageHandle,\r
159              ServiceBindingGuid,\r
160              ChildHandle\r
161              );\r
162   if (EFI_ERROR (Status)) {\r
163     return Status;\r
164   }\r
165 \r
166   //\r
167   // Open the IP protocol installed on the *ChildHandle.\r
168   //\r
169   Status = gBS->OpenProtocol (\r
170                   *ChildHandle,\r
171                   IpProtocolGuid,\r
172                   Interface,\r
173                   ImageHandle,\r
174                   ControllerHandle,\r
175                   EFI_OPEN_PROTOCOL_BY_DRIVER\r
176                   );\r
177   if (EFI_ERROR (Status)) {\r
178     //\r
179     // On failure, destroy the IP child.\r
180     //\r
181     NetLibDestroyServiceChild (\r
182       ControllerHandle,\r
183       ImageHandle,\r
184       ServiceBindingGuid,\r
185       *ChildHandle\r
186       );\r
187   }\r
188 \r
189   return Status;\r
190 }\r
191 \r
192 \r
193 /**\r
194   This function close the previously openned IP protocol and destroy the IP child.\r
195 \r
196   @param[in]  ControllerHandle    The controller handle.\r
197   @param[in]  ImageHandle         The image handle.\r
198   @param[in]  ChildHandle         The child handle of the IP child.\r
199   @param[in]  IpVersion           The version of the IP protocol to use, either\r
200                                   IPv4 or IPv6.\r
201 \r
202   @retval     EFI_SUCCESS         The IP protocol is closed and the relevant IP child\r
203                                   is destroyed.\r
204   @retval     EFI_UNSUPPORTED     Upsupported IpVersion.\r
205   @retval     Others              The required operation failed.\r
206 \r
207 **/\r
208 EFI_STATUS\r
209 IpIoCloseProtocolDestroyIpChild (\r
210   IN EFI_HANDLE  ControllerHandle,\r
211   IN EFI_HANDLE  ImageHandle,\r
212   IN EFI_HANDLE  ChildHandle,\r
213   IN UINT8       IpVersion\r
214   )\r
215 {\r
216   EFI_STATUS  Status;\r
217   EFI_GUID    *ServiceBindingGuid;\r
218   EFI_GUID    *IpProtocolGuid;\r
219 \r
220   if (IpVersion == IP_VERSION_4) {\r
221     ServiceBindingGuid = &gEfiIp4ServiceBindingProtocolGuid;\r
222     IpProtocolGuid     = &gEfiIp4ProtocolGuid;\r
223   } else if (IpVersion == IP_VERSION_6) {\r
224     ServiceBindingGuid = &gEfiIp6ServiceBindingProtocolGuid;\r
225     IpProtocolGuid     = &gEfiIp6ProtocolGuid;\r
226   } else {\r
227     return EFI_UNSUPPORTED;\r
228   }\r
229 \r
230   //\r
231   // Close the previously openned IP protocol.\r
232   //\r
233   Status = gBS->CloseProtocol (\r
234                   ChildHandle,\r
235                   IpProtocolGuid,\r
236                   ImageHandle,\r
237                   ControllerHandle\r
238                   );\r
239   if (EFI_ERROR (Status)) {\r
240     return Status;\r
241   }\r
242 \r
243   //\r
244   // Destroy the IP child.\r
245   //\r
246   return NetLibDestroyServiceChild (\r
247            ControllerHandle,\r
248            ImageHandle,\r
249            ServiceBindingGuid,\r
250            ChildHandle\r
251            );\r
252 }\r
253 \r
254 /**\r
255   This function handles ICMPv4 packets. It is the worker function of\r
256   IpIoIcmpHandler.\r
257 \r
258   @param[in]       IpIo            Pointer to the IP_IO instance.\r
259   @param[in, out]  Pkt             Pointer to the ICMPv4 packet.\r
260   @param[in]       Session         Pointer to the net session of this ICMPv4 packet.\r
261 \r
262   @retval          EFI_SUCCESS     The ICMPv4 packet is handled successfully.\r
263   @retval          EFI_ABORTED     This type of ICMPv4 packet is not supported.\r
264 \r
265 **/\r
266 EFI_STATUS\r
267 IpIoIcmpv4Handler (\r
268   IN     IP_IO                *IpIo,\r
269   IN OUT NET_BUF              *Pkt,\r
270   IN     EFI_NET_SESSION_DATA *Session\r
271   )\r
272 {\r
273   IP4_ICMP_ERROR_HEAD  *IcmpHdr;\r
274   EFI_IP4_HEADER       *IpHdr;\r
275   UINT8                IcmpErr;\r
276   UINT8                *PayLoadHdr;\r
277   UINT8                Type;\r
278   UINT8                Code;\r
279   UINT32               TrimBytes;\r
280 \r
281   ASSERT (IpIo != NULL);\r
282   ASSERT (Pkt != NULL);\r
283   ASSERT (Session != NULL);\r
284   ASSERT (IpIo->IpVersion == IP_VERSION_4);\r
285 \r
286   //\r
287   // Check the ICMP packet length.\r
288   //\r
289   if (Pkt->TotalSize < sizeof (IP4_ICMP_ERROR_HEAD)) {\r
290     return EFI_ABORTED;\r
291   }\r
292 \r
293   IcmpHdr = NET_PROTO_HDR (Pkt, IP4_ICMP_ERROR_HEAD);\r
294   IpHdr   = (EFI_IP4_HEADER *) (&IcmpHdr->IpHead);\r
295 \r
296   if (Pkt->TotalSize < ICMP_ERRLEN (IpHdr)) {\r
297 \r
298     return EFI_ABORTED;\r
299   }\r
300 \r
301   Type = IcmpHdr->Head.Type;\r
302   Code = IcmpHdr->Head.Code;\r
303 \r
304   //\r
305   // Analyze the ICMP Error in this ICMP pkt\r
306   //\r
307   switch (Type) {\r
308   case ICMP_TYPE_UNREACH:\r
309     switch (Code) {\r
310     case ICMP_CODE_UNREACH_NET:\r
311     case ICMP_CODE_UNREACH_HOST:\r
312     case ICMP_CODE_UNREACH_PROTOCOL:\r
313     case ICMP_CODE_UNREACH_PORT:\r
314     case ICMP_CODE_UNREACH_SRCFAIL:\r
315       IcmpErr = (UINT8) (ICMP_ERR_UNREACH_NET + Code);\r
316 \r
317       break;\r
318 \r
319     case ICMP_CODE_UNREACH_NEEDFRAG:\r
320       IcmpErr = ICMP_ERR_MSGSIZE;\r
321 \r
322       break;\r
323 \r
324     case ICMP_CODE_UNREACH_NET_UNKNOWN:\r
325     case ICMP_CODE_UNREACH_NET_PROHIB:\r
326     case ICMP_CODE_UNREACH_TOSNET:\r
327       IcmpErr = ICMP_ERR_UNREACH_NET;\r
328 \r
329       break;\r
330 \r
331     case ICMP_CODE_UNREACH_HOST_UNKNOWN:\r
332     case ICMP_CODE_UNREACH_ISOLATED:\r
333     case ICMP_CODE_UNREACH_HOST_PROHIB:\r
334     case ICMP_CODE_UNREACH_TOSHOST:\r
335       IcmpErr = ICMP_ERR_UNREACH_HOST;\r
336 \r
337       break;\r
338 \r
339     default:\r
340       return EFI_ABORTED;\r
341     }\r
342 \r
343     break;\r
344 \r
345   case ICMP_TYPE_TIMXCEED:\r
346     if (Code > 1) {\r
347       return EFI_ABORTED;\r
348     }\r
349 \r
350     IcmpErr = (UINT8) (Code + ICMP_ERR_TIMXCEED_INTRANS);\r
351 \r
352     break;\r
353 \r
354   case ICMP_TYPE_PARAMPROB:\r
355     if (Code > 1) {\r
356       return EFI_ABORTED;\r
357     }\r
358 \r
359     IcmpErr = ICMP_ERR_PARAMPROB;\r
360 \r
361     break;\r
362 \r
363   case ICMP_TYPE_SOURCEQUENCH:\r
364     if (Code != 0) {\r
365       return EFI_ABORTED;\r
366     }\r
367 \r
368     IcmpErr = ICMP_ERR_QUENCH;\r
369 \r
370     break;\r
371 \r
372   default:\r
373     return EFI_ABORTED;\r
374   }\r
375 \r
376   //\r
377   // Notify user the ICMP pkt only containing payload except\r
378   // IP and ICMP header\r
379   //\r
380   PayLoadHdr = (UINT8 *) ((UINT8 *) IpHdr + EFI_IP4_HEADER_LEN (IpHdr));\r
381   TrimBytes  = (UINT32) (PayLoadHdr - (UINT8 *) IcmpHdr);\r
382 \r
383   NetbufTrim (Pkt, TrimBytes, TRUE);\r
384 \r
385   //\r
386   // If the input packet has invalid format, and TrimBytes is larger than\r
387   // the packet size, the NetbufTrim might trim the packet to zero.\r
388   //\r
389   if (Pkt->TotalSize != 0) {\r
390     IpIo->PktRcvdNotify (EFI_ICMP_ERROR, IcmpErr, Session, Pkt, IpIo->RcvdContext);\r
391   }\r
392 \r
393   return EFI_SUCCESS;\r
394 }\r
395 \r
396 /**\r
397   This function handles ICMPv6 packets. It is the worker function of\r
398   IpIoIcmpHandler.\r
399 \r
400   @param[in]       IpIo            Pointer to the IP_IO instance.\r
401   @param[in, out]  Pkt             Pointer to the ICMPv6 packet.\r
402   @param[in]       Session         Pointer to the net session of this ICMPv6 packet.\r
403 \r
404   @retval          EFI_SUCCESS     The ICMPv6 packet is handled successfully.\r
405   @retval          EFI_ABORTED     This type of ICMPv6 packet is not supported.\r
406 \r
407 **/\r
408 EFI_STATUS\r
409 IpIoIcmpv6Handler (\r
410   IN     IP_IO                *IpIo,\r
411   IN OUT NET_BUF              *Pkt,\r
412   IN     EFI_NET_SESSION_DATA *Session\r
413   )\r
414 {\r
415   IP6_ICMP_ERROR_HEAD  *IcmpHdr;\r
416   EFI_IP6_HEADER       *IpHdr;\r
417   UINT8                IcmpErr;\r
418   UINT8                *PayLoadHdr;\r
419   UINT8                Type;\r
420   UINT8                Code;\r
421   UINT8                NextHeader;\r
422   UINT32               TrimBytes;\r
423   BOOLEAN              Flag;\r
424 \r
425   ASSERT (IpIo != NULL);\r
426   ASSERT (Pkt != NULL);\r
427   ASSERT (Session != NULL);\r
428   ASSERT (IpIo->IpVersion == IP_VERSION_6);\r
429 \r
430   //\r
431   // Check the ICMPv6 packet length.\r
432   //\r
433   if (Pkt->TotalSize < sizeof (IP6_ICMP_ERROR_HEAD)) {\r
434 \r
435     return EFI_ABORTED;\r
436   }\r
437 \r
438   IcmpHdr = NET_PROTO_HDR (Pkt, IP6_ICMP_ERROR_HEAD);\r
439   Type    = IcmpHdr->Head.Type;\r
440   Code    = IcmpHdr->Head.Code;\r
441 \r
442   //\r
443   // Analyze the ICMPv6 Error in this ICMPv6 packet\r
444   //\r
445   switch (Type) {\r
446   case ICMP_V6_DEST_UNREACHABLE:\r
447     switch (Code) {\r
448     case ICMP_V6_NO_ROUTE_TO_DEST:\r
449     case ICMP_V6_BEYOND_SCOPE:\r
450     case ICMP_V6_ROUTE_REJECTED:\r
451       IcmpErr = ICMP6_ERR_UNREACH_NET;\r
452 \r
453       break;\r
454 \r
455     case ICMP_V6_COMM_PROHIBITED:\r
456     case ICMP_V6_ADDR_UNREACHABLE:\r
457     case ICMP_V6_SOURCE_ADDR_FAILED:\r
458       IcmpErr = ICMP6_ERR_UNREACH_HOST;\r
459 \r
460       break;\r
461 \r
462     case ICMP_V6_PORT_UNREACHABLE:\r
463       IcmpErr = ICMP6_ERR_UNREACH_PORT;\r
464 \r
465       break;\r
466 \r
467      default:\r
468       return EFI_ABORTED;\r
469     }\r
470 \r
471     break;\r
472 \r
473   case ICMP_V6_PACKET_TOO_BIG:\r
474     if (Code >= 1) {\r
475       return EFI_ABORTED;\r
476     }\r
477 \r
478     IcmpErr = ICMP6_ERR_PACKAGE_TOOBIG;\r
479 \r
480     break;\r
481 \r
482   case ICMP_V6_TIME_EXCEEDED:\r
483     if (Code > 1) {\r
484       return EFI_ABORTED;\r
485     }\r
486 \r
487     IcmpErr = (UINT8) (ICMP6_ERR_TIMXCEED_HOPLIMIT + Code);\r
488 \r
489     break;\r
490 \r
491   case ICMP_V6_PARAMETER_PROBLEM:\r
492     if (Code > 3) {\r
493       return EFI_ABORTED;\r
494     }\r
495 \r
496     IcmpErr = (UINT8) (ICMP6_ERR_PARAMPROB_HEADER + Code);\r
497 \r
498     break;\r
499 \r
500    default:\r
501 \r
502      return EFI_ABORTED;\r
503    }\r
504 \r
505   //\r
506   // Notify user the ICMPv6 packet only containing payload except\r
507   // IPv6 basic header, extension header and ICMP header\r
508   //\r
509 \r
510   IpHdr      = (EFI_IP6_HEADER *) (&IcmpHdr->IpHead);\r
511   NextHeader = IpHdr->NextHeader;\r
512   PayLoadHdr = (UINT8 *) ((UINT8 *) IcmpHdr + sizeof (IP6_ICMP_ERROR_HEAD));\r
513   Flag       = TRUE;\r
514 \r
515   do {\r
516     switch (NextHeader) {\r
517     case EFI_IP_PROTO_UDP:\r
518     case EFI_IP_PROTO_TCP:\r
519     case EFI_IP_PROTO_ICMP:\r
520     case IP6_NO_NEXT_HEADER:\r
521       Flag = FALSE;\r
522 \r
523       break;\r
524 \r
525     case IP6_HOP_BY_HOP:\r
526     case IP6_DESTINATION:\r
527       //\r
528       // The Hdr Ext Len is 8-bit unsigned integer in 8-octet units, not including\r
529       // the first 8 octets.\r
530       //\r
531       NextHeader = *(PayLoadHdr);\r
532       PayLoadHdr = (UINT8 *) (PayLoadHdr + (*(PayLoadHdr + 1) + 1) * 8);\r
533 \r
534       break;\r
535 \r
536     case IP6_FRAGMENT:\r
537       //\r
538       // The Fragment Header Length is 8 octets.\r
539       //\r
540       NextHeader = *(PayLoadHdr);\r
541       PayLoadHdr = (UINT8 *) (PayLoadHdr + 8);\r
542 \r
543       break;\r
544 \r
545     default:\r
546 \r
547       return EFI_ABORTED;\r
548     }\r
549   } while (Flag);\r
550 \r
551   TrimBytes = (UINT32) (PayLoadHdr - (UINT8 *) IcmpHdr);\r
552 \r
553   NetbufTrim (Pkt, TrimBytes, TRUE);\r
554 \r
555   //\r
556   // If the input packet has invalid format, and TrimBytes is larger than\r
557   // the packet size, the NetbufTrim might trim the packet to zero.\r
558   //\r
559   if (Pkt->TotalSize != 0) {\r
560     IpIo->PktRcvdNotify (EFI_ICMP_ERROR, IcmpErr, Session, Pkt, IpIo->RcvdContext);\r
561   }\r
562 \r
563   return EFI_SUCCESS;\r
564 }\r
565 \r
566 /**\r
567   This function handles ICMP packets.\r
568 \r
569   @param[in]       IpIo            Pointer to the IP_IO instance.\r
570   @param[in, out]  Pkt             Pointer to the ICMP packet.\r
571   @param[in]       Session         Pointer to the net session of this ICMP packet.\r
572 \r
573   @retval          EFI_SUCCESS     The ICMP packet is handled successfully.\r
574   @retval          EFI_ABORTED     This type of ICMP packet is not supported.\r
575   @retval          EFI_UNSUPPORTED The IP protocol version in IP_IO is not supported.\r
576 \r
577 **/\r
578 EFI_STATUS\r
579 IpIoIcmpHandler (\r
580   IN     IP_IO                *IpIo,\r
581   IN OUT NET_BUF              *Pkt,\r
582   IN     EFI_NET_SESSION_DATA *Session\r
583   )\r
584 {\r
585 \r
586   if (IpIo->IpVersion == IP_VERSION_4) {\r
587 \r
588     return IpIoIcmpv4Handler (IpIo, Pkt, Session);\r
589 \r
590   } else if (IpIo->IpVersion == IP_VERSION_6) {\r
591 \r
592     return IpIoIcmpv6Handler (IpIo, Pkt, Session);\r
593 \r
594   } else {\r
595 \r
596     return EFI_UNSUPPORTED;\r
597   }\r
598 }\r
599 \r
600 \r
601 /**\r
602   Free function for receive token of IP_IO. It is used to\r
603   signal the recycle event to notify IP to recycle the\r
604   data buffer.\r
605 \r
606   @param[in]  Event                 The event to be signaled.\r
607 \r
608 **/\r
609 VOID\r
610 EFIAPI\r
611 IpIoExtFree (\r
612   IN VOID  *Event\r
613   )\r
614 {\r
615   gBS->SignalEvent ((EFI_EVENT) Event);\r
616 }\r
617 \r
618 \r
619 /**\r
620   Create a send entry to wrap a packet before sending\r
621   out it through IP.\r
622 \r
623   @param[in, out]  IpIo                 Pointer to the IP_IO instance.\r
624   @param[in, out]  Pkt                  Pointer to the packet.\r
625   @param[in]       Sender               Pointer to the IP sender.\r
626   @param[in]       Context              Pointer to the context.\r
627   @param[in]       NotifyData           Pointer to the notify data.\r
628   @param[in]       Dest                 Pointer to the destination IP address.\r
629   @param[in]       Override             Pointer to the overriden IP_IO data.\r
630 \r
631   @return Pointer to the data structure created to wrap the packet. If any error occurs,\r
632           then return NULL.\r
633 \r
634 **/\r
635 IP_IO_SEND_ENTRY *\r
636 IpIoCreateSndEntry (\r
637   IN OUT IP_IO             *IpIo,\r
638   IN OUT NET_BUF           *Pkt,\r
639   IN     IP_IO_IP_PROTOCOL Sender,\r
640   IN     VOID              *Context    OPTIONAL,\r
641   IN     VOID              *NotifyData OPTIONAL,\r
642   IN     EFI_IP_ADDRESS    *Dest       OPTIONAL,\r
643   IN     IP_IO_OVERRIDE    *Override\r
644   )\r
645 {\r
646   IP_IO_SEND_ENTRY          *SndEntry;\r
647   EFI_EVENT                 Event;\r
648   EFI_STATUS                Status;\r
649   NET_FRAGMENT              *ExtFragment;\r
650   UINT32                    FragmentCount;\r
651   IP_IO_OVERRIDE            *OverrideData;\r
652   IP_IO_IP_TX_DATA          *TxData;\r
653   EFI_IP4_TRANSMIT_DATA     *Ip4TxData;\r
654   EFI_IP6_TRANSMIT_DATA     *Ip6TxData;\r
655 \r
656   if ((IpIo->IpVersion != IP_VERSION_4) && (IpIo->IpVersion != IP_VERSION_6)) {\r
657     return NULL;\r
658   }\r
659 \r
660   Event        = NULL;\r
661   TxData       = NULL;\r
662   OverrideData = NULL;\r
663 \r
664   //\r
665   // Allocate resource for SndEntry\r
666   //\r
667   SndEntry = AllocatePool (sizeof (IP_IO_SEND_ENTRY));\r
668   if (NULL == SndEntry) {\r
669     return NULL;\r
670   }\r
671 \r
672   Status = gBS->CreateEvent (\r
673                   EVT_NOTIFY_SIGNAL,\r
674                   TPL_NOTIFY,\r
675                   IpIoTransmitHandler,\r
676                   SndEntry,\r
677                   &Event\r
678                   );\r
679   if (EFI_ERROR (Status)) {\r
680     goto ON_ERROR;\r
681   }\r
682 \r
683   FragmentCount = Pkt->BlockOpNum;\r
684 \r
685   //\r
686   // Allocate resource for TxData\r
687   //\r
688   TxData = (IP_IO_IP_TX_DATA *) AllocatePool (\r
689     sizeof (IP_IO_IP_TX_DATA) + sizeof (NET_FRAGMENT) * (FragmentCount - 1)\r
690     );\r
691 \r
692   if (NULL == TxData) {\r
693     goto ON_ERROR;\r
694   }\r
695 \r
696   //\r
697   // Build a fragment table to contain the fragments in the packet.\r
698   //\r
699   if (IpIo->IpVersion == IP_VERSION_4) {\r
700     ExtFragment = (NET_FRAGMENT *) TxData->Ip4TxData.FragmentTable;\r
701   } else {\r
702     ExtFragment = (NET_FRAGMENT *) TxData->Ip6TxData.FragmentTable;\r
703   }\r
704 \r
705   NetbufBuildExt (Pkt, ExtFragment, &FragmentCount);\r
706 \r
707 \r
708   //\r
709   // Allocate resource for OverrideData if needed\r
710   //\r
711   if (NULL != Override) {\r
712 \r
713     OverrideData = AllocateCopyPool (sizeof (IP_IO_OVERRIDE), Override);\r
714     if (NULL == OverrideData) {\r
715       goto ON_ERROR;\r
716     }\r
717   }\r
718 \r
719   //\r
720   // Set other fields of TxData except the fragment table\r
721   //\r
722   if (IpIo->IpVersion == IP_VERSION_4) {\r
723 \r
724     Ip4TxData = &TxData->Ip4TxData;\r
725 \r
726     IP4_COPY_ADDRESS (&Ip4TxData->DestinationAddress, Dest);\r
727 \r
728     Ip4TxData->OverrideData    = &OverrideData->Ip4OverrideData;\r
729     Ip4TxData->OptionsLength   = 0;\r
730     Ip4TxData->OptionsBuffer   = NULL;\r
731     Ip4TxData->TotalDataLength = Pkt->TotalSize;\r
732     Ip4TxData->FragmentCount   = FragmentCount;\r
733 \r
734     //\r
735     // Set the fields of SndToken\r
736     //\r
737     SndEntry->SndToken.Ip4Token.Event         = Event;\r
738     SndEntry->SndToken.Ip4Token.Packet.TxData = Ip4TxData;\r
739   } else {\r
740 \r
741     Ip6TxData = &TxData->Ip6TxData;\r
742 \r
743     if (Dest != NULL) {\r
744       CopyMem (&Ip6TxData->DestinationAddress, Dest, sizeof (EFI_IPv6_ADDRESS));\r
745     } else {\r
746       ZeroMem (&Ip6TxData->DestinationAddress, sizeof (EFI_IPv6_ADDRESS));\r
747     }\r
748 \r
749     Ip6TxData->OverrideData  = &OverrideData->Ip6OverrideData;\r
750     Ip6TxData->DataLength    = Pkt->TotalSize;\r
751     Ip6TxData->FragmentCount = FragmentCount;\r
752     Ip6TxData->ExtHdrsLength = 0;\r
753     Ip6TxData->ExtHdrs       = NULL;\r
754 \r
755     //\r
756     // Set the fields of SndToken\r
757     //\r
758     SndEntry->SndToken.Ip6Token.Event         = Event;\r
759     SndEntry->SndToken.Ip6Token.Packet.TxData = Ip6TxData;\r
760   }\r
761 \r
762   //\r
763   // Set the fields of SndEntry\r
764   //\r
765   SndEntry->IpIo        = IpIo;\r
766   SndEntry->Ip          = Sender;\r
767   SndEntry->Context     = Context;\r
768   SndEntry->NotifyData  = NotifyData;\r
769 \r
770   SndEntry->Pkt         = Pkt;\r
771   NET_GET_REF (Pkt);\r
772 \r
773   InsertTailList (&IpIo->PendingSndList, &SndEntry->Entry);\r
774 \r
775   return SndEntry;\r
776 \r
777 ON_ERROR:\r
778 \r
779   if (OverrideData != NULL) {\r
780     FreePool (OverrideData);\r
781   }\r
782 \r
783   if (TxData != NULL) {\r
784     FreePool (TxData);\r
785   }\r
786 \r
787   if (SndEntry != NULL) {\r
788     FreePool (SndEntry);\r
789   }\r
790 \r
791   if (Event != NULL) {\r
792     gBS->CloseEvent (Event);\r
793   }\r
794 \r
795   return NULL;\r
796 }\r
797 \r
798 \r
799 /**\r
800   Destroy the SndEntry.\r
801 \r
802   This function pairs with IpIoCreateSndEntry().\r
803 \r
804   @param[in]  SndEntry              Pointer to the send entry to be destroyed.\r
805 \r
806 **/\r
807 VOID\r
808 IpIoDestroySndEntry (\r
809   IN IP_IO_SEND_ENTRY  *SndEntry\r
810   )\r
811 {\r
812   EFI_EVENT         Event;\r
813   IP_IO_IP_TX_DATA  *TxData;\r
814   IP_IO_OVERRIDE    *Override;\r
815 \r
816   if (SndEntry->IpIo->IpVersion == IP_VERSION_4) {\r
817     Event              = SndEntry->SndToken.Ip4Token.Event;\r
818     TxData             = (IP_IO_IP_TX_DATA *) SndEntry->SndToken.Ip4Token.Packet.TxData;\r
819     Override           = (IP_IO_OVERRIDE *) TxData->Ip4TxData.OverrideData;\r
820   } else if (SndEntry->IpIo->IpVersion == IP_VERSION_6) {\r
821     Event              = SndEntry->SndToken.Ip6Token.Event;\r
822     TxData             = (IP_IO_IP_TX_DATA *) SndEntry->SndToken.Ip6Token.Packet.TxData;\r
823     Override           = (IP_IO_OVERRIDE *) TxData->Ip6TxData.OverrideData;\r
824   } else {\r
825     return ;\r
826   }\r
827 \r
828   gBS->CloseEvent (Event);\r
829 \r
830   FreePool (TxData);\r
831 \r
832   if (NULL != Override) {\r
833     FreePool (Override);\r
834   }\r
835 \r
836   NetbufFree (SndEntry->Pkt);\r
837 \r
838   RemoveEntryList (&SndEntry->Entry);\r
839 \r
840   FreePool (SndEntry);\r
841 }\r
842 \r
843 \r
844 /**\r
845   Notify function for IP transmit token.\r
846 \r
847   @param[in]  Context               The context passed in by the event notifier.\r
848 \r
849 **/\r
850 VOID\r
851 EFIAPI\r
852 IpIoTransmitHandlerDpc (\r
853   IN VOID      *Context\r
854   )\r
855 {\r
856   IP_IO             *IpIo;\r
857   IP_IO_SEND_ENTRY  *SndEntry;\r
858   EFI_STATUS        Status;\r
859 \r
860   SndEntry  = (IP_IO_SEND_ENTRY *) Context;\r
861 \r
862   IpIo      = SndEntry->IpIo;\r
863 \r
864   if (IpIo->IpVersion == IP_VERSION_4) {\r
865     Status = SndEntry->SndToken.Ip4Token.Status;\r
866   } else if (IpIo->IpVersion == IP_VERSION_6){\r
867     Status = SndEntry->SndToken.Ip6Token.Status;\r
868   } else {\r
869     return ;\r
870   }\r
871 \r
872   if ((IpIo->PktSentNotify != NULL) && (SndEntry->NotifyData != NULL)) {\r
873     IpIo->PktSentNotify (\r
874             Status,\r
875             SndEntry->Context,\r
876             SndEntry->Ip,\r
877             SndEntry->NotifyData\r
878             );\r
879   }\r
880 \r
881   IpIoDestroySndEntry (SndEntry);\r
882 }\r
883 \r
884 \r
885 /**\r
886   Notify function for IP transmit token.\r
887 \r
888   @param[in]  Event                 The event signaled.\r
889   @param[in]  Context               The context passed in by the event notifier.\r
890 \r
891 **/\r
892 VOID\r
893 EFIAPI\r
894 IpIoTransmitHandler (\r
895   IN EFI_EVENT Event,\r
896   IN VOID      *Context\r
897   )\r
898 {\r
899   //\r
900   // Request IpIoTransmitHandlerDpc as a DPC at TPL_CALLBACK\r
901   //\r
902   QueueDpc (TPL_CALLBACK, IpIoTransmitHandlerDpc, Context);\r
903 }\r
904 \r
905 \r
906 /**\r
907   The dummy handler for the dummy IP receive token.\r
908 \r
909   @param[in]  Context               The context passed in by the event notifier.\r
910 \r
911 **/\r
912 VOID\r
913 EFIAPI\r
914 IpIoDummyHandlerDpc (\r
915   IN VOID      *Context\r
916   )\r
917 {\r
918   IP_IO_IP_INFO             *IpInfo;\r
919   EFI_STATUS                 Status;\r
920   EFI_EVENT                  RecycleEvent;\r
921 \r
922   IpInfo      = (IP_IO_IP_INFO *) Context;\r
923 \r
924   if ((IpInfo->IpVersion != IP_VERSION_4) && (IpInfo->IpVersion != IP_VERSION_6)) {\r
925     return ;\r
926   }\r
927 \r
928   RecycleEvent = NULL;\r
929 \r
930   if (IpInfo->IpVersion == IP_VERSION_4) {\r
931     Status = IpInfo->DummyRcvToken.Ip4Token.Status;\r
932 \r
933     if (IpInfo->DummyRcvToken.Ip4Token.Packet.RxData != NULL) {\r
934       RecycleEvent = IpInfo->DummyRcvToken.Ip4Token.Packet.RxData->RecycleSignal;\r
935     }\r
936   } else {\r
937     Status = IpInfo->DummyRcvToken.Ip6Token.Status;\r
938 \r
939     if (IpInfo->DummyRcvToken.Ip6Token.Packet.RxData != NULL) {\r
940       RecycleEvent = IpInfo->DummyRcvToken.Ip6Token.Packet.RxData->RecycleSignal;\r
941     }\r
942   }\r
943 \r
944 \r
945 \r
946   if (EFI_ABORTED == Status) {\r
947     //\r
948     // The reception is actively aborted by the consumer, directly return.\r
949     //\r
950     return;\r
951   } else if (EFI_SUCCESS == Status) {\r
952     //\r
953     // Recycle the RxData.\r
954     //\r
955     ASSERT (RecycleEvent != NULL);\r
956 \r
957     gBS->SignalEvent (RecycleEvent);\r
958   }\r
959 \r
960   //\r
961   // Continue the receive.\r
962   //\r
963   if (IpInfo->IpVersion == IP_VERSION_4) {\r
964     IpInfo->Ip.Ip4->Receive (\r
965                       IpInfo->Ip.Ip4,\r
966                       &IpInfo->DummyRcvToken.Ip4Token\r
967                       );\r
968   } else {\r
969     IpInfo->Ip.Ip6->Receive (\r
970                       IpInfo->Ip.Ip6,\r
971                       &IpInfo->DummyRcvToken.Ip6Token\r
972                       );\r
973   }\r
974 }\r
975 \r
976 \r
977 /**\r
978   This function add IpIoDummyHandlerDpc to the end of the DPC queue.\r
979 \r
980   @param[in]  Event                 The event signaled.\r
981   @param[in]  Context               The context passed in by the event notifier.\r
982 \r
983 **/\r
984 VOID\r
985 EFIAPI\r
986 IpIoDummyHandler (\r
987   IN EFI_EVENT Event,\r
988   IN VOID      *Context\r
989   )\r
990 {\r
991   //\r
992   // Request IpIoDummyHandlerDpc as a DPC at TPL_CALLBACK\r
993   //\r
994   QueueDpc (TPL_CALLBACK, IpIoDummyHandlerDpc, Context);\r
995 }\r
996 \r
997 \r
998 /**\r
999   Notify function for the IP receive token, used to process\r
1000   the received IP packets.\r
1001 \r
1002   @param[in]  Context               The context passed in by the event notifier.\r
1003 \r
1004 **/\r
1005 VOID\r
1006 EFIAPI\r
1007 IpIoListenHandlerDpc (\r
1008   IN VOID      *Context\r
1009   )\r
1010 {\r
1011   IP_IO                 *IpIo;\r
1012   EFI_STATUS            Status;\r
1013   IP_IO_IP_RX_DATA      *RxData;\r
1014   EFI_NET_SESSION_DATA  Session;\r
1015   NET_BUF               *Pkt;\r
1016 \r
1017   IpIo = (IP_IO *) Context;\r
1018 \r
1019   if (IpIo->IpVersion == IP_VERSION_4) {\r
1020     Status = IpIo->RcvToken.Ip4Token.Status;\r
1021     RxData = (IP_IO_IP_RX_DATA *) IpIo->RcvToken.Ip4Token.Packet.RxData;\r
1022   } else if (IpIo->IpVersion == IP_VERSION_6) {\r
1023     Status = IpIo->RcvToken.Ip6Token.Status;\r
1024     RxData = (IP_IO_IP_RX_DATA *) IpIo->RcvToken.Ip6Token.Packet.RxData;\r
1025   } else {\r
1026     return;\r
1027   }\r
1028 \r
1029   if (EFI_ABORTED == Status) {\r
1030     //\r
1031     // The reception is actively aborted by the consumer, directly return.\r
1032     //\r
1033     return;\r
1034   }\r
1035 \r
1036   if ((EFI_SUCCESS != Status) && (EFI_ICMP_ERROR != Status)) {\r
1037     //\r
1038     // Only process the normal packets and the icmp error packets.\r
1039     //\r
1040     if (RxData != NULL) {\r
1041       goto CleanUp;\r
1042     } else {\r
1043       goto Resume;\r
1044     }\r
1045   }\r
1046 \r
1047   //\r
1048   // if RxData is NULL with Status == EFI_SUCCESS or EFI_ICMP_ERROR, this should be a code issue in the low layer (IP).\r
1049   //\r
1050   ASSERT (RxData != NULL);\r
1051   if (RxData == NULL) {\r
1052     goto Resume;\r
1053   }\r
1054 \r
1055   if (NULL == IpIo->PktRcvdNotify) {\r
1056     goto CleanUp;\r
1057   }\r
1058 \r
1059   if (IpIo->IpVersion == IP_VERSION_4) {\r
1060     ASSERT (RxData->Ip4RxData.Header != NULL);\r
1061     if (IP4_IS_LOCAL_BROADCAST (EFI_IP4 (RxData->Ip4RxData.Header->SourceAddress))) {\r
1062       //\r
1063       // The source address is a broadcast address, discard it.\r
1064       //\r
1065       goto CleanUp;\r
1066     }\r
1067     if ((EFI_IP4 (RxData->Ip4RxData.Header->SourceAddress) != 0) &&\r
1068         (IpIo->SubnetMask != 0) &&\r
1069         IP4_NET_EQUAL (IpIo->StationIp, EFI_NTOHL (((EFI_IP4_RECEIVE_DATA *) RxData)->Header->SourceAddress), IpIo->SubnetMask) &&\r
1070         !NetIp4IsUnicast (EFI_NTOHL (((EFI_IP4_RECEIVE_DATA *) RxData)->Header->SourceAddress), IpIo->SubnetMask)) {\r
1071       //\r
1072       // The source address doesn't match StationIp and it's not a unicast IP address, discard it.\r
1073       //\r
1074       goto CleanUp;\r
1075     }\r
1076 \r
1077     if (RxData->Ip4RxData.DataLength == 0) {\r
1078       //\r
1079       // Discard zero length data payload packet.\r
1080       //\r
1081       goto CleanUp;\r
1082     }\r
1083 \r
1084     //\r
1085     // The fragment should always be valid for non-zero length packet.\r
1086     //\r
1087     ASSERT (RxData->Ip4RxData.FragmentCount != 0);\r
1088 \r
1089     //\r
1090     // Create a netbuffer representing IPv4 packet\r
1091     //\r
1092     Pkt = NetbufFromExt (\r
1093             (NET_FRAGMENT *) RxData->Ip4RxData.FragmentTable,\r
1094             RxData->Ip4RxData.FragmentCount,\r
1095             0,\r
1096             0,\r
1097             IpIoExtFree,\r
1098             RxData->Ip4RxData.RecycleSignal\r
1099             );\r
1100     if (NULL == Pkt) {\r
1101       goto CleanUp;\r
1102     }\r
1103 \r
1104     //\r
1105     // Create a net session\r
1106     //\r
1107     Session.Source.Addr[0] = EFI_IP4 (RxData->Ip4RxData.Header->SourceAddress);\r
1108     Session.Dest.Addr[0]   = EFI_IP4 (RxData->Ip4RxData.Header->DestinationAddress);\r
1109     Session.IpHdr.Ip4Hdr   = RxData->Ip4RxData.Header;\r
1110     Session.IpHdrLen       = RxData->Ip4RxData.HeaderLength;\r
1111     Session.IpVersion      = IP_VERSION_4;\r
1112   } else {\r
1113     ASSERT (RxData->Ip6RxData.Header != NULL);\r
1114     if (!NetIp6IsValidUnicast(&RxData->Ip6RxData.Header->SourceAddress)) {\r
1115       goto CleanUp;\r
1116     }\r
1117 \r
1118     if (RxData->Ip6RxData.DataLength == 0) {\r
1119       //\r
1120       // Discard zero length data payload packet.\r
1121       //\r
1122       goto CleanUp;\r
1123     }\r
1124 \r
1125     //\r
1126     // The fragment should always be valid for non-zero length packet.\r
1127     //\r
1128     ASSERT (RxData->Ip6RxData.FragmentCount != 0);\r
1129 \r
1130     //\r
1131     // Create a netbuffer representing IPv6 packet\r
1132     //\r
1133     Pkt = NetbufFromExt (\r
1134             (NET_FRAGMENT *) RxData->Ip6RxData.FragmentTable,\r
1135             RxData->Ip6RxData.FragmentCount,\r
1136             0,\r
1137             0,\r
1138             IpIoExtFree,\r
1139             RxData->Ip6RxData.RecycleSignal\r
1140             );\r
1141     if (NULL == Pkt) {\r
1142       goto CleanUp;\r
1143     }\r
1144 \r
1145     //\r
1146     // Create a net session\r
1147     //\r
1148     CopyMem (\r
1149       &Session.Source,\r
1150       &RxData->Ip6RxData.Header->SourceAddress,\r
1151       sizeof(EFI_IPv6_ADDRESS)\r
1152       );\r
1153     CopyMem (\r
1154       &Session.Dest,\r
1155       &RxData->Ip6RxData.Header->DestinationAddress,\r
1156       sizeof(EFI_IPv6_ADDRESS)\r
1157       );\r
1158     Session.IpHdr.Ip6Hdr = RxData->Ip6RxData.Header;\r
1159     Session.IpHdrLen     = RxData->Ip6RxData.HeaderLength;\r
1160     Session.IpVersion    = IP_VERSION_6;\r
1161   }\r
1162 \r
1163   if (EFI_SUCCESS == Status) {\r
1164 \r
1165     IpIo->PktRcvdNotify (EFI_SUCCESS, 0, &Session, Pkt, IpIo->RcvdContext);\r
1166   } else {\r
1167     //\r
1168     // Status is EFI_ICMP_ERROR\r
1169     //\r
1170     Status = IpIoIcmpHandler (IpIo, Pkt, &Session);\r
1171     if (EFI_ERROR (Status)) {\r
1172       NetbufFree (Pkt);\r
1173     }\r
1174   }\r
1175 \r
1176   goto Resume;\r
1177 \r
1178 CleanUp:\r
1179 \r
1180   if (IpIo->IpVersion == IP_VERSION_4){\r
1181     gBS->SignalEvent (RxData->Ip4RxData.RecycleSignal);\r
1182   } else {\r
1183     gBS->SignalEvent (RxData->Ip6RxData.RecycleSignal);\r
1184   }\r
1185 \r
1186 Resume:\r
1187 \r
1188   if (IpIo->IpVersion == IP_VERSION_4){\r
1189     IpIo->Ip.Ip4->Receive (IpIo->Ip.Ip4, &(IpIo->RcvToken.Ip4Token));\r
1190   } else {\r
1191     IpIo->Ip.Ip6->Receive (IpIo->Ip.Ip6, &(IpIo->RcvToken.Ip6Token));\r
1192   }\r
1193 }\r
1194 \r
1195 /**\r
1196   This function add IpIoListenHandlerDpc to the end of the DPC queue.\r
1197 \r
1198   @param[in]  Event                The event signaled.\r
1199   @param[in]  Context              The context passed in by the event notifier.\r
1200 \r
1201 **/\r
1202 VOID\r
1203 EFIAPI\r
1204 IpIoListenHandler (\r
1205   IN EFI_EVENT Event,\r
1206   IN VOID      *Context\r
1207   )\r
1208 {\r
1209   //\r
1210   // Request IpIoListenHandlerDpc as a DPC at TPL_CALLBACK\r
1211   //\r
1212   QueueDpc (TPL_CALLBACK, IpIoListenHandlerDpc, Context);\r
1213 }\r
1214 \r
1215 \r
1216 /**\r
1217   Create a new IP_IO instance.\r
1218 \r
1219   If IpVersion is not IP_VERSION_4 or IP_VERSION_6, then ASSERT().\r
1220 \r
1221   This function uses IP4/IP6 service binding protocol in Controller to create\r
1222   an IP4/IP6 child (aka IP4/IP6 instance).\r
1223 \r
1224   @param[in]  Image             The image handle of the driver or application that\r
1225                                 consumes IP_IO.\r
1226   @param[in]  Controller        The controller handle that has IP4 or IP6 service\r
1227                                 binding protocol installed.\r
1228   @param[in]  IpVersion         The version of the IP protocol to use, either\r
1229                                 IPv4 or IPv6.\r
1230 \r
1231   @return Pointer to a newly created IP_IO instance, or NULL if failed.\r
1232 \r
1233 **/\r
1234 IP_IO *\r
1235 EFIAPI\r
1236 IpIoCreate (\r
1237   IN EFI_HANDLE Image,\r
1238   IN EFI_HANDLE Controller,\r
1239   IN UINT8      IpVersion\r
1240   )\r
1241 {\r
1242   EFI_STATUS  Status;\r
1243   IP_IO       *IpIo;\r
1244   EFI_EVENT   Event;\r
1245 \r
1246   ASSERT ((IpVersion == IP_VERSION_4) || (IpVersion == IP_VERSION_6));\r
1247 \r
1248   IpIo = AllocateZeroPool (sizeof (IP_IO));\r
1249   if (NULL == IpIo) {\r
1250     return NULL;\r
1251   }\r
1252 \r
1253   InitializeListHead (&(IpIo->PendingSndList));\r
1254   InitializeListHead (&(IpIo->IpList));\r
1255   IpIo->Controller  = Controller;\r
1256   IpIo->Image       = Image;\r
1257   IpIo->IpVersion   = IpVersion;\r
1258   Event             = NULL;\r
1259 \r
1260   Status = gBS->CreateEvent (\r
1261                   EVT_NOTIFY_SIGNAL,\r
1262                   TPL_NOTIFY,\r
1263                   IpIoListenHandler,\r
1264                   IpIo,\r
1265                   &Event\r
1266                   );\r
1267   if (EFI_ERROR (Status)) {\r
1268     goto ReleaseIpIo;\r
1269   }\r
1270 \r
1271   if (IpVersion == IP_VERSION_4) {\r
1272     IpIo->RcvToken.Ip4Token.Event = Event;\r
1273   } else {\r
1274     IpIo->RcvToken.Ip6Token.Event = Event;\r
1275   }\r
1276 \r
1277   //\r
1278   // Create an IP child and open IP protocol\r
1279   //\r
1280   Status = IpIoCreateIpChildOpenProtocol (\r
1281              Controller,\r
1282              Image,\r
1283              &IpIo->ChildHandle,\r
1284              IpVersion,\r
1285              (VOID **) & (IpIo->Ip)\r
1286              );\r
1287   if (EFI_ERROR (Status)) {\r
1288     goto ReleaseIpIo;\r
1289   }\r
1290 \r
1291   return IpIo;\r
1292 \r
1293 ReleaseIpIo:\r
1294 \r
1295   if (Event != NULL) {\r
1296     gBS->CloseEvent (Event);\r
1297   }\r
1298 \r
1299   gBS->FreePool (IpIo);\r
1300 \r
1301   return NULL;\r
1302 }\r
1303 \r
1304 \r
1305 /**\r
1306   Open an IP_IO instance for use.\r
1307 \r
1308   If Ip version is not IP_VERSION_4 or IP_VERSION_6, then ASSERT().\r
1309 \r
1310   This function is called after IpIoCreate(). It is used for configuring the IP\r
1311   instance and register the callbacks and their context data for sending and\r
1312   receiving IP packets.\r
1313 \r
1314   @param[in, out]  IpIo               Pointer to an IP_IO instance that needs\r
1315                                       to open.\r
1316   @param[in]       OpenData           The configuration data and callbacks for\r
1317                                       the IP_IO instance.\r
1318 \r
1319   @retval          EFI_SUCCESS            The IP_IO instance opened with OpenData\r
1320                                           successfully.\r
1321   @retval          EFI_ACCESS_DENIED      The IP_IO instance is configured, avoid to\r
1322                                           reopen it.\r
1323   @retval          EFI_UNSUPPORTED        IPv4 RawData mode is no supported.\r
1324   @retval          EFI_INVALID_PARAMETER  Invalid input parameter.\r
1325   @retval          Others                 Error condition occurred.\r
1326 \r
1327 **/\r
1328 EFI_STATUS\r
1329 EFIAPI\r
1330 IpIoOpen (\r
1331   IN OUT IP_IO           *IpIo,\r
1332   IN     IP_IO_OPEN_DATA *OpenData\r
1333   )\r
1334 {\r
1335   EFI_STATUS        Status;\r
1336   UINT8             IpVersion;\r
1337 \r
1338   if (IpIo == NULL || OpenData == NULL) {\r
1339     return EFI_INVALID_PARAMETER;\r
1340   }\r
1341 \r
1342   if (IpIo->IsConfigured) {\r
1343     return EFI_ACCESS_DENIED;\r
1344   }\r
1345 \r
1346   IpVersion = IpIo->IpVersion;\r
1347 \r
1348   ASSERT ((IpVersion == IP_VERSION_4) || (IpVersion == IP_VERSION_6));\r
1349 \r
1350   //\r
1351   // configure ip\r
1352   //\r
1353   if (IpVersion == IP_VERSION_4){\r
1354     //\r
1355     // RawData mode is no supported.\r
1356     //\r
1357     ASSERT (!OpenData->IpConfigData.Ip4CfgData.RawData);\r
1358     if (OpenData->IpConfigData.Ip4CfgData.RawData) {\r
1359       return EFI_UNSUPPORTED;\r
1360     }\r
1361 \r
1362     if (!OpenData->IpConfigData.Ip4CfgData.UseDefaultAddress) {\r
1363       IpIo->StationIp = EFI_NTOHL (OpenData->IpConfigData.Ip4CfgData.StationAddress);\r
1364       IpIo->SubnetMask = EFI_NTOHL (OpenData->IpConfigData.Ip4CfgData.SubnetMask);\r
1365     }\r
1366 \r
1367     Status = IpIo->Ip.Ip4->Configure (\r
1368                              IpIo->Ip.Ip4,\r
1369                              &OpenData->IpConfigData.Ip4CfgData\r
1370                              );\r
1371   } else {\r
1372 \r
1373     Status = IpIo->Ip.Ip6->Configure (\r
1374                              IpIo->Ip.Ip6,\r
1375                              &OpenData->IpConfigData.Ip6CfgData\r
1376                              );\r
1377   }\r
1378 \r
1379   if (EFI_ERROR (Status)) {\r
1380     return Status;\r
1381   }\r
1382 \r
1383   //\r
1384   // @bug To delete the default route entry in this Ip, if it is:\r
1385   // @bug (0.0.0.0, 0.0.0.0, 0.0.0.0). Delete this statement if Ip modified\r
1386   // @bug its code\r
1387   //\r
1388   if (IpVersion == IP_VERSION_4){\r
1389     Status = IpIo->Ip.Ip4->Routes (\r
1390                              IpIo->Ip.Ip4,\r
1391                              TRUE,\r
1392                              &mZeroIp4Addr,\r
1393                              &mZeroIp4Addr,\r
1394                              &mZeroIp4Addr\r
1395                              );\r
1396 \r
1397     if (EFI_ERROR (Status) && (EFI_NOT_FOUND != Status)) {\r
1398       return Status;\r
1399     }\r
1400   }\r
1401 \r
1402   IpIo->PktRcvdNotify = OpenData->PktRcvdNotify;\r
1403   IpIo->PktSentNotify = OpenData->PktSentNotify;\r
1404 \r
1405   IpIo->RcvdContext   = OpenData->RcvdContext;\r
1406   IpIo->SndContext    = OpenData->SndContext;\r
1407 \r
1408   if (IpVersion == IP_VERSION_4){\r
1409     IpIo->Protocol = OpenData->IpConfigData.Ip4CfgData.DefaultProtocol;\r
1410 \r
1411     //\r
1412     // start to listen incoming packet\r
1413     //\r
1414     Status = IpIo->Ip.Ip4->Receive (\r
1415                              IpIo->Ip.Ip4,\r
1416                              &(IpIo->RcvToken.Ip4Token)\r
1417                              );\r
1418     if (EFI_ERROR (Status)) {\r
1419       IpIo->Ip.Ip4->Configure (IpIo->Ip.Ip4, NULL);\r
1420       return Status;\r
1421     }\r
1422 \r
1423   } else {\r
1424 \r
1425     IpIo->Protocol = OpenData->IpConfigData.Ip6CfgData.DefaultProtocol;\r
1426     Status = IpIo->Ip.Ip6->Receive (\r
1427                              IpIo->Ip.Ip6,\r
1428                              &(IpIo->RcvToken.Ip6Token)\r
1429                              );\r
1430     if (EFI_ERROR (Status)) {\r
1431       IpIo->Ip.Ip6->Configure (IpIo->Ip.Ip6, NULL);\r
1432       return Status;\r
1433     }\r
1434   }\r
1435 \r
1436   IpIo->IsConfigured = TRUE;\r
1437   InsertTailList (&mActiveIpIoList, &IpIo->Entry);\r
1438 \r
1439   return Status;\r
1440 }\r
1441 \r
1442 \r
1443 /**\r
1444   Stop an IP_IO instance.\r
1445 \r
1446   If Ip version is not IP_VERSION_4 or IP_VERSION_6, then ASSERT().\r
1447 \r
1448   This function is paired with IpIoOpen(). The IP_IO will be unconfigured and all\r
1449   the pending send/receive tokens will be canceled.\r
1450 \r
1451   @param[in, out]  IpIo            Pointer to the IP_IO instance that needs to stop.\r
1452 \r
1453   @retval          EFI_SUCCESS            The IP_IO instance stopped successfully.\r
1454   @retval          EFI_INVALID_PARAMETER  Invalid input parameter.\r
1455   @retval          Others                 Error condition occurred.\r
1456 \r
1457 **/\r
1458 EFI_STATUS\r
1459 EFIAPI\r
1460 IpIoStop (\r
1461   IN OUT IP_IO *IpIo\r
1462   )\r
1463 {\r
1464   EFI_STATUS        Status;\r
1465   IP_IO_IP_INFO     *IpInfo;\r
1466   UINT8             IpVersion;\r
1467 \r
1468   if (IpIo == NULL) {\r
1469     return EFI_INVALID_PARAMETER;\r
1470   }\r
1471 \r
1472   if (!IpIo->IsConfigured) {\r
1473     return EFI_SUCCESS;\r
1474   }\r
1475 \r
1476   IpVersion = IpIo->IpVersion;\r
1477 \r
1478   ASSERT ((IpVersion == IP_VERSION_4) || (IpVersion == IP_VERSION_6));\r
1479 \r
1480   //\r
1481   // Remove the IpIo from the active IpIo list.\r
1482   //\r
1483   RemoveEntryList (&IpIo->Entry);\r
1484 \r
1485   //\r
1486   // Configure NULL Ip\r
1487   //\r
1488   if (IpVersion == IP_VERSION_4) {\r
1489     Status = IpIo->Ip.Ip4->Configure (IpIo->Ip.Ip4, NULL);\r
1490   } else {\r
1491     Status = IpIo->Ip.Ip6->Configure (IpIo->Ip.Ip6, NULL);\r
1492   }\r
1493   if (EFI_ERROR (Status)) {\r
1494     return Status;\r
1495   }\r
1496 \r
1497   IpIo->IsConfigured = FALSE;\r
1498 \r
1499   //\r
1500   // Detroy the Ip List used by IpIo\r
1501   //\r
1502 \r
1503   while (!IsListEmpty (&(IpIo->IpList))) {\r
1504     IpInfo = NET_LIST_HEAD (&(IpIo->IpList), IP_IO_IP_INFO, Entry);\r
1505 \r
1506     IpIoRemoveIp (IpIo, IpInfo);\r
1507   }\r
1508 \r
1509   //\r
1510   // All pending send tokens should be flushed by resetting the IP instances.\r
1511   //\r
1512   ASSERT (IsListEmpty (&IpIo->PendingSndList));\r
1513 \r
1514   //\r
1515   // Close the receive event.\r
1516   //\r
1517   if (IpVersion == IP_VERSION_4){\r
1518     gBS->CloseEvent (IpIo->RcvToken.Ip4Token.Event);\r
1519   } else {\r
1520     gBS->CloseEvent (IpIo->RcvToken.Ip6Token.Event);\r
1521   }\r
1522 \r
1523   return EFI_SUCCESS;\r
1524 }\r
1525 \r
1526 \r
1527 /**\r
1528   Destroy an IP_IO instance.\r
1529 \r
1530   This function is paired with IpIoCreate(). The IP_IO will be closed first.\r
1531   Resource will be freed afterwards. See IpIoCloseProtocolDestroyIpChild().\r
1532 \r
1533   @param[in, out]  IpIo         Pointer to the IP_IO instance that needs to be\r
1534                                 destroyed.\r
1535 \r
1536   @retval          EFI_SUCCESS  The IP_IO instance destroyed successfully.\r
1537   @retval          Others       Error condition occurred.\r
1538 \r
1539 **/\r
1540 EFI_STATUS\r
1541 EFIAPI\r
1542 IpIoDestroy (\r
1543   IN OUT IP_IO *IpIo\r
1544   )\r
1545 {\r
1546   EFI_STATUS    Status;\r
1547 \r
1548   //\r
1549   // Stop the IpIo.\r
1550   //\r
1551   Status = IpIoStop (IpIo);\r
1552   if (EFI_ERROR (Status)) {\r
1553     return Status;\r
1554   }\r
1555 \r
1556   //\r
1557   // Close the IP protocol and destroy the child.\r
1558   //\r
1559   Status = IpIoCloseProtocolDestroyIpChild (\r
1560              IpIo->Controller,\r
1561              IpIo->Image,\r
1562              IpIo->ChildHandle,\r
1563              IpIo->IpVersion\r
1564              );\r
1565   if (EFI_ERROR (Status)) {\r
1566     return Status;\r
1567   }\r
1568 \r
1569   gBS->FreePool (IpIo);\r
1570 \r
1571   return EFI_SUCCESS;\r
1572 }\r
1573 \r
1574 \r
1575 /**\r
1576   Send out an IP packet.\r
1577 \r
1578   This function is called after IpIoOpen(). The data to be sent is wrapped in\r
1579   Pkt. The IP instance wrapped in IpIo is used for sending by default but can be\r
1580   overriden by Sender. Other sending configs, like source address and gateway\r
1581   address etc., are specified in OverrideData.\r
1582 \r
1583   @param[in, out]  IpIo                  Pointer to an IP_IO instance used for sending IP\r
1584                                          packet.\r
1585   @param[in, out]  Pkt                   Pointer to the IP packet to be sent.\r
1586   @param[in]       Sender                The IP protocol instance used for sending.\r
1587   @param[in]       Context               Optional context data.\r
1588   @param[in]       NotifyData            Optional notify data.\r
1589   @param[in]       Dest                  The destination IP address to send this packet to.\r
1590                                          This parameter is optional when using IPv6.\r
1591   @param[in]       OverrideData          The data to override some configuration of the IP\r
1592                                          instance used for sending.\r
1593 \r
1594   @retval          EFI_SUCCESS           The operation is completed successfully.\r
1595   @retval          EFI_INVALID_PARAMETER The input parameter is not correct.\r
1596   @retval          EFI_NOT_STARTED       The IpIo is not configured.\r
1597   @retval          EFI_OUT_OF_RESOURCES  Failed due to resource limit.\r
1598   @retval          Others                Error condition occurred.\r
1599 \r
1600 **/\r
1601 EFI_STATUS\r
1602 EFIAPI\r
1603 IpIoSend (\r
1604   IN OUT IP_IO          *IpIo,\r
1605   IN OUT NET_BUF        *Pkt,\r
1606   IN     IP_IO_IP_INFO  *Sender        OPTIONAL,\r
1607   IN     VOID           *Context       OPTIONAL,\r
1608   IN     VOID           *NotifyData    OPTIONAL,\r
1609   IN     EFI_IP_ADDRESS *Dest          OPTIONAL,\r
1610   IN     IP_IO_OVERRIDE *OverrideData  OPTIONAL\r
1611   )\r
1612 {\r
1613   EFI_STATUS        Status;\r
1614   IP_IO_IP_PROTOCOL Ip;\r
1615   IP_IO_SEND_ENTRY  *SndEntry;\r
1616 \r
1617   if ((IpIo == NULL) || (Pkt == NULL)) {\r
1618     return EFI_INVALID_PARAMETER;\r
1619   }\r
1620 \r
1621   if ((IpIo->IpVersion == IP_VERSION_4) && (Dest == NULL)) {\r
1622     return EFI_INVALID_PARAMETER;\r
1623   }\r
1624 \r
1625   if (!IpIo->IsConfigured) {\r
1626     return EFI_NOT_STARTED;\r
1627   }\r
1628 \r
1629   Ip = (NULL == Sender) ? IpIo->Ip : Sender->Ip;\r
1630 \r
1631   //\r
1632   // create a new SndEntry\r
1633   //\r
1634   SndEntry = IpIoCreateSndEntry (IpIo, Pkt, Ip, Context, NotifyData, Dest, OverrideData);\r
1635   if (NULL == SndEntry) {\r
1636     return EFI_OUT_OF_RESOURCES;\r
1637   }\r
1638 \r
1639   //\r
1640   // Send this Packet\r
1641   //\r
1642   if (IpIo->IpVersion == IP_VERSION_4){\r
1643     Status = Ip.Ip4->Transmit (\r
1644                        Ip.Ip4,\r
1645                        &SndEntry->SndToken.Ip4Token\r
1646                        );\r
1647   } else {\r
1648     Status = Ip.Ip6->Transmit (\r
1649                        Ip.Ip6,\r
1650                        &SndEntry->SndToken.Ip6Token\r
1651                        );\r
1652   }\r
1653 \r
1654   if (EFI_ERROR (Status)) {\r
1655     IpIoDestroySndEntry (SndEntry);\r
1656   }\r
1657 \r
1658   return Status;\r
1659 }\r
1660 \r
1661 \r
1662 /**\r
1663   Cancel the IP transmit token which wraps this Packet.\r
1664 \r
1665   If IpIo is NULL, then ASSERT().\r
1666   If Packet is NULL, then ASSERT().\r
1667 \r
1668   @param[in]  IpIo                  Pointer to the IP_IO instance.\r
1669   @param[in]  Packet                Pointer to the packet of NET_BUF to cancel.\r
1670 \r
1671 **/\r
1672 VOID\r
1673 EFIAPI\r
1674 IpIoCancelTxToken (\r
1675   IN IP_IO  *IpIo,\r
1676   IN VOID   *Packet\r
1677   )\r
1678 {\r
1679   LIST_ENTRY        *Node;\r
1680   IP_IO_SEND_ENTRY  *SndEntry;\r
1681   IP_IO_IP_PROTOCOL Ip;\r
1682 \r
1683   ASSERT ((IpIo != NULL) && (Packet != NULL));\r
1684 \r
1685   NET_LIST_FOR_EACH (Node, &IpIo->PendingSndList) {\r
1686 \r
1687     SndEntry = NET_LIST_USER_STRUCT (Node, IP_IO_SEND_ENTRY, Entry);\r
1688 \r
1689     if (SndEntry->Pkt == Packet) {\r
1690 \r
1691       Ip = SndEntry->Ip;\r
1692 \r
1693       if (IpIo->IpVersion == IP_VERSION_4) {\r
1694         Ip.Ip4->Cancel (\r
1695                   Ip.Ip4,\r
1696                   &SndEntry->SndToken.Ip4Token\r
1697                   );\r
1698       } else {\r
1699         Ip.Ip6->Cancel (\r
1700                   Ip.Ip6,\r
1701                   &SndEntry->SndToken.Ip6Token\r
1702                   );\r
1703       }\r
1704 \r
1705       break;\r
1706     }\r
1707   }\r
1708 \r
1709 }\r
1710 \r
1711 \r
1712 /**\r
1713   Add a new IP instance for sending data.\r
1714 \r
1715   If IpIo is NULL, then ASSERT().\r
1716   If Ip version is not IP_VERSION_4 or IP_VERSION_6, then ASSERT().\r
1717 \r
1718   The function is used to add the IP_IO to the IP_IO sending list. The caller\r
1719   can later use IpIoFindSender() to get the IP_IO and call IpIoSend() to send\r
1720   data.\r
1721 \r
1722   @param[in, out]  IpIo               Pointer to a IP_IO instance to add a new IP\r
1723                                       instance for sending purpose.\r
1724 \r
1725   @return Pointer to the created IP_IO_IP_INFO structure, NULL if failed.\r
1726 \r
1727 **/\r
1728 IP_IO_IP_INFO *\r
1729 EFIAPI\r
1730 IpIoAddIp (\r
1731   IN OUT IP_IO  *IpIo\r
1732   )\r
1733 {\r
1734   EFI_STATUS     Status;\r
1735   IP_IO_IP_INFO  *IpInfo;\r
1736   EFI_EVENT      Event;\r
1737 \r
1738   ASSERT (IpIo != NULL);\r
1739   ASSERT ((IpIo->IpVersion == IP_VERSION_4) || (IpIo->IpVersion == IP_VERSION_6));\r
1740 \r
1741   IpInfo = AllocatePool (sizeof (IP_IO_IP_INFO));\r
1742   if (IpInfo == NULL) {\r
1743     return NULL;\r
1744   }\r
1745 \r
1746   //\r
1747   // Init this IpInfo, set the Addr and SubnetMask to 0 before we configure the IP\r
1748   // instance.\r
1749   //\r
1750   InitializeListHead (&IpInfo->Entry);\r
1751   IpInfo->ChildHandle = NULL;\r
1752   ZeroMem (&IpInfo->Addr, sizeof (IpInfo->Addr));\r
1753   ZeroMem (&IpInfo->PreMask, sizeof (IpInfo->PreMask));\r
1754 \r
1755   IpInfo->RefCnt    = 1;\r
1756   IpInfo->IpVersion = IpIo->IpVersion;\r
1757 \r
1758   //\r
1759   // Create the IP instance and open the IP protocol.\r
1760   //\r
1761   Status = IpIoCreateIpChildOpenProtocol (\r
1762              IpIo->Controller,\r
1763              IpIo->Image,\r
1764              &IpInfo->ChildHandle,\r
1765              IpInfo->IpVersion,\r
1766              (VOID **) &IpInfo->Ip\r
1767              );\r
1768   if (EFI_ERROR (Status)) {\r
1769     goto ReleaseIpInfo;\r
1770   }\r
1771 \r
1772   //\r
1773   // Create the event for the DummyRcvToken.\r
1774   //\r
1775   Status = gBS->CreateEvent (\r
1776                   EVT_NOTIFY_SIGNAL,\r
1777                   TPL_NOTIFY,\r
1778                   IpIoDummyHandler,\r
1779                   IpInfo,\r
1780                   &Event\r
1781                   );\r
1782   if (EFI_ERROR (Status)) {\r
1783     goto ReleaseIpChild;\r
1784   }\r
1785 \r
1786   if (IpInfo->IpVersion == IP_VERSION_4) {\r
1787     IpInfo->DummyRcvToken.Ip4Token.Event = Event;\r
1788   } else {\r
1789     IpInfo->DummyRcvToken.Ip6Token.Event = Event;\r
1790   }\r
1791 \r
1792   //\r
1793   // Link this IpInfo into the IpIo.\r
1794   //\r
1795   InsertTailList (&IpIo->IpList, &IpInfo->Entry);\r
1796 \r
1797   return IpInfo;\r
1798 \r
1799 ReleaseIpChild:\r
1800 \r
1801   IpIoCloseProtocolDestroyIpChild (\r
1802     IpIo->Controller,\r
1803     IpIo->Image,\r
1804     IpInfo->ChildHandle,\r
1805     IpInfo->IpVersion\r
1806     );\r
1807 \r
1808 ReleaseIpInfo:\r
1809 \r
1810   gBS->FreePool (IpInfo);\r
1811 \r
1812   return NULL;\r
1813 }\r
1814 \r
1815 \r
1816 /**\r
1817   Configure the IP instance of this IpInfo and start the receiving if IpConfigData\r
1818   is not NULL.\r
1819 \r
1820   If IpInfo is NULL, then ASSERT().\r
1821   If Ip version is not IP_VERSION_4 or IP_VERSION_6, then ASSERT().\r
1822 \r
1823   @param[in, out]  IpInfo          Pointer to the IP_IO_IP_INFO instance.\r
1824   @param[in, out]  IpConfigData    The IP configure data used to configure the IP\r
1825                                    instance, if NULL the IP instance is reset. If\r
1826                                    UseDefaultAddress is set to TRUE, and the configure\r
1827                                    operation succeeds, the default address information\r
1828                                    is written back in this IpConfigData.\r
1829 \r
1830   @retval          EFI_SUCCESS     The IP instance of this IpInfo is configured successfully\r
1831                                    or no need to reconfigure it.\r
1832   @retval          Others          Configuration fails.\r
1833 \r
1834 **/\r
1835 EFI_STATUS\r
1836 EFIAPI\r
1837 IpIoConfigIp (\r
1838   IN OUT IP_IO_IP_INFO        *IpInfo,\r
1839   IN OUT VOID                 *IpConfigData OPTIONAL\r
1840   )\r
1841 {\r
1842   EFI_STATUS         Status;\r
1843   IP_IO_IP_PROTOCOL  Ip;\r
1844   UINT8              IpVersion;\r
1845   EFI_IP4_MODE_DATA  Ip4ModeData;\r
1846   EFI_IP6_MODE_DATA  Ip6ModeData;\r
1847 \r
1848   ASSERT (IpInfo != NULL);\r
1849 \r
1850   if (IpInfo->RefCnt > 1) {\r
1851     //\r
1852     // This IP instance is shared, don't reconfigure it until it has only one\r
1853     // consumer. Currently, only the tcp children cloned from their passive parent\r
1854     // will share the same IP. So this cases only happens while IpConfigData is NULL,\r
1855     // let the last consumer clean the IP instance.\r
1856     //\r
1857     return EFI_SUCCESS;\r
1858   }\r
1859 \r
1860   IpVersion = IpInfo->IpVersion;\r
1861   ASSERT ((IpVersion == IP_VERSION_4) || (IpVersion == IP_VERSION_6));\r
1862 \r
1863   Ip = IpInfo->Ip;\r
1864 \r
1865   if (IpInfo->IpVersion == IP_VERSION_4) {\r
1866     Status = Ip.Ip4->Configure (Ip.Ip4, IpConfigData);\r
1867   } else {\r
1868     Status = Ip.Ip6->Configure (Ip.Ip6, IpConfigData);\r
1869   }\r
1870 \r
1871   if (EFI_ERROR (Status)) {\r
1872     return Status;\r
1873   }\r
1874 \r
1875   if (IpConfigData != NULL) {\r
1876     if (IpInfo->IpVersion == IP_VERSION_4) {\r
1877 \r
1878       if (((EFI_IP4_CONFIG_DATA *) IpConfigData)->UseDefaultAddress) {\r
1879         Status = Ip.Ip4->GetModeData (\r
1880                            Ip.Ip4,\r
1881                            &Ip4ModeData,\r
1882                            NULL,\r
1883                            NULL\r
1884                            );\r
1885         if (EFI_ERROR (Status)) {\r
1886           Ip.Ip4->Configure (Ip.Ip4, NULL);\r
1887           return Status;\r
1888         }\r
1889 \r
1890         IP4_COPY_ADDRESS (&((EFI_IP4_CONFIG_DATA*) IpConfigData)->StationAddress, &Ip4ModeData.ConfigData.StationAddress);\r
1891         IP4_COPY_ADDRESS (&((EFI_IP4_CONFIG_DATA*) IpConfigData)->SubnetMask, &Ip4ModeData.ConfigData.SubnetMask);\r
1892       }\r
1893 \r
1894       CopyMem (\r
1895         &IpInfo->Addr.Addr,\r
1896         &((EFI_IP4_CONFIG_DATA *) IpConfigData)->StationAddress,\r
1897         sizeof (IP4_ADDR)\r
1898         );\r
1899       CopyMem (\r
1900         &IpInfo->PreMask.SubnetMask,\r
1901         &((EFI_IP4_CONFIG_DATA *) IpConfigData)->SubnetMask,\r
1902         sizeof (IP4_ADDR)\r
1903         );\r
1904 \r
1905       Status = Ip.Ip4->Receive (\r
1906                          Ip.Ip4,\r
1907                          &IpInfo->DummyRcvToken.Ip4Token\r
1908                          );\r
1909       if (EFI_ERROR (Status)) {\r
1910         Ip.Ip4->Configure (Ip.Ip4, NULL);\r
1911       }\r
1912     } else {\r
1913       Status = Ip.Ip6->GetModeData (\r
1914                          Ip.Ip6,\r
1915                          &Ip6ModeData,\r
1916                          NULL,\r
1917                          NULL\r
1918                          );\r
1919       if (EFI_ERROR (Status)) {\r
1920         Ip.Ip6->Configure (Ip.Ip6, NULL);\r
1921         return Status;\r
1922       }\r
1923 \r
1924       if (Ip6ModeData.IsConfigured) {\r
1925         CopyMem (\r
1926           &((EFI_IP6_CONFIG_DATA *) IpConfigData)->StationAddress,\r
1927           &Ip6ModeData.ConfigData.StationAddress,\r
1928           sizeof (EFI_IPv6_ADDRESS)\r
1929           );\r
1930 \r
1931         if (Ip6ModeData.AddressList != NULL) {\r
1932           FreePool (Ip6ModeData.AddressList);\r
1933         }\r
1934 \r
1935         if (Ip6ModeData.GroupTable != NULL) {\r
1936           FreePool (Ip6ModeData.GroupTable);\r
1937         }\r
1938 \r
1939         if (Ip6ModeData.RouteTable != NULL) {\r
1940           FreePool (Ip6ModeData.RouteTable);\r
1941         }\r
1942 \r
1943         if (Ip6ModeData.NeighborCache != NULL) {\r
1944           FreePool (Ip6ModeData.NeighborCache);\r
1945         }\r
1946 \r
1947         if (Ip6ModeData.PrefixTable != NULL) {\r
1948           FreePool (Ip6ModeData.PrefixTable);\r
1949         }\r
1950 \r
1951         if (Ip6ModeData.IcmpTypeList != NULL) {\r
1952           FreePool (Ip6ModeData.IcmpTypeList);\r
1953         }\r
1954 \r
1955       } else {\r
1956         Status = EFI_NO_MAPPING;\r
1957         return Status;\r
1958       }\r
1959 \r
1960       CopyMem (\r
1961         &IpInfo->Addr,\r
1962         &Ip6ModeData.ConfigData.StationAddress,\r
1963         sizeof (EFI_IPv6_ADDRESS)\r
1964         );\r
1965 \r
1966       Status = Ip.Ip6->Receive (\r
1967                          Ip.Ip6,\r
1968                          &IpInfo->DummyRcvToken.Ip6Token\r
1969                          );\r
1970       if (EFI_ERROR (Status)) {\r
1971         Ip.Ip6->Configure (Ip.Ip6, NULL);\r
1972       }\r
1973     }\r
1974   } else {\r
1975     //\r
1976     // The IP instance is reset, set the stored Addr and SubnetMask to zero.\r
1977     //\r
1978     ZeroMem (&IpInfo->Addr, sizeof (IpInfo->Addr));\r
1979     ZeroMem (&IpInfo->PreMask, sizeof (IpInfo->PreMask));\r
1980   }\r
1981 \r
1982   return Status;\r
1983 }\r
1984 \r
1985 \r
1986 /**\r
1987   Destroy an IP instance maintained in IpIo->IpList for\r
1988   sending purpose.\r
1989 \r
1990   If Ip version is not IP_VERSION_4 or IP_VERSION_6, then ASSERT().\r
1991 \r
1992   This function pairs with IpIoAddIp(). The IpInfo is previously created by\r
1993   IpIoAddIp(). The IP_IO_IP_INFO::RefCnt is decremented and the IP instance\r
1994   will be dstroyed if the RefCnt is zero.\r
1995 \r
1996   @param[in]  IpIo                  Pointer to the IP_IO instance.\r
1997   @param[in]  IpInfo                Pointer to the IpInfo to be removed.\r
1998 \r
1999 **/\r
2000 VOID\r
2001 EFIAPI\r
2002 IpIoRemoveIp (\r
2003   IN IP_IO            *IpIo,\r
2004   IN IP_IO_IP_INFO    *IpInfo\r
2005   )\r
2006 {\r
2007 \r
2008   UINT8               IpVersion;\r
2009 \r
2010   if (IpIo == NULL || IpInfo == NULL) {\r
2011     return;\r
2012   }\r
2013 \r
2014   ASSERT (IpInfo->RefCnt > 0);\r
2015 \r
2016   NET_PUT_REF (IpInfo);\r
2017 \r
2018   if (IpInfo->RefCnt > 0) {\r
2019 \r
2020     return;\r
2021   }\r
2022 \r
2023   IpVersion = IpIo->IpVersion;\r
2024 \r
2025   ASSERT ((IpVersion == IP_VERSION_4) || (IpVersion == IP_VERSION_6));\r
2026 \r
2027   RemoveEntryList (&IpInfo->Entry);\r
2028 \r
2029   if (IpVersion == IP_VERSION_4){\r
2030     IpInfo->Ip.Ip4->Configure (\r
2031                       IpInfo->Ip.Ip4,\r
2032                       NULL\r
2033                       );\r
2034     IpIoCloseProtocolDestroyIpChild (\r
2035       IpIo->Controller,\r
2036       IpIo->Image,\r
2037       IpInfo->ChildHandle,\r
2038       IP_VERSION_4\r
2039       );\r
2040 \r
2041     gBS->CloseEvent (IpInfo->DummyRcvToken.Ip4Token.Event);\r
2042 \r
2043   } else {\r
2044 \r
2045     IpInfo->Ip.Ip6->Configure (\r
2046                       IpInfo->Ip.Ip6,\r
2047                       NULL\r
2048                       );\r
2049 \r
2050     IpIoCloseProtocolDestroyIpChild (\r
2051       IpIo->Controller,\r
2052       IpIo->Image,\r
2053       IpInfo->ChildHandle,\r
2054       IP_VERSION_6\r
2055       );\r
2056 \r
2057     gBS->CloseEvent (IpInfo->DummyRcvToken.Ip6Token.Event);\r
2058   }\r
2059 \r
2060   FreePool (IpInfo);\r
2061 }\r
2062 \r
2063 \r
2064 /**\r
2065   Find the first IP protocol maintained in IpIo whose local\r
2066   address is the same as Src.\r
2067 \r
2068   This function is called when the caller needs the IpIo to send data to the\r
2069   specified Src. The IpIo was added previously by IpIoAddIp().\r
2070 \r
2071   @param[in, out]  IpIo              Pointer to the pointer of the IP_IO instance.\r
2072   @param[in]       IpVersion         The version of the IP protocol to use, either\r
2073                                      IPv4 or IPv6.\r
2074   @param[in]       Src               The local IP address.\r
2075 \r
2076   @return Pointer to the IP protocol can be used for sending purpose and its local\r
2077           address is the same with Src. NULL if failed.\r
2078 \r
2079 **/\r
2080 IP_IO_IP_INFO *\r
2081 EFIAPI\r
2082 IpIoFindSender (\r
2083   IN OUT IP_IO           **IpIo,\r
2084   IN     UINT8           IpVersion,\r
2085   IN     EFI_IP_ADDRESS  *Src\r
2086   )\r
2087 {\r
2088   LIST_ENTRY      *IpIoEntry;\r
2089   IP_IO           *IpIoPtr;\r
2090   LIST_ENTRY      *IpInfoEntry;\r
2091   IP_IO_IP_INFO   *IpInfo;\r
2092 \r
2093   if (IpIo == NULL || Src == NULL) {\r
2094     return NULL;\r
2095   }\r
2096 \r
2097   if ((IpVersion != IP_VERSION_4) && (IpVersion != IP_VERSION_6)) {\r
2098     return NULL;\r
2099   }\r
2100 \r
2101   NET_LIST_FOR_EACH (IpIoEntry, &mActiveIpIoList) {\r
2102     IpIoPtr = NET_LIST_USER_STRUCT (IpIoEntry, IP_IO, Entry);\r
2103 \r
2104     if (((*IpIo != NULL) && (*IpIo != IpIoPtr)) || (IpIoPtr->IpVersion != IpVersion)) {\r
2105       continue;\r
2106     }\r
2107 \r
2108     NET_LIST_FOR_EACH (IpInfoEntry, &IpIoPtr->IpList) {\r
2109       IpInfo = NET_LIST_USER_STRUCT (IpInfoEntry, IP_IO_IP_INFO, Entry);\r
2110       if (IpInfo->IpVersion == IP_VERSION_4){\r
2111 \r
2112         if (EFI_IP4_EQUAL (&IpInfo->Addr.v4, &Src->v4)) {\r
2113           *IpIo = IpIoPtr;\r
2114           return IpInfo;\r
2115         }\r
2116 \r
2117       } else {\r
2118 \r
2119         if (EFI_IP6_EQUAL (&IpInfo->Addr.v6, &Src->v6)) {\r
2120           *IpIo = IpIoPtr;\r
2121           return IpInfo;\r
2122         }\r
2123       }\r
2124     }\r
2125   }\r
2126 \r
2127   //\r
2128   // No match.\r
2129   //\r
2130   return NULL;\r
2131 }\r
2132 \r
2133 \r
2134 /**\r
2135   Get the ICMP error map information.\r
2136 \r
2137   The ErrorStatus will be returned. The IsHard and Notify are optional. If they\r
2138   are not NULL, this routine will fill them.\r
2139 \r
2140   @param[in]   IcmpError             IcmpError Type.\r
2141   @param[in]   IpVersion             The version of the IP protocol to use,\r
2142                                      either IPv4 or IPv6.\r
2143   @param[out]  IsHard                If TRUE, indicates that it is a hard error.\r
2144   @param[out]  Notify                If TRUE, SockError needs to be notified.\r
2145 \r
2146   @retval EFI_UNSUPPORTED            Unrecognizable ICMP error code.\r
2147   @return ICMP Error Status, such as EFI_NETWORK_UNREACHABLE.\r
2148 \r
2149 **/\r
2150 EFI_STATUS\r
2151 EFIAPI\r
2152 IpIoGetIcmpErrStatus (\r
2153   IN  UINT8       IcmpError,\r
2154   IN  UINT8       IpVersion,\r
2155   OUT BOOLEAN     *IsHard  OPTIONAL,\r
2156   OUT BOOLEAN     *Notify  OPTIONAL\r
2157   )\r
2158 {\r
2159   if (IpVersion == IP_VERSION_4 ) {\r
2160     ASSERT (IcmpError <= ICMP_ERR_PARAMPROB);\r
2161 \r
2162     if (IsHard != NULL) {\r
2163       *IsHard = mIcmpErrMap[IcmpError].IsHard;\r
2164     }\r
2165 \r
2166     if (Notify != NULL) {\r
2167       *Notify = mIcmpErrMap[IcmpError].Notify;\r
2168     }\r
2169 \r
2170     switch (IcmpError) {\r
2171     case ICMP_ERR_UNREACH_NET:\r
2172       return  EFI_NETWORK_UNREACHABLE;\r
2173 \r
2174     case ICMP_ERR_TIMXCEED_INTRANS:\r
2175     case ICMP_ERR_TIMXCEED_REASS:\r
2176     case ICMP_ERR_UNREACH_HOST:\r
2177       return  EFI_HOST_UNREACHABLE;\r
2178 \r
2179     case ICMP_ERR_UNREACH_PROTOCOL:\r
2180       return  EFI_PROTOCOL_UNREACHABLE;\r
2181 \r
2182     case ICMP_ERR_UNREACH_PORT:\r
2183       return  EFI_PORT_UNREACHABLE;\r
2184 \r
2185     case ICMP_ERR_MSGSIZE:\r
2186     case ICMP_ERR_UNREACH_SRCFAIL:\r
2187     case ICMP_ERR_QUENCH:\r
2188     case ICMP_ERR_PARAMPROB:\r
2189       return  EFI_ICMP_ERROR;\r
2190 \r
2191     default:\r
2192       ASSERT (FALSE);\r
2193       return EFI_UNSUPPORTED;\r
2194     }\r
2195 \r
2196   } else if (IpVersion == IP_VERSION_6) {\r
2197 \r
2198     ASSERT (IcmpError <= ICMP6_ERR_PARAMPROB_IPV6OPTION);\r
2199 \r
2200     if (IsHard != NULL) {\r
2201       *IsHard = mIcmp6ErrMap[IcmpError].IsHard;\r
2202     }\r
2203 \r
2204     if (Notify != NULL) {\r
2205       *Notify = mIcmp6ErrMap[IcmpError].Notify;\r
2206     }\r
2207 \r
2208     switch (IcmpError) {\r
2209     case ICMP6_ERR_UNREACH_NET:\r
2210       return EFI_NETWORK_UNREACHABLE;\r
2211 \r
2212     case ICMP6_ERR_UNREACH_HOST:\r
2213     case ICMP6_ERR_TIMXCEED_HOPLIMIT:\r
2214     case ICMP6_ERR_TIMXCEED_REASS:\r
2215       return EFI_HOST_UNREACHABLE;\r
2216 \r
2217     case ICMP6_ERR_UNREACH_PROTOCOL:\r
2218       return EFI_PROTOCOL_UNREACHABLE;\r
2219 \r
2220     case ICMP6_ERR_UNREACH_PORT:\r
2221       return EFI_PORT_UNREACHABLE;\r
2222 \r
2223     case ICMP6_ERR_PACKAGE_TOOBIG:\r
2224     case ICMP6_ERR_PARAMPROB_HEADER:\r
2225     case ICMP6_ERR_PARAMPROB_NEXHEADER:\r
2226     case ICMP6_ERR_PARAMPROB_IPV6OPTION:\r
2227       return EFI_ICMP_ERROR;\r
2228 \r
2229     default:\r
2230       ASSERT (FALSE);\r
2231       return EFI_UNSUPPORTED;\r
2232     }\r
2233 \r
2234   } else {\r
2235     //\r
2236     // Should never be here\r
2237     //\r
2238     ASSERT (FALSE);\r
2239     return EFI_UNSUPPORTED;\r
2240   }\r
2241 }\r
2242 \r
2243 \r
2244 /**\r
2245   Refresh the remote peer's Neighbor Cache entries.\r
2246 \r
2247   This function is called when the caller needs the IpIo to refresh the existing\r
2248   IPv6 neighbor cache entries since the neighbor is considered reachable by the\r
2249   node has recently received a confirmation that packets sent recently to the\r
2250   neighbor were received by its IP layer.\r
2251 \r
2252   @param[in]   IpIo                  Pointer to an IP_IO instance\r
2253   @param[in]   Neighbor              The IP address of the neighbor\r
2254   @param[in]   Timeout               Time in 100-ns units that this entry will\r
2255                                      remain in the neighbor cache. A value of\r
2256                                      zero means that the entry is permanent.\r
2257                                      A value of non-zero means that the entry is\r
2258                                      dynamic and will be deleted after Timeout.\r
2259 \r
2260   @retval      EFI_SUCCESS           The operation is completed successfully.\r
2261   @retval      EFI_NOT_STARTED       The IpIo is not configured.\r
2262   @retval      EFI_INVALID_PARAMETER Neighbor Address is invalid.\r
2263   @retval      EFI_NOT_FOUND         The neighbor cache entry is not in the\r
2264                                      neighbor table.\r
2265   @retval      EFI_UNSUPPORTED       IP version is IPv4, which doesn't support neighbor cache refresh.\r
2266   @retval      EFI_OUT_OF_RESOURCES  Failed due to resource limit.\r
2267 \r
2268 **/\r
2269 EFI_STATUS\r
2270 EFIAPI\r
2271 IpIoRefreshNeighbor (\r
2272   IN IP_IO           *IpIo,\r
2273   IN EFI_IP_ADDRESS  *Neighbor,\r
2274   IN UINT32          Timeout\r
2275   )\r
2276 {\r
2277   EFI_IP6_PROTOCOL  *Ip;\r
2278 \r
2279   if (!IpIo->IsConfigured) {\r
2280     return EFI_NOT_STARTED;\r
2281   }\r
2282 \r
2283   if (IpIo->IpVersion != IP_VERSION_6) {\r
2284     return EFI_UNSUPPORTED;\r
2285   }\r
2286 \r
2287   Ip = IpIo->Ip.Ip6;\r
2288 \r
2289   return Ip->Neighbors (Ip, FALSE, &Neighbor->v6, NULL, Timeout, TRUE);\r
2290 }\r
2291 \r