]> git.proxmox.com Git - mirror_edk2.git/blob - ShellPkg/Library/UefiShellNetwork2CommandsLib/Ping6.c
ShellPkg: Merge Ping6 and Ifconfig6 tools to Shell command.
[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 EFI_SERVICE_BINDING_PROTOCOL *Ip6Sb;
667 EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg;
668 EFI_IP6_CONFIG_DATA Ip6Config;
669 EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo;
670 UINTN IfInfoSize;
671 EFI_IPv6_ADDRESS *Addr;
672 UINTN AddrIndex;
673
674 HandleBuffer = NULL;
675 Ip6Sb = NULL;
676 IfInfo = NULL;
677 IfInfoSize = 0;
678
679 //
680 // Locate all the handles with ip6 service binding protocol.
681 //
682 Status = gBS->LocateHandleBuffer (
683 ByProtocol,
684 &gEfiIp6ServiceBindingProtocolGuid,
685 NULL,
686 &HandleNum,
687 &HandleBuffer
688 );
689 if (EFI_ERROR (Status) || (HandleNum == 0)) {
690 return EFI_ABORTED;
691 }
692 //
693 // Source address is required when pinging a link-local address on multi-
694 // interfaces host.
695 //
696 if (NetIp6IsLinkLocalAddr (&Private->DstAddress) &&
697 NetIp6IsUnspecifiedAddr (&Private->SrcAddress) &&
698 (HandleNum > 1)) {
699 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_SOURCE), gShellNetwork2HiiHandle);
700 Status = EFI_INVALID_PARAMETER;
701 goto ON_ERROR;
702 }
703 //
704 // For each ip6 protocol, check interface addresses list.
705 //
706 for (HandleIndex = 0; HandleIndex < HandleNum; HandleIndex++) {
707
708 Ip6Sb = NULL;
709 IfInfo = NULL;
710 IfInfoSize = 0;
711
712 Status = gBS->HandleProtocol (
713 HandleBuffer[HandleIndex],
714 &gEfiIp6ServiceBindingProtocolGuid,
715 (VOID **) &Ip6Sb
716 );
717 if (EFI_ERROR (Status)) {
718 goto ON_ERROR;
719 }
720
721 if (NetIp6IsUnspecifiedAddr (&Private->SrcAddress)) {
722 //
723 // No need to match interface address.
724 //
725 break;
726 } else {
727 //
728 // Ip6config protocol and ip6 service binding protocol are installed
729 // on the same handle.
730 //
731 Status = gBS->HandleProtocol (
732 HandleBuffer[HandleIndex],
733 &gEfiIp6ConfigProtocolGuid,
734 (VOID **) &Ip6Cfg
735 );
736
737 if (EFI_ERROR (Status)) {
738 goto ON_ERROR;
739 }
740 //
741 // Get the interface information size.
742 //
743 Status = Ip6Cfg->GetData (
744 Ip6Cfg,
745 Ip6ConfigDataTypeInterfaceInfo,
746 &IfInfoSize,
747 NULL
748 );
749
750 if (Status != EFI_BUFFER_TOO_SMALL) {
751 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6CFG_GETDATA), gShellNetwork2HiiHandle, Status);
752 goto ON_ERROR;
753 }
754
755 IfInfo = AllocateZeroPool (IfInfoSize);
756
757 if (IfInfo == NULL) {
758 Status = EFI_OUT_OF_RESOURCES;
759 goto ON_ERROR;
760 }
761 //
762 // Get the interface info.
763 //
764 Status = Ip6Cfg->GetData (
765 Ip6Cfg,
766 Ip6ConfigDataTypeInterfaceInfo,
767 &IfInfoSize,
768 IfInfo
769 );
770
771 if (EFI_ERROR (Status)) {
772 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6CFG_GETDATA), gShellNetwork2HiiHandle, Status);
773 goto ON_ERROR;
774 }
775 //
776 // Check whether the source address is one of the interface addresses.
777 //
778 for (AddrIndex = 0; AddrIndex < IfInfo->AddressInfoCount; AddrIndex++) {
779
780 Addr = &(IfInfo->AddressInfo[AddrIndex].Address);
781 if (EFI_IP6_EQUAL (&Private->SrcAddress, Addr)) {
782 //
783 // Match a certain interface address.
784 //
785 break;
786 }
787 }
788
789 if (AddrIndex < IfInfo->AddressInfoCount) {
790 //
791 // Found a nic handle with right interface address.
792 //
793 break;
794 }
795 }
796
797 FreePool (IfInfo);
798 IfInfo = NULL;
799 }
800 //
801 // No exact interface address matched.
802 //
803
804 if (HandleIndex == HandleNum) {
805 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_SOURCE_NOT_FOUND), gShellNetwork2HiiHandle, mIp6SrcString);
806 Status = EFI_NOT_FOUND;
807 goto ON_ERROR;
808 }
809
810 Private->NicHandle = HandleBuffer[HandleIndex];
811
812 ASSERT (Ip6Sb != NULL);
813 Status = Ip6Sb->CreateChild (Ip6Sb, &Private->Ip6ChildHandle);
814
815 if (EFI_ERROR (Status)) {
816 goto ON_ERROR;
817 }
818
819 Status = gBS->OpenProtocol (
820 Private->Ip6ChildHandle,
821 &gEfiIp6ProtocolGuid,
822 (VOID **) &Private->Ip6,
823 Private->ImageHandle,
824 Private->Ip6ChildHandle,
825 EFI_OPEN_PROTOCOL_GET_PROTOCOL
826 );
827 if (EFI_ERROR (Status)) {
828 goto ON_ERROR;
829 }
830
831 ZeroMem (&Ip6Config, sizeof (EFI_IP6_CONFIG_DATA));
832
833 //
834 // Configure the ip6 instance for icmp6 packet exchange.
835 //
836 Ip6Config.DefaultProtocol = 58;
837 Ip6Config.AcceptAnyProtocol = FALSE;
838 Ip6Config.AcceptIcmpErrors = TRUE;
839 Ip6Config.AcceptPromiscuous = FALSE;
840 Ip6Config.TrafficClass = 0;
841 Ip6Config.HopLimit = 128;
842 Ip6Config.FlowLabel = 0;
843 Ip6Config.ReceiveTimeout = 0;
844 Ip6Config.TransmitTimeout = 0;
845
846 IP6_COPY_ADDRESS (&Ip6Config.StationAddress, &Private->SrcAddress);
847
848 IP6_COPY_ADDRESS (&Ip6Config.DestinationAddress, &Private->DstAddress);
849
850 Status = Private->Ip6->Configure (Private->Ip6, &Ip6Config);
851
852 if (EFI_ERROR (Status)) {
853 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6_CONFIG), gShellNetwork2HiiHandle, Status);
854 goto ON_ERROR;
855 }
856
857 return EFI_SUCCESS;
858
859 ON_ERROR:
860 if (HandleBuffer != NULL) {
861 FreePool (HandleBuffer);
862 }
863
864 if (IfInfo != NULL) {
865 FreePool (IfInfo);
866 }
867
868 if ((Ip6Sb != NULL) && (Private->Ip6ChildHandle != NULL)) {
869 Ip6Sb->DestroyChild (Ip6Sb, Private->Ip6ChildHandle);
870 }
871
872 return Status;
873 }
874
875 /**
876 Destroy the IP6 instance.
877
878 @param[in] Private The pointer of PING6_PRIVATE_DATA.
879
880 **/
881 VOID
882 Ping6DestroyIpInstance (
883 IN PING6_PRIVATE_DATA *Private
884 )
885 {
886 EFI_STATUS Status;
887 EFI_SERVICE_BINDING_PROTOCOL *Ip6Sb;
888
889 gBS->CloseProtocol (
890 Private->Ip6ChildHandle,
891 &gEfiIp6ProtocolGuid,
892 Private->ImageHandle,
893 Private->Ip6ChildHandle
894 );
895
896 Status = gBS->HandleProtocol (
897 Private->NicHandle,
898 &gEfiIp6ServiceBindingProtocolGuid,
899 (VOID **) &Ip6Sb
900 );
901
902 if (!EFI_ERROR(Status)) {
903 Ip6Sb->DestroyChild (Ip6Sb, Private->Ip6ChildHandle);
904 }
905 }
906
907 /**
908 The Ping6 Process.
909
910 @param[in] ImageHandle The firmware allocated handle for the UEFI image.
911 @param[in] SendNumber The send request count.
912 @param[in] BufferSize The send buffer size.
913 @param[in] SrcAddress The source IPv6 address.
914 @param[in] DstAddress The destination IPv6 address.
915
916 @retval SHELL_SUCCESS The ping6 processed successfullly.
917 @retval others The ping6 processed unsuccessfully.
918
919 **/
920 SHELL_STATUS
921 ShellPing6 (
922 IN EFI_HANDLE ImageHandle,
923 IN UINT32 SendNumber,
924 IN UINT32 BufferSize,
925 IN EFI_IPv6_ADDRESS *SrcAddress,
926 IN EFI_IPv6_ADDRESS *DstAddress
927 )
928 {
929 EFI_STATUS Status;
930 EFI_INPUT_KEY Key;
931 PING6_PRIVATE_DATA *Private;
932 PING6_ICMP6_TX_INFO *TxInfo;
933 LIST_ENTRY *Entry;
934 LIST_ENTRY *NextEntry;
935 SHELL_STATUS ShellStatus;
936
937 ShellStatus = SHELL_SUCCESS;
938 Private = AllocateZeroPool (sizeof (PING6_PRIVATE_DATA));
939
940 ASSERT (Private != NULL);
941
942 Private->ImageHandle = ImageHandle;
943 Private->SendNum = SendNumber;
944 Private->BufferSize = BufferSize;
945 Private->RttMin = ~((UINT64 )(0x0));
946 Private->Status = EFI_NOT_READY;
947
948 InitializeListHead (&Private->TxList);
949
950 IP6_COPY_ADDRESS (&Private->SrcAddress, SrcAddress);
951 IP6_COPY_ADDRESS (&Private->DstAddress, DstAddress);
952
953 //
954 // Open and configure a ip6 instance for ping6.
955 //
956 Status = Ping6CreateIpInstance (Private);
957
958 if (EFI_ERROR (Status)) {
959 ShellStatus = SHELL_ACCESS_DENIED;
960 goto ON_EXIT;
961 }
962 //
963 // Print the command line itself.
964 //
965 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_START), gShellNetwork2HiiHandle, mIp6DstString, Private->BufferSize);
966 //
967 // Create a ipv6 token to receive the first icmp6 echo reply packet.
968 //
969 Status = Ping6OnReceiveEchoReply (Private);
970
971 if (EFI_ERROR (Status)) {
972 ShellStatus = SHELL_ACCESS_DENIED;
973 goto ON_EXIT;
974 }
975 //
976 // Create and start timer to send icmp6 echo request packet per second.
977 //
978 Status = gBS->CreateEvent (
979 EVT_TIMER | EVT_NOTIFY_SIGNAL,
980 TPL_CALLBACK,
981 Ping6OnTimerRoutine6,
982 Private,
983 &Private->Timer
984 );
985
986 if (EFI_ERROR (Status)) {
987 ShellStatus = SHELL_ACCESS_DENIED;
988 goto ON_EXIT;
989 }
990 //
991 // Create a ipv6 token to send the first icmp6 echo request packet.
992 //
993 Status = Ping6SendEchoRequest (Private);
994 //
995 // EFI_NOT_READY for IPsec is enable and IKE is not established.
996 //
997 if (EFI_ERROR (Status) && (Status != EFI_NOT_READY)) {
998 ShellStatus = SHELL_ACCESS_DENIED;
999 if(Status == EFI_NOT_FOUND) {
1000 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_NOSOURCE_INDOMAIN), gShellNetwork2HiiHandle, mIp6DstString);
1001 }
1002
1003 goto ON_EXIT;
1004 }
1005
1006 Status = gBS->SetTimer (
1007 Private->Timer,
1008 TimerPeriodic,
1009 PING6_ONE_SECOND
1010 );
1011
1012 if (EFI_ERROR (Status)) {
1013 ShellStatus = SHELL_ACCESS_DENIED;
1014 goto ON_EXIT;
1015 }
1016 //
1017 // Control the ping6 process by two factors:
1018 // 1. Hot key
1019 // 2. Private->Status
1020 // 2.1. success means all icmp6 echo request packets get reply packets.
1021 // 2.2. timeout means the last icmp6 echo reply request timeout to get reply.
1022 // 2.3. noready means ping6 process is on-the-go.
1023 //
1024 while (Private->Status == EFI_NOT_READY) {
1025 Private->Ip6->Poll (Private->Ip6);
1026
1027 //
1028 // Terminate the ping6 process by 'esc' or 'ctl-c'.
1029 //
1030 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
1031
1032 if (!EFI_ERROR(Status)) {
1033 if ((Key.UnicodeChar == 0x1b) || (Key.UnicodeChar == 0x03) ||
1034 ((Key.UnicodeChar == 0) && (Key.ScanCode == SCAN_ESC))) {
1035 goto ON_STAT;
1036 }
1037 }
1038 }
1039
1040 ON_STAT:
1041 //
1042 // Display the statistics in all.
1043 //
1044 gBS->SetTimer (Private->Timer, TimerCancel, 0);
1045
1046 if (Private->TxCount != 0) {
1047 ShellPrintHiiEx (
1048 -1,
1049 -1,
1050 NULL,
1051 STRING_TOKEN (STR_PING6_STAT),
1052 gShellNetwork2HiiHandle,
1053 Private->TxCount,
1054 Private->RxCount,
1055 (100 * (Private->TxCount - Private->RxCount)) / Private->TxCount,
1056 Private->RttSum
1057 );
1058 }
1059
1060 if (Private->RxCount != 0) {
1061 ShellPrintHiiEx (
1062 -1,
1063 -1,
1064 NULL,
1065 STRING_TOKEN (STR_PING6_RTT),
1066 gShellNetwork2HiiHandle,
1067 Private->RttMin,
1068 Private->RttMax,
1069 DivU64x64Remainder (Private->RttSum, Private->RxCount, NULL)
1070 );
1071 }
1072
1073 ON_EXIT:
1074
1075 if (Private != NULL) {
1076 Private->Status = EFI_ABORTED;
1077
1078 NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) {
1079 TxInfo = BASE_CR (Entry, PING6_ICMP6_TX_INFO, Link);
1080
1081 Status = Private->Ip6->Cancel (Private->Ip6, TxInfo->Token);
1082
1083 RemoveEntryList (&TxInfo->Link);
1084 Ping6DestroyTxInfo (TxInfo);
1085 }
1086
1087 if (Private->Timer != NULL) {
1088 gBS->CloseEvent (Private->Timer);
1089 }
1090
1091 if (Private->Ip6 != NULL) {
1092 Status = Private->Ip6->Cancel (Private->Ip6, &Private->RxToken);
1093 }
1094
1095 if (Private->RxToken.Event != NULL) {
1096 gBS->CloseEvent (Private->RxToken.Event);
1097 }
1098
1099 if (Private->Ip6ChildHandle != NULL) {
1100 Ping6DestroyIpInstance (Private);
1101 }
1102
1103 FreePool (Private);
1104 }
1105
1106 return ShellStatus;
1107 }
1108
1109 /**
1110 Function for 'ping6' command.
1111
1112 @param[in] ImageHandle Handle to the Image (NULL if Internal).
1113 @param[in] SystemTable Pointer to the System Table (NULL if Internal).
1114
1115 @retval SHELL_SUCCESS The ping6 processed successfullly.
1116 @retval others The ping6 processed unsuccessfully.
1117
1118 **/
1119 SHELL_STATUS
1120 EFIAPI
1121 ShellCommandRunPing6 (
1122 IN EFI_HANDLE ImageHandle,
1123 IN EFI_SYSTEM_TABLE *SystemTable
1124 )
1125 {
1126 EFI_STATUS Status;
1127 SHELL_STATUS ShellStatus;
1128 EFI_IPv6_ADDRESS DstAddress;
1129 EFI_IPv6_ADDRESS SrcAddress;
1130 UINT64 BufferSize;
1131 UINTN SendNumber;
1132 LIST_ENTRY *ParamPackage;
1133 CONST CHAR16 *ValueStr;
1134 CONST CHAR16 *ValueStrPtr;
1135 UINTN NonOptionCount;
1136 CHAR16 *ProblemParam;
1137
1138 ProblemParam = NULL;
1139 ShellStatus = SHELL_SUCCESS;
1140
1141 Status = ShellCommandLineParseEx (Ping6ParamList, &ParamPackage, &ProblemParam, TRUE, FALSE);
1142 if (EFI_ERROR(Status)) {
1143 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_INPUT), gShellNetwork2HiiHandle);
1144 ShellStatus = SHELL_INVALID_PARAMETER;
1145 goto ON_EXIT;
1146 }
1147
1148 SendNumber = 10;
1149 BufferSize = 16;
1150
1151 //
1152 // Parse the paramter of count number.
1153 //
1154 ValueStr = ShellCommandLineGetValue (ParamPackage, L"-n");
1155 ValueStrPtr = ValueStr;
1156 if (ValueStr != NULL) {
1157 SendNumber = ShellStrToUintn (ValueStrPtr);
1158
1159 //
1160 // ShellStrToUintn will return 0 when input is 0 or an invalid input string.
1161 //
1162 if ((SendNumber == 0) || (SendNumber > PING6_MAX_SEND_NUMBER)) {
1163 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_SEND_NUMBER), gShellNetwork2HiiHandle, ValueStr);
1164 ShellStatus = SHELL_INVALID_PARAMETER;
1165 goto ON_EXIT;
1166 }
1167 }
1168 //
1169 // Parse the paramter of buffer size.
1170 //
1171 ValueStr = ShellCommandLineGetValue (ParamPackage, L"-l");
1172 ValueStrPtr = ValueStr;
1173 if (ValueStr != NULL) {
1174 BufferSize = ShellStrToUintn (ValueStrPtr);
1175
1176 //
1177 // ShellStrToUintn will return 0 when input is 0 or an invalid input string.
1178 //
1179 if ((BufferSize < 16) || (BufferSize > PING6_MAX_BUFFER_SIZE)) {
1180 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_BUFFER_SIZE), gShellNetwork2HiiHandle, ValueStr);
1181 ShellStatus = SHELL_INVALID_PARAMETER;
1182 goto ON_EXIT;
1183 }
1184 }
1185
1186 ZeroMem (&SrcAddress, sizeof (EFI_IPv6_ADDRESS));
1187 ZeroMem (&DstAddress, sizeof (EFI_IPv6_ADDRESS));
1188
1189 //
1190 // Parse the paramter of source ip address.
1191 //
1192 ValueStr = ShellCommandLineGetValue (ParamPackage, L"-s");
1193 ValueStrPtr = ValueStr;
1194 if (ValueStr != NULL) {
1195 mIp6SrcString = ValueStr;
1196 Status = NetLibStrToIp6 (ValueStrPtr, &SrcAddress);
1197 if (EFI_ERROR (Status)) {
1198 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_IP), gShellNetwork2HiiHandle, ValueStr);
1199 ShellStatus = SHELL_INVALID_PARAMETER;
1200 goto ON_EXIT;
1201 }
1202 }
1203 //
1204 // Parse the paramter of destination ip address.
1205 //
1206 NonOptionCount = ShellCommandLineGetCount(ParamPackage);
1207 ValueStr = ShellCommandLineGetRawValue (ParamPackage, (UINT32)(NonOptionCount-1));
1208 if (NonOptionCount != 2) {
1209 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_INPUT), gShellNetwork2HiiHandle);
1210 ShellStatus = SHELL_INVALID_PARAMETER;
1211 goto ON_EXIT;
1212 }
1213 ValueStrPtr = ValueStr;
1214 if (ValueStr != NULL) {
1215 mIp6DstString = ValueStr;
1216 Status = NetLibStrToIp6 (ValueStrPtr, &DstAddress);
1217 if (EFI_ERROR (Status)) {
1218 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_IP), gShellNetwork2HiiHandle, ValueStr);
1219 ShellStatus = SHELL_INVALID_PARAMETER;
1220 goto ON_EXIT;
1221 }
1222 }
1223 //
1224 // Get frequency to calculate the time from ticks.
1225 //
1226 Status = Ping6GetFrequency ();
1227
1228 if (EFI_ERROR(Status)) {
1229 ShellStatus = SHELL_ACCESS_DENIED;
1230 goto ON_EXIT;
1231 }
1232 //
1233 // Enter into ping6 process.
1234 //
1235 ShellStatus = ShellPing6 (
1236 ImageHandle,
1237 (UINT32)SendNumber,
1238 (UINT32)BufferSize,
1239 &SrcAddress,
1240 &DstAddress
1241 );
1242
1243 ON_EXIT:
1244 ShellCommandLineFreeVarList (ParamPackage);
1245 return ShellStatus;
1246 }
1247