Files

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1732 lines
50 KiB
C
Raw Permalink Normal View History

2011-03-25 20:47:02 +00:00
/** @file
The implementation for Ping shell command.
(C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR>
2018-06-27 21:13:38 +08:00
Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
(C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
2011-03-25 20:47:02 +00:00
SPDX-License-Identifier: BSD-2-Clause-Patent
2011-03-25 20:47:02 +00:00
**/
#include "UefiShellNetwork1CommandsLib.h"
#define PING_IP4_COPY_ADDRESS(Dest, Src) (CopyMem ((Dest), (Src), sizeof (EFI_IPv4_ADDRESS)))
UINT64 mCurrentTick = 0;
2011-03-25 20:47:02 +00:00
//
// Function templates to match the IPv4 and IPv6 commands that we use.
//
2018-06-27 21:13:38 +08:00
typedef
2011-03-25 20:47:02 +00:00
EFI_STATUS
(EFIAPI *PING_IPX_POLL)(
IN VOID *This
2018-06-27 21:13:38 +08:00
);
2011-03-25 20:47:02 +00:00
2018-06-27 21:13:38 +08:00
typedef
2011-03-25 20:47:02 +00:00
EFI_STATUS
(EFIAPI *PING_IPX_TRANSMIT)(
IN VOID *This,
IN VOID *Token
);
2018-06-27 21:13:38 +08:00
typedef
2011-03-25 20:47:02 +00:00
EFI_STATUS
(EFIAPI *PING_IPX_RECEIVE)(
IN VOID *This,
IN VOID *Token
2018-06-27 21:13:38 +08:00
);
2011-03-25 20:47:02 +00:00
typedef
EFI_STATUS
(EFIAPI *PING_IPX_CANCEL)(
IN VOID *This,
IN VOID *Token OPTIONAL
);
///
2018-06-27 21:13:38 +08:00
/// A set of pointers to either IPv6 or IPv4 functions.
2011-03-25 20:47:02 +00:00
/// 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
2018-06-27 21:13:38 +08:00
// structures are used for both transmit and receive operations.
2011-03-25 20:47:02 +00:00
// 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;
2011-03-25 20:47:02 +00:00
UINT8 Data[1];
} ICMPX_ECHO_REQUEST_REPLY;
#pragma pack()
typedef struct _PING_ICMP_TX_INFO {
LIST_ENTRY Link;
UINT16 SequenceNum;
UINT32 TimeStamp;
2011-03-25 20:47:02 +00:00
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
2011-03-25 20:47:02 +00:00
#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;
2018-06-27 21:13:38 +08:00
UINT32 RttTimerTick;
EFI_EVENT RttTimer;
2011-03-25 20:47:02 +00:00
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;
2011-03-25 20:47:02 +00:00
} 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.
2018-06-27 21:13:38 +08:00
@retval Checksum Returns the 16 bit ones complement of
ones complement sum of 16 bit words
**/
2011-03-25 20:47:02 +00:00
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) {
2011-03-25 20:47:02 +00:00
Sum += *Packet++;
}
if (Odd != 0) {
2011-03-25 20:47:02 +00:00
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
},
2011-03-25 20:47:02 +00:00
{
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;
UINT32 TimerPeriod;
RttTimerTick = 0;
StallCounter = 0;
TimerPeriod = 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);
TimerPeriod = StallCounter / RttTimerTick;
if (TimerPeriod != 0) {
return TimerPeriod;
} else {
return 1;
}
}
2011-03-25 20:47:02 +00:00
/**
Initialize the timer event for RTT (round trip time).
2011-03-25 20:47:02 +00:00
@param[in] Private The pointer to PING_PRIVATE_DATA.
@retval EFI_SUCCESS RTT timer is started.
@retval Others Failed to start the RTT timer.
2011-03-25 20:47:02 +00:00
**/
EFI_STATUS
PingInitRttTimer (
PING_PRIVATE_DATA *Private
2011-03-25 20:47:02 +00:00
)
{
EFI_STATUS Status;
2011-03-25 20:47:02 +00:00
Private->TimerPeriod = GetTimerPeriod ();
if (Private->TimerPeriod == 0) {
return EFI_ABORTED;
}
2018-06-27 21:13:38 +08:00
Private->RttTimerTick = 0;
Status = gBS->CreateEvent (
EVT_TIMER | EVT_NOTIFY_SIGNAL,
TPL_NOTIFY,
RttTimerTickRoutine,
&Private->RttTimerTick,
&Private->RttTimer
);
2011-03-25 20:47:02 +00:00
if (EFI_ERROR (Status)) {
return Status;
}
Status = gBS->SetTimer (
Private->RttTimer,
TimerPeriodic,
TICKS_PER_MS
);
2011-03-25 20:47:02 +00:00
if (EFI_ERROR (Status)) {
gBS->CloseEvent (Private->RttTimer);
return Status;
2011-03-25 20:47:02 +00:00
}
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.
2018-06-27 21:13:38 +08:00
@param[in] Private The pointer to PING_PRIVATE_DATA.
@retval the current tick value.
**/
UINT32
ReadTime (
PING_PRIVATE_DATA *Private
)
{
return Private->RttTimerTick;
}
2011-03-25 20:47:02 +00:00
/**
2015-03-17 01:58:10 +00:00
Calculate a duration in ms.
2011-03-25 20:47:02 +00:00
@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.
2011-03-25 20:47:02 +00:00
@return The duration in ms.
@retval 0 The parameters were not valid.
**/
UINT32
2011-03-25 20:47:02 +00:00
CalculateTick (
PING_PRIVATE_DATA *Private,
IN UINT32 Begin,
IN UINT32 End
2011-03-25 20:47:02 +00:00
)
{
if (End < Begin) {
2011-03-25 20:47:02 +00:00
return (0);
}
return (End - Begin) * Private->TimerPeriod;
2011-03-25 20:47:02 +00:00
}
/**
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.
2023-11-09 11:27:09 -08:00
Because nothing can be done here, all things move to the timer routine.
2011-03-25 20:47:02 +00:00
@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;
2011-03-25 20:47:02 +00:00
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;
}
2021-12-05 14:54:13 -08:00
2018-06-27 21:13:38 +08:00
if (!IP6_IS_MULTICAST ((EFI_IPv6_ADDRESS *)&Private->DstAddress) &&
2011-03-25 20:47:02 +00:00
!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;
2018-06-27 21:13:38 +08:00
if (!IP4_IS_MULTICAST (EFI_IP4 (*(EFI_IPv4_ADDRESS *)Private->DstAddress)) &&
2011-03-25 20:47:02 +00:00
!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;
}
}
2018-06-27 21:13:38 +08:00
2011-03-25 20:47:02 +00:00
if (PayLoad != Private->BufferSize) {
goto ON_EXIT;
}
2021-12-05 14:54:13 -08:00
2011-03-25 20:47:02 +00:00
//
// Check whether the reply matches the sent request before.
//
Status = Ping6MatchEchoReply (Private, Reply);
if (EFI_ERROR (Status)) {
goto ON_EXIT;
}
2021-12-05 14:54:13 -08:00
2011-03-25 20:47:02 +00:00
//
// Display statistics on this icmp6 echo reply packet.
//
Rtt = CalculateTick (Private, Reply->TimeStamp, ReadTime (Private));
2011-03-25 20:47:02 +00:00
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
2011-03-25 20:47:02 +00:00
);
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);
2011-03-25 20:47:02 +00:00
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);
2011-03-25 20:47:02 +00:00
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,
2011-03-25 20:47:02 +00:00
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;
}
2021-12-05 14:54:13 -08:00
2011-03-25 20:47:02 +00:00
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;
}
2021-12-05 14:54:13 -08:00
2011-03-25 20:47:02 +00:00
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;
2018-06-27 21:13:38 +08:00
Request->SequenceNum = SequenceNum;
2011-03-25 20:47:02 +00:00
Request->Identifier = 0;
Request->Checksum = 0;
//
// Assembly token for transmit.
//
if (Private->IpChoice == PING_IP_CHOICE_IP6) {
Request->TimeStamp = TimeStamp;
2011-03-25 20:47:02 +00:00
((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;
2011-03-25 20:47:02 +00:00
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);
2011-03-25 20:47:02 +00:00
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);
2018-02-16 14:15:11 +05:30
InsertTailList (&Private->TxList, &TxInfo->Link);
2011-03-25 20:47:02 +00:00
Status = Private->ProtocolPointers.Transmit (Private->IpProtocol, TxInfo->Token);
if (EFI_ERROR (Status)) {
2018-02-16 14:15:11 +05:30
RemoveEntryList (&TxInfo->Link);
2011-03-25 20:47:02 +00:00
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);
}
2021-12-05 14:54:13 -08:00
return Status;
2011-03-25 20:47:02 +00:00
}
/**
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);
}
}
}
2021-12-05 14:54:13 -08:00
2011-03-25 20:47:02 +00:00
//
// 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));
2011-03-25 20:47:02 +00:00
//
// Remove the timeout echo request from txlist.
//
if (Time > DEFAULT_TIMEOUT) {
if (EFI_ERROR (TxInfo->Token->Status)) {
Private->ProtocolPointers.Cancel (Private->IpProtocol, TxInfo->Token);
}
2021-12-05 14:54:13 -08:00
2011-03-25 20:47:02 +00:00
//
// 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++;
2011-03-25 20:47:02 +00:00
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;
2011-03-25 20:47:02 +00:00
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;
2011-03-25 20:47:02 +00:00
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)) {
2011-03-25 20:47:02 +00:00
return EFI_ABORTED;
}
if ((Private->IpChoice == PING_IP_CHOICE_IP6) ? NetIp6IsUnspecifiedAddr ((EFI_IPv6_ADDRESS *)&Private->SrcAddress) : \
PingNetIp4IsUnspecifiedAddr ((EFI_IPv4_ADDRESS *)&Private->SrcAddress))
{
//
2018-06-27 21:13:38 +08:00
// SrcAddress is unspecified. So, both connected and configured interface will be automatic selected.
//
UnspecifiedSrc = TRUE;
}
2018-06-27 21:13:38 +08:00
2011-03-25 20:47:02 +00:00
//
// Source address is required when pinging a link-local address.
2011-03-25 20:47:02 +00:00
//
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);
2011-03-25 20:47:02 +00:00
Status = EFI_INVALID_PARAMETER;
goto ON_ERROR;
}
} else {
ASSERT (Private->IpChoice == PING_IP_CHOICE_IP4);
if (PingNetIp4IsLinkLocalAddr ((EFI_IPv4_ADDRESS *)&Private->DstAddress) && UnspecifiedSrc) {
2018-06-27 21:13:38 +08:00
ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_INVALID_SOURCE), gShellNetwork1HiiHandle);
2011-03-25 20:47:02 +00:00
Status = EFI_INVALID_PARAMETER;
goto ON_ERROR;
}
}
2018-06-27 21:13:38 +08:00
2011-03-25 20:47:02 +00:00
//
// 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;
}
}
2011-03-25 20:47:02 +00:00
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;
}
2021-12-05 14:54:13 -08:00
//
// Get the interface information size.
//
if (Private->IpChoice == PING_IP_CHOICE_IP6) {
Status = ((EFI_IP6_CONFIG_PROTOCOL *)IpXCfg)->GetData (
IpXCfg,
Ip6ConfigDataTypeInterfaceInfo,
&IfInfoSize,
NULL
);
2011-03-25 20:47:02 +00:00
} else {
Status = ((EFI_IP4_CONFIG2_PROTOCOL *)IpXCfg)->GetData (
IpXCfg,
Ip4Config2DataTypeInterfaceInfo,
&IfInfoSize,
NULL
);
}
2018-06-27 21:13:38 +08:00
//
// Skip the ones not in current use.
//
if (Status == EFI_NOT_STARTED) {
continue;
}
2011-03-25 20:47:02 +00:00
if (Status != EFI_BUFFER_TOO_SMALL) {
ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_GETDATA), gShellNetwork1HiiHandle, Status);
goto ON_ERROR;
}
2011-03-25 20:47:02 +00:00
IpXInterfaceInfo = AllocateZeroPool (IfInfoSize);
2011-03-25 20:47:02 +00:00
if (IpXInterfaceInfo == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto ON_ERROR;
}
2021-12-05 14:54:13 -08:00
//
// 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
);
}
2011-03-25 20:47:02 +00:00
if (EFI_ERROR (Status)) {
ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_GETDATA), gShellNetwork1HiiHandle, Status);
goto ON_ERROR;
}
2021-12-05 14:54:13 -08:00
//
// 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);
2011-03-25 20:47:02 +00:00
if (UnspecifiedSrc) {
if (!NetIp6IsUnspecifiedAddr (Addr) && !NetIp6IsLinkLocalAddr (Addr)) {
2011-03-25 20:47:02 +00:00
//
// Select the interface automatically.
2011-03-25 20:47:02 +00:00
//
CopyMem (&Private->SrcAddress, Addr, sizeof (Private->SrcAddress));
2011-03-25 20:47:02 +00:00
break;
}
} else if (EFI_IP6_EQUAL (&Private->SrcAddress, Addr)) {
2011-03-25 20:47:02 +00:00
//
// 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) {
2018-06-27 21:13:38 +08:00
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;
}
2011-03-25 20:47:02 +00:00
}
FreePool (IpXInterfaceInfo);
IpXInterfaceInfo = NULL;
}
2021-12-05 14:54:13 -08:00
2011-03-25 20:47:02 +00:00
//
// No exact interface address matched.
//
if (HandleIndex == HandleNum) {
2018-06-27 21:13:38 +08:00
ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_CONFIGD_NIC_NF), gShellNetwork1HiiHandle, L"ping");
2011-03-25 20:47:02 +00:00
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;
}
2021-12-05 14:54:13 -08:00
2011-03-25 20:47:02 +00:00
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;
2018-06-27 21:13:38 +08:00
Private->ProtocolPointers.Poll = (PING_IPX_POLL)((EFI_IP4_PROTOCOL *)Private->IpProtocol)->Poll;
2011-03-25 20:47:02 +00:00
}
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.
2011-03-25 20:47:02 +00:00
@param[in] Private The pointer of PING_PRIVATE_DATA.
**/
VOID
Ping6DestroyIp6Instance (
2011-03-25 20:47:02 +00:00
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;
}
2021-12-05 14:54:13 -08:00
2011-03-25 20:47:02 +00:00
//
// 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;
}
2021-12-05 14:54:13 -08:00
2011-03-25 20:47:02 +00:00
//
// 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;
}
2018-06-27 21:13:38 +08:00
2011-03-25 20:47:02 +00:00
//
// 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 {
2018-06-27 21:13:38 +08:00
ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_NETWORK_ERROR), gShellNetwork1HiiHandle, L"ping", Status);
2011-03-25 20:47:02 +00:00
}
goto ON_EXIT;
}
Status = gBS->SetTimer (
Private->Timer,
TimerPeriodic,
ONE_SECOND
);
if (EFI_ERROR (Status)) {
ShellStatus = SHELL_ACCESS_DENIED;
goto ON_EXIT;
}
2021-12-05 14:54:13 -08:00
2011-03-25 20:47:02 +00:00
//
// 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)),
2011-03-25 20:47:02 +00:00
Private->RttSum
);
}
if (Private->RxCount > Private->FailedCount) {
2011-03-25 20:47:02 +00:00
ShellPrintHiiEx (
-1,
-1,
NULL,
STRING_TOKEN (STR_PING_RTT),
gShellNetwork1HiiHandle,
Private->RttMin,
Private->RttMin + Private->TimerPeriod,
2011-03-25 20:47:02 +00:00
Private->RttMax,
Private->RttMax + Private->TimerPeriod,
DivU64x64Remainder (Private->RttSum, (Private->RxCount - Private->FailedCount), NULL),
DivU64x64Remainder (Private->RttSum, (Private->RxCount - Private->FailedCount), NULL) + Private->TimerPeriod
2011-03-25 20:47:02 +00:00
);
}
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);
2011-03-25 20:47:02 +00:00
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);
2011-03-25 20:47:02 +00:00
}
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.
2018-06-27 21:13:38 +08:00
2011-03-25 20:47:02 +00:00
**/
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;
2011-03-25 20:47:02 +00:00
//
2018-06-27 21:13:38 +08:00
// we use IPv6 buffers to hold items...
2011-03-25 20:47:02 +00:00
// 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;
2011-03-25 20:47:02 +00:00
Status = ShellCommandLineParseEx (PingParamList, &ParamPackage, &ProblemParam, TRUE, FALSE);
2011-03-25 20:47:02 +00:00
if (EFI_ERROR (Status)) {
ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellNetwork1HiiHandle, L"ping", ProblemParam);
2011-03-25 20:47:02 +00:00
ShellStatus = SHELL_INVALID_PARAMETER;
goto ON_EXIT;
}
if (ShellCommandLineGetFlag (ParamPackage, L"-_ip6")) {
IpChoice = PING_IP_CHOICE_IP6;
}
//
// Parse the parameter of count number.
2011-03-25 20:47:02 +00:00
//
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)) {
2018-06-27 21:13:38 +08:00
ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellNetwork1HiiHandle, L"ping", ValueStr);
2011-03-25 20:47:02 +00:00
ShellStatus = SHELL_INVALID_PARAMETER;
goto ON_EXIT;
}
} else {
SendNumber = DEFAULT_SEND_COUNT;
}
2021-12-05 14:54:13 -08:00
2011-03-25 20:47:02 +00:00
//
// Parse the parameter of buffer size.
2011-03-25 20:47:02 +00:00
//
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)) {
2018-06-27 21:13:38 +08:00
ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellNetwork1HiiHandle, L"ping", ValueStr);
2011-03-25 20:47:02 +00:00
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.
2011-03-25 20:47:02 +00:00
//
ValueStr = ShellCommandLineGetValue (ParamPackage, L"-s");
if (ValueStr == NULL) {
ValueStr = ShellCommandLineGetValue (ParamPackage, L"-_s");
}
2018-06-27 21:13:38 +08:00
2011-03-25 20:47:02 +00:00
if (ValueStr != NULL) {
mSrcString = ValueStr;
if (IpChoice == PING_IP_CHOICE_IP6) {
Status = NetLibStrToIp6 (ValueStr, &SrcAddress);
} else {
Status = NetLibStrToIp4 (ValueStr, (EFI_IPv4_ADDRESS *)&SrcAddress);
}
2021-12-05 14:54:13 -08:00
2011-03-25 20:47:02 +00:00
if (EFI_ERROR (Status)) {
2018-06-27 21:13:38 +08:00
ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellNetwork1HiiHandle, L"ping", ValueStr);
2011-03-25 20:47:02 +00:00
ShellStatus = SHELL_INVALID_PARAMETER;
goto ON_EXIT;
}
}
2021-12-05 14:54:13 -08:00
2011-03-25 20:47:02 +00:00
//
// Parse the parameter of destination ip address.
2011-03-25 20:47:02 +00:00
//
NonOptionCount = ShellCommandLineGetCount (ParamPackage);
if (NonOptionCount < 2) {
2018-06-27 21:13:38 +08:00
ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_FEW), gShellNetwork1HiiHandle, L"ping");
2011-03-25 20:47:02 +00:00
ShellStatus = SHELL_INVALID_PARAMETER;
goto ON_EXIT;
}
2021-12-05 14:54:13 -08:00
2011-03-25 20:47:02 +00:00
if (NonOptionCount > 2) {
2018-06-27 21:13:38 +08:00
ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_MANY), gShellNetwork1HiiHandle, L"ping");
2011-03-25 20:47:02 +00:00
ShellStatus = SHELL_INVALID_PARAMETER;
goto ON_EXIT;
}
2021-12-05 14:54:13 -08:00
2011-03-25 20:47:02 +00:00
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);
}
2021-12-05 14:54:13 -08:00
2011-03-25 20:47:02 +00:00
if (EFI_ERROR (Status)) {
2018-06-27 21:13:38 +08:00
ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellNetwork1HiiHandle, L"ping", ValueStr);
2011-03-25 20:47:02 +00:00
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;
}