mirror of https://github.com/acidanthera/audk.git
1180 lines
31 KiB
C
1180 lines
31 KiB
C
/** @file
|
|
The implementation for Ping6 application.
|
|
|
|
Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
|
|
|
|
This program and the accompanying materials
|
|
are licensed and made available under the terms and conditions of the BSD License
|
|
which accompanies this distribution. The full text of the license may be found at
|
|
http://opensource.org/licenses/bsd-license.php.
|
|
|
|
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
|
|
|
**/
|
|
|
|
#include <Library/ShellLib.h>
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/BaseLib.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
#include <Library/DebugLib.h>
|
|
#include <Library/UefiBootServicesTableLib.h>
|
|
#include <Library/HiiLib.h>
|
|
#include <Library/NetLib.h>
|
|
|
|
#include <Protocol/Cpu.h>
|
|
#include <Protocol/ServiceBinding.h>
|
|
#include <Protocol/Ip6.h>
|
|
#include <Protocol/Ip6Config.h>
|
|
|
|
#include "Ping6.h"
|
|
|
|
SHELL_PARAM_ITEM Ping6ParamList[] = {
|
|
{
|
|
L"-l",
|
|
TypeValue
|
|
},
|
|
{
|
|
L"-n",
|
|
TypeValue
|
|
},
|
|
{
|
|
L"-s",
|
|
TypeValue
|
|
},
|
|
{
|
|
L"-?",
|
|
TypeFlag
|
|
},
|
|
{
|
|
NULL,
|
|
TypeMax
|
|
},
|
|
};
|
|
|
|
//
|
|
// Global Variables in Ping6 application.
|
|
//
|
|
EFI_HII_HANDLE mHiiHandle;
|
|
CONST CHAR16 *mIp6DstString;
|
|
CONST CHAR16 *mIp6SrcString;
|
|
EFI_GUID mEfiPing6Guid = EFI_PING6_GUID;
|
|
UINT64 mFrequency = 0;
|
|
/**
|
|
Get and caculate the frequency in tick/ms.
|
|
The result is saved in the globle variable mFrequency
|
|
|
|
@retval EFI_SUCCESS Caculated the frequency successfully.
|
|
@retval Others Failed to caculate the frequency.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
Ping6GetFrequency (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_CPU_ARCH_PROTOCOL *Cpu;
|
|
UINT64 CurrentTick;
|
|
UINT64 TimerPeriod;
|
|
|
|
Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **) &Cpu);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = Cpu->GetTimerValue (Cpu, 0, &CurrentTick, &TimerPeriod);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
//
|
|
// For NT32 Simulator only. 358049 is a similar value to keep timer granularity.
|
|
// Set the timer period by ourselves.
|
|
//
|
|
TimerPeriod = (UINT64) NTTIMERPERIOD;
|
|
}
|
|
//
|
|
// The timer period is in femtosecond (1 femtosecond is 1e-15 second).
|
|
// So 1e+12 is divided by timer period to produce the freq in tick/ms.
|
|
//
|
|
mFrequency = DivU64x64Remainder (1000000000000ULL, TimerPeriod, NULL);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Get and caculate the duration in ms.
|
|
|
|
@param[in] Begin The start point of time.
|
|
@param[in] End The end point of time.
|
|
|
|
@return The duration in ms.
|
|
|
|
**/
|
|
UINT64
|
|
Ping6CalculateTick (
|
|
IN UINT64 Begin,
|
|
IN UINT64 End
|
|
)
|
|
{
|
|
ASSERT (End > Begin);
|
|
return DivU64x64Remainder (End - Begin, mFrequency, NULL);
|
|
}
|
|
|
|
/**
|
|
Destroy IPING6_ICMP6_TX_INFO, and recollect the memory.
|
|
|
|
@param[in] TxInfo The pointer to PING6_ICMP6_TX_INFO.
|
|
|
|
**/
|
|
VOID
|
|
Ping6DestroyTxInfo (
|
|
IN PING6_ICMP6_TX_INFO *TxInfo
|
|
)
|
|
{
|
|
EFI_IP6_TRANSMIT_DATA *TxData;
|
|
EFI_IP6_FRAGMENT_DATA *FragData;
|
|
UINTN Index;
|
|
|
|
ASSERT (TxInfo != NULL);
|
|
|
|
if (TxInfo->Token != NULL) {
|
|
|
|
if (TxInfo->Token->Event != NULL) {
|
|
gBS->CloseEvent (TxInfo->Token->Event);
|
|
}
|
|
|
|
TxData = TxInfo->Token->Packet.TxData;
|
|
if (TxData != NULL) {
|
|
|
|
if (TxData->OverrideData != NULL) {
|
|
FreePool (TxData->OverrideData);
|
|
}
|
|
|
|
if (TxData->ExtHdrs != NULL) {
|
|
FreePool (TxData->ExtHdrs);
|
|
}
|
|
|
|
for (Index = 0; Index < TxData->FragmentCount; Index++) {
|
|
FragData = TxData->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 PING6_PRIVATE_DATA.
|
|
@param[in] Packet The pointer to ICMP6_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 PING6_PRIVATE_DATA *Private,
|
|
IN ICMP6_ECHO_REQUEST_REPLY *Packet
|
|
)
|
|
{
|
|
PING6_ICMP6_TX_INFO *TxInfo;
|
|
LIST_ENTRY *Entry;
|
|
LIST_ENTRY *NextEntry;
|
|
|
|
NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) {
|
|
TxInfo = BASE_CR (Entry, PING6_ICMP6_TX_INFO, Link);
|
|
|
|
if ((TxInfo->SequenceNum == Packet->SequenceNum) && (TxInfo->TimeStamp == Packet->TimeStamp)) {
|
|
Private->RxCount++;
|
|
RemoveEntryList (&TxInfo->Link);
|
|
Ping6DestroyTxInfo (TxInfo);
|
|
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;
|
|
PING6_PRIVATE_DATA *Private;
|
|
EFI_IP6_COMPLETION_TOKEN *RxToken;
|
|
EFI_IP6_RECEIVE_DATA *RxData;
|
|
ICMP6_ECHO_REQUEST_REPLY *Reply;
|
|
UINT32 PayLoad;
|
|
UINT64 Rtt;
|
|
CHAR8 Near;
|
|
|
|
Private = (PING6_PRIVATE_DATA *) Context;
|
|
|
|
if (Private->Status == EFI_ABORTED) {
|
|
return;
|
|
}
|
|
|
|
RxToken = &Private->RxToken;
|
|
RxData = RxToken->Packet.RxData;
|
|
Reply = RxData->FragmentTable[0].FragmentBuffer;
|
|
PayLoad = RxData->DataLength;
|
|
|
|
if (RxData->Header->NextHeader != IP6_ICMP) {
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
if (!IP6_IS_MULTICAST (&Private->DstAddress) &&
|
|
!EFI_IP6_EQUAL (&RxData->Header->SourceAddress, &Private->DstAddress)) {
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
if ((Reply->Type != ICMP_V6_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 = Ping6CalculateTick (Reply->TimeStamp, ReadTime ());
|
|
if (Rtt != 0) {
|
|
Near = (CHAR8) '=';
|
|
} else {
|
|
Near = (CHAR8) '<';
|
|
}
|
|
|
|
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_PING6_REPLY_INFO),
|
|
mHiiHandle,
|
|
PayLoad,
|
|
mIp6DstString,
|
|
Reply->SequenceNum,
|
|
RxData->Header->HopLimit,
|
|
Near,
|
|
Rtt
|
|
);
|
|
|
|
ON_EXIT:
|
|
|
|
if (Private->RxCount < Private->SendNum) {
|
|
//
|
|
// Continue to receive icmp6 echo reply packets.
|
|
//
|
|
RxToken->Status = EFI_ABORTED;
|
|
|
|
Status = Private->Ip6->Receive (Private->Ip6, RxToken);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
Private->Status = EFI_ABORTED;
|
|
}
|
|
} else {
|
|
//
|
|
// All reply have already been received from the dest host.
|
|
//
|
|
Private->Status = EFI_SUCCESS;
|
|
}
|
|
//
|
|
// Singal to recycle the each rxdata here, not at the end of process.
|
|
//
|
|
gBS->SignalEvent (RxData->RecycleSignal);
|
|
}
|
|
|
|
/**
|
|
Initial EFI_IP6_COMPLETION_TOKEN.
|
|
|
|
@param[in] Private The pointer of PING6_PRIVATE_DATA.
|
|
@param[in] TimeStamp The TimeStamp of request.
|
|
@param[in] SequenceNum The SequenceNum of request.
|
|
|
|
@return The pointer of EFI_IP6_COMPLETION_TOKEN.
|
|
|
|
**/
|
|
EFI_IP6_COMPLETION_TOKEN *
|
|
Ping6GenerateToken (
|
|
IN PING6_PRIVATE_DATA *Private,
|
|
IN UINT64 TimeStamp,
|
|
IN UINT16 SequenceNum
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_IP6_COMPLETION_TOKEN *Token;
|
|
EFI_IP6_TRANSMIT_DATA *TxData;
|
|
ICMP6_ECHO_REQUEST_REPLY *Request;
|
|
|
|
Request = AllocateZeroPool (Private->BufferSize);
|
|
|
|
if (Request == NULL) {
|
|
return NULL;
|
|
}
|
|
//
|
|
// Assembly icmp6 echo request packet.
|
|
//
|
|
Request->Type = ICMP_V6_ECHO_REQUEST;
|
|
Request->Code = 0;
|
|
Request->SequenceNum = SequenceNum;
|
|
Request->TimeStamp = TimeStamp;
|
|
Request->Identifier = 0;
|
|
//
|
|
// Leave check sum to ip6 layer, since it has no idea of source address
|
|
// selection.
|
|
//
|
|
Request->Checksum = 0;
|
|
|
|
TxData = AllocateZeroPool (sizeof (EFI_IP6_TRANSMIT_DATA));
|
|
|
|
if (TxData == NULL) {
|
|
FreePool (Request);
|
|
return NULL;
|
|
}
|
|
//
|
|
// Assembly ipv6 token for transmit.
|
|
//
|
|
TxData->OverrideData = 0;
|
|
TxData->ExtHdrsLength = 0;
|
|
TxData->ExtHdrs = NULL;
|
|
TxData->DataLength = Private->BufferSize;
|
|
TxData->FragmentCount = 1;
|
|
TxData->FragmentTable[0].FragmentBuffer = (VOID *) Request;
|
|
TxData->FragmentTable[0].FragmentLength = Private->BufferSize;
|
|
|
|
Token = AllocateZeroPool (sizeof (EFI_IP6_COMPLETION_TOKEN));
|
|
|
|
if (Token == NULL) {
|
|
FreePool (Request);
|
|
FreePool (TxData);
|
|
return NULL;
|
|
}
|
|
|
|
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 EFI_IP6_COMPLETION_TOKEN.
|
|
|
|
@param[in] Private The pointer of PING6_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
|
|
Ping6SendEchoRequest (
|
|
IN PING6_PRIVATE_DATA *Private
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
PING6_ICMP6_TX_INFO *TxInfo;
|
|
|
|
TxInfo = AllocateZeroPool (sizeof (PING6_ICMP6_TX_INFO));
|
|
|
|
if (TxInfo == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
TxInfo->TimeStamp = ReadTime ();
|
|
TxInfo->SequenceNum = (UINT16) (Private->TxCount + 1);
|
|
|
|
TxInfo->Token = Ping6GenerateToken (
|
|
Private,
|
|
TxInfo->TimeStamp,
|
|
TxInfo->SequenceNum
|
|
);
|
|
|
|
if (TxInfo->Token == NULL) {
|
|
Ping6DestroyTxInfo (TxInfo);
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
Status = Private->Ip6->Transmit (Private->Ip6, TxInfo->Token);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
Ping6DestroyTxInfo (TxInfo);
|
|
return Status;
|
|
}
|
|
|
|
InsertTailList (&Private->TxList, &TxInfo->Link);
|
|
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 PING6_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 PING6_PRIVATE_DATA *Private
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
ZeroMem (&Private->RxToken, sizeof (EFI_IP6_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;
|
|
|
|
return Private->Ip6->Receive (Private->Ip6, &Private->RxToken);
|
|
}
|
|
|
|
/**
|
|
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;
|
|
PING6_PRIVATE_DATA *Private;
|
|
PING6_ICMP6_TX_INFO *TxInfo;
|
|
LIST_ENTRY *Entry;
|
|
LIST_ENTRY *NextEntry;
|
|
UINT64 Time;
|
|
|
|
Private = (PING6_PRIVATE_DATA *) Context;
|
|
|
|
//
|
|
// Retransmit icmp6 echo request packets per second in sendnumber times.
|
|
//
|
|
if (Private->TxCount < Private->SendNum) {
|
|
|
|
Status = Ping6SendEchoRequest (Private);
|
|
if (Private->TxCount != 0){
|
|
if (EFI_ERROR (Status)) {
|
|
ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_SEND_REQUEST), mHiiHandle, 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, PING6_ICMP6_TX_INFO, Link);
|
|
Time = Ping6CalculateTick (TxInfo->TimeStamp, ReadTime ());
|
|
|
|
//
|
|
// Remove the timeout echo request from txlist.
|
|
//
|
|
if (Time > PING6_DEFAULT_TIMEOUT) {
|
|
|
|
if (EFI_ERROR (TxInfo->Token->Status)) {
|
|
Private->Ip6->Cancel (Private->Ip6, TxInfo->Token);
|
|
}
|
|
//
|
|
// Remove the timeout icmp6 echo request from list.
|
|
//
|
|
ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_TIMEOUT), mHiiHandle, TxInfo->SequenceNum);
|
|
|
|
RemoveEntryList (&TxInfo->Link);
|
|
Ping6DestroyTxInfo (TxInfo);
|
|
|
|
if (IsListEmpty (&Private->TxList) && (Private->TxCount == Private->SendNum)) {
|
|
//
|
|
// All the left icmp6 echo request in the list timeout.
|
|
//
|
|
Private->Status = EFI_TIMEOUT;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Create a valid IP6 instance.
|
|
|
|
@param[in] Private The pointer of PING6_PRIVATE_DATA.
|
|
|
|
@retval EFI_SUCCESS Create a valid IP6 instance successfully.
|
|
@retval EFI_ABORTED Locate handle with ip6 service binding protocol unsuccessfully.
|
|
@retval EFI_INVALID_PARAMETER The source address is unspecified when the destination address is a link -ocal 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
|
|
Ping6CreateIp6Instance (
|
|
IN PING6_PRIVATE_DATA *Private
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN HandleIndex;
|
|
UINTN HandleNum;
|
|
EFI_HANDLE *HandleBuffer;
|
|
EFI_SERVICE_BINDING_PROTOCOL *Ip6Sb;
|
|
EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg;
|
|
EFI_IP6_CONFIG_DATA Ip6Config;
|
|
EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo;
|
|
UINTN IfInfoSize;
|
|
EFI_IPv6_ADDRESS *Addr;
|
|
UINTN AddrIndex;
|
|
|
|
HandleBuffer = NULL;
|
|
Ip6Sb = NULL;
|
|
IfInfo = NULL;
|
|
IfInfoSize = 0;
|
|
|
|
//
|
|
// Locate all the handles with ip6 service binding protocol.
|
|
//
|
|
Status = gBS->LocateHandleBuffer (
|
|
ByProtocol,
|
|
&gEfiIp6ServiceBindingProtocolGuid,
|
|
NULL,
|
|
&HandleNum,
|
|
&HandleBuffer
|
|
);
|
|
if (EFI_ERROR (Status) || (HandleNum == 0)) {
|
|
return EFI_ABORTED;
|
|
}
|
|
//
|
|
// Source address is required when pinging a link-local address on multi-
|
|
// interfaces host.
|
|
//
|
|
if (NetIp6IsLinkLocalAddr (&Private->DstAddress) &&
|
|
NetIp6IsUnspecifiedAddr (&Private->SrcAddress) &&
|
|
(HandleNum > 1)) {
|
|
ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_SOURCE), mHiiHandle);
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto ON_ERROR;
|
|
}
|
|
//
|
|
// For each ip6 protocol, check interface addresses list.
|
|
//
|
|
for (HandleIndex = 0; HandleIndex < HandleNum; HandleIndex++) {
|
|
|
|
Ip6Sb = NULL;
|
|
IfInfo = NULL;
|
|
IfInfoSize = 0;
|
|
|
|
Status = gBS->HandleProtocol (
|
|
HandleBuffer[HandleIndex],
|
|
&gEfiIp6ServiceBindingProtocolGuid,
|
|
(VOID **) &Ip6Sb
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto ON_ERROR;
|
|
}
|
|
|
|
if (NetIp6IsUnspecifiedAddr (&Private->SrcAddress)) {
|
|
//
|
|
// No need to match interface address.
|
|
//
|
|
break;
|
|
} else {
|
|
//
|
|
// Ip6config protocol and ip6 service binding protocol are installed
|
|
// on the same handle.
|
|
//
|
|
Status = gBS->HandleProtocol (
|
|
HandleBuffer[HandleIndex],
|
|
&gEfiIp6ConfigProtocolGuid,
|
|
(VOID **) &Ip6Cfg
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
goto ON_ERROR;
|
|
}
|
|
//
|
|
// Get the interface information size.
|
|
//
|
|
Status = Ip6Cfg->GetData (
|
|
Ip6Cfg,
|
|
Ip6ConfigDataTypeInterfaceInfo,
|
|
&IfInfoSize,
|
|
NULL
|
|
);
|
|
|
|
if (Status != EFI_BUFFER_TOO_SMALL) {
|
|
ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6CFG_GETDATA), mHiiHandle, Status);
|
|
goto ON_ERROR;
|
|
}
|
|
|
|
IfInfo = AllocateZeroPool (IfInfoSize);
|
|
|
|
if (IfInfo == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto ON_ERROR;
|
|
}
|
|
//
|
|
// Get the interface info.
|
|
//
|
|
Status = Ip6Cfg->GetData (
|
|
Ip6Cfg,
|
|
Ip6ConfigDataTypeInterfaceInfo,
|
|
&IfInfoSize,
|
|
IfInfo
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6CFG_GETDATA), mHiiHandle, Status);
|
|
goto ON_ERROR;
|
|
}
|
|
//
|
|
// Check whether the source address is one of the interface addresses.
|
|
//
|
|
for (AddrIndex = 0; AddrIndex < IfInfo->AddressInfoCount; AddrIndex++) {
|
|
|
|
Addr = &(IfInfo->AddressInfo[AddrIndex].Address);
|
|
if (EFI_IP6_EQUAL (&Private->SrcAddress, Addr)) {
|
|
//
|
|
// Match a certain interface address.
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (AddrIndex < IfInfo->AddressInfoCount) {
|
|
//
|
|
// Found a nic handle with right interface address.
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
|
|
FreePool (IfInfo);
|
|
IfInfo = NULL;
|
|
}
|
|
//
|
|
// No exact interface address matched.
|
|
//
|
|
|
|
if (HandleIndex == HandleNum) {
|
|
ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_SOURCE_NOT_FOUND), mHiiHandle, mIp6SrcString);
|
|
Status = EFI_NOT_FOUND;
|
|
goto ON_ERROR;
|
|
}
|
|
|
|
Private->NicHandle = HandleBuffer[HandleIndex];
|
|
|
|
ASSERT (Ip6Sb != NULL);
|
|
Status = Ip6Sb->CreateChild (Ip6Sb, &Private->Ip6ChildHandle);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
goto ON_ERROR;
|
|
}
|
|
|
|
Status = gBS->OpenProtocol (
|
|
Private->Ip6ChildHandle,
|
|
&gEfiIp6ProtocolGuid,
|
|
(VOID **) &Private->Ip6,
|
|
Private->ImageHandle,
|
|
Private->Ip6ChildHandle,
|
|
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 = Private->Ip6->Configure (Private->Ip6, &Ip6Config);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6_CONFIG), mHiiHandle, Status);
|
|
goto ON_ERROR;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
ON_ERROR:
|
|
if (HandleBuffer != NULL) {
|
|
FreePool (HandleBuffer);
|
|
}
|
|
|
|
if (IfInfo != NULL) {
|
|
FreePool (IfInfo);
|
|
}
|
|
|
|
if ((Ip6Sb != NULL) && (Private->Ip6ChildHandle != NULL)) {
|
|
Ip6Sb->DestroyChild (Ip6Sb, Private->Ip6ChildHandle);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Destory the IP6 instance.
|
|
|
|
@param[in] Private The pointer of PING6_PRIVATE_DATA.
|
|
|
|
**/
|
|
VOID
|
|
Ping6DestoryIp6Instance (
|
|
IN PING6_PRIVATE_DATA *Private
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_SERVICE_BINDING_PROTOCOL *Ip6Sb;
|
|
|
|
gBS->CloseProtocol (
|
|
Private->Ip6ChildHandle,
|
|
&gEfiIp6ProtocolGuid,
|
|
Private->ImageHandle,
|
|
Private->Ip6ChildHandle
|
|
);
|
|
|
|
Status = gBS->HandleProtocol (
|
|
Private->NicHandle,
|
|
&gEfiIp6ServiceBindingProtocolGuid,
|
|
(VOID **) &Ip6Sb
|
|
);
|
|
|
|
if (!EFI_ERROR(Status)) {
|
|
Ip6Sb->DestroyChild (Ip6Sb, Private->Ip6ChildHandle);
|
|
}
|
|
}
|
|
|
|
/**
|
|
The Ping6 Process.
|
|
|
|
@param[in] ImageHandle The firmware allocated handle for the UEFI image.
|
|
@param[in] SendNumber The send request count.
|
|
@param[in] BufferSize The send buffer size.
|
|
@param[in] SrcAddress The source IPv6 address.
|
|
@param[in] DstAddress The destination IPv6 address.
|
|
|
|
@retval EFI_SUCCESS The ping6 processed successfullly.
|
|
@retval others The ping6 processed unsuccessfully.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
Ping6 (
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN UINT32 SendNumber,
|
|
IN UINT32 BufferSize,
|
|
IN EFI_IPv6_ADDRESS *SrcAddress,
|
|
IN EFI_IPv6_ADDRESS *DstAddress
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_INPUT_KEY Key;
|
|
PING6_PRIVATE_DATA *Private;
|
|
PING6_ICMP6_TX_INFO *TxInfo;
|
|
LIST_ENTRY *Entry;
|
|
LIST_ENTRY *NextEntry;
|
|
|
|
Private = AllocateZeroPool (sizeof (PING6_PRIVATE_DATA));
|
|
|
|
ASSERT (Private != NULL);
|
|
|
|
Private->ImageHandle = ImageHandle;
|
|
Private->SendNum = SendNumber;
|
|
Private->BufferSize = BufferSize;
|
|
Private->RttMin = ~((UINT64 )(0x0));
|
|
Private->Status = EFI_NOT_READY;
|
|
|
|
InitializeListHead (&Private->TxList);
|
|
|
|
IP6_COPY_ADDRESS (&Private->SrcAddress, SrcAddress);
|
|
IP6_COPY_ADDRESS (&Private->DstAddress, DstAddress);
|
|
|
|
//
|
|
// Open and configure a ip6 instance for ping6.
|
|
//
|
|
Status = Ping6CreateIp6Instance (Private);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
goto ON_EXIT;
|
|
}
|
|
//
|
|
// Print the command line itself.
|
|
//
|
|
ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_START), mHiiHandle, mIp6DstString, Private->BufferSize);
|
|
//
|
|
// Create a ipv6 token to receive the first icmp6 echo reply packet.
|
|
//
|
|
Status = Ping6ReceiveEchoReply (Private);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
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)) {
|
|
goto ON_EXIT;
|
|
}
|
|
//
|
|
// Create a ipv6 token to send the first icmp6 echo request packet.
|
|
//
|
|
Status = Ping6SendEchoRequest (Private);
|
|
//
|
|
// EFI_NOT_READY for IPsec is enable and IKE is not established.
|
|
//
|
|
if (EFI_ERROR (Status) && (Status != EFI_NOT_READY)) {
|
|
if(Status == EFI_NOT_FOUND) {
|
|
ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_NOSOURCE_INDOMAIN), mHiiHandle, mIp6DstString);
|
|
}
|
|
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
Status = gBS->SetTimer (
|
|
Private->Timer,
|
|
TimerPeriodic,
|
|
PING6_ONE_SECOND
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
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) {
|
|
Private->Ip6->Poll (Private->Ip6);
|
|
|
|
//
|
|
// Terminate the ping6 process by 'esc' or 'ctl-c'.
|
|
//
|
|
Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
|
|
|
|
if (!EFI_ERROR(Status)) {
|
|
if ((Key.UnicodeChar == 0x1b) || (Key.UnicodeChar == 0x03) ||
|
|
((Key.UnicodeChar == 0) && (Key.ScanCode == SCAN_ESC))) {
|
|
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_PING6_STAT),
|
|
mHiiHandle,
|
|
Private->TxCount,
|
|
Private->RxCount,
|
|
(100 * (Private->TxCount - Private->RxCount)) / Private->TxCount,
|
|
Private->RttSum
|
|
);
|
|
}
|
|
|
|
if (Private->RxCount != 0) {
|
|
ShellPrintHiiEx (
|
|
-1,
|
|
-1,
|
|
NULL,
|
|
STRING_TOKEN (STR_PING6_RTT),
|
|
mHiiHandle,
|
|
Private->RttMin,
|
|
Private->RttMax,
|
|
DivU64x64Remainder (Private->RttSum, Private->RxCount, NULL)
|
|
);
|
|
}
|
|
|
|
ON_EXIT:
|
|
|
|
if (Private != NULL) {
|
|
Private->Status = EFI_ABORTED;
|
|
|
|
NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) {
|
|
TxInfo = BASE_CR (Entry, PING6_ICMP6_TX_INFO, Link);
|
|
|
|
Status = Private->Ip6->Cancel (Private->Ip6, TxInfo->Token);
|
|
|
|
RemoveEntryList (&TxInfo->Link);
|
|
Ping6DestroyTxInfo (TxInfo);
|
|
}
|
|
|
|
if (Private->Timer != NULL) {
|
|
gBS->CloseEvent (Private->Timer);
|
|
}
|
|
|
|
if (Private->Ip6 != NULL) {
|
|
Status = Private->Ip6->Cancel (Private->Ip6, &Private->RxToken);
|
|
}
|
|
|
|
if (Private->RxToken.Event != NULL) {
|
|
gBS->CloseEvent (Private->RxToken.Event);
|
|
}
|
|
|
|
if (Private->Ip6ChildHandle != NULL) {
|
|
Ping6DestoryIp6Instance (Private);
|
|
}
|
|
|
|
FreePool (Private);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
This is the declaration of an EFI image entry point. This entry point is
|
|
the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers, including
|
|
both device drivers and bus drivers.
|
|
|
|
The entry point for the Ping6 application that parses the command line input and calls the Ping6 process.
|
|
|
|
@param[in] ImageHandle The firmware allocated handle for the UEFI image.
|
|
@param[in] SystemTable A pointer to the EFI System Table.
|
|
|
|
@retval EFI_SUCCESS The operation completed successfully.
|
|
@retval EFI_INVALID_PARAMETETR Input parameters combination is invalid.
|
|
@retval Others Some errors occur.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
InitializePing6 (
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_IPv6_ADDRESS DstAddress;
|
|
EFI_IPv6_ADDRESS SrcAddress;
|
|
UINT64 BufferSize;
|
|
UINTN SendNumber;
|
|
LIST_ENTRY *ParamPackage;
|
|
CONST CHAR16 *ValueStr;
|
|
CONST CHAR16 *ValueStrPtr;
|
|
UINTN NonOptionCount;
|
|
|
|
//
|
|
// Register our string package with HII and return the handle to it.
|
|
//
|
|
mHiiHandle = HiiAddPackages (&gEfiCallerIdGuid, ImageHandle, Ping6Strings, NULL);
|
|
ASSERT (mHiiHandle != NULL);
|
|
|
|
Status = ShellCommandLineParseEx (Ping6ParamList, &ParamPackage, NULL, TRUE, FALSE);
|
|
if (EFI_ERROR(Status)) {
|
|
ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_INPUT), mHiiHandle);
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
if (ShellCommandLineGetFlag (ParamPackage, L"-?")) {
|
|
ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_HELP), mHiiHandle);
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
SendNumber = 10;
|
|
BufferSize = 16;
|
|
|
|
//
|
|
// Parse the paramter of count number.
|
|
//
|
|
ValueStr = ShellCommandLineGetValue (ParamPackage, L"-n");
|
|
ValueStrPtr = ValueStr;
|
|
if (ValueStr != NULL) {
|
|
SendNumber = ShellStrToUintn (ValueStrPtr);
|
|
|
|
//
|
|
// ShellStrToUintn will return 0 when input is 0 or an invalid input string.
|
|
//
|
|
if ((SendNumber == 0) || (SendNumber > PING6_MAX_SEND_NUMBER)) {
|
|
ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_SEND_NUMBER), mHiiHandle, ValueStr);
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto ON_EXIT;
|
|
}
|
|
}
|
|
//
|
|
// Parse the paramter of buffer size.
|
|
//
|
|
ValueStr = ShellCommandLineGetValue (ParamPackage, L"-l");
|
|
ValueStrPtr = ValueStr;
|
|
if (ValueStr != NULL) {
|
|
BufferSize = ShellStrToUintn (ValueStrPtr);
|
|
|
|
//
|
|
// ShellStrToUintn will return 0 when input is 0 or an invalid input string.
|
|
//
|
|
if ((BufferSize < 16) || (BufferSize > PING6_MAX_BUFFER_SIZE)) {
|
|
ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_BUFFER_SIZE), mHiiHandle, ValueStr);
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto ON_EXIT;
|
|
}
|
|
}
|
|
|
|
ZeroMem (&SrcAddress, sizeof (EFI_IPv6_ADDRESS));
|
|
ZeroMem (&DstAddress, sizeof (EFI_IPv6_ADDRESS));
|
|
|
|
//
|
|
// Parse the paramter of source ip address.
|
|
//
|
|
ValueStr = ShellCommandLineGetValue (ParamPackage, L"-s");
|
|
ValueStrPtr = ValueStr;
|
|
if (ValueStr != NULL) {
|
|
mIp6SrcString = ValueStr;
|
|
Status = NetLibStrToIp6 (ValueStrPtr, &SrcAddress);
|
|
if (EFI_ERROR (Status)) {
|
|
ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_IP), mHiiHandle, ValueStr);
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto ON_EXIT;
|
|
}
|
|
}
|
|
//
|
|
// Parse the paramter of destination ip address.
|
|
//
|
|
NonOptionCount = ShellCommandLineGetCount(ParamPackage);
|
|
ValueStr = ShellCommandLineGetRawValue (ParamPackage, (UINT32)(NonOptionCount-1));
|
|
if (NonOptionCount != 2) {
|
|
ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_INPUT), mHiiHandle);
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto ON_EXIT;
|
|
}
|
|
ValueStrPtr = ValueStr;
|
|
if (ValueStr != NULL) {
|
|
mIp6DstString = ValueStr;
|
|
Status = NetLibStrToIp6 (ValueStrPtr, &DstAddress);
|
|
if (EFI_ERROR (Status)) {
|
|
ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_IP), mHiiHandle, ValueStr);
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto ON_EXIT;
|
|
}
|
|
}
|
|
//
|
|
// Get frequency to calculate the time from ticks.
|
|
//
|
|
Status = Ping6GetFrequency ();
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
goto ON_EXIT;
|
|
}
|
|
//
|
|
// Enter into ping6 process.
|
|
//
|
|
Status = Ping6 (
|
|
ImageHandle,
|
|
(UINT32)SendNumber,
|
|
(UINT32)BufferSize,
|
|
&SrcAddress,
|
|
&DstAddress
|
|
);
|
|
|
|
ON_EXIT:
|
|
ShellCommandLineFreeVarList (ParamPackage);
|
|
HiiRemovePackages (mHiiHandle);
|
|
return Status;
|
|
}
|