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