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