| /** @file | |
| The implementation for Ping6 application. | |
| Copyright (c) 2016, Intel Corporation. All rights reserved.<BR> | |
| This program and the accompanying materials | |
| are licensed and made available under the terms and conditions of the BSD License | |
| which accompanies this distribution. The full text of the license may be found at | |
| http://opensource.org/licenses/bsd-license.php. | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
| **/ | |
| #include "UefiShellNetwork2CommandsLib.h" | |
| #define PING6_DEFAULT_TIMEOUT 5000 | |
| #define PING6_MAX_SEND_NUMBER 10000 | |
| #define PING6_MAX_BUFFER_SIZE 32768 | |
| #define PING6_ONE_SECOND 10000000 | |
| // | |
| // A similar amount of time that passes in femtoseconds | |
| // for each increment of TimerValue. It is for NT32 only. | |
| // | |
| #define NTTIMERPERIOD 358049 | |
| #pragma pack(1) | |
| typedef struct _ICMP6_ECHO_REQUEST_REPLY { | |
| UINT8 Type; | |
| UINT8 Code; | |
| UINT16 Checksum; | |
| UINT16 Identifier; | |
| UINT16 SequenceNum; | |
| UINT64 TimeStamp; | |
| UINT8 Data[1]; | |
| } ICMP6_ECHO_REQUEST_REPLY; | |
| #pragma pack() | |
| typedef struct _PING6_ICMP6_TX_INFO { | |
| LIST_ENTRY Link; | |
| UINT16 SequenceNum; | |
| UINT64 TimeStamp; | |
| EFI_IP6_COMPLETION_TOKEN *Token; | |
| } PING6_ICMP6_TX_INFO; | |
| typedef struct _PING6_PRIVATE_DATA { | |
| EFI_HANDLE ImageHandle; | |
| EFI_HANDLE NicHandle; | |
| EFI_HANDLE Ip6ChildHandle; | |
| EFI_IP6_PROTOCOL *Ip6; | |
| EFI_EVENT Timer; | |
| EFI_STATUS Status; | |
| LIST_ENTRY TxList; | |
| EFI_IP6_COMPLETION_TOKEN RxToken; | |
| UINT16 RxCount; | |
| UINT16 TxCount; | |
| UINT64 RttSum; | |
| UINT64 RttMin; | |
| UINT64 RttMax; | |
| UINT32 SequenceNum; | |
| EFI_IPv6_ADDRESS SrcAddress; | |
| EFI_IPv6_ADDRESS DstAddress; | |
| UINT32 SendNum; | |
| UINT32 BufferSize; | |
| } PING6_PRIVATE_DATA; | |
| SHELL_PARAM_ITEM Ping6ParamList[] = { | |
| { | |
| L"-l", | |
| TypeValue | |
| }, | |
| { | |
| L"-n", | |
| TypeValue | |
| }, | |
| { | |
| L"-s", | |
| TypeValue | |
| }, | |
| { | |
| L"-?", | |
| TypeFlag | |
| }, | |
| { | |
| NULL, | |
| TypeMax | |
| }, | |
| }; | |
| // | |
| // Global Variables in Ping6 application. | |
| // | |
| CONST CHAR16 *mIp6DstString; | |
| CONST CHAR16 *mIp6SrcString; | |
| UINT64 mFrequency = 0; | |
| UINT64 mIp6CurrentTick = 0; | |
| EFI_CPU_ARCH_PROTOCOL *Cpu = NULL; | |
| /** | |
| Reads and returns the current value of the Time. | |
| @return The current tick value. | |
| **/ | |
| UINT64 | |
| Ping6ReadTime () | |
| { | |
| UINT64 TimerPeriod; | |
| EFI_STATUS Status; | |
| ASSERT (Cpu != NULL); | |
| Status = Cpu->GetTimerValue (Cpu, 0, &mIp6CurrentTick, &TimerPeriod); | |
| if (EFI_ERROR (Status)) { | |
| // | |
| // The WinntGetTimerValue will return EFI_UNSUPPORTED. Set the | |
| // TimerPeriod by ourselves. | |
| // | |
| mIp6CurrentTick += 1000000; | |
| } | |
| return mIp6CurrentTick; | |
| } | |
| /** | |
| Get and calculate the frequency in tick/ms. | |
| The result is saved in the globle variable mFrequency | |
| @retval EFI_SUCCESS Calculated the frequency successfully. | |
| @retval Others Failed to calculate the frequency. | |
| **/ | |
| EFI_STATUS | |
| Ping6GetFrequency ( | |
| VOID | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINT64 CurrentTick; | |
| UINT64 TimerPeriod; | |
| Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **) &Cpu); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| Status = Cpu->GetTimerValue (Cpu, 0, &CurrentTick, &TimerPeriod); | |
| if (EFI_ERROR (Status)) { | |
| // | |
| // For NT32 Simulator only. 358049 is a similar value to keep timer granularity. | |
| // Set the timer period by ourselves. | |
| // | |
| TimerPeriod = (UINT64) NTTIMERPERIOD; | |
| } | |
| // | |
| // The timer period is in femtosecond (1 femtosecond is 1e-15 second). | |
| // So 1e+12 is divided by timer period to produce the freq in tick/ms. | |
| // | |
| mFrequency = DivU64x64Remainder (1000000000000ULL, TimerPeriod, NULL); | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Get and calculate the duration in ms. | |
| @param[in] Begin The start point of time. | |
| @param[in] End The end point of time. | |
| @return The duration in ms. | |
| **/ | |
| UINT64 | |
| Ping6CalculateTick ( | |
| IN UINT64 Begin, | |
| IN UINT64 End | |
| ) | |
| { | |
| ASSERT (End > Begin); | |
| return DivU64x64Remainder (End - Begin, mFrequency, NULL); | |
| } | |
| /** | |
| Destroy IPING6_ICMP6_TX_INFO, and recollect the memory. | |
| @param[in] TxInfo The pointer to PING6_ICMP6_TX_INFO. | |
| **/ | |
| VOID | |
| Ping6DestroyTxInfo ( | |
| IN PING6_ICMP6_TX_INFO *TxInfo | |
| ) | |
| { | |
| EFI_IP6_TRANSMIT_DATA *TxData; | |
| EFI_IP6_FRAGMENT_DATA *FragData; | |
| UINTN Index; | |
| ASSERT (TxInfo != NULL); | |
| if (TxInfo->Token != NULL) { | |
| if (TxInfo->Token->Event != NULL) { | |
| gBS->CloseEvent (TxInfo->Token->Event); | |
| } | |
| TxData = TxInfo->Token->Packet.TxData; | |
| if (TxData != NULL) { | |
| if (TxData->OverrideData != NULL) { | |
| FreePool (TxData->OverrideData); | |
| } | |
| if (TxData->ExtHdrs != NULL) { | |
| FreePool (TxData->ExtHdrs); | |
| } | |
| for (Index = 0; Index < TxData->FragmentCount; Index++) { | |
| FragData = TxData->FragmentTable[Index].FragmentBuffer; | |
| if (FragData != NULL) { | |
| FreePool (FragData); | |
| } | |
| } | |
| } | |
| FreePool (TxInfo->Token); | |
| } | |
| FreePool (TxInfo); | |
| } | |
| /** | |
| Match the request, and reply with SequenceNum/TimeStamp. | |
| @param[in] Private The pointer to PING6_PRIVATE_DATA. | |
| @param[in] Packet The pointer to ICMP6_ECHO_REQUEST_REPLY. | |
| @retval EFI_SUCCESS The match is successful. | |
| @retval EFI_NOT_FOUND The reply can't be matched with any request. | |
| **/ | |
| EFI_STATUS | |
| Ping6OnMatchEchoReply ( | |
| IN PING6_PRIVATE_DATA *Private, | |
| IN ICMP6_ECHO_REQUEST_REPLY *Packet | |
| ) | |
| { | |
| PING6_ICMP6_TX_INFO *TxInfo; | |
| LIST_ENTRY *Entry; | |
| LIST_ENTRY *NextEntry; | |
| NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) { | |
| TxInfo = BASE_CR (Entry, PING6_ICMP6_TX_INFO, Link); | |
| if ((TxInfo->SequenceNum == Packet->SequenceNum) && (TxInfo->TimeStamp == Packet->TimeStamp)) { | |
| Private->RxCount++; | |
| RemoveEntryList (&TxInfo->Link); | |
| Ping6DestroyTxInfo (TxInfo); | |
| return EFI_SUCCESS; | |
| } | |
| } | |
| return EFI_NOT_FOUND; | |
| } | |
| /** | |
| The original intention is to send a request. | |
| Currently, the application retransmits an icmp6 echo request packet | |
| per second in sendnumber times that is specified by the user. | |
| Because nothing can be done here, all things move to the timer rountine. | |
| @param[in] Event A EFI_EVENT type event. | |
| @param[in] Context The pointer to Context. | |
| **/ | |
| VOID | |
| EFIAPI | |
| Ping6OnEchoRequestSent6 ( | |
| IN EFI_EVENT Event, | |
| IN VOID *Context | |
| ) | |
| { | |
| } | |
| /** | |
| receive reply, match and print reply infomation. | |
| @param[in] Event A EFI_EVENT type event. | |
| @param[in] Context The pointer to context. | |
| **/ | |
| VOID | |
| EFIAPI | |
| Ping6OnEchoReplyReceived6 ( | |
| IN EFI_EVENT Event, | |
| IN VOID *Context | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| PING6_PRIVATE_DATA *Private; | |
| EFI_IP6_COMPLETION_TOKEN *RxToken; | |
| EFI_IP6_RECEIVE_DATA *RxData; | |
| ICMP6_ECHO_REQUEST_REPLY *Reply; | |
| UINT32 PayLoad; | |
| UINT64 Rtt; | |
| CHAR8 Near; | |
| Private = (PING6_PRIVATE_DATA *) Context; | |
| if (Private->Status == EFI_ABORTED) { | |
| return; | |
| } | |
| RxToken = &Private->RxToken; | |
| RxData = RxToken->Packet.RxData; | |
| Reply = RxData->FragmentTable[0].FragmentBuffer; | |
| PayLoad = RxData->DataLength; | |
| if (RxData->Header->NextHeader != IP6_ICMP) { | |
| goto ON_EXIT; | |
| } | |
| if (!IP6_IS_MULTICAST (&Private->DstAddress) && | |
| !EFI_IP6_EQUAL (&RxData->Header->SourceAddress, &Private->DstAddress)) { | |
| goto ON_EXIT; | |
| } | |
| if ((Reply->Type != ICMP_V6_ECHO_REPLY) || (Reply->Code != 0)) { | |
| goto ON_EXIT; | |
| } | |
| if (PayLoad != Private->BufferSize) { | |
| goto ON_EXIT; | |
| } | |
| // | |
| // Check whether the reply matches the sent request before. | |
| // | |
| Status = Ping6OnMatchEchoReply (Private, Reply); | |
| if (EFI_ERROR(Status)) { | |
| goto ON_EXIT; | |
| } | |
| // | |
| // Display statistics on this icmp6 echo reply packet. | |
| // | |
| Rtt = Ping6CalculateTick (Reply->TimeStamp, Ping6ReadTime ()); | |
| if (Rtt != 0) { | |
| Near = (CHAR8) '='; | |
| } else { | |
| Near = (CHAR8) '<'; | |
| } | |
| Private->RttSum += Rtt; | |
| Private->RttMin = Private->RttMin > Rtt ? Rtt : Private->RttMin; | |
| Private->RttMax = Private->RttMax < Rtt ? Rtt : Private->RttMax; | |
| ShellPrintHiiEx ( | |
| -1, | |
| -1, | |
| NULL, | |
| STRING_TOKEN (STR_PING6_REPLY_INFO), | |
| gShellNetwork2HiiHandle, | |
| PayLoad, | |
| mIp6DstString, | |
| Reply->SequenceNum, | |
| RxData->Header->HopLimit, | |
| Near, | |
| Rtt | |
| ); | |
| ON_EXIT: | |
| if (Private->RxCount < Private->SendNum) { | |
| // | |
| // Continue to receive icmp6 echo reply packets. | |
| // | |
| RxToken->Status = EFI_ABORTED; | |
| Status = Private->Ip6->Receive (Private->Ip6, RxToken); | |
| if (EFI_ERROR (Status)) { | |
| Private->Status = EFI_ABORTED; | |
| } | |
| } else { | |
| // | |
| // All reply have already been received from the dest host. | |
| // | |
| Private->Status = EFI_SUCCESS; | |
| } | |
| // | |
| // Singal to recycle the each rxdata here, not at the end of process. | |
| // | |
| gBS->SignalEvent (RxData->RecycleSignal); | |
| } | |
| /** | |
| Initial EFI_IP6_COMPLETION_TOKEN. | |
| @param[in] Private The pointer of PING6_PRIVATE_DATA. | |
| @param[in] TimeStamp The TimeStamp of request. | |
| @param[in] SequenceNum The SequenceNum of request. | |
| @return The pointer of EFI_IP6_COMPLETION_TOKEN. | |
| **/ | |
| EFI_IP6_COMPLETION_TOKEN * | |
| Ping6GenerateToken ( | |
| IN PING6_PRIVATE_DATA *Private, | |
| IN UINT64 TimeStamp, | |
| IN UINT16 SequenceNum | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_IP6_COMPLETION_TOKEN *Token; | |
| EFI_IP6_TRANSMIT_DATA *TxData; | |
| ICMP6_ECHO_REQUEST_REPLY *Request; | |
| Request = AllocateZeroPool (Private->BufferSize); | |
| if (Request == NULL) { | |
| return NULL; | |
| } | |
| // | |
| // Assembly icmp6 echo request packet. | |
| // | |
| Request->Type = ICMP_V6_ECHO_REQUEST; | |
| Request->Code = 0; | |
| Request->SequenceNum = SequenceNum; | |
| Request->TimeStamp = TimeStamp; | |
| Request->Identifier = 0; | |
| // | |
| // Leave check sum to ip6 layer, since it has no idea of source address | |
| // selection. | |
| // | |
| Request->Checksum = 0; | |
| TxData = AllocateZeroPool (sizeof (EFI_IP6_TRANSMIT_DATA)); | |
| if (TxData == NULL) { | |
| FreePool (Request); | |
| return NULL; | |
| } | |
| // | |
| // Assembly ipv6 token for transmit. | |
| // | |
| TxData->OverrideData = 0; | |
| TxData->ExtHdrsLength = 0; | |
| TxData->ExtHdrs = NULL; | |
| TxData->DataLength = Private->BufferSize; | |
| TxData->FragmentCount = 1; | |
| TxData->FragmentTable[0].FragmentBuffer = (VOID *) Request; | |
| TxData->FragmentTable[0].FragmentLength = Private->BufferSize; | |
| Token = AllocateZeroPool (sizeof (EFI_IP6_COMPLETION_TOKEN)); | |
| if (Token == NULL) { | |
| FreePool (Request); | |
| FreePool (TxData); | |
| return NULL; | |
| } | |
| Token->Status = EFI_ABORTED; | |
| Token->Packet.TxData = TxData; | |
| Status = gBS->CreateEvent ( | |
| EVT_NOTIFY_SIGNAL, | |
| TPL_CALLBACK, | |
| Ping6OnEchoRequestSent6, | |
| Private, | |
| &Token->Event | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| FreePool (Request); | |
| FreePool (TxData); | |
| FreePool (Token); | |
| return NULL; | |
| } | |
| return Token; | |
| } | |
| /** | |
| Transmit the EFI_IP6_COMPLETION_TOKEN. | |
| @param[in] Private The pointer of PING6_PRIVATE_DATA. | |
| @retval EFI_SUCCESS Transmitted successfully. | |
| @retval EFI_OUT_OF_RESOURCES No memory is available on the platform. | |
| @retval others Transmitted unsuccessfully. | |
| **/ | |
| EFI_STATUS | |
| Ping6SendEchoRequest ( | |
| IN PING6_PRIVATE_DATA *Private | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| PING6_ICMP6_TX_INFO *TxInfo; | |
| TxInfo = AllocateZeroPool (sizeof (PING6_ICMP6_TX_INFO)); | |
| if (TxInfo == NULL) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| TxInfo->TimeStamp = Ping6ReadTime (); | |
| TxInfo->SequenceNum = (UINT16) (Private->TxCount + 1); | |
| TxInfo->Token = Ping6GenerateToken ( | |
| Private, | |
| TxInfo->TimeStamp, | |
| TxInfo->SequenceNum | |
| ); | |
| if (TxInfo->Token == NULL) { | |
| Ping6DestroyTxInfo (TxInfo); | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| Status = Private->Ip6->Transmit (Private->Ip6, TxInfo->Token); | |
| if (EFI_ERROR (Status)) { | |
| Ping6DestroyTxInfo (TxInfo); | |
| return Status; | |
| } | |
| InsertTailList (&Private->TxList, &TxInfo->Link); | |
| Private->TxCount++; | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Place a completion token into the receive packet queue to receive the echo reply. | |
| @param[in] Private The pointer of PING6_PRIVATE_DATA. | |
| @retval EFI_SUCCESS Put the token into the receive packet queue successfully. | |
| @retval others Put the token into the receive packet queue unsuccessfully. | |
| **/ | |
| EFI_STATUS | |
| Ping6OnReceiveEchoReply ( | |
| IN PING6_PRIVATE_DATA *Private | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| ZeroMem (&Private->RxToken, sizeof (EFI_IP6_COMPLETION_TOKEN)); | |
| Status = gBS->CreateEvent ( | |
| EVT_NOTIFY_SIGNAL, | |
| TPL_CALLBACK, | |
| Ping6OnEchoReplyReceived6, | |
| Private, | |
| &Private->RxToken.Event | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| Private->RxToken.Status = EFI_NOT_READY; | |
| return Private->Ip6->Receive (Private->Ip6, &Private->RxToken); | |
| } | |
| /** | |
| Remove the timeout request from the list. | |
| @param[in] Event A EFI_EVENT type event. | |
| @param[in] Context The pointer to Context. | |
| **/ | |
| VOID | |
| EFIAPI | |
| Ping6OnTimerRoutine6 ( | |
| IN EFI_EVENT Event, | |
| IN VOID *Context | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| PING6_PRIVATE_DATA *Private; | |
| PING6_ICMP6_TX_INFO *TxInfo; | |
| LIST_ENTRY *Entry; | |
| LIST_ENTRY *NextEntry; | |
| UINT64 Time; | |
| Private = (PING6_PRIVATE_DATA *) Context; | |
| // | |
| // Retransmit icmp6 echo request packets per second in sendnumber times. | |
| // | |
| if (Private->TxCount < Private->SendNum) { | |
| Status = Ping6SendEchoRequest (Private); | |
| if (Private->TxCount != 0){ | |
| if (EFI_ERROR (Status)) { | |
| ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_SEND_REQUEST), gShellNetwork2HiiHandle, Private->TxCount + 1); | |
| } | |
| } | |
| } | |
| // | |
| // Check whether any icmp6 echo request in the list timeout. | |
| // | |
| NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) { | |
| TxInfo = BASE_CR (Entry, PING6_ICMP6_TX_INFO, Link); | |
| Time = Ping6CalculateTick (TxInfo->TimeStamp, Ping6ReadTime ()); | |
| // | |
| // Remove the timeout echo request from txlist. | |
| // | |
| if (Time > PING6_DEFAULT_TIMEOUT) { | |
| if (EFI_ERROR (TxInfo->Token->Status)) { | |
| Private->Ip6->Cancel (Private->Ip6, TxInfo->Token); | |
| } | |
| // | |
| // Remove the timeout icmp6 echo request from list. | |
| // | |
| ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_TIMEOUT), gShellNetwork2HiiHandle, TxInfo->SequenceNum); | |
| RemoveEntryList (&TxInfo->Link); | |
| Ping6DestroyTxInfo (TxInfo); | |
| if (IsListEmpty (&Private->TxList) && (Private->TxCount == Private->SendNum)) { | |
| // | |
| // All the left icmp6 echo request in the list timeout. | |
| // | |
| Private->Status = EFI_TIMEOUT; | |
| } | |
| } | |
| } | |
| } | |
| /** | |
| Create a valid IP6 instance. | |
| @param[in] Private The pointer of PING6_PRIVATE_DATA. | |
| @retval EFI_SUCCESS Create a valid IP6 instance successfully. | |
| @retval EFI_ABORTED Locate handle with ip6 service binding protocol unsuccessfully. | |
| @retval EFI_INVALID_PARAMETER The source address is unspecified when the destination address is a link -ocal address. | |
| @retval EFI_OUT_OF_RESOURCES No memory is available on the platform. | |
| @retval EFI_NOT_FOUND The source address is not found. | |
| **/ | |
| EFI_STATUS | |
| Ping6CreateIpInstance ( | |
| IN PING6_PRIVATE_DATA *Private | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINTN HandleIndex; | |
| UINTN HandleNum; | |
| EFI_HANDLE *HandleBuffer; | |
| BOOLEAN UnspecifiedSrc; | |
| BOOLEAN MediaPresent; | |
| EFI_SERVICE_BINDING_PROTOCOL *Ip6Sb; | |
| EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg; | |
| EFI_IP6_CONFIG_DATA Ip6Config; | |
| EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo; | |
| UINTN IfInfoSize; | |
| EFI_IPv6_ADDRESS *Addr; | |
| UINTN AddrIndex; | |
| HandleBuffer = NULL; | |
| UnspecifiedSrc = FALSE; | |
| MediaPresent = TRUE; | |
| Ip6Sb = NULL; | |
| IfInfo = NULL; | |
| IfInfoSize = 0; | |
| // | |
| // Locate all the handles with ip6 service binding protocol. | |
| // | |
| Status = gBS->LocateHandleBuffer ( | |
| ByProtocol, | |
| &gEfiIp6ServiceBindingProtocolGuid, | |
| NULL, | |
| &HandleNum, | |
| &HandleBuffer | |
| ); | |
| if (EFI_ERROR (Status) || (HandleNum == 0)) { | |
| return EFI_ABORTED; | |
| } | |
| if (NetIp6IsUnspecifiedAddr (&Private->SrcAddress)) { | |
| // | |
| // SrcAddress is unspecified. So, both connected and configured interface will be automatic selected. | |
| // | |
| UnspecifiedSrc = TRUE; | |
| } | |
| // | |
| // Source address is required when pinging a link-local address. | |
| // | |
| if (NetIp6IsLinkLocalAddr (&Private->DstAddress) && UnspecifiedSrc) { | |
| ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_SOURCE), gShellNetwork2HiiHandle); | |
| Status = EFI_INVALID_PARAMETER; | |
| goto ON_ERROR; | |
| } | |
| // | |
| // For each ip6 protocol, check interface addresses list. | |
| // | |
| for (HandleIndex = 0; HandleIndex < HandleNum; HandleIndex++) { | |
| Ip6Sb = NULL; | |
| IfInfo = NULL; | |
| IfInfoSize = 0; | |
| if (UnspecifiedSrc) { | |
| // | |
| // Check media. | |
| // | |
| NetLibDetectMedia (HandleBuffer[HandleIndex], &MediaPresent); | |
| if (!MediaPresent) { | |
| // | |
| // Skip this one. | |
| // | |
| continue; | |
| } | |
| } | |
| Status = gBS->HandleProtocol ( | |
| HandleBuffer[HandleIndex], | |
| &gEfiIp6ServiceBindingProtocolGuid, | |
| (VOID **) &Ip6Sb | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| goto ON_ERROR; | |
| } | |
| // | |
| // Ip6config protocol and ip6 service binding protocol are installed | |
| // on the same handle. | |
| // | |
| Status = gBS->HandleProtocol ( | |
| HandleBuffer[HandleIndex], | |
| &gEfiIp6ConfigProtocolGuid, | |
| (VOID **) &Ip6Cfg | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| goto ON_ERROR; | |
| } | |
| // | |
| // Get the interface information size. | |
| // | |
| Status = Ip6Cfg->GetData ( | |
| Ip6Cfg, | |
| Ip6ConfigDataTypeInterfaceInfo, | |
| &IfInfoSize, | |
| NULL | |
| ); | |
| if (Status != EFI_BUFFER_TOO_SMALL) { | |
| ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6CFG_GETDATA), gShellNetwork2HiiHandle, Status); | |
| goto ON_ERROR; | |
| } | |
| IfInfo = AllocateZeroPool (IfInfoSize); | |
| if (IfInfo == NULL) { | |
| Status = EFI_OUT_OF_RESOURCES; | |
| goto ON_ERROR; | |
| } | |
| // | |
| // Get the interface info. | |
| // | |
| Status = Ip6Cfg->GetData ( | |
| Ip6Cfg, | |
| Ip6ConfigDataTypeInterfaceInfo, | |
| &IfInfoSize, | |
| IfInfo | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6CFG_GETDATA), gShellNetwork2HiiHandle, Status); | |
| goto ON_ERROR; | |
| } | |
| // | |
| // Check whether the source address is one of the interface addresses. | |
| // | |
| for (AddrIndex = 0; AddrIndex < IfInfo->AddressInfoCount; AddrIndex++) { | |
| Addr = &(IfInfo->AddressInfo[AddrIndex].Address); | |
| if (UnspecifiedSrc) { | |
| if (!NetIp6IsUnspecifiedAddr (Addr) && !NetIp6IsLinkLocalAddr (Addr)) { | |
| // | |
| // Select the interface automatically. | |
| // | |
| CopyMem(&Private->SrcAddress, Addr, sizeof(Private->SrcAddress)); | |
| break; | |
| } | |
| } else if (EFI_IP6_EQUAL (&Private->SrcAddress, Addr)) { | |
| // | |
| // Match a certain interface address. | |
| // | |
| break; | |
| } | |
| } | |
| if (AddrIndex < IfInfo->AddressInfoCount) { | |
| // | |
| // Found a nic handle with right interface address. | |
| // | |
| break; | |
| } | |
| FreePool (IfInfo); | |
| IfInfo = NULL; | |
| } | |
| // | |
| // No exact interface address matched. | |
| // | |
| if (HandleIndex == HandleNum) { | |
| ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_CONFIGD_NIC_NF), gShellNetwork2HiiHandle); | |
| Status = EFI_NOT_FOUND; | |
| goto ON_ERROR; | |
| } | |
| Private->NicHandle = HandleBuffer[HandleIndex]; | |
| ASSERT (Ip6Sb != NULL); | |
| Status = Ip6Sb->CreateChild (Ip6Sb, &Private->Ip6ChildHandle); | |
| if (EFI_ERROR (Status)) { | |
| goto ON_ERROR; | |
| } | |
| Status = gBS->OpenProtocol ( | |
| Private->Ip6ChildHandle, | |
| &gEfiIp6ProtocolGuid, | |
| (VOID **) &Private->Ip6, | |
| Private->ImageHandle, | |
| Private->Ip6ChildHandle, | |
| EFI_OPEN_PROTOCOL_GET_PROTOCOL | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| goto ON_ERROR; | |
| } | |
| ZeroMem (&Ip6Config, sizeof (EFI_IP6_CONFIG_DATA)); | |
| // | |
| // Configure the ip6 instance for icmp6 packet exchange. | |
| // | |
| Ip6Config.DefaultProtocol = 58; | |
| Ip6Config.AcceptAnyProtocol = FALSE; | |
| Ip6Config.AcceptIcmpErrors = TRUE; | |
| Ip6Config.AcceptPromiscuous = FALSE; | |
| Ip6Config.TrafficClass = 0; | |
| Ip6Config.HopLimit = 128; | |
| Ip6Config.FlowLabel = 0; | |
| Ip6Config.ReceiveTimeout = 0; | |
| Ip6Config.TransmitTimeout = 0; | |
| IP6_COPY_ADDRESS (&Ip6Config.StationAddress, &Private->SrcAddress); | |
| IP6_COPY_ADDRESS (&Ip6Config.DestinationAddress, &Private->DstAddress); | |
| Status = Private->Ip6->Configure (Private->Ip6, &Ip6Config); | |
| if (EFI_ERROR (Status)) { | |
| ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6_CONFIG), gShellNetwork2HiiHandle, Status); | |
| goto ON_ERROR; | |
| } | |
| return EFI_SUCCESS; | |
| ON_ERROR: | |
| if (HandleBuffer != NULL) { | |
| FreePool (HandleBuffer); | |
| } | |
| if (IfInfo != NULL) { | |
| FreePool (IfInfo); | |
| } | |
| if ((Ip6Sb != NULL) && (Private->Ip6ChildHandle != NULL)) { | |
| Ip6Sb->DestroyChild (Ip6Sb, Private->Ip6ChildHandle); | |
| } | |
| return Status; | |
| } | |
| /** | |
| Destroy the IP6 instance. | |
| @param[in] Private The pointer of PING6_PRIVATE_DATA. | |
| **/ | |
| VOID | |
| Ping6DestroyIpInstance ( | |
| IN PING6_PRIVATE_DATA *Private | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_SERVICE_BINDING_PROTOCOL *Ip6Sb; | |
| gBS->CloseProtocol ( | |
| Private->Ip6ChildHandle, | |
| &gEfiIp6ProtocolGuid, | |
| Private->ImageHandle, | |
| Private->Ip6ChildHandle | |
| ); | |
| Status = gBS->HandleProtocol ( | |
| Private->NicHandle, | |
| &gEfiIp6ServiceBindingProtocolGuid, | |
| (VOID **) &Ip6Sb | |
| ); | |
| if (!EFI_ERROR(Status)) { | |
| Ip6Sb->DestroyChild (Ip6Sb, Private->Ip6ChildHandle); | |
| } | |
| } | |
| /** | |
| The Ping6 Process. | |
| @param[in] ImageHandle The firmware allocated handle for the UEFI image. | |
| @param[in] SendNumber The send request count. | |
| @param[in] BufferSize The send buffer size. | |
| @param[in] SrcAddress The source IPv6 address. | |
| @param[in] DstAddress The destination IPv6 address. | |
| @retval SHELL_SUCCESS The ping6 processed successfullly. | |
| @retval others The ping6 processed unsuccessfully. | |
| **/ | |
| SHELL_STATUS | |
| ShellPing6 ( | |
| IN EFI_HANDLE ImageHandle, | |
| IN UINT32 SendNumber, | |
| IN UINT32 BufferSize, | |
| IN EFI_IPv6_ADDRESS *SrcAddress, | |
| IN EFI_IPv6_ADDRESS *DstAddress | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_INPUT_KEY Key; | |
| PING6_PRIVATE_DATA *Private; | |
| PING6_ICMP6_TX_INFO *TxInfo; | |
| LIST_ENTRY *Entry; | |
| LIST_ENTRY *NextEntry; | |
| SHELL_STATUS ShellStatus; | |
| ShellStatus = SHELL_SUCCESS; | |
| Private = AllocateZeroPool (sizeof (PING6_PRIVATE_DATA)); | |
| if (Private == NULL) { | |
| ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_OUT_MEM), gShellNetwork2HiiHandle, L"Ping6"); | |
| ShellStatus = SHELL_OUT_OF_RESOURCES; | |
| goto ON_EXIT; | |
| } | |
| Private->ImageHandle = ImageHandle; | |
| Private->SendNum = SendNumber; | |
| Private->BufferSize = BufferSize; | |
| Private->RttMin = ~((UINT64 )(0x0)); | |
| Private->Status = EFI_NOT_READY; | |
| InitializeListHead (&Private->TxList); | |
| IP6_COPY_ADDRESS (&Private->SrcAddress, SrcAddress); | |
| IP6_COPY_ADDRESS (&Private->DstAddress, DstAddress); | |
| // | |
| // Open and configure a ip6 instance for ping6. | |
| // | |
| Status = Ping6CreateIpInstance (Private); | |
| if (EFI_ERROR (Status)) { | |
| ShellStatus = SHELL_ACCESS_DENIED; | |
| goto ON_EXIT; | |
| } | |
| // | |
| // Print the command line itself. | |
| // | |
| ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_START), gShellNetwork2HiiHandle, mIp6DstString, Private->BufferSize); | |
| // | |
| // Create a ipv6 token to receive the first icmp6 echo reply packet. | |
| // | |
| Status = Ping6OnReceiveEchoReply (Private); | |
| if (EFI_ERROR (Status)) { | |
| ShellStatus = SHELL_ACCESS_DENIED; | |
| goto ON_EXIT; | |
| } | |
| // | |
| // Create and start timer to send icmp6 echo request packet per second. | |
| // | |
| Status = gBS->CreateEvent ( | |
| EVT_TIMER | EVT_NOTIFY_SIGNAL, | |
| TPL_CALLBACK, | |
| Ping6OnTimerRoutine6, | |
| Private, | |
| &Private->Timer | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| ShellStatus = SHELL_ACCESS_DENIED; | |
| goto ON_EXIT; | |
| } | |
| // | |
| // Create a ipv6 token to send the first icmp6 echo request packet. | |
| // | |
| Status = Ping6SendEchoRequest (Private); | |
| // | |
| // EFI_NOT_READY for IPsec is enable and IKE is not established. | |
| // | |
| if (EFI_ERROR (Status) && (Status != EFI_NOT_READY)) { | |
| ShellStatus = SHELL_ACCESS_DENIED; | |
| if(Status == EFI_NOT_FOUND) { | |
| ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_NOSOURCE_INDOMAIN), gShellNetwork2HiiHandle, mIp6DstString); | |
| } | |
| goto ON_EXIT; | |
| } | |
| Status = gBS->SetTimer ( | |
| Private->Timer, | |
| TimerPeriodic, | |
| PING6_ONE_SECOND | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| ShellStatus = SHELL_ACCESS_DENIED; | |
| goto ON_EXIT; | |
| } | |
| // | |
| // Control the ping6 process by two factors: | |
| // 1. Hot key | |
| // 2. Private->Status | |
| // 2.1. success means all icmp6 echo request packets get reply packets. | |
| // 2.2. timeout means the last icmp6 echo reply request timeout to get reply. | |
| // 2.3. noready means ping6 process is on-the-go. | |
| // | |
| while (Private->Status == EFI_NOT_READY) { | |
| Private->Ip6->Poll (Private->Ip6); | |
| // | |
| // Terminate the ping6 process by 'esc' or 'ctl-c'. | |
| // | |
| Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); | |
| if (!EFI_ERROR(Status)) { | |
| if ((Key.UnicodeChar == 0x1b) || (Key.UnicodeChar == 0x03) || | |
| ((Key.UnicodeChar == 0) && (Key.ScanCode == SCAN_ESC))) { | |
| goto ON_STAT; | |
| } | |
| } | |
| } | |
| ON_STAT: | |
| // | |
| // Display the statistics in all. | |
| // | |
| gBS->SetTimer (Private->Timer, TimerCancel, 0); | |
| if (Private->TxCount != 0) { | |
| ShellPrintHiiEx ( | |
| -1, | |
| -1, | |
| NULL, | |
| STRING_TOKEN (STR_PING6_STAT), | |
| gShellNetwork2HiiHandle, | |
| Private->TxCount, | |
| Private->RxCount, | |
| (100 * (Private->TxCount - Private->RxCount)) / Private->TxCount, | |
| Private->RttSum | |
| ); | |
| } | |
| if (Private->RxCount != 0) { | |
| ShellPrintHiiEx ( | |
| -1, | |
| -1, | |
| NULL, | |
| STRING_TOKEN (STR_PING6_RTT), | |
| gShellNetwork2HiiHandle, | |
| Private->RttMin, | |
| Private->RttMax, | |
| DivU64x64Remainder (Private->RttSum, Private->RxCount, NULL) | |
| ); | |
| } | |
| ON_EXIT: | |
| if (Private != NULL) { | |
| Private->Status = EFI_ABORTED; | |
| NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) { | |
| TxInfo = BASE_CR (Entry, PING6_ICMP6_TX_INFO, Link); | |
| Status = Private->Ip6->Cancel (Private->Ip6, TxInfo->Token); | |
| RemoveEntryList (&TxInfo->Link); | |
| Ping6DestroyTxInfo (TxInfo); | |
| } | |
| if (Private->Timer != NULL) { | |
| gBS->CloseEvent (Private->Timer); | |
| } | |
| if (Private->Ip6 != NULL) { | |
| Status = Private->Ip6->Cancel (Private->Ip6, &Private->RxToken); | |
| } | |
| if (Private->RxToken.Event != NULL) { | |
| gBS->CloseEvent (Private->RxToken.Event); | |
| } | |
| if (Private->Ip6ChildHandle != NULL) { | |
| Ping6DestroyIpInstance (Private); | |
| } | |
| FreePool (Private); | |
| } | |
| return ShellStatus; | |
| } | |
| /** | |
| Function for 'ping6' command. | |
| @param[in] ImageHandle Handle to the Image (NULL if Internal). | |
| @param[in] SystemTable Pointer to the System Table (NULL if Internal). | |
| @retval SHELL_SUCCESS The ping6 processed successfullly. | |
| @retval others The ping6 processed unsuccessfully. | |
| **/ | |
| SHELL_STATUS | |
| EFIAPI | |
| ShellCommandRunPing6 ( | |
| IN EFI_HANDLE ImageHandle, | |
| IN EFI_SYSTEM_TABLE *SystemTable | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| SHELL_STATUS ShellStatus; | |
| EFI_IPv6_ADDRESS DstAddress; | |
| EFI_IPv6_ADDRESS SrcAddress; | |
| UINT64 BufferSize; | |
| UINTN SendNumber; | |
| LIST_ENTRY *ParamPackage; | |
| CONST CHAR16 *ValueStr; | |
| CONST CHAR16 *ValueStrPtr; | |
| UINTN NonOptionCount; | |
| CHAR16 *ProblemParam; | |
| ProblemParam = NULL; | |
| ShellStatus = SHELL_SUCCESS; | |
| Status = ShellCommandLineParseEx (Ping6ParamList, &ParamPackage, &ProblemParam, TRUE, FALSE); | |
| if (EFI_ERROR(Status)) { | |
| ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_INPUT), gShellNetwork2HiiHandle); | |
| ShellStatus = SHELL_INVALID_PARAMETER; | |
| goto ON_EXIT; | |
| } | |
| SendNumber = 10; | |
| BufferSize = 16; | |
| // | |
| // Parse the parameter of count number. | |
| // | |
| ValueStr = ShellCommandLineGetValue (ParamPackage, L"-n"); | |
| ValueStrPtr = ValueStr; | |
| if (ValueStr != NULL) { | |
| SendNumber = ShellStrToUintn (ValueStrPtr); | |
| // | |
| // ShellStrToUintn will return 0 when input is 0 or an invalid input string. | |
| // | |
| if ((SendNumber == 0) || (SendNumber > PING6_MAX_SEND_NUMBER)) { | |
| ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_SEND_NUMBER), gShellNetwork2HiiHandle, ValueStr); | |
| ShellStatus = SHELL_INVALID_PARAMETER; | |
| goto ON_EXIT; | |
| } | |
| } | |
| // | |
| // Parse the parameter of buffer size. | |
| // | |
| ValueStr = ShellCommandLineGetValue (ParamPackage, L"-l"); | |
| ValueStrPtr = ValueStr; | |
| if (ValueStr != NULL) { | |
| BufferSize = ShellStrToUintn (ValueStrPtr); | |
| // | |
| // ShellStrToUintn will return 0 when input is 0 or an invalid input string. | |
| // | |
| if ((BufferSize < 16) || (BufferSize > PING6_MAX_BUFFER_SIZE)) { | |
| ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_BUFFER_SIZE), gShellNetwork2HiiHandle, ValueStr); | |
| ShellStatus = SHELL_INVALID_PARAMETER; | |
| goto ON_EXIT; | |
| } | |
| } | |
| ZeroMem (&SrcAddress, sizeof (EFI_IPv6_ADDRESS)); | |
| ZeroMem (&DstAddress, sizeof (EFI_IPv6_ADDRESS)); | |
| // | |
| // Parse the parameter of source ip address. | |
| // | |
| ValueStr = ShellCommandLineGetValue (ParamPackage, L"-s"); | |
| ValueStrPtr = ValueStr; | |
| if (ValueStr != NULL) { | |
| mIp6SrcString = ValueStr; | |
| Status = NetLibStrToIp6 (ValueStrPtr, &SrcAddress); | |
| if (EFI_ERROR (Status)) { | |
| ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_IP), gShellNetwork2HiiHandle, ValueStr); | |
| ShellStatus = SHELL_INVALID_PARAMETER; | |
| goto ON_EXIT; | |
| } | |
| } | |
| // | |
| // Parse the parameter of destination ip address. | |
| // | |
| NonOptionCount = ShellCommandLineGetCount(ParamPackage); | |
| ValueStr = ShellCommandLineGetRawValue (ParamPackage, (UINT32)(NonOptionCount-1)); | |
| if (NonOptionCount != 2) { | |
| ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_INPUT), gShellNetwork2HiiHandle); | |
| ShellStatus = SHELL_INVALID_PARAMETER; | |
| goto ON_EXIT; | |
| } | |
| ValueStrPtr = ValueStr; | |
| if (ValueStr != NULL) { | |
| mIp6DstString = ValueStr; | |
| Status = NetLibStrToIp6 (ValueStrPtr, &DstAddress); | |
| if (EFI_ERROR (Status)) { | |
| ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_IP), gShellNetwork2HiiHandle, ValueStr); | |
| ShellStatus = SHELL_INVALID_PARAMETER; | |
| goto ON_EXIT; | |
| } | |
| } | |
| // | |
| // Get frequency to calculate the time from ticks. | |
| // | |
| Status = Ping6GetFrequency (); | |
| if (EFI_ERROR(Status)) { | |
| ShellStatus = SHELL_ACCESS_DENIED; | |
| goto ON_EXIT; | |
| } | |
| // | |
| // Enter into ping6 process. | |
| // | |
| ShellStatus = ShellPing6 ( | |
| ImageHandle, | |
| (UINT32)SendNumber, | |
| (UINT32)BufferSize, | |
| &SrcAddress, | |
| &DstAddress | |
| ); | |
| ON_EXIT: | |
| ShellCommandLineFreeVarList (ParamPackage); | |
| return ShellStatus; | |
| } | |