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