2 The implementation for Ping6 application.
4 Copyright (c) 2016 - 2017, 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 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_IP6_RECEIVE
), gShellNetwork2HiiHandle
, Status
);
478 Private
->Status
= EFI_ABORTED
;
482 // All reply have already been received from the dest host.
484 Private
->Status
= EFI_SUCCESS
;
487 // Singal to recycle the each rxdata here, not at the end of process.
489 gBS
->SignalEvent (RxData
->RecycleSignal
);
493 Initial EFI_IP6_COMPLETION_TOKEN.
495 @param[in] Private The pointer of PING6_PRIVATE_DATA.
496 @param[in] TimeStamp The TimeStamp of request.
497 @param[in] SequenceNum The SequenceNum of request.
499 @return The pointer of EFI_IP6_COMPLETION_TOKEN.
502 EFI_IP6_COMPLETION_TOKEN
*
504 IN PING6_PRIVATE_DATA
*Private
,
506 IN UINT16 SequenceNum
510 EFI_IP6_COMPLETION_TOKEN
*Token
;
511 EFI_IP6_TRANSMIT_DATA
*TxData
;
512 ICMP6_ECHO_REQUEST_REPLY
*Request
;
514 Request
= AllocateZeroPool (Private
->BufferSize
);
516 if (Request
== NULL
) {
520 // Assembly icmp6 echo request packet.
522 Request
->Type
= ICMP_V6_ECHO_REQUEST
;
524 Request
->SequenceNum
= SequenceNum
;
525 Request
->TimeStamp
= TimeStamp
;
526 Request
->Identifier
= 0;
528 // Leave check sum to ip6 layer, since it has no idea of source address
531 Request
->Checksum
= 0;
533 TxData
= AllocateZeroPool (sizeof (EFI_IP6_TRANSMIT_DATA
));
535 if (TxData
== NULL
) {
540 // Assembly ipv6 token for transmit.
542 TxData
->OverrideData
= 0;
543 TxData
->ExtHdrsLength
= 0;
544 TxData
->ExtHdrs
= NULL
;
545 TxData
->DataLength
= Private
->BufferSize
;
546 TxData
->FragmentCount
= 1;
547 TxData
->FragmentTable
[0].FragmentBuffer
= (VOID
*) Request
;
548 TxData
->FragmentTable
[0].FragmentLength
= Private
->BufferSize
;
550 Token
= AllocateZeroPool (sizeof (EFI_IP6_COMPLETION_TOKEN
));
558 Token
->Status
= EFI_ABORTED
;
559 Token
->Packet
.TxData
= TxData
;
561 Status
= gBS
->CreateEvent (
564 Ping6OnEchoRequestSent6
,
569 if (EFI_ERROR (Status
)) {
580 Transmit the EFI_IP6_COMPLETION_TOKEN.
582 @param[in] Private The pointer of PING6_PRIVATE_DATA.
584 @retval EFI_SUCCESS Transmitted successfully.
585 @retval EFI_OUT_OF_RESOURCES No memory is available on the platform.
586 @retval others Transmitted unsuccessfully.
590 Ping6SendEchoRequest (
591 IN PING6_PRIVATE_DATA
*Private
595 PING6_ICMP6_TX_INFO
*TxInfo
;
597 TxInfo
= AllocateZeroPool (sizeof (PING6_ICMP6_TX_INFO
));
599 if (TxInfo
== NULL
) {
600 return EFI_OUT_OF_RESOURCES
;
603 TxInfo
->TimeStamp
= Ping6ReadTime (Private
);
604 TxInfo
->SequenceNum
= (UINT16
) (Private
->TxCount
+ 1);
606 TxInfo
->Token
= Ping6GenerateToken (
612 if (TxInfo
->Token
== NULL
) {
613 Ping6DestroyTxInfo (TxInfo
);
614 return EFI_OUT_OF_RESOURCES
;
617 Status
= Private
->Ip6
->Transmit (Private
->Ip6
, TxInfo
->Token
);
619 if (EFI_ERROR (Status
)) {
620 Ping6DestroyTxInfo (TxInfo
);
624 InsertTailList (&Private
->TxList
, &TxInfo
->Link
);
631 Place a completion token into the receive packet queue to receive the echo reply.
633 @param[in] Private The pointer of PING6_PRIVATE_DATA.
635 @retval EFI_SUCCESS Put the token into the receive packet queue successfully.
636 @retval others Put the token into the receive packet queue unsuccessfully.
640 Ping6OnReceiveEchoReply (
641 IN PING6_PRIVATE_DATA
*Private
646 ZeroMem (&Private
->RxToken
, sizeof (EFI_IP6_COMPLETION_TOKEN
));
648 Status
= gBS
->CreateEvent (
651 Ping6OnEchoReplyReceived6
,
653 &Private
->RxToken
.Event
656 if (EFI_ERROR (Status
)) {
660 Private
->RxToken
.Status
= EFI_NOT_READY
;
662 Status
= Private
->Ip6
->Receive (Private
->Ip6
, &Private
->RxToken
);
663 if (EFI_ERROR (Status
)) {
664 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_IP6_RECEIVE
), gShellNetwork2HiiHandle
, Status
);
670 Remove the timeout request from the list.
672 @param[in] Event A EFI_EVENT type event.
673 @param[in] Context The pointer to Context.
678 Ping6OnTimerRoutine6 (
684 PING6_PRIVATE_DATA
*Private
;
685 PING6_ICMP6_TX_INFO
*TxInfo
;
687 LIST_ENTRY
*NextEntry
;
690 Private
= (PING6_PRIVATE_DATA
*) Context
;
693 // Retransmit icmp6 echo request packets per second in sendnumber times.
695 if (Private
->TxCount
< Private
->SendNum
) {
697 Status
= Ping6SendEchoRequest (Private
);
698 if (Private
->TxCount
!= 0){
699 if (EFI_ERROR (Status
)) {
700 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_SEND_REQUEST
), gShellNetwork2HiiHandle
, Private
->TxCount
+ 1);
705 // Check whether any icmp6 echo request in the list timeout.
707 NET_LIST_FOR_EACH_SAFE (Entry
, NextEntry
, &Private
->TxList
) {
708 TxInfo
= BASE_CR (Entry
, PING6_ICMP6_TX_INFO
, Link
);
709 Time
= Ping6CalculateTick (Private
, TxInfo
->TimeStamp
, Ping6ReadTime (Private
));
712 // Remove the timeout echo request from txlist.
714 if (Time
> PING6_DEFAULT_TIMEOUT
) {
716 if (EFI_ERROR (TxInfo
->Token
->Status
)) {
717 Private
->Ip6
->Cancel (Private
->Ip6
, TxInfo
->Token
);
720 // Remove the timeout icmp6 echo request from list.
722 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_TIMEOUT
), gShellNetwork2HiiHandle
, TxInfo
->SequenceNum
);
724 RemoveEntryList (&TxInfo
->Link
);
725 Ping6DestroyTxInfo (TxInfo
);
727 if (IsListEmpty (&Private
->TxList
) && (Private
->TxCount
== Private
->SendNum
)) {
729 // All the left icmp6 echo request in the list timeout.
731 Private
->Status
= EFI_TIMEOUT
;
738 Create a valid IP6 instance.
740 @param[in] Private The pointer of PING6_PRIVATE_DATA.
742 @retval EFI_SUCCESS Create a valid IP6 instance successfully.
743 @retval EFI_ABORTED Locate handle with ip6 service binding protocol unsuccessfully.
744 @retval EFI_INVALID_PARAMETER The source address is unspecified when the destination address is a link -ocal address.
745 @retval EFI_OUT_OF_RESOURCES No memory is available on the platform.
746 @retval EFI_NOT_FOUND The source address is not found.
749 Ping6CreateIpInstance (
750 IN PING6_PRIVATE_DATA
*Private
756 EFI_HANDLE
*HandleBuffer
;
757 BOOLEAN UnspecifiedSrc
;
758 EFI_STATUS MediaStatus
;
759 EFI_SERVICE_BINDING_PROTOCOL
*Ip6Sb
;
760 EFI_IP6_CONFIG_PROTOCOL
*Ip6Cfg
;
761 EFI_IP6_CONFIG_DATA Ip6Config
;
762 EFI_IP6_CONFIG_INTERFACE_INFO
*IfInfo
;
764 EFI_IPv6_ADDRESS
*Addr
;
768 UnspecifiedSrc
= FALSE
;
769 MediaStatus
= EFI_SUCCESS
775 // Locate all the handles with ip6 service binding protocol.
777 Status
= gBS
->LocateHandleBuffer (
779 &gEfiIp6ServiceBindingProtocolGuid
,
784 if (EFI_ERROR (Status
) || (HandleNum
== 0)) {
788 if (NetIp6IsUnspecifiedAddr (&Private
->SrcAddress
)) {
790 // SrcAddress is unspecified. So, both connected and configured interface will be automatic selected.
792 UnspecifiedSrc
= TRUE
;
796 // Source address is required when pinging a link-local address.
798 if (NetIp6IsLinkLocalAddr (&Private
->DstAddress
) && UnspecifiedSrc
) {
799 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_INVALID_SOURCE
), gShellNetwork2HiiHandle
);
800 Status
= EFI_INVALID_PARAMETER
;
805 // For each ip6 protocol, check interface addresses list.
807 for (HandleIndex
= 0; HandleIndex
< HandleNum
; HandleIndex
++) {
813 if (UnspecifiedSrc
) {
817 NetLibDetectMediaWaitTimeout (HandleBuffer
[HandleIndex
], 0, &MediaStatus
);
818 if (MediaStatus
!= EFI_SUCCESS
) {
826 Status
= gBS
->HandleProtocol (
827 HandleBuffer
[HandleIndex
],
828 &gEfiIp6ServiceBindingProtocolGuid
,
831 if (EFI_ERROR (Status
)) {
836 // Ip6config protocol and ip6 service binding protocol are installed
837 // on the same handle.
839 Status
= gBS
->HandleProtocol (
840 HandleBuffer
[HandleIndex
],
841 &gEfiIp6ConfigProtocolGuid
,
845 if (EFI_ERROR (Status
)) {
849 // Get the interface information size.
851 Status
= Ip6Cfg
->GetData (
853 Ip6ConfigDataTypeInterfaceInfo
,
858 if (Status
!= EFI_BUFFER_TOO_SMALL
) {
859 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_IP6CFG_GETDATA
), gShellNetwork2HiiHandle
, Status
);
863 IfInfo
= AllocateZeroPool (IfInfoSize
);
865 if (IfInfo
== NULL
) {
866 Status
= EFI_OUT_OF_RESOURCES
;
870 // Get the interface info.
872 Status
= Ip6Cfg
->GetData (
874 Ip6ConfigDataTypeInterfaceInfo
,
879 if (EFI_ERROR (Status
)) {
880 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_IP6CFG_GETDATA
), gShellNetwork2HiiHandle
, Status
);
884 // Check whether the source address is one of the interface addresses.
886 for (AddrIndex
= 0; AddrIndex
< IfInfo
->AddressInfoCount
; AddrIndex
++) {
887 Addr
= &(IfInfo
->AddressInfo
[AddrIndex
].Address
);
889 if (UnspecifiedSrc
) {
890 if (!NetIp6IsUnspecifiedAddr (Addr
) && !NetIp6IsLinkLocalAddr (Addr
)) {
892 // Select the interface automatically.
894 CopyMem(&Private
->SrcAddress
, Addr
, sizeof(Private
->SrcAddress
));
897 } else if (EFI_IP6_EQUAL (&Private
->SrcAddress
, Addr
)) {
899 // Match a certain interface address.
905 if (AddrIndex
< IfInfo
->AddressInfoCount
) {
907 // Found a nic handle with right interface address.
916 // No exact interface address matched.
919 if (HandleIndex
== HandleNum
) {
920 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_CONFIGD_NIC_NF
), gShellNetwork2HiiHandle
);
921 Status
= EFI_NOT_FOUND
;
925 Private
->NicHandle
= HandleBuffer
[HandleIndex
];
927 ASSERT (Ip6Sb
!= NULL
);
928 Status
= Ip6Sb
->CreateChild (Ip6Sb
, &Private
->Ip6ChildHandle
);
930 if (EFI_ERROR (Status
)) {
934 Status
= gBS
->OpenProtocol (
935 Private
->Ip6ChildHandle
,
936 &gEfiIp6ProtocolGuid
,
937 (VOID
**) &Private
->Ip6
,
938 Private
->ImageHandle
,
939 Private
->Ip6ChildHandle
,
940 EFI_OPEN_PROTOCOL_GET_PROTOCOL
942 if (EFI_ERROR (Status
)) {
946 ZeroMem (&Ip6Config
, sizeof (EFI_IP6_CONFIG_DATA
));
949 // Configure the ip6 instance for icmp6 packet exchange.
951 Ip6Config
.DefaultProtocol
= 58;
952 Ip6Config
.AcceptAnyProtocol
= FALSE
;
953 Ip6Config
.AcceptIcmpErrors
= TRUE
;
954 Ip6Config
.AcceptPromiscuous
= FALSE
;
955 Ip6Config
.TrafficClass
= 0;
956 Ip6Config
.HopLimit
= 128;
957 Ip6Config
.FlowLabel
= 0;
958 Ip6Config
.ReceiveTimeout
= 0;
959 Ip6Config
.TransmitTimeout
= 0;
961 IP6_COPY_ADDRESS (&Ip6Config
.StationAddress
, &Private
->SrcAddress
);
963 IP6_COPY_ADDRESS (&Ip6Config
.DestinationAddress
, &Private
->DstAddress
);
965 Status
= Private
->Ip6
->Configure (Private
->Ip6
, &Ip6Config
);
967 if (EFI_ERROR (Status
)) {
968 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_IP6_CONFIG
), gShellNetwork2HiiHandle
, Status
);
975 if (HandleBuffer
!= NULL
) {
976 FreePool (HandleBuffer
);
979 if (IfInfo
!= NULL
) {
983 if ((Ip6Sb
!= NULL
) && (Private
->Ip6ChildHandle
!= NULL
)) {
984 Ip6Sb
->DestroyChild (Ip6Sb
, Private
->Ip6ChildHandle
);
991 Destroy the IP6 instance.
993 @param[in] Private The pointer of PING6_PRIVATE_DATA.
997 Ping6DestroyIpInstance (
998 IN PING6_PRIVATE_DATA
*Private
1002 EFI_SERVICE_BINDING_PROTOCOL
*Ip6Sb
;
1004 gBS
->CloseProtocol (
1005 Private
->Ip6ChildHandle
,
1006 &gEfiIp6ProtocolGuid
,
1007 Private
->ImageHandle
,
1008 Private
->Ip6ChildHandle
1011 Status
= gBS
->HandleProtocol (
1013 &gEfiIp6ServiceBindingProtocolGuid
,
1017 if (!EFI_ERROR(Status
)) {
1018 Ip6Sb
->DestroyChild (Ip6Sb
, Private
->Ip6ChildHandle
);
1025 @param[in] ImageHandle The firmware allocated handle for the UEFI image.
1026 @param[in] SendNumber The send request count.
1027 @param[in] BufferSize The send buffer size.
1028 @param[in] SrcAddress The source IPv6 address.
1029 @param[in] DstAddress The destination IPv6 address.
1031 @retval SHELL_SUCCESS The ping6 processed successfullly.
1032 @retval others The ping6 processed unsuccessfully.
1037 IN EFI_HANDLE ImageHandle
,
1038 IN UINT32 SendNumber
,
1039 IN UINT32 BufferSize
,
1040 IN EFI_IPv6_ADDRESS
*SrcAddress
,
1041 IN EFI_IPv6_ADDRESS
*DstAddress
1046 PING6_PRIVATE_DATA
*Private
;
1047 PING6_ICMP6_TX_INFO
*TxInfo
;
1049 LIST_ENTRY
*NextEntry
;
1050 SHELL_STATUS ShellStatus
;
1052 ShellStatus
= SHELL_SUCCESS
;
1053 Private
= AllocateZeroPool (sizeof (PING6_PRIVATE_DATA
));
1055 if (Private
== NULL
) {
1056 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_GEN_OUT_MEM
), gShellNetwork2HiiHandle
, L
"Ping6");
1057 ShellStatus
= SHELL_OUT_OF_RESOURCES
;
1061 Private
->ImageHandle
= ImageHandle
;
1062 Private
->SendNum
= SendNumber
;
1063 Private
->BufferSize
= BufferSize
;
1064 Private
->RttMin
= ~((UINT64
)(0x0));
1065 Private
->Status
= EFI_NOT_READY
;
1067 InitializeListHead (&Private
->TxList
);
1069 IP6_COPY_ADDRESS (&Private
->SrcAddress
, SrcAddress
);
1070 IP6_COPY_ADDRESS (&Private
->DstAddress
, DstAddress
);
1073 // Open and configure a ip6 instance for ping6.
1075 Status
= Ping6CreateIpInstance (Private
);
1077 if (EFI_ERROR (Status
)) {
1078 ShellStatus
= SHELL_ACCESS_DENIED
;
1082 // Print the command line itself.
1084 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_START
), gShellNetwork2HiiHandle
, mIp6DstString
, Private
->BufferSize
);
1086 // Create a ipv6 token to receive the first icmp6 echo reply packet.
1088 Status
= Ping6OnReceiveEchoReply (Private
);
1090 if (EFI_ERROR (Status
)) {
1091 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
;
1146 // Control the ping6 process by two factors:
1148 // 2. Private->Status
1149 // 2.1. success means all icmp6 echo request packets get reply packets.
1150 // 2.2. timeout means the last icmp6 echo reply request timeout to get reply.
1151 // 2.3. noready means ping6 process is on-the-go.
1153 while (Private
->Status
== EFI_NOT_READY
) {
1154 Private
->Ip6
->Poll (Private
->Ip6
);
1157 // Terminate the ping6 process by 'esc' or 'ctl-c'.
1159 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
);
1161 if (!EFI_ERROR(Status
)) {
1162 if ((Key
.UnicodeChar
== 0x1b) || (Key
.UnicodeChar
== 0x03) ||
1163 ((Key
.UnicodeChar
== 0) && (Key
.ScanCode
== SCAN_ESC
))) {
1171 // Display the statistics in all.
1173 gBS
->SetTimer (Private
->Timer
, TimerCancel
, 0);
1175 if (Private
->TxCount
!= 0) {
1180 STRING_TOKEN (STR_PING6_STAT
),
1181 gShellNetwork2HiiHandle
,
1184 (100 * (Private
->TxCount
- Private
->RxCount
)) / Private
->TxCount
,
1189 if (Private
->RxCount
!= 0) {
1194 STRING_TOKEN (STR_PING6_RTT
),
1195 gShellNetwork2HiiHandle
,
1197 Private
->RttMin
+ Private
->TimerPeriod
,
1199 Private
->RttMax
+ Private
->TimerPeriod
,
1200 DivU64x64Remainder (Private
->RttSum
, Private
->RxCount
, NULL
),
1201 DivU64x64Remainder (Private
->RttSum
, Private
->RxCount
, NULL
) + Private
->TimerPeriod
1207 if (Private
!= NULL
) {
1208 Private
->Status
= EFI_ABORTED
;
1210 NET_LIST_FOR_EACH_SAFE (Entry
, NextEntry
, &Private
->TxList
) {
1211 TxInfo
= BASE_CR (Entry
, PING6_ICMP6_TX_INFO
, Link
);
1213 Status
= Private
->Ip6
->Cancel (Private
->Ip6
, TxInfo
->Token
);
1215 RemoveEntryList (&TxInfo
->Link
);
1216 Ping6DestroyTxInfo (TxInfo
);
1219 Ping6FreeRttTimer (Private
);
1221 if (Private
->Timer
!= NULL
) {
1222 gBS
->CloseEvent (Private
->Timer
);
1225 if (Private
->Ip6
!= NULL
) {
1226 Status
= Private
->Ip6
->Cancel (Private
->Ip6
, &Private
->RxToken
);
1229 if (Private
->RxToken
.Event
!= NULL
) {
1230 gBS
->CloseEvent (Private
->RxToken
.Event
);
1233 if (Private
->Ip6ChildHandle
!= NULL
) {
1234 Ping6DestroyIpInstance (Private
);
1244 Function for 'ping6' command.
1246 @param[in] ImageHandle Handle to the Image (NULL if Internal).
1247 @param[in] SystemTable Pointer to the System Table (NULL if Internal).
1249 @retval SHELL_SUCCESS The ping6 processed successfullly.
1250 @retval others The ping6 processed unsuccessfully.
1255 ShellCommandRunPing6 (
1256 IN EFI_HANDLE ImageHandle
,
1257 IN EFI_SYSTEM_TABLE
*SystemTable
1261 SHELL_STATUS ShellStatus
;
1262 EFI_IPv6_ADDRESS DstAddress
;
1263 EFI_IPv6_ADDRESS SrcAddress
;
1266 LIST_ENTRY
*ParamPackage
;
1267 CONST CHAR16
*ValueStr
;
1268 CONST CHAR16
*ValueStrPtr
;
1269 UINTN NonOptionCount
;
1270 CHAR16
*ProblemParam
;
1272 ProblemParam
= NULL
;
1273 ShellStatus
= SHELL_SUCCESS
;
1275 Status
= ShellCommandLineParseEx (Ping6ParamList
, &ParamPackage
, &ProblemParam
, TRUE
, FALSE
);
1276 if (EFI_ERROR(Status
)) {
1277 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_INVALID_INPUT
), gShellNetwork2HiiHandle
);
1278 ShellStatus
= SHELL_INVALID_PARAMETER
;
1286 // Parse the parameter of count number.
1288 ValueStr
= ShellCommandLineGetValue (ParamPackage
, L
"-n");
1289 ValueStrPtr
= ValueStr
;
1290 if (ValueStr
!= NULL
) {
1291 SendNumber
= ShellStrToUintn (ValueStrPtr
);
1294 // ShellStrToUintn will return 0 when input is 0 or an invalid input string.
1296 if ((SendNumber
== 0) || (SendNumber
> PING6_MAX_SEND_NUMBER
)) {
1297 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_INVALID_SEND_NUMBER
), gShellNetwork2HiiHandle
, ValueStr
);
1298 ShellStatus
= SHELL_INVALID_PARAMETER
;
1303 // Parse the parameter of buffer size.
1305 ValueStr
= ShellCommandLineGetValue (ParamPackage
, L
"-l");
1306 ValueStrPtr
= ValueStr
;
1307 if (ValueStr
!= NULL
) {
1308 BufferSize
= ShellStrToUintn (ValueStrPtr
);
1311 // ShellStrToUintn will return 0 when input is 0 or an invalid input string.
1313 if ((BufferSize
< 16) || (BufferSize
> PING6_MAX_BUFFER_SIZE
)) {
1314 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_INVALID_BUFFER_SIZE
), gShellNetwork2HiiHandle
, ValueStr
);
1315 ShellStatus
= SHELL_INVALID_PARAMETER
;
1320 ZeroMem (&SrcAddress
, sizeof (EFI_IPv6_ADDRESS
));
1321 ZeroMem (&DstAddress
, sizeof (EFI_IPv6_ADDRESS
));
1324 // Parse the parameter of source ip address.
1326 ValueStr
= ShellCommandLineGetValue (ParamPackage
, L
"-s");
1327 ValueStrPtr
= ValueStr
;
1328 if (ValueStr
!= NULL
) {
1329 mIp6SrcString
= ValueStr
;
1330 Status
= NetLibStrToIp6 (ValueStrPtr
, &SrcAddress
);
1331 if (EFI_ERROR (Status
)) {
1332 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_INVALID_IP
), gShellNetwork2HiiHandle
, ValueStr
);
1333 ShellStatus
= SHELL_INVALID_PARAMETER
;
1338 // Parse the parameter of destination ip address.
1340 NonOptionCount
= ShellCommandLineGetCount(ParamPackage
);
1341 ValueStr
= ShellCommandLineGetRawValue (ParamPackage
, (UINT32
)(NonOptionCount
-1));
1342 if (NonOptionCount
!= 2) {
1343 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_INVALID_INPUT
), gShellNetwork2HiiHandle
);
1344 ShellStatus
= SHELL_INVALID_PARAMETER
;
1347 ValueStrPtr
= ValueStr
;
1348 if (ValueStr
!= NULL
) {
1349 mIp6DstString
= ValueStr
;
1350 Status
= NetLibStrToIp6 (ValueStrPtr
, &DstAddress
);
1351 if (EFI_ERROR (Status
)) {
1352 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_INVALID_IP
), gShellNetwork2HiiHandle
, ValueStr
);
1353 ShellStatus
= SHELL_INVALID_PARAMETER
;
1359 // Enter into ping6 process.
1361 ShellStatus
= ShellPing6 (
1370 ShellCommandLineFreeVarList (ParamPackage
);