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