--- /dev/null
+/** @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