Home | History | Annotate | Download | only in UefiShellNetwork1CommandsLib
      1 /** @file
      2   The implementation for Ping shell command.
      3 
      4   (C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR>
      5   Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR>
      6   (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
      7 
      8   This program and the accompanying materials
      9   are licensed and made available under the terms and conditions of the BSD License
     10   which accompanies this distribution.  The full text of the license may be found at
     11   http://opensource.org/licenses/bsd-license.php.
     12 
     13   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     14   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     15 
     16 **/
     17 
     18 #include "UefiShellNetwork1CommandsLib.h"
     19 
     20 #define PING_IP4_COPY_ADDRESS(Dest, Src) (CopyMem ((Dest), (Src), sizeof (EFI_IPv4_ADDRESS)))
     21 
     22 UINT64          mCurrentTick = 0;
     23 
     24 //
     25 // Function templates to match the IPv4 and IPv6 commands that we use.
     26 //
     27 typedef
     28 EFI_STATUS
     29 (EFIAPI *PING_IPX_POLL)(
     30   IN VOID          *This
     31   );
     32 
     33 typedef
     34 EFI_STATUS
     35 (EFIAPI *PING_IPX_TRANSMIT)(
     36   IN VOID          *This,
     37   IN VOID          *Token
     38   );
     39 
     40 typedef
     41 EFI_STATUS
     42 (EFIAPI *PING_IPX_RECEIVE)(
     43   IN VOID          *This,
     44   IN VOID          *Token
     45   );
     46 
     47 typedef
     48 EFI_STATUS
     49 (EFIAPI *PING_IPX_CANCEL)(
     50   IN VOID          *This,
     51   IN VOID          *Token OPTIONAL
     52   );
     53 
     54 ///
     55 /// A set of pointers to either IPv6 or IPv4 functions.
     56 /// Unknown which one to the ping command.
     57 ///
     58 typedef struct {
     59   PING_IPX_TRANSMIT             Transmit;
     60   PING_IPX_RECEIVE              Receive;
     61   PING_IPX_CANCEL               Cancel;
     62   PING_IPX_POLL                 Poll;
     63 }PING_IPX_PROTOCOL;
     64 
     65 
     66 typedef union {
     67   VOID                  *RxData;
     68   VOID                  *TxData;
     69 } PING_PACKET;
     70 
     71 //
     72 // PING_IPX_COMPLETION_TOKEN
     73 // structures are used for both transmit and receive operations.
     74 // This version is IP-unaware.
     75 //
     76 typedef struct {
     77   EFI_EVENT               Event;
     78   EFI_STATUS              Status;
     79   PING_PACKET             Packet;
     80 } PING_IPX_COMPLETION_TOKEN;
     81 
     82 #pragma pack(1)
     83 typedef struct _ICMPX_ECHO_REQUEST_REPLY {
     84   UINT8                       Type;
     85   UINT8                       Code;
     86   UINT16                      Checksum;
     87   UINT16                      Identifier;
     88   UINT16                      SequenceNum;
     89   UINT32                      TimeStamp;
     90   UINT8                       Data[1];
     91 } ICMPX_ECHO_REQUEST_REPLY;
     92 #pragma pack()
     93 
     94 typedef struct _PING_ICMP_TX_INFO {
     95   LIST_ENTRY                Link;
     96   UINT16                    SequenceNum;
     97   UINT32                    TimeStamp;
     98   PING_IPX_COMPLETION_TOKEN *Token;
     99 } PING_ICMPX_TX_INFO;
    100 
    101 #define DEFAULT_TIMEOUT       5000
    102 #define MAX_SEND_NUMBER       10000
    103 #define MAX_BUFFER_SIZE       32768
    104 #define DEFAULT_TIMER_PERIOD  358049
    105 #define ONE_SECOND            10000000
    106 #define PING_IP_CHOICE_IP4    1
    107 #define PING_IP_CHOICE_IP6    2
    108 #define DEFAULT_SEND_COUNT    10
    109 #define DEFAULT_BUFFER_SIZE   16
    110 #define ICMP_V4_ECHO_REQUEST  0x8
    111 #define ICMP_V4_ECHO_REPLY    0x0
    112 #define STALL_1_MILLI_SECOND  1000
    113 
    114 #define PING_PRIVATE_DATA_SIGNATURE  SIGNATURE_32 ('P', 'i', 'n', 'g')
    115 typedef struct _PING_PRIVATE_DATA {
    116   UINT32                      Signature;
    117   EFI_HANDLE                  NicHandle;
    118   EFI_HANDLE                  IpChildHandle;
    119   EFI_EVENT                   Timer;
    120 
    121   UINT32                      TimerPeriod;
    122   UINT32                      RttTimerTick;
    123   EFI_EVENT                   RttTimer;
    124 
    125   EFI_STATUS                  Status;
    126   LIST_ENTRY                  TxList;
    127   UINT16                      RxCount;
    128   UINT16                      TxCount;
    129   UINT64                      RttSum;
    130   UINT64                      RttMin;
    131   UINT64                      RttMax;
    132   UINT32                      SequenceNum;
    133 
    134   UINT32                      SendNum;
    135   UINT32                      BufferSize;
    136   UINT32                      IpChoice;
    137 
    138   PING_IPX_PROTOCOL           ProtocolPointers;
    139   VOID                        *IpProtocol;
    140   UINT8                       SrcAddress[MAX(sizeof(EFI_IPv6_ADDRESS)        , sizeof(EFI_IPv4_ADDRESS)          )];
    141   UINT8                       DstAddress[MAX(sizeof(EFI_IPv6_ADDRESS)        , sizeof(EFI_IPv4_ADDRESS)          )];
    142   PING_IPX_COMPLETION_TOKEN   RxToken;
    143   UINT16                      FailedCount;
    144 } PING_PRIVATE_DATA;
    145 
    146 /**
    147   Calculate the internet checksum (see RFC 1071).
    148 
    149   @param[in] Packet  Buffer which contains the data to be checksummed.
    150   @param[in] Length  Length to be checksummed.
    151 
    152   @retval Checksum     Returns the 16 bit ones complement of
    153                        ones complement sum of 16 bit words
    154 **/
    155 UINT16
    156 NetChecksum (
    157   IN UINT8   *Buffer,
    158   IN UINT32  Length
    159   )
    160 {
    161   UINT32  Sum;
    162   UINT8   Odd;
    163   UINT16  *Packet;
    164 
    165   Packet  = (UINT16 *) Buffer;
    166 
    167   Sum     = 0;
    168   Odd     = (UINT8) (Length & 1);
    169   Length >>= 1;
    170   while ((Length--) != 0) {
    171     Sum += *Packet++;
    172   }
    173 
    174   if (Odd != 0) {
    175     Sum += *(UINT8 *) Packet;
    176   }
    177 
    178   Sum = (Sum & 0xffff) + (Sum >> 16);
    179 
    180   //
    181   // in case above carried
    182   //
    183   Sum += Sum >> 16;
    184 
    185   return (UINT16) Sum;
    186 }
    187 
    188 /**
    189   Reads and returns the current value of register.
    190   In IA64, the register is the Interval Timer Vector (ITV).
    191   In X86(IA32/X64), the register is the Time Stamp Counter (TSC)
    192 
    193   @return The current value of the register.
    194 
    195 **/
    196 
    197 STATIC CONST SHELL_PARAM_ITEM    PingParamList[] = {
    198   {
    199     L"-l",
    200     TypeValue
    201   },
    202   {
    203     L"-n",
    204     TypeValue
    205   },
    206   {
    207     L"-s",
    208     TypeValue
    209   },
    210   {
    211     L"-_s",
    212     TypeValue
    213   },
    214   {
    215     L"-_ip6",
    216     TypeFlag
    217   },
    218   {
    219     NULL,
    220     TypeMax
    221   },
    222 };
    223 
    224 //
    225 // Global Variables in Ping command.
    226 //
    227 STATIC CONST CHAR16      *mDstString;
    228 STATIC CONST CHAR16      *mSrcString;
    229 
    230 /**
    231   RTT timer tick routine.
    232 
    233   @param[in]    Event    A EFI_EVENT type event.
    234   @param[in]    Context  The pointer to Context.
    235 
    236 **/
    237 VOID
    238 EFIAPI
    239 RttTimerTickRoutine (
    240   IN EFI_EVENT    Event,
    241   IN VOID         *Context
    242   )
    243 {
    244   UINT32     *RttTimerTick;
    245 
    246   RttTimerTick = (UINT32*) Context;
    247   (*RttTimerTick)++;
    248 }
    249 
    250 /**
    251   Get the timer period of the system.
    252 
    253   This function tries to get the system timer period by creating
    254   an 1ms period timer.
    255 
    256   @return     System timer period in MS, or 0 if operation failed.
    257 
    258 **/
    259 UINT32
    260 GetTimerPeriod(
    261   VOID
    262   )
    263 {
    264   EFI_STATUS                 Status;
    265   UINT32                     RttTimerTick;
    266   EFI_EVENT                  TimerEvent;
    267   UINT32                     StallCounter;
    268   EFI_TPL                    OldTpl;
    269 
    270   RttTimerTick = 0;
    271   StallCounter   = 0;
    272 
    273   Status = gBS->CreateEvent (
    274                   EVT_TIMER | EVT_NOTIFY_SIGNAL,
    275                   TPL_NOTIFY,
    276                   RttTimerTickRoutine,
    277                   &RttTimerTick,
    278                   &TimerEvent
    279                   );
    280   if (EFI_ERROR (Status)) {
    281     return 0;
    282   }
    283 
    284   OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
    285   Status = gBS->SetTimer (
    286                   TimerEvent,
    287                   TimerPeriodic,
    288                   TICKS_PER_MS
    289                   );
    290   if (EFI_ERROR (Status)) {
    291     gBS->CloseEvent (TimerEvent);
    292     return 0;
    293   }
    294 
    295   while (RttTimerTick < 10) {
    296     gBS->Stall (STALL_1_MILLI_SECOND);
    297     ++StallCounter;
    298   }
    299 
    300   gBS->RestoreTPL (OldTpl);
    301 
    302   gBS->SetTimer (TimerEvent, TimerCancel, 0);
    303   gBS->CloseEvent (TimerEvent);
    304 
    305   return StallCounter / RttTimerTick;
    306 }
    307 
    308 /**
    309   Initialize the timer event for RTT (round trip time).
    310 
    311   @param[in]    Private    The pointer to PING_PRIVATE_DATA.
    312 
    313   @retval EFI_SUCCESS      RTT timer is started.
    314   @retval Others           Failed to start the RTT timer.
    315 
    316 **/
    317 EFI_STATUS
    318 PingInitRttTimer (
    319   PING_PRIVATE_DATA      *Private
    320   )
    321 {
    322   EFI_STATUS                 Status;
    323 
    324   Private->TimerPeriod = GetTimerPeriod ();
    325   if (Private->TimerPeriod == 0) {
    326     return EFI_ABORTED;
    327   }
    328 
    329   Private->RttTimerTick = 0;
    330   Status = gBS->CreateEvent (
    331                   EVT_TIMER | EVT_NOTIFY_SIGNAL,
    332                   TPL_NOTIFY,
    333                   RttTimerTickRoutine,
    334                   &Private->RttTimerTick,
    335                   &Private->RttTimer
    336                   );
    337   if (EFI_ERROR (Status)) {
    338     return Status;
    339   }
    340 
    341   Status = gBS->SetTimer (
    342                   Private->RttTimer,
    343                   TimerPeriodic,
    344                   TICKS_PER_MS
    345                   );
    346   if (EFI_ERROR (Status)) {
    347     gBS->CloseEvent (Private->RttTimer);
    348     return Status;
    349   }
    350 
    351   return EFI_SUCCESS;
    352 }
    353 
    354 /**
    355   Free RTT timer event resource.
    356 
    357   @param[in]    Private    The pointer to PING_PRIVATE_DATA.
    358 
    359 **/
    360 VOID
    361 PingFreeRttTimer (
    362   PING_PRIVATE_DATA      *Private
    363   )
    364 {
    365   if (Private->RttTimer != NULL) {
    366     gBS->SetTimer (Private->RttTimer, TimerCancel, 0);
    367     gBS->CloseEvent (Private->RttTimer);
    368   }
    369 }
    370 
    371 /**
    372   Read the current time.
    373 
    374   @param[in]    Private    The pointer to PING_PRIVATE_DATA.
    375 
    376   @retval the current tick value.
    377 **/
    378 UINT32
    379 ReadTime (
    380   PING_PRIVATE_DATA      *Private
    381   )
    382 {
    383   return Private->RttTimerTick;
    384 }
    385 
    386 /**
    387   Calculate a duration in ms.
    388 
    389   @param[in]    Private   The pointer to PING_PRIVATE_DATA.
    390   @param[in]    Begin     The start point of time.
    391   @param[in]    End       The end point of time.
    392 
    393   @return               The duration in ms.
    394   @retval 0             The parameters were not valid.
    395 **/
    396 UINT32
    397 CalculateTick (
    398   PING_PRIVATE_DATA      *Private,
    399   IN UINT32              Begin,
    400   IN UINT32              End
    401   )
    402 {
    403   if (End < Begin) {
    404     return (0);
    405   }
    406 
    407   return (End - Begin) * Private->TimerPeriod;
    408 }
    409 
    410 /**
    411   Destroy PING_ICMPX_TX_INFO, and recollect the memory.
    412 
    413   @param[in]    TxInfo    The pointer to PING_ICMPX_TX_INFO.
    414   @param[in]    IpChoice  Whether the token is IPv4 or IPv6
    415 **/
    416 VOID
    417 PingDestroyTxInfo (
    418   IN PING_ICMPX_TX_INFO    *TxInfo,
    419   IN UINT32                IpChoice
    420   )
    421 {
    422   EFI_IP6_TRANSMIT_DATA    *Ip6TxData;
    423   EFI_IP4_TRANSMIT_DATA    *Ip4TxData;
    424   EFI_IP6_FRAGMENT_DATA    *FragData;
    425   UINTN                    Index;
    426 
    427   if (TxInfo == NULL) {
    428     return;
    429   }
    430 
    431   if (TxInfo->Token != NULL) {
    432 
    433     if (TxInfo->Token->Event != NULL) {
    434       gBS->CloseEvent (TxInfo->Token->Event);
    435     }
    436 
    437     if (TxInfo->Token->Packet.TxData != NULL) {
    438       if (IpChoice == PING_IP_CHOICE_IP6) {
    439         Ip6TxData = TxInfo->Token->Packet.TxData;
    440 
    441         if (Ip6TxData->OverrideData != NULL) {
    442           FreePool (Ip6TxData->OverrideData);
    443         }
    444 
    445         if (Ip6TxData->ExtHdrs != NULL) {
    446           FreePool (Ip6TxData->ExtHdrs);
    447         }
    448 
    449         for (Index = 0; Index < Ip6TxData->FragmentCount; Index++) {
    450           FragData = Ip6TxData->FragmentTable[Index].FragmentBuffer;
    451           if (FragData != NULL) {
    452             FreePool (FragData);
    453           }
    454         }
    455       } else {
    456         Ip4TxData = TxInfo->Token->Packet.TxData;
    457 
    458         if (Ip4TxData->OverrideData != NULL) {
    459           FreePool (Ip4TxData->OverrideData);
    460         }
    461 
    462         for (Index = 0; Index < Ip4TxData->FragmentCount; Index++) {
    463           FragData = Ip4TxData->FragmentTable[Index].FragmentBuffer;
    464           if (FragData != NULL) {
    465             FreePool (FragData);
    466           }
    467         }
    468       }
    469     }
    470 
    471     FreePool (TxInfo->Token);
    472   }
    473 
    474   FreePool (TxInfo);
    475 }
    476 
    477 /**
    478   Match the request, and reply with SequenceNum/TimeStamp.
    479 
    480   @param[in]    Private    The pointer to PING_PRIVATE_DATA.
    481   @param[in]    Packet     The pointer to ICMPX_ECHO_REQUEST_REPLY.
    482 
    483   @retval EFI_SUCCESS      The match is successful.
    484   @retval EFI_NOT_FOUND    The reply can't be matched with any request.
    485 
    486 **/
    487 EFI_STATUS
    488 Ping6MatchEchoReply (
    489   IN PING_PRIVATE_DATA           *Private,
    490   IN ICMPX_ECHO_REQUEST_REPLY    *Packet
    491   )
    492 {
    493   PING_ICMPX_TX_INFO     *TxInfo;
    494   LIST_ENTRY             *Entry;
    495   LIST_ENTRY             *NextEntry;
    496 
    497   NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) {
    498     TxInfo = BASE_CR (Entry, PING_ICMPX_TX_INFO, Link);
    499 
    500     if ((TxInfo->SequenceNum == Packet->SequenceNum) && (TxInfo->TimeStamp == Packet->TimeStamp)) {
    501       Private->RxCount++;
    502       RemoveEntryList (&TxInfo->Link);
    503       PingDestroyTxInfo (TxInfo, Private->IpChoice);
    504       return EFI_SUCCESS;
    505     }
    506   }
    507 
    508   return EFI_NOT_FOUND;
    509 }
    510 
    511 /**
    512   The original intention is to send a request.
    513   Currently, the application retransmits an icmp6 echo request packet
    514   per second in sendnumber times that is specified by the user.
    515   Because nothing can be done here, all things move to the timer rountine.
    516 
    517   @param[in]    Event      A EFI_EVENT type event.
    518   @param[in]    Context    The pointer to Context.
    519 
    520 **/
    521 VOID
    522 EFIAPI
    523 Ping6OnEchoRequestSent (
    524   IN EFI_EVENT    Event,
    525   IN VOID         *Context
    526   )
    527 {
    528 }
    529 
    530 /**
    531   receive reply, match and print reply infomation.
    532 
    533   @param[in]    Event      A EFI_EVENT type event.
    534   @param[in]    Context    The pointer to context.
    535 
    536 **/
    537 VOID
    538 EFIAPI
    539 Ping6OnEchoReplyReceived (
    540   IN EFI_EVENT    Event,
    541   IN VOID         *Context
    542   )
    543 {
    544   EFI_STATUS                  Status;
    545   PING_PRIVATE_DATA           *Private;
    546   ICMPX_ECHO_REQUEST_REPLY    *Reply;
    547   UINT32                      PayLoad;
    548   UINT32                      Rtt;
    549 
    550   Private = (PING_PRIVATE_DATA *) Context;
    551 
    552   if (Private == NULL || Private->Status == EFI_ABORTED || Private->Signature != PING_PRIVATE_DATA_SIGNATURE) {
    553     return;
    554   }
    555 
    556   if (Private->RxToken.Packet.RxData == NULL) {
    557     return;
    558   }
    559 
    560   if (Private->IpChoice == PING_IP_CHOICE_IP6) {
    561     Reply   = ((EFI_IP6_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->FragmentTable[0].FragmentBuffer;
    562     PayLoad = ((EFI_IP6_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->DataLength;
    563     if (((EFI_IP6_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->Header->NextHeader != IP6_ICMP) {
    564       goto ON_EXIT;
    565     }
    566     if (!IP6_IS_MULTICAST ((EFI_IPv6_ADDRESS*)&Private->DstAddress) &&
    567         !EFI_IP6_EQUAL (&((EFI_IP6_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->Header->SourceAddress, (EFI_IPv6_ADDRESS*)&Private->DstAddress)) {
    568       goto ON_EXIT;
    569     }
    570 
    571     if ((Reply->Type != ICMP_V6_ECHO_REPLY) || (Reply->Code != 0)) {
    572       goto ON_EXIT;
    573     }
    574   } else {
    575     Reply   = ((EFI_IP4_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->FragmentTable[0].FragmentBuffer;
    576     PayLoad = ((EFI_IP4_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->DataLength;
    577     if (!IP4_IS_MULTICAST (EFI_IP4(*(EFI_IPv4_ADDRESS*)Private->DstAddress)) &&
    578         !EFI_IP4_EQUAL (&((EFI_IP4_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->Header->SourceAddress, (EFI_IPv4_ADDRESS*)&Private->DstAddress)) {
    579       goto ON_EXIT;
    580     }
    581 
    582     if ((Reply->Type != ICMP_V4_ECHO_REPLY) || (Reply->Code != 0)) {
    583       goto ON_EXIT;
    584     }
    585   }
    586 
    587 
    588   if (PayLoad != Private->BufferSize) {
    589     goto ON_EXIT;
    590   }
    591   //
    592   // Check whether the reply matches the sent request before.
    593   //
    594   Status = Ping6MatchEchoReply (Private, Reply);
    595   if (EFI_ERROR(Status)) {
    596     goto ON_EXIT;
    597   }
    598   //
    599   // Display statistics on this icmp6 echo reply packet.
    600   //
    601   Rtt  = CalculateTick (Private, Reply->TimeStamp, ReadTime (Private));
    602 
    603   Private->RttSum += Rtt;
    604   Private->RttMin  = Private->RttMin > Rtt ? Rtt : Private->RttMin;
    605   Private->RttMax  = Private->RttMax < Rtt ? Rtt : Private->RttMax;
    606 
    607   ShellPrintHiiEx (
    608     -1,
    609     -1,
    610     NULL,
    611     STRING_TOKEN (STR_PING_REPLY_INFO),
    612     gShellNetwork1HiiHandle,
    613     PayLoad,
    614     mDstString,
    615     Reply->SequenceNum,
    616     Private->IpChoice == PING_IP_CHOICE_IP6?((EFI_IP6_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->Header->HopLimit:0,
    617     Rtt,
    618     Rtt + Private->TimerPeriod
    619     );
    620 
    621 ON_EXIT:
    622 
    623   if (Private->RxCount < Private->SendNum) {
    624     //
    625     // Continue to receive icmp echo reply packets.
    626     //
    627     Private->RxToken.Status = EFI_ABORTED;
    628 
    629     Status = Private->ProtocolPointers.Receive (Private->IpProtocol, &Private->RxToken);
    630 
    631     if (EFI_ERROR (Status)) {
    632       Private->Status = EFI_ABORTED;
    633     }
    634   } else {
    635     //
    636     // All reply have already been received from the dest host.
    637     //
    638     Private->Status = EFI_SUCCESS;
    639   }
    640   //
    641   // Singal to recycle the each rxdata here, not at the end of process.
    642   //
    643   gBS->SignalEvent (Private->IpChoice == PING_IP_CHOICE_IP6?((EFI_IP6_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->RecycleSignal:((EFI_IP4_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->RecycleSignal);
    644 }
    645 
    646 /**
    647   Create a PING_IPX_COMPLETION_TOKEN.
    648 
    649   @param[in]    Private        The pointer of PING_PRIVATE_DATA.
    650   @param[in]    TimeStamp      The TimeStamp of request.
    651   @param[in]    SequenceNum    The SequenceNum of request.
    652 
    653   @return The pointer of PING_IPX_COMPLETION_TOKEN.
    654 
    655 **/
    656 PING_IPX_COMPLETION_TOKEN *
    657 PingGenerateToken (
    658   IN PING_PRIVATE_DATA    *Private,
    659   IN UINT32                TimeStamp,
    660   IN UINT16                SequenceNum
    661   )
    662 {
    663   EFI_STATUS                  Status;
    664   PING_IPX_COMPLETION_TOKEN   *Token;
    665   VOID                        *TxData;
    666   ICMPX_ECHO_REQUEST_REPLY    *Request;
    667   UINT16                        HeadSum;
    668   UINT16                        TempChecksum;
    669 
    670   Request = AllocateZeroPool (Private->BufferSize);
    671   if (Request == NULL) {
    672     return NULL;
    673   }
    674   TxData = AllocateZeroPool (Private->IpChoice==PING_IP_CHOICE_IP6?sizeof (EFI_IP6_TRANSMIT_DATA):sizeof (EFI_IP4_TRANSMIT_DATA));
    675   if (TxData == NULL) {
    676     FreePool (Request);
    677     return NULL;
    678   }
    679   Token = AllocateZeroPool (sizeof (PING_IPX_COMPLETION_TOKEN));
    680   if (Token == NULL) {
    681     FreePool (Request);
    682     FreePool (TxData);
    683     return NULL;
    684   }
    685 
    686   //
    687   // Assembly echo request packet.
    688   //
    689   Request->Type        = (UINT8)(Private->IpChoice==PING_IP_CHOICE_IP6?ICMP_V6_ECHO_REQUEST:ICMP_V4_ECHO_REQUEST);
    690   Request->Code        = 0;
    691   Request->SequenceNum = SequenceNum;
    692   Request->Identifier  = 0;
    693   Request->Checksum    = 0;
    694 
    695   //
    696   // Assembly token for transmit.
    697   //
    698   if (Private->IpChoice==PING_IP_CHOICE_IP6) {
    699     Request->TimeStamp   = TimeStamp;
    700     ((EFI_IP6_TRANSMIT_DATA*)TxData)->ExtHdrsLength                   = 0;
    701     ((EFI_IP6_TRANSMIT_DATA*)TxData)->ExtHdrs                         = NULL;
    702     ((EFI_IP6_TRANSMIT_DATA*)TxData)->OverrideData                    = 0;
    703     ((EFI_IP6_TRANSMIT_DATA*)TxData)->DataLength                      = Private->BufferSize;
    704     ((EFI_IP6_TRANSMIT_DATA*)TxData)->FragmentCount                   = 1;
    705     ((EFI_IP6_TRANSMIT_DATA*)TxData)->FragmentTable[0].FragmentBuffer = (VOID *) Request;
    706     ((EFI_IP6_TRANSMIT_DATA*)TxData)->FragmentTable[0].FragmentLength = Private->BufferSize;
    707   } else {
    708     ((EFI_IP4_TRANSMIT_DATA*)TxData)->OptionsLength                   = 0;
    709     ((EFI_IP4_TRANSMIT_DATA*)TxData)->OptionsBuffer                   = NULL;
    710     ((EFI_IP4_TRANSMIT_DATA*)TxData)->OverrideData                    = 0;
    711     ((EFI_IP4_TRANSMIT_DATA*)TxData)->TotalDataLength                 = Private->BufferSize;
    712     ((EFI_IP4_TRANSMIT_DATA*)TxData)->FragmentCount                   = 1;
    713     ((EFI_IP4_TRANSMIT_DATA*)TxData)->FragmentTable[0].FragmentBuffer = (VOID *) Request;
    714     ((EFI_IP4_TRANSMIT_DATA*)TxData)->FragmentTable[0].FragmentLength = Private->BufferSize;
    715     ((EFI_IP4_TRANSMIT_DATA*)TxData)->DestinationAddress.Addr[0]      = Private->DstAddress[0];
    716     ((EFI_IP4_TRANSMIT_DATA*)TxData)->DestinationAddress.Addr[1]      = Private->DstAddress[1];
    717     ((EFI_IP4_TRANSMIT_DATA*)TxData)->DestinationAddress.Addr[2]      = Private->DstAddress[2];
    718     ((EFI_IP4_TRANSMIT_DATA*)TxData)->DestinationAddress.Addr[3]      = Private->DstAddress[3];
    719 
    720     HeadSum = NetChecksum ((UINT8 *) Request, Private->BufferSize);
    721     Request->TimeStamp   = TimeStamp;
    722     TempChecksum = NetChecksum ((UINT8 *) &Request->TimeStamp, sizeof (UINT64));
    723     Request->Checksum = (UINT16)(~NetAddChecksum (HeadSum, TempChecksum));
    724   }
    725 
    726 
    727   Token->Status         = EFI_ABORTED;
    728   Token->Packet.TxData  = TxData;
    729 
    730   Status = gBS->CreateEvent (
    731                   EVT_NOTIFY_SIGNAL,
    732                   TPL_CALLBACK,
    733                   Ping6OnEchoRequestSent,
    734                   Private,
    735                   &Token->Event
    736                   );
    737 
    738   if (EFI_ERROR (Status)) {
    739     FreePool (Request);
    740     FreePool (TxData);
    741     FreePool (Token);
    742     return NULL;
    743   }
    744 
    745   return Token;
    746 }
    747 
    748 /**
    749   Transmit the PING_IPX_COMPLETION_TOKEN.
    750 
    751   @param[in]    Private    The pointer of PING_PRIVATE_DATA.
    752 
    753   @retval EFI_SUCCESS             Transmitted successfully.
    754   @retval EFI_OUT_OF_RESOURCES    No memory is available on the platform.
    755   @retval others                  Transmitted unsuccessfully.
    756 
    757 **/
    758 EFI_STATUS
    759 PingSendEchoRequest (
    760   IN PING_PRIVATE_DATA    *Private
    761   )
    762 {
    763   EFI_STATUS             Status;
    764   PING_ICMPX_TX_INFO     *TxInfo;
    765 
    766   TxInfo = AllocateZeroPool (sizeof (PING_ICMPX_TX_INFO));
    767 
    768   if (TxInfo == NULL) {
    769     return EFI_OUT_OF_RESOURCES;
    770   }
    771 
    772   TxInfo->TimeStamp   = ReadTime (Private);
    773   TxInfo->SequenceNum = (UINT16) (Private->TxCount + 1);
    774   TxInfo->Token       = PingGenerateToken (
    775                           Private,
    776                           TxInfo->TimeStamp,
    777                           TxInfo->SequenceNum
    778                           );
    779 
    780   if (TxInfo->Token == NULL) {
    781     PingDestroyTxInfo (TxInfo, Private->IpChoice);
    782     return EFI_OUT_OF_RESOURCES;
    783   }
    784 
    785   ASSERT(Private->ProtocolPointers.Transmit != NULL);
    786   Status = Private->ProtocolPointers.Transmit (Private->IpProtocol, TxInfo->Token);
    787 
    788   if (EFI_ERROR (Status)) {
    789     PingDestroyTxInfo (TxInfo, Private->IpChoice);
    790     return Status;
    791   }
    792 
    793   InsertTailList (&Private->TxList, &TxInfo->Link);
    794   Private->TxCount++;
    795 
    796   return EFI_SUCCESS;
    797 }
    798 
    799 /**
    800   Place a completion token into the receive packet queue to receive the echo reply.
    801 
    802   @param[in]    Private    The pointer of PING_PRIVATE_DATA.
    803 
    804   @retval EFI_SUCCESS      Put the token into the receive packet queue successfully.
    805   @retval others           Put the token into the receive packet queue unsuccessfully.
    806 
    807 **/
    808 EFI_STATUS
    809 Ping6ReceiveEchoReply (
    810   IN PING_PRIVATE_DATA    *Private
    811   )
    812 {
    813   EFI_STATUS    Status;
    814 
    815   ZeroMem (&Private->RxToken, sizeof (PING_IPX_COMPLETION_TOKEN));
    816 
    817   Status = gBS->CreateEvent (
    818                   EVT_NOTIFY_SIGNAL,
    819                   TPL_CALLBACK,
    820                   Ping6OnEchoReplyReceived,
    821                   Private,
    822                   &Private->RxToken.Event
    823                   );
    824 
    825   if (EFI_ERROR (Status)) {
    826     return Status;
    827   }
    828 
    829   Private->RxToken.Status = EFI_NOT_READY;
    830 
    831   return (Private->ProtocolPointers.Receive (Private->IpProtocol, &Private->RxToken));
    832 }
    833 
    834 /**
    835   Remove the timeout request from the list.
    836 
    837   @param[in]    Event    A EFI_EVENT type event.
    838   @param[in]    Context  The pointer to Context.
    839 
    840 **/
    841 VOID
    842 EFIAPI
    843 Ping6OnTimerRoutine (
    844   IN EFI_EVENT    Event,
    845   IN VOID         *Context
    846   )
    847 {
    848   EFI_STATUS             Status;
    849   PING_PRIVATE_DATA      *Private;
    850   PING_ICMPX_TX_INFO     *TxInfo;
    851   LIST_ENTRY             *Entry;
    852   LIST_ENTRY             *NextEntry;
    853   UINT64                 Time;
    854 
    855   Private = (PING_PRIVATE_DATA *) Context;
    856   if (Private->Signature != PING_PRIVATE_DATA_SIGNATURE) {
    857     Private->Status = EFI_NOT_FOUND;
    858     return;
    859   }
    860 
    861   //
    862   // Retransmit icmp6 echo request packets per second in sendnumber times.
    863   //
    864   if (Private->TxCount < Private->SendNum) {
    865 
    866     Status = PingSendEchoRequest (Private);
    867     if (Private->TxCount != 0){
    868       if (EFI_ERROR (Status)) {
    869         ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_SEND_REQUEST), gShellNetwork1HiiHandle, Private->TxCount + 1);
    870       }
    871     }
    872   }
    873   //
    874   // Check whether any icmp6 echo request in the list timeout.
    875   //
    876   NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) {
    877     TxInfo = BASE_CR (Entry, PING_ICMPX_TX_INFO, Link);
    878     Time   = CalculateTick (Private, TxInfo->TimeStamp, ReadTime (Private));
    879 
    880     //
    881     // Remove the timeout echo request from txlist.
    882     //
    883     if (Time > DEFAULT_TIMEOUT) {
    884 
    885       if (EFI_ERROR (TxInfo->Token->Status)) {
    886         Private->ProtocolPointers.Cancel (Private->IpProtocol, TxInfo->Token);
    887       }
    888       //
    889       // Remove the timeout icmp6 echo request from list.
    890       //
    891       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_TIMEOUT), gShellNetwork1HiiHandle, TxInfo->SequenceNum);
    892 
    893       RemoveEntryList (&TxInfo->Link);
    894       PingDestroyTxInfo (TxInfo, Private->IpChoice);
    895 
    896       Private->RxCount++;
    897       Private->FailedCount++;
    898 
    899       if (IsListEmpty (&Private->TxList) && (Private->TxCount == Private->SendNum)) {
    900         //
    901         // All the left icmp6 echo request in the list timeout.
    902         //
    903         Private->Status = EFI_TIMEOUT;
    904       }
    905     }
    906   }
    907 }
    908 
    909 /**
    910   Determine if a IP4 address is Link Local.
    911 
    912   169.254.1.0 through 169.254.254.255 is link local.
    913 
    914   @param[in] Address  The address to test.
    915 
    916   @retval TRUE      It is.
    917   @retval FALSE     It is not.
    918 **/
    919 BOOLEAN
    920 PingNetIp4IsLinkLocalAddr (
    921   IN CONST EFI_IPv4_ADDRESS *Address
    922   )
    923 {
    924   return ((BOOLEAN)(Address->Addr[0] == 169 && Address->Addr[1] == 254 && Address->Addr[2] >= 1 && Address->Addr[2] <= 254));
    925 }
    926 
    927 /**
    928   Determine if a IP4 address is unspecified.
    929 
    930   @param[in] Address  The address to test.
    931 
    932   @retval TRUE      It is.
    933   @retval FALSE     It is not.
    934 **/
    935 BOOLEAN
    936 PingNetIp4IsUnspecifiedAddr (
    937   IN CONST EFI_IPv4_ADDRESS *Address
    938   )
    939 {
    940   return  ((BOOLEAN)((ReadUnaligned32 ((UINT32*)&Address->Addr[0])) == 0x00000000));
    941 }
    942 
    943 /**
    944   Create a valid IP instance.
    945 
    946   @param[in]    Private    The pointer of PING_PRIVATE_DATA.
    947 
    948   @retval EFI_SUCCESS              Create a valid IPx instance successfully.
    949   @retval EFI_ABORTED              Locate handle with ipx service binding protocol unsuccessfully.
    950   @retval EFI_INVALID_PARAMETER    The source address is unspecified when the destination address is a link-local address.
    951   @retval EFI_OUT_OF_RESOURCES     No memory is available on the platform.
    952   @retval EFI_NOT_FOUND            The source address is not found.
    953 **/
    954 EFI_STATUS
    955 PingCreateIpInstance (
    956   IN  PING_PRIVATE_DATA    *Private
    957   )
    958 {
    959   EFI_STATUS                       Status;
    960   UINTN                            HandleIndex;
    961   UINTN                            HandleNum;
    962   EFI_HANDLE                       *HandleBuffer;
    963   BOOLEAN                          UnspecifiedSrc;
    964   BOOLEAN                          MediaPresent;
    965   EFI_SERVICE_BINDING_PROTOCOL     *EfiSb;
    966   VOID                             *IpXCfg;
    967   EFI_IP6_CONFIG_DATA              Ip6Config;
    968   EFI_IP4_CONFIG_DATA              Ip4Config;
    969   VOID                             *IpXInterfaceInfo;
    970   UINTN                            IfInfoSize;
    971   EFI_IPv6_ADDRESS                 *Addr;
    972   UINTN                            AddrIndex;
    973 
    974   HandleBuffer      = NULL;
    975   UnspecifiedSrc    = FALSE;
    976   MediaPresent      = TRUE;
    977   EfiSb             = NULL;
    978   IpXInterfaceInfo  = NULL;
    979   IfInfoSize        = 0;
    980 
    981   //
    982   // Locate all the handles with ip6 service binding protocol.
    983   //
    984   Status = gBS->LocateHandleBuffer (
    985                   ByProtocol,
    986                   Private->IpChoice == PING_IP_CHOICE_IP6?&gEfiIp6ServiceBindingProtocolGuid:&gEfiIp4ServiceBindingProtocolGuid,
    987                   NULL,
    988                   &HandleNum,
    989                   &HandleBuffer
    990                   );
    991   if (EFI_ERROR (Status) || (HandleNum == 0) || (HandleBuffer == NULL)) {
    992     return EFI_ABORTED;
    993   }
    994 
    995   if (Private->IpChoice == PING_IP_CHOICE_IP6 ? NetIp6IsUnspecifiedAddr ((EFI_IPv6_ADDRESS*)&Private->SrcAddress) : \
    996       PingNetIp4IsUnspecifiedAddr ((EFI_IPv4_ADDRESS*)&Private->SrcAddress)) {
    997     //
    998     // SrcAddress is unspecified. So, both connected and configured interface will be automatic selected.
    999     //
   1000     UnspecifiedSrc = TRUE;
   1001   }
   1002 
   1003   //
   1004   // Source address is required when pinging a link-local address.
   1005   //
   1006   if (Private->IpChoice == PING_IP_CHOICE_IP6) {
   1007     if (NetIp6IsLinkLocalAddr ((EFI_IPv6_ADDRESS*)&Private->DstAddress) && UnspecifiedSrc) {
   1008       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_INVALID_SOURCE), gShellNetwork1HiiHandle);
   1009       Status = EFI_INVALID_PARAMETER;
   1010       goto ON_ERROR;
   1011     }
   1012   } else {
   1013     ASSERT(Private->IpChoice == PING_IP_CHOICE_IP4);
   1014     if (PingNetIp4IsLinkLocalAddr ((EFI_IPv4_ADDRESS*)&Private->DstAddress) && UnspecifiedSrc) {
   1015       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_INVALID_SOURCE), gShellNetwork1HiiHandle);
   1016       Status = EFI_INVALID_PARAMETER;
   1017       goto ON_ERROR;
   1018     }
   1019   }
   1020 
   1021   //
   1022   // For each ip6 protocol, check interface addresses list.
   1023   //
   1024   for (HandleIndex = 0; HandleIndex < HandleNum; HandleIndex++) {
   1025     EfiSb             = NULL;
   1026     IpXInterfaceInfo  = NULL;
   1027     IfInfoSize        = 0;
   1028 
   1029     if (UnspecifiedSrc) {
   1030       //
   1031       // Check media.
   1032       //
   1033       NetLibDetectMedia (HandleBuffer[HandleIndex], &MediaPresent);
   1034       if (!MediaPresent) {
   1035         //
   1036         // Skip this one.
   1037         //
   1038         continue;
   1039       }
   1040     }
   1041 
   1042     Status = gBS->HandleProtocol (
   1043                     HandleBuffer[HandleIndex],
   1044                     Private->IpChoice == PING_IP_CHOICE_IP6?&gEfiIp6ServiceBindingProtocolGuid:&gEfiIp4ServiceBindingProtocolGuid,
   1045                     (VOID **) &EfiSb
   1046                     );
   1047     if (EFI_ERROR (Status)) {
   1048       goto ON_ERROR;
   1049     }
   1050 
   1051     //
   1052     // Ip6config protocol and ip6 service binding protocol are installed
   1053     // on the same handle.
   1054     //
   1055     Status = gBS->HandleProtocol (
   1056                     HandleBuffer[HandleIndex],
   1057                     Private->IpChoice == PING_IP_CHOICE_IP6?&gEfiIp6ConfigProtocolGuid:&gEfiIp4Config2ProtocolGuid,
   1058                     (VOID **) &IpXCfg
   1059                     );
   1060 
   1061     if (EFI_ERROR (Status)) {
   1062       goto ON_ERROR;
   1063     }
   1064     //
   1065     // Get the interface information size.
   1066     //
   1067     if (Private->IpChoice == PING_IP_CHOICE_IP6) {
   1068       Status = ((EFI_IP6_CONFIG_PROTOCOL*)IpXCfg)->GetData (
   1069                          IpXCfg,
   1070                          Ip6ConfigDataTypeInterfaceInfo,
   1071                          &IfInfoSize,
   1072                          NULL
   1073                          );
   1074     } else {
   1075       Status = ((EFI_IP4_CONFIG2_PROTOCOL*)IpXCfg)->GetData (
   1076                          IpXCfg,
   1077                          Ip4Config2DataTypeInterfaceInfo,
   1078                          &IfInfoSize,
   1079                          NULL
   1080                          );
   1081     }
   1082 
   1083     //
   1084     // Skip the ones not in current use.
   1085     //
   1086     if (Status == EFI_NOT_STARTED) {
   1087       continue;
   1088     }
   1089 
   1090     if (Status != EFI_BUFFER_TOO_SMALL) {
   1091       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_GETDATA), gShellNetwork1HiiHandle, Status);
   1092       goto ON_ERROR;
   1093     }
   1094 
   1095     IpXInterfaceInfo = AllocateZeroPool (IfInfoSize);
   1096 
   1097     if (IpXInterfaceInfo == NULL) {
   1098       Status = EFI_OUT_OF_RESOURCES;
   1099       goto ON_ERROR;
   1100     }
   1101     //
   1102     // Get the interface info.
   1103     //
   1104     if (Private->IpChoice == PING_IP_CHOICE_IP6) {
   1105       Status = ((EFI_IP6_CONFIG_PROTOCOL*)IpXCfg)->GetData (
   1106                          IpXCfg,
   1107                          Ip6ConfigDataTypeInterfaceInfo,
   1108                          &IfInfoSize,
   1109                          IpXInterfaceInfo
   1110                          );
   1111     } else {
   1112       Status = ((EFI_IP4_CONFIG2_PROTOCOL*)IpXCfg)->GetData (
   1113                          IpXCfg,
   1114                          Ip4Config2DataTypeInterfaceInfo,
   1115                          &IfInfoSize,
   1116                          IpXInterfaceInfo
   1117                          );
   1118     }
   1119 
   1120     if (EFI_ERROR (Status)) {
   1121       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_GETDATA), gShellNetwork1HiiHandle, Status);
   1122       goto ON_ERROR;
   1123     }
   1124     //
   1125     // Check whether the source address is one of the interface addresses.
   1126     //
   1127     if (Private->IpChoice == PING_IP_CHOICE_IP6) {
   1128       for (AddrIndex = 0; AddrIndex < ((EFI_IP6_CONFIG_INTERFACE_INFO*)IpXInterfaceInfo)->AddressInfoCount; AddrIndex++) {
   1129         Addr = &(((EFI_IP6_CONFIG_INTERFACE_INFO*)IpXInterfaceInfo)->AddressInfo[AddrIndex].Address);
   1130 
   1131         if (UnspecifiedSrc) {
   1132           if (!NetIp6IsUnspecifiedAddr (Addr) && !NetIp6IsLinkLocalAddr (Addr)) {
   1133             //
   1134             // Select the interface automatically.
   1135             //
   1136             CopyMem(&Private->SrcAddress, Addr, sizeof(Private->SrcAddress));
   1137             break;
   1138           }
   1139         } else if (EFI_IP6_EQUAL (&Private->SrcAddress, Addr)) {
   1140           //
   1141           // Match a certain interface address.
   1142           //
   1143           break;
   1144         }
   1145       }
   1146 
   1147       if (AddrIndex < ((EFI_IP6_CONFIG_INTERFACE_INFO*)IpXInterfaceInfo)->AddressInfoCount) {
   1148         //
   1149         // Found a nic handle with right interface address.
   1150         //
   1151         break;
   1152       }
   1153     } else {
   1154       if (UnspecifiedSrc) {
   1155         if (!PingNetIp4IsUnspecifiedAddr (&((EFI_IP4_CONFIG2_INTERFACE_INFO*)IpXInterfaceInfo)->StationAddress) &&
   1156             !PingNetIp4IsLinkLocalAddr (&((EFI_IP4_CONFIG2_INTERFACE_INFO*)IpXInterfaceInfo)->StationAddress)) {
   1157           //
   1158           // Select the interface automatically.
   1159           //
   1160           break;
   1161         }
   1162       } else if (EFI_IP4_EQUAL (&Private->SrcAddress, &((EFI_IP4_CONFIG2_INTERFACE_INFO*)IpXInterfaceInfo)->StationAddress)) {
   1163         //
   1164         // Match a certain interface address.
   1165         //
   1166         break;
   1167       }
   1168     }
   1169 
   1170     FreePool (IpXInterfaceInfo);
   1171     IpXInterfaceInfo = NULL;
   1172   }
   1173   //
   1174   // No exact interface address matched.
   1175   //
   1176 
   1177   if (HandleIndex == HandleNum) {
   1178     ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_CONFIGD_NIC_NF), gShellNetwork1HiiHandle, L"ping");
   1179     Status = EFI_NOT_FOUND;
   1180     goto ON_ERROR;
   1181   }
   1182 
   1183   Private->NicHandle = HandleBuffer[HandleIndex];
   1184 
   1185   ASSERT (EfiSb != NULL);
   1186   Status = EfiSb->CreateChild (EfiSb, &Private->IpChildHandle);
   1187 
   1188   if (EFI_ERROR (Status)) {
   1189     goto ON_ERROR;
   1190   }
   1191   if (Private->IpChoice == PING_IP_CHOICE_IP6) {
   1192     Status = gBS->OpenProtocol (
   1193                     Private->IpChildHandle,
   1194                     &gEfiIp6ProtocolGuid,
   1195                     &Private->IpProtocol,
   1196                     gImageHandle,
   1197                     Private->IpChildHandle,
   1198                     EFI_OPEN_PROTOCOL_GET_PROTOCOL
   1199                     );
   1200     if (EFI_ERROR (Status)) {
   1201       goto ON_ERROR;
   1202     }
   1203 
   1204 
   1205     ZeroMem (&Ip6Config, sizeof (EFI_IP6_CONFIG_DATA));
   1206 
   1207     //
   1208     // Configure the ip6 instance for icmp6 packet exchange.
   1209     //
   1210     Ip6Config.DefaultProtocol   = 58;
   1211     Ip6Config.AcceptAnyProtocol = FALSE;
   1212     Ip6Config.AcceptIcmpErrors  = TRUE;
   1213     Ip6Config.AcceptPromiscuous = FALSE;
   1214     Ip6Config.TrafficClass      = 0;
   1215     Ip6Config.HopLimit          = 128;
   1216     Ip6Config.FlowLabel         = 0;
   1217     Ip6Config.ReceiveTimeout    = 0;
   1218     Ip6Config.TransmitTimeout   = 0;
   1219 
   1220     IP6_COPY_ADDRESS (&Ip6Config.StationAddress,     &Private->SrcAddress);
   1221     IP6_COPY_ADDRESS (&Ip6Config.DestinationAddress, &Private->DstAddress);
   1222 
   1223     Status = ((EFI_IP6_PROTOCOL*)(Private->IpProtocol))->Configure (Private->IpProtocol, &Ip6Config);
   1224 
   1225     if (EFI_ERROR (Status)) {
   1226       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_CONFIG), gShellNetwork1HiiHandle, Status);
   1227       goto ON_ERROR;
   1228     }
   1229 
   1230     Private->ProtocolPointers.Transmit  = (PING_IPX_TRANSMIT )((EFI_IP6_PROTOCOL*)Private->IpProtocol)->Transmit;
   1231     Private->ProtocolPointers.Receive   = (PING_IPX_RECEIVE  )((EFI_IP6_PROTOCOL*)Private->IpProtocol)->Receive;
   1232     Private->ProtocolPointers.Cancel    = (PING_IPX_CANCEL   )((EFI_IP6_PROTOCOL*)Private->IpProtocol)->Cancel;
   1233     Private->ProtocolPointers.Poll      = (PING_IPX_POLL     )((EFI_IP6_PROTOCOL*)Private->IpProtocol)->Poll;
   1234   } else {
   1235     Status = gBS->OpenProtocol (
   1236                     Private->IpChildHandle,
   1237                     &gEfiIp4ProtocolGuid,
   1238                     &Private->IpProtocol,
   1239                     gImageHandle,
   1240                     Private->IpChildHandle,
   1241                     EFI_OPEN_PROTOCOL_GET_PROTOCOL
   1242                     );
   1243     if (EFI_ERROR (Status)) {
   1244       goto ON_ERROR;
   1245     }
   1246 
   1247 
   1248     ZeroMem (&Ip4Config, sizeof (EFI_IP4_CONFIG_DATA));
   1249 
   1250     //
   1251     // Configure the ip4 instance for icmp4 packet exchange.
   1252     //
   1253     Ip4Config.DefaultProtocol   = 1;
   1254     Ip4Config.AcceptAnyProtocol = FALSE;
   1255     Ip4Config.AcceptBroadcast   = FALSE;
   1256     Ip4Config.AcceptIcmpErrors  = TRUE;
   1257     Ip4Config.AcceptPromiscuous = FALSE;
   1258     Ip4Config.DoNotFragment     = FALSE;
   1259     Ip4Config.RawData           = FALSE;
   1260     Ip4Config.ReceiveTimeout    = 0;
   1261     Ip4Config.TransmitTimeout   = 0;
   1262     Ip4Config.UseDefaultAddress = TRUE;
   1263     Ip4Config.TimeToLive        = 128;
   1264     Ip4Config.TypeOfService     = 0;
   1265 
   1266     Status = ((EFI_IP4_PROTOCOL*)(Private->IpProtocol))->Configure (Private->IpProtocol, &Ip4Config);
   1267 
   1268     if (EFI_ERROR (Status)) {
   1269       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_CONFIG), gShellNetwork1HiiHandle, Status);
   1270       goto ON_ERROR;
   1271     }
   1272 
   1273     Private->ProtocolPointers.Transmit  = (PING_IPX_TRANSMIT )((EFI_IP4_PROTOCOL*)Private->IpProtocol)->Transmit;
   1274     Private->ProtocolPointers.Receive   = (PING_IPX_RECEIVE  )((EFI_IP4_PROTOCOL*)Private->IpProtocol)->Receive;
   1275     Private->ProtocolPointers.Cancel    = (PING_IPX_CANCEL   )((EFI_IP4_PROTOCOL*)Private->IpProtocol)->Cancel;
   1276     Private->ProtocolPointers.Poll      = (PING_IPX_POLL     )((EFI_IP4_PROTOCOL*)Private->IpProtocol)->Poll;
   1277   }
   1278 
   1279   if (HandleBuffer != NULL) {
   1280     FreePool (HandleBuffer);
   1281   }
   1282 
   1283   return EFI_SUCCESS;
   1284 
   1285 ON_ERROR:
   1286   if (HandleBuffer != NULL) {
   1287     FreePool (HandleBuffer);
   1288   }
   1289 
   1290   if (IpXInterfaceInfo != NULL) {
   1291     FreePool (IpXInterfaceInfo);
   1292   }
   1293 
   1294   if ((EfiSb != NULL) && (Private->IpChildHandle != NULL)) {
   1295     EfiSb->DestroyChild (EfiSb, Private->IpChildHandle);
   1296   }
   1297 
   1298   return Status;
   1299 }
   1300 
   1301 /**
   1302   Destroy the IP instance.
   1303 
   1304   @param[in]    Private    The pointer of PING_PRIVATE_DATA.
   1305 
   1306 **/
   1307 VOID
   1308 Ping6DestroyIp6Instance (
   1309   IN PING_PRIVATE_DATA    *Private
   1310   )
   1311 {
   1312   EFI_STATUS                      Status;
   1313   EFI_SERVICE_BINDING_PROTOCOL    *IpSb;
   1314 
   1315   gBS->CloseProtocol (
   1316          Private->IpChildHandle,
   1317          Private->IpChoice == PING_IP_CHOICE_IP6?&gEfiIp6ProtocolGuid:&gEfiIp4ProtocolGuid,
   1318          gImageHandle,
   1319          Private->IpChildHandle
   1320          );
   1321 
   1322   Status = gBS->HandleProtocol (
   1323                   Private->NicHandle,
   1324                   Private->IpChoice == PING_IP_CHOICE_IP6?&gEfiIp6ServiceBindingProtocolGuid:&gEfiIp4ServiceBindingProtocolGuid,
   1325                   (VOID **) &IpSb
   1326                   );
   1327 
   1328   if (!EFI_ERROR(Status)) {
   1329     IpSb->DestroyChild (IpSb, Private->IpChildHandle);
   1330   }
   1331 }
   1332 
   1333 
   1334 /**
   1335   The Ping Process.
   1336 
   1337   @param[in]   SendNumber     The send request count.
   1338   @param[in]   BufferSize     The send buffer size.
   1339   @param[in]   SrcAddress     The source address.
   1340   @param[in]   DstAddress     The destination address.
   1341   @param[in]   IpChoice       The choice between IPv4 and IPv6.
   1342 
   1343   @retval SHELL_SUCCESS  The ping processed successfullly.
   1344   @retval others         The ping processed unsuccessfully.
   1345 **/
   1346 SHELL_STATUS
   1347 ShellPing (
   1348   IN UINT32              SendNumber,
   1349   IN UINT32              BufferSize,
   1350   IN EFI_IPv6_ADDRESS    *SrcAddress,
   1351   IN EFI_IPv6_ADDRESS    *DstAddress,
   1352   IN UINT32              IpChoice
   1353   )
   1354 {
   1355   EFI_STATUS             Status;
   1356   PING_PRIVATE_DATA      *Private;
   1357   PING_ICMPX_TX_INFO     *TxInfo;
   1358   LIST_ENTRY             *Entry;
   1359   LIST_ENTRY             *NextEntry;
   1360   SHELL_STATUS           ShellStatus;
   1361 
   1362   ShellStatus = SHELL_SUCCESS;
   1363   Private     = AllocateZeroPool (sizeof (PING_PRIVATE_DATA));
   1364 
   1365   if (Private == NULL) {
   1366     return (SHELL_OUT_OF_RESOURCES);
   1367   }
   1368 
   1369   Private->IpChoice    = IpChoice;
   1370   Private->Signature   = PING_PRIVATE_DATA_SIGNATURE;
   1371   Private->SendNum     = SendNumber;
   1372   Private->BufferSize  = BufferSize;
   1373   Private->RttMin      = ~((UINT64 )(0x0));
   1374   Private->Status      = EFI_NOT_READY;
   1375 
   1376   CopyMem(&Private->SrcAddress, SrcAddress, sizeof(Private->SrcAddress));
   1377   CopyMem(&Private->DstAddress, DstAddress, sizeof(Private->DstAddress));
   1378 
   1379   InitializeListHead (&Private->TxList);
   1380 
   1381   //
   1382   // Open and configure a ip instance for us.
   1383   //
   1384   Status = PingCreateIpInstance (Private);
   1385 
   1386   if (EFI_ERROR (Status)) {
   1387     ShellStatus = SHELL_ACCESS_DENIED;
   1388     goto ON_EXIT;
   1389   }
   1390   //
   1391   // Print the command line itself.
   1392   //
   1393   ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_START), gShellNetwork1HiiHandle, mDstString, Private->BufferSize);
   1394   //
   1395   // Create a ipv6 token to receive the first icmp6 echo reply packet.
   1396   //
   1397   Status = Ping6ReceiveEchoReply (Private);
   1398 
   1399   if (EFI_ERROR (Status)) {
   1400     ShellStatus = SHELL_ACCESS_DENIED;
   1401     goto ON_EXIT;
   1402   }
   1403   //
   1404   // Create and start timer to send icmp6 echo request packet per second.
   1405   //
   1406   Status = gBS->CreateEvent (
   1407                   EVT_TIMER | EVT_NOTIFY_SIGNAL,
   1408                   TPL_CALLBACK,
   1409                   Ping6OnTimerRoutine,
   1410                   Private,
   1411                   &Private->Timer
   1412                   );
   1413 
   1414   if (EFI_ERROR (Status)) {
   1415     ShellStatus = SHELL_ACCESS_DENIED;
   1416     goto ON_EXIT;
   1417   }
   1418 
   1419   //
   1420   // Start a timer to calculate the RTT.
   1421   //
   1422   Status = PingInitRttTimer (Private);
   1423   if (EFI_ERROR (Status)) {
   1424     ShellStatus = SHELL_ACCESS_DENIED;
   1425     goto ON_EXIT;
   1426   }
   1427 
   1428   //
   1429   // Create a ipv6 token to send the first icmp6 echo request packet.
   1430   //
   1431   Status = PingSendEchoRequest (Private);
   1432   //
   1433   // EFI_NOT_READY for IPsec is enable and IKE is not established.
   1434   //
   1435   if (EFI_ERROR (Status) && (Status != EFI_NOT_READY)) {
   1436     ShellStatus = SHELL_ACCESS_DENIED;
   1437     if(Status == EFI_NOT_FOUND) {
   1438       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_NOSOURCE_INDO), gShellNetwork1HiiHandle, mDstString);
   1439     } else if (Status == RETURN_NO_MAPPING) {
   1440       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_NOROUTE_FOUND), gShellNetwork1HiiHandle, mDstString, mSrcString);
   1441     } else {
   1442       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_NETWORK_ERROR), gShellNetwork1HiiHandle, L"ping", Status);
   1443     }
   1444 
   1445     goto ON_EXIT;
   1446   }
   1447 
   1448   Status = gBS->SetTimer (
   1449                   Private->Timer,
   1450                   TimerPeriodic,
   1451                   ONE_SECOND
   1452                   );
   1453 
   1454   if (EFI_ERROR (Status)) {
   1455     ShellStatus = SHELL_ACCESS_DENIED;
   1456     goto ON_EXIT;
   1457   }
   1458   //
   1459   // Control the ping6 process by two factors:
   1460   // 1. Hot key
   1461   // 2. Private->Status
   1462   //   2.1. success means all icmp6 echo request packets get reply packets.
   1463   //   2.2. timeout means the last icmp6 echo reply request timeout to get reply.
   1464   //   2.3. noready means ping6 process is on-the-go.
   1465   //
   1466   while (Private->Status == EFI_NOT_READY) {
   1467     Status = Private->ProtocolPointers.Poll (Private->IpProtocol);
   1468     if (ShellGetExecutionBreakFlag()) {
   1469       Private->Status = EFI_ABORTED;
   1470       goto ON_STAT;
   1471     }
   1472   }
   1473 
   1474 ON_STAT:
   1475   //
   1476   // Display the statistics in all.
   1477   //
   1478   gBS->SetTimer (Private->Timer, TimerCancel, 0);
   1479 
   1480   if (Private->TxCount != 0) {
   1481     ShellPrintHiiEx (
   1482       -1,
   1483       -1,
   1484       NULL,
   1485       STRING_TOKEN (STR_PING_STAT),
   1486       gShellNetwork1HiiHandle,
   1487       Private->TxCount,
   1488       (Private->RxCount - Private->FailedCount),
   1489       (100 - ((100 * (Private->RxCount - Private->FailedCount)) / Private->TxCount)),
   1490       Private->RttSum
   1491       );
   1492   }
   1493 
   1494   if (Private->RxCount > Private->FailedCount) {
   1495     ShellPrintHiiEx (
   1496       -1,
   1497       -1,
   1498       NULL,
   1499       STRING_TOKEN (STR_PING_RTT),
   1500       gShellNetwork1HiiHandle,
   1501       Private->RttMin,
   1502       Private->RttMin + Private->TimerPeriod,
   1503       Private->RttMax,
   1504       Private->RttMax + Private->TimerPeriod,
   1505       DivU64x64Remainder (Private->RttSum, (Private->RxCount - Private->FailedCount), NULL),
   1506       DivU64x64Remainder (Private->RttSum, (Private->RxCount - Private->FailedCount), NULL) + Private->TimerPeriod
   1507       );
   1508   }
   1509 
   1510 ON_EXIT:
   1511 
   1512   if (Private != NULL) {
   1513 
   1514     NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) {
   1515       TxInfo = BASE_CR (Entry, PING_ICMPX_TX_INFO, Link);
   1516 
   1517       if (Private->IpProtocol != NULL && Private->ProtocolPointers.Cancel != NULL) {
   1518         Status = Private->ProtocolPointers.Cancel (Private->IpProtocol, TxInfo->Token);
   1519       }
   1520 
   1521       RemoveEntryList (&TxInfo->Link);
   1522       PingDestroyTxInfo (TxInfo, Private->IpChoice);
   1523     }
   1524 
   1525     PingFreeRttTimer (Private);
   1526 
   1527     if (Private->Timer != NULL) {
   1528       gBS->CloseEvent (Private->Timer);
   1529     }
   1530 
   1531     if (Private->IpProtocol != NULL && Private->ProtocolPointers.Cancel != NULL) {
   1532       Status = Private->ProtocolPointers.Cancel (Private->IpProtocol, &Private->RxToken);
   1533     }
   1534 
   1535     if (Private->RxToken.Event != NULL) {
   1536       gBS->CloseEvent (Private->RxToken.Event);
   1537     }
   1538 
   1539     if (Private->IpChildHandle != NULL) {
   1540       Ping6DestroyIp6Instance (Private);
   1541     }
   1542 
   1543     FreePool (Private);
   1544   }
   1545 
   1546   return ShellStatus;
   1547 }
   1548 
   1549 /**
   1550   Function for 'ping' command.
   1551 
   1552   @param[in] ImageHandle  Handle to the Image (NULL if Internal).
   1553   @param[in] SystemTable  Pointer to the System Table (NULL if Internal).
   1554 
   1555   @retval SHELL_SUCCESS  The ping processed successfullly.
   1556   @retval others         The ping processed unsuccessfully.
   1557 
   1558 **/
   1559 SHELL_STATUS
   1560 EFIAPI
   1561 ShellCommandRunPing (
   1562   IN EFI_HANDLE        ImageHandle,
   1563   IN EFI_SYSTEM_TABLE  *SystemTable
   1564   )
   1565 {
   1566   EFI_STATUS          Status;
   1567   SHELL_STATUS        ShellStatus;
   1568   EFI_IPv6_ADDRESS    DstAddress;
   1569   EFI_IPv6_ADDRESS    SrcAddress;
   1570   UINT64              BufferSize;
   1571   UINTN               SendNumber;
   1572   LIST_ENTRY          *ParamPackage;
   1573   CONST CHAR16        *ValueStr;
   1574   UINTN               NonOptionCount;
   1575   UINT32              IpChoice;
   1576   CHAR16              *ProblemParam;
   1577 
   1578   //
   1579   // we use IPv6 buffers to hold items...
   1580   // make sure this is enough space!
   1581   //
   1582   ASSERT(sizeof(EFI_IPv4_ADDRESS        ) <= sizeof(EFI_IPv6_ADDRESS         ));
   1583   ASSERT(sizeof(EFI_IP4_COMPLETION_TOKEN) <= sizeof(EFI_IP6_COMPLETION_TOKEN ));
   1584 
   1585   IpChoice = PING_IP_CHOICE_IP4;
   1586 
   1587   ShellStatus = SHELL_SUCCESS;
   1588   ProblemParam = NULL;
   1589 
   1590   Status = ShellCommandLineParseEx (PingParamList, &ParamPackage, &ProblemParam, TRUE, FALSE);
   1591   if (EFI_ERROR(Status)) {
   1592     ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellNetwork1HiiHandle, L"ping", ProblemParam);
   1593     ShellStatus = SHELL_INVALID_PARAMETER;
   1594     goto ON_EXIT;
   1595   }
   1596 
   1597   if (ShellCommandLineGetFlag (ParamPackage, L"-_ip6")) {
   1598     IpChoice = PING_IP_CHOICE_IP6;
   1599   }
   1600 
   1601   //
   1602   // Parse the parameter of count number.
   1603   //
   1604   ValueStr = ShellCommandLineGetValue (ParamPackage, L"-n");
   1605   if (ValueStr != NULL) {
   1606     SendNumber = ShellStrToUintn (ValueStr);
   1607 
   1608     //
   1609     // ShellStrToUintn will return 0 when input is 0 or an invalid input string.
   1610     //
   1611     if ((SendNumber == 0) || (SendNumber > MAX_SEND_NUMBER)) {
   1612       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellNetwork1HiiHandle, L"ping", ValueStr);
   1613       ShellStatus = SHELL_INVALID_PARAMETER;
   1614       goto ON_EXIT;
   1615     }
   1616   } else {
   1617     SendNumber = DEFAULT_SEND_COUNT;
   1618   }
   1619   //
   1620   // Parse the parameter of buffer size.
   1621   //
   1622   ValueStr = ShellCommandLineGetValue (ParamPackage, L"-l");
   1623   if (ValueStr != NULL) {
   1624     BufferSize = ShellStrToUintn (ValueStr);
   1625 
   1626     //
   1627     // ShellStrToUintn will return 0 when input is 0 or an invalid input string.
   1628     //
   1629     if ((BufferSize < 16) || (BufferSize > MAX_BUFFER_SIZE)) {
   1630       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellNetwork1HiiHandle, L"ping", ValueStr);
   1631       ShellStatus = SHELL_INVALID_PARAMETER;
   1632       goto ON_EXIT;
   1633     }
   1634   } else {
   1635     BufferSize = DEFAULT_BUFFER_SIZE;
   1636   }
   1637 
   1638   ZeroMem (&SrcAddress, sizeof (EFI_IPv6_ADDRESS));
   1639   ZeroMem (&DstAddress, sizeof (EFI_IPv6_ADDRESS));
   1640 
   1641   //
   1642   // Parse the parameter of source ip address.
   1643   //
   1644   ValueStr = ShellCommandLineGetValue (ParamPackage, L"-s");
   1645   if (ValueStr == NULL) {
   1646     ValueStr = ShellCommandLineGetValue (ParamPackage, L"-_s");
   1647   }
   1648 
   1649   if (ValueStr != NULL) {
   1650     mSrcString = ValueStr;
   1651     if (IpChoice == PING_IP_CHOICE_IP6) {
   1652       Status = NetLibStrToIp6 (ValueStr, &SrcAddress);
   1653     } else {
   1654       Status = NetLibStrToIp4 (ValueStr, (EFI_IPv4_ADDRESS*)&SrcAddress);
   1655     }
   1656     if (EFI_ERROR (Status)) {
   1657       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellNetwork1HiiHandle, L"ping", ValueStr);
   1658       ShellStatus = SHELL_INVALID_PARAMETER;
   1659       goto ON_EXIT;
   1660     }
   1661   }
   1662   //
   1663   // Parse the parameter of destination ip address.
   1664   //
   1665   NonOptionCount = ShellCommandLineGetCount(ParamPackage);
   1666   if (NonOptionCount < 2) {
   1667     ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_FEW), gShellNetwork1HiiHandle, L"ping");
   1668     ShellStatus = SHELL_INVALID_PARAMETER;
   1669     goto ON_EXIT;
   1670   }
   1671   if (NonOptionCount > 2) {
   1672     ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_MANY), gShellNetwork1HiiHandle, L"ping");
   1673     ShellStatus = SHELL_INVALID_PARAMETER;
   1674     goto ON_EXIT;
   1675   }
   1676   ValueStr = ShellCommandLineGetRawValue (ParamPackage, 1);
   1677   if (ValueStr != NULL) {
   1678     mDstString = ValueStr;
   1679     if (IpChoice == PING_IP_CHOICE_IP6) {
   1680       Status = NetLibStrToIp6 (ValueStr, &DstAddress);
   1681     } else {
   1682       Status = NetLibStrToIp4 (ValueStr, (EFI_IPv4_ADDRESS*)&DstAddress);
   1683     }
   1684     if (EFI_ERROR (Status)) {
   1685       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellNetwork1HiiHandle, L"ping", ValueStr);
   1686       ShellStatus = SHELL_INVALID_PARAMETER;
   1687       goto ON_EXIT;
   1688     }
   1689   }
   1690 
   1691   //
   1692   // Enter into ping process.
   1693   //
   1694   ShellStatus = ShellPing (
   1695              (UINT32)SendNumber,
   1696              (UINT32)BufferSize,
   1697              &SrcAddress,
   1698              &DstAddress,
   1699              IpChoice
   1700              );
   1701 
   1702 ON_EXIT:
   1703   ShellCommandLineFreeVarList (ParamPackage);
   1704   return ShellStatus;
   1705 }
   1706