Home | History | Annotate | Download | only in UefiShellNetwork2CommandsLib
      1 /** @file
      2   The implementation for Ping6 application.
      3 
      4   Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
      5 
      6   This program and the accompanying materials
      7   are licensed and made available under the terms and conditions of the BSD License
      8   which accompanies this distribution.  The full text of the license may be found at
      9   http://opensource.org/licenses/bsd-license.php.
     10 
     11   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     12   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     13 
     14 **/
     15 
     16 #include "UefiShellNetwork2CommandsLib.h"
     17 
     18 #define PING6_DEFAULT_TIMEOUT      5000
     19 #define PING6_MAX_SEND_NUMBER      10000
     20 #define PING6_MAX_BUFFER_SIZE      32768
     21 #define PING6_ONE_SECOND           10000000
     22 #define STALL_1_MILLI_SECOND  1000
     23 
     24 #pragma pack(1)
     25 
     26 typedef struct _ICMP6_ECHO_REQUEST_REPLY {
     27   UINT8                       Type;
     28   UINT8                       Code;
     29   UINT16                      Checksum;
     30   UINT16                      Identifier;
     31   UINT16                      SequenceNum;
     32   UINT32                      TimeStamp;
     33   UINT8                       Data[1];
     34 } ICMP6_ECHO_REQUEST_REPLY;
     35 
     36 #pragma pack()
     37 
     38 typedef struct _PING6_ICMP6_TX_INFO {
     39   LIST_ENTRY                  Link;
     40   UINT16                      SequenceNum;
     41   UINT32                      TimeStamp;
     42   EFI_IP6_COMPLETION_TOKEN    *Token;
     43 } PING6_ICMP6_TX_INFO;
     44 
     45 typedef struct _PING6_PRIVATE_DATA {
     46   EFI_HANDLE                  ImageHandle;
     47   EFI_HANDLE                  NicHandle;
     48   EFI_HANDLE                  Ip6ChildHandle;
     49   EFI_IP6_PROTOCOL            *Ip6;
     50   EFI_EVENT                   Timer;
     51 
     52   UINT32                      TimerPeriod;
     53   UINT32                      RttTimerTick;
     54   EFI_EVENT                   RttTimer;
     55 
     56   EFI_STATUS                  Status;
     57   LIST_ENTRY                  TxList;
     58   EFI_IP6_COMPLETION_TOKEN    RxToken;
     59   UINT16                      RxCount;
     60   UINT16                      TxCount;
     61   UINT64                      RttSum;
     62   UINT64                      RttMin;
     63   UINT64                      RttMax;
     64   UINT32                      SequenceNum;
     65 
     66   EFI_IPv6_ADDRESS            SrcAddress;
     67   EFI_IPv6_ADDRESS            DstAddress;
     68   UINT32                      SendNum;
     69   UINT32                      BufferSize;
     70 } PING6_PRIVATE_DATA;
     71 
     72 
     73 SHELL_PARAM_ITEM    Ping6ParamList[] = {
     74   {
     75     L"-l",
     76     TypeValue
     77   },
     78   {
     79     L"-n",
     80     TypeValue
     81   },
     82   {
     83     L"-s",
     84     TypeValue
     85   },
     86   {
     87     L"-?",
     88     TypeFlag
     89   },
     90   {
     91     NULL,
     92     TypeMax
     93   },
     94 };
     95 
     96 //
     97 // Global Variables in Ping6 application.
     98 //
     99 CONST CHAR16            *mIp6DstString;
    100 CONST CHAR16            *mIp6SrcString;
    101 EFI_CPU_ARCH_PROTOCOL   *Cpu = NULL;
    102 
    103 /**
    104   RTT timer tick routine.
    105 
    106   @param[in]    Event    A EFI_EVENT type event.
    107   @param[in]    Context  The pointer to Context.
    108 
    109 **/
    110 VOID
    111 EFIAPI
    112 Ping6RttTimerTickRoutine (
    113   IN EFI_EVENT    Event,
    114   IN VOID         *Context
    115   )
    116 {
    117   UINT32     *RttTimerTick;
    118 
    119   RttTimerTick = (UINT32*) Context;
    120   (*RttTimerTick)++;
    121 }
    122 
    123 /**
    124   Get the timer period of the system.
    125 
    126   This function tries to get the system timer period by creating
    127   an 1ms period timer.
    128 
    129   @return     System timer period in MS, or 0 if operation failed.
    130 
    131 **/
    132 UINT32
    133 Ping6GetTimerPeriod(
    134   VOID
    135   )
    136 {
    137   EFI_STATUS                 Status;
    138   UINT32                     RttTimerTick;
    139   EFI_EVENT                  TimerEvent;
    140   UINT32                     StallCounter;
    141   EFI_TPL                    OldTpl;
    142 
    143   RttTimerTick = 0;
    144   StallCounter   = 0;
    145 
    146   Status = gBS->CreateEvent (
    147                   EVT_TIMER | EVT_NOTIFY_SIGNAL,
    148                   TPL_NOTIFY,
    149                   Ping6RttTimerTickRoutine,
    150                   &RttTimerTick,
    151                   &TimerEvent
    152                   );
    153   if (EFI_ERROR (Status)) {
    154     return 0;
    155   }
    156 
    157   OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
    158   Status = gBS->SetTimer (
    159                   TimerEvent,
    160                   TimerPeriodic,
    161                   TICKS_PER_MS
    162                   );
    163   if (EFI_ERROR (Status)) {
    164     gBS->CloseEvent (TimerEvent);
    165     return 0;
    166   }
    167 
    168   while (RttTimerTick < 10) {
    169     gBS->Stall (STALL_1_MILLI_SECOND);
    170     ++StallCounter;
    171   }
    172 
    173   gBS->RestoreTPL (OldTpl);
    174 
    175   gBS->SetTimer (TimerEvent, TimerCancel, 0);
    176   gBS->CloseEvent (TimerEvent);
    177 
    178   return StallCounter / RttTimerTick;
    179 }
    180 
    181 
    182 /**
    183   Initialize the timer event for RTT (round trip time).
    184 
    185   @param[in]    Private    The pointer to PING6_PRIVATE_DATA.
    186 
    187   @retval EFI_SUCCESS      RTT timer is started.
    188   @retval Others           Failed to start the RTT timer.
    189 
    190 **/
    191 EFI_STATUS
    192 Ping6InitRttTimer (
    193   IN  PING6_PRIVATE_DATA      *Private
    194   )
    195 {
    196   EFI_STATUS                 Status;
    197 
    198   Private->TimerPeriod = Ping6GetTimerPeriod ();
    199   if (Private->TimerPeriod == 0) {
    200     return EFI_ABORTED;
    201   }
    202 
    203   Private->RttTimerTick = 0;
    204   Status = gBS->CreateEvent (
    205                   EVT_TIMER | EVT_NOTIFY_SIGNAL,
    206                   TPL_NOTIFY,
    207                   Ping6RttTimerTickRoutine,
    208                   &Private->RttTimerTick,
    209                   &Private->RttTimer
    210                   );
    211   if (EFI_ERROR (Status)) {
    212     return Status;
    213   }
    214 
    215   Status = gBS->SetTimer (
    216                   Private->RttTimer,
    217                   TimerPeriodic,
    218                   TICKS_PER_MS
    219                   );
    220   if (EFI_ERROR (Status)) {
    221     gBS->CloseEvent (Private->RttTimer);
    222     return Status;
    223   }
    224 
    225   return EFI_SUCCESS;
    226 
    227 }
    228 
    229 /**
    230   Free RTT timer event resource.
    231 
    232   @param[in]    Private    The pointer to PING6_PRIVATE_DATA.
    233 
    234 **/
    235 VOID
    236 Ping6FreeRttTimer (
    237   IN  PING6_PRIVATE_DATA      *Private
    238   )
    239 {
    240   if (Private->RttTimer != NULL) {
    241     gBS->SetTimer (Private->RttTimer, TimerCancel, 0);
    242     gBS->CloseEvent (Private->RttTimer);
    243   }
    244 }
    245 
    246 /**
    247   Read the current time.
    248 
    249   @param[in]    Private    The pointer to PING6_PRIVATE_DATA.
    250 
    251   @retval the current tick value.
    252 **/
    253 UINT32
    254 Ping6ReadTime (
    255   IN  PING6_PRIVATE_DATA      *Private
    256   )
    257 {
    258   return Private->RttTimerTick;
    259 }
    260 
    261 /**
    262   Get and calculate the duration in ms.
    263 
    264   @param[in]  Private  The pointer to PING6_PRIVATE_DATA.
    265   @param[in]  Begin    The start point of time.
    266   @param[in]  End      The end point of time.
    267 
    268   @return The duration in ms.
    269 
    270 **/
    271 UINT32
    272 Ping6CalculateTick (
    273   IN PING6_PRIVATE_DATA      *Private,
    274   IN UINT32    Begin,
    275   IN UINT32    End
    276   )
    277 {
    278   if (End < Begin) {
    279     return (0);
    280   }
    281 
    282   return (End - Begin) * Private->TimerPeriod;
    283 
    284 }
    285 
    286 /**
    287   Destroy IPING6_ICMP6_TX_INFO, and recollect the memory.
    288 
    289   @param[in]    TxInfo    The pointer to PING6_ICMP6_TX_INFO.
    290 
    291 **/
    292 VOID
    293 Ping6DestroyTxInfo (
    294   IN PING6_ICMP6_TX_INFO    *TxInfo
    295   )
    296 {
    297   EFI_IP6_TRANSMIT_DATA    *TxData;
    298   EFI_IP6_FRAGMENT_DATA    *FragData;
    299   UINTN                    Index;
    300 
    301   ASSERT (TxInfo != NULL);
    302 
    303   if (TxInfo->Token != NULL) {
    304 
    305     if (TxInfo->Token->Event != NULL) {
    306       gBS->CloseEvent (TxInfo->Token->Event);
    307     }
    308 
    309     TxData = TxInfo->Token->Packet.TxData;
    310     if (TxData != NULL) {
    311 
    312       if (TxData->OverrideData != NULL) {
    313         FreePool (TxData->OverrideData);
    314       }
    315 
    316       if (TxData->ExtHdrs != NULL) {
    317         FreePool (TxData->ExtHdrs);
    318       }
    319 
    320       for (Index = 0; Index < TxData->FragmentCount; Index++) {
    321         FragData = TxData->FragmentTable[Index].FragmentBuffer;
    322         if (FragData != NULL) {
    323           FreePool (FragData);
    324         }
    325       }
    326     }
    327 
    328     FreePool (TxInfo->Token);
    329   }
    330 
    331   FreePool (TxInfo);
    332 }
    333 
    334 /**
    335   Match the request, and reply with SequenceNum/TimeStamp.
    336 
    337   @param[in]    Private    The pointer to PING6_PRIVATE_DATA.
    338   @param[in]    Packet     The pointer to ICMP6_ECHO_REQUEST_REPLY.
    339 
    340   @retval EFI_SUCCESS      The match is successful.
    341   @retval EFI_NOT_FOUND    The reply can't be matched with any request.
    342 
    343 **/
    344 EFI_STATUS
    345 Ping6OnMatchEchoReply (
    346   IN PING6_PRIVATE_DATA          *Private,
    347   IN ICMP6_ECHO_REQUEST_REPLY    *Packet
    348   )
    349 {
    350   PING6_ICMP6_TX_INFO    *TxInfo;
    351   LIST_ENTRY             *Entry;
    352   LIST_ENTRY             *NextEntry;
    353 
    354   NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) {
    355     TxInfo = BASE_CR (Entry, PING6_ICMP6_TX_INFO, Link);
    356 
    357     if ((TxInfo->SequenceNum == Packet->SequenceNum) && (TxInfo->TimeStamp == Packet->TimeStamp)) {
    358       Private->RxCount++;
    359       RemoveEntryList (&TxInfo->Link);
    360       Ping6DestroyTxInfo (TxInfo);
    361       return EFI_SUCCESS;
    362     }
    363   }
    364 
    365   return EFI_NOT_FOUND;
    366 }
    367 
    368 /**
    369   The original intention is to send a request.
    370   Currently, the application retransmits an icmp6 echo request packet
    371   per second in sendnumber times that is specified by the user.
    372   Because nothing can be done here, all things move to the timer rountine.
    373 
    374   @param[in]    Event      A EFI_EVENT type event.
    375   @param[in]    Context    The pointer to Context.
    376 
    377 **/
    378 VOID
    379 EFIAPI
    380 Ping6OnEchoRequestSent6 (
    381   IN EFI_EVENT    Event,
    382   IN VOID         *Context
    383   )
    384 {
    385 }
    386 
    387 /**
    388   receive reply, match and print reply infomation.
    389 
    390   @param[in]    Event      A EFI_EVENT type event.
    391   @param[in]    Context    The pointer to context.
    392 
    393 **/
    394 VOID
    395 EFIAPI
    396 Ping6OnEchoReplyReceived6 (
    397   IN EFI_EVENT    Event,
    398   IN VOID         *Context
    399   )
    400 {
    401   EFI_STATUS                  Status;
    402   PING6_PRIVATE_DATA          *Private;
    403   EFI_IP6_COMPLETION_TOKEN    *RxToken;
    404   EFI_IP6_RECEIVE_DATA        *RxData;
    405   ICMP6_ECHO_REQUEST_REPLY    *Reply;
    406   UINT32                      PayLoad;
    407   UINT32                      Rtt;
    408 
    409   Private = (PING6_PRIVATE_DATA *) Context;
    410 
    411   if (Private->Status == EFI_ABORTED) {
    412     return;
    413   }
    414 
    415   RxToken = &Private->RxToken;
    416   RxData  = RxToken->Packet.RxData;
    417   Reply   = RxData->FragmentTable[0].FragmentBuffer;
    418   PayLoad = RxData->DataLength;
    419 
    420   if (RxData->Header->NextHeader != IP6_ICMP) {
    421     goto ON_EXIT;
    422   }
    423 
    424   if (!IP6_IS_MULTICAST (&Private->DstAddress) &&
    425       !EFI_IP6_EQUAL (&RxData->Header->SourceAddress, &Private->DstAddress)) {
    426     goto ON_EXIT;
    427   }
    428 
    429   if ((Reply->Type != ICMP_V6_ECHO_REPLY) || (Reply->Code != 0)) {
    430     goto ON_EXIT;
    431   }
    432 
    433   if (PayLoad != Private->BufferSize) {
    434     goto ON_EXIT;
    435   }
    436   //
    437   // Check whether the reply matches the sent request before.
    438   //
    439   Status = Ping6OnMatchEchoReply (Private, Reply);
    440   if (EFI_ERROR(Status)) {
    441     goto ON_EXIT;
    442   }
    443   //
    444   // Display statistics on this icmp6 echo reply packet.
    445   //
    446   Rtt  = Ping6CalculateTick (Private, Reply->TimeStamp, Ping6ReadTime (Private));
    447 
    448   Private->RttSum += Rtt;
    449   Private->RttMin  = Private->RttMin > Rtt ? Rtt : Private->RttMin;
    450   Private->RttMax  = Private->RttMax < Rtt ? Rtt : Private->RttMax;
    451 
    452   ShellPrintHiiEx (
    453     -1,
    454     -1,
    455     NULL,
    456     STRING_TOKEN (STR_PING6_REPLY_INFO),
    457     gShellNetwork2HiiHandle,
    458     PayLoad,
    459     mIp6DstString,
    460     Reply->SequenceNum,
    461     RxData->Header->HopLimit,
    462     Rtt,
    463     Rtt + Private->TimerPeriod
    464     );
    465 
    466 ON_EXIT:
    467 
    468   if (Private->RxCount < Private->SendNum) {
    469     //
    470     // Continue to receive icmp6 echo reply packets.
    471     //
    472     RxToken->Status = EFI_ABORTED;
    473 
    474     Status = Private->Ip6->Receive (Private->Ip6, RxToken);
    475 
    476     if (EFI_ERROR (Status)) {
    477       Private->Status = EFI_ABORTED;
    478     }
    479   } else {
    480     //
    481     // All reply have already been received from the dest host.
    482     //
    483     Private->Status = EFI_SUCCESS;
    484   }
    485   //
    486   // Singal to recycle the each rxdata here, not at the end of process.
    487   //
    488   gBS->SignalEvent (RxData->RecycleSignal);
    489 }
    490 
    491 /**
    492   Initial EFI_IP6_COMPLETION_TOKEN.
    493 
    494   @param[in]    Private        The pointer of PING6_PRIVATE_DATA.
    495   @param[in]    TimeStamp      The TimeStamp of request.
    496   @param[in]    SequenceNum    The SequenceNum of request.
    497 
    498   @return The pointer of EFI_IP6_COMPLETION_TOKEN.
    499 
    500 **/
    501 EFI_IP6_COMPLETION_TOKEN *
    502 Ping6GenerateToken (
    503   IN PING6_PRIVATE_DATA    *Private,
    504   IN UINT32                TimeStamp,
    505   IN UINT16                SequenceNum
    506   )
    507 {
    508   EFI_STATUS                  Status;
    509   EFI_IP6_COMPLETION_TOKEN    *Token;
    510   EFI_IP6_TRANSMIT_DATA       *TxData;
    511   ICMP6_ECHO_REQUEST_REPLY    *Request;
    512 
    513   Request = AllocateZeroPool (Private->BufferSize);
    514 
    515   if (Request == NULL) {
    516     return NULL;
    517   }
    518   //
    519   // Assembly icmp6 echo request packet.
    520   //
    521   Request->Type        = ICMP_V6_ECHO_REQUEST;
    522   Request->Code        = 0;
    523   Request->SequenceNum = SequenceNum;
    524   Request->TimeStamp   = TimeStamp;
    525   Request->Identifier  = 0;
    526   //
    527   // Leave check sum to ip6 layer, since it has no idea of source address
    528   // selection.
    529   //
    530   Request->Checksum    = 0;
    531 
    532   TxData = AllocateZeroPool (sizeof (EFI_IP6_TRANSMIT_DATA));
    533 
    534   if (TxData == NULL) {
    535     FreePool (Request);
    536     return NULL;
    537   }
    538   //
    539   // Assembly ipv6 token for transmit.
    540   //
    541   TxData->OverrideData       = 0;
    542   TxData->ExtHdrsLength      = 0;
    543   TxData->ExtHdrs            = NULL;
    544   TxData->DataLength         = Private->BufferSize;
    545   TxData->FragmentCount      = 1;
    546   TxData->FragmentTable[0].FragmentBuffer = (VOID *) Request;
    547   TxData->FragmentTable[0].FragmentLength = Private->BufferSize;
    548 
    549   Token = AllocateZeroPool (sizeof (EFI_IP6_COMPLETION_TOKEN));
    550 
    551   if (Token == NULL) {
    552     FreePool (Request);
    553     FreePool (TxData);
    554     return NULL;
    555   }
    556 
    557   Token->Status         = EFI_ABORTED;
    558   Token->Packet.TxData  = TxData;
    559 
    560   Status = gBS->CreateEvent (
    561                   EVT_NOTIFY_SIGNAL,
    562                   TPL_CALLBACK,
    563                   Ping6OnEchoRequestSent6,
    564                   Private,
    565                   &Token->Event
    566                   );
    567 
    568   if (EFI_ERROR (Status)) {
    569     FreePool (Request);
    570     FreePool (TxData);
    571     FreePool (Token);
    572     return NULL;
    573   }
    574 
    575   return Token;
    576 }
    577 
    578 /**
    579   Transmit the EFI_IP6_COMPLETION_TOKEN.
    580 
    581   @param[in]    Private    The pointer of PING6_PRIVATE_DATA.
    582 
    583   @retval EFI_SUCCESS             Transmitted successfully.
    584   @retval EFI_OUT_OF_RESOURCES    No memory is available on the platform.
    585   @retval others                  Transmitted unsuccessfully.
    586 
    587 **/
    588 EFI_STATUS
    589 Ping6SendEchoRequest (
    590   IN PING6_PRIVATE_DATA    *Private
    591   )
    592 {
    593   EFI_STATUS             Status;
    594   PING6_ICMP6_TX_INFO    *TxInfo;
    595 
    596   TxInfo = AllocateZeroPool (sizeof (PING6_ICMP6_TX_INFO));
    597 
    598   if (TxInfo == NULL) {
    599     return EFI_OUT_OF_RESOURCES;
    600   }
    601 
    602   TxInfo->TimeStamp   = Ping6ReadTime (Private);
    603   TxInfo->SequenceNum = (UINT16) (Private->TxCount + 1);
    604 
    605   TxInfo->Token       = Ping6GenerateToken (
    606                           Private,
    607                           TxInfo->TimeStamp,
    608                           TxInfo->SequenceNum
    609                           );
    610 
    611   if (TxInfo->Token == NULL) {
    612     Ping6DestroyTxInfo (TxInfo);
    613     return EFI_OUT_OF_RESOURCES;
    614   }
    615 
    616   Status = Private->Ip6->Transmit (Private->Ip6, TxInfo->Token);
    617 
    618   if (EFI_ERROR (Status)) {
    619     Ping6DestroyTxInfo (TxInfo);
    620     return Status;
    621   }
    622 
    623   InsertTailList (&Private->TxList, &TxInfo->Link);
    624   Private->TxCount++;
    625 
    626   return EFI_SUCCESS;
    627 }
    628 
    629 /**
    630   Place a completion token into the receive packet queue to receive the echo reply.
    631 
    632   @param[in]    Private    The pointer of PING6_PRIVATE_DATA.
    633 
    634   @retval EFI_SUCCESS      Put the token into the receive packet queue successfully.
    635   @retval others           Put the token into the receive packet queue unsuccessfully.
    636 
    637 **/
    638 EFI_STATUS
    639 Ping6OnReceiveEchoReply (
    640   IN PING6_PRIVATE_DATA    *Private
    641   )
    642 {
    643   EFI_STATUS    Status;
    644 
    645   ZeroMem (&Private->RxToken, sizeof (EFI_IP6_COMPLETION_TOKEN));
    646 
    647   Status = gBS->CreateEvent (
    648                   EVT_NOTIFY_SIGNAL,
    649                   TPL_CALLBACK,
    650                   Ping6OnEchoReplyReceived6,
    651                   Private,
    652                   &Private->RxToken.Event
    653                   );
    654 
    655   if (EFI_ERROR (Status)) {
    656     return Status;
    657   }
    658 
    659   Private->RxToken.Status = EFI_NOT_READY;
    660 
    661   return Private->Ip6->Receive (Private->Ip6, &Private->RxToken);
    662 }
    663 
    664 /**
    665   Remove the timeout request from the list.
    666 
    667   @param[in]    Event    A EFI_EVENT type event.
    668   @param[in]    Context  The pointer to Context.
    669 
    670 **/
    671 VOID
    672 EFIAPI
    673 Ping6OnTimerRoutine6 (
    674   IN EFI_EVENT    Event,
    675   IN VOID         *Context
    676   )
    677 {
    678   EFI_STATUS             Status;
    679   PING6_PRIVATE_DATA     *Private;
    680   PING6_ICMP6_TX_INFO    *TxInfo;
    681   LIST_ENTRY             *Entry;
    682   LIST_ENTRY             *NextEntry;
    683   UINT64                 Time;
    684 
    685   Private = (PING6_PRIVATE_DATA *) Context;
    686 
    687   //
    688   // Retransmit icmp6 echo request packets per second in sendnumber times.
    689   //
    690   if (Private->TxCount < Private->SendNum) {
    691 
    692     Status = Ping6SendEchoRequest (Private);
    693     if (Private->TxCount != 0){
    694       if (EFI_ERROR (Status)) {
    695         ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_SEND_REQUEST), gShellNetwork2HiiHandle, Private->TxCount + 1);
    696       }
    697     }
    698   }
    699   //
    700   // Check whether any icmp6 echo request in the list timeout.
    701   //
    702   NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) {
    703     TxInfo = BASE_CR (Entry, PING6_ICMP6_TX_INFO, Link);
    704     Time   = Ping6CalculateTick (Private, TxInfo->TimeStamp, Ping6ReadTime (Private));
    705 
    706     //
    707     // Remove the timeout echo request from txlist.
    708     //
    709     if (Time > PING6_DEFAULT_TIMEOUT) {
    710 
    711       if (EFI_ERROR (TxInfo->Token->Status)) {
    712         Private->Ip6->Cancel (Private->Ip6, TxInfo->Token);
    713       }
    714       //
    715       // Remove the timeout icmp6 echo request from list.
    716       //
    717       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_TIMEOUT), gShellNetwork2HiiHandle, TxInfo->SequenceNum);
    718 
    719       RemoveEntryList (&TxInfo->Link);
    720       Ping6DestroyTxInfo (TxInfo);
    721 
    722       if (IsListEmpty (&Private->TxList) && (Private->TxCount == Private->SendNum)) {
    723         //
    724         // All the left icmp6 echo request in the list timeout.
    725         //
    726         Private->Status = EFI_TIMEOUT;
    727       }
    728     }
    729   }
    730 }
    731 
    732 /**
    733   Create a valid IP6 instance.
    734 
    735   @param[in]    Private    The pointer of PING6_PRIVATE_DATA.
    736 
    737   @retval EFI_SUCCESS              Create a valid IP6 instance successfully.
    738   @retval EFI_ABORTED              Locate handle with ip6 service binding protocol unsuccessfully.
    739   @retval EFI_INVALID_PARAMETER    The source address is unspecified when the destination address is a link -ocal address.
    740   @retval EFI_OUT_OF_RESOURCES     No memory is available on the platform.
    741   @retval EFI_NOT_FOUND            The source address is not found.
    742 **/
    743 EFI_STATUS
    744 Ping6CreateIpInstance (
    745   IN  PING6_PRIVATE_DATA    *Private
    746   )
    747 {
    748   EFI_STATUS                       Status;
    749   UINTN                            HandleIndex;
    750   UINTN                            HandleNum;
    751   EFI_HANDLE                       *HandleBuffer;
    752   BOOLEAN                          UnspecifiedSrc;
    753   BOOLEAN                          MediaPresent;
    754   EFI_SERVICE_BINDING_PROTOCOL     *Ip6Sb;
    755   EFI_IP6_CONFIG_PROTOCOL          *Ip6Cfg;
    756   EFI_IP6_CONFIG_DATA              Ip6Config;
    757   EFI_IP6_CONFIG_INTERFACE_INFO    *IfInfo;
    758   UINTN                            IfInfoSize;
    759   EFI_IPv6_ADDRESS                 *Addr;
    760   UINTN                            AddrIndex;
    761 
    762   HandleBuffer      = NULL;
    763   UnspecifiedSrc    = FALSE;
    764   MediaPresent      = TRUE;
    765   Ip6Sb             = NULL;
    766   IfInfo            = NULL;
    767   IfInfoSize        = 0;
    768 
    769   //
    770   // Locate all the handles with ip6 service binding protocol.
    771   //
    772   Status = gBS->LocateHandleBuffer (
    773                   ByProtocol,
    774                   &gEfiIp6ServiceBindingProtocolGuid,
    775                   NULL,
    776                   &HandleNum,
    777                   &HandleBuffer
    778                   );
    779   if (EFI_ERROR (Status) || (HandleNum == 0)) {
    780     return EFI_ABORTED;
    781   }
    782 
    783   if (NetIp6IsUnspecifiedAddr (&Private->SrcAddress)) {
    784     //
    785     // SrcAddress is unspecified. So, both connected and configured interface will be automatic selected.
    786     //
    787     UnspecifiedSrc = TRUE;
    788   }
    789 
    790   //
    791   // Source address is required when pinging a link-local address.
    792   //
    793   if (NetIp6IsLinkLocalAddr (&Private->DstAddress) && UnspecifiedSrc) {
    794     ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_SOURCE), gShellNetwork2HiiHandle);
    795     Status = EFI_INVALID_PARAMETER;
    796     goto ON_ERROR;
    797   }
    798 
    799   //
    800   // For each ip6 protocol, check interface addresses list.
    801   //
    802   for (HandleIndex = 0; HandleIndex < HandleNum; HandleIndex++) {
    803 
    804     Ip6Sb      = NULL;
    805     IfInfo     = NULL;
    806     IfInfoSize = 0;
    807 
    808     if (UnspecifiedSrc) {
    809       //
    810       // Check media.
    811       //
    812       NetLibDetectMedia (HandleBuffer[HandleIndex], &MediaPresent);
    813       if (!MediaPresent) {
    814         //
    815         // Skip this one.
    816         //
    817         continue;
    818       }
    819     }
    820 
    821     Status = gBS->HandleProtocol (
    822                     HandleBuffer[HandleIndex],
    823                     &gEfiIp6ServiceBindingProtocolGuid,
    824                     (VOID **) &Ip6Sb
    825                     );
    826     if (EFI_ERROR (Status)) {
    827       goto ON_ERROR;
    828     }
    829 
    830     //
    831     // Ip6config protocol and ip6 service binding protocol are installed
    832     // on the same handle.
    833     //
    834     Status = gBS->HandleProtocol (
    835                     HandleBuffer[HandleIndex],
    836                     &gEfiIp6ConfigProtocolGuid,
    837                     (VOID **) &Ip6Cfg
    838                     );
    839 
    840     if (EFI_ERROR (Status)) {
    841       goto ON_ERROR;
    842     }
    843     //
    844     // Get the interface information size.
    845     //
    846     Status = Ip6Cfg->GetData (
    847                        Ip6Cfg,
    848                        Ip6ConfigDataTypeInterfaceInfo,
    849                        &IfInfoSize,
    850                        NULL
    851                        );
    852 
    853     if (Status != EFI_BUFFER_TOO_SMALL) {
    854       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6CFG_GETDATA), gShellNetwork2HiiHandle, Status);
    855       goto ON_ERROR;
    856     }
    857 
    858     IfInfo = AllocateZeroPool (IfInfoSize);
    859 
    860     if (IfInfo == NULL) {
    861       Status = EFI_OUT_OF_RESOURCES;
    862       goto ON_ERROR;
    863     }
    864     //
    865     // Get the interface info.
    866     //
    867     Status = Ip6Cfg->GetData (
    868                        Ip6Cfg,
    869                        Ip6ConfigDataTypeInterfaceInfo,
    870                        &IfInfoSize,
    871                        IfInfo
    872                        );
    873 
    874     if (EFI_ERROR (Status)) {
    875       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6CFG_GETDATA), gShellNetwork2HiiHandle, Status);
    876       goto ON_ERROR;
    877     }
    878     //
    879     // Check whether the source address is one of the interface addresses.
    880     //
    881     for (AddrIndex = 0; AddrIndex < IfInfo->AddressInfoCount; AddrIndex++) {
    882       Addr = &(IfInfo->AddressInfo[AddrIndex].Address);
    883 
    884       if (UnspecifiedSrc) {
    885         if (!NetIp6IsUnspecifiedAddr (Addr) && !NetIp6IsLinkLocalAddr (Addr)) {
    886           //
    887           // Select the interface automatically.
    888           //
    889           CopyMem(&Private->SrcAddress, Addr, sizeof(Private->SrcAddress));
    890           break;
    891         }
    892       } else if (EFI_IP6_EQUAL (&Private->SrcAddress, Addr)) {
    893         //
    894         // Match a certain interface address.
    895         //
    896         break;
    897       }
    898     }
    899 
    900     if (AddrIndex < IfInfo->AddressInfoCount) {
    901       //
    902       // Found a nic handle with right interface address.
    903       //
    904       break;
    905     }
    906 
    907     FreePool (IfInfo);
    908     IfInfo = NULL;
    909   }
    910   //
    911   // No exact interface address matched.
    912   //
    913 
    914   if (HandleIndex == HandleNum) {
    915     ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_CONFIGD_NIC_NF), gShellNetwork2HiiHandle);
    916     Status = EFI_NOT_FOUND;
    917     goto ON_ERROR;
    918   }
    919 
    920   Private->NicHandle = HandleBuffer[HandleIndex];
    921 
    922   ASSERT (Ip6Sb != NULL);
    923   Status = Ip6Sb->CreateChild (Ip6Sb, &Private->Ip6ChildHandle);
    924 
    925   if (EFI_ERROR (Status)) {
    926     goto ON_ERROR;
    927   }
    928 
    929   Status = gBS->OpenProtocol (
    930                   Private->Ip6ChildHandle,
    931                   &gEfiIp6ProtocolGuid,
    932                   (VOID **) &Private->Ip6,
    933                   Private->ImageHandle,
    934                   Private->Ip6ChildHandle,
    935                   EFI_OPEN_PROTOCOL_GET_PROTOCOL
    936                   );
    937   if (EFI_ERROR (Status)) {
    938     goto ON_ERROR;
    939   }
    940 
    941   ZeroMem (&Ip6Config, sizeof (EFI_IP6_CONFIG_DATA));
    942 
    943   //
    944   // Configure the ip6 instance for icmp6 packet exchange.
    945   //
    946   Ip6Config.DefaultProtocol   = 58;
    947   Ip6Config.AcceptAnyProtocol = FALSE;
    948   Ip6Config.AcceptIcmpErrors  = TRUE;
    949   Ip6Config.AcceptPromiscuous = FALSE;
    950   Ip6Config.TrafficClass      = 0;
    951   Ip6Config.HopLimit          = 128;
    952   Ip6Config.FlowLabel         = 0;
    953   Ip6Config.ReceiveTimeout    = 0;
    954   Ip6Config.TransmitTimeout   = 0;
    955 
    956   IP6_COPY_ADDRESS (&Ip6Config.StationAddress, &Private->SrcAddress);
    957 
    958   IP6_COPY_ADDRESS (&Ip6Config.DestinationAddress, &Private->DstAddress);
    959 
    960   Status = Private->Ip6->Configure (Private->Ip6, &Ip6Config);
    961 
    962   if (EFI_ERROR (Status)) {
    963     ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6_CONFIG), gShellNetwork2HiiHandle, Status);
    964     goto ON_ERROR;
    965   }
    966 
    967   return EFI_SUCCESS;
    968 
    969 ON_ERROR:
    970   if (HandleBuffer != NULL) {
    971     FreePool (HandleBuffer);
    972   }
    973 
    974   if (IfInfo != NULL) {
    975     FreePool (IfInfo);
    976   }
    977 
    978   if ((Ip6Sb != NULL) && (Private->Ip6ChildHandle != NULL)) {
    979     Ip6Sb->DestroyChild (Ip6Sb, Private->Ip6ChildHandle);
    980   }
    981 
    982   return Status;
    983 }
    984 
    985 /**
    986   Destroy the IP6 instance.
    987 
    988   @param[in]    Private    The pointer of PING6_PRIVATE_DATA.
    989 
    990 **/
    991 VOID
    992 Ping6DestroyIpInstance (
    993   IN PING6_PRIVATE_DATA    *Private
    994   )
    995 {
    996   EFI_STATUS                      Status;
    997   EFI_SERVICE_BINDING_PROTOCOL    *Ip6Sb;
    998 
    999   gBS->CloseProtocol (
   1000          Private->Ip6ChildHandle,
   1001          &gEfiIp6ProtocolGuid,
   1002          Private->ImageHandle,
   1003          Private->Ip6ChildHandle
   1004          );
   1005 
   1006   Status = gBS->HandleProtocol (
   1007                   Private->NicHandle,
   1008                   &gEfiIp6ServiceBindingProtocolGuid,
   1009                   (VOID **) &Ip6Sb
   1010                   );
   1011 
   1012   if (!EFI_ERROR(Status)) {
   1013     Ip6Sb->DestroyChild (Ip6Sb, Private->Ip6ChildHandle);
   1014   }
   1015 }
   1016 
   1017 /**
   1018   The Ping6 Process.
   1019 
   1020   @param[in]   ImageHandle    The firmware allocated handle for the UEFI image.
   1021   @param[in]   SendNumber     The send request count.
   1022   @param[in]   BufferSize     The send buffer size.
   1023   @param[in]   SrcAddress     The source IPv6 address.
   1024   @param[in]   DstAddress     The destination IPv6 address.
   1025 
   1026   @retval SHELL_SUCCESS    The ping6 processed successfullly.
   1027   @retval others           The ping6 processed unsuccessfully.
   1028 
   1029 **/
   1030 SHELL_STATUS
   1031 ShellPing6 (
   1032   IN EFI_HANDLE          ImageHandle,
   1033   IN UINT32              SendNumber,
   1034   IN UINT32              BufferSize,
   1035   IN EFI_IPv6_ADDRESS    *SrcAddress,
   1036   IN EFI_IPv6_ADDRESS    *DstAddress
   1037   )
   1038 {
   1039   EFI_STATUS             Status;
   1040   EFI_INPUT_KEY          Key;
   1041   PING6_PRIVATE_DATA     *Private;
   1042   PING6_ICMP6_TX_INFO    *TxInfo;
   1043   LIST_ENTRY             *Entry;
   1044   LIST_ENTRY             *NextEntry;
   1045   SHELL_STATUS           ShellStatus;
   1046 
   1047   ShellStatus = SHELL_SUCCESS;
   1048   Private = AllocateZeroPool (sizeof (PING6_PRIVATE_DATA));
   1049 
   1050   if (Private == NULL) {
   1051     ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_OUT_MEM), gShellNetwork2HiiHandle, L"Ping6");
   1052     ShellStatus = SHELL_OUT_OF_RESOURCES;
   1053     goto ON_EXIT;
   1054   }
   1055 
   1056   Private->ImageHandle = ImageHandle;
   1057   Private->SendNum     = SendNumber;
   1058   Private->BufferSize  = BufferSize;
   1059   Private->RttMin      = ~((UINT64 )(0x0));
   1060   Private->Status      = EFI_NOT_READY;
   1061 
   1062   InitializeListHead (&Private->TxList);
   1063 
   1064   IP6_COPY_ADDRESS (&Private->SrcAddress, SrcAddress);
   1065   IP6_COPY_ADDRESS (&Private->DstAddress, DstAddress);
   1066 
   1067   //
   1068   // Open and configure a ip6 instance for ping6.
   1069   //
   1070   Status = Ping6CreateIpInstance (Private);
   1071 
   1072   if (EFI_ERROR (Status)) {
   1073     ShellStatus = SHELL_ACCESS_DENIED;
   1074     goto ON_EXIT;
   1075   }
   1076   //
   1077   // Print the command line itself.
   1078   //
   1079   ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_START), gShellNetwork2HiiHandle, mIp6DstString, Private->BufferSize);
   1080   //
   1081   // Create a ipv6 token to receive the first icmp6 echo reply packet.
   1082   //
   1083   Status = Ping6OnReceiveEchoReply (Private);
   1084 
   1085   if (EFI_ERROR (Status)) {
   1086     ShellStatus = SHELL_ACCESS_DENIED;
   1087     goto ON_EXIT;
   1088   }
   1089   //
   1090   // Create and start timer to send icmp6 echo request packet per second.
   1091   //
   1092   Status = gBS->CreateEvent (
   1093                   EVT_TIMER | EVT_NOTIFY_SIGNAL,
   1094                   TPL_CALLBACK,
   1095                   Ping6OnTimerRoutine6,
   1096                   Private,
   1097                   &Private->Timer
   1098                   );
   1099 
   1100   if (EFI_ERROR (Status)) {
   1101     ShellStatus = SHELL_ACCESS_DENIED;
   1102     goto ON_EXIT;
   1103   }
   1104 
   1105   //
   1106   // Start a timer to calculate the RTT.
   1107   //
   1108   Status = Ping6InitRttTimer (Private);
   1109   if (EFI_ERROR (Status)) {
   1110     ShellStatus = SHELL_ACCESS_DENIED;
   1111     goto ON_EXIT;
   1112   }
   1113 
   1114   //
   1115   // Create a ipv6 token to send the first icmp6 echo request packet.
   1116   //
   1117   Status = Ping6SendEchoRequest (Private);
   1118   //
   1119   // EFI_NOT_READY for IPsec is enable and IKE is not established.
   1120   //
   1121   if (EFI_ERROR (Status) && (Status != EFI_NOT_READY)) {
   1122     ShellStatus = SHELL_ACCESS_DENIED;
   1123     if(Status == EFI_NOT_FOUND) {
   1124       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_NOSOURCE_INDOMAIN), gShellNetwork2HiiHandle, mIp6DstString);
   1125     }
   1126 
   1127     goto ON_EXIT;
   1128   }
   1129 
   1130   Status = gBS->SetTimer (
   1131                   Private->Timer,
   1132                   TimerPeriodic,
   1133                   PING6_ONE_SECOND
   1134                   );
   1135 
   1136   if (EFI_ERROR (Status)) {
   1137     ShellStatus = SHELL_ACCESS_DENIED;
   1138     goto ON_EXIT;
   1139   }
   1140   //
   1141   // Control the ping6 process by two factors:
   1142   // 1. Hot key
   1143   // 2. Private->Status
   1144   //   2.1. success means all icmp6 echo request packets get reply packets.
   1145   //   2.2. timeout means the last icmp6 echo reply request timeout to get reply.
   1146   //   2.3. noready means ping6 process is on-the-go.
   1147   //
   1148   while (Private->Status == EFI_NOT_READY) {
   1149     Private->Ip6->Poll (Private->Ip6);
   1150 
   1151     //
   1152     // Terminate the ping6 process by 'esc' or 'ctl-c'.
   1153     //
   1154     Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
   1155 
   1156     if (!EFI_ERROR(Status)) {
   1157       if ((Key.UnicodeChar == 0x1b) || (Key.UnicodeChar == 0x03) ||
   1158          ((Key.UnicodeChar == 0) && (Key.ScanCode == SCAN_ESC))) {
   1159         goto ON_STAT;
   1160       }
   1161     }
   1162   }
   1163 
   1164 ON_STAT:
   1165   //
   1166   // Display the statistics in all.
   1167   //
   1168   gBS->SetTimer (Private->Timer, TimerCancel, 0);
   1169 
   1170   if (Private->TxCount != 0) {
   1171     ShellPrintHiiEx (
   1172       -1,
   1173       -1,
   1174       NULL,
   1175       STRING_TOKEN (STR_PING6_STAT),
   1176       gShellNetwork2HiiHandle,
   1177       Private->TxCount,
   1178       Private->RxCount,
   1179       (100 * (Private->TxCount - Private->RxCount)) / Private->TxCount,
   1180       Private->RttSum
   1181       );
   1182   }
   1183 
   1184   if (Private->RxCount != 0) {
   1185     ShellPrintHiiEx (
   1186       -1,
   1187       -1,
   1188       NULL,
   1189       STRING_TOKEN (STR_PING6_RTT),
   1190       gShellNetwork2HiiHandle,
   1191       Private->RttMin,
   1192       Private->RttMin + Private->TimerPeriod,
   1193       Private->RttMax,
   1194       Private->RttMax + Private->TimerPeriod,
   1195       DivU64x64Remainder (Private->RttSum, Private->RxCount, NULL),
   1196       DivU64x64Remainder (Private->RttSum, Private->RxCount, NULL) + Private->TimerPeriod
   1197       );
   1198   }
   1199 
   1200 ON_EXIT:
   1201 
   1202   if (Private != NULL) {
   1203     Private->Status = EFI_ABORTED;
   1204 
   1205     NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) {
   1206       TxInfo = BASE_CR (Entry, PING6_ICMP6_TX_INFO, Link);
   1207 
   1208       Status = Private->Ip6->Cancel (Private->Ip6, TxInfo->Token);
   1209 
   1210       RemoveEntryList (&TxInfo->Link);
   1211       Ping6DestroyTxInfo (TxInfo);
   1212     }
   1213 
   1214     Ping6FreeRttTimer (Private);
   1215 
   1216     if (Private->Timer != NULL) {
   1217       gBS->CloseEvent (Private->Timer);
   1218     }
   1219 
   1220     if (Private->Ip6 != NULL) {
   1221       Status = Private->Ip6->Cancel (Private->Ip6, &Private->RxToken);
   1222     }
   1223 
   1224     if (Private->RxToken.Event != NULL) {
   1225       gBS->CloseEvent (Private->RxToken.Event);
   1226     }
   1227 
   1228     if (Private->Ip6ChildHandle != NULL) {
   1229       Ping6DestroyIpInstance (Private);
   1230     }
   1231 
   1232     FreePool (Private);
   1233   }
   1234 
   1235   return ShellStatus;
   1236 }
   1237 
   1238 /**
   1239   Function for 'ping6' command.
   1240 
   1241   @param[in] ImageHandle  Handle to the Image (NULL if Internal).
   1242   @param[in] SystemTable  Pointer to the System Table (NULL if Internal).
   1243 
   1244   @retval SHELL_SUCCESS  The ping6 processed successfullly.
   1245   @retval others         The ping6 processed unsuccessfully.
   1246 
   1247 **/
   1248 SHELL_STATUS
   1249 EFIAPI
   1250 ShellCommandRunPing6 (
   1251   IN EFI_HANDLE        ImageHandle,
   1252   IN EFI_SYSTEM_TABLE  *SystemTable
   1253   )
   1254 {
   1255   EFI_STATUS          Status;
   1256   SHELL_STATUS        ShellStatus;
   1257   EFI_IPv6_ADDRESS    DstAddress;
   1258   EFI_IPv6_ADDRESS    SrcAddress;
   1259   UINT64              BufferSize;
   1260   UINTN               SendNumber;
   1261   LIST_ENTRY          *ParamPackage;
   1262   CONST CHAR16        *ValueStr;
   1263   CONST CHAR16        *ValueStrPtr;
   1264   UINTN               NonOptionCount;
   1265   CHAR16              *ProblemParam;
   1266 
   1267   ProblemParam = NULL;
   1268   ShellStatus = SHELL_SUCCESS;
   1269 
   1270   Status = ShellCommandLineParseEx (Ping6ParamList, &ParamPackage, &ProblemParam, TRUE, FALSE);
   1271   if (EFI_ERROR(Status)) {
   1272     ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_INPUT), gShellNetwork2HiiHandle);
   1273     ShellStatus = SHELL_INVALID_PARAMETER;
   1274     goto ON_EXIT;
   1275   }
   1276 
   1277   SendNumber = 10;
   1278   BufferSize = 16;
   1279 
   1280   //
   1281   // Parse the parameter of count number.
   1282   //
   1283   ValueStr = ShellCommandLineGetValue (ParamPackage, L"-n");
   1284   ValueStrPtr = ValueStr;
   1285   if (ValueStr != NULL) {
   1286     SendNumber = ShellStrToUintn (ValueStrPtr);
   1287 
   1288     //
   1289     // ShellStrToUintn will return 0 when input is 0 or an invalid input string.
   1290     //
   1291     if ((SendNumber == 0) || (SendNumber > PING6_MAX_SEND_NUMBER)) {
   1292       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_SEND_NUMBER), gShellNetwork2HiiHandle, ValueStr);
   1293       ShellStatus = SHELL_INVALID_PARAMETER;
   1294       goto ON_EXIT;
   1295     }
   1296   }
   1297   //
   1298   // Parse the parameter of buffer size.
   1299   //
   1300   ValueStr = ShellCommandLineGetValue (ParamPackage, L"-l");
   1301   ValueStrPtr = ValueStr;
   1302   if (ValueStr != NULL) {
   1303     BufferSize = ShellStrToUintn (ValueStrPtr);
   1304 
   1305     //
   1306     // ShellStrToUintn will return 0 when input is 0 or an invalid input string.
   1307     //
   1308     if ((BufferSize < 16) || (BufferSize > PING6_MAX_BUFFER_SIZE)) {
   1309       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_BUFFER_SIZE), gShellNetwork2HiiHandle, ValueStr);
   1310       ShellStatus = SHELL_INVALID_PARAMETER;
   1311       goto ON_EXIT;
   1312     }
   1313   }
   1314 
   1315   ZeroMem (&SrcAddress, sizeof (EFI_IPv6_ADDRESS));
   1316   ZeroMem (&DstAddress, sizeof (EFI_IPv6_ADDRESS));
   1317 
   1318   //
   1319   // Parse the parameter of source ip address.
   1320   //
   1321   ValueStr = ShellCommandLineGetValue (ParamPackage, L"-s");
   1322   ValueStrPtr = ValueStr;
   1323   if (ValueStr != NULL) {
   1324     mIp6SrcString = ValueStr;
   1325     Status = NetLibStrToIp6 (ValueStrPtr, &SrcAddress);
   1326     if (EFI_ERROR (Status)) {
   1327       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_IP), gShellNetwork2HiiHandle, ValueStr);
   1328       ShellStatus = SHELL_INVALID_PARAMETER;
   1329       goto ON_EXIT;
   1330     }
   1331   }
   1332   //
   1333   // Parse the parameter of destination ip address.
   1334   //
   1335   NonOptionCount = ShellCommandLineGetCount(ParamPackage);
   1336   ValueStr = ShellCommandLineGetRawValue (ParamPackage, (UINT32)(NonOptionCount-1));
   1337   if (NonOptionCount != 2) {
   1338     ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_INPUT), gShellNetwork2HiiHandle);
   1339     ShellStatus = SHELL_INVALID_PARAMETER;
   1340     goto ON_EXIT;
   1341   }
   1342   ValueStrPtr = ValueStr;
   1343   if (ValueStr != NULL) {
   1344     mIp6DstString = ValueStr;
   1345     Status = NetLibStrToIp6 (ValueStrPtr, &DstAddress);
   1346     if (EFI_ERROR (Status)) {
   1347       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_IP), gShellNetwork2HiiHandle, ValueStr);
   1348       ShellStatus = SHELL_INVALID_PARAMETER;
   1349       goto ON_EXIT;
   1350     }
   1351   }
   1352 
   1353   //
   1354   // Enter into ping6 process.
   1355   //
   1356   ShellStatus = ShellPing6 (
   1357               ImageHandle,
   1358               (UINT32)SendNumber,
   1359               (UINT32)BufferSize,
   1360               &SrcAddress,
   1361               &DstAddress
   1362               );
   1363 
   1364 ON_EXIT:
   1365   ShellCommandLineFreeVarList (ParamPackage);
   1366   return ShellStatus;
   1367 }
   1368 
   1369