• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
Ping6GetFrequency(VOID)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
Ping6CalculateTick(IN UINT64 Begin,IN UINT64 End)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
Ping6DestroyTxInfo(IN PING6_ICMP6_TX_INFO * TxInfo)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
Ping6MatchEchoReply(IN PING6_PRIVATE_DATA * Private,IN ICMP6_ECHO_REQUEST_REPLY * Packet)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
Ping6OnEchoRequestSent(IN EFI_EVENT Event,IN VOID * Context)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
Ping6OnEchoReplyReceived(IN EFI_EVENT Event,IN VOID * Context)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 *
Ping6GenerateToken(IN PING6_PRIVATE_DATA * Private,IN UINT64 TimeStamp,IN UINT16 SequenceNum)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
Ping6SendEchoRequest(IN PING6_PRIVATE_DATA * Private)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
Ping6ReceiveEchoReply(IN PING6_PRIVATE_DATA * Private)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
Ping6OnTimerRoutine(IN EFI_EVENT Event,IN VOID * Context)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
Ping6CreateIp6Instance(IN PING6_PRIVATE_DATA * Private)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
Ping6DestroyIp6Instance(IN PING6_PRIVATE_DATA * Private)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
Ping6(IN EFI_HANDLE ImageHandle,IN UINT32 SendNumber,IN UINT32 BufferSize,IN EFI_IPv6_ADDRESS * SrcAddress,IN EFI_IPv6_ADDRESS * DstAddress)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
InitializePing6(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)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 }
1179