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