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