]> 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 SHELL_PARAM_ITEM Ping6ParamList[] = {
67 {
68 L"-l",
69 TypeValue
70 },
71 {
72 L"-n",
73 TypeValue
74 },
75 {
76 L"-s",
77 TypeValue
78 },
79 {
80 L"-?",
81 TypeFlag
82 },
83 {
84 NULL,
85 TypeMax
86 },
87 };
88
89 //
90 // Global Variables in Ping6 application.
91 //
92 CONST CHAR16 *mIp6DstString;
93 CONST CHAR16 *mIp6SrcString;
94 EFI_CPU_ARCH_PROTOCOL *Cpu = NULL;
95
96 /**
97 RTT timer tick routine.
98
99 @param[in] Event A EFI_EVENT type event.
100 @param[in] Context The pointer to Context.
101
102 **/
103 VOID
104 EFIAPI
105 Ping6RttTimerTickRoutine (
106 IN EFI_EVENT Event,
107 IN VOID *Context
108 )
109 {
110 UINT32 *RttTimerTick;
111
112 RttTimerTick = (UINT32 *)Context;
113 (*RttTimerTick)++;
114 }
115
116 /**
117 Get the timer period of the system.
118
119 This function tries to get the system timer period by creating
120 an 1ms period timer.
121
122 @return System timer period in MS, or 0 if operation failed.
123
124 **/
125 UINT32
126 Ping6GetTimerPeriod (
127 VOID
128 )
129 {
130 EFI_STATUS Status;
131 UINT32 RttTimerTick;
132 EFI_EVENT TimerEvent;
133 UINT32 StallCounter;
134 EFI_TPL OldTpl;
135
136 RttTimerTick = 0;
137 StallCounter = 0;
138
139 Status = gBS->CreateEvent (
140 EVT_TIMER | EVT_NOTIFY_SIGNAL,
141 TPL_NOTIFY,
142 Ping6RttTimerTickRoutine,
143 &RttTimerTick,
144 &TimerEvent
145 );
146 if (EFI_ERROR (Status)) {
147 return 0;
148 }
149
150 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
151 Status = gBS->SetTimer (
152 TimerEvent,
153 TimerPeriodic,
154 TICKS_PER_MS
155 );
156 if (EFI_ERROR (Status)) {
157 gBS->CloseEvent (TimerEvent);
158 return 0;
159 }
160
161 while (RttTimerTick < 10) {
162 gBS->Stall (STALL_1_MILLI_SECOND);
163 ++StallCounter;
164 }
165
166 gBS->RestoreTPL (OldTpl);
167
168 gBS->SetTimer (TimerEvent, TimerCancel, 0);
169 gBS->CloseEvent (TimerEvent);
170
171 return StallCounter / RttTimerTick;
172 }
173
174 /**
175 Initialize the timer event for RTT (round trip time).
176
177 @param[in] Private The pointer to PING6_PRIVATE_DATA.
178
179 @retval EFI_SUCCESS RTT timer is started.
180 @retval Others Failed to start the RTT timer.
181
182 **/
183 EFI_STATUS
184 Ping6InitRttTimer (
185 IN PING6_PRIVATE_DATA *Private
186 )
187 {
188 EFI_STATUS Status;
189
190 Private->TimerPeriod = Ping6GetTimerPeriod ();
191 if (Private->TimerPeriod == 0) {
192 return EFI_ABORTED;
193 }
194
195 Private->RttTimerTick = 0;
196 Status = gBS->CreateEvent (
197 EVT_TIMER | EVT_NOTIFY_SIGNAL,
198 TPL_NOTIFY,
199 Ping6RttTimerTickRoutine,
200 &Private->RttTimerTick,
201 &Private->RttTimer
202 );
203 if (EFI_ERROR (Status)) {
204 return Status;
205 }
206
207 Status = gBS->SetTimer (
208 Private->RttTimer,
209 TimerPeriodic,
210 TICKS_PER_MS
211 );
212 if (EFI_ERROR (Status)) {
213 gBS->CloseEvent (Private->RttTimer);
214 return Status;
215 }
216
217 return EFI_SUCCESS;
218 }
219
220 /**
221 Free RTT timer event resource.
222
223 @param[in] Private The pointer to PING6_PRIVATE_DATA.
224
225 **/
226 VOID
227 Ping6FreeRttTimer (
228 IN PING6_PRIVATE_DATA *Private
229 )
230 {
231 if (Private->RttTimer != NULL) {
232 gBS->SetTimer (Private->RttTimer, TimerCancel, 0);
233 gBS->CloseEvent (Private->RttTimer);
234 }
235 }
236
237 /**
238 Read the current time.
239
240 @param[in] Private The pointer to PING6_PRIVATE_DATA.
241
242 @retval the current tick value.
243 **/
244 UINT32
245 Ping6ReadTime (
246 IN PING6_PRIVATE_DATA *Private
247 )
248 {
249 return Private->RttTimerTick;
250 }
251
252 /**
253 Get and calculate the duration in ms.
254
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.
258
259 @return The duration in ms.
260
261 **/
262 UINT32
263 Ping6CalculateTick (
264 IN PING6_PRIVATE_DATA *Private,
265 IN UINT32 Begin,
266 IN UINT32 End
267 )
268 {
269 if (End < Begin) {
270 return (0);
271 }
272
273 return (End - Begin) * Private->TimerPeriod;
274 }
275
276 /**
277 Destroy IPING6_ICMP6_TX_INFO, and recollect the memory.
278
279 @param[in] TxInfo The pointer to PING6_ICMP6_TX_INFO.
280
281 **/
282 VOID
283 Ping6DestroyTxInfo (
284 IN PING6_ICMP6_TX_INFO *TxInfo
285 )
286 {
287 EFI_IP6_TRANSMIT_DATA *TxData;
288 EFI_IP6_FRAGMENT_DATA *FragData;
289 UINTN Index;
290
291 ASSERT (TxInfo != NULL);
292
293 if (TxInfo->Token != NULL) {
294 if (TxInfo->Token->Event != NULL) {
295 gBS->CloseEvent (TxInfo->Token->Event);
296 }
297
298 TxData = TxInfo->Token->Packet.TxData;
299 if (TxData != NULL) {
300 if (TxData->OverrideData != NULL) {
301 FreePool (TxData->OverrideData);
302 }
303
304 if (TxData->ExtHdrs != NULL) {
305 FreePool (TxData->ExtHdrs);
306 }
307
308 for (Index = 0; Index < TxData->FragmentCount; Index++) {
309 FragData = TxData->FragmentTable[Index].FragmentBuffer;
310 if (FragData != NULL) {
311 FreePool (FragData);
312 }
313 }
314 }
315
316 FreePool (TxInfo->Token);
317 }
318
319 FreePool (TxInfo);
320 }
321
322 /**
323 Match the request, and reply with SequenceNum/TimeStamp.
324
325 @param[in] Private The pointer to PING6_PRIVATE_DATA.
326 @param[in] Packet The pointer to ICMP6_ECHO_REQUEST_REPLY.
327
328 @retval EFI_SUCCESS The match is successful.
329 @retval EFI_NOT_FOUND The reply can't be matched with any request.
330
331 **/
332 EFI_STATUS
333 Ping6OnMatchEchoReply (
334 IN PING6_PRIVATE_DATA *Private,
335 IN ICMP6_ECHO_REQUEST_REPLY *Packet
336 )
337 {
338 PING6_ICMP6_TX_INFO *TxInfo;
339 LIST_ENTRY *Entry;
340 LIST_ENTRY *NextEntry;
341
342 NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) {
343 TxInfo = BASE_CR (Entry, PING6_ICMP6_TX_INFO, Link);
344
345 if ((TxInfo->SequenceNum == Packet->SequenceNum) && (TxInfo->TimeStamp == Packet->TimeStamp)) {
346 Private->RxCount++;
347 RemoveEntryList (&TxInfo->Link);
348 Ping6DestroyTxInfo (TxInfo);
349 return EFI_SUCCESS;
350 }
351 }
352
353 return EFI_NOT_FOUND;
354 }
355
356 /**
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.
361
362 @param[in] Event A EFI_EVENT type event.
363 @param[in] Context The pointer to Context.
364
365 **/
366 VOID
367 EFIAPI
368 Ping6OnEchoRequestSent6 (
369 IN EFI_EVENT Event,
370 IN VOID *Context
371 )
372 {
373 }
374
375 /**
376 receive reply, match and print reply infomation.
377
378 @param[in] Event A EFI_EVENT type event.
379 @param[in] Context The pointer to context.
380
381 **/
382 VOID
383 EFIAPI
384 Ping6OnEchoReplyReceived6 (
385 IN EFI_EVENT Event,
386 IN VOID *Context
387 )
388 {
389 EFI_STATUS Status;
390 PING6_PRIVATE_DATA *Private;
391 EFI_IP6_COMPLETION_TOKEN *RxToken;
392 EFI_IP6_RECEIVE_DATA *RxData;
393 ICMP6_ECHO_REQUEST_REPLY *Reply;
394 UINT32 PayLoad;
395 UINT32 Rtt;
396
397 Private = (PING6_PRIVATE_DATA *)Context;
398
399 if (Private->Status == EFI_ABORTED) {
400 return;
401 }
402
403 RxToken = &Private->RxToken;
404 RxData = RxToken->Packet.RxData;
405 Reply = RxData->FragmentTable[0].FragmentBuffer;
406 PayLoad = RxData->DataLength;
407
408 if (RxData->Header->NextHeader != IP6_ICMP) {
409 goto ON_EXIT;
410 }
411
412 if (!IP6_IS_MULTICAST (&Private->DstAddress) &&
413 !EFI_IP6_EQUAL (&RxData->Header->SourceAddress, &Private->DstAddress))
414 {
415 goto ON_EXIT;
416 }
417
418 if ((Reply->Type != ICMP_V6_ECHO_REPLY) || (Reply->Code != 0)) {
419 goto ON_EXIT;
420 }
421
422 if (PayLoad != Private->BufferSize) {
423 goto ON_EXIT;
424 }
425
426 //
427 // Check whether the reply matches the sent request before.
428 //
429 Status = Ping6OnMatchEchoReply (Private, Reply);
430 if (EFI_ERROR (Status)) {
431 goto ON_EXIT;
432 }
433
434 //
435 // Display statistics on this icmp6 echo reply packet.
436 //
437 Rtt = Ping6CalculateTick (Private, Reply->TimeStamp, Ping6ReadTime (Private));
438
439 Private->RttSum += Rtt;
440 Private->RttMin = Private->RttMin > Rtt ? Rtt : Private->RttMin;
441 Private->RttMax = Private->RttMax < Rtt ? Rtt : Private->RttMax;
442
443 ShellPrintHiiEx (
444 -1,
445 -1,
446 NULL,
447 STRING_TOKEN (STR_PING6_REPLY_INFO),
448 gShellNetwork2HiiHandle,
449 PayLoad,
450 mIp6DstString,
451 Reply->SequenceNum,
452 RxData->Header->HopLimit,
453 Rtt,
454 Rtt + Private->TimerPeriod
455 );
456
457 ON_EXIT:
458
459 if (Private->RxCount < Private->SendNum) {
460 //
461 // Continue to receive icmp6 echo reply packets.
462 //
463 RxToken->Status = EFI_ABORTED;
464
465 Status = Private->Ip6->Receive (Private->Ip6, RxToken);
466
467 if (EFI_ERROR (Status)) {
468 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6_RECEIVE), gShellNetwork2HiiHandle, Status);
469 Private->Status = EFI_ABORTED;
470 }
471 } else {
472 //
473 // All reply have already been received from the dest host.
474 //
475 Private->Status = EFI_SUCCESS;
476 }
477
478 //
479 // Singal to recycle the each rxdata here, not at the end of process.
480 //
481 gBS->SignalEvent (RxData->RecycleSignal);
482 }
483
484 /**
485 Initial EFI_IP6_COMPLETION_TOKEN.
486
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.
490
491 @return The pointer of EFI_IP6_COMPLETION_TOKEN.
492
493 **/
494 EFI_IP6_COMPLETION_TOKEN *
495 Ping6GenerateToken (
496 IN PING6_PRIVATE_DATA *Private,
497 IN UINT32 TimeStamp,
498 IN UINT16 SequenceNum
499 )
500 {
501 EFI_STATUS Status;
502 EFI_IP6_COMPLETION_TOKEN *Token;
503 EFI_IP6_TRANSMIT_DATA *TxData;
504 ICMP6_ECHO_REQUEST_REPLY *Request;
505
506 Request = AllocateZeroPool (Private->BufferSize);
507
508 if (Request == NULL) {
509 return NULL;
510 }
511
512 //
513 // Assembly icmp6 echo request packet.
514 //
515 Request->Type = ICMP_V6_ECHO_REQUEST;
516 Request->Code = 0;
517 Request->SequenceNum = SequenceNum;
518 Request->TimeStamp = TimeStamp;
519 Request->Identifier = 0;
520 //
521 // Leave check sum to ip6 layer, since it has no idea of source address
522 // selection.
523 //
524 Request->Checksum = 0;
525
526 TxData = AllocateZeroPool (sizeof (EFI_IP6_TRANSMIT_DATA));
527
528 if (TxData == NULL) {
529 FreePool (Request);
530 return NULL;
531 }
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
661 return Status;
662 }
663
664 /**
665 Remove the timeout request from the list.
666
667 @param[in] Event A EFI_EVENT type event.
668 @param[in] Context The pointer to Context.
669
670 **/
671 VOID
672 EFIAPI
673 Ping6OnTimerRoutine6 (
674 IN EFI_EVENT Event,
675 IN VOID *Context
676 )
677 {
678 EFI_STATUS Status;
679 PING6_PRIVATE_DATA *Private;
680 PING6_ICMP6_TX_INFO *TxInfo;
681 LIST_ENTRY *Entry;
682 LIST_ENTRY *NextEntry;
683 UINT64 Time;
684
685 Private = (PING6_PRIVATE_DATA *)Context;
686
687 //
688 // Retransmit icmp6 echo request packets per second in sendnumber times.
689 //
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);
695 }
696 }
697 }
698
699 //
700 // Check whether any icmp6 echo request in the list timeout.
701 //
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));
705
706 //
707 // Remove the timeout echo request from txlist.
708 //
709 if (Time > PING6_DEFAULT_TIMEOUT) {
710 if (EFI_ERROR (TxInfo->Token->Status)) {
711 Private->Ip6->Cancel (Private->Ip6, TxInfo->Token);
712 }
713
714 //
715 // Remove the timeout icmp6 echo request from list.
716 //
717 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_TIMEOUT), gShellNetwork2HiiHandle, TxInfo->SequenceNum);
718
719 RemoveEntryList (&TxInfo->Link);
720 Ping6DestroyTxInfo (TxInfo);
721
722 if (IsListEmpty (&Private->TxList) && (Private->TxCount == Private->SendNum)) {
723 //
724 // All the left icmp6 echo request in the list timeout.
725 //
726 Private->Status = EFI_TIMEOUT;
727 }
728 }
729 }
730 }
731
732 /**
733 Create a valid IP6 instance.
734
735 @param[in] Private The pointer of PING6_PRIVATE_DATA.
736
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.
742 **/
743 EFI_STATUS
744 Ping6CreateIpInstance (
745 IN PING6_PRIVATE_DATA *Private
746 )
747 {
748 EFI_STATUS Status;
749 UINTN HandleIndex;
750 UINTN HandleNum;
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;
758 UINTN IfInfoSize;
759 EFI_IPv6_ADDRESS *Addr;
760 UINTN AddrIndex;
761
762 HandleBuffer = NULL;
763 UnspecifiedSrc = FALSE;
764 MediaStatus = EFI_SUCCESS;
765 Ip6Sb = NULL;
766 IfInfo = NULL;
767 IfInfoSize = 0;
768
769 //
770 // Locate all the handles with ip6 service binding protocol.
771 //
772 Status = gBS->LocateHandleBuffer (
773 ByProtocol,
774 &gEfiIp6ServiceBindingProtocolGuid,
775 NULL,
776 &HandleNum,
777 &HandleBuffer
778 );
779 if (EFI_ERROR (Status) || (HandleNum == 0)) {
780 return EFI_ABORTED;
781 }
782
783 if (NetIp6IsUnspecifiedAddr (&Private->SrcAddress)) {
784 //
785 // SrcAddress is unspecified. So, both connected and configured interface will be automatic selected.
786 //
787 UnspecifiedSrc = TRUE;
788 }
789
790 //
791 // Source address is required when pinging a link-local address.
792 //
793 if (NetIp6IsLinkLocalAddr (&Private->DstAddress) && UnspecifiedSrc) {
794 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_SOURCE), gShellNetwork2HiiHandle);
795 Status = EFI_INVALID_PARAMETER;
796 goto ON_ERROR;
797 }
798
799 //
800 // For each ip6 protocol, check interface addresses list.
801 //
802 for (HandleIndex = 0; HandleIndex < HandleNum; HandleIndex++) {
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 //
844 // Get the interface information size.
845 //
846 Status = Ip6Cfg->GetData (
847 Ip6Cfg,
848 Ip6ConfigDataTypeInterfaceInfo,
849 &IfInfoSize,
850 NULL
851 );
852
853 if (Status != EFI_BUFFER_TOO_SMALL) {
854 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6CFG_GETDATA), gShellNetwork2HiiHandle, Status);
855 goto ON_ERROR;
856 }
857
858 IfInfo = AllocateZeroPool (IfInfoSize);
859
860 if (IfInfo == NULL) {
861 Status = EFI_OUT_OF_RESOURCES;
862 goto ON_ERROR;
863 }
864
865 //
866 // Get the interface info.
867 //
868 Status = Ip6Cfg->GetData (
869 Ip6Cfg,
870 Ip6ConfigDataTypeInterfaceInfo,
871 &IfInfoSize,
872 IfInfo
873 );
874
875 if (EFI_ERROR (Status)) {
876 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6CFG_GETDATA), gShellNetwork2HiiHandle, Status);
877 goto ON_ERROR;
878 }
879
880 //
881 // Check whether the source address is one of the interface addresses.
882 //
883 for (AddrIndex = 0; AddrIndex < IfInfo->AddressInfoCount; AddrIndex++) {
884 Addr = &(IfInfo->AddressInfo[AddrIndex].Address);
885
886 if (UnspecifiedSrc) {
887 if (!NetIp6IsUnspecifiedAddr (Addr) && !NetIp6IsLinkLocalAddr (Addr)) {
888 //
889 // Select the interface automatically.
890 //
891 CopyMem (&Private->SrcAddress, Addr, sizeof (Private->SrcAddress));
892 break;
893 }
894 } else if (EFI_IP6_EQUAL (&Private->SrcAddress, Addr)) {
895 //
896 // Match a certain interface address.
897 //
898 break;
899 }
900 }
901
902 if (AddrIndex < IfInfo->AddressInfoCount) {
903 //
904 // Found a nic handle with right interface address.
905 //
906 break;
907 }
908
909 FreePool (IfInfo);
910 IfInfo = NULL;
911 }
912
913 //
914 // No exact interface address matched.
915 //
916
917 if (HandleIndex == HandleNum) {
918 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_CONFIGD_NIC_NF), gShellNetwork2HiiHandle);
919 Status = EFI_NOT_FOUND;
920 goto ON_ERROR;
921 }
922
923 Private->NicHandle = HandleBuffer[HandleIndex];
924
925 ASSERT (Ip6Sb != NULL);
926 Status = Ip6Sb->CreateChild (Ip6Sb, &Private->Ip6ChildHandle);
927
928 if (EFI_ERROR (Status)) {
929 goto ON_ERROR;
930 }
931
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
939 );
940 if (EFI_ERROR (Status)) {
941 goto ON_ERROR;
942 }
943
944 ZeroMem (&Ip6Config, sizeof (EFI_IP6_CONFIG_DATA));
945
946 //
947 // Configure the ip6 instance for icmp6 packet exchange.
948 //
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;
958
959 IP6_COPY_ADDRESS (&Ip6Config.StationAddress, &Private->SrcAddress);
960
961 IP6_COPY_ADDRESS (&Ip6Config.DestinationAddress, &Private->DstAddress);
962
963 Status = Private->Ip6->Configure (Private->Ip6, &Ip6Config);
964
965 if (EFI_ERROR (Status)) {
966 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6_CONFIG), gShellNetwork2HiiHandle, Status);
967 goto ON_ERROR;
968 }
969
970 return EFI_SUCCESS;
971
972 ON_ERROR:
973 if (HandleBuffer != NULL) {
974 FreePool (HandleBuffer);
975 }
976
977 if (IfInfo != NULL) {
978 FreePool (IfInfo);
979 }
980
981 if ((Ip6Sb != NULL) && (Private->Ip6ChildHandle != NULL)) {
982 Ip6Sb->DestroyChild (Ip6Sb, Private->Ip6ChildHandle);
983 }
984
985 return Status;
986 }
987
988 /**
989 Destroy the IP6 instance.
990
991 @param[in] Private The pointer of PING6_PRIVATE_DATA.
992
993 **/
994 VOID
995 Ping6DestroyIpInstance (
996 IN PING6_PRIVATE_DATA *Private
997 )
998 {
999 EFI_STATUS Status;
1000 EFI_SERVICE_BINDING_PROTOCOL *Ip6Sb;
1001
1002 gBS->CloseProtocol (
1003 Private->Ip6ChildHandle,
1004 &gEfiIp6ProtocolGuid,
1005 Private->ImageHandle,
1006 Private->Ip6ChildHandle
1007 );
1008
1009 Status = gBS->HandleProtocol (
1010 Private->NicHandle,
1011 &gEfiIp6ServiceBindingProtocolGuid,
1012 (VOID **)&Ip6Sb
1013 );
1014
1015 if (!EFI_ERROR (Status)) {
1016 Ip6Sb->DestroyChild (Ip6Sb, Private->Ip6ChildHandle);
1017 }
1018 }
1019
1020 /**
1021 The Ping6 Process.
1022
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.
1028
1029 @retval SHELL_SUCCESS The ping6 processed successfullly.
1030 @retval others The ping6 processed unsuccessfully.
1031
1032 **/
1033 SHELL_STATUS
1034 ShellPing6 (
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
1040 )
1041 {
1042 EFI_STATUS Status;
1043 EFI_INPUT_KEY Key;
1044 PING6_PRIVATE_DATA *Private;
1045 PING6_ICMP6_TX_INFO *TxInfo;
1046 LIST_ENTRY *Entry;
1047 LIST_ENTRY *NextEntry;
1048 SHELL_STATUS ShellStatus;
1049
1050 ShellStatus = SHELL_SUCCESS;
1051 Private = AllocateZeroPool (sizeof (PING6_PRIVATE_DATA));
1052
1053 if (Private == NULL) {
1054 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_OUT_MEM), gShellNetwork2HiiHandle, L"Ping6");
1055 ShellStatus = SHELL_OUT_OF_RESOURCES;
1056 goto ON_EXIT;
1057 }
1058
1059 Private->ImageHandle = ImageHandle;
1060 Private->SendNum = SendNumber;
1061 Private->BufferSize = BufferSize;
1062 Private->RttMin = ~((UINT64)(0x0));
1063 Private->Status = EFI_NOT_READY;
1064
1065 InitializeListHead (&Private->TxList);
1066
1067 IP6_COPY_ADDRESS (&Private->SrcAddress, SrcAddress);
1068 IP6_COPY_ADDRESS (&Private->DstAddress, DstAddress);
1069
1070 //
1071 // Open and configure a ip6 instance for ping6.
1072 //
1073 Status = Ping6CreateIpInstance (Private);
1074
1075 if (EFI_ERROR (Status)) {
1076 ShellStatus = SHELL_ACCESS_DENIED;
1077 goto ON_EXIT;
1078 }
1079
1080 //
1081 // Print the command line itself.
1082 //
1083 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_START), gShellNetwork2HiiHandle, mIp6DstString, Private->BufferSize);
1084 //
1085 // Create a ipv6 token to receive the first icmp6 echo reply packet.
1086 //
1087 Status = Ping6OnReceiveEchoReply (Private);
1088
1089 if (EFI_ERROR (Status)) {
1090 ShellStatus = SHELL_ACCESS_DENIED;
1091 goto ON_EXIT;
1092 }
1093
1094 //
1095 // Create and start timer to send icmp6 echo request packet per second.
1096 //
1097 Status = gBS->CreateEvent (
1098 EVT_TIMER | EVT_NOTIFY_SIGNAL,
1099 TPL_CALLBACK,
1100 Ping6OnTimerRoutine6,
1101 Private,
1102 &Private->Timer
1103 );
1104
1105 if (EFI_ERROR (Status)) {
1106 ShellStatus = SHELL_ACCESS_DENIED;
1107 goto ON_EXIT;
1108 }
1109
1110 //
1111 // Start a timer to calculate the RTT.
1112 //
1113 Status = Ping6InitRttTimer (Private);
1114 if (EFI_ERROR (Status)) {
1115 ShellStatus = SHELL_ACCESS_DENIED;
1116 goto ON_EXIT;
1117 }
1118
1119 //
1120 // Create a ipv6 token to send the first icmp6 echo request packet.
1121 //
1122 Status = Ping6SendEchoRequest (Private);
1123 //
1124 // EFI_NOT_READY for IPsec is enable and IKE is not established.
1125 //
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);
1130 }
1131
1132 goto ON_EXIT;
1133 }
1134
1135 Status = gBS->SetTimer (
1136 Private->Timer,
1137 TimerPeriodic,
1138 PING6_ONE_SECOND
1139 );
1140
1141 if (EFI_ERROR (Status)) {
1142 ShellStatus = SHELL_ACCESS_DENIED;
1143 goto ON_EXIT;
1144 }
1145
1146 //
1147 // Control the ping6 process by two factors:
1148 // 1. Hot key
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.
1153 //
1154 while (Private->Status == EFI_NOT_READY) {
1155 Private->Ip6->Poll (Private->Ip6);
1156
1157 //
1158 // Terminate the ping6 process by 'esc' or 'ctl-c'.
1159 //
1160 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
1161
1162 if (!EFI_ERROR (Status)) {
1163 if ((Key.UnicodeChar == 0x1b) || (Key.UnicodeChar == 0x03) ||
1164 ((Key.UnicodeChar == 0) && (Key.ScanCode == SCAN_ESC)))
1165 {
1166 goto ON_STAT;
1167 }
1168 }
1169 }
1170
1171 ON_STAT:
1172 //
1173 // Display the statistics in all.
1174 //
1175 gBS->SetTimer (Private->Timer, TimerCancel, 0);
1176
1177 if (Private->TxCount != 0) {
1178 ShellPrintHiiEx (
1179 -1,
1180 -1,
1181 NULL,
1182 STRING_TOKEN (STR_PING6_STAT),
1183 gShellNetwork2HiiHandle,
1184 Private->TxCount,
1185 Private->RxCount,
1186 (100 * (Private->TxCount - Private->RxCount)) / Private->TxCount,
1187 Private->RttSum
1188 );
1189 }
1190
1191 if (Private->RxCount != 0) {
1192 ShellPrintHiiEx (
1193 -1,
1194 -1,
1195 NULL,
1196 STRING_TOKEN (STR_PING6_RTT),
1197 gShellNetwork2HiiHandle,
1198 Private->RttMin,
1199 Private->RttMin + Private->TimerPeriod,
1200 Private->RttMax,
1201 Private->RttMax + Private->TimerPeriod,
1202 DivU64x64Remainder (Private->RttSum, Private->RxCount, NULL),
1203 DivU64x64Remainder (Private->RttSum, Private->RxCount, NULL) + Private->TimerPeriod
1204 );
1205 }
1206
1207 ON_EXIT:
1208
1209 if (Private != NULL) {
1210 Private->Status = EFI_ABORTED;
1211
1212 NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) {
1213 TxInfo = BASE_CR (Entry, PING6_ICMP6_TX_INFO, Link);
1214
1215 Status = Private->Ip6->Cancel (Private->Ip6, TxInfo->Token);
1216
1217 RemoveEntryList (&TxInfo->Link);
1218 Ping6DestroyTxInfo (TxInfo);
1219 }
1220
1221 Ping6FreeRttTimer (Private);
1222
1223 if (Private->Timer != NULL) {
1224 gBS->CloseEvent (Private->Timer);
1225 }
1226
1227 if (Private->Ip6 != NULL) {
1228 Status = Private->Ip6->Cancel (Private->Ip6, &Private->RxToken);
1229 }
1230
1231 if (Private->RxToken.Event != NULL) {
1232 gBS->CloseEvent (Private->RxToken.Event);
1233 }
1234
1235 if (Private->Ip6ChildHandle != NULL) {
1236 Ping6DestroyIpInstance (Private);
1237 }
1238
1239 FreePool (Private);
1240 }
1241
1242 return ShellStatus;
1243 }
1244
1245 /**
1246 Function for 'ping6' command.
1247
1248 @param[in] ImageHandle Handle to the Image (NULL if Internal).
1249 @param[in] SystemTable Pointer to the System Table (NULL if Internal).
1250
1251 @retval SHELL_SUCCESS The ping6 processed successfullly.
1252 @retval others The ping6 processed unsuccessfully.
1253
1254 **/
1255 SHELL_STATUS
1256 EFIAPI
1257 ShellCommandRunPing6 (
1258 IN EFI_HANDLE ImageHandle,
1259 IN EFI_SYSTEM_TABLE *SystemTable
1260 )
1261 {
1262 EFI_STATUS Status;
1263 SHELL_STATUS ShellStatus;
1264 EFI_IPv6_ADDRESS DstAddress;
1265 EFI_IPv6_ADDRESS SrcAddress;
1266 UINT64 BufferSize;
1267 UINTN SendNumber;
1268 LIST_ENTRY *ParamPackage;
1269 CONST CHAR16 *ValueStr;
1270 CONST CHAR16 *ValueStrPtr;
1271 UINTN NonOptionCount;
1272 CHAR16 *ProblemParam;
1273
1274 ProblemParam = NULL;
1275 ShellStatus = SHELL_SUCCESS;
1276
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;
1281 goto ON_EXIT;
1282 }
1283
1284 SendNumber = 10;
1285 BufferSize = 16;
1286
1287 //
1288 // Parse the parameter of count number.
1289 //
1290 ValueStr = ShellCommandLineGetValue (ParamPackage, L"-n");
1291 ValueStrPtr = ValueStr;
1292 if (ValueStr != NULL) {
1293 SendNumber = ShellStrToUintn (ValueStrPtr);
1294
1295 //
1296 // ShellStrToUintn will return 0 when input is 0 or an invalid input string.
1297 //
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;
1301 goto ON_EXIT;
1302 }
1303 }
1304
1305 //
1306 // Parse the parameter of buffer size.
1307 //
1308 ValueStr = ShellCommandLineGetValue (ParamPackage, L"-l");
1309 ValueStrPtr = ValueStr;
1310 if (ValueStr != NULL) {
1311 BufferSize = ShellStrToUintn (ValueStrPtr);
1312
1313 //
1314 // ShellStrToUintn will return 0 when input is 0 or an invalid input string.
1315 //
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;
1319 goto ON_EXIT;
1320 }
1321 }
1322
1323 ZeroMem (&SrcAddress, sizeof (EFI_IPv6_ADDRESS));
1324 ZeroMem (&DstAddress, sizeof (EFI_IPv6_ADDRESS));
1325
1326 //
1327 // Parse the parameter of source ip address.
1328 //
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;
1337 goto ON_EXIT;
1338 }
1339 }
1340
1341 //
1342 // Parse the parameter of destination ip address.
1343 //
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;
1349 goto ON_EXIT;
1350 }
1351
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;
1359 goto ON_EXIT;
1360 }
1361 }
1362
1363 //
1364 // Enter into ping6 process.
1365 //
1366 ShellStatus = ShellPing6 (
1367 ImageHandle,
1368 (UINT32)SendNumber,
1369 (UINT32)BufferSize,
1370 &SrcAddress,
1371 &DstAddress
1372 );
1373
1374 ON_EXIT:
1375 ShellCommandLineFreeVarList (ParamPackage);
1376 return ShellStatus;
1377 }