2 The implementation for Ping6 application.
4 Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php.
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
16 #include "UefiShellNetwork2CommandsLib.h"
18 #define PING6_DEFAULT_TIMEOUT 5000
19 #define PING6_MAX_SEND_NUMBER 10000
20 #define PING6_MAX_BUFFER_SIZE 32768
21 #define PING6_ONE_SECOND 10000000
22 #define STALL_1_MILLI_SECOND 1000
26 typedef struct _ICMP6_ECHO_REQUEST_REPLY
{
34 } ICMP6_ECHO_REQUEST_REPLY
;
38 typedef struct _PING6_ICMP6_TX_INFO
{
42 EFI_IP6_COMPLETION_TOKEN
*Token
;
43 } PING6_ICMP6_TX_INFO
;
45 typedef struct _PING6_PRIVATE_DATA
{
46 EFI_HANDLE ImageHandle
;
48 EFI_HANDLE Ip6ChildHandle
;
49 EFI_IP6_PROTOCOL
*Ip6
;
58 EFI_IP6_COMPLETION_TOKEN RxToken
;
66 EFI_IPv6_ADDRESS SrcAddress
;
67 EFI_IPv6_ADDRESS DstAddress
;
73 SHELL_PARAM_ITEM Ping6ParamList
[] = {
97 // Global Variables in Ping6 application.
99 CONST CHAR16
*mIp6DstString
;
100 CONST CHAR16
*mIp6SrcString
;
101 EFI_CPU_ARCH_PROTOCOL
*Cpu
= NULL
;
104 RTT timer tick routine.
106 @param[in] Event A EFI_EVENT type event.
107 @param[in] Context The pointer to Context.
112 Ping6RttTimerTickRoutine (
117 UINT32
*RttTimerTick
;
119 RttTimerTick
= (UINT32
*) Context
;
124 Get the timer period of the system.
126 This function tries to get the system timer period by creating
129 @return System timer period in MS, or 0 if operation failed.
139 EFI_EVENT TimerEvent
;
146 Status
= gBS
->CreateEvent (
147 EVT_TIMER
| EVT_NOTIFY_SIGNAL
,
149 Ping6RttTimerTickRoutine
,
153 if (EFI_ERROR (Status
)) {
157 OldTpl
= gBS
->RaiseTPL (TPL_CALLBACK
);
158 Status
= gBS
->SetTimer (
163 if (EFI_ERROR (Status
)) {
164 gBS
->CloseEvent (TimerEvent
);
168 while (RttTimerTick
< 10) {
169 gBS
->Stall (STALL_1_MILLI_SECOND
);
173 gBS
->RestoreTPL (OldTpl
);
175 gBS
->SetTimer (TimerEvent
, TimerCancel
, 0);
176 gBS
->CloseEvent (TimerEvent
);
178 return StallCounter
/ RttTimerTick
;
183 Initialize the timer event for RTT (round trip time).
185 @param[in] Private The pointer to PING6_PRIVATE_DATA.
187 @retval EFI_SUCCESS RTT timer is started.
188 @retval Others Failed to start the RTT timer.
193 IN PING6_PRIVATE_DATA
*Private
198 Private
->TimerPeriod
= Ping6GetTimerPeriod ();
199 if (Private
->TimerPeriod
== 0) {
203 Private
->RttTimerTick
= 0;
204 Status
= gBS
->CreateEvent (
205 EVT_TIMER
| EVT_NOTIFY_SIGNAL
,
207 Ping6RttTimerTickRoutine
,
208 &Private
->RttTimerTick
,
211 if (EFI_ERROR (Status
)) {
215 Status
= gBS
->SetTimer (
220 if (EFI_ERROR (Status
)) {
221 gBS
->CloseEvent (Private
->RttTimer
);
230 Free RTT timer event resource.
232 @param[in] Private The pointer to PING6_PRIVATE_DATA.
237 IN PING6_PRIVATE_DATA
*Private
240 if (Private
->RttTimer
!= NULL
) {
241 gBS
->SetTimer (Private
->RttTimer
, TimerCancel
, 0);
242 gBS
->CloseEvent (Private
->RttTimer
);
247 Read the current time.
249 @param[in] Private The pointer to PING6_PRIVATE_DATA.
251 @retval the current tick value.
255 IN PING6_PRIVATE_DATA
*Private
258 return Private
->RttTimerTick
;
262 Get and calculate the duration in ms.
264 @param[in] Private The pointer to PING6_PRIVATE_DATA.
265 @param[in] Begin The start point of time.
266 @param[in] End The end point of time.
268 @return The duration in ms.
273 IN PING6_PRIVATE_DATA
*Private
,
282 return (End
- Begin
) * Private
->TimerPeriod
;
287 Destroy IPING6_ICMP6_TX_INFO, and recollect the memory.
289 @param[in] TxInfo The pointer to PING6_ICMP6_TX_INFO.
294 IN PING6_ICMP6_TX_INFO
*TxInfo
297 EFI_IP6_TRANSMIT_DATA
*TxData
;
298 EFI_IP6_FRAGMENT_DATA
*FragData
;
301 ASSERT (TxInfo
!= NULL
);
303 if (TxInfo
->Token
!= NULL
) {
305 if (TxInfo
->Token
->Event
!= NULL
) {
306 gBS
->CloseEvent (TxInfo
->Token
->Event
);
309 TxData
= TxInfo
->Token
->Packet
.TxData
;
310 if (TxData
!= NULL
) {
312 if (TxData
->OverrideData
!= NULL
) {
313 FreePool (TxData
->OverrideData
);
316 if (TxData
->ExtHdrs
!= NULL
) {
317 FreePool (TxData
->ExtHdrs
);
320 for (Index
= 0; Index
< TxData
->FragmentCount
; Index
++) {
321 FragData
= TxData
->FragmentTable
[Index
].FragmentBuffer
;
322 if (FragData
!= NULL
) {
328 FreePool (TxInfo
->Token
);
335 Match the request, and reply with SequenceNum/TimeStamp.
337 @param[in] Private The pointer to PING6_PRIVATE_DATA.
338 @param[in] Packet The pointer to ICMP6_ECHO_REQUEST_REPLY.
340 @retval EFI_SUCCESS The match is successful.
341 @retval EFI_NOT_FOUND The reply can't be matched with any request.
345 Ping6OnMatchEchoReply (
346 IN PING6_PRIVATE_DATA
*Private
,
347 IN ICMP6_ECHO_REQUEST_REPLY
*Packet
350 PING6_ICMP6_TX_INFO
*TxInfo
;
352 LIST_ENTRY
*NextEntry
;
354 NET_LIST_FOR_EACH_SAFE (Entry
, NextEntry
, &Private
->TxList
) {
355 TxInfo
= BASE_CR (Entry
, PING6_ICMP6_TX_INFO
, Link
);
357 if ((TxInfo
->SequenceNum
== Packet
->SequenceNum
) && (TxInfo
->TimeStamp
== Packet
->TimeStamp
)) {
359 RemoveEntryList (&TxInfo
->Link
);
360 Ping6DestroyTxInfo (TxInfo
);
365 return EFI_NOT_FOUND
;
369 The original intention is to send a request.
370 Currently, the application retransmits an icmp6 echo request packet
371 per second in sendnumber times that is specified by the user.
372 Because nothing can be done here, all things move to the timer rountine.
374 @param[in] Event A EFI_EVENT type event.
375 @param[in] Context The pointer to Context.
380 Ping6OnEchoRequestSent6 (
388 receive reply, match and print reply infomation.
390 @param[in] Event A EFI_EVENT type event.
391 @param[in] Context The pointer to context.
396 Ping6OnEchoReplyReceived6 (
402 PING6_PRIVATE_DATA
*Private
;
403 EFI_IP6_COMPLETION_TOKEN
*RxToken
;
404 EFI_IP6_RECEIVE_DATA
*RxData
;
405 ICMP6_ECHO_REQUEST_REPLY
*Reply
;
409 Private
= (PING6_PRIVATE_DATA
*) Context
;
411 if (Private
->Status
== EFI_ABORTED
) {
415 RxToken
= &Private
->RxToken
;
416 RxData
= RxToken
->Packet
.RxData
;
417 Reply
= RxData
->FragmentTable
[0].FragmentBuffer
;
418 PayLoad
= RxData
->DataLength
;
420 if (RxData
->Header
->NextHeader
!= IP6_ICMP
) {
424 if (!IP6_IS_MULTICAST (&Private
->DstAddress
) &&
425 !EFI_IP6_EQUAL (&RxData
->Header
->SourceAddress
, &Private
->DstAddress
)) {
429 if ((Reply
->Type
!= ICMP_V6_ECHO_REPLY
) || (Reply
->Code
!= 0)) {
433 if (PayLoad
!= Private
->BufferSize
) {
437 // Check whether the reply matches the sent request before.
439 Status
= Ping6OnMatchEchoReply (Private
, Reply
);
440 if (EFI_ERROR(Status
)) {
444 // Display statistics on this icmp6 echo reply packet.
446 Rtt
= Ping6CalculateTick (Private
, Reply
->TimeStamp
, Ping6ReadTime (Private
));
448 Private
->RttSum
+= Rtt
;
449 Private
->RttMin
= Private
->RttMin
> Rtt
? Rtt
: Private
->RttMin
;
450 Private
->RttMax
= Private
->RttMax
< Rtt
? Rtt
: Private
->RttMax
;
456 STRING_TOKEN (STR_PING6_REPLY_INFO
),
457 gShellNetwork2HiiHandle
,
461 RxData
->Header
->HopLimit
,
463 Rtt
+ Private
->TimerPeriod
468 if (Private
->RxCount
< Private
->SendNum
) {
470 // Continue to receive icmp6 echo reply packets.
472 RxToken
->Status
= EFI_ABORTED
;
474 Status
= Private
->Ip6
->Receive (Private
->Ip6
, RxToken
);
476 if (EFI_ERROR (Status
)) {
477 Private
->Status
= EFI_ABORTED
;
481 // All reply have already been received from the dest host.
483 Private
->Status
= EFI_SUCCESS
;
486 // Singal to recycle the each rxdata here, not at the end of process.
488 gBS
->SignalEvent (RxData
->RecycleSignal
);
492 Initial EFI_IP6_COMPLETION_TOKEN.
494 @param[in] Private The pointer of PING6_PRIVATE_DATA.
495 @param[in] TimeStamp The TimeStamp of request.
496 @param[in] SequenceNum The SequenceNum of request.
498 @return The pointer of EFI_IP6_COMPLETION_TOKEN.
501 EFI_IP6_COMPLETION_TOKEN
*
503 IN PING6_PRIVATE_DATA
*Private
,
505 IN UINT16 SequenceNum
509 EFI_IP6_COMPLETION_TOKEN
*Token
;
510 EFI_IP6_TRANSMIT_DATA
*TxData
;
511 ICMP6_ECHO_REQUEST_REPLY
*Request
;
513 Request
= AllocateZeroPool (Private
->BufferSize
);
515 if (Request
== NULL
) {
519 // Assembly icmp6 echo request packet.
521 Request
->Type
= ICMP_V6_ECHO_REQUEST
;
523 Request
->SequenceNum
= SequenceNum
;
524 Request
->TimeStamp
= TimeStamp
;
525 Request
->Identifier
= 0;
527 // Leave check sum to ip6 layer, since it has no idea of source address
530 Request
->Checksum
= 0;
532 TxData
= AllocateZeroPool (sizeof (EFI_IP6_TRANSMIT_DATA
));
534 if (TxData
== NULL
) {
539 // Assembly ipv6 token for transmit.
541 TxData
->OverrideData
= 0;
542 TxData
->ExtHdrsLength
= 0;
543 TxData
->ExtHdrs
= NULL
;
544 TxData
->DataLength
= Private
->BufferSize
;
545 TxData
->FragmentCount
= 1;
546 TxData
->FragmentTable
[0].FragmentBuffer
= (VOID
*) Request
;
547 TxData
->FragmentTable
[0].FragmentLength
= Private
->BufferSize
;
549 Token
= AllocateZeroPool (sizeof (EFI_IP6_COMPLETION_TOKEN
));
557 Token
->Status
= EFI_ABORTED
;
558 Token
->Packet
.TxData
= TxData
;
560 Status
= gBS
->CreateEvent (
563 Ping6OnEchoRequestSent6
,
568 if (EFI_ERROR (Status
)) {
579 Transmit the EFI_IP6_COMPLETION_TOKEN.
581 @param[in] Private The pointer of PING6_PRIVATE_DATA.
583 @retval EFI_SUCCESS Transmitted successfully.
584 @retval EFI_OUT_OF_RESOURCES No memory is available on the platform.
585 @retval others Transmitted unsuccessfully.
589 Ping6SendEchoRequest (
590 IN PING6_PRIVATE_DATA
*Private
594 PING6_ICMP6_TX_INFO
*TxInfo
;
596 TxInfo
= AllocateZeroPool (sizeof (PING6_ICMP6_TX_INFO
));
598 if (TxInfo
== NULL
) {
599 return EFI_OUT_OF_RESOURCES
;
602 TxInfo
->TimeStamp
= Ping6ReadTime (Private
);
603 TxInfo
->SequenceNum
= (UINT16
) (Private
->TxCount
+ 1);
605 TxInfo
->Token
= Ping6GenerateToken (
611 if (TxInfo
->Token
== NULL
) {
612 Ping6DestroyTxInfo (TxInfo
);
613 return EFI_OUT_OF_RESOURCES
;
616 Status
= Private
->Ip6
->Transmit (Private
->Ip6
, TxInfo
->Token
);
618 if (EFI_ERROR (Status
)) {
619 Ping6DestroyTxInfo (TxInfo
);
623 InsertTailList (&Private
->TxList
, &TxInfo
->Link
);
630 Place a completion token into the receive packet queue to receive the echo reply.
632 @param[in] Private The pointer of PING6_PRIVATE_DATA.
634 @retval EFI_SUCCESS Put the token into the receive packet queue successfully.
635 @retval others Put the token into the receive packet queue unsuccessfully.
639 Ping6OnReceiveEchoReply (
640 IN PING6_PRIVATE_DATA
*Private
645 ZeroMem (&Private
->RxToken
, sizeof (EFI_IP6_COMPLETION_TOKEN
));
647 Status
= gBS
->CreateEvent (
650 Ping6OnEchoReplyReceived6
,
652 &Private
->RxToken
.Event
655 if (EFI_ERROR (Status
)) {
659 Private
->RxToken
.Status
= EFI_NOT_READY
;
661 return Private
->Ip6
->Receive (Private
->Ip6
, &Private
->RxToken
);
665 Remove the timeout request from the list.
667 @param[in] Event A EFI_EVENT type event.
668 @param[in] Context The pointer to Context.
673 Ping6OnTimerRoutine6 (
679 PING6_PRIVATE_DATA
*Private
;
680 PING6_ICMP6_TX_INFO
*TxInfo
;
682 LIST_ENTRY
*NextEntry
;
685 Private
= (PING6_PRIVATE_DATA
*) Context
;
688 // Retransmit icmp6 echo request packets per second in sendnumber times.
690 if (Private
->TxCount
< Private
->SendNum
) {
692 Status
= Ping6SendEchoRequest (Private
);
693 if (Private
->TxCount
!= 0){
694 if (EFI_ERROR (Status
)) {
695 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_SEND_REQUEST
), gShellNetwork2HiiHandle
, Private
->TxCount
+ 1);
700 // Check whether any icmp6 echo request in the list timeout.
702 NET_LIST_FOR_EACH_SAFE (Entry
, NextEntry
, &Private
->TxList
) {
703 TxInfo
= BASE_CR (Entry
, PING6_ICMP6_TX_INFO
, Link
);
704 Time
= Ping6CalculateTick (Private
, TxInfo
->TimeStamp
, Ping6ReadTime (Private
));
707 // Remove the timeout echo request from txlist.
709 if (Time
> PING6_DEFAULT_TIMEOUT
) {
711 if (EFI_ERROR (TxInfo
->Token
->Status
)) {
712 Private
->Ip6
->Cancel (Private
->Ip6
, TxInfo
->Token
);
715 // Remove the timeout icmp6 echo request from list.
717 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_TIMEOUT
), gShellNetwork2HiiHandle
, TxInfo
->SequenceNum
);
719 RemoveEntryList (&TxInfo
->Link
);
720 Ping6DestroyTxInfo (TxInfo
);
722 if (IsListEmpty (&Private
->TxList
) && (Private
->TxCount
== Private
->SendNum
)) {
724 // All the left icmp6 echo request in the list timeout.
726 Private
->Status
= EFI_TIMEOUT
;
733 Create a valid IP6 instance.
735 @param[in] Private The pointer of PING6_PRIVATE_DATA.
737 @retval EFI_SUCCESS Create a valid IP6 instance successfully.
738 @retval EFI_ABORTED Locate handle with ip6 service binding protocol unsuccessfully.
739 @retval EFI_INVALID_PARAMETER The source address is unspecified when the destination address is a link -ocal address.
740 @retval EFI_OUT_OF_RESOURCES No memory is available on the platform.
741 @retval EFI_NOT_FOUND The source address is not found.
744 Ping6CreateIpInstance (
745 IN PING6_PRIVATE_DATA
*Private
751 EFI_HANDLE
*HandleBuffer
;
752 BOOLEAN UnspecifiedSrc
;
753 BOOLEAN MediaPresent
;
754 EFI_SERVICE_BINDING_PROTOCOL
*Ip6Sb
;
755 EFI_IP6_CONFIG_PROTOCOL
*Ip6Cfg
;
756 EFI_IP6_CONFIG_DATA Ip6Config
;
757 EFI_IP6_CONFIG_INTERFACE_INFO
*IfInfo
;
759 EFI_IPv6_ADDRESS
*Addr
;
763 UnspecifiedSrc
= FALSE
;
770 // Locate all the handles with ip6 service binding protocol.
772 Status
= gBS
->LocateHandleBuffer (
774 &gEfiIp6ServiceBindingProtocolGuid
,
779 if (EFI_ERROR (Status
) || (HandleNum
== 0)) {
783 if (NetIp6IsUnspecifiedAddr (&Private
->SrcAddress
)) {
785 // SrcAddress is unspecified. So, both connected and configured interface will be automatic selected.
787 UnspecifiedSrc
= TRUE
;
791 // Source address is required when pinging a link-local address.
793 if (NetIp6IsLinkLocalAddr (&Private
->DstAddress
) && UnspecifiedSrc
) {
794 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_INVALID_SOURCE
), gShellNetwork2HiiHandle
);
795 Status
= EFI_INVALID_PARAMETER
;
800 // For each ip6 protocol, check interface addresses list.
802 for (HandleIndex
= 0; HandleIndex
< HandleNum
; HandleIndex
++) {
808 if (UnspecifiedSrc
) {
812 NetLibDetectMedia (HandleBuffer
[HandleIndex
], &MediaPresent
);
821 Status
= gBS
->HandleProtocol (
822 HandleBuffer
[HandleIndex
],
823 &gEfiIp6ServiceBindingProtocolGuid
,
826 if (EFI_ERROR (Status
)) {
831 // Ip6config protocol and ip6 service binding protocol are installed
832 // on the same handle.
834 Status
= gBS
->HandleProtocol (
835 HandleBuffer
[HandleIndex
],
836 &gEfiIp6ConfigProtocolGuid
,
840 if (EFI_ERROR (Status
)) {
844 // Get the interface information size.
846 Status
= Ip6Cfg
->GetData (
848 Ip6ConfigDataTypeInterfaceInfo
,
853 if (Status
!= EFI_BUFFER_TOO_SMALL
) {
854 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_IP6CFG_GETDATA
), gShellNetwork2HiiHandle
, Status
);
858 IfInfo
= AllocateZeroPool (IfInfoSize
);
860 if (IfInfo
== NULL
) {
861 Status
= EFI_OUT_OF_RESOURCES
;
865 // Get the interface info.
867 Status
= Ip6Cfg
->GetData (
869 Ip6ConfigDataTypeInterfaceInfo
,
874 if (EFI_ERROR (Status
)) {
875 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_IP6CFG_GETDATA
), gShellNetwork2HiiHandle
, Status
);
879 // Check whether the source address is one of the interface addresses.
881 for (AddrIndex
= 0; AddrIndex
< IfInfo
->AddressInfoCount
; AddrIndex
++) {
882 Addr
= &(IfInfo
->AddressInfo
[AddrIndex
].Address
);
884 if (UnspecifiedSrc
) {
885 if (!NetIp6IsUnspecifiedAddr (Addr
) && !NetIp6IsLinkLocalAddr (Addr
)) {
887 // Select the interface automatically.
889 CopyMem(&Private
->SrcAddress
, Addr
, sizeof(Private
->SrcAddress
));
892 } else if (EFI_IP6_EQUAL (&Private
->SrcAddress
, Addr
)) {
894 // Match a certain interface address.
900 if (AddrIndex
< IfInfo
->AddressInfoCount
) {
902 // Found a nic handle with right interface address.
911 // No exact interface address matched.
914 if (HandleIndex
== HandleNum
) {
915 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_CONFIGD_NIC_NF
), gShellNetwork2HiiHandle
);
916 Status
= EFI_NOT_FOUND
;
920 Private
->NicHandle
= HandleBuffer
[HandleIndex
];
922 ASSERT (Ip6Sb
!= NULL
);
923 Status
= Ip6Sb
->CreateChild (Ip6Sb
, &Private
->Ip6ChildHandle
);
925 if (EFI_ERROR (Status
)) {
929 Status
= gBS
->OpenProtocol (
930 Private
->Ip6ChildHandle
,
931 &gEfiIp6ProtocolGuid
,
932 (VOID
**) &Private
->Ip6
,
933 Private
->ImageHandle
,
934 Private
->Ip6ChildHandle
,
935 EFI_OPEN_PROTOCOL_GET_PROTOCOL
937 if (EFI_ERROR (Status
)) {
941 ZeroMem (&Ip6Config
, sizeof (EFI_IP6_CONFIG_DATA
));
944 // Configure the ip6 instance for icmp6 packet exchange.
946 Ip6Config
.DefaultProtocol
= 58;
947 Ip6Config
.AcceptAnyProtocol
= FALSE
;
948 Ip6Config
.AcceptIcmpErrors
= TRUE
;
949 Ip6Config
.AcceptPromiscuous
= FALSE
;
950 Ip6Config
.TrafficClass
= 0;
951 Ip6Config
.HopLimit
= 128;
952 Ip6Config
.FlowLabel
= 0;
953 Ip6Config
.ReceiveTimeout
= 0;
954 Ip6Config
.TransmitTimeout
= 0;
956 IP6_COPY_ADDRESS (&Ip6Config
.StationAddress
, &Private
->SrcAddress
);
958 IP6_COPY_ADDRESS (&Ip6Config
.DestinationAddress
, &Private
->DstAddress
);
960 Status
= Private
->Ip6
->Configure (Private
->Ip6
, &Ip6Config
);
962 if (EFI_ERROR (Status
)) {
963 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_IP6_CONFIG
), gShellNetwork2HiiHandle
, Status
);
970 if (HandleBuffer
!= NULL
) {
971 FreePool (HandleBuffer
);
974 if (IfInfo
!= NULL
) {
978 if ((Ip6Sb
!= NULL
) && (Private
->Ip6ChildHandle
!= NULL
)) {
979 Ip6Sb
->DestroyChild (Ip6Sb
, Private
->Ip6ChildHandle
);
986 Destroy the IP6 instance.
988 @param[in] Private The pointer of PING6_PRIVATE_DATA.
992 Ping6DestroyIpInstance (
993 IN PING6_PRIVATE_DATA
*Private
997 EFI_SERVICE_BINDING_PROTOCOL
*Ip6Sb
;
1000 Private
->Ip6ChildHandle
,
1001 &gEfiIp6ProtocolGuid
,
1002 Private
->ImageHandle
,
1003 Private
->Ip6ChildHandle
1006 Status
= gBS
->HandleProtocol (
1008 &gEfiIp6ServiceBindingProtocolGuid
,
1012 if (!EFI_ERROR(Status
)) {
1013 Ip6Sb
->DestroyChild (Ip6Sb
, Private
->Ip6ChildHandle
);
1020 @param[in] ImageHandle The firmware allocated handle for the UEFI image.
1021 @param[in] SendNumber The send request count.
1022 @param[in] BufferSize The send buffer size.
1023 @param[in] SrcAddress The source IPv6 address.
1024 @param[in] DstAddress The destination IPv6 address.
1026 @retval SHELL_SUCCESS The ping6 processed successfullly.
1027 @retval others The ping6 processed unsuccessfully.
1032 IN EFI_HANDLE ImageHandle
,
1033 IN UINT32 SendNumber
,
1034 IN UINT32 BufferSize
,
1035 IN EFI_IPv6_ADDRESS
*SrcAddress
,
1036 IN EFI_IPv6_ADDRESS
*DstAddress
1041 PING6_PRIVATE_DATA
*Private
;
1042 PING6_ICMP6_TX_INFO
*TxInfo
;
1044 LIST_ENTRY
*NextEntry
;
1045 SHELL_STATUS ShellStatus
;
1047 ShellStatus
= SHELL_SUCCESS
;
1048 Private
= AllocateZeroPool (sizeof (PING6_PRIVATE_DATA
));
1050 if (Private
== NULL
) {
1051 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_GEN_OUT_MEM
), gShellNetwork2HiiHandle
, L
"Ping6");
1052 ShellStatus
= SHELL_OUT_OF_RESOURCES
;
1056 Private
->ImageHandle
= ImageHandle
;
1057 Private
->SendNum
= SendNumber
;
1058 Private
->BufferSize
= BufferSize
;
1059 Private
->RttMin
= ~((UINT64
)(0x0));
1060 Private
->Status
= EFI_NOT_READY
;
1062 InitializeListHead (&Private
->TxList
);
1064 IP6_COPY_ADDRESS (&Private
->SrcAddress
, SrcAddress
);
1065 IP6_COPY_ADDRESS (&Private
->DstAddress
, DstAddress
);
1068 // Open and configure a ip6 instance for ping6.
1070 Status
= Ping6CreateIpInstance (Private
);
1072 if (EFI_ERROR (Status
)) {
1073 ShellStatus
= SHELL_ACCESS_DENIED
;
1077 // Print the command line itself.
1079 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_START
), gShellNetwork2HiiHandle
, mIp6DstString
, Private
->BufferSize
);
1081 // Create a ipv6 token to receive the first icmp6 echo reply packet.
1083 Status
= Ping6OnReceiveEchoReply (Private
);
1085 if (EFI_ERROR (Status
)) {
1086 ShellStatus
= SHELL_ACCESS_DENIED
;
1090 // Create and start timer to send icmp6 echo request packet per second.
1092 Status
= gBS
->CreateEvent (
1093 EVT_TIMER
| EVT_NOTIFY_SIGNAL
,
1095 Ping6OnTimerRoutine6
,
1100 if (EFI_ERROR (Status
)) {
1101 ShellStatus
= SHELL_ACCESS_DENIED
;
1106 // Start a timer to calculate the RTT.
1108 Status
= Ping6InitRttTimer (Private
);
1109 if (EFI_ERROR (Status
)) {
1110 ShellStatus
= SHELL_ACCESS_DENIED
;
1115 // Create a ipv6 token to send the first icmp6 echo request packet.
1117 Status
= Ping6SendEchoRequest (Private
);
1119 // EFI_NOT_READY for IPsec is enable and IKE is not established.
1121 if (EFI_ERROR (Status
) && (Status
!= EFI_NOT_READY
)) {
1122 ShellStatus
= SHELL_ACCESS_DENIED
;
1123 if(Status
== EFI_NOT_FOUND
) {
1124 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_NOSOURCE_INDOMAIN
), gShellNetwork2HiiHandle
, mIp6DstString
);
1130 Status
= gBS
->SetTimer (
1136 if (EFI_ERROR (Status
)) {
1137 ShellStatus
= SHELL_ACCESS_DENIED
;
1141 // Control the ping6 process by two factors:
1143 // 2. Private->Status
1144 // 2.1. success means all icmp6 echo request packets get reply packets.
1145 // 2.2. timeout means the last icmp6 echo reply request timeout to get reply.
1146 // 2.3. noready means ping6 process is on-the-go.
1148 while (Private
->Status
== EFI_NOT_READY
) {
1149 Private
->Ip6
->Poll (Private
->Ip6
);
1152 // Terminate the ping6 process by 'esc' or 'ctl-c'.
1154 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
);
1156 if (!EFI_ERROR(Status
)) {
1157 if ((Key
.UnicodeChar
== 0x1b) || (Key
.UnicodeChar
== 0x03) ||
1158 ((Key
.UnicodeChar
== 0) && (Key
.ScanCode
== SCAN_ESC
))) {
1166 // Display the statistics in all.
1168 gBS
->SetTimer (Private
->Timer
, TimerCancel
, 0);
1170 if (Private
->TxCount
!= 0) {
1175 STRING_TOKEN (STR_PING6_STAT
),
1176 gShellNetwork2HiiHandle
,
1179 (100 * (Private
->TxCount
- Private
->RxCount
)) / Private
->TxCount
,
1184 if (Private
->RxCount
!= 0) {
1189 STRING_TOKEN (STR_PING6_RTT
),
1190 gShellNetwork2HiiHandle
,
1192 Private
->RttMin
+ Private
->TimerPeriod
,
1194 Private
->RttMax
+ Private
->TimerPeriod
,
1195 DivU64x64Remainder (Private
->RttSum
, Private
->RxCount
, NULL
),
1196 DivU64x64Remainder (Private
->RttSum
, Private
->RxCount
, NULL
) + Private
->TimerPeriod
1202 if (Private
!= NULL
) {
1203 Private
->Status
= EFI_ABORTED
;
1205 NET_LIST_FOR_EACH_SAFE (Entry
, NextEntry
, &Private
->TxList
) {
1206 TxInfo
= BASE_CR (Entry
, PING6_ICMP6_TX_INFO
, Link
);
1208 Status
= Private
->Ip6
->Cancel (Private
->Ip6
, TxInfo
->Token
);
1210 RemoveEntryList (&TxInfo
->Link
);
1211 Ping6DestroyTxInfo (TxInfo
);
1214 Ping6FreeRttTimer (Private
);
1216 if (Private
->Timer
!= NULL
) {
1217 gBS
->CloseEvent (Private
->Timer
);
1220 if (Private
->Ip6
!= NULL
) {
1221 Status
= Private
->Ip6
->Cancel (Private
->Ip6
, &Private
->RxToken
);
1224 if (Private
->RxToken
.Event
!= NULL
) {
1225 gBS
->CloseEvent (Private
->RxToken
.Event
);
1228 if (Private
->Ip6ChildHandle
!= NULL
) {
1229 Ping6DestroyIpInstance (Private
);
1239 Function for 'ping6' command.
1241 @param[in] ImageHandle Handle to the Image (NULL if Internal).
1242 @param[in] SystemTable Pointer to the System Table (NULL if Internal).
1244 @retval SHELL_SUCCESS The ping6 processed successfullly.
1245 @retval others The ping6 processed unsuccessfully.
1250 ShellCommandRunPing6 (
1251 IN EFI_HANDLE ImageHandle
,
1252 IN EFI_SYSTEM_TABLE
*SystemTable
1256 SHELL_STATUS ShellStatus
;
1257 EFI_IPv6_ADDRESS DstAddress
;
1258 EFI_IPv6_ADDRESS SrcAddress
;
1261 LIST_ENTRY
*ParamPackage
;
1262 CONST CHAR16
*ValueStr
;
1263 CONST CHAR16
*ValueStrPtr
;
1264 UINTN NonOptionCount
;
1265 CHAR16
*ProblemParam
;
1267 ProblemParam
= NULL
;
1268 ShellStatus
= SHELL_SUCCESS
;
1270 Status
= ShellCommandLineParseEx (Ping6ParamList
, &ParamPackage
, &ProblemParam
, TRUE
, FALSE
);
1271 if (EFI_ERROR(Status
)) {
1272 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_INVALID_INPUT
), gShellNetwork2HiiHandle
);
1273 ShellStatus
= SHELL_INVALID_PARAMETER
;
1281 // Parse the parameter of count number.
1283 ValueStr
= ShellCommandLineGetValue (ParamPackage
, L
"-n");
1284 ValueStrPtr
= ValueStr
;
1285 if (ValueStr
!= NULL
) {
1286 SendNumber
= ShellStrToUintn (ValueStrPtr
);
1289 // ShellStrToUintn will return 0 when input is 0 or an invalid input string.
1291 if ((SendNumber
== 0) || (SendNumber
> PING6_MAX_SEND_NUMBER
)) {
1292 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_INVALID_SEND_NUMBER
), gShellNetwork2HiiHandle
, ValueStr
);
1293 ShellStatus
= SHELL_INVALID_PARAMETER
;
1298 // Parse the parameter of buffer size.
1300 ValueStr
= ShellCommandLineGetValue (ParamPackage
, L
"-l");
1301 ValueStrPtr
= ValueStr
;
1302 if (ValueStr
!= NULL
) {
1303 BufferSize
= ShellStrToUintn (ValueStrPtr
);
1306 // ShellStrToUintn will return 0 when input is 0 or an invalid input string.
1308 if ((BufferSize
< 16) || (BufferSize
> PING6_MAX_BUFFER_SIZE
)) {
1309 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_INVALID_BUFFER_SIZE
), gShellNetwork2HiiHandle
, ValueStr
);
1310 ShellStatus
= SHELL_INVALID_PARAMETER
;
1315 ZeroMem (&SrcAddress
, sizeof (EFI_IPv6_ADDRESS
));
1316 ZeroMem (&DstAddress
, sizeof (EFI_IPv6_ADDRESS
));
1319 // Parse the parameter of source ip address.
1321 ValueStr
= ShellCommandLineGetValue (ParamPackage
, L
"-s");
1322 ValueStrPtr
= ValueStr
;
1323 if (ValueStr
!= NULL
) {
1324 mIp6SrcString
= ValueStr
;
1325 Status
= NetLibStrToIp6 (ValueStrPtr
, &SrcAddress
);
1326 if (EFI_ERROR (Status
)) {
1327 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_INVALID_IP
), gShellNetwork2HiiHandle
, ValueStr
);
1328 ShellStatus
= SHELL_INVALID_PARAMETER
;
1333 // Parse the parameter of destination ip address.
1335 NonOptionCount
= ShellCommandLineGetCount(ParamPackage
);
1336 ValueStr
= ShellCommandLineGetRawValue (ParamPackage
, (UINT32
)(NonOptionCount
-1));
1337 if (NonOptionCount
!= 2) {
1338 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_INVALID_INPUT
), gShellNetwork2HiiHandle
);
1339 ShellStatus
= SHELL_INVALID_PARAMETER
;
1342 ValueStrPtr
= ValueStr
;
1343 if (ValueStr
!= NULL
) {
1344 mIp6DstString
= ValueStr
;
1345 Status
= NetLibStrToIp6 (ValueStrPtr
, &DstAddress
);
1346 if (EFI_ERROR (Status
)) {
1347 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_INVALID_IP
), gShellNetwork2HiiHandle
, ValueStr
);
1348 ShellStatus
= SHELL_INVALID_PARAMETER
;
1354 // Enter into ping6 process.
1356 ShellStatus
= ShellPing6 (
1365 ShellCommandLineFreeVarList (ParamPackage
);