2 The implementation for Ping6 application.
4 Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR>
6 SPDX-License-Identifier: BSD-2-Clause-Patent
10 #include "UefiShellNetwork2CommandsLib.h"
12 #define PING6_DEFAULT_TIMEOUT 5000
13 #define PING6_MAX_SEND_NUMBER 10000
14 #define PING6_MAX_BUFFER_SIZE 32768
15 #define PING6_ONE_SECOND 10000000
16 #define STALL_1_MILLI_SECOND 1000
20 typedef struct _ICMP6_ECHO_REQUEST_REPLY
{
28 } ICMP6_ECHO_REQUEST_REPLY
;
32 typedef struct _PING6_ICMP6_TX_INFO
{
36 EFI_IP6_COMPLETION_TOKEN
*Token
;
37 } PING6_ICMP6_TX_INFO
;
39 typedef struct _PING6_PRIVATE_DATA
{
40 EFI_HANDLE ImageHandle
;
42 EFI_HANDLE Ip6ChildHandle
;
43 EFI_IP6_PROTOCOL
*Ip6
;
52 EFI_IP6_COMPLETION_TOKEN RxToken
;
60 EFI_IPv6_ADDRESS SrcAddress
;
61 EFI_IPv6_ADDRESS DstAddress
;
66 SHELL_PARAM_ITEM Ping6ParamList
[] = {
90 // Global Variables in Ping6 application.
92 CONST CHAR16
*mIp6DstString
;
93 CONST CHAR16
*mIp6SrcString
;
94 EFI_CPU_ARCH_PROTOCOL
*Cpu
= NULL
;
97 RTT timer tick routine.
99 @param[in] Event A EFI_EVENT type event.
100 @param[in] Context The pointer to Context.
105 Ping6RttTimerTickRoutine (
110 UINT32
*RttTimerTick
;
112 RttTimerTick
= (UINT32
*)Context
;
117 Get the timer period of the system.
119 This function tries to get the system timer period by creating
122 @return System timer period in MS, or 0 if operation failed.
126 Ping6GetTimerPeriod (
132 EFI_EVENT TimerEvent
;
139 Status
= gBS
->CreateEvent (
140 EVT_TIMER
| EVT_NOTIFY_SIGNAL
,
142 Ping6RttTimerTickRoutine
,
146 if (EFI_ERROR (Status
)) {
150 OldTpl
= gBS
->RaiseTPL (TPL_CALLBACK
);
151 Status
= gBS
->SetTimer (
156 if (EFI_ERROR (Status
)) {
157 gBS
->CloseEvent (TimerEvent
);
161 while (RttTimerTick
< 10) {
162 gBS
->Stall (STALL_1_MILLI_SECOND
);
166 gBS
->RestoreTPL (OldTpl
);
168 gBS
->SetTimer (TimerEvent
, TimerCancel
, 0);
169 gBS
->CloseEvent (TimerEvent
);
171 return StallCounter
/ RttTimerTick
;
175 Initialize the timer event for RTT (round trip time).
177 @param[in] Private The pointer to PING6_PRIVATE_DATA.
179 @retval EFI_SUCCESS RTT timer is started.
180 @retval Others Failed to start the RTT timer.
185 IN PING6_PRIVATE_DATA
*Private
190 Private
->TimerPeriod
= Ping6GetTimerPeriod ();
191 if (Private
->TimerPeriod
== 0) {
195 Private
->RttTimerTick
= 0;
196 Status
= gBS
->CreateEvent (
197 EVT_TIMER
| EVT_NOTIFY_SIGNAL
,
199 Ping6RttTimerTickRoutine
,
200 &Private
->RttTimerTick
,
203 if (EFI_ERROR (Status
)) {
207 Status
= gBS
->SetTimer (
212 if (EFI_ERROR (Status
)) {
213 gBS
->CloseEvent (Private
->RttTimer
);
221 Free RTT timer event resource.
223 @param[in] Private The pointer to PING6_PRIVATE_DATA.
228 IN PING6_PRIVATE_DATA
*Private
231 if (Private
->RttTimer
!= NULL
) {
232 gBS
->SetTimer (Private
->RttTimer
, TimerCancel
, 0);
233 gBS
->CloseEvent (Private
->RttTimer
);
238 Read the current time.
240 @param[in] Private The pointer to PING6_PRIVATE_DATA.
242 @retval the current tick value.
246 IN PING6_PRIVATE_DATA
*Private
249 return Private
->RttTimerTick
;
253 Get and calculate the duration in ms.
255 @param[in] Private The pointer to PING6_PRIVATE_DATA.
256 @param[in] Begin The start point of time.
257 @param[in] End The end point of time.
259 @return The duration in ms.
264 IN PING6_PRIVATE_DATA
*Private
,
273 return (End
- Begin
) * Private
->TimerPeriod
;
277 Destroy IPING6_ICMP6_TX_INFO, and recollect the memory.
279 @param[in] TxInfo The pointer to PING6_ICMP6_TX_INFO.
284 IN PING6_ICMP6_TX_INFO
*TxInfo
287 EFI_IP6_TRANSMIT_DATA
*TxData
;
288 EFI_IP6_FRAGMENT_DATA
*FragData
;
291 ASSERT (TxInfo
!= NULL
);
293 if (TxInfo
->Token
!= NULL
) {
294 if (TxInfo
->Token
->Event
!= NULL
) {
295 gBS
->CloseEvent (TxInfo
->Token
->Event
);
298 TxData
= TxInfo
->Token
->Packet
.TxData
;
299 if (TxData
!= NULL
) {
300 if (TxData
->OverrideData
!= NULL
) {
301 FreePool (TxData
->OverrideData
);
304 if (TxData
->ExtHdrs
!= NULL
) {
305 FreePool (TxData
->ExtHdrs
);
308 for (Index
= 0; Index
< TxData
->FragmentCount
; Index
++) {
309 FragData
= TxData
->FragmentTable
[Index
].FragmentBuffer
;
310 if (FragData
!= NULL
) {
316 FreePool (TxInfo
->Token
);
323 Match the request, and reply with SequenceNum/TimeStamp.
325 @param[in] Private The pointer to PING6_PRIVATE_DATA.
326 @param[in] Packet The pointer to ICMP6_ECHO_REQUEST_REPLY.
328 @retval EFI_SUCCESS The match is successful.
329 @retval EFI_NOT_FOUND The reply can't be matched with any request.
333 Ping6OnMatchEchoReply (
334 IN PING6_PRIVATE_DATA
*Private
,
335 IN ICMP6_ECHO_REQUEST_REPLY
*Packet
338 PING6_ICMP6_TX_INFO
*TxInfo
;
340 LIST_ENTRY
*NextEntry
;
342 NET_LIST_FOR_EACH_SAFE (Entry
, NextEntry
, &Private
->TxList
) {
343 TxInfo
= BASE_CR (Entry
, PING6_ICMP6_TX_INFO
, Link
);
345 if ((TxInfo
->SequenceNum
== Packet
->SequenceNum
) && (TxInfo
->TimeStamp
== Packet
->TimeStamp
)) {
347 RemoveEntryList (&TxInfo
->Link
);
348 Ping6DestroyTxInfo (TxInfo
);
353 return EFI_NOT_FOUND
;
357 The original intention is to send a request.
358 Currently, the application retransmits an icmp6 echo request packet
359 per second in sendnumber times that is specified by the user.
360 Because nothing can be done here, all things move to the timer rountine.
362 @param[in] Event A EFI_EVENT type event.
363 @param[in] Context The pointer to Context.
368 Ping6OnEchoRequestSent6 (
376 receive reply, match and print reply infomation.
378 @param[in] Event A EFI_EVENT type event.
379 @param[in] Context The pointer to context.
384 Ping6OnEchoReplyReceived6 (
390 PING6_PRIVATE_DATA
*Private
;
391 EFI_IP6_COMPLETION_TOKEN
*RxToken
;
392 EFI_IP6_RECEIVE_DATA
*RxData
;
393 ICMP6_ECHO_REQUEST_REPLY
*Reply
;
397 Private
= (PING6_PRIVATE_DATA
*)Context
;
399 if (Private
->Status
== EFI_ABORTED
) {
403 RxToken
= &Private
->RxToken
;
404 RxData
= RxToken
->Packet
.RxData
;
405 Reply
= RxData
->FragmentTable
[0].FragmentBuffer
;
406 PayLoad
= RxData
->DataLength
;
408 if (RxData
->Header
->NextHeader
!= IP6_ICMP
) {
412 if (!IP6_IS_MULTICAST (&Private
->DstAddress
) &&
413 !EFI_IP6_EQUAL (&RxData
->Header
->SourceAddress
, &Private
->DstAddress
))
418 if ((Reply
->Type
!= ICMP_V6_ECHO_REPLY
) || (Reply
->Code
!= 0)) {
422 if (PayLoad
!= Private
->BufferSize
) {
427 // Check whether the reply matches the sent request before.
429 Status
= Ping6OnMatchEchoReply (Private
, Reply
);
430 if (EFI_ERROR (Status
)) {
435 // Display statistics on this icmp6 echo reply packet.
437 Rtt
= Ping6CalculateTick (Private
, Reply
->TimeStamp
, Ping6ReadTime (Private
));
439 Private
->RttSum
+= Rtt
;
440 Private
->RttMin
= Private
->RttMin
> Rtt
? Rtt
: Private
->RttMin
;
441 Private
->RttMax
= Private
->RttMax
< Rtt
? Rtt
: Private
->RttMax
;
447 STRING_TOKEN (STR_PING6_REPLY_INFO
),
448 gShellNetwork2HiiHandle
,
452 RxData
->Header
->HopLimit
,
454 Rtt
+ Private
->TimerPeriod
459 if (Private
->RxCount
< Private
->SendNum
) {
461 // Continue to receive icmp6 echo reply packets.
463 RxToken
->Status
= EFI_ABORTED
;
465 Status
= Private
->Ip6
->Receive (Private
->Ip6
, RxToken
);
467 if (EFI_ERROR (Status
)) {
468 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_IP6_RECEIVE
), gShellNetwork2HiiHandle
, Status
);
469 Private
->Status
= EFI_ABORTED
;
473 // All reply have already been received from the dest host.
475 Private
->Status
= EFI_SUCCESS
;
479 // Singal to recycle the each rxdata here, not at the end of process.
481 gBS
->SignalEvent (RxData
->RecycleSignal
);
485 Initial EFI_IP6_COMPLETION_TOKEN.
487 @param[in] Private The pointer of PING6_PRIVATE_DATA.
488 @param[in] TimeStamp The TimeStamp of request.
489 @param[in] SequenceNum The SequenceNum of request.
491 @return The pointer of EFI_IP6_COMPLETION_TOKEN.
494 EFI_IP6_COMPLETION_TOKEN
*
496 IN PING6_PRIVATE_DATA
*Private
,
498 IN UINT16 SequenceNum
502 EFI_IP6_COMPLETION_TOKEN
*Token
;
503 EFI_IP6_TRANSMIT_DATA
*TxData
;
504 ICMP6_ECHO_REQUEST_REPLY
*Request
;
506 Request
= AllocateZeroPool (Private
->BufferSize
);
508 if (Request
== NULL
) {
513 // Assembly icmp6 echo request packet.
515 Request
->Type
= ICMP_V6_ECHO_REQUEST
;
517 Request
->SequenceNum
= SequenceNum
;
518 Request
->TimeStamp
= TimeStamp
;
519 Request
->Identifier
= 0;
521 // Leave check sum to ip6 layer, since it has no idea of source address
524 Request
->Checksum
= 0;
526 TxData
= AllocateZeroPool (sizeof (EFI_IP6_TRANSMIT_DATA
));
528 if (TxData
== NULL
) {
534 // Assembly ipv6 token for transmit.
536 TxData
->OverrideData
= 0;
537 TxData
->ExtHdrsLength
= 0;
538 TxData
->ExtHdrs
= NULL
;
539 TxData
->DataLength
= Private
->BufferSize
;
540 TxData
->FragmentCount
= 1;
541 TxData
->FragmentTable
[0].FragmentBuffer
= (VOID
*)Request
;
542 TxData
->FragmentTable
[0].FragmentLength
= Private
->BufferSize
;
544 Token
= AllocateZeroPool (sizeof (EFI_IP6_COMPLETION_TOKEN
));
552 Token
->Status
= EFI_ABORTED
;
553 Token
->Packet
.TxData
= TxData
;
555 Status
= gBS
->CreateEvent (
558 Ping6OnEchoRequestSent6
,
563 if (EFI_ERROR (Status
)) {
574 Transmit the EFI_IP6_COMPLETION_TOKEN.
576 @param[in] Private The pointer of PING6_PRIVATE_DATA.
578 @retval EFI_SUCCESS Transmitted successfully.
579 @retval EFI_OUT_OF_RESOURCES No memory is available on the platform.
580 @retval others Transmitted unsuccessfully.
584 Ping6SendEchoRequest (
585 IN PING6_PRIVATE_DATA
*Private
589 PING6_ICMP6_TX_INFO
*TxInfo
;
591 TxInfo
= AllocateZeroPool (sizeof (PING6_ICMP6_TX_INFO
));
593 if (TxInfo
== NULL
) {
594 return EFI_OUT_OF_RESOURCES
;
597 TxInfo
->TimeStamp
= Ping6ReadTime (Private
);
598 TxInfo
->SequenceNum
= (UINT16
)(Private
->TxCount
+ 1);
600 TxInfo
->Token
= Ping6GenerateToken (
606 if (TxInfo
->Token
== NULL
) {
607 Ping6DestroyTxInfo (TxInfo
);
608 return EFI_OUT_OF_RESOURCES
;
611 Status
= Private
->Ip6
->Transmit (Private
->Ip6
, TxInfo
->Token
);
613 if (EFI_ERROR (Status
)) {
614 Ping6DestroyTxInfo (TxInfo
);
618 InsertTailList (&Private
->TxList
, &TxInfo
->Link
);
625 Place a completion token into the receive packet queue to receive the echo reply.
627 @param[in] Private The pointer of PING6_PRIVATE_DATA.
629 @retval EFI_SUCCESS Put the token into the receive packet queue successfully.
630 @retval others Put the token into the receive packet queue unsuccessfully.
634 Ping6OnReceiveEchoReply (
635 IN PING6_PRIVATE_DATA
*Private
640 ZeroMem (&Private
->RxToken
, sizeof (EFI_IP6_COMPLETION_TOKEN
));
642 Status
= gBS
->CreateEvent (
645 Ping6OnEchoReplyReceived6
,
647 &Private
->RxToken
.Event
650 if (EFI_ERROR (Status
)) {
654 Private
->RxToken
.Status
= EFI_NOT_READY
;
656 Status
= Private
->Ip6
->Receive (Private
->Ip6
, &Private
->RxToken
);
657 if (EFI_ERROR (Status
)) {
658 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_IP6_RECEIVE
), gShellNetwork2HiiHandle
, Status
);
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
) {
691 Status
= Ping6SendEchoRequest (Private
);
692 if (Private
->TxCount
!= 0) {
693 if (EFI_ERROR (Status
)) {
694 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
) {
710 if (EFI_ERROR (TxInfo
->Token
->Status
)) {
711 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 EFI_STATUS MediaStatus
;
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
;
764 MediaStatus
= EFI_SUCCESS
;
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
++) {
807 if (UnspecifiedSrc
) {
811 NetLibDetectMediaWaitTimeout (HandleBuffer
[HandleIndex
], 0, &MediaStatus
);
812 if (MediaStatus
!= EFI_SUCCESS
) {
820 Status
= gBS
->HandleProtocol (
821 HandleBuffer
[HandleIndex
],
822 &gEfiIp6ServiceBindingProtocolGuid
,
825 if (EFI_ERROR (Status
)) {
830 // Ip6config protocol and ip6 service binding protocol are installed
831 // on the same handle.
833 Status
= gBS
->HandleProtocol (
834 HandleBuffer
[HandleIndex
],
835 &gEfiIp6ConfigProtocolGuid
,
839 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
;
866 // Get the interface info.
868 Status
= Ip6Cfg
->GetData (
870 Ip6ConfigDataTypeInterfaceInfo
,
875 if (EFI_ERROR (Status
)) {
876 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_IP6CFG_GETDATA
), gShellNetwork2HiiHandle
, Status
);
881 // Check whether the source address is one of the interface addresses.
883 for (AddrIndex
= 0; AddrIndex
< IfInfo
->AddressInfoCount
; AddrIndex
++) {
884 Addr
= &(IfInfo
->AddressInfo
[AddrIndex
].Address
);
886 if (UnspecifiedSrc
) {
887 if (!NetIp6IsUnspecifiedAddr (Addr
) && !NetIp6IsLinkLocalAddr (Addr
)) {
889 // Select the interface automatically.
891 CopyMem (&Private
->SrcAddress
, Addr
, sizeof (Private
->SrcAddress
));
894 } else if (EFI_IP6_EQUAL (&Private
->SrcAddress
, Addr
)) {
896 // Match a certain interface address.
902 if (AddrIndex
< IfInfo
->AddressInfoCount
) {
904 // Found a nic handle with right interface address.
914 // No exact interface address matched.
917 if (HandleIndex
== HandleNum
) {
918 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_CONFIGD_NIC_NF
), gShellNetwork2HiiHandle
);
919 Status
= EFI_NOT_FOUND
;
923 Private
->NicHandle
= HandleBuffer
[HandleIndex
];
925 ASSERT (Ip6Sb
!= NULL
);
926 Status
= Ip6Sb
->CreateChild (Ip6Sb
, &Private
->Ip6ChildHandle
);
928 if (EFI_ERROR (Status
)) {
932 Status
= gBS
->OpenProtocol (
933 Private
->Ip6ChildHandle
,
934 &gEfiIp6ProtocolGuid
,
935 (VOID
**)&Private
->Ip6
,
936 Private
->ImageHandle
,
937 Private
->Ip6ChildHandle
,
938 EFI_OPEN_PROTOCOL_GET_PROTOCOL
940 if (EFI_ERROR (Status
)) {
944 ZeroMem (&Ip6Config
, sizeof (EFI_IP6_CONFIG_DATA
));
947 // Configure the ip6 instance for icmp6 packet exchange.
949 Ip6Config
.DefaultProtocol
= 58;
950 Ip6Config
.AcceptAnyProtocol
= FALSE
;
951 Ip6Config
.AcceptIcmpErrors
= TRUE
;
952 Ip6Config
.AcceptPromiscuous
= FALSE
;
953 Ip6Config
.TrafficClass
= 0;
954 Ip6Config
.HopLimit
= 128;
955 Ip6Config
.FlowLabel
= 0;
956 Ip6Config
.ReceiveTimeout
= 0;
957 Ip6Config
.TransmitTimeout
= 0;
959 IP6_COPY_ADDRESS (&Ip6Config
.StationAddress
, &Private
->SrcAddress
);
961 IP6_COPY_ADDRESS (&Ip6Config
.DestinationAddress
, &Private
->DstAddress
);
963 Status
= Private
->Ip6
->Configure (Private
->Ip6
, &Ip6Config
);
965 if (EFI_ERROR (Status
)) {
966 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_IP6_CONFIG
), gShellNetwork2HiiHandle
, Status
);
973 if (HandleBuffer
!= NULL
) {
974 FreePool (HandleBuffer
);
977 if (IfInfo
!= NULL
) {
981 if ((Ip6Sb
!= NULL
) && (Private
->Ip6ChildHandle
!= NULL
)) {
982 Ip6Sb
->DestroyChild (Ip6Sb
, Private
->Ip6ChildHandle
);
989 Destroy the IP6 instance.
991 @param[in] Private The pointer of PING6_PRIVATE_DATA.
995 Ping6DestroyIpInstance (
996 IN PING6_PRIVATE_DATA
*Private
1000 EFI_SERVICE_BINDING_PROTOCOL
*Ip6Sb
;
1002 gBS
->CloseProtocol (
1003 Private
->Ip6ChildHandle
,
1004 &gEfiIp6ProtocolGuid
,
1005 Private
->ImageHandle
,
1006 Private
->Ip6ChildHandle
1009 Status
= gBS
->HandleProtocol (
1011 &gEfiIp6ServiceBindingProtocolGuid
,
1015 if (!EFI_ERROR (Status
)) {
1016 Ip6Sb
->DestroyChild (Ip6Sb
, Private
->Ip6ChildHandle
);
1023 @param[in] ImageHandle The firmware allocated handle for the UEFI image.
1024 @param[in] SendNumber The send request count.
1025 @param[in] BufferSize The send buffer size.
1026 @param[in] SrcAddress The source IPv6 address.
1027 @param[in] DstAddress The destination IPv6 address.
1029 @retval SHELL_SUCCESS The ping6 processed successfullly.
1030 @retval others The ping6 processed unsuccessfully.
1035 IN EFI_HANDLE ImageHandle
,
1036 IN UINT32 SendNumber
,
1037 IN UINT32 BufferSize
,
1038 IN EFI_IPv6_ADDRESS
*SrcAddress
,
1039 IN EFI_IPv6_ADDRESS
*DstAddress
1044 PING6_PRIVATE_DATA
*Private
;
1045 PING6_ICMP6_TX_INFO
*TxInfo
;
1047 LIST_ENTRY
*NextEntry
;
1048 SHELL_STATUS ShellStatus
;
1050 ShellStatus
= SHELL_SUCCESS
;
1051 Private
= AllocateZeroPool (sizeof (PING6_PRIVATE_DATA
));
1053 if (Private
== NULL
) {
1054 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_GEN_OUT_MEM
), gShellNetwork2HiiHandle
, L
"Ping6");
1055 ShellStatus
= SHELL_OUT_OF_RESOURCES
;
1059 Private
->ImageHandle
= ImageHandle
;
1060 Private
->SendNum
= SendNumber
;
1061 Private
->BufferSize
= BufferSize
;
1062 Private
->RttMin
= ~((UINT64
)(0x0));
1063 Private
->Status
= EFI_NOT_READY
;
1065 InitializeListHead (&Private
->TxList
);
1067 IP6_COPY_ADDRESS (&Private
->SrcAddress
, SrcAddress
);
1068 IP6_COPY_ADDRESS (&Private
->DstAddress
, DstAddress
);
1071 // Open and configure a ip6 instance for ping6.
1073 Status
= Ping6CreateIpInstance (Private
);
1075 if (EFI_ERROR (Status
)) {
1076 ShellStatus
= SHELL_ACCESS_DENIED
;
1081 // Print the command line itself.
1083 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_START
), gShellNetwork2HiiHandle
, mIp6DstString
, Private
->BufferSize
);
1085 // Create a ipv6 token to receive the first icmp6 echo reply packet.
1087 Status
= Ping6OnReceiveEchoReply (Private
);
1089 if (EFI_ERROR (Status
)) {
1090 ShellStatus
= SHELL_ACCESS_DENIED
;
1095 // Create and start timer to send icmp6 echo request packet per second.
1097 Status
= gBS
->CreateEvent (
1098 EVT_TIMER
| EVT_NOTIFY_SIGNAL
,
1100 Ping6OnTimerRoutine6
,
1105 if (EFI_ERROR (Status
)) {
1106 ShellStatus
= SHELL_ACCESS_DENIED
;
1111 // Start a timer to calculate the RTT.
1113 Status
= Ping6InitRttTimer (Private
);
1114 if (EFI_ERROR (Status
)) {
1115 ShellStatus
= SHELL_ACCESS_DENIED
;
1120 // Create a ipv6 token to send the first icmp6 echo request packet.
1122 Status
= Ping6SendEchoRequest (Private
);
1124 // EFI_NOT_READY for IPsec is enable and IKE is not established.
1126 if (EFI_ERROR (Status
) && (Status
!= EFI_NOT_READY
)) {
1127 ShellStatus
= SHELL_ACCESS_DENIED
;
1128 if (Status
== EFI_NOT_FOUND
) {
1129 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_NOSOURCE_INDOMAIN
), gShellNetwork2HiiHandle
, mIp6DstString
);
1135 Status
= gBS
->SetTimer (
1141 if (EFI_ERROR (Status
)) {
1142 ShellStatus
= SHELL_ACCESS_DENIED
;
1147 // Control the ping6 process by two factors:
1149 // 2. Private->Status
1150 // 2.1. success means all icmp6 echo request packets get reply packets.
1151 // 2.2. timeout means the last icmp6 echo reply request timeout to get reply.
1152 // 2.3. noready means ping6 process is on-the-go.
1154 while (Private
->Status
== EFI_NOT_READY
) {
1155 Private
->Ip6
->Poll (Private
->Ip6
);
1158 // Terminate the ping6 process by 'esc' or 'ctl-c'.
1160 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
);
1162 if (!EFI_ERROR (Status
)) {
1163 if ((Key
.UnicodeChar
== 0x1b) || (Key
.UnicodeChar
== 0x03) ||
1164 ((Key
.UnicodeChar
== 0) && (Key
.ScanCode
== SCAN_ESC
)))
1173 // Display the statistics in all.
1175 gBS
->SetTimer (Private
->Timer
, TimerCancel
, 0);
1177 if (Private
->TxCount
!= 0) {
1182 STRING_TOKEN (STR_PING6_STAT
),
1183 gShellNetwork2HiiHandle
,
1186 (100 * (Private
->TxCount
- Private
->RxCount
)) / Private
->TxCount
,
1191 if (Private
->RxCount
!= 0) {
1196 STRING_TOKEN (STR_PING6_RTT
),
1197 gShellNetwork2HiiHandle
,
1199 Private
->RttMin
+ Private
->TimerPeriod
,
1201 Private
->RttMax
+ Private
->TimerPeriod
,
1202 DivU64x64Remainder (Private
->RttSum
, Private
->RxCount
, NULL
),
1203 DivU64x64Remainder (Private
->RttSum
, Private
->RxCount
, NULL
) + Private
->TimerPeriod
1209 if (Private
!= NULL
) {
1210 Private
->Status
= EFI_ABORTED
;
1212 NET_LIST_FOR_EACH_SAFE (Entry
, NextEntry
, &Private
->TxList
) {
1213 TxInfo
= BASE_CR (Entry
, PING6_ICMP6_TX_INFO
, Link
);
1215 Status
= Private
->Ip6
->Cancel (Private
->Ip6
, TxInfo
->Token
);
1217 RemoveEntryList (&TxInfo
->Link
);
1218 Ping6DestroyTxInfo (TxInfo
);
1221 Ping6FreeRttTimer (Private
);
1223 if (Private
->Timer
!= NULL
) {
1224 gBS
->CloseEvent (Private
->Timer
);
1227 if (Private
->Ip6
!= NULL
) {
1228 Status
= Private
->Ip6
->Cancel (Private
->Ip6
, &Private
->RxToken
);
1231 if (Private
->RxToken
.Event
!= NULL
) {
1232 gBS
->CloseEvent (Private
->RxToken
.Event
);
1235 if (Private
->Ip6ChildHandle
!= NULL
) {
1236 Ping6DestroyIpInstance (Private
);
1246 Function for 'ping6' command.
1248 @param[in] ImageHandle Handle to the Image (NULL if Internal).
1249 @param[in] SystemTable Pointer to the System Table (NULL if Internal).
1251 @retval SHELL_SUCCESS The ping6 processed successfullly.
1252 @retval others The ping6 processed unsuccessfully.
1257 ShellCommandRunPing6 (
1258 IN EFI_HANDLE ImageHandle
,
1259 IN EFI_SYSTEM_TABLE
*SystemTable
1263 SHELL_STATUS ShellStatus
;
1264 EFI_IPv6_ADDRESS DstAddress
;
1265 EFI_IPv6_ADDRESS SrcAddress
;
1268 LIST_ENTRY
*ParamPackage
;
1269 CONST CHAR16
*ValueStr
;
1270 CONST CHAR16
*ValueStrPtr
;
1271 UINTN NonOptionCount
;
1272 CHAR16
*ProblemParam
;
1274 ProblemParam
= NULL
;
1275 ShellStatus
= SHELL_SUCCESS
;
1277 Status
= ShellCommandLineParseEx (Ping6ParamList
, &ParamPackage
, &ProblemParam
, TRUE
, FALSE
);
1278 if (EFI_ERROR (Status
)) {
1279 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_INVALID_INPUT
), gShellNetwork2HiiHandle
);
1280 ShellStatus
= SHELL_INVALID_PARAMETER
;
1288 // Parse the parameter of count number.
1290 ValueStr
= ShellCommandLineGetValue (ParamPackage
, L
"-n");
1291 ValueStrPtr
= ValueStr
;
1292 if (ValueStr
!= NULL
) {
1293 SendNumber
= ShellStrToUintn (ValueStrPtr
);
1296 // ShellStrToUintn will return 0 when input is 0 or an invalid input string.
1298 if ((SendNumber
== 0) || (SendNumber
> PING6_MAX_SEND_NUMBER
)) {
1299 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_INVALID_SEND_NUMBER
), gShellNetwork2HiiHandle
, ValueStr
);
1300 ShellStatus
= SHELL_INVALID_PARAMETER
;
1306 // Parse the parameter of buffer size.
1308 ValueStr
= ShellCommandLineGetValue (ParamPackage
, L
"-l");
1309 ValueStrPtr
= ValueStr
;
1310 if (ValueStr
!= NULL
) {
1311 BufferSize
= ShellStrToUintn (ValueStrPtr
);
1314 // ShellStrToUintn will return 0 when input is 0 or an invalid input string.
1316 if ((BufferSize
< 16) || (BufferSize
> PING6_MAX_BUFFER_SIZE
)) {
1317 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_INVALID_BUFFER_SIZE
), gShellNetwork2HiiHandle
, ValueStr
);
1318 ShellStatus
= SHELL_INVALID_PARAMETER
;
1323 ZeroMem (&SrcAddress
, sizeof (EFI_IPv6_ADDRESS
));
1324 ZeroMem (&DstAddress
, sizeof (EFI_IPv6_ADDRESS
));
1327 // Parse the parameter of source ip address.
1329 ValueStr
= ShellCommandLineGetValue (ParamPackage
, L
"-s");
1330 ValueStrPtr
= ValueStr
;
1331 if (ValueStr
!= NULL
) {
1332 mIp6SrcString
= ValueStr
;
1333 Status
= NetLibStrToIp6 (ValueStrPtr
, &SrcAddress
);
1334 if (EFI_ERROR (Status
)) {
1335 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_INVALID_IP
), gShellNetwork2HiiHandle
, ValueStr
);
1336 ShellStatus
= SHELL_INVALID_PARAMETER
;
1342 // Parse the parameter of destination ip address.
1344 NonOptionCount
= ShellCommandLineGetCount (ParamPackage
);
1345 ValueStr
= ShellCommandLineGetRawValue (ParamPackage
, (UINT32
)(NonOptionCount
-1));
1346 if (NonOptionCount
!= 2) {
1347 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_INVALID_INPUT
), gShellNetwork2HiiHandle
);
1348 ShellStatus
= SHELL_INVALID_PARAMETER
;
1352 ValueStrPtr
= ValueStr
;
1353 if (ValueStr
!= NULL
) {
1354 mIp6DstString
= ValueStr
;
1355 Status
= NetLibStrToIp6 (ValueStrPtr
, &DstAddress
);
1356 if (EFI_ERROR (Status
)) {
1357 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_INVALID_IP
), gShellNetwork2HiiHandle
, ValueStr
);
1358 ShellStatus
= SHELL_INVALID_PARAMETER
;
1364 // Enter into ping6 process.
1366 ShellStatus
= ShellPing6 (
1375 ShellCommandLineFreeVarList (ParamPackage
);