diff options
Diffstat (limited to '')
-rw-r--r-- | src/VBox/Devices/EFI/Firmware/ShellPkg/Library/UefiShellNetwork1CommandsLib/Ping.c | 1708 |
1 files changed, 1708 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/ShellPkg/Library/UefiShellNetwork1CommandsLib/Ping.c b/src/VBox/Devices/EFI/Firmware/ShellPkg/Library/UefiShellNetwork1CommandsLib/Ping.c new file mode 100644 index 00000000..33106eae --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/ShellPkg/Library/UefiShellNetwork1CommandsLib/Ping.c @@ -0,0 +1,1708 @@ +/** @file + The implementation for Ping shell command. + + (C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR> + Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> + (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "UefiShellNetwork1CommandsLib.h" + +#define PING_IP4_COPY_ADDRESS(Dest, Src) (CopyMem ((Dest), (Src), sizeof (EFI_IPv4_ADDRESS))) + +UINT64 mCurrentTick = 0; + +// +// Function templates to match the IPv4 and IPv6 commands that we use. +// +typedef +EFI_STATUS +(EFIAPI *PING_IPX_POLL)( + IN VOID *This + ); + +typedef +EFI_STATUS +(EFIAPI *PING_IPX_TRANSMIT)( + IN VOID *This, + IN VOID *Token + ); + +typedef +EFI_STATUS +(EFIAPI *PING_IPX_RECEIVE)( + IN VOID *This, + IN VOID *Token + ); + +typedef +EFI_STATUS +(EFIAPI *PING_IPX_CANCEL)( + IN VOID *This, + IN VOID *Token OPTIONAL + ); + +/// +/// A set of pointers to either IPv6 or IPv4 functions. +/// Unknown which one to the ping command. +/// +typedef struct { + PING_IPX_TRANSMIT Transmit; + PING_IPX_RECEIVE Receive; + PING_IPX_CANCEL Cancel; + PING_IPX_POLL Poll; +}PING_IPX_PROTOCOL; + + +typedef union { + VOID *RxData; + VOID *TxData; +} PING_PACKET; + +// +// PING_IPX_COMPLETION_TOKEN +// structures are used for both transmit and receive operations. +// This version is IP-unaware. +// +typedef struct { + EFI_EVENT Event; + EFI_STATUS Status; + PING_PACKET Packet; +} PING_IPX_COMPLETION_TOKEN; + +#pragma pack(1) +typedef struct _ICMPX_ECHO_REQUEST_REPLY { + UINT8 Type; + UINT8 Code; + UINT16 Checksum; + UINT16 Identifier; + UINT16 SequenceNum; + UINT32 TimeStamp; + UINT8 Data[1]; +} ICMPX_ECHO_REQUEST_REPLY; +#pragma pack() + +typedef struct _PING_ICMP_TX_INFO { + LIST_ENTRY Link; + UINT16 SequenceNum; + UINT32 TimeStamp; + PING_IPX_COMPLETION_TOKEN *Token; +} PING_ICMPX_TX_INFO; + +#define DEFAULT_TIMEOUT 5000 +#define MAX_SEND_NUMBER 10000 +#define MAX_BUFFER_SIZE 32768 +#define DEFAULT_TIMER_PERIOD 358049 +#define ONE_SECOND 10000000 +#define PING_IP_CHOICE_IP4 1 +#define PING_IP_CHOICE_IP6 2 +#define DEFAULT_SEND_COUNT 10 +#define DEFAULT_BUFFER_SIZE 16 +#define ICMP_V4_ECHO_REQUEST 0x8 +#define ICMP_V4_ECHO_REPLY 0x0 +#define STALL_1_MILLI_SECOND 1000 + +#define PING_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('P', 'i', 'n', 'g') +typedef struct _PING_PRIVATE_DATA { + UINT32 Signature; + EFI_HANDLE NicHandle; + EFI_HANDLE IpChildHandle; + EFI_EVENT Timer; + + UINT32 TimerPeriod; + UINT32 RttTimerTick; + EFI_EVENT RttTimer; + + EFI_STATUS Status; + LIST_ENTRY TxList; + UINT16 RxCount; + UINT16 TxCount; + UINT64 RttSum; + UINT64 RttMin; + UINT64 RttMax; + UINT32 SequenceNum; + + UINT32 SendNum; + UINT32 BufferSize; + UINT32 IpChoice; + + PING_IPX_PROTOCOL ProtocolPointers; + VOID *IpProtocol; + UINT8 SrcAddress[MAX(sizeof(EFI_IPv6_ADDRESS) , sizeof(EFI_IPv4_ADDRESS) )]; + UINT8 DstAddress[MAX(sizeof(EFI_IPv6_ADDRESS) , sizeof(EFI_IPv4_ADDRESS) )]; + PING_IPX_COMPLETION_TOKEN RxToken; + UINT16 FailedCount; +} PING_PRIVATE_DATA; + +/** + Calculate the internet checksum (see RFC 1071). + + @param[in] Packet Buffer which contains the data to be checksummed. + @param[in] Length Length to be checksummed. + + @retval Checksum Returns the 16 bit ones complement of + ones complement sum of 16 bit words +**/ +UINT16 +NetChecksum ( + IN UINT8 *Buffer, + IN UINT32 Length + ) +{ + UINT32 Sum; + UINT8 Odd; + UINT16 *Packet; + + Packet = (UINT16 *) Buffer; + + Sum = 0; + Odd = (UINT8) (Length & 1); + Length >>= 1; + while ((Length--) != 0) { + Sum += *Packet++; + } + + if (Odd != 0) { + Sum += *(UINT8 *) Packet; + } + + Sum = (Sum & 0xffff) + (Sum >> 16); + + // + // in case above carried + // + Sum += Sum >> 16; + + return (UINT16) Sum; +} + +/** + Reads and returns the current value of register. + In IA64, the register is the Interval Timer Vector (ITV). + In X86(IA32/X64), the register is the Time Stamp Counter (TSC) + + @return The current value of the register. + +**/ + +STATIC CONST SHELL_PARAM_ITEM PingParamList[] = { + { + L"-l", + TypeValue + }, + { + L"-n", + TypeValue + }, + { + L"-s", + TypeValue + }, + { + L"-_s", + TypeValue + }, + { + L"-_ip6", + TypeFlag + }, + { + NULL, + TypeMax + }, +}; + +// +// Global Variables in Ping command. +// +STATIC CONST CHAR16 *mDstString; +STATIC CONST CHAR16 *mSrcString; + +/** + RTT timer tick routine. + + @param[in] Event A EFI_EVENT type event. + @param[in] Context The pointer to Context. + +**/ +VOID +EFIAPI +RttTimerTickRoutine ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + UINT32 *RttTimerTick; + + RttTimerTick = (UINT32*) Context; + (*RttTimerTick)++; +} + +/** + Get the timer period of the system. + + This function tries to get the system timer period by creating + an 1ms period timer. + + @return System timer period in MS, or 0 if operation failed. + +**/ +UINT32 +GetTimerPeriod( + VOID + ) +{ + EFI_STATUS Status; + UINT32 RttTimerTick; + EFI_EVENT TimerEvent; + UINT32 StallCounter; + EFI_TPL OldTpl; + + RttTimerTick = 0; + StallCounter = 0; + + Status = gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + RttTimerTickRoutine, + &RttTimerTick, + &TimerEvent + ); + if (EFI_ERROR (Status)) { + return 0; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + Status = gBS->SetTimer ( + TimerEvent, + TimerPeriodic, + TICKS_PER_MS + ); + if (EFI_ERROR (Status)) { + gBS->CloseEvent (TimerEvent); + return 0; + } + + while (RttTimerTick < 10) { + gBS->Stall (STALL_1_MILLI_SECOND); + ++StallCounter; + } + + gBS->RestoreTPL (OldTpl); + + gBS->SetTimer (TimerEvent, TimerCancel, 0); + gBS->CloseEvent (TimerEvent); + + return StallCounter / RttTimerTick; +} + +/** + Initialize the timer event for RTT (round trip time). + + @param[in] Private The pointer to PING_PRIVATE_DATA. + + @retval EFI_SUCCESS RTT timer is started. + @retval Others Failed to start the RTT timer. + +**/ +EFI_STATUS +PingInitRttTimer ( + PING_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + + Private->TimerPeriod = GetTimerPeriod (); + if (Private->TimerPeriod == 0) { + return EFI_ABORTED; + } + + Private->RttTimerTick = 0; + Status = gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + RttTimerTickRoutine, + &Private->RttTimerTick, + &Private->RttTimer + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->SetTimer ( + Private->RttTimer, + TimerPeriodic, + TICKS_PER_MS + ); + if (EFI_ERROR (Status)) { + gBS->CloseEvent (Private->RttTimer); + return Status; + } + + return EFI_SUCCESS; +} + +/** + Free RTT timer event resource. + + @param[in] Private The pointer to PING_PRIVATE_DATA. + +**/ +VOID +PingFreeRttTimer ( + PING_PRIVATE_DATA *Private + ) +{ + if (Private->RttTimer != NULL) { + gBS->SetTimer (Private->RttTimer, TimerCancel, 0); + gBS->CloseEvent (Private->RttTimer); + } +} + +/** + Read the current time. + + @param[in] Private The pointer to PING_PRIVATE_DATA. + + @retval the current tick value. +**/ +UINT32 +ReadTime ( + PING_PRIVATE_DATA *Private + ) +{ + return Private->RttTimerTick; +} + +/** + Calculate a duration in ms. + + @param[in] Private The pointer to PING_PRIVATE_DATA. + @param[in] Begin The start point of time. + @param[in] End The end point of time. + + @return The duration in ms. + @retval 0 The parameters were not valid. +**/ +UINT32 +CalculateTick ( + PING_PRIVATE_DATA *Private, + IN UINT32 Begin, + IN UINT32 End + ) +{ + if (End < Begin) { + return (0); + } + + return (End - Begin) * Private->TimerPeriod; +} + +/** + Destroy PING_ICMPX_TX_INFO, and recollect the memory. + + @param[in] TxInfo The pointer to PING_ICMPX_TX_INFO. + @param[in] IpChoice Whether the token is IPv4 or IPv6 +**/ +VOID +PingDestroyTxInfo ( + IN PING_ICMPX_TX_INFO *TxInfo, + IN UINT32 IpChoice + ) +{ + EFI_IP6_TRANSMIT_DATA *Ip6TxData; + EFI_IP4_TRANSMIT_DATA *Ip4TxData; + EFI_IP6_FRAGMENT_DATA *FragData; + UINTN Index; + + if (TxInfo == NULL) { + return; + } + + if (TxInfo->Token != NULL) { + + if (TxInfo->Token->Event != NULL) { + gBS->CloseEvent (TxInfo->Token->Event); + } + + if (TxInfo->Token->Packet.TxData != NULL) { + if (IpChoice == PING_IP_CHOICE_IP6) { + Ip6TxData = TxInfo->Token->Packet.TxData; + + if (Ip6TxData->OverrideData != NULL) { + FreePool (Ip6TxData->OverrideData); + } + + if (Ip6TxData->ExtHdrs != NULL) { + FreePool (Ip6TxData->ExtHdrs); + } + + for (Index = 0; Index < Ip6TxData->FragmentCount; Index++) { + FragData = Ip6TxData->FragmentTable[Index].FragmentBuffer; + if (FragData != NULL) { + FreePool (FragData); + } + } + } else { + Ip4TxData = TxInfo->Token->Packet.TxData; + + if (Ip4TxData->OverrideData != NULL) { + FreePool (Ip4TxData->OverrideData); + } + + for (Index = 0; Index < Ip4TxData->FragmentCount; Index++) { + FragData = Ip4TxData->FragmentTable[Index].FragmentBuffer; + if (FragData != NULL) { + FreePool (FragData); + } + } + } + } + + FreePool (TxInfo->Token); + } + + FreePool (TxInfo); +} + +/** + Match the request, and reply with SequenceNum/TimeStamp. + + @param[in] Private The pointer to PING_PRIVATE_DATA. + @param[in] Packet The pointer to ICMPX_ECHO_REQUEST_REPLY. + + @retval EFI_SUCCESS The match is successful. + @retval EFI_NOT_FOUND The reply can't be matched with any request. + +**/ +EFI_STATUS +Ping6MatchEchoReply ( + IN PING_PRIVATE_DATA *Private, + IN ICMPX_ECHO_REQUEST_REPLY *Packet + ) +{ + PING_ICMPX_TX_INFO *TxInfo; + LIST_ENTRY *Entry; + LIST_ENTRY *NextEntry; + + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) { + TxInfo = BASE_CR (Entry, PING_ICMPX_TX_INFO, Link); + + if ((TxInfo->SequenceNum == Packet->SequenceNum) && (TxInfo->TimeStamp == Packet->TimeStamp)) { + Private->RxCount++; + RemoveEntryList (&TxInfo->Link); + PingDestroyTxInfo (TxInfo, Private->IpChoice); + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + +/** + The original intention is to send a request. + Currently, the application retransmits an icmp6 echo request packet + per second in sendnumber times that is specified by the user. + Because nothing can be done here, all things move to the timer rountine. + + @param[in] Event A EFI_EVENT type event. + @param[in] Context The pointer to Context. + +**/ +VOID +EFIAPI +Ping6OnEchoRequestSent ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ +} + +/** + receive reply, match and print reply infomation. + + @param[in] Event A EFI_EVENT type event. + @param[in] Context The pointer to context. + +**/ +VOID +EFIAPI +Ping6OnEchoReplyReceived ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + PING_PRIVATE_DATA *Private; + ICMPX_ECHO_REQUEST_REPLY *Reply; + UINT32 PayLoad; + UINT32 Rtt; + + Private = (PING_PRIVATE_DATA *) Context; + + if (Private == NULL || Private->Status == EFI_ABORTED || Private->Signature != PING_PRIVATE_DATA_SIGNATURE) { + return; + } + + if (Private->RxToken.Packet.RxData == NULL) { + return; + } + + if (Private->IpChoice == PING_IP_CHOICE_IP6) { + Reply = ((EFI_IP6_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->FragmentTable[0].FragmentBuffer; + PayLoad = ((EFI_IP6_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->DataLength; + if (((EFI_IP6_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->Header->NextHeader != IP6_ICMP) { + goto ON_EXIT; + } + if (!IP6_IS_MULTICAST ((EFI_IPv6_ADDRESS*)&Private->DstAddress) && + !EFI_IP6_EQUAL (&((EFI_IP6_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->Header->SourceAddress, (EFI_IPv6_ADDRESS*)&Private->DstAddress)) { + goto ON_EXIT; + } + + if ((Reply->Type != ICMP_V6_ECHO_REPLY) || (Reply->Code != 0)) { + goto ON_EXIT; + } + } else { + Reply = ((EFI_IP4_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->FragmentTable[0].FragmentBuffer; + PayLoad = ((EFI_IP4_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->DataLength; + if (!IP4_IS_MULTICAST (EFI_IP4(*(EFI_IPv4_ADDRESS*)Private->DstAddress)) && + !EFI_IP4_EQUAL (&((EFI_IP4_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->Header->SourceAddress, (EFI_IPv4_ADDRESS*)&Private->DstAddress)) { + goto ON_EXIT; + } + + if ((Reply->Type != ICMP_V4_ECHO_REPLY) || (Reply->Code != 0)) { + goto ON_EXIT; + } + } + + + if (PayLoad != Private->BufferSize) { + goto ON_EXIT; + } + // + // Check whether the reply matches the sent request before. + // + Status = Ping6MatchEchoReply (Private, Reply); + if (EFI_ERROR(Status)) { + goto ON_EXIT; + } + // + // Display statistics on this icmp6 echo reply packet. + // + Rtt = CalculateTick (Private, Reply->TimeStamp, ReadTime (Private)); + + Private->RttSum += Rtt; + Private->RttMin = Private->RttMin > Rtt ? Rtt : Private->RttMin; + Private->RttMax = Private->RttMax < Rtt ? Rtt : Private->RttMax; + + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_PING_REPLY_INFO), + gShellNetwork1HiiHandle, + PayLoad, + mDstString, + Reply->SequenceNum, + Private->IpChoice == PING_IP_CHOICE_IP6?((EFI_IP6_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->Header->HopLimit:0, + Rtt, + Rtt + Private->TimerPeriod + ); + +ON_EXIT: + + // + // Recycle the packet before reusing RxToken + // + gBS->SignalEvent (Private->IpChoice == PING_IP_CHOICE_IP6?((EFI_IP6_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->RecycleSignal:((EFI_IP4_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->RecycleSignal); + + if (Private->RxCount < Private->SendNum) { + // + // Continue to receive icmp echo reply packets. + // + Private->RxToken.Status = EFI_ABORTED; + + Status = Private->ProtocolPointers.Receive (Private->IpProtocol, &Private->RxToken); + + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_RECEIVE), gShellNetwork1HiiHandle, Status); + Private->Status = EFI_ABORTED; + } + } else { + // + // All reply have already been received from the dest host. + // + Private->Status = EFI_SUCCESS; + } +} + +/** + Create a PING_IPX_COMPLETION_TOKEN. + + @param[in] Private The pointer of PING_PRIVATE_DATA. + @param[in] TimeStamp The TimeStamp of request. + @param[in] SequenceNum The SequenceNum of request. + + @return The pointer of PING_IPX_COMPLETION_TOKEN. + +**/ +PING_IPX_COMPLETION_TOKEN * +PingGenerateToken ( + IN PING_PRIVATE_DATA *Private, + IN UINT32 TimeStamp, + IN UINT16 SequenceNum + ) +{ + EFI_STATUS Status; + PING_IPX_COMPLETION_TOKEN *Token; + VOID *TxData; + ICMPX_ECHO_REQUEST_REPLY *Request; + UINT16 HeadSum; + UINT16 TempChecksum; + + Request = AllocateZeroPool (Private->BufferSize); + if (Request == NULL) { + return NULL; + } + TxData = AllocateZeroPool (Private->IpChoice==PING_IP_CHOICE_IP6?sizeof (EFI_IP6_TRANSMIT_DATA):sizeof (EFI_IP4_TRANSMIT_DATA)); + if (TxData == NULL) { + FreePool (Request); + return NULL; + } + Token = AllocateZeroPool (sizeof (PING_IPX_COMPLETION_TOKEN)); + if (Token == NULL) { + FreePool (Request); + FreePool (TxData); + return NULL; + } + + // + // Assembly echo request packet. + // + Request->Type = (UINT8)(Private->IpChoice==PING_IP_CHOICE_IP6?ICMP_V6_ECHO_REQUEST:ICMP_V4_ECHO_REQUEST); + Request->Code = 0; + Request->SequenceNum = SequenceNum; + Request->Identifier = 0; + Request->Checksum = 0; + + // + // Assembly token for transmit. + // + if (Private->IpChoice==PING_IP_CHOICE_IP6) { + Request->TimeStamp = TimeStamp; + ((EFI_IP6_TRANSMIT_DATA*)TxData)->ExtHdrsLength = 0; + ((EFI_IP6_TRANSMIT_DATA*)TxData)->ExtHdrs = NULL; + ((EFI_IP6_TRANSMIT_DATA*)TxData)->OverrideData = 0; + ((EFI_IP6_TRANSMIT_DATA*)TxData)->DataLength = Private->BufferSize; + ((EFI_IP6_TRANSMIT_DATA*)TxData)->FragmentCount = 1; + ((EFI_IP6_TRANSMIT_DATA*)TxData)->FragmentTable[0].FragmentBuffer = (VOID *) Request; + ((EFI_IP6_TRANSMIT_DATA*)TxData)->FragmentTable[0].FragmentLength = Private->BufferSize; + } else { + ((EFI_IP4_TRANSMIT_DATA*)TxData)->OptionsLength = 0; + ((EFI_IP4_TRANSMIT_DATA*)TxData)->OptionsBuffer = NULL; + ((EFI_IP4_TRANSMIT_DATA*)TxData)->OverrideData = 0; + ((EFI_IP4_TRANSMIT_DATA*)TxData)->TotalDataLength = Private->BufferSize; + ((EFI_IP4_TRANSMIT_DATA*)TxData)->FragmentCount = 1; + ((EFI_IP4_TRANSMIT_DATA*)TxData)->FragmentTable[0].FragmentBuffer = (VOID *) Request; + ((EFI_IP4_TRANSMIT_DATA*)TxData)->FragmentTable[0].FragmentLength = Private->BufferSize; + ((EFI_IP4_TRANSMIT_DATA*)TxData)->DestinationAddress.Addr[0] = Private->DstAddress[0]; + ((EFI_IP4_TRANSMIT_DATA*)TxData)->DestinationAddress.Addr[1] = Private->DstAddress[1]; + ((EFI_IP4_TRANSMIT_DATA*)TxData)->DestinationAddress.Addr[2] = Private->DstAddress[2]; + ((EFI_IP4_TRANSMIT_DATA*)TxData)->DestinationAddress.Addr[3] = Private->DstAddress[3]; + + HeadSum = NetChecksum ((UINT8 *) Request, Private->BufferSize); + Request->TimeStamp = TimeStamp; + TempChecksum = NetChecksum ((UINT8 *) &Request->TimeStamp, sizeof (UINT64)); + Request->Checksum = (UINT16)(~NetAddChecksum (HeadSum, TempChecksum)); + } + + + Token->Status = EFI_ABORTED; + Token->Packet.TxData = TxData; + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + Ping6OnEchoRequestSent, + Private, + &Token->Event + ); + + if (EFI_ERROR (Status)) { + FreePool (Request); + FreePool (TxData); + FreePool (Token); + return NULL; + } + + return Token; +} + +/** + Transmit the PING_IPX_COMPLETION_TOKEN. + + @param[in] Private The pointer of PING_PRIVATE_DATA. + + @retval EFI_SUCCESS Transmitted successfully. + @retval EFI_OUT_OF_RESOURCES No memory is available on the platform. + @retval others Transmitted unsuccessfully. + +**/ +EFI_STATUS +PingSendEchoRequest ( + IN PING_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + PING_ICMPX_TX_INFO *TxInfo; + + TxInfo = AllocateZeroPool (sizeof (PING_ICMPX_TX_INFO)); + + if (TxInfo == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + TxInfo->TimeStamp = ReadTime (Private); + TxInfo->SequenceNum = (UINT16) (Private->TxCount + 1); + TxInfo->Token = PingGenerateToken ( + Private, + TxInfo->TimeStamp, + TxInfo->SequenceNum + ); + + if (TxInfo->Token == NULL) { + PingDestroyTxInfo (TxInfo, Private->IpChoice); + return EFI_OUT_OF_RESOURCES; + } + + ASSERT(Private->ProtocolPointers.Transmit != NULL); + + InsertTailList (&Private->TxList, &TxInfo->Link); + + Status = Private->ProtocolPointers.Transmit (Private->IpProtocol, TxInfo->Token); + + if (EFI_ERROR (Status)) { + RemoveEntryList (&TxInfo->Link); + PingDestroyTxInfo (TxInfo, Private->IpChoice); + return Status; + } + + Private->TxCount++; + + return EFI_SUCCESS; +} + +/** + Place a completion token into the receive packet queue to receive the echo reply. + + @param[in] Private The pointer of PING_PRIVATE_DATA. + + @retval EFI_SUCCESS Put the token into the receive packet queue successfully. + @retval others Put the token into the receive packet queue unsuccessfully. + +**/ +EFI_STATUS +Ping6ReceiveEchoReply ( + IN PING_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + + ZeroMem (&Private->RxToken, sizeof (PING_IPX_COMPLETION_TOKEN)); + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + Ping6OnEchoReplyReceived, + Private, + &Private->RxToken.Event + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + Private->RxToken.Status = EFI_NOT_READY; + + Status = Private->ProtocolPointers.Receive (Private->IpProtocol, &Private->RxToken); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_RECEIVE), gShellNetwork1HiiHandle, Status); + } + return Status; +} + +/** + Remove the timeout request from the list. + + @param[in] Event A EFI_EVENT type event. + @param[in] Context The pointer to Context. + +**/ +VOID +EFIAPI +Ping6OnTimerRoutine ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + PING_PRIVATE_DATA *Private; + PING_ICMPX_TX_INFO *TxInfo; + LIST_ENTRY *Entry; + LIST_ENTRY *NextEntry; + UINT64 Time; + + Private = (PING_PRIVATE_DATA *) Context; + if (Private->Signature != PING_PRIVATE_DATA_SIGNATURE) { + Private->Status = EFI_NOT_FOUND; + return; + } + + // + // Retransmit icmp6 echo request packets per second in sendnumber times. + // + if (Private->TxCount < Private->SendNum) { + + Status = PingSendEchoRequest (Private); + if (Private->TxCount != 0){ + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_SEND_REQUEST), gShellNetwork1HiiHandle, Private->TxCount + 1); + } + } + } + // + // Check whether any icmp6 echo request in the list timeout. + // + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) { + TxInfo = BASE_CR (Entry, PING_ICMPX_TX_INFO, Link); + Time = CalculateTick (Private, TxInfo->TimeStamp, ReadTime (Private)); + + // + // Remove the timeout echo request from txlist. + // + if (Time > DEFAULT_TIMEOUT) { + + if (EFI_ERROR (TxInfo->Token->Status)) { + Private->ProtocolPointers.Cancel (Private->IpProtocol, TxInfo->Token); + } + // + // Remove the timeout icmp6 echo request from list. + // + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_TIMEOUT), gShellNetwork1HiiHandle, TxInfo->SequenceNum); + + RemoveEntryList (&TxInfo->Link); + PingDestroyTxInfo (TxInfo, Private->IpChoice); + + Private->RxCount++; + Private->FailedCount++; + + if (IsListEmpty (&Private->TxList) && (Private->TxCount == Private->SendNum)) { + // + // All the left icmp6 echo request in the list timeout. + // + Private->Status = EFI_TIMEOUT; + } + } + } +} + +/** + Determine if a IP4 address is Link Local. + + 169.254.1.0 through 169.254.254.255 is link local. + + @param[in] Address The address to test. + + @retval TRUE It is. + @retval FALSE It is not. +**/ +BOOLEAN +PingNetIp4IsLinkLocalAddr ( + IN CONST EFI_IPv4_ADDRESS *Address + ) +{ + return ((BOOLEAN)(Address->Addr[0] == 169 && Address->Addr[1] == 254 && Address->Addr[2] >= 1 && Address->Addr[2] <= 254)); +} + +/** + Determine if a IP4 address is unspecified. + + @param[in] Address The address to test. + + @retval TRUE It is. + @retval FALSE It is not. +**/ +BOOLEAN +PingNetIp4IsUnspecifiedAddr ( + IN CONST EFI_IPv4_ADDRESS *Address + ) +{ + return ((BOOLEAN)((ReadUnaligned32 ((UINT32*)&Address->Addr[0])) == 0x00000000)); +} + +/** + Create a valid IP instance. + + @param[in] Private The pointer of PING_PRIVATE_DATA. + + @retval EFI_SUCCESS Create a valid IPx instance successfully. + @retval EFI_ABORTED Locate handle with ipx service binding protocol unsuccessfully. + @retval EFI_INVALID_PARAMETER The source address is unspecified when the destination address is a link-local address. + @retval EFI_OUT_OF_RESOURCES No memory is available on the platform. + @retval EFI_NOT_FOUND The source address is not found. +**/ +EFI_STATUS +PingCreateIpInstance ( + IN PING_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + UINTN HandleIndex; + UINTN HandleNum; + EFI_HANDLE *HandleBuffer; + BOOLEAN UnspecifiedSrc; + EFI_STATUS MediaStatus; + EFI_SERVICE_BINDING_PROTOCOL *EfiSb; + VOID *IpXCfg; + EFI_IP6_CONFIG_DATA Ip6Config; + EFI_IP4_CONFIG_DATA Ip4Config; + VOID *IpXInterfaceInfo; + UINTN IfInfoSize; + EFI_IPv6_ADDRESS *Addr; + UINTN AddrIndex; + + HandleBuffer = NULL; + UnspecifiedSrc = FALSE; + MediaStatus = EFI_SUCCESS; + EfiSb = NULL; + IpXInterfaceInfo = NULL; + IfInfoSize = 0; + + // + // Locate all the handles with ip6 service binding protocol. + // + Status = gBS->LocateHandleBuffer ( + ByProtocol, + Private->IpChoice == PING_IP_CHOICE_IP6?&gEfiIp6ServiceBindingProtocolGuid:&gEfiIp4ServiceBindingProtocolGuid, + NULL, + &HandleNum, + &HandleBuffer + ); + if (EFI_ERROR (Status) || (HandleNum == 0) || (HandleBuffer == NULL)) { + return EFI_ABORTED; + } + + if (Private->IpChoice == PING_IP_CHOICE_IP6 ? NetIp6IsUnspecifiedAddr ((EFI_IPv6_ADDRESS*)&Private->SrcAddress) : \ + PingNetIp4IsUnspecifiedAddr ((EFI_IPv4_ADDRESS*)&Private->SrcAddress)) { + // + // SrcAddress is unspecified. So, both connected and configured interface will be automatic selected. + // + UnspecifiedSrc = TRUE; + } + + // + // Source address is required when pinging a link-local address. + // + if (Private->IpChoice == PING_IP_CHOICE_IP6) { + if (NetIp6IsLinkLocalAddr ((EFI_IPv6_ADDRESS*)&Private->DstAddress) && UnspecifiedSrc) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_INVALID_SOURCE), gShellNetwork1HiiHandle); + Status = EFI_INVALID_PARAMETER; + goto ON_ERROR; + } + } else { + ASSERT(Private->IpChoice == PING_IP_CHOICE_IP4); + if (PingNetIp4IsLinkLocalAddr ((EFI_IPv4_ADDRESS*)&Private->DstAddress) && UnspecifiedSrc) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_INVALID_SOURCE), gShellNetwork1HiiHandle); + Status = EFI_INVALID_PARAMETER; + goto ON_ERROR; + } + } + + // + // For each ip6 protocol, check interface addresses list. + // + for (HandleIndex = 0; HandleIndex < HandleNum; HandleIndex++) { + EfiSb = NULL; + IpXInterfaceInfo = NULL; + IfInfoSize = 0; + + if (UnspecifiedSrc) { + // + // Check media. + // + NetLibDetectMediaWaitTimeout (HandleBuffer[HandleIndex], 0, &MediaStatus); + if (MediaStatus != EFI_SUCCESS) { + // + // Skip this one. + // + continue; + } + } + + Status = gBS->HandleProtocol ( + HandleBuffer[HandleIndex], + Private->IpChoice == PING_IP_CHOICE_IP6?&gEfiIp6ServiceBindingProtocolGuid:&gEfiIp4ServiceBindingProtocolGuid, + (VOID **) &EfiSb + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Ip6config protocol and ip6 service binding protocol are installed + // on the same handle. + // + Status = gBS->HandleProtocol ( + HandleBuffer[HandleIndex], + Private->IpChoice == PING_IP_CHOICE_IP6?&gEfiIp6ConfigProtocolGuid:&gEfiIp4Config2ProtocolGuid, + (VOID **) &IpXCfg + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + // + // Get the interface information size. + // + if (Private->IpChoice == PING_IP_CHOICE_IP6) { + Status = ((EFI_IP6_CONFIG_PROTOCOL*)IpXCfg)->GetData ( + IpXCfg, + Ip6ConfigDataTypeInterfaceInfo, + &IfInfoSize, + NULL + ); + } else { + Status = ((EFI_IP4_CONFIG2_PROTOCOL*)IpXCfg)->GetData ( + IpXCfg, + Ip4Config2DataTypeInterfaceInfo, + &IfInfoSize, + NULL + ); + } + + // + // Skip the ones not in current use. + // + if (Status == EFI_NOT_STARTED) { + continue; + } + + if (Status != EFI_BUFFER_TOO_SMALL) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_GETDATA), gShellNetwork1HiiHandle, Status); + goto ON_ERROR; + } + + IpXInterfaceInfo = AllocateZeroPool (IfInfoSize); + + if (IpXInterfaceInfo == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + // + // Get the interface info. + // + if (Private->IpChoice == PING_IP_CHOICE_IP6) { + Status = ((EFI_IP6_CONFIG_PROTOCOL*)IpXCfg)->GetData ( + IpXCfg, + Ip6ConfigDataTypeInterfaceInfo, + &IfInfoSize, + IpXInterfaceInfo + ); + } else { + Status = ((EFI_IP4_CONFIG2_PROTOCOL*)IpXCfg)->GetData ( + IpXCfg, + Ip4Config2DataTypeInterfaceInfo, + &IfInfoSize, + IpXInterfaceInfo + ); + } + + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_GETDATA), gShellNetwork1HiiHandle, Status); + goto ON_ERROR; + } + // + // Check whether the source address is one of the interface addresses. + // + if (Private->IpChoice == PING_IP_CHOICE_IP6) { + for (AddrIndex = 0; AddrIndex < ((EFI_IP6_CONFIG_INTERFACE_INFO*)IpXInterfaceInfo)->AddressInfoCount; AddrIndex++) { + Addr = &(((EFI_IP6_CONFIG_INTERFACE_INFO*)IpXInterfaceInfo)->AddressInfo[AddrIndex].Address); + + if (UnspecifiedSrc) { + if (!NetIp6IsUnspecifiedAddr (Addr) && !NetIp6IsLinkLocalAddr (Addr)) { + // + // Select the interface automatically. + // + CopyMem(&Private->SrcAddress, Addr, sizeof(Private->SrcAddress)); + break; + } + } else if (EFI_IP6_EQUAL (&Private->SrcAddress, Addr)) { + // + // Match a certain interface address. + // + break; + } + } + + if (AddrIndex < ((EFI_IP6_CONFIG_INTERFACE_INFO*)IpXInterfaceInfo)->AddressInfoCount) { + // + // Found a nic handle with right interface address. + // + break; + } + } else { + if (UnspecifiedSrc) { + if (!PingNetIp4IsUnspecifiedAddr (&((EFI_IP4_CONFIG2_INTERFACE_INFO*)IpXInterfaceInfo)->StationAddress) && + !PingNetIp4IsLinkLocalAddr (&((EFI_IP4_CONFIG2_INTERFACE_INFO*)IpXInterfaceInfo)->StationAddress)) { + // + // Select the interface automatically. + // + break; + } + } else if (EFI_IP4_EQUAL (&Private->SrcAddress, &((EFI_IP4_CONFIG2_INTERFACE_INFO*)IpXInterfaceInfo)->StationAddress)) { + // + // Match a certain interface address. + // + break; + } + } + + FreePool (IpXInterfaceInfo); + IpXInterfaceInfo = NULL; + } + // + // No exact interface address matched. + // + + if (HandleIndex == HandleNum) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_CONFIGD_NIC_NF), gShellNetwork1HiiHandle, L"ping"); + Status = EFI_NOT_FOUND; + goto ON_ERROR; + } + + Private->NicHandle = HandleBuffer[HandleIndex]; + + ASSERT (EfiSb != NULL); + Status = EfiSb->CreateChild (EfiSb, &Private->IpChildHandle); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + if (Private->IpChoice == PING_IP_CHOICE_IP6) { + Status = gBS->OpenProtocol ( + Private->IpChildHandle, + &gEfiIp6ProtocolGuid, + &Private->IpProtocol, + gImageHandle, + Private->IpChildHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + + ZeroMem (&Ip6Config, sizeof (EFI_IP6_CONFIG_DATA)); + + // + // Configure the ip6 instance for icmp6 packet exchange. + // + Ip6Config.DefaultProtocol = 58; + Ip6Config.AcceptAnyProtocol = FALSE; + Ip6Config.AcceptIcmpErrors = TRUE; + Ip6Config.AcceptPromiscuous = FALSE; + Ip6Config.TrafficClass = 0; + Ip6Config.HopLimit = 128; + Ip6Config.FlowLabel = 0; + Ip6Config.ReceiveTimeout = 0; + Ip6Config.TransmitTimeout = 0; + + IP6_COPY_ADDRESS (&Ip6Config.StationAddress, &Private->SrcAddress); + IP6_COPY_ADDRESS (&Ip6Config.DestinationAddress, &Private->DstAddress); + + Status = ((EFI_IP6_PROTOCOL*)(Private->IpProtocol))->Configure (Private->IpProtocol, &Ip6Config); + + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_CONFIG), gShellNetwork1HiiHandle, Status); + goto ON_ERROR; + } + + Private->ProtocolPointers.Transmit = (PING_IPX_TRANSMIT )((EFI_IP6_PROTOCOL*)Private->IpProtocol)->Transmit; + Private->ProtocolPointers.Receive = (PING_IPX_RECEIVE )((EFI_IP6_PROTOCOL*)Private->IpProtocol)->Receive; + Private->ProtocolPointers.Cancel = (PING_IPX_CANCEL )((EFI_IP6_PROTOCOL*)Private->IpProtocol)->Cancel; + Private->ProtocolPointers.Poll = (PING_IPX_POLL )((EFI_IP6_PROTOCOL*)Private->IpProtocol)->Poll; + } else { + Status = gBS->OpenProtocol ( + Private->IpChildHandle, + &gEfiIp4ProtocolGuid, + &Private->IpProtocol, + gImageHandle, + Private->IpChildHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + + ZeroMem (&Ip4Config, sizeof (EFI_IP4_CONFIG_DATA)); + + // + // Configure the ip4 instance for icmp4 packet exchange. + // + Ip4Config.DefaultProtocol = 1; + Ip4Config.AcceptAnyProtocol = FALSE; + Ip4Config.AcceptBroadcast = FALSE; + Ip4Config.AcceptIcmpErrors = TRUE; + Ip4Config.AcceptPromiscuous = FALSE; + Ip4Config.DoNotFragment = FALSE; + Ip4Config.RawData = FALSE; + Ip4Config.ReceiveTimeout = 0; + Ip4Config.TransmitTimeout = 0; + Ip4Config.UseDefaultAddress = TRUE; + Ip4Config.TimeToLive = 128; + Ip4Config.TypeOfService = 0; + + Status = ((EFI_IP4_PROTOCOL*)(Private->IpProtocol))->Configure (Private->IpProtocol, &Ip4Config); + + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_CONFIG), gShellNetwork1HiiHandle, Status); + goto ON_ERROR; + } + + Private->ProtocolPointers.Transmit = (PING_IPX_TRANSMIT )((EFI_IP4_PROTOCOL*)Private->IpProtocol)->Transmit; + Private->ProtocolPointers.Receive = (PING_IPX_RECEIVE )((EFI_IP4_PROTOCOL*)Private->IpProtocol)->Receive; + Private->ProtocolPointers.Cancel = (PING_IPX_CANCEL )((EFI_IP4_PROTOCOL*)Private->IpProtocol)->Cancel; + Private->ProtocolPointers.Poll = (PING_IPX_POLL )((EFI_IP4_PROTOCOL*)Private->IpProtocol)->Poll; + } + + if (HandleBuffer != NULL) { + FreePool (HandleBuffer); + } + + return EFI_SUCCESS; + +ON_ERROR: + if (HandleBuffer != NULL) { + FreePool (HandleBuffer); + } + + if (IpXInterfaceInfo != NULL) { + FreePool (IpXInterfaceInfo); + } + + if ((EfiSb != NULL) && (Private->IpChildHandle != NULL)) { + EfiSb->DestroyChild (EfiSb, Private->IpChildHandle); + } + + return Status; +} + +/** + Destroy the IP instance. + + @param[in] Private The pointer of PING_PRIVATE_DATA. + +**/ +VOID +Ping6DestroyIp6Instance ( + IN PING_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + EFI_SERVICE_BINDING_PROTOCOL *IpSb; + + gBS->CloseProtocol ( + Private->IpChildHandle, + Private->IpChoice == PING_IP_CHOICE_IP6?&gEfiIp6ProtocolGuid:&gEfiIp4ProtocolGuid, + gImageHandle, + Private->IpChildHandle + ); + + Status = gBS->HandleProtocol ( + Private->NicHandle, + Private->IpChoice == PING_IP_CHOICE_IP6?&gEfiIp6ServiceBindingProtocolGuid:&gEfiIp4ServiceBindingProtocolGuid, + (VOID **) &IpSb + ); + + if (!EFI_ERROR(Status)) { + IpSb->DestroyChild (IpSb, Private->IpChildHandle); + } +} + + +/** + The Ping Process. + + @param[in] SendNumber The send request count. + @param[in] BufferSize The send buffer size. + @param[in] SrcAddress The source address. + @param[in] DstAddress The destination address. + @param[in] IpChoice The choice between IPv4 and IPv6. + + @retval SHELL_SUCCESS The ping processed successfullly. + @retval others The ping processed unsuccessfully. +**/ +SHELL_STATUS +ShellPing ( + IN UINT32 SendNumber, + IN UINT32 BufferSize, + IN EFI_IPv6_ADDRESS *SrcAddress, + IN EFI_IPv6_ADDRESS *DstAddress, + IN UINT32 IpChoice + ) +{ + EFI_STATUS Status; + PING_PRIVATE_DATA *Private; + PING_ICMPX_TX_INFO *TxInfo; + LIST_ENTRY *Entry; + LIST_ENTRY *NextEntry; + SHELL_STATUS ShellStatus; + + ShellStatus = SHELL_SUCCESS; + Private = AllocateZeroPool (sizeof (PING_PRIVATE_DATA)); + + if (Private == NULL) { + return (SHELL_OUT_OF_RESOURCES); + } + + Private->IpChoice = IpChoice; + Private->Signature = PING_PRIVATE_DATA_SIGNATURE; + Private->SendNum = SendNumber; + Private->BufferSize = BufferSize; + Private->RttMin = ~((UINT64 )(0x0)); + Private->Status = EFI_NOT_READY; + + CopyMem(&Private->SrcAddress, SrcAddress, sizeof(Private->SrcAddress)); + CopyMem(&Private->DstAddress, DstAddress, sizeof(Private->DstAddress)); + + InitializeListHead (&Private->TxList); + + // + // Open and configure a ip instance for us. + // + Status = PingCreateIpInstance (Private); + + if (EFI_ERROR (Status)) { + ShellStatus = SHELL_ACCESS_DENIED; + goto ON_EXIT; + } + // + // Print the command line itself. + // + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_START), gShellNetwork1HiiHandle, mDstString, Private->BufferSize); + // + // Create a ipv6 token to receive the first icmp6 echo reply packet. + // + Status = Ping6ReceiveEchoReply (Private); + + if (EFI_ERROR (Status)) { + ShellStatus = SHELL_ACCESS_DENIED; + goto ON_EXIT; + } + // + // Create and start timer to send icmp6 echo request packet per second. + // + Status = gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + Ping6OnTimerRoutine, + Private, + &Private->Timer + ); + + if (EFI_ERROR (Status)) { + ShellStatus = SHELL_ACCESS_DENIED; + goto ON_EXIT; + } + + // + // Start a timer to calculate the RTT. + // + Status = PingInitRttTimer (Private); + if (EFI_ERROR (Status)) { + ShellStatus = SHELL_ACCESS_DENIED; + goto ON_EXIT; + } + + // + // Create a ipv6 token to send the first icmp6 echo request packet. + // + Status = PingSendEchoRequest (Private); + // + // EFI_NOT_READY for IPsec is enable and IKE is not established. + // + if (EFI_ERROR (Status) && (Status != EFI_NOT_READY)) { + ShellStatus = SHELL_ACCESS_DENIED; + if(Status == EFI_NOT_FOUND) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_NOSOURCE_INDO), gShellNetwork1HiiHandle, mDstString); + } else if (Status == RETURN_NO_MAPPING) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_NOROUTE_FOUND), gShellNetwork1HiiHandle, mDstString, mSrcString); + } else { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_NETWORK_ERROR), gShellNetwork1HiiHandle, L"ping", Status); + } + + goto ON_EXIT; + } + + Status = gBS->SetTimer ( + Private->Timer, + TimerPeriodic, + ONE_SECOND + ); + + if (EFI_ERROR (Status)) { + ShellStatus = SHELL_ACCESS_DENIED; + goto ON_EXIT; + } + // + // Control the ping6 process by two factors: + // 1. Hot key + // 2. Private->Status + // 2.1. success means all icmp6 echo request packets get reply packets. + // 2.2. timeout means the last icmp6 echo reply request timeout to get reply. + // 2.3. noready means ping6 process is on-the-go. + // + while (Private->Status == EFI_NOT_READY) { + Status = Private->ProtocolPointers.Poll (Private->IpProtocol); + if (ShellGetExecutionBreakFlag()) { + Private->Status = EFI_ABORTED; + goto ON_STAT; + } + } + +ON_STAT: + // + // Display the statistics in all. + // + gBS->SetTimer (Private->Timer, TimerCancel, 0); + + if (Private->TxCount != 0) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_PING_STAT), + gShellNetwork1HiiHandle, + Private->TxCount, + (Private->RxCount - Private->FailedCount), + (100 - ((100 * (Private->RxCount - Private->FailedCount)) / Private->TxCount)), + Private->RttSum + ); + } + + if (Private->RxCount > Private->FailedCount) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_PING_RTT), + gShellNetwork1HiiHandle, + Private->RttMin, + Private->RttMin + Private->TimerPeriod, + Private->RttMax, + Private->RttMax + Private->TimerPeriod, + DivU64x64Remainder (Private->RttSum, (Private->RxCount - Private->FailedCount), NULL), + DivU64x64Remainder (Private->RttSum, (Private->RxCount - Private->FailedCount), NULL) + Private->TimerPeriod + ); + } + +ON_EXIT: + + if (Private != NULL) { + + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) { + TxInfo = BASE_CR (Entry, PING_ICMPX_TX_INFO, Link); + + if (Private->IpProtocol != NULL && Private->ProtocolPointers.Cancel != NULL) { + Status = Private->ProtocolPointers.Cancel (Private->IpProtocol, TxInfo->Token); + } + + RemoveEntryList (&TxInfo->Link); + PingDestroyTxInfo (TxInfo, Private->IpChoice); + } + + PingFreeRttTimer (Private); + + if (Private->Timer != NULL) { + gBS->CloseEvent (Private->Timer); + } + + if (Private->IpProtocol != NULL && Private->ProtocolPointers.Cancel != NULL) { + Status = Private->ProtocolPointers.Cancel (Private->IpProtocol, &Private->RxToken); + } + + if (Private->RxToken.Event != NULL) { + gBS->CloseEvent (Private->RxToken.Event); + } + + if (Private->IpChildHandle != NULL) { + Ping6DestroyIp6Instance (Private); + } + + FreePool (Private); + } + + return ShellStatus; +} + +/** + Function for 'ping' command. + + @param[in] ImageHandle Handle to the Image (NULL if Internal). + @param[in] SystemTable Pointer to the System Table (NULL if Internal). + + @retval SHELL_SUCCESS The ping processed successfullly. + @retval others The ping processed unsuccessfully. + +**/ +SHELL_STATUS +EFIAPI +ShellCommandRunPing ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + SHELL_STATUS ShellStatus; + EFI_IPv6_ADDRESS DstAddress; + EFI_IPv6_ADDRESS SrcAddress; + UINT64 BufferSize; + UINTN SendNumber; + LIST_ENTRY *ParamPackage; + CONST CHAR16 *ValueStr; + UINTN NonOptionCount; + UINT32 IpChoice; + CHAR16 *ProblemParam; + + // + // we use IPv6 buffers to hold items... + // make sure this is enough space! + // + ASSERT(sizeof(EFI_IPv4_ADDRESS ) <= sizeof(EFI_IPv6_ADDRESS )); + ASSERT(sizeof(EFI_IP4_COMPLETION_TOKEN) <= sizeof(EFI_IP6_COMPLETION_TOKEN )); + + IpChoice = PING_IP_CHOICE_IP4; + + ShellStatus = SHELL_SUCCESS; + ProblemParam = NULL; + + Status = ShellCommandLineParseEx (PingParamList, &ParamPackage, &ProblemParam, TRUE, FALSE); + if (EFI_ERROR(Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellNetwork1HiiHandle, L"ping", ProblemParam); + ShellStatus = SHELL_INVALID_PARAMETER; + goto ON_EXIT; + } + + if (ShellCommandLineGetFlag (ParamPackage, L"-_ip6")) { + IpChoice = PING_IP_CHOICE_IP6; + } + + // + // Parse the parameter of count number. + // + ValueStr = ShellCommandLineGetValue (ParamPackage, L"-n"); + if (ValueStr != NULL) { + SendNumber = ShellStrToUintn (ValueStr); + + // + // ShellStrToUintn will return 0 when input is 0 or an invalid input string. + // + if ((SendNumber == 0) || (SendNumber > MAX_SEND_NUMBER)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellNetwork1HiiHandle, L"ping", ValueStr); + ShellStatus = SHELL_INVALID_PARAMETER; + goto ON_EXIT; + } + } else { + SendNumber = DEFAULT_SEND_COUNT; + } + // + // Parse the parameter of buffer size. + // + ValueStr = ShellCommandLineGetValue (ParamPackage, L"-l"); + if (ValueStr != NULL) { + BufferSize = ShellStrToUintn (ValueStr); + + // + // ShellStrToUintn will return 0 when input is 0 or an invalid input string. + // + if ((BufferSize < 16) || (BufferSize > MAX_BUFFER_SIZE)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellNetwork1HiiHandle, L"ping", ValueStr); + ShellStatus = SHELL_INVALID_PARAMETER; + goto ON_EXIT; + } + } else { + BufferSize = DEFAULT_BUFFER_SIZE; + } + + ZeroMem (&SrcAddress, sizeof (EFI_IPv6_ADDRESS)); + ZeroMem (&DstAddress, sizeof (EFI_IPv6_ADDRESS)); + + // + // Parse the parameter of source ip address. + // + ValueStr = ShellCommandLineGetValue (ParamPackage, L"-s"); + if (ValueStr == NULL) { + ValueStr = ShellCommandLineGetValue (ParamPackage, L"-_s"); + } + + if (ValueStr != NULL) { + mSrcString = ValueStr; + if (IpChoice == PING_IP_CHOICE_IP6) { + Status = NetLibStrToIp6 (ValueStr, &SrcAddress); + } else { + Status = NetLibStrToIp4 (ValueStr, (EFI_IPv4_ADDRESS*)&SrcAddress); + } + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellNetwork1HiiHandle, L"ping", ValueStr); + ShellStatus = SHELL_INVALID_PARAMETER; + goto ON_EXIT; + } + } + // + // Parse the parameter of destination ip address. + // + NonOptionCount = ShellCommandLineGetCount(ParamPackage); + if (NonOptionCount < 2) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_FEW), gShellNetwork1HiiHandle, L"ping"); + ShellStatus = SHELL_INVALID_PARAMETER; + goto ON_EXIT; + } + if (NonOptionCount > 2) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_MANY), gShellNetwork1HiiHandle, L"ping"); + ShellStatus = SHELL_INVALID_PARAMETER; + goto ON_EXIT; + } + ValueStr = ShellCommandLineGetRawValue (ParamPackage, 1); + if (ValueStr != NULL) { + mDstString = ValueStr; + if (IpChoice == PING_IP_CHOICE_IP6) { + Status = NetLibStrToIp6 (ValueStr, &DstAddress); + } else { + Status = NetLibStrToIp4 (ValueStr, (EFI_IPv4_ADDRESS*)&DstAddress); + } + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellNetwork1HiiHandle, L"ping", ValueStr); + ShellStatus = SHELL_INVALID_PARAMETER; + goto ON_EXIT; + } + } + + // + // Enter into ping process. + // + ShellStatus = ShellPing ( + (UINT32)SendNumber, + (UINT32)BufferSize, + &SrcAddress, + &DstAddress, + IpChoice + ); + +ON_EXIT: + ShellCommandLineFreeVarList (ParamPackage); + return ShellStatus; +} |