begining->beginning dirve->drive duplicat->duplicate fuly->fully Funciton->Function Functino->Function optioanl->optional poitners->pointers rountine->routine sucessful->successful sucessfully->successfully Signed-off-by: Page Chen <paiching_chen@apple.com> Cc: Zhichao Gao <zhichao.gao@intel.com> Cc: Andrew Fish <afish@apple.com> Message-Id: <829d2bed2a848229d719d7ae7b64ef1a47782720.1699557986.git.paiching_chen@apple.com> Reviewed-by: Zhichao Gao <zhichao.gao@intel.com>
		
			
				
	
	
		
			1732 lines
		
	
	
		
			50 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1732 lines
		
	
	
		
			50 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/** @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;
 | 
						|
  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;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  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 routine.
 | 
						|
 | 
						|
  @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;
 | 
						|
}
 |