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