]> git.proxmox.com Git - mirror_edk2.git/blob - ShellPkg/Library/UefiShellNetwork2CommandsLib/Ping6.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / ShellPkg / Library / UefiShellNetwork2CommandsLib / Ping6.c
1 /** @file
2 The implementation for Ping6 application.
3
4 Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR>
5
6 SPDX-License-Identifier: BSD-2-Clause-Patent
7
8 **/
9
10 #include "UefiShellNetwork2CommandsLib.h"
11
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
17
18 #pragma pack(1)
19
20 typedef struct _ICMP6_ECHO_REQUEST_REPLY {
21 UINT8 Type;
22 UINT8 Code;
23 UINT16 Checksum;
24 UINT16 Identifier;
25 UINT16 SequenceNum;
26 UINT32 TimeStamp;
27 UINT8 Data[1];
28 } ICMP6_ECHO_REQUEST_REPLY;
29
30 #pragma pack()
31
32 typedef struct _PING6_ICMP6_TX_INFO {
33 LIST_ENTRY Link;
34 UINT16 SequenceNum;
35 UINT32 TimeStamp;
36 EFI_IP6_COMPLETION_TOKEN *Token;
37 } PING6_ICMP6_TX_INFO;
38
39 typedef struct _PING6_PRIVATE_DATA {
40 EFI_HANDLE ImageHandle;
41 EFI_HANDLE NicHandle;
42 EFI_HANDLE Ip6ChildHandle;
43 EFI_IP6_PROTOCOL *Ip6;
44 EFI_EVENT Timer;
45
46 UINT32 TimerPeriod;
47 UINT32 RttTimerTick;
48 EFI_EVENT RttTimer;
49
50 EFI_STATUS Status;
51 LIST_ENTRY TxList;
52 EFI_IP6_COMPLETION_TOKEN RxToken;
53 UINT16 RxCount;
54 UINT16 TxCount;
55 UINT64 RttSum;
56 UINT64 RttMin;
57 UINT64 RttMax;
58 UINT32 SequenceNum;
59
60 EFI_IPv6_ADDRESS SrcAddress;
61 EFI_IPv6_ADDRESS DstAddress;
62 UINT32 SendNum;
63 UINT32 BufferSize;
64 } PING6_PRIVATE_DATA;
65
66
67 SHELL_PARAM_ITEM Ping6ParamList[] = {
68 {
69 L"-l",
70 TypeValue
71 },
72 {
73 L"-n",
74 TypeValue
75 },
76 {
77 L"-s",
78 TypeValue
79 },
80 {
81 L"-?",
82 TypeFlag
83 },
84 {
85 NULL,
86 TypeMax
87 },
88 };
89
90 //
91 // Global Variables in Ping6 application.
92 //
93 CONST CHAR16 *mIp6DstString;
94 CONST CHAR16 *mIp6SrcString;
95 EFI_CPU_ARCH_PROTOCOL *Cpu = NULL;
96
97 /**
98 RTT timer tick routine.
99
100 @param[in] Event A EFI_EVENT type event.
101 @param[in] Context The pointer to Context.
102
103 **/
104 VOID
105 EFIAPI
106 Ping6RttTimerTickRoutine (
107 IN EFI_EVENT Event,
108 IN VOID *Context
109 )
110 {
111 UINT32 *RttTimerTick;
112
113 RttTimerTick = (UINT32*) Context;
114 (*RttTimerTick)++;
115 }
116
117 /**
118 Get the timer period of the system.
119
120 This function tries to get the system timer period by creating
121 an 1ms period timer.
122
123 @return System timer period in MS, or 0 if operation failed.
124
125 **/
126 UINT32
127 Ping6GetTimerPeriod(
128 VOID
129 )
130 {
131 EFI_STATUS Status;
132 UINT32 RttTimerTick;
133 EFI_EVENT TimerEvent;
134 UINT32 StallCounter;
135 EFI_TPL OldTpl;
136
137 RttTimerTick = 0;
138 StallCounter = 0;
139
140 Status = gBS->CreateEvent (
141 EVT_TIMER | EVT_NOTIFY_SIGNAL,
142 TPL_NOTIFY,
143 Ping6RttTimerTickRoutine,
144 &RttTimerTick,
145 &TimerEvent
146 );
147 if (EFI_ERROR (Status)) {
148 return 0;
149 }
150
151 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
152 Status = gBS->SetTimer (
153 TimerEvent,
154 TimerPeriodic,
155 TICKS_PER_MS
156 );
157 if (EFI_ERROR (Status)) {
158 gBS->CloseEvent (TimerEvent);
159 return 0;
160 }
161
162 while (RttTimerTick < 10) {
163 gBS->Stall (STALL_1_MILLI_SECOND);
164 ++StallCounter;
165 }
166
167 gBS->RestoreTPL (OldTpl);
168
169 gBS->SetTimer (TimerEvent, TimerCancel, 0);
170 gBS->CloseEvent (TimerEvent);
171
172 return StallCounter / RttTimerTick;
173 }
174
175
176 /**
177 Initialize the timer event for RTT (round trip time).
178
179 @param[in] Private The pointer to PING6_PRIVATE_DATA.
180
181 @retval EFI_SUCCESS RTT timer is started.
182 @retval Others Failed to start the RTT timer.
183
184 **/
185 EFI_STATUS
186 Ping6InitRttTimer (
187 IN PING6_PRIVATE_DATA *Private
188 )
189 {
190 EFI_STATUS Status;
191
192 Private->TimerPeriod = Ping6GetTimerPeriod ();
193 if (Private->TimerPeriod == 0) {
194 return EFI_ABORTED;
195 }
196
197 Private->RttTimerTick = 0;
198 Status = gBS->CreateEvent (
199 EVT_TIMER | EVT_NOTIFY_SIGNAL,
200 TPL_NOTIFY,
201 Ping6RttTimerTickRoutine,
202 &Private->RttTimerTick,
203 &Private->RttTimer
204 );
205 if (EFI_ERROR (Status)) {
206 return Status;
207 }
208
209 Status = gBS->SetTimer (
210 Private->RttTimer,
211 TimerPeriodic,
212 TICKS_PER_MS
213 );
214 if (EFI_ERROR (Status)) {
215 gBS->CloseEvent (Private->RttTimer);
216 return Status;
217 }
218
219 return EFI_SUCCESS;
220
221 }
222
223 /**
224 Free RTT timer event resource.
225
226 @param[in] Private The pointer to PING6_PRIVATE_DATA.
227
228 **/
229 VOID
230 Ping6FreeRttTimer (
231 IN PING6_PRIVATE_DATA *Private
232 )
233 {
234 if (Private->RttTimer != NULL) {
235 gBS->SetTimer (Private->RttTimer, TimerCancel, 0);
236 gBS->CloseEvent (Private->RttTimer);
237 }
238 }
239
240 /**
241 Read the current time.
242
243 @param[in] Private The pointer to PING6_PRIVATE_DATA.
244
245 @retval the current tick value.
246 **/
247 UINT32
248 Ping6ReadTime (
249 IN PING6_PRIVATE_DATA *Private
250 )
251 {
252 return Private->RttTimerTick;
253 }
254
255 /**
256 Get and calculate the duration in ms.
257
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.
261
262 @return The duration in ms.
263
264 **/
265 UINT32
266 Ping6CalculateTick (
267 IN PING6_PRIVATE_DATA *Private,
268 IN UINT32 Begin,
269 IN UINT32 End
270 )
271 {
272 if (End < Begin) {
273 return (0);
274 }
275
276 return (End - Begin) * Private->TimerPeriod;
277
278 }
279
280 /**
281 Destroy IPING6_ICMP6_TX_INFO, and recollect the memory.
282
283 @param[in] TxInfo The pointer to PING6_ICMP6_TX_INFO.
284
285 **/
286 VOID
287 Ping6DestroyTxInfo (
288 IN PING6_ICMP6_TX_INFO *TxInfo
289 )
290 {
291 EFI_IP6_TRANSMIT_DATA *TxData;
292 EFI_IP6_FRAGMENT_DATA *FragData;
293 UINTN Index;
294
295 ASSERT (TxInfo != NULL);
296
297 if (TxInfo->Token != NULL) {
298
299 if (TxInfo->Token->Event != NULL) {
300 gBS->CloseEvent (TxInfo->Token->Event);
301 }
302
303 TxData = TxInfo->Token->Packet.TxData;
304 if (TxData != NULL) {
305
306 if (TxData->OverrideData != NULL) {
307 FreePool (TxData->OverrideData);
308 }
309
310 if (TxData->ExtHdrs != NULL) {
311 FreePool (TxData->ExtHdrs);
312 }
313
314 for (Index = 0; Index < TxData->FragmentCount; Index++) {
315 FragData = TxData->FragmentTable[Index].FragmentBuffer;
316 if (FragData != NULL) {
317 FreePool (FragData);
318 }
319 }
320 }
321
322 FreePool (TxInfo->Token);
323 }
324
325 FreePool (TxInfo);
326 }
327
328 /**
329 Match the request, and reply with SequenceNum/TimeStamp.
330
331 @param[in] Private The pointer to PING6_PRIVATE_DATA.
332 @param[in] Packet The pointer to ICMP6_ECHO_REQUEST_REPLY.
333
334 @retval EFI_SUCCESS The match is successful.
335 @retval EFI_NOT_FOUND The reply can't be matched with any request.
336
337 **/
338 EFI_STATUS
339 Ping6OnMatchEchoReply (
340 IN PING6_PRIVATE_DATA *Private,
341 IN ICMP6_ECHO_REQUEST_REPLY *Packet
342 )
343 {
344 PING6_ICMP6_TX_INFO *TxInfo;
345 LIST_ENTRY *Entry;
346 LIST_ENTRY *NextEntry;
347
348 NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) {
349 TxInfo = BASE_CR (Entry, PING6_ICMP6_TX_INFO, Link);
350
351 if ((TxInfo->SequenceNum == Packet->SequenceNum) && (TxInfo->TimeStamp == Packet->TimeStamp)) {
352 Private->RxCount++;
353 RemoveEntryList (&TxInfo->Link);
354 Ping6DestroyTxInfo (TxInfo);
355 return EFI_SUCCESS;
356 }
357 }
358
359 return EFI_NOT_FOUND;
360 }
361
362 /**
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.
367
368 @param[in] Event A EFI_EVENT type event.
369 @param[in] Context The pointer to Context.
370
371 **/
372 VOID
373 EFIAPI
374 Ping6OnEchoRequestSent6 (
375 IN EFI_EVENT Event,
376 IN VOID *Context
377 )
378 {
379 }
380
381 /**
382 receive reply, match and print reply infomation.
383
384 @param[in] Event A EFI_EVENT type event.
385 @param[in] Context The pointer to context.
386
387 **/
388 VOID
389 EFIAPI
390 Ping6OnEchoReplyReceived6 (
391 IN EFI_EVENT Event,
392 IN VOID *Context
393 )
394 {
395 EFI_STATUS Status;
396 PING6_PRIVATE_DATA *Private;
397 EFI_IP6_COMPLETION_TOKEN *RxToken;
398 EFI_IP6_RECEIVE_DATA *RxData;
399 ICMP6_ECHO_REQUEST_REPLY *Reply;
400 UINT32 PayLoad;
401 UINT32 Rtt;
402
403 Private = (PING6_PRIVATE_DATA *) Context;
404
405 if (Private->Status == EFI_ABORTED) {
406 return;
407 }
408
409 RxToken = &Private->RxToken;
410 RxData = RxToken->Packet.RxData;
411 Reply = RxData->FragmentTable[0].FragmentBuffer;
412 PayLoad = RxData->DataLength;
413
414 if (RxData->Header->NextHeader != IP6_ICMP) {
415 goto ON_EXIT;
416 }
417
418 if (!IP6_IS_MULTICAST (&Private->DstAddress) &&
419 !EFI_IP6_EQUAL (&RxData->Header->SourceAddress, &Private->DstAddress)) {
420 goto ON_EXIT;
421 }
422
423 if ((Reply->Type != ICMP_V6_ECHO_REPLY) || (Reply->Code != 0)) {
424 goto ON_EXIT;
425 }
426
427 if (PayLoad != Private->BufferSize) {
428 goto ON_EXIT;
429 }
430 //
431 // Check whether the reply matches the sent request before.
432 //
433 Status = Ping6OnMatchEchoReply (Private, Reply);
434 if (EFI_ERROR(Status)) {
435 goto ON_EXIT;
436 }
437 //
438 // Display statistics on this icmp6 echo reply packet.
439 //
440 Rtt = Ping6CalculateTick (Private, Reply->TimeStamp, Ping6ReadTime (Private));
441
442 Private->RttSum += Rtt;
443 Private->RttMin = Private->RttMin > Rtt ? Rtt : Private->RttMin;
444 Private->RttMax = Private->RttMax < Rtt ? Rtt : Private->RttMax;
445
446 ShellPrintHiiEx (
447 -1,
448 -1,
449 NULL,
450 STRING_TOKEN (STR_PING6_REPLY_INFO),
451 gShellNetwork2HiiHandle,
452 PayLoad,
453 mIp6DstString,
454 Reply->SequenceNum,
455 RxData->Header->HopLimit,
456 Rtt,
457 Rtt + Private->TimerPeriod
458 );
459
460 ON_EXIT:
461
462 if (Private->RxCount < Private->SendNum) {
463 //
464 // Continue to receive icmp6 echo reply packets.
465 //
466 RxToken->Status = EFI_ABORTED;
467
468 Status = Private->Ip6->Receive (Private->Ip6, RxToken);
469
470 if (EFI_ERROR (Status)) {
471 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6_RECEIVE), gShellNetwork2HiiHandle, Status);
472 Private->Status = EFI_ABORTED;
473 }
474 } else {
475 //
476 // All reply have already been received from the dest host.
477 //
478 Private->Status = EFI_SUCCESS;
479 }
480 //
481 // Singal to recycle the each rxdata here, not at the end of process.
482 //
483 gBS->SignalEvent (RxData->RecycleSignal);
484 }
485
486 /**
487 Initial EFI_IP6_COMPLETION_TOKEN.
488
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.
492
493 @return The pointer of EFI_IP6_COMPLETION_TOKEN.
494
495 **/
496 EFI_IP6_COMPLETION_TOKEN *
497 Ping6GenerateToken (
498 IN PING6_PRIVATE_DATA *Private,
499 IN UINT32 TimeStamp,
500 IN UINT16 SequenceNum
501 )
502 {
503 EFI_STATUS Status;
504 EFI_IP6_COMPLETION_TOKEN *Token;
505 EFI_IP6_TRANSMIT_DATA *TxData;
506 ICMP6_ECHO_REQUEST_REPLY *Request;
507
508 Request = AllocateZeroPool (Private->BufferSize);
509
510 if (Request == NULL) {
511 return NULL;
512 }
513 //
514 // Assembly icmp6 echo request packet.
515 //
516 Request->Type = ICMP_V6_ECHO_REQUEST;
517 Request->Code = 0;
518 Request->SequenceNum = SequenceNum;
519 Request->TimeStamp = TimeStamp;
520 Request->Identifier = 0;
521 //
522 // Leave check sum to ip6 layer, since it has no idea of source address
523 // selection.
524 //
525 Request->Checksum = 0;
526
527 TxData = AllocateZeroPool (sizeof (EFI_IP6_TRANSMIT_DATA));
528
529 if (TxData == NULL) {
530 FreePool (Request);
531 return NULL;
532 }
533 //
534 // Assembly ipv6 token for transmit.
535 //
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;
543
544 Token = AllocateZeroPool (sizeof (EFI_IP6_COMPLETION_TOKEN));
545
546 if (Token == NULL) {
547 FreePool (Request);
548 FreePool (TxData);
549 return NULL;
550 }
551
552 Token->Status = EFI_ABORTED;
553 Token->Packet.TxData = TxData;
554
555 Status = gBS->CreateEvent (
556 EVT_NOTIFY_SIGNAL,
557 TPL_CALLBACK,
558 Ping6OnEchoRequestSent6,
559 Private,
560 &Token->Event
561 );
562
563 if (EFI_ERROR (Status)) {
564 FreePool (Request);
565 FreePool (TxData);
566 FreePool (Token);
567 return NULL;
568 }
569
570 return Token;
571 }
572
573 /**
574 Transmit the EFI_IP6_COMPLETION_TOKEN.
575
576 @param[in] Private The pointer of PING6_PRIVATE_DATA.
577
578 @retval EFI_SUCCESS Transmitted successfully.
579 @retval EFI_OUT_OF_RESOURCES No memory is available on the platform.
580 @retval others Transmitted unsuccessfully.
581
582 **/
583 EFI_STATUS
584 Ping6SendEchoRequest (
585 IN PING6_PRIVATE_DATA *Private
586 )
587 {
588 EFI_STATUS Status;
589 PING6_ICMP6_TX_INFO *TxInfo;
590
591 TxInfo = AllocateZeroPool (sizeof (PING6_ICMP6_TX_INFO));
592
593 if (TxInfo == NULL) {
594 return EFI_OUT_OF_RESOURCES;
595 }
596
597 TxInfo->TimeStamp = Ping6ReadTime (Private);
598 TxInfo->SequenceNum = (UINT16) (Private->TxCount + 1);
599
600 TxInfo->Token = Ping6GenerateToken (
601 Private,
602 TxInfo->TimeStamp,
603 TxInfo->SequenceNum
604 );
605
606 if (TxInfo->Token == NULL) {
607 Ping6DestroyTxInfo (TxInfo);
608 return EFI_OUT_OF_RESOURCES;
609 }
610
611 Status = Private->Ip6->Transmit (Private->Ip6, TxInfo->Token);
612
613 if (EFI_ERROR (Status)) {
614 Ping6DestroyTxInfo (TxInfo);
615 return Status;
616 }
617
618 InsertTailList (&Private->TxList, &TxInfo->Link);
619 Private->TxCount++;
620
621 return EFI_SUCCESS;
622 }
623
624 /**
625 Place a completion token into the receive packet queue to receive the echo reply.
626
627 @param[in] Private The pointer of PING6_PRIVATE_DATA.
628
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.
631
632 **/
633 EFI_STATUS
634 Ping6OnReceiveEchoReply (
635 IN PING6_PRIVATE_DATA *Private
636 )
637 {
638 EFI_STATUS Status;
639
640 ZeroMem (&Private->RxToken, sizeof (EFI_IP6_COMPLETION_TOKEN));
641
642 Status = gBS->CreateEvent (
643 EVT_NOTIFY_SIGNAL,
644 TPL_CALLBACK,
645 Ping6OnEchoReplyReceived6,
646 Private,
647 &Private->RxToken.Event
648 );
649
650 if (EFI_ERROR (Status)) {
651 return Status;
652 }
653
654 Private->RxToken.Status = EFI_NOT_READY;
655
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);
659 }
660 return Status;
661 }
662
663 /**
664 Remove the timeout request from the list.
665
666 @param[in] Event A EFI_EVENT type event.
667 @param[in] Context The pointer to Context.
668
669 **/
670 VOID
671 EFIAPI
672 Ping6OnTimerRoutine6 (
673 IN EFI_EVENT Event,
674 IN VOID *Context
675 )
676 {
677 EFI_STATUS Status;
678 PING6_PRIVATE_DATA *Private;
679 PING6_ICMP6_TX_INFO *TxInfo;
680 LIST_ENTRY *Entry;
681 LIST_ENTRY *NextEntry;
682 UINT64 Time;
683
684 Private = (PING6_PRIVATE_DATA *) Context;
685
686 //
687 // Retransmit icmp6 echo request packets per second in sendnumber times.
688 //
689 if (Private->TxCount < Private->SendNum) {
690
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);
695 }
696 }
697 }
698 //
699 // Check whether any icmp6 echo request in the list timeout.
700 //
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));
704
705 //
706 // Remove the timeout echo request from txlist.
707 //
708 if (Time > PING6_DEFAULT_TIMEOUT) {
709
710 if (EFI_ERROR (TxInfo->Token->Status)) {
711 Private->Ip6->Cancel (Private->Ip6, TxInfo->Token);
712 }
713 //
714 // Remove the timeout icmp6 echo request from list.
715 //
716 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_TIMEOUT), gShellNetwork2HiiHandle, TxInfo->SequenceNum);
717
718 RemoveEntryList (&TxInfo->Link);
719 Ping6DestroyTxInfo (TxInfo);
720
721 if (IsListEmpty (&Private->TxList) && (Private->TxCount == Private->SendNum)) {
722 //
723 // All the left icmp6 echo request in the list timeout.
724 //
725 Private->Status = EFI_TIMEOUT;
726 }
727 }
728 }
729 }
730
731 /**
732 Create a valid IP6 instance.
733
734 @param[in] Private The pointer of PING6_PRIVATE_DATA.
735
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.
741 **/
742 EFI_STATUS
743 Ping6CreateIpInstance (
744 IN PING6_PRIVATE_DATA *Private
745 )
746 {
747 EFI_STATUS Status;
748 UINTN HandleIndex;
749 UINTN HandleNum;
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;
757 UINTN IfInfoSize;
758 EFI_IPv6_ADDRESS *Addr;
759 UINTN AddrIndex;
760
761 HandleBuffer = NULL;
762 UnspecifiedSrc = FALSE;
763 MediaStatus = EFI_SUCCESS;
764 Ip6Sb = NULL;
765 IfInfo = NULL;
766 IfInfoSize = 0;
767
768 //
769 // Locate all the handles with ip6 service binding protocol.
770 //
771 Status = gBS->LocateHandleBuffer (
772 ByProtocol,
773 &gEfiIp6ServiceBindingProtocolGuid,
774 NULL,
775 &HandleNum,
776 &HandleBuffer
777 );
778 if (EFI_ERROR (Status) || (HandleNum == 0)) {
779 return EFI_ABORTED;
780 }
781
782 if (NetIp6IsUnspecifiedAddr (&Private->SrcAddress)) {
783 //
784 // SrcAddress is unspecified. So, both connected and configured interface will be automatic selected.
785 //
786 UnspecifiedSrc = TRUE;
787 }
788
789 //
790 // Source address is required when pinging a link-local address.
791 //
792 if (NetIp6IsLinkLocalAddr (&Private->DstAddress) && UnspecifiedSrc) {
793 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_SOURCE), gShellNetwork2HiiHandle);
794 Status = EFI_INVALID_PARAMETER;
795 goto ON_ERROR;
796 }
797
798 //
799 // For each ip6 protocol, check interface addresses list.
800 //
801 for (HandleIndex = 0; HandleIndex < HandleNum; HandleIndex++) {
802
803 Ip6Sb = NULL;
804 IfInfo = NULL;
805 IfInfoSize = 0;
806
807 if (UnspecifiedSrc) {
808 //
809 // Check media.
810 //
811 NetLibDetectMediaWaitTimeout (HandleBuffer[HandleIndex], 0, &MediaStatus);
812 if (MediaStatus != EFI_SUCCESS) {
813 //
814 // Skip this one.
815 //
816 continue;
817 }
818 }
819
820 Status = gBS->HandleProtocol (
821 HandleBuffer[HandleIndex],
822 &gEfiIp6ServiceBindingProtocolGuid,
823 (VOID **) &Ip6Sb
824 );
825 if (EFI_ERROR (Status)) {
826 goto ON_ERROR;
827 }
828
829 //
830 // Ip6config protocol and ip6 service binding protocol are installed
831 // on the same handle.
832 //
833 Status = gBS->HandleProtocol (
834 HandleBuffer[HandleIndex],
835 &gEfiIp6ConfigProtocolGuid,
836 (VOID **) &Ip6Cfg
837 );
838
839 if (EFI_ERROR (Status)) {
840 goto ON_ERROR;
841 }
842 //
843 // Get the interface information size.
844 //
845 Status = Ip6Cfg->GetData (
846 Ip6Cfg,
847 Ip6ConfigDataTypeInterfaceInfo,
848 &IfInfoSize,
849 NULL
850 );
851
852 if (Status != EFI_BUFFER_TOO_SMALL) {
853 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6CFG_GETDATA), gShellNetwork2HiiHandle, Status);
854 goto ON_ERROR;
855 }
856
857 IfInfo = AllocateZeroPool (IfInfoSize);
858
859 if (IfInfo == NULL) {
860 Status = EFI_OUT_OF_RESOURCES;
861 goto ON_ERROR;
862 }
863 //
864 // Get the interface info.
865 //
866 Status = Ip6Cfg->GetData (
867 Ip6Cfg,
868 Ip6ConfigDataTypeInterfaceInfo,
869 &IfInfoSize,
870 IfInfo
871 );
872
873 if (EFI_ERROR (Status)) {
874 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6CFG_GETDATA), gShellNetwork2HiiHandle, Status);
875 goto ON_ERROR;
876 }
877 //
878 // Check whether the source address is one of the interface addresses.
879 //
880 for (AddrIndex = 0; AddrIndex < IfInfo->AddressInfoCount; AddrIndex++) {
881 Addr = &(IfInfo->AddressInfo[AddrIndex].Address);
882
883 if (UnspecifiedSrc) {
884 if (!NetIp6IsUnspecifiedAddr (Addr) && !NetIp6IsLinkLocalAddr (Addr)) {
885 //
886 // Select the interface automatically.
887 //
888 CopyMem(&Private->SrcAddress, Addr, sizeof(Private->SrcAddress));
889 break;
890 }
891 } else if (EFI_IP6_EQUAL (&Private->SrcAddress, Addr)) {
892 //
893 // Match a certain interface address.
894 //
895 break;
896 }
897 }
898
899 if (AddrIndex < IfInfo->AddressInfoCount) {
900 //
901 // Found a nic handle with right interface address.
902 //
903 break;
904 }
905
906 FreePool (IfInfo);
907 IfInfo = NULL;
908 }
909 //
910 // No exact interface address matched.
911 //
912
913 if (HandleIndex == HandleNum) {
914 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_CONFIGD_NIC_NF), gShellNetwork2HiiHandle);
915 Status = EFI_NOT_FOUND;
916 goto ON_ERROR;
917 }
918
919 Private->NicHandle = HandleBuffer[HandleIndex];
920
921 ASSERT (Ip6Sb != NULL);
922 Status = Ip6Sb->CreateChild (Ip6Sb, &Private->Ip6ChildHandle);
923
924 if (EFI_ERROR (Status)) {
925 goto ON_ERROR;
926 }
927
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
935 );
936 if (EFI_ERROR (Status)) {
937 goto ON_ERROR;
938 }
939
940 ZeroMem (&Ip6Config, sizeof (EFI_IP6_CONFIG_DATA));
941
942 //
943 // Configure the ip6 instance for icmp6 packet exchange.
944 //
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;
954
955 IP6_COPY_ADDRESS (&Ip6Config.StationAddress, &Private->SrcAddress);
956
957 IP6_COPY_ADDRESS (&Ip6Config.DestinationAddress, &Private->DstAddress);
958
959 Status = Private->Ip6->Configure (Private->Ip6, &Ip6Config);
960
961 if (EFI_ERROR (Status)) {
962 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6_CONFIG), gShellNetwork2HiiHandle, Status);
963 goto ON_ERROR;
964 }
965
966 return EFI_SUCCESS;
967
968 ON_ERROR:
969 if (HandleBuffer != NULL) {
970 FreePool (HandleBuffer);
971 }
972
973 if (IfInfo != NULL) {
974 FreePool (IfInfo);
975 }
976
977 if ((Ip6Sb != NULL) && (Private->Ip6ChildHandle != NULL)) {
978 Ip6Sb->DestroyChild (Ip6Sb, Private->Ip6ChildHandle);
979 }
980
981 return Status;
982 }
983
984 /**
985 Destroy the IP6 instance.
986
987 @param[in] Private The pointer of PING6_PRIVATE_DATA.
988
989 **/
990 VOID
991 Ping6DestroyIpInstance (
992 IN PING6_PRIVATE_DATA *Private
993 )
994 {
995 EFI_STATUS Status;
996 EFI_SERVICE_BINDING_PROTOCOL *Ip6Sb;
997
998 gBS->CloseProtocol (
999 Private->Ip6ChildHandle,
1000 &gEfiIp6ProtocolGuid,
1001 Private->ImageHandle,
1002 Private->Ip6ChildHandle
1003 );
1004
1005 Status = gBS->HandleProtocol (
1006 Private->NicHandle,
1007 &gEfiIp6ServiceBindingProtocolGuid,
1008 (VOID **) &Ip6Sb
1009 );
1010
1011 if (!EFI_ERROR(Status)) {
1012 Ip6Sb->DestroyChild (Ip6Sb, Private->Ip6ChildHandle);
1013 }
1014 }
1015
1016 /**
1017 The Ping6 Process.
1018
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.
1024
1025 @retval SHELL_SUCCESS The ping6 processed successfullly.
1026 @retval others The ping6 processed unsuccessfully.
1027
1028 **/
1029 SHELL_STATUS
1030 ShellPing6 (
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
1036 )
1037 {
1038 EFI_STATUS Status;
1039 EFI_INPUT_KEY Key;
1040 PING6_PRIVATE_DATA *Private;
1041 PING6_ICMP6_TX_INFO *TxInfo;
1042 LIST_ENTRY *Entry;
1043 LIST_ENTRY *NextEntry;
1044 SHELL_STATUS ShellStatus;
1045
1046 ShellStatus = SHELL_SUCCESS;
1047 Private = AllocateZeroPool (sizeof (PING6_PRIVATE_DATA));
1048
1049 if (Private == NULL) {
1050 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_OUT_MEM), gShellNetwork2HiiHandle, L"Ping6");
1051 ShellStatus = SHELL_OUT_OF_RESOURCES;
1052 goto ON_EXIT;
1053 }
1054
1055 Private->ImageHandle = ImageHandle;
1056 Private->SendNum = SendNumber;
1057 Private->BufferSize = BufferSize;
1058 Private->RttMin = ~((UINT64 )(0x0));
1059 Private->Status = EFI_NOT_READY;
1060
1061 InitializeListHead (&Private->TxList);
1062
1063 IP6_COPY_ADDRESS (&Private->SrcAddress, SrcAddress);
1064 IP6_COPY_ADDRESS (&Private->DstAddress, DstAddress);
1065
1066 //
1067 // Open and configure a ip6 instance for ping6.
1068 //
1069 Status = Ping6CreateIpInstance (Private);
1070
1071 if (EFI_ERROR (Status)) {
1072 ShellStatus = SHELL_ACCESS_DENIED;
1073 goto ON_EXIT;
1074 }
1075 //
1076 // Print the command line itself.
1077 //
1078 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_START), gShellNetwork2HiiHandle, mIp6DstString, Private->BufferSize);
1079 //
1080 // Create a ipv6 token to receive the first icmp6 echo reply packet.
1081 //
1082 Status = Ping6OnReceiveEchoReply (Private);
1083
1084 if (EFI_ERROR (Status)) {
1085 ShellStatus = SHELL_ACCESS_DENIED;
1086 goto ON_EXIT;
1087 }
1088 //
1089 // Create and start timer to send icmp6 echo request packet per second.
1090 //
1091 Status = gBS->CreateEvent (
1092 EVT_TIMER | EVT_NOTIFY_SIGNAL,
1093 TPL_CALLBACK,
1094 Ping6OnTimerRoutine6,
1095 Private,
1096 &Private->Timer
1097 );
1098
1099 if (EFI_ERROR (Status)) {
1100 ShellStatus = SHELL_ACCESS_DENIED;
1101 goto ON_EXIT;
1102 }
1103
1104 //
1105 // Start a timer to calculate the RTT.
1106 //
1107 Status = Ping6InitRttTimer (Private);
1108 if (EFI_ERROR (Status)) {
1109 ShellStatus = SHELL_ACCESS_DENIED;
1110 goto ON_EXIT;
1111 }
1112
1113 //
1114 // Create a ipv6 token to send the first icmp6 echo request packet.
1115 //
1116 Status = Ping6SendEchoRequest (Private);
1117 //
1118 // EFI_NOT_READY for IPsec is enable and IKE is not established.
1119 //
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);
1124 }
1125
1126 goto ON_EXIT;
1127 }
1128
1129 Status = gBS->SetTimer (
1130 Private->Timer,
1131 TimerPeriodic,
1132 PING6_ONE_SECOND
1133 );
1134
1135 if (EFI_ERROR (Status)) {
1136 ShellStatus = SHELL_ACCESS_DENIED;
1137 goto ON_EXIT;
1138 }
1139 //
1140 // Control the ping6 process by two factors:
1141 // 1. Hot key
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.
1146 //
1147 while (Private->Status == EFI_NOT_READY) {
1148 Private->Ip6->Poll (Private->Ip6);
1149
1150 //
1151 // Terminate the ping6 process by 'esc' or 'ctl-c'.
1152 //
1153 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
1154
1155 if (!EFI_ERROR(Status)) {
1156 if ((Key.UnicodeChar == 0x1b) || (Key.UnicodeChar == 0x03) ||
1157 ((Key.UnicodeChar == 0) && (Key.ScanCode == SCAN_ESC))) {
1158 goto ON_STAT;
1159 }
1160 }
1161 }
1162
1163 ON_STAT:
1164 //
1165 // Display the statistics in all.
1166 //
1167 gBS->SetTimer (Private->Timer, TimerCancel, 0);
1168
1169 if (Private->TxCount != 0) {
1170 ShellPrintHiiEx (
1171 -1,
1172 -1,
1173 NULL,
1174 STRING_TOKEN (STR_PING6_STAT),
1175 gShellNetwork2HiiHandle,
1176 Private->TxCount,
1177 Private->RxCount,
1178 (100 * (Private->TxCount - Private->RxCount)) / Private->TxCount,
1179 Private->RttSum
1180 );
1181 }
1182
1183 if (Private->RxCount != 0) {
1184 ShellPrintHiiEx (
1185 -1,
1186 -1,
1187 NULL,
1188 STRING_TOKEN (STR_PING6_RTT),
1189 gShellNetwork2HiiHandle,
1190 Private->RttMin,
1191 Private->RttMin + Private->TimerPeriod,
1192 Private->RttMax,
1193 Private->RttMax + Private->TimerPeriod,
1194 DivU64x64Remainder (Private->RttSum, Private->RxCount, NULL),
1195 DivU64x64Remainder (Private->RttSum, Private->RxCount, NULL) + Private->TimerPeriod
1196 );
1197 }
1198
1199 ON_EXIT:
1200
1201 if (Private != NULL) {
1202 Private->Status = EFI_ABORTED;
1203
1204 NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) {
1205 TxInfo = BASE_CR (Entry, PING6_ICMP6_TX_INFO, Link);
1206
1207 Status = Private->Ip6->Cancel (Private->Ip6, TxInfo->Token);
1208
1209 RemoveEntryList (&TxInfo->Link);
1210 Ping6DestroyTxInfo (TxInfo);
1211 }
1212
1213 Ping6FreeRttTimer (Private);
1214
1215 if (Private->Timer != NULL) {
1216 gBS->CloseEvent (Private->Timer);
1217 }
1218
1219 if (Private->Ip6 != NULL) {
1220 Status = Private->Ip6->Cancel (Private->Ip6, &Private->RxToken);
1221 }
1222
1223 if (Private->RxToken.Event != NULL) {
1224 gBS->CloseEvent (Private->RxToken.Event);
1225 }
1226
1227 if (Private->Ip6ChildHandle != NULL) {
1228 Ping6DestroyIpInstance (Private);
1229 }
1230
1231 FreePool (Private);
1232 }
1233
1234 return ShellStatus;
1235 }
1236
1237 /**
1238 Function for 'ping6' command.
1239
1240 @param[in] ImageHandle Handle to the Image (NULL if Internal).
1241 @param[in] SystemTable Pointer to the System Table (NULL if Internal).
1242
1243 @retval SHELL_SUCCESS The ping6 processed successfullly.
1244 @retval others The ping6 processed unsuccessfully.
1245
1246 **/
1247 SHELL_STATUS
1248 EFIAPI
1249 ShellCommandRunPing6 (
1250 IN EFI_HANDLE ImageHandle,
1251 IN EFI_SYSTEM_TABLE *SystemTable
1252 )
1253 {
1254 EFI_STATUS Status;
1255 SHELL_STATUS ShellStatus;
1256 EFI_IPv6_ADDRESS DstAddress;
1257 EFI_IPv6_ADDRESS SrcAddress;
1258 UINT64 BufferSize;
1259 UINTN SendNumber;
1260 LIST_ENTRY *ParamPackage;
1261 CONST CHAR16 *ValueStr;
1262 CONST CHAR16 *ValueStrPtr;
1263 UINTN NonOptionCount;
1264 CHAR16 *ProblemParam;
1265
1266 ProblemParam = NULL;
1267 ShellStatus = SHELL_SUCCESS;
1268
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;
1273 goto ON_EXIT;
1274 }
1275
1276 SendNumber = 10;
1277 BufferSize = 16;
1278
1279 //
1280 // Parse the parameter of count number.
1281 //
1282 ValueStr = ShellCommandLineGetValue (ParamPackage, L"-n");
1283 ValueStrPtr = ValueStr;
1284 if (ValueStr != NULL) {
1285 SendNumber = ShellStrToUintn (ValueStrPtr);
1286
1287 //
1288 // ShellStrToUintn will return 0 when input is 0 or an invalid input string.
1289 //
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;
1293 goto ON_EXIT;
1294 }
1295 }
1296 //
1297 // Parse the parameter of buffer size.
1298 //
1299 ValueStr = ShellCommandLineGetValue (ParamPackage, L"-l");
1300 ValueStrPtr = ValueStr;
1301 if (ValueStr != NULL) {
1302 BufferSize = ShellStrToUintn (ValueStrPtr);
1303
1304 //
1305 // ShellStrToUintn will return 0 when input is 0 or an invalid input string.
1306 //
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;
1310 goto ON_EXIT;
1311 }
1312 }
1313
1314 ZeroMem (&SrcAddress, sizeof (EFI_IPv6_ADDRESS));
1315 ZeroMem (&DstAddress, sizeof (EFI_IPv6_ADDRESS));
1316
1317 //
1318 // Parse the parameter of source ip address.
1319 //
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;
1328 goto ON_EXIT;
1329 }
1330 }
1331 //
1332 // Parse the parameter of destination ip address.
1333 //
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;
1339 goto ON_EXIT;
1340 }
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;
1348 goto ON_EXIT;
1349 }
1350 }
1351
1352 //
1353 // Enter into ping6 process.
1354 //
1355 ShellStatus = ShellPing6 (
1356 ImageHandle,
1357 (UINT32)SendNumber,
1358 (UINT32)BufferSize,
1359 &SrcAddress,
1360 &DstAddress
1361 );
1362
1363 ON_EXIT:
1364 ShellCommandLineFreeVarList (ParamPackage);
1365 return ShellStatus;
1366 }
1367