Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Fu Siyuan <siyuan.fu@intel.com>
Reviewed-by: Wu Jiaxin <jiaxin.wu@intel.com>
Reviewed-by: Ye Ting <ting.ye@intel.com>
(cherry picked from commit 5d8aa7eb6f)
		
	
		
			
				
	
	
		
			1711 lines
		
	
	
		
			50 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1711 lines
		
	
	
		
			50 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
|   The implementation for Ping shell command.
 | |
| 
 | |
|   (C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR>
 | |
|   Copyright (c) 2009 - 2017, Intel Corporation. All rights reserved.<BR>
 | |
|   (C) Copyright 2016 Hewlett Packard Enterprise Development LP<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 "UefiShellNetwork1CommandsLib.h"
 | |
| 
 | |
| #define PING_IP4_COPY_ADDRESS(Dest, Src) (CopyMem ((Dest), (Src), sizeof (EFI_IPv4_ADDRESS)))
 | |
| 
 | |
| UINT64          mCurrentTick = 0;
 | |
| 
 | |
| //
 | |
| // Function templates to match the IPv4 and IPv6 commands that we use.
 | |
| //
 | |
| typedef 
 | |
| EFI_STATUS
 | |
| (EFIAPI *PING_IPX_POLL)(
 | |
|   IN VOID          *This
 | |
|   );  
 | |
| 
 | |
| typedef 
 | |
| EFI_STATUS
 | |
| (EFIAPI *PING_IPX_TRANSMIT)(
 | |
|   IN VOID          *This,
 | |
|   IN VOID          *Token
 | |
|   );
 | |
| 
 | |
| typedef 
 | |
| EFI_STATUS
 | |
| (EFIAPI *PING_IPX_RECEIVE)(
 | |
|   IN VOID          *This,
 | |
|   IN VOID          *Token
 | |
|   ); 
 | |
| 
 | |
| typedef
 | |
| EFI_STATUS
 | |
| (EFIAPI *PING_IPX_CANCEL)(
 | |
|   IN VOID          *This,
 | |
|   IN VOID          *Token OPTIONAL
 | |
|   );
 | |
| 
 | |
| ///
 | |
| /// A set of pointers to either IPv6 or IPv4 functions.  
 | |
| /// Unknown which one to the ping command.
 | |
| ///
 | |
| typedef struct {
 | |
|   PING_IPX_TRANSMIT             Transmit;
 | |
|   PING_IPX_RECEIVE              Receive;
 | |
|   PING_IPX_CANCEL               Cancel;
 | |
|   PING_IPX_POLL                 Poll;
 | |
| }PING_IPX_PROTOCOL;
 | |
| 
 | |
| 
 | |
| typedef union {
 | |
|   VOID                  *RxData;
 | |
|   VOID                  *TxData;
 | |
| } PING_PACKET;
 | |
| 
 | |
| //
 | |
| // PING_IPX_COMPLETION_TOKEN
 | |
| // structures are used for both transmit and receive operations. 
 | |
| // This version is IP-unaware.
 | |
| //
 | |
| typedef struct {
 | |
|   EFI_EVENT               Event;
 | |
|   EFI_STATUS              Status;
 | |
|   PING_PACKET             Packet;
 | |
| } PING_IPX_COMPLETION_TOKEN;
 | |
| 
 | |
| #pragma pack(1)
 | |
| typedef struct _ICMPX_ECHO_REQUEST_REPLY {
 | |
|   UINT8                       Type;
 | |
|   UINT8                       Code;
 | |
|   UINT16                      Checksum;
 | |
|   UINT16                      Identifier;
 | |
|   UINT16                      SequenceNum;
 | |
|   UINT32                      TimeStamp;
 | |
|   UINT8                       Data[1];
 | |
| } ICMPX_ECHO_REQUEST_REPLY;
 | |
| #pragma pack()
 | |
| 
 | |
| typedef struct _PING_ICMP_TX_INFO {
 | |
|   LIST_ENTRY                Link;
 | |
|   UINT16                    SequenceNum;
 | |
|   UINT32                    TimeStamp;
 | |
|   PING_IPX_COMPLETION_TOKEN *Token;
 | |
| } PING_ICMPX_TX_INFO;
 | |
| 
 | |
| #define DEFAULT_TIMEOUT       5000
 | |
| #define MAX_SEND_NUMBER       10000
 | |
| #define MAX_BUFFER_SIZE       32768
 | |
| #define DEFAULT_TIMER_PERIOD  358049
 | |
| #define ONE_SECOND            10000000
 | |
| #define PING_IP_CHOICE_IP4    1
 | |
| #define PING_IP_CHOICE_IP6    2
 | |
| #define DEFAULT_SEND_COUNT    10
 | |
| #define DEFAULT_BUFFER_SIZE   16
 | |
| #define ICMP_V4_ECHO_REQUEST  0x8
 | |
| #define ICMP_V4_ECHO_REPLY    0x0
 | |
| #define STALL_1_MILLI_SECOND  1000
 | |
| 
 | |
| #define PING_PRIVATE_DATA_SIGNATURE  SIGNATURE_32 ('P', 'i', 'n', 'g')
 | |
| typedef struct _PING_PRIVATE_DATA {
 | |
|   UINT32                      Signature;
 | |
|   EFI_HANDLE                  NicHandle;
 | |
|   EFI_HANDLE                  IpChildHandle;
 | |
|   EFI_EVENT                   Timer;
 | |
| 
 | |
|   UINT32                      TimerPeriod;
 | |
|   UINT32                      RttTimerTick;   
 | |
|   EFI_EVENT                   RttTimer;
 | |
| 
 | |
|   EFI_STATUS                  Status;
 | |
|   LIST_ENTRY                  TxList;
 | |
|   UINT16                      RxCount;
 | |
|   UINT16                      TxCount;
 | |
|   UINT64                      RttSum;
 | |
|   UINT64                      RttMin;
 | |
|   UINT64                      RttMax;
 | |
|   UINT32                      SequenceNum;
 | |
| 
 | |
|   UINT32                      SendNum;
 | |
|   UINT32                      BufferSize;
 | |
|   UINT32                      IpChoice;
 | |
| 
 | |
|   PING_IPX_PROTOCOL           ProtocolPointers;
 | |
|   VOID                        *IpProtocol;
 | |
|   UINT8                       SrcAddress[MAX(sizeof(EFI_IPv6_ADDRESS)        , sizeof(EFI_IPv4_ADDRESS)          )];
 | |
|   UINT8                       DstAddress[MAX(sizeof(EFI_IPv6_ADDRESS)        , sizeof(EFI_IPv4_ADDRESS)          )];
 | |
|   PING_IPX_COMPLETION_TOKEN   RxToken;
 | |
|   UINT16                      FailedCount;
 | |
| } PING_PRIVATE_DATA;
 | |
| 
 | |
| /**
 | |
|   Calculate the internet checksum (see RFC 1071).
 | |
| 
 | |
|   @param[in] Packet  Buffer which contains the data to be checksummed.
 | |
|   @param[in] Length  Length to be checksummed.
 | |
| 
 | |
|   @retval Checksum     Returns the 16 bit ones complement of 
 | |
|                        ones complement sum of 16 bit words
 | |
| **/
 | |
| UINT16
 | |
| NetChecksum (
 | |
|   IN UINT8   *Buffer,
 | |
|   IN UINT32  Length
 | |
|   )
 | |
| {
 | |
|   UINT32  Sum;
 | |
|   UINT8   Odd;
 | |
|   UINT16  *Packet;
 | |
| 
 | |
|   Packet  = (UINT16 *) Buffer;
 | |
| 
 | |
|   Sum     = 0;
 | |
|   Odd     = (UINT8) (Length & 1);
 | |
|   Length >>= 1;
 | |
|   while ((Length--) != 0) {
 | |
|     Sum += *Packet++;
 | |
|   }
 | |
| 
 | |
|   if (Odd != 0) {
 | |
|     Sum += *(UINT8 *) Packet;
 | |
|   }
 | |
| 
 | |
|   Sum = (Sum & 0xffff) + (Sum >> 16);
 | |
| 
 | |
|   //
 | |
|   // in case above carried
 | |
|   //
 | |
|   Sum += Sum >> 16;
 | |
| 
 | |
|   return (UINT16) Sum;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Reads and returns the current value of register.
 | |
|   In IA64, the register is the Interval Timer Vector (ITV).
 | |
|   In X86(IA32/X64), the register is the Time Stamp Counter (TSC)
 | |
| 
 | |
|   @return The current value of the register.
 | |
| 
 | |
| **/
 | |
| 
 | |
| STATIC CONST SHELL_PARAM_ITEM    PingParamList[] = {
 | |
|   {
 | |
|     L"-l",
 | |
|     TypeValue
 | |
|   },
 | |
|   {
 | |
|     L"-n",
 | |
|     TypeValue
 | |
|   },
 | |
|   {
 | |
|     L"-s",
 | |
|     TypeValue
 | |
|   },
 | |
|   {
 | |
|     L"-_s",
 | |
|     TypeValue
 | |
|   },
 | |
|   {
 | |
|     L"-_ip6",
 | |
|     TypeFlag
 | |
|   },
 | |
|   {
 | |
|     NULL,
 | |
|     TypeMax
 | |
|   },
 | |
| };
 | |
| 
 | |
| //
 | |
| // Global Variables in Ping command.
 | |
| //
 | |
| STATIC CONST CHAR16      *mDstString;
 | |
| STATIC CONST CHAR16      *mSrcString;
 | |
| 
 | |
| /**
 | |
|   RTT timer tick routine.
 | |
| 
 | |
|   @param[in]    Event    A EFI_EVENT type event.
 | |
|   @param[in]    Context  The pointer to Context.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| EFIAPI
 | |
| RttTimerTickRoutine (
 | |
|   IN EFI_EVENT    Event,
 | |
|   IN VOID         *Context
 | |
|   )
 | |
| {
 | |
|   UINT32     *RttTimerTick;
 | |
| 
 | |
|   RttTimerTick = (UINT32*) Context;
 | |
|   (*RttTimerTick)++;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Get the timer period of the system.
 | |
| 
 | |
|   This function tries to get the system timer period by creating
 | |
|   an 1ms period timer.
 | |
| 
 | |
|   @return     System timer period in MS, or 0 if operation failed.
 | |
| 
 | |
| **/
 | |
| UINT32
 | |
| GetTimerPeriod(
 | |
|   VOID
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS                 Status;
 | |
|   UINT32                     RttTimerTick;
 | |
|   EFI_EVENT                  TimerEvent;
 | |
|   UINT32                     StallCounter;
 | |
|   EFI_TPL                    OldTpl;
 | |
| 
 | |
|   RttTimerTick = 0;
 | |
|   StallCounter   = 0;
 | |
| 
 | |
|   Status = gBS->CreateEvent (
 | |
|                   EVT_TIMER | EVT_NOTIFY_SIGNAL,
 | |
|                   TPL_NOTIFY,
 | |
|                   RttTimerTickRoutine,
 | |
|                   &RttTimerTick,
 | |
|                   &TimerEvent
 | |
|                   );
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return 0;
 | |
|   }
 | |
| 
 | |
|   OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
 | |
|   Status = gBS->SetTimer (
 | |
|                   TimerEvent,
 | |
|                   TimerPeriodic,
 | |
|                   TICKS_PER_MS
 | |
|                   );
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     gBS->CloseEvent (TimerEvent);
 | |
|     return 0;
 | |
|   }
 | |
| 
 | |
|   while (RttTimerTick < 10) {
 | |
|     gBS->Stall (STALL_1_MILLI_SECOND);
 | |
|     ++StallCounter;
 | |
|   }
 | |
| 
 | |
|   gBS->RestoreTPL (OldTpl);
 | |
| 
 | |
|   gBS->SetTimer (TimerEvent, TimerCancel, 0);
 | |
|   gBS->CloseEvent (TimerEvent);
 | |
| 
 | |
|   return StallCounter / RttTimerTick;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Initialize the timer event for RTT (round trip time).
 | |
| 
 | |
|   @param[in]    Private    The pointer to PING_PRIVATE_DATA.
 | |
| 
 | |
|   @retval EFI_SUCCESS      RTT timer is started.
 | |
|   @retval Others           Failed to start the RTT timer.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| PingInitRttTimer (
 | |
|   PING_PRIVATE_DATA      *Private
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS                 Status;
 | |
| 
 | |
|   Private->TimerPeriod = GetTimerPeriod ();
 | |
|   if (Private->TimerPeriod == 0) {
 | |
|     return EFI_ABORTED;
 | |
|   }
 | |
|   
 | |
|   Private->RttTimerTick = 0;
 | |
|   Status = gBS->CreateEvent (
 | |
|                   EVT_TIMER | EVT_NOTIFY_SIGNAL,
 | |
|                   TPL_NOTIFY,
 | |
|                   RttTimerTickRoutine,
 | |
|                   &Private->RttTimerTick,
 | |
|                   &Private->RttTimer
 | |
|                   );
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   Status = gBS->SetTimer (
 | |
|                   Private->RttTimer,
 | |
|                   TimerPeriodic,
 | |
|                   TICKS_PER_MS
 | |
|                   );
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     gBS->CloseEvent (Private->RttTimer);
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Free RTT timer event resource.
 | |
| 
 | |
|   @param[in]    Private    The pointer to PING_PRIVATE_DATA.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| PingFreeRttTimer (
 | |
|   PING_PRIVATE_DATA      *Private
 | |
|   )
 | |
| {
 | |
|   if (Private->RttTimer != NULL) {
 | |
|     gBS->SetTimer (Private->RttTimer, TimerCancel, 0);
 | |
|     gBS->CloseEvent (Private->RttTimer);
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Read the current time.
 | |
|   
 | |
|   @param[in]    Private    The pointer to PING_PRIVATE_DATA.
 | |
| 
 | |
|   @retval the current tick value.
 | |
| **/
 | |
| UINT32
 | |
| ReadTime (
 | |
|   PING_PRIVATE_DATA      *Private
 | |
|   )
 | |
| {
 | |
|   return Private->RttTimerTick;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Calculate a duration in ms.
 | |
| 
 | |
|   @param[in]    Private   The pointer to PING_PRIVATE_DATA.
 | |
|   @param[in]    Begin     The start point of time.
 | |
|   @param[in]    End       The end point of time.
 | |
| 
 | |
|   @return               The duration in ms.
 | |
|   @retval 0             The parameters were not valid.
 | |
| **/
 | |
| UINT32
 | |
| CalculateTick (
 | |
|   PING_PRIVATE_DATA      *Private,
 | |
|   IN UINT32              Begin,
 | |
|   IN UINT32              End
 | |
|   )
 | |
| {
 | |
|   if (End < Begin) {
 | |
|     return (0);
 | |
|   }
 | |
| 
 | |
|   return (End - Begin) * Private->TimerPeriod;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Destroy PING_ICMPX_TX_INFO, and recollect the memory.
 | |
| 
 | |
|   @param[in]    TxInfo    The pointer to PING_ICMPX_TX_INFO.
 | |
|   @param[in]    IpChoice  Whether the token is IPv4 or IPv6
 | |
| **/
 | |
| VOID
 | |
| PingDestroyTxInfo (
 | |
|   IN PING_ICMPX_TX_INFO    *TxInfo,
 | |
|   IN UINT32                IpChoice
 | |
|   )
 | |
| {
 | |
|   EFI_IP6_TRANSMIT_DATA    *Ip6TxData;
 | |
|   EFI_IP4_TRANSMIT_DATA    *Ip4TxData;
 | |
|   EFI_IP6_FRAGMENT_DATA    *FragData;
 | |
|   UINTN                    Index;
 | |
| 
 | |
|   if (TxInfo == NULL) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (TxInfo->Token != NULL) {
 | |
| 
 | |
|     if (TxInfo->Token->Event != NULL) {
 | |
|       gBS->CloseEvent (TxInfo->Token->Event);
 | |
|     }
 | |
| 
 | |
|     if (TxInfo->Token->Packet.TxData != NULL) {
 | |
|       if (IpChoice == PING_IP_CHOICE_IP6) {
 | |
|         Ip6TxData = TxInfo->Token->Packet.TxData;
 | |
| 
 | |
|         if (Ip6TxData->OverrideData != NULL) {
 | |
|           FreePool (Ip6TxData->OverrideData);
 | |
|         }
 | |
| 
 | |
|         if (Ip6TxData->ExtHdrs != NULL) {
 | |
|           FreePool (Ip6TxData->ExtHdrs);
 | |
|         }
 | |
| 
 | |
|         for (Index = 0; Index < Ip6TxData->FragmentCount; Index++) {
 | |
|           FragData = Ip6TxData->FragmentTable[Index].FragmentBuffer;
 | |
|           if (FragData != NULL) {
 | |
|             FreePool (FragData);
 | |
|           }
 | |
|         }
 | |
|       } else {
 | |
|         Ip4TxData = TxInfo->Token->Packet.TxData;
 | |
| 
 | |
|         if (Ip4TxData->OverrideData != NULL) {
 | |
|           FreePool (Ip4TxData->OverrideData);
 | |
|         }
 | |
| 
 | |
|         for (Index = 0; Index < Ip4TxData->FragmentCount; Index++) {
 | |
|           FragData = Ip4TxData->FragmentTable[Index].FragmentBuffer;
 | |
|           if (FragData != NULL) {
 | |
|             FreePool (FragData);
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     FreePool (TxInfo->Token);
 | |
|   }
 | |
| 
 | |
|   FreePool (TxInfo);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Match the request, and reply with SequenceNum/TimeStamp.
 | |
| 
 | |
|   @param[in]    Private    The pointer to PING_PRIVATE_DATA.
 | |
|   @param[in]    Packet     The pointer to ICMPX_ECHO_REQUEST_REPLY.
 | |
| 
 | |
|   @retval EFI_SUCCESS      The match is successful.
 | |
|   @retval EFI_NOT_FOUND    The reply can't be matched with any request.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| Ping6MatchEchoReply (
 | |
|   IN PING_PRIVATE_DATA           *Private,
 | |
|   IN ICMPX_ECHO_REQUEST_REPLY    *Packet
 | |
|   )
 | |
| {
 | |
|   PING_ICMPX_TX_INFO     *TxInfo;
 | |
|   LIST_ENTRY             *Entry;
 | |
|   LIST_ENTRY             *NextEntry;
 | |
| 
 | |
|   NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) {
 | |
|     TxInfo = BASE_CR (Entry, PING_ICMPX_TX_INFO, Link);
 | |
| 
 | |
|     if ((TxInfo->SequenceNum == Packet->SequenceNum) && (TxInfo->TimeStamp == Packet->TimeStamp)) {
 | |
|       Private->RxCount++;
 | |
|       RemoveEntryList (&TxInfo->Link);
 | |
|       PingDestroyTxInfo (TxInfo, Private->IpChoice);
 | |
|       return EFI_SUCCESS;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return EFI_NOT_FOUND;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   The original intention is to send a request.
 | |
|   Currently, the application retransmits an icmp6 echo request packet
 | |
|   per second in sendnumber times that is specified by the user.
 | |
|   Because nothing can be done here, all things move to the timer rountine.
 | |
| 
 | |
|   @param[in]    Event      A EFI_EVENT type event.
 | |
|   @param[in]    Context    The pointer to Context.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| EFIAPI
 | |
| Ping6OnEchoRequestSent (
 | |
|   IN EFI_EVENT    Event,
 | |
|   IN VOID         *Context
 | |
|   )
 | |
| {
 | |
| }
 | |
| 
 | |
| /**
 | |
|   receive reply, match and print reply infomation.
 | |
| 
 | |
|   @param[in]    Event      A EFI_EVENT type event.
 | |
|   @param[in]    Context    The pointer to context.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| EFIAPI
 | |
| Ping6OnEchoReplyReceived (
 | |
|   IN EFI_EVENT    Event,
 | |
|   IN VOID         *Context
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS                  Status;
 | |
|   PING_PRIVATE_DATA           *Private;
 | |
|   ICMPX_ECHO_REQUEST_REPLY    *Reply;
 | |
|   UINT32                      PayLoad;
 | |
|   UINT32                      Rtt;
 | |
| 
 | |
|   Private = (PING_PRIVATE_DATA *) Context;
 | |
| 
 | |
|   if (Private == NULL || Private->Status == EFI_ABORTED || Private->Signature != PING_PRIVATE_DATA_SIGNATURE) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (Private->RxToken.Packet.RxData == NULL) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (Private->IpChoice == PING_IP_CHOICE_IP6) {
 | |
|     Reply   = ((EFI_IP6_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->FragmentTable[0].FragmentBuffer;
 | |
|     PayLoad = ((EFI_IP6_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->DataLength;
 | |
|     if (((EFI_IP6_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->Header->NextHeader != IP6_ICMP) {
 | |
|       goto ON_EXIT;
 | |
|     }
 | |
|     if (!IP6_IS_MULTICAST ((EFI_IPv6_ADDRESS*)&Private->DstAddress) && 
 | |
|         !EFI_IP6_EQUAL (&((EFI_IP6_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->Header->SourceAddress, (EFI_IPv6_ADDRESS*)&Private->DstAddress)) {
 | |
|       goto ON_EXIT;
 | |
|     }
 | |
| 
 | |
|     if ((Reply->Type != ICMP_V6_ECHO_REPLY) || (Reply->Code != 0)) {
 | |
|       goto ON_EXIT;
 | |
|     }
 | |
|   } else {
 | |
|     Reply   = ((EFI_IP4_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->FragmentTable[0].FragmentBuffer;
 | |
|     PayLoad = ((EFI_IP4_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->DataLength;
 | |
|     if (!IP4_IS_MULTICAST (EFI_IP4(*(EFI_IPv4_ADDRESS*)Private->DstAddress)) && 
 | |
|         !EFI_IP4_EQUAL (&((EFI_IP4_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->Header->SourceAddress, (EFI_IPv4_ADDRESS*)&Private->DstAddress)) {
 | |
|       goto ON_EXIT;
 | |
|     }
 | |
| 
 | |
|     if ((Reply->Type != ICMP_V4_ECHO_REPLY) || (Reply->Code != 0)) {
 | |
|       goto ON_EXIT;
 | |
|     }
 | |
|   }
 | |
|   
 | |
| 
 | |
|   if (PayLoad != Private->BufferSize) {
 | |
|     goto ON_EXIT;
 | |
|   }
 | |
|   //
 | |
|   // Check whether the reply matches the sent request before.
 | |
|   //
 | |
|   Status = Ping6MatchEchoReply (Private, Reply);
 | |
|   if (EFI_ERROR(Status)) {
 | |
|     goto ON_EXIT;
 | |
|   }
 | |
|   //
 | |
|   // Display statistics on this icmp6 echo reply packet.
 | |
|   //
 | |
|   Rtt  = CalculateTick (Private, Reply->TimeStamp, ReadTime (Private));
 | |
| 
 | |
|   Private->RttSum += Rtt;
 | |
|   Private->RttMin  = Private->RttMin > Rtt ? Rtt : Private->RttMin;
 | |
|   Private->RttMax  = Private->RttMax < Rtt ? Rtt : Private->RttMax;
 | |
| 
 | |
|   ShellPrintHiiEx (
 | |
|     -1,
 | |
|     -1,
 | |
|     NULL,
 | |
|     STRING_TOKEN (STR_PING_REPLY_INFO),
 | |
|     gShellNetwork1HiiHandle,
 | |
|     PayLoad,
 | |
|     mDstString,
 | |
|     Reply->SequenceNum,
 | |
|     Private->IpChoice == PING_IP_CHOICE_IP6?((EFI_IP6_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->Header->HopLimit:0,
 | |
|     Rtt,
 | |
|     Rtt + Private->TimerPeriod
 | |
|     );
 | |
| 
 | |
| ON_EXIT:
 | |
| 
 | |
|   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;
 | |
|   }
 | |
|   //
 | |
|   // Singal to recycle the each rxdata here, not at the end of process.
 | |
|   //
 | |
|   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);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   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);
 | |
|   Status = Private->ProtocolPointers.Transmit (Private->IpProtocol, TxInfo->Token);
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     PingDestroyTxInfo (TxInfo, Private->IpChoice);
 | |
|     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 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;
 | |
|   BOOLEAN                          MediaPresent;
 | |
|   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;
 | |
|   MediaPresent      = TRUE;
 | |
|   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.
 | |
|       //
 | |
|       NetLibDetectMedia (HandleBuffer[HandleIndex], &MediaPresent);
 | |
|       if (!MediaPresent) {
 | |
|         //
 | |
|         // 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;
 | |
| }
 |