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