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