]> git.proxmox.com Git - mirror_edk2.git/blobdiff - ShellPkg/Library/UefiShellNetwork2CommandsLib/Ping6.c
ShellPkg: Merge Ping6 and Ifconfig6 tools to Shell command.
[mirror_edk2.git] / ShellPkg / Library / UefiShellNetwork2CommandsLib / Ping6.c
diff --git a/ShellPkg/Library/UefiShellNetwork2CommandsLib/Ping6.c b/ShellPkg/Library/UefiShellNetwork2CommandsLib/Ping6.c
new file mode 100644 (file)
index 0000000..af7d08f
--- /dev/null
@@ -0,0 +1,1247 @@
+/** @file\r
+  The implementation for Ping6 application.\r
+\r
+  Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>\r
+\r
+  This program and the accompanying materials\r
+  are licensed and made available under the terms and conditions of the BSD License\r
+  which accompanies this distribution.  The full text of the license may be found at\r
+  http://opensource.org/licenses/bsd-license.php.\r
+\r
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "UefiShellNetwork2CommandsLib.h"\r
+\r
+#define PING6_DEFAULT_TIMEOUT      5000\r
+#define PING6_MAX_SEND_NUMBER      10000\r
+#define PING6_MAX_BUFFER_SIZE      32768\r
+#define PING6_ONE_SECOND           10000000\r
+\r
+//\r
+// A similar amount of time that passes in femtoseconds\r
+// for each increment of TimerValue. It is for NT32 only.\r
+//\r
+#define NTTIMERPERIOD    358049\r
+\r
+#pragma pack(1)\r
+\r
+typedef struct _ICMP6_ECHO_REQUEST_REPLY {\r
+  UINT8                       Type;\r
+  UINT8                       Code;\r
+  UINT16                      Checksum;\r
+  UINT16                      Identifier;\r
+  UINT16                      SequenceNum;\r
+  UINT64                      TimeStamp;\r
+  UINT8                       Data[1];\r
+} ICMP6_ECHO_REQUEST_REPLY;\r
+\r
+#pragma pack()\r
+\r
+typedef struct _PING6_ICMP6_TX_INFO {\r
+  LIST_ENTRY                  Link;\r
+  UINT16                      SequenceNum;\r
+  UINT64                      TimeStamp;\r
+  EFI_IP6_COMPLETION_TOKEN    *Token;\r
+} PING6_ICMP6_TX_INFO;\r
+\r
+typedef struct _PING6_PRIVATE_DATA {\r
+  EFI_HANDLE                  ImageHandle;\r
+  EFI_HANDLE                  NicHandle;\r
+  EFI_HANDLE                  Ip6ChildHandle;\r
+  EFI_IP6_PROTOCOL            *Ip6;\r
+  EFI_EVENT                   Timer;\r
+\r
+  EFI_STATUS                  Status;\r
+  LIST_ENTRY                  TxList;\r
+  EFI_IP6_COMPLETION_TOKEN    RxToken;\r
+  UINT16                      RxCount;\r
+  UINT16                      TxCount;\r
+  UINT64                      RttSum;\r
+  UINT64                      RttMin;\r
+  UINT64                      RttMax;\r
+  UINT32                      SequenceNum;\r
+\r
+  EFI_IPv6_ADDRESS            SrcAddress;\r
+  EFI_IPv6_ADDRESS            DstAddress;\r
+  UINT32                      SendNum;\r
+  UINT32                      BufferSize;\r
+} PING6_PRIVATE_DATA;\r
+\r
+\r
+SHELL_PARAM_ITEM    Ping6ParamList[] = {\r
+  {\r
+    L"-l",\r
+    TypeValue\r
+  },\r
+  {\r
+    L"-n",\r
+    TypeValue\r
+  },\r
+  {\r
+    L"-s",\r
+    TypeValue\r
+  },\r
+  {\r
+    L"-?",\r
+    TypeFlag\r
+  },\r
+  {\r
+    NULL,\r
+    TypeMax\r
+  },\r
+};\r
+\r
+//\r
+// Global Variables in Ping6 application.\r
+//\r
+CONST CHAR16            *mIp6DstString;\r
+CONST CHAR16            *mIp6SrcString;\r
+UINT64                  mFrequency = 0;\r
+UINT64                  mIp6CurrentTick = 0;\r
+EFI_CPU_ARCH_PROTOCOL   *Cpu = NULL;\r
+\r
+\r
+\r
+/**\r
+  Reads and returns the current value of the Time.\r
+\r
+  @return The current tick value.\r
+\r
+**/\r
+UINT64\r
+Ping6ReadTime ()\r
+{\r
+  UINT64                 TimerPeriod;\r
+  EFI_STATUS             Status;\r
+\r
+  ASSERT (Cpu != NULL);\r
+\r
+  Status = Cpu->GetTimerValue (Cpu, 0, &mIp6CurrentTick, &TimerPeriod);\r
+  if (EFI_ERROR (Status)) {\r
+    //\r
+    // The WinntGetTimerValue will return EFI_UNSUPPORTED. Set the\r
+    // TimerPeriod by ourselves.\r
+    //\r
+    mIp6CurrentTick += 1000000;\r
+  }\r
+\r
+  return mIp6CurrentTick;\r
+}\r
+\r
+/**\r
+  Get and calculate the frequency in tick/ms.\r
+  The result is saved in the globle variable mFrequency\r
+\r
+  @retval EFI_SUCCESS    Calculated the frequency successfully.\r
+  @retval Others         Failed to calculate the frequency.\r
+\r
+**/\r
+EFI_STATUS\r
+Ping6GetFrequency (\r
+  VOID\r
+  )\r
+{\r
+  EFI_STATUS               Status;\r
+  UINT64                   CurrentTick;\r
+  UINT64                   TimerPeriod;\r
+\r
+  Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **) &Cpu);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Status = Cpu->GetTimerValue (Cpu, 0, &CurrentTick, &TimerPeriod);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    //\r
+    // For NT32 Simulator only. 358049 is a similar value to keep timer granularity.\r
+    // Set the timer period by ourselves.\r
+    //\r
+    TimerPeriod = (UINT64) NTTIMERPERIOD;\r
+  }\r
+  //\r
+  // The timer period is in femtosecond (1 femtosecond is 1e-15 second).\r
+  // So 1e+12 is divided by timer period to produce the freq in tick/ms.\r
+  //\r
+  mFrequency = DivU64x64Remainder (1000000000000ULL, TimerPeriod, NULL);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Get and calculate the duration in ms.\r
+\r
+  @param[in]  Begin    The start point of time.\r
+  @param[in]  End      The end point of time.\r
+\r
+  @return The duration in ms.\r
+\r
+**/\r
+UINT64\r
+Ping6CalculateTick (\r
+  IN UINT64    Begin,\r
+  IN UINT64    End\r
+  )\r
+{\r
+  ASSERT (End > Begin);\r
+  return DivU64x64Remainder (End - Begin, mFrequency, NULL);\r
+}\r
+\r
+/**\r
+  Destroy IPING6_ICMP6_TX_INFO, and recollect the memory.\r
+\r
+  @param[in]    TxInfo    The pointer to PING6_ICMP6_TX_INFO.\r
+\r
+**/\r
+VOID\r
+Ping6DestroyTxInfo (\r
+  IN PING6_ICMP6_TX_INFO    *TxInfo\r
+  )\r
+{\r
+  EFI_IP6_TRANSMIT_DATA    *TxData;\r
+  EFI_IP6_FRAGMENT_DATA    *FragData;\r
+  UINTN                    Index;\r
+\r
+  ASSERT (TxInfo != NULL);\r
+\r
+  if (TxInfo->Token != NULL) {\r
+\r
+    if (TxInfo->Token->Event != NULL) {\r
+      gBS->CloseEvent (TxInfo->Token->Event);\r
+    }\r
+\r
+    TxData = TxInfo->Token->Packet.TxData;\r
+    if (TxData != NULL) {\r
+\r
+      if (TxData->OverrideData != NULL) {\r
+        FreePool (TxData->OverrideData);\r
+      }\r
+\r
+      if (TxData->ExtHdrs != NULL) {\r
+        FreePool (TxData->ExtHdrs);\r
+      }\r
+\r
+      for (Index = 0; Index < TxData->FragmentCount; Index++) {\r
+        FragData = TxData->FragmentTable[Index].FragmentBuffer;\r
+        if (FragData != NULL) {\r
+          FreePool (FragData);\r
+        }\r
+      }\r
+    }\r
+\r
+    FreePool (TxInfo->Token);\r
+  }\r
+\r
+  FreePool (TxInfo);\r
+}\r
+\r
+/**\r
+  Match the request, and reply with SequenceNum/TimeStamp.\r
+\r
+  @param[in]    Private    The pointer to PING6_PRIVATE_DATA.\r
+  @param[in]    Packet     The pointer to ICMP6_ECHO_REQUEST_REPLY.\r
+\r
+  @retval EFI_SUCCESS      The match is successful.\r
+  @retval EFI_NOT_FOUND    The reply can't be matched with any request.\r
+\r
+**/\r
+EFI_STATUS\r
+Ping6OnMatchEchoReply (\r
+  IN PING6_PRIVATE_DATA          *Private,\r
+  IN ICMP6_ECHO_REQUEST_REPLY    *Packet\r
+  )\r
+{\r
+  PING6_ICMP6_TX_INFO    *TxInfo;\r
+  LIST_ENTRY             *Entry;\r
+  LIST_ENTRY             *NextEntry;\r
+\r
+  NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) {\r
+    TxInfo = BASE_CR (Entry, PING6_ICMP6_TX_INFO, Link);\r
+\r
+    if ((TxInfo->SequenceNum == Packet->SequenceNum) && (TxInfo->TimeStamp == Packet->TimeStamp)) {\r
+      Private->RxCount++;\r
+      RemoveEntryList (&TxInfo->Link);\r
+      Ping6DestroyTxInfo (TxInfo);\r
+      return EFI_SUCCESS;\r
+    }\r
+  }\r
+\r
+  return EFI_NOT_FOUND;\r
+}\r
+\r
+/**\r
+  The original intention is to send a request.\r
+  Currently, the application retransmits an icmp6 echo request packet\r
+  per second in sendnumber times that is specified by the user.\r
+  Because nothing can be done here, all things move to the timer rountine.\r
+\r
+  @param[in]    Event      A EFI_EVENT type event.\r
+  @param[in]    Context    The pointer to Context.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ping6OnEchoRequestSent6 (\r
+  IN EFI_EVENT    Event,\r
+  IN VOID         *Context\r
+  )\r
+{\r
+}\r
+\r
+/**\r
+  receive reply, match and print reply infomation.\r
+\r
+  @param[in]    Event      A EFI_EVENT type event.\r
+  @param[in]    Context    The pointer to context.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ping6OnEchoReplyReceived6 (\r
+  IN EFI_EVENT    Event,\r
+  IN VOID         *Context\r
+  )\r
+{\r
+  EFI_STATUS                  Status;\r
+  PING6_PRIVATE_DATA          *Private;\r
+  EFI_IP6_COMPLETION_TOKEN    *RxToken;\r
+  EFI_IP6_RECEIVE_DATA        *RxData;\r
+  ICMP6_ECHO_REQUEST_REPLY    *Reply;\r
+  UINT32                      PayLoad;\r
+  UINT64                      Rtt;\r
+  CHAR8                       Near;\r
+\r
+  Private = (PING6_PRIVATE_DATA *) Context;\r
+\r
+  if (Private->Status == EFI_ABORTED) {\r
+    return;\r
+  }\r
+\r
+  RxToken = &Private->RxToken;\r
+  RxData  = RxToken->Packet.RxData;\r
+  Reply   = RxData->FragmentTable[0].FragmentBuffer;\r
+  PayLoad = RxData->DataLength;\r
+\r
+  if (RxData->Header->NextHeader != IP6_ICMP) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  if (!IP6_IS_MULTICAST (&Private->DstAddress) &&\r
+      !EFI_IP6_EQUAL (&RxData->Header->SourceAddress, &Private->DstAddress)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  if ((Reply->Type != ICMP_V6_ECHO_REPLY) || (Reply->Code != 0)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  if (PayLoad != Private->BufferSize) {\r
+    goto ON_EXIT;\r
+  }\r
+  //\r
+  // Check whether the reply matches the sent request before.\r
+  //\r
+  Status = Ping6OnMatchEchoReply (Private, Reply);\r
+  if (EFI_ERROR(Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+  //\r
+  // Display statistics on this icmp6 echo reply packet.\r
+  //\r
+  Rtt  = Ping6CalculateTick (Reply->TimeStamp, Ping6ReadTime ());\r
+  if (Rtt != 0) {\r
+    Near = (CHAR8) '=';\r
+  } else {\r
+    Near = (CHAR8) '<';\r
+  }\r
+\r
+  Private->RttSum += Rtt;\r
+  Private->RttMin  = Private->RttMin > Rtt ? Rtt : Private->RttMin;\r
+  Private->RttMax  = Private->RttMax < Rtt ? Rtt : Private->RttMax;\r
+\r
+  ShellPrintHiiEx (\r
+    -1,\r
+    -1,\r
+    NULL,\r
+    STRING_TOKEN (STR_PING6_REPLY_INFO),\r
+    gShellNetwork2HiiHandle,\r
+    PayLoad,\r
+    mIp6DstString,\r
+    Reply->SequenceNum,\r
+    RxData->Header->HopLimit,\r
+    Near,\r
+    Rtt\r
+    );\r
+\r
+ON_EXIT:\r
+\r
+  if (Private->RxCount < Private->SendNum) {\r
+    //\r
+    // Continue to receive icmp6 echo reply packets.\r
+    //\r
+    RxToken->Status = EFI_ABORTED;\r
+\r
+    Status = Private->Ip6->Receive (Private->Ip6, RxToken);\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      Private->Status = EFI_ABORTED;\r
+    }\r
+  } else {\r
+    //\r
+    // All reply have already been received from the dest host.\r
+    //\r
+    Private->Status = EFI_SUCCESS;\r
+  }\r
+  //\r
+  // Singal to recycle the each rxdata here, not at the end of process.\r
+  //\r
+  gBS->SignalEvent (RxData->RecycleSignal);\r
+}\r
+\r
+/**\r
+  Initial EFI_IP6_COMPLETION_TOKEN.\r
+\r
+  @param[in]    Private        The pointer of PING6_PRIVATE_DATA.\r
+  @param[in]    TimeStamp      The TimeStamp of request.\r
+  @param[in]    SequenceNum    The SequenceNum of request.\r
+\r
+  @return The pointer of EFI_IP6_COMPLETION_TOKEN.\r
+\r
+**/\r
+EFI_IP6_COMPLETION_TOKEN *\r
+Ping6GenerateToken (\r
+  IN PING6_PRIVATE_DATA    *Private,\r
+  IN UINT64                TimeStamp,\r
+  IN UINT16                SequenceNum\r
+  )\r
+{\r
+  EFI_STATUS                  Status;\r
+  EFI_IP6_COMPLETION_TOKEN    *Token;\r
+  EFI_IP6_TRANSMIT_DATA       *TxData;\r
+  ICMP6_ECHO_REQUEST_REPLY    *Request;\r
+\r
+  Request = AllocateZeroPool (Private->BufferSize);\r
+\r
+  if (Request == NULL) {\r
+    return NULL;\r
+  }\r
+  //\r
+  // Assembly icmp6 echo request packet.\r
+  //\r
+  Request->Type        = ICMP_V6_ECHO_REQUEST;\r
+  Request->Code        = 0;\r
+  Request->SequenceNum = SequenceNum;\r
+  Request->TimeStamp   = TimeStamp;\r
+  Request->Identifier  = 0;\r
+  //\r
+  // Leave check sum to ip6 layer, since it has no idea of source address\r
+  // selection.\r
+  //\r
+  Request->Checksum    = 0;\r
+\r
+  TxData = AllocateZeroPool (sizeof (EFI_IP6_TRANSMIT_DATA));\r
+\r
+  if (TxData == NULL) {\r
+    FreePool (Request);\r
+    return NULL;\r
+  }\r
+  //\r
+  // Assembly ipv6 token for transmit.\r
+  //\r
+  TxData->OverrideData       = 0;\r
+  TxData->ExtHdrsLength      = 0;\r
+  TxData->ExtHdrs            = NULL;\r
+  TxData->DataLength         = Private->BufferSize;\r
+  TxData->FragmentCount      = 1;\r
+  TxData->FragmentTable[0].FragmentBuffer = (VOID *) Request;\r
+  TxData->FragmentTable[0].FragmentLength = Private->BufferSize;\r
+\r
+  Token = AllocateZeroPool (sizeof (EFI_IP6_COMPLETION_TOKEN));\r
+\r
+  if (Token == NULL) {\r
+    FreePool (Request);\r
+    FreePool (TxData);\r
+    return NULL;\r
+  }\r
+\r
+  Token->Status         = EFI_ABORTED;\r
+  Token->Packet.TxData  = TxData;\r
+\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_CALLBACK,\r
+                  Ping6OnEchoRequestSent6,\r
+                  Private,\r
+                  &Token->Event\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    FreePool (Request);\r
+    FreePool (TxData);\r
+    FreePool (Token);\r
+    return NULL;\r
+  }\r
+\r
+  return Token;\r
+}\r
+\r
+/**\r
+  Transmit the EFI_IP6_COMPLETION_TOKEN.\r
+\r
+  @param[in]    Private    The pointer of PING6_PRIVATE_DATA.\r
+\r
+  @retval EFI_SUCCESS             Transmitted successfully.\r
+  @retval EFI_OUT_OF_RESOURCES    No memory is available on the platform.\r
+  @retval others                  Transmitted unsuccessfully.\r
+\r
+**/\r
+EFI_STATUS\r
+Ping6SendEchoRequest (\r
+  IN PING6_PRIVATE_DATA    *Private\r
+  )\r
+{\r
+  EFI_STATUS             Status;\r
+  PING6_ICMP6_TX_INFO    *TxInfo;\r
+\r
+  TxInfo = AllocateZeroPool (sizeof (PING6_ICMP6_TX_INFO));\r
+\r
+  if (TxInfo == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  TxInfo->TimeStamp   = Ping6ReadTime ();\r
+  TxInfo->SequenceNum = (UINT16) (Private->TxCount + 1);\r
+\r
+  TxInfo->Token       = Ping6GenerateToken (\r
+                          Private,\r
+                          TxInfo->TimeStamp,\r
+                          TxInfo->SequenceNum\r
+                          );\r
+\r
+  if (TxInfo->Token == NULL) {\r
+    Ping6DestroyTxInfo (TxInfo);\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Status = Private->Ip6->Transmit (Private->Ip6, TxInfo->Token);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    Ping6DestroyTxInfo (TxInfo);\r
+    return Status;\r
+  }\r
+\r
+  InsertTailList (&Private->TxList, &TxInfo->Link);\r
+  Private->TxCount++;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Place a completion token into the receive packet queue to receive the echo reply.\r
+\r
+  @param[in]    Private    The pointer of PING6_PRIVATE_DATA.\r
+\r
+  @retval EFI_SUCCESS      Put the token into the receive packet queue successfully.\r
+  @retval others           Put the token into the receive packet queue unsuccessfully.\r
+\r
+**/\r
+EFI_STATUS\r
+Ping6OnReceiveEchoReply (\r
+  IN PING6_PRIVATE_DATA    *Private\r
+  )\r
+{\r
+  EFI_STATUS    Status;\r
+\r
+  ZeroMem (&Private->RxToken, sizeof (EFI_IP6_COMPLETION_TOKEN));\r
+\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_CALLBACK,\r
+                  Ping6OnEchoReplyReceived6,\r
+                  Private,\r
+                  &Private->RxToken.Event\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Private->RxToken.Status = EFI_NOT_READY;\r
+\r
+  return Private->Ip6->Receive (Private->Ip6, &Private->RxToken);\r
+}\r
+\r
+/**\r
+  Remove the timeout request from the list.\r
+\r
+  @param[in]    Event    A EFI_EVENT type event.\r
+  @param[in]    Context  The pointer to Context.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ping6OnTimerRoutine6 (\r
+  IN EFI_EVENT    Event,\r
+  IN VOID         *Context\r
+  )\r
+{\r
+  EFI_STATUS             Status;\r
+  PING6_PRIVATE_DATA     *Private;\r
+  PING6_ICMP6_TX_INFO    *TxInfo;\r
+  LIST_ENTRY             *Entry;\r
+  LIST_ENTRY             *NextEntry;\r
+  UINT64                 Time;\r
+\r
+  Private = (PING6_PRIVATE_DATA *) Context;\r
+\r
+  //\r
+  // Retransmit icmp6 echo request packets per second in sendnumber times.\r
+  //\r
+  if (Private->TxCount < Private->SendNum) {\r
+\r
+    Status = Ping6SendEchoRequest (Private);\r
+    if (Private->TxCount != 0){\r
+      if (EFI_ERROR (Status)) {\r
+        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_SEND_REQUEST), gShellNetwork2HiiHandle, Private->TxCount + 1);\r
+      }\r
+    }\r
+  }\r
+  //\r
+  // Check whether any icmp6 echo request in the list timeout.\r
+  //\r
+  NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) {\r
+    TxInfo = BASE_CR (Entry, PING6_ICMP6_TX_INFO, Link);\r
+    Time   = Ping6CalculateTick (TxInfo->TimeStamp, Ping6ReadTime ());\r
+\r
+    //\r
+    // Remove the timeout echo request from txlist.\r
+    //\r
+    if (Time > PING6_DEFAULT_TIMEOUT) {\r
+\r
+      if (EFI_ERROR (TxInfo->Token->Status)) {\r
+        Private->Ip6->Cancel (Private->Ip6, TxInfo->Token);\r
+      }\r
+      //\r
+      // Remove the timeout icmp6 echo request from list.\r
+      //\r
+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_TIMEOUT), gShellNetwork2HiiHandle, TxInfo->SequenceNum);\r
+\r
+      RemoveEntryList (&TxInfo->Link);\r
+      Ping6DestroyTxInfo (TxInfo);\r
+\r
+      if (IsListEmpty (&Private->TxList) && (Private->TxCount == Private->SendNum)) {\r
+        //\r
+        // All the left icmp6 echo request in the list timeout.\r
+        //\r
+        Private->Status = EFI_TIMEOUT;\r
+      }\r
+    }\r
+  }\r
+}\r
+\r
+/**\r
+  Create a valid IP6 instance.\r
+\r
+  @param[in]    Private    The pointer of PING6_PRIVATE_DATA.\r
+\r
+  @retval EFI_SUCCESS              Create a valid IP6 instance successfully.\r
+  @retval EFI_ABORTED              Locate handle with ip6 service binding protocol unsuccessfully.\r
+  @retval EFI_INVALID_PARAMETER    The source address is unspecified when the destination address is a link -ocal address.\r
+  @retval EFI_OUT_OF_RESOURCES     No memory is available on the platform.\r
+  @retval EFI_NOT_FOUND            The source address is not found.\r
+**/\r
+EFI_STATUS\r
+Ping6CreateIpInstance (\r
+  IN  PING6_PRIVATE_DATA    *Private\r
+  )\r
+{\r
+  EFI_STATUS                       Status;\r
+  UINTN                            HandleIndex;\r
+  UINTN                            HandleNum;\r
+  EFI_HANDLE                       *HandleBuffer;\r
+  EFI_SERVICE_BINDING_PROTOCOL     *Ip6Sb;\r
+  EFI_IP6_CONFIG_PROTOCOL          *Ip6Cfg;\r
+  EFI_IP6_CONFIG_DATA              Ip6Config;\r
+  EFI_IP6_CONFIG_INTERFACE_INFO    *IfInfo;\r
+  UINTN                            IfInfoSize;\r
+  EFI_IPv6_ADDRESS                 *Addr;\r
+  UINTN                            AddrIndex;\r
+\r
+  HandleBuffer = NULL;\r
+  Ip6Sb        = NULL;\r
+  IfInfo       = NULL;\r
+  IfInfoSize   = 0;\r
+\r
+  //\r
+  // Locate all the handles with ip6 service binding protocol.\r
+  //\r
+  Status = gBS->LocateHandleBuffer (\r
+                  ByProtocol,\r
+                  &gEfiIp6ServiceBindingProtocolGuid,\r
+                  NULL,\r
+                  &HandleNum,\r
+                  &HandleBuffer\r
+                  );\r
+  if (EFI_ERROR (Status) || (HandleNum == 0)) {\r
+    return EFI_ABORTED;\r
+  }\r
+  //\r
+  // Source address is required when pinging a link-local address on multi-\r
+  // interfaces host.\r
+  //\r
+  if (NetIp6IsLinkLocalAddr (&Private->DstAddress) &&\r
+      NetIp6IsUnspecifiedAddr (&Private->SrcAddress) &&\r
+      (HandleNum > 1)) {\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_SOURCE), gShellNetwork2HiiHandle);\r
+    Status = EFI_INVALID_PARAMETER;\r
+    goto ON_ERROR;\r
+  }\r
+  //\r
+  // For each ip6 protocol, check interface addresses list.\r
+  //\r
+  for (HandleIndex = 0; HandleIndex < HandleNum; HandleIndex++) {\r
+\r
+    Ip6Sb      = NULL;\r
+    IfInfo     = NULL;\r
+    IfInfoSize = 0;\r
+\r
+    Status = gBS->HandleProtocol (\r
+                    HandleBuffer[HandleIndex],\r
+                    &gEfiIp6ServiceBindingProtocolGuid,\r
+                    (VOID **) &Ip6Sb\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      goto ON_ERROR;\r
+    }\r
+\r
+    if (NetIp6IsUnspecifiedAddr (&Private->SrcAddress)) {\r
+      //\r
+      // No need to match interface address.\r
+      //\r
+      break;\r
+    } else {\r
+      //\r
+      // Ip6config protocol and ip6 service binding protocol are installed\r
+      // on the same handle.\r
+      //\r
+      Status = gBS->HandleProtocol (\r
+                      HandleBuffer[HandleIndex],\r
+                      &gEfiIp6ConfigProtocolGuid,\r
+                      (VOID **) &Ip6Cfg\r
+                      );\r
+\r
+      if (EFI_ERROR (Status)) {\r
+        goto ON_ERROR;\r
+      }\r
+      //\r
+      // Get the interface information size.\r
+      //\r
+      Status = Ip6Cfg->GetData (\r
+                         Ip6Cfg,\r
+                         Ip6ConfigDataTypeInterfaceInfo,\r
+                         &IfInfoSize,\r
+                         NULL\r
+                         );\r
+\r
+      if (Status != EFI_BUFFER_TOO_SMALL) {\r
+        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6CFG_GETDATA), gShellNetwork2HiiHandle, Status);\r
+        goto ON_ERROR;\r
+      }\r
+\r
+      IfInfo = AllocateZeroPool (IfInfoSize);\r
+\r
+      if (IfInfo == NULL) {\r
+        Status = EFI_OUT_OF_RESOURCES;\r
+        goto ON_ERROR;\r
+      }\r
+      //\r
+      // Get the interface info.\r
+      //\r
+      Status = Ip6Cfg->GetData (\r
+                         Ip6Cfg,\r
+                         Ip6ConfigDataTypeInterfaceInfo,\r
+                         &IfInfoSize,\r
+                         IfInfo\r
+                         );\r
+\r
+      if (EFI_ERROR (Status)) {\r
+        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6CFG_GETDATA), gShellNetwork2HiiHandle, Status);\r
+        goto ON_ERROR;\r
+      }\r
+      //\r
+      // Check whether the source address is one of the interface addresses.\r
+      //\r
+      for (AddrIndex = 0; AddrIndex < IfInfo->AddressInfoCount; AddrIndex++) {\r
+\r
+        Addr = &(IfInfo->AddressInfo[AddrIndex].Address);\r
+        if (EFI_IP6_EQUAL (&Private->SrcAddress, Addr)) {\r
+          //\r
+          // Match a certain interface address.\r
+          //\r
+          break;\r
+        }\r
+      }\r
+\r
+      if (AddrIndex < IfInfo->AddressInfoCount) {\r
+        //\r
+        // Found a nic handle with right interface address.\r
+        //\r
+        break;\r
+      }\r
+    }\r
+\r
+    FreePool (IfInfo);\r
+    IfInfo = NULL;\r
+  }\r
+  //\r
+  // No exact interface address matched.\r
+  //\r
+\r
+  if (HandleIndex == HandleNum) {\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_SOURCE_NOT_FOUND), gShellNetwork2HiiHandle, mIp6SrcString);\r
+    Status = EFI_NOT_FOUND;\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Private->NicHandle = HandleBuffer[HandleIndex];\r
+\r
+  ASSERT (Ip6Sb != NULL);\r
+  Status = Ip6Sb->CreateChild (Ip6Sb, &Private->Ip6ChildHandle);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  Private->Ip6ChildHandle,\r
+                  &gEfiIp6ProtocolGuid,\r
+                  (VOID **) &Private->Ip6,\r
+                  Private->ImageHandle,\r
+                  Private->Ip6ChildHandle,\r
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  ZeroMem (&Ip6Config, sizeof (EFI_IP6_CONFIG_DATA));\r
+\r
+  //\r
+  // Configure the ip6 instance for icmp6 packet exchange.\r
+  //\r
+  Ip6Config.DefaultProtocol   = 58;\r
+  Ip6Config.AcceptAnyProtocol = FALSE;\r
+  Ip6Config.AcceptIcmpErrors  = TRUE;\r
+  Ip6Config.AcceptPromiscuous = FALSE;\r
+  Ip6Config.TrafficClass      = 0;\r
+  Ip6Config.HopLimit          = 128;\r
+  Ip6Config.FlowLabel         = 0;\r
+  Ip6Config.ReceiveTimeout    = 0;\r
+  Ip6Config.TransmitTimeout   = 0;\r
+\r
+  IP6_COPY_ADDRESS (&Ip6Config.StationAddress, &Private->SrcAddress);\r
+\r
+  IP6_COPY_ADDRESS (&Ip6Config.DestinationAddress, &Private->DstAddress);\r
+\r
+  Status = Private->Ip6->Configure (Private->Ip6, &Ip6Config);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6_CONFIG), gShellNetwork2HiiHandle, Status);\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+  if (HandleBuffer != NULL) {\r
+    FreePool (HandleBuffer);\r
+  }\r
+\r
+  if (IfInfo != NULL) {\r
+    FreePool (IfInfo);\r
+  }\r
+\r
+  if ((Ip6Sb != NULL) && (Private->Ip6ChildHandle != NULL)) {\r
+    Ip6Sb->DestroyChild (Ip6Sb, Private->Ip6ChildHandle);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Destroy the IP6 instance.\r
+\r
+  @param[in]    Private    The pointer of PING6_PRIVATE_DATA.\r
+\r
+**/\r
+VOID\r
+Ping6DestroyIpInstance (\r
+  IN PING6_PRIVATE_DATA    *Private\r
+  )\r
+{\r
+  EFI_STATUS                      Status;\r
+  EFI_SERVICE_BINDING_PROTOCOL    *Ip6Sb;\r
+\r
+  gBS->CloseProtocol (\r
+         Private->Ip6ChildHandle,\r
+         &gEfiIp6ProtocolGuid,\r
+         Private->ImageHandle,\r
+         Private->Ip6ChildHandle\r
+         );\r
+\r
+  Status = gBS->HandleProtocol (\r
+                  Private->NicHandle,\r
+                  &gEfiIp6ServiceBindingProtocolGuid,\r
+                  (VOID **) &Ip6Sb\r
+                  );\r
+\r
+  if (!EFI_ERROR(Status)) {\r
+    Ip6Sb->DestroyChild (Ip6Sb, Private->Ip6ChildHandle);\r
+  }\r
+}\r
+\r
+/**\r
+  The Ping6 Process.\r
+\r
+  @param[in]   ImageHandle    The firmware allocated handle for the UEFI image.\r
+  @param[in]   SendNumber     The send request count.\r
+  @param[in]   BufferSize     The send buffer size.\r
+  @param[in]   SrcAddress     The source IPv6 address.\r
+  @param[in]   DstAddress     The destination IPv6 address.\r
+\r
+  @retval SHELL_SUCCESS    The ping6 processed successfullly.\r
+  @retval others           The ping6 processed unsuccessfully.\r
+\r
+**/\r
+SHELL_STATUS\r
+ShellPing6 (\r
+  IN EFI_HANDLE          ImageHandle,\r
+  IN UINT32              SendNumber,\r
+  IN UINT32              BufferSize,\r
+  IN EFI_IPv6_ADDRESS    *SrcAddress,\r
+  IN EFI_IPv6_ADDRESS    *DstAddress\r
+  )\r
+{\r
+  EFI_STATUS             Status;\r
+  EFI_INPUT_KEY          Key;\r
+  PING6_PRIVATE_DATA     *Private;\r
+  PING6_ICMP6_TX_INFO    *TxInfo;\r
+  LIST_ENTRY             *Entry;\r
+  LIST_ENTRY             *NextEntry;\r
+  SHELL_STATUS           ShellStatus;\r
+\r
+  ShellStatus = SHELL_SUCCESS;\r
+  Private = AllocateZeroPool (sizeof (PING6_PRIVATE_DATA));\r
+\r
+  ASSERT (Private != NULL);\r
+\r
+  Private->ImageHandle = ImageHandle;\r
+  Private->SendNum     = SendNumber;\r
+  Private->BufferSize  = BufferSize;\r
+  Private->RttMin      = ~((UINT64 )(0x0));\r
+  Private->Status      = EFI_NOT_READY;\r
+\r
+  InitializeListHead (&Private->TxList);\r
+\r
+  IP6_COPY_ADDRESS (&Private->SrcAddress, SrcAddress);\r
+  IP6_COPY_ADDRESS (&Private->DstAddress, DstAddress);\r
+\r
+  //\r
+  // Open and configure a ip6 instance for ping6.\r
+  //\r
+  Status = Ping6CreateIpInstance (Private);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    ShellStatus = SHELL_ACCESS_DENIED;\r
+    goto ON_EXIT;\r
+  }\r
+  //\r
+  // Print the command line itself.\r
+  //\r
+  ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_START), gShellNetwork2HiiHandle, mIp6DstString, Private->BufferSize);\r
+  //\r
+  // Create a ipv6 token to receive the first icmp6 echo reply packet.\r
+  //\r
+  Status = Ping6OnReceiveEchoReply (Private);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    ShellStatus = SHELL_ACCESS_DENIED;\r
+    goto ON_EXIT;\r
+  }\r
+  //\r
+  // Create and start timer to send icmp6 echo request packet per second.\r
+  //\r
+  Status = gBS->CreateEvent (\r
+                  EVT_TIMER | EVT_NOTIFY_SIGNAL,\r
+                  TPL_CALLBACK,\r
+                  Ping6OnTimerRoutine6,\r
+                  Private,\r
+                  &Private->Timer\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    ShellStatus = SHELL_ACCESS_DENIED;\r
+    goto ON_EXIT;\r
+  }\r
+  //\r
+  // Create a ipv6 token to send the first icmp6 echo request packet.\r
+  //\r
+  Status = Ping6SendEchoRequest (Private);\r
+  //\r
+  // EFI_NOT_READY for IPsec is enable and IKE is not established.\r
+  //\r
+  if (EFI_ERROR (Status) && (Status != EFI_NOT_READY)) {\r
+    ShellStatus = SHELL_ACCESS_DENIED;\r
+    if(Status == EFI_NOT_FOUND) {\r
+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_NOSOURCE_INDOMAIN), gShellNetwork2HiiHandle, mIp6DstString);\r
+    }\r
+\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  Status = gBS->SetTimer (\r
+                  Private->Timer,\r
+                  TimerPeriodic,\r
+                  PING6_ONE_SECOND\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    ShellStatus = SHELL_ACCESS_DENIED;\r
+    goto ON_EXIT;\r
+  }\r
+  //\r
+  // Control the ping6 process by two factors:\r
+  // 1. Hot key\r
+  // 2. Private->Status\r
+  //   2.1. success means all icmp6 echo request packets get reply packets.\r
+  //   2.2. timeout means the last icmp6 echo reply request timeout to get reply.\r
+  //   2.3. noready means ping6 process is on-the-go.\r
+  //\r
+  while (Private->Status == EFI_NOT_READY) {\r
+    Private->Ip6->Poll (Private->Ip6);\r
+\r
+    //\r
+    // Terminate the ping6 process by 'esc' or 'ctl-c'.\r
+    //\r
+    Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);\r
+\r
+    if (!EFI_ERROR(Status)) {\r
+      if ((Key.UnicodeChar == 0x1b) || (Key.UnicodeChar == 0x03) ||\r
+         ((Key.UnicodeChar == 0) && (Key.ScanCode == SCAN_ESC))) {\r
+        goto ON_STAT;\r
+      }\r
+    }\r
+  }\r
+\r
+ON_STAT:\r
+  //\r
+  // Display the statistics in all.\r
+  //\r
+  gBS->SetTimer (Private->Timer, TimerCancel, 0);\r
+\r
+  if (Private->TxCount != 0) {\r
+    ShellPrintHiiEx (\r
+      -1,\r
+      -1,\r
+      NULL,\r
+      STRING_TOKEN (STR_PING6_STAT),\r
+      gShellNetwork2HiiHandle,\r
+      Private->TxCount,\r
+      Private->RxCount,\r
+      (100 * (Private->TxCount - Private->RxCount)) / Private->TxCount,\r
+      Private->RttSum\r
+      );\r
+  }\r
+\r
+  if (Private->RxCount != 0) {\r
+    ShellPrintHiiEx (\r
+      -1,\r
+      -1,\r
+      NULL,\r
+      STRING_TOKEN (STR_PING6_RTT),\r
+      gShellNetwork2HiiHandle,\r
+      Private->RttMin,\r
+      Private->RttMax,\r
+      DivU64x64Remainder (Private->RttSum, Private->RxCount, NULL)\r
+      );\r
+  }\r
+\r
+ON_EXIT:\r
+\r
+  if (Private != NULL) {\r
+    Private->Status = EFI_ABORTED;\r
+\r
+    NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) {\r
+      TxInfo = BASE_CR (Entry, PING6_ICMP6_TX_INFO, Link);\r
+\r
+      Status = Private->Ip6->Cancel (Private->Ip6, TxInfo->Token);\r
+\r
+      RemoveEntryList (&TxInfo->Link);\r
+      Ping6DestroyTxInfo (TxInfo);\r
+    }\r
+\r
+    if (Private->Timer != NULL) {\r
+      gBS->CloseEvent (Private->Timer);\r
+    }\r
+\r
+    if (Private->Ip6 != NULL) {\r
+      Status = Private->Ip6->Cancel (Private->Ip6, &Private->RxToken);\r
+    }\r
+\r
+    if (Private->RxToken.Event != NULL) {\r
+      gBS->CloseEvent (Private->RxToken.Event);\r
+    }\r
+\r
+    if (Private->Ip6ChildHandle != NULL) {\r
+      Ping6DestroyIpInstance (Private);\r
+    }\r
+\r
+    FreePool (Private);\r
+  }\r
+\r
+  return ShellStatus;\r
+}\r
+\r
+/**\r
+  Function for 'ping6' command.\r
+\r
+  @param[in] ImageHandle  Handle to the Image (NULL if Internal).\r
+  @param[in] SystemTable  Pointer to the System Table (NULL if Internal).\r
+\r
+  @retval SHELL_SUCCESS  The ping6 processed successfullly.\r
+  @retval others         The ping6 processed unsuccessfully.\r
+\r
+**/\r
+SHELL_STATUS\r
+EFIAPI\r
+ShellCommandRunPing6 (\r
+  IN EFI_HANDLE        ImageHandle,\r
+  IN EFI_SYSTEM_TABLE  *SystemTable\r
+  )\r
+{\r
+  EFI_STATUS          Status;\r
+  SHELL_STATUS        ShellStatus;\r
+  EFI_IPv6_ADDRESS    DstAddress;\r
+  EFI_IPv6_ADDRESS    SrcAddress;\r
+  UINT64              BufferSize;\r
+  UINTN               SendNumber;\r
+  LIST_ENTRY          *ParamPackage;\r
+  CONST CHAR16        *ValueStr;\r
+  CONST CHAR16        *ValueStrPtr;\r
+  UINTN               NonOptionCount;\r
+  CHAR16              *ProblemParam;\r
+\r
+  ProblemParam = NULL;\r
+  ShellStatus = SHELL_SUCCESS;\r
+\r
+  Status = ShellCommandLineParseEx (Ping6ParamList, &ParamPackage, &ProblemParam, TRUE, FALSE);\r
+  if (EFI_ERROR(Status)) {\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_INPUT), gShellNetwork2HiiHandle);\r
+    ShellStatus = SHELL_INVALID_PARAMETER;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  SendNumber = 10;\r
+  BufferSize = 16;\r
+\r
+  //\r
+  // Parse the paramter of count number.\r
+  //\r
+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"-n");\r
+  ValueStrPtr = ValueStr;\r
+  if (ValueStr != NULL) {\r
+    SendNumber = ShellStrToUintn (ValueStrPtr);\r
+\r
+    //\r
+    // ShellStrToUintn will return 0 when input is 0 or an invalid input string.\r
+    //\r
+    if ((SendNumber == 0) || (SendNumber > PING6_MAX_SEND_NUMBER)) {\r
+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_SEND_NUMBER), gShellNetwork2HiiHandle, ValueStr);\r
+      ShellStatus = SHELL_INVALID_PARAMETER;\r
+      goto ON_EXIT;\r
+    }\r
+  }\r
+  //\r
+  // Parse the paramter of buffer size.\r
+  //\r
+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"-l");\r
+  ValueStrPtr = ValueStr;\r
+  if (ValueStr != NULL) {\r
+    BufferSize = ShellStrToUintn (ValueStrPtr);\r
+\r
+    //\r
+    // ShellStrToUintn will return 0 when input is 0 or an invalid input string.\r
+    //\r
+    if ((BufferSize < 16) || (BufferSize > PING6_MAX_BUFFER_SIZE)) {\r
+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_BUFFER_SIZE), gShellNetwork2HiiHandle, ValueStr);\r
+      ShellStatus = SHELL_INVALID_PARAMETER;\r
+      goto ON_EXIT;\r
+    }\r
+  }\r
+\r
+  ZeroMem (&SrcAddress, sizeof (EFI_IPv6_ADDRESS));\r
+  ZeroMem (&DstAddress, sizeof (EFI_IPv6_ADDRESS));\r
+\r
+  //\r
+  // Parse the paramter of source ip address.\r
+  //\r
+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"-s");\r
+  ValueStrPtr = ValueStr;\r
+  if (ValueStr != NULL) {\r
+    mIp6SrcString = ValueStr;\r
+    Status = NetLibStrToIp6 (ValueStrPtr, &SrcAddress);\r
+    if (EFI_ERROR (Status)) {\r
+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_IP), gShellNetwork2HiiHandle, ValueStr);\r
+      ShellStatus = SHELL_INVALID_PARAMETER;\r
+      goto ON_EXIT;\r
+    }\r
+  }\r
+  //\r
+  // Parse the paramter of destination ip address.\r
+  //\r
+  NonOptionCount = ShellCommandLineGetCount(ParamPackage);\r
+  ValueStr = ShellCommandLineGetRawValue (ParamPackage, (UINT32)(NonOptionCount-1));\r
+  if (NonOptionCount != 2) {\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_INPUT), gShellNetwork2HiiHandle);\r
+    ShellStatus = SHELL_INVALID_PARAMETER;\r
+    goto ON_EXIT;\r
+  }\r
+  ValueStrPtr = ValueStr;\r
+  if (ValueStr != NULL) {\r
+    mIp6DstString = ValueStr;\r
+    Status = NetLibStrToIp6 (ValueStrPtr, &DstAddress);\r
+    if (EFI_ERROR (Status)) {\r
+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_IP), gShellNetwork2HiiHandle, ValueStr);\r
+      ShellStatus = SHELL_INVALID_PARAMETER;\r
+      goto ON_EXIT;\r
+    }\r
+  }\r
+  //\r
+  // Get frequency to calculate the time from ticks.\r
+  //\r
+  Status = Ping6GetFrequency ();\r
+\r
+  if (EFI_ERROR(Status)) {\r
+    ShellStatus = SHELL_ACCESS_DENIED;\r
+    goto ON_EXIT;\r
+  }\r
+  //\r
+  // Enter into ping6 process.\r
+  //\r
+  ShellStatus = ShellPing6 (\r
+              ImageHandle,\r
+              (UINT32)SendNumber,\r
+              (UINT32)BufferSize,\r
+              &SrcAddress,\r
+              &DstAddress\r
+              );\r
+\r
+ON_EXIT:\r
+  ShellCommandLineFreeVarList (ParamPackage);\r
+  return ShellStatus;\r
+}\r
+\r