/** @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; | |
} |