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
;
67 SHELL_PARAM_ITEM Ping6ParamList
[] = {
91 // Global Variables in Ping6 application.
93 CONST CHAR16
*mIp6DstString
;
94 CONST CHAR16
*mIp6SrcString
;
95 EFI_CPU_ARCH_PROTOCOL
*Cpu
= NULL
;
98 RTT timer tick routine.
100 @param[in] Event A EFI_EVENT type event.
101 @param[in] Context The pointer to Context.
106 Ping6RttTimerTickRoutine (
111 UINT32
*RttTimerTick
;
113 RttTimerTick
= (UINT32
*) Context
;
118 Get the timer period of the system.
120 This function tries to get the system timer period by creating
123 @return System timer period in MS, or 0 if operation failed.
133 EFI_EVENT TimerEvent
;
140 Status
= gBS
->CreateEvent (
141 EVT_TIMER
| EVT_NOTIFY_SIGNAL
,
143 Ping6RttTimerTickRoutine
,
147 if (EFI_ERROR (Status
)) {
151 OldTpl
= gBS
->RaiseTPL (TPL_CALLBACK
);
152 Status
= gBS
->SetTimer (
157 if (EFI_ERROR (Status
)) {
158 gBS
->CloseEvent (TimerEvent
);
162 while (RttTimerTick
< 10) {
163 gBS
->Stall (STALL_1_MILLI_SECOND
);
167 gBS
->RestoreTPL (OldTpl
);
169 gBS
->SetTimer (TimerEvent
, TimerCancel
, 0);
170 gBS
->CloseEvent (TimerEvent
);
172 return StallCounter
/ RttTimerTick
;
177 Initialize the timer event for RTT (round trip time).
179 @param[in] Private The pointer to PING6_PRIVATE_DATA.
181 @retval EFI_SUCCESS RTT timer is started.
182 @retval Others Failed to start the RTT timer.
187 IN PING6_PRIVATE_DATA
*Private
192 Private
->TimerPeriod
= Ping6GetTimerPeriod ();
193 if (Private
->TimerPeriod
== 0) {
197 Private
->RttTimerTick
= 0;
198 Status
= gBS
->CreateEvent (
199 EVT_TIMER
| EVT_NOTIFY_SIGNAL
,
201 Ping6RttTimerTickRoutine
,
202 &Private
->RttTimerTick
,
205 if (EFI_ERROR (Status
)) {
209 Status
= gBS
->SetTimer (
214 if (EFI_ERROR (Status
)) {
215 gBS
->CloseEvent (Private
->RttTimer
);
224 Free RTT timer event resource.
226 @param[in] Private The pointer to PING6_PRIVATE_DATA.
231 IN PING6_PRIVATE_DATA
*Private
234 if (Private
->RttTimer
!= NULL
) {
235 gBS
->SetTimer (Private
->RttTimer
, TimerCancel
, 0);
236 gBS
->CloseEvent (Private
->RttTimer
);
241 Read the current time.
243 @param[in] Private The pointer to PING6_PRIVATE_DATA.
245 @retval the current tick value.
249 IN PING6_PRIVATE_DATA
*Private
252 return Private
->RttTimerTick
;
256 Get and calculate the duration in ms.
258 @param[in] Private The pointer to PING6_PRIVATE_DATA.
259 @param[in] Begin The start point of time.
260 @param[in] End The end point of time.
262 @return The duration in ms.
267 IN PING6_PRIVATE_DATA
*Private
,
276 return (End
- Begin
) * Private
->TimerPeriod
;
281 Destroy IPING6_ICMP6_TX_INFO, and recollect the memory.
283 @param[in] TxInfo The pointer to PING6_ICMP6_TX_INFO.
288 IN PING6_ICMP6_TX_INFO
*TxInfo
291 EFI_IP6_TRANSMIT_DATA
*TxData
;
292 EFI_IP6_FRAGMENT_DATA
*FragData
;
295 ASSERT (TxInfo
!= NULL
);
297 if (TxInfo
->Token
!= NULL
) {
299 if (TxInfo
->Token
->Event
!= NULL
) {
300 gBS
->CloseEvent (TxInfo
->Token
->Event
);
303 TxData
= TxInfo
->Token
->Packet
.TxData
;
304 if (TxData
!= NULL
) {
306 if (TxData
->OverrideData
!= NULL
) {
307 FreePool (TxData
->OverrideData
);
310 if (TxData
->ExtHdrs
!= NULL
) {
311 FreePool (TxData
->ExtHdrs
);
314 for (Index
= 0; Index
< TxData
->FragmentCount
; Index
++) {
315 FragData
= TxData
->FragmentTable
[Index
].FragmentBuffer
;
316 if (FragData
!= NULL
) {
322 FreePool (TxInfo
->Token
);
329 Match the request, and reply with SequenceNum/TimeStamp.
331 @param[in] Private The pointer to PING6_PRIVATE_DATA.
332 @param[in] Packet The pointer to ICMP6_ECHO_REQUEST_REPLY.
334 @retval EFI_SUCCESS The match is successful.
335 @retval EFI_NOT_FOUND The reply can't be matched with any request.
339 Ping6OnMatchEchoReply (
340 IN PING6_PRIVATE_DATA
*Private
,
341 IN ICMP6_ECHO_REQUEST_REPLY
*Packet
344 PING6_ICMP6_TX_INFO
*TxInfo
;
346 LIST_ENTRY
*NextEntry
;
348 NET_LIST_FOR_EACH_SAFE (Entry
, NextEntry
, &Private
->TxList
) {
349 TxInfo
= BASE_CR (Entry
, PING6_ICMP6_TX_INFO
, Link
);
351 if ((TxInfo
->SequenceNum
== Packet
->SequenceNum
) && (TxInfo
->TimeStamp
== Packet
->TimeStamp
)) {
353 RemoveEntryList (&TxInfo
->Link
);
354 Ping6DestroyTxInfo (TxInfo
);
359 return EFI_NOT_FOUND
;
363 The original intention is to send a request.
364 Currently, the application retransmits an icmp6 echo request packet
365 per second in sendnumber times that is specified by the user.
366 Because nothing can be done here, all things move to the timer rountine.
368 @param[in] Event A EFI_EVENT type event.
369 @param[in] Context The pointer to Context.
374 Ping6OnEchoRequestSent6 (
382 receive reply, match and print reply infomation.
384 @param[in] Event A EFI_EVENT type event.
385 @param[in] Context The pointer to context.
390 Ping6OnEchoReplyReceived6 (
396 PING6_PRIVATE_DATA
*Private
;
397 EFI_IP6_COMPLETION_TOKEN
*RxToken
;
398 EFI_IP6_RECEIVE_DATA
*RxData
;
399 ICMP6_ECHO_REQUEST_REPLY
*Reply
;
403 Private
= (PING6_PRIVATE_DATA
*) Context
;
405 if (Private
->Status
== EFI_ABORTED
) {
409 RxToken
= &Private
->RxToken
;
410 RxData
= RxToken
->Packet
.RxData
;
411 Reply
= RxData
->FragmentTable
[0].FragmentBuffer
;
412 PayLoad
= RxData
->DataLength
;
414 if (RxData
->Header
->NextHeader
!= IP6_ICMP
) {
418 if (!IP6_IS_MULTICAST (&Private
->DstAddress
) &&
419 !EFI_IP6_EQUAL (&RxData
->Header
->SourceAddress
, &Private
->DstAddress
)) {
423 if ((Reply
->Type
!= ICMP_V6_ECHO_REPLY
) || (Reply
->Code
!= 0)) {
427 if (PayLoad
!= Private
->BufferSize
) {
431 // Check whether the reply matches the sent request before.
433 Status
= Ping6OnMatchEchoReply (Private
, Reply
);
434 if (EFI_ERROR(Status
)) {
438 // Display statistics on this icmp6 echo reply packet.
440 Rtt
= Ping6CalculateTick (Private
, Reply
->TimeStamp
, Ping6ReadTime (Private
));
442 Private
->RttSum
+= Rtt
;
443 Private
->RttMin
= Private
->RttMin
> Rtt
? Rtt
: Private
->RttMin
;
444 Private
->RttMax
= Private
->RttMax
< Rtt
? Rtt
: Private
->RttMax
;
450 STRING_TOKEN (STR_PING6_REPLY_INFO
),
451 gShellNetwork2HiiHandle
,
455 RxData
->Header
->HopLimit
,
457 Rtt
+ Private
->TimerPeriod
462 if (Private
->RxCount
< Private
->SendNum
) {
464 // Continue to receive icmp6 echo reply packets.
466 RxToken
->Status
= EFI_ABORTED
;
468 Status
= Private
->Ip6
->Receive (Private
->Ip6
, RxToken
);
470 if (EFI_ERROR (Status
)) {
471 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_IP6_RECEIVE
), gShellNetwork2HiiHandle
, Status
);
472 Private
->Status
= EFI_ABORTED
;
476 // All reply have already been received from the dest host.
478 Private
->Status
= EFI_SUCCESS
;
481 // Singal to recycle the each rxdata here, not at the end of process.
483 gBS
->SignalEvent (RxData
->RecycleSignal
);
487 Initial EFI_IP6_COMPLETION_TOKEN.
489 @param[in] Private The pointer of PING6_PRIVATE_DATA.
490 @param[in] TimeStamp The TimeStamp of request.
491 @param[in] SequenceNum The SequenceNum of request.
493 @return The pointer of EFI_IP6_COMPLETION_TOKEN.
496 EFI_IP6_COMPLETION_TOKEN
*
498 IN PING6_PRIVATE_DATA
*Private
,
500 IN UINT16 SequenceNum
504 EFI_IP6_COMPLETION_TOKEN
*Token
;
505 EFI_IP6_TRANSMIT_DATA
*TxData
;
506 ICMP6_ECHO_REQUEST_REPLY
*Request
;
508 Request
= AllocateZeroPool (Private
->BufferSize
);
510 if (Request
== NULL
) {
514 // Assembly icmp6 echo request packet.
516 Request
->Type
= ICMP_V6_ECHO_REQUEST
;
518 Request
->SequenceNum
= SequenceNum
;
519 Request
->TimeStamp
= TimeStamp
;
520 Request
->Identifier
= 0;
522 // Leave check sum to ip6 layer, since it has no idea of source address
525 Request
->Checksum
= 0;
527 TxData
= AllocateZeroPool (sizeof (EFI_IP6_TRANSMIT_DATA
));
529 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
);
664 Remove the timeout request from the list.
666 @param[in] Event A EFI_EVENT type event.
667 @param[in] Context The pointer to Context.
672 Ping6OnTimerRoutine6 (
678 PING6_PRIVATE_DATA
*Private
;
679 PING6_ICMP6_TX_INFO
*TxInfo
;
681 LIST_ENTRY
*NextEntry
;
684 Private
= (PING6_PRIVATE_DATA
*) Context
;
687 // Retransmit icmp6 echo request packets per second in sendnumber times.
689 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);
699 // Check whether any icmp6 echo request in the list timeout.
701 NET_LIST_FOR_EACH_SAFE (Entry
, NextEntry
, &Private
->TxList
) {
702 TxInfo
= BASE_CR (Entry
, PING6_ICMP6_TX_INFO
, Link
);
703 Time
= Ping6CalculateTick (Private
, TxInfo
->TimeStamp
, Ping6ReadTime (Private
));
706 // Remove the timeout echo request from txlist.
708 if (Time
> PING6_DEFAULT_TIMEOUT
) {
710 if (EFI_ERROR (TxInfo
->Token
->Status
)) {
711 Private
->Ip6
->Cancel (Private
->Ip6
, TxInfo
->Token
);
714 // Remove the timeout icmp6 echo request from list.
716 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_TIMEOUT
), gShellNetwork2HiiHandle
, TxInfo
->SequenceNum
);
718 RemoveEntryList (&TxInfo
->Link
);
719 Ping6DestroyTxInfo (TxInfo
);
721 if (IsListEmpty (&Private
->TxList
) && (Private
->TxCount
== Private
->SendNum
)) {
723 // All the left icmp6 echo request in the list timeout.
725 Private
->Status
= EFI_TIMEOUT
;
732 Create a valid IP6 instance.
734 @param[in] Private The pointer of PING6_PRIVATE_DATA.
736 @retval EFI_SUCCESS Create a valid IP6 instance successfully.
737 @retval EFI_ABORTED Locate handle with ip6 service binding protocol unsuccessfully.
738 @retval EFI_INVALID_PARAMETER The source address is unspecified when the destination address is a link -ocal address.
739 @retval EFI_OUT_OF_RESOURCES No memory is available on the platform.
740 @retval EFI_NOT_FOUND The source address is not found.
743 Ping6CreateIpInstance (
744 IN PING6_PRIVATE_DATA
*Private
750 EFI_HANDLE
*HandleBuffer
;
751 BOOLEAN UnspecifiedSrc
;
752 EFI_STATUS MediaStatus
;
753 EFI_SERVICE_BINDING_PROTOCOL
*Ip6Sb
;
754 EFI_IP6_CONFIG_PROTOCOL
*Ip6Cfg
;
755 EFI_IP6_CONFIG_DATA Ip6Config
;
756 EFI_IP6_CONFIG_INTERFACE_INFO
*IfInfo
;
758 EFI_IPv6_ADDRESS
*Addr
;
762 UnspecifiedSrc
= FALSE
;
763 MediaStatus
= EFI_SUCCESS
;
769 // Locate all the handles with ip6 service binding protocol.
771 Status
= gBS
->LocateHandleBuffer (
773 &gEfiIp6ServiceBindingProtocolGuid
,
778 if (EFI_ERROR (Status
) || (HandleNum
== 0)) {
782 if (NetIp6IsUnspecifiedAddr (&Private
->SrcAddress
)) {
784 // SrcAddress is unspecified. So, both connected and configured interface will be automatic selected.
786 UnspecifiedSrc
= TRUE
;
790 // Source address is required when pinging a link-local address.
792 if (NetIp6IsLinkLocalAddr (&Private
->DstAddress
) && UnspecifiedSrc
) {
793 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_INVALID_SOURCE
), gShellNetwork2HiiHandle
);
794 Status
= EFI_INVALID_PARAMETER
;
799 // For each ip6 protocol, check interface addresses list.
801 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
)) {
843 // Get the interface information size.
845 Status
= Ip6Cfg
->GetData (
847 Ip6ConfigDataTypeInterfaceInfo
,
852 if (Status
!= EFI_BUFFER_TOO_SMALL
) {
853 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_IP6CFG_GETDATA
), gShellNetwork2HiiHandle
, Status
);
857 IfInfo
= AllocateZeroPool (IfInfoSize
);
859 if (IfInfo
== NULL
) {
860 Status
= EFI_OUT_OF_RESOURCES
;
864 // Get the interface info.
866 Status
= Ip6Cfg
->GetData (
868 Ip6ConfigDataTypeInterfaceInfo
,
873 if (EFI_ERROR (Status
)) {
874 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_IP6CFG_GETDATA
), gShellNetwork2HiiHandle
, Status
);
878 // Check whether the source address is one of the interface addresses.
880 for (AddrIndex
= 0; AddrIndex
< IfInfo
->AddressInfoCount
; AddrIndex
++) {
881 Addr
= &(IfInfo
->AddressInfo
[AddrIndex
].Address
);
883 if (UnspecifiedSrc
) {
884 if (!NetIp6IsUnspecifiedAddr (Addr
) && !NetIp6IsLinkLocalAddr (Addr
)) {
886 // Select the interface automatically.
888 CopyMem(&Private
->SrcAddress
, Addr
, sizeof(Private
->SrcAddress
));
891 } else if (EFI_IP6_EQUAL (&Private
->SrcAddress
, Addr
)) {
893 // Match a certain interface address.
899 if (AddrIndex
< IfInfo
->AddressInfoCount
) {
901 // Found a nic handle with right interface address.
910 // No exact interface address matched.
913 if (HandleIndex
== HandleNum
) {
914 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_CONFIGD_NIC_NF
), gShellNetwork2HiiHandle
);
915 Status
= EFI_NOT_FOUND
;
919 Private
->NicHandle
= HandleBuffer
[HandleIndex
];
921 ASSERT (Ip6Sb
!= NULL
);
922 Status
= Ip6Sb
->CreateChild (Ip6Sb
, &Private
->Ip6ChildHandle
);
924 if (EFI_ERROR (Status
)) {
928 Status
= gBS
->OpenProtocol (
929 Private
->Ip6ChildHandle
,
930 &gEfiIp6ProtocolGuid
,
931 (VOID
**) &Private
->Ip6
,
932 Private
->ImageHandle
,
933 Private
->Ip6ChildHandle
,
934 EFI_OPEN_PROTOCOL_GET_PROTOCOL
936 if (EFI_ERROR (Status
)) {
940 ZeroMem (&Ip6Config
, sizeof (EFI_IP6_CONFIG_DATA
));
943 // Configure the ip6 instance for icmp6 packet exchange.
945 Ip6Config
.DefaultProtocol
= 58;
946 Ip6Config
.AcceptAnyProtocol
= FALSE
;
947 Ip6Config
.AcceptIcmpErrors
= TRUE
;
948 Ip6Config
.AcceptPromiscuous
= FALSE
;
949 Ip6Config
.TrafficClass
= 0;
950 Ip6Config
.HopLimit
= 128;
951 Ip6Config
.FlowLabel
= 0;
952 Ip6Config
.ReceiveTimeout
= 0;
953 Ip6Config
.TransmitTimeout
= 0;
955 IP6_COPY_ADDRESS (&Ip6Config
.StationAddress
, &Private
->SrcAddress
);
957 IP6_COPY_ADDRESS (&Ip6Config
.DestinationAddress
, &Private
->DstAddress
);
959 Status
= Private
->Ip6
->Configure (Private
->Ip6
, &Ip6Config
);
961 if (EFI_ERROR (Status
)) {
962 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_IP6_CONFIG
), gShellNetwork2HiiHandle
, Status
);
969 if (HandleBuffer
!= NULL
) {
970 FreePool (HandleBuffer
);
973 if (IfInfo
!= NULL
) {
977 if ((Ip6Sb
!= NULL
) && (Private
->Ip6ChildHandle
!= NULL
)) {
978 Ip6Sb
->DestroyChild (Ip6Sb
, Private
->Ip6ChildHandle
);
985 Destroy the IP6 instance.
987 @param[in] Private The pointer of PING6_PRIVATE_DATA.
991 Ping6DestroyIpInstance (
992 IN PING6_PRIVATE_DATA
*Private
996 EFI_SERVICE_BINDING_PROTOCOL
*Ip6Sb
;
999 Private
->Ip6ChildHandle
,
1000 &gEfiIp6ProtocolGuid
,
1001 Private
->ImageHandle
,
1002 Private
->Ip6ChildHandle
1005 Status
= gBS
->HandleProtocol (
1007 &gEfiIp6ServiceBindingProtocolGuid
,
1011 if (!EFI_ERROR(Status
)) {
1012 Ip6Sb
->DestroyChild (Ip6Sb
, Private
->Ip6ChildHandle
);
1019 @param[in] ImageHandle The firmware allocated handle for the UEFI image.
1020 @param[in] SendNumber The send request count.
1021 @param[in] BufferSize The send buffer size.
1022 @param[in] SrcAddress The source IPv6 address.
1023 @param[in] DstAddress The destination IPv6 address.
1025 @retval SHELL_SUCCESS The ping6 processed successfullly.
1026 @retval others The ping6 processed unsuccessfully.
1031 IN EFI_HANDLE ImageHandle
,
1032 IN UINT32 SendNumber
,
1033 IN UINT32 BufferSize
,
1034 IN EFI_IPv6_ADDRESS
*SrcAddress
,
1035 IN EFI_IPv6_ADDRESS
*DstAddress
1040 PING6_PRIVATE_DATA
*Private
;
1041 PING6_ICMP6_TX_INFO
*TxInfo
;
1043 LIST_ENTRY
*NextEntry
;
1044 SHELL_STATUS ShellStatus
;
1046 ShellStatus
= SHELL_SUCCESS
;
1047 Private
= AllocateZeroPool (sizeof (PING6_PRIVATE_DATA
));
1049 if (Private
== NULL
) {
1050 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_GEN_OUT_MEM
), gShellNetwork2HiiHandle
, L
"Ping6");
1051 ShellStatus
= SHELL_OUT_OF_RESOURCES
;
1055 Private
->ImageHandle
= ImageHandle
;
1056 Private
->SendNum
= SendNumber
;
1057 Private
->BufferSize
= BufferSize
;
1058 Private
->RttMin
= ~((UINT64
)(0x0));
1059 Private
->Status
= EFI_NOT_READY
;
1061 InitializeListHead (&Private
->TxList
);
1063 IP6_COPY_ADDRESS (&Private
->SrcAddress
, SrcAddress
);
1064 IP6_COPY_ADDRESS (&Private
->DstAddress
, DstAddress
);
1067 // Open and configure a ip6 instance for ping6.
1069 Status
= Ping6CreateIpInstance (Private
);
1071 if (EFI_ERROR (Status
)) {
1072 ShellStatus
= SHELL_ACCESS_DENIED
;
1076 // Print the command line itself.
1078 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_START
), gShellNetwork2HiiHandle
, mIp6DstString
, Private
->BufferSize
);
1080 // Create a ipv6 token to receive the first icmp6 echo reply packet.
1082 Status
= Ping6OnReceiveEchoReply (Private
);
1084 if (EFI_ERROR (Status
)) {
1085 ShellStatus
= SHELL_ACCESS_DENIED
;
1089 // Create and start timer to send icmp6 echo request packet per second.
1091 Status
= gBS
->CreateEvent (
1092 EVT_TIMER
| EVT_NOTIFY_SIGNAL
,
1094 Ping6OnTimerRoutine6
,
1099 if (EFI_ERROR (Status
)) {
1100 ShellStatus
= SHELL_ACCESS_DENIED
;
1105 // Start a timer to calculate the RTT.
1107 Status
= Ping6InitRttTimer (Private
);
1108 if (EFI_ERROR (Status
)) {
1109 ShellStatus
= SHELL_ACCESS_DENIED
;
1114 // Create a ipv6 token to send the first icmp6 echo request packet.
1116 Status
= Ping6SendEchoRequest (Private
);
1118 // EFI_NOT_READY for IPsec is enable and IKE is not established.
1120 if (EFI_ERROR (Status
) && (Status
!= EFI_NOT_READY
)) {
1121 ShellStatus
= SHELL_ACCESS_DENIED
;
1122 if(Status
== EFI_NOT_FOUND
) {
1123 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_NOSOURCE_INDOMAIN
), gShellNetwork2HiiHandle
, mIp6DstString
);
1129 Status
= gBS
->SetTimer (
1135 if (EFI_ERROR (Status
)) {
1136 ShellStatus
= SHELL_ACCESS_DENIED
;
1140 // Control the ping6 process by two factors:
1142 // 2. Private->Status
1143 // 2.1. success means all icmp6 echo request packets get reply packets.
1144 // 2.2. timeout means the last icmp6 echo reply request timeout to get reply.
1145 // 2.3. noready means ping6 process is on-the-go.
1147 while (Private
->Status
== EFI_NOT_READY
) {
1148 Private
->Ip6
->Poll (Private
->Ip6
);
1151 // Terminate the ping6 process by 'esc' or 'ctl-c'.
1153 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
);
1155 if (!EFI_ERROR(Status
)) {
1156 if ((Key
.UnicodeChar
== 0x1b) || (Key
.UnicodeChar
== 0x03) ||
1157 ((Key
.UnicodeChar
== 0) && (Key
.ScanCode
== SCAN_ESC
))) {
1165 // Display the statistics in all.
1167 gBS
->SetTimer (Private
->Timer
, TimerCancel
, 0);
1169 if (Private
->TxCount
!= 0) {
1174 STRING_TOKEN (STR_PING6_STAT
),
1175 gShellNetwork2HiiHandle
,
1178 (100 * (Private
->TxCount
- Private
->RxCount
)) / Private
->TxCount
,
1183 if (Private
->RxCount
!= 0) {
1188 STRING_TOKEN (STR_PING6_RTT
),
1189 gShellNetwork2HiiHandle
,
1191 Private
->RttMin
+ Private
->TimerPeriod
,
1193 Private
->RttMax
+ Private
->TimerPeriod
,
1194 DivU64x64Remainder (Private
->RttSum
, Private
->RxCount
, NULL
),
1195 DivU64x64Remainder (Private
->RttSum
, Private
->RxCount
, NULL
) + Private
->TimerPeriod
1201 if (Private
!= NULL
) {
1202 Private
->Status
= EFI_ABORTED
;
1204 NET_LIST_FOR_EACH_SAFE (Entry
, NextEntry
, &Private
->TxList
) {
1205 TxInfo
= BASE_CR (Entry
, PING6_ICMP6_TX_INFO
, Link
);
1207 Status
= Private
->Ip6
->Cancel (Private
->Ip6
, TxInfo
->Token
);
1209 RemoveEntryList (&TxInfo
->Link
);
1210 Ping6DestroyTxInfo (TxInfo
);
1213 Ping6FreeRttTimer (Private
);
1215 if (Private
->Timer
!= NULL
) {
1216 gBS
->CloseEvent (Private
->Timer
);
1219 if (Private
->Ip6
!= NULL
) {
1220 Status
= Private
->Ip6
->Cancel (Private
->Ip6
, &Private
->RxToken
);
1223 if (Private
->RxToken
.Event
!= NULL
) {
1224 gBS
->CloseEvent (Private
->RxToken
.Event
);
1227 if (Private
->Ip6ChildHandle
!= NULL
) {
1228 Ping6DestroyIpInstance (Private
);
1238 Function for 'ping6' command.
1240 @param[in] ImageHandle Handle to the Image (NULL if Internal).
1241 @param[in] SystemTable Pointer to the System Table (NULL if Internal).
1243 @retval SHELL_SUCCESS The ping6 processed successfullly.
1244 @retval others The ping6 processed unsuccessfully.
1249 ShellCommandRunPing6 (
1250 IN EFI_HANDLE ImageHandle
,
1251 IN EFI_SYSTEM_TABLE
*SystemTable
1255 SHELL_STATUS ShellStatus
;
1256 EFI_IPv6_ADDRESS DstAddress
;
1257 EFI_IPv6_ADDRESS SrcAddress
;
1260 LIST_ENTRY
*ParamPackage
;
1261 CONST CHAR16
*ValueStr
;
1262 CONST CHAR16
*ValueStrPtr
;
1263 UINTN NonOptionCount
;
1264 CHAR16
*ProblemParam
;
1266 ProblemParam
= NULL
;
1267 ShellStatus
= SHELL_SUCCESS
;
1269 Status
= ShellCommandLineParseEx (Ping6ParamList
, &ParamPackage
, &ProblemParam
, TRUE
, FALSE
);
1270 if (EFI_ERROR(Status
)) {
1271 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_INVALID_INPUT
), gShellNetwork2HiiHandle
);
1272 ShellStatus
= SHELL_INVALID_PARAMETER
;
1280 // Parse the parameter of count number.
1282 ValueStr
= ShellCommandLineGetValue (ParamPackage
, L
"-n");
1283 ValueStrPtr
= ValueStr
;
1284 if (ValueStr
!= NULL
) {
1285 SendNumber
= ShellStrToUintn (ValueStrPtr
);
1288 // ShellStrToUintn will return 0 when input is 0 or an invalid input string.
1290 if ((SendNumber
== 0) || (SendNumber
> PING6_MAX_SEND_NUMBER
)) {
1291 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_INVALID_SEND_NUMBER
), gShellNetwork2HiiHandle
, ValueStr
);
1292 ShellStatus
= SHELL_INVALID_PARAMETER
;
1297 // Parse the parameter of buffer size.
1299 ValueStr
= ShellCommandLineGetValue (ParamPackage
, L
"-l");
1300 ValueStrPtr
= ValueStr
;
1301 if (ValueStr
!= NULL
) {
1302 BufferSize
= ShellStrToUintn (ValueStrPtr
);
1305 // ShellStrToUintn will return 0 when input is 0 or an invalid input string.
1307 if ((BufferSize
< 16) || (BufferSize
> PING6_MAX_BUFFER_SIZE
)) {
1308 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_INVALID_BUFFER_SIZE
), gShellNetwork2HiiHandle
, ValueStr
);
1309 ShellStatus
= SHELL_INVALID_PARAMETER
;
1314 ZeroMem (&SrcAddress
, sizeof (EFI_IPv6_ADDRESS
));
1315 ZeroMem (&DstAddress
, sizeof (EFI_IPv6_ADDRESS
));
1318 // Parse the parameter of source ip address.
1320 ValueStr
= ShellCommandLineGetValue (ParamPackage
, L
"-s");
1321 ValueStrPtr
= ValueStr
;
1322 if (ValueStr
!= NULL
) {
1323 mIp6SrcString
= ValueStr
;
1324 Status
= NetLibStrToIp6 (ValueStrPtr
, &SrcAddress
);
1325 if (EFI_ERROR (Status
)) {
1326 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_INVALID_IP
), gShellNetwork2HiiHandle
, ValueStr
);
1327 ShellStatus
= SHELL_INVALID_PARAMETER
;
1332 // Parse the parameter of destination ip address.
1334 NonOptionCount
= ShellCommandLineGetCount(ParamPackage
);
1335 ValueStr
= ShellCommandLineGetRawValue (ParamPackage
, (UINT32
)(NonOptionCount
-1));
1336 if (NonOptionCount
!= 2) {
1337 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_INVALID_INPUT
), gShellNetwork2HiiHandle
);
1338 ShellStatus
= SHELL_INVALID_PARAMETER
;
1341 ValueStrPtr
= ValueStr
;
1342 if (ValueStr
!= NULL
) {
1343 mIp6DstString
= ValueStr
;
1344 Status
= NetLibStrToIp6 (ValueStrPtr
, &DstAddress
);
1345 if (EFI_ERROR (Status
)) {
1346 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_PING6_INVALID_IP
), gShellNetwork2HiiHandle
, ValueStr
);
1347 ShellStatus
= SHELL_INVALID_PARAMETER
;
1353 // Enter into ping6 process.
1355 ShellStatus
= ShellPing6 (
1364 ShellCommandLineFreeVarList (ParamPackage
);