| /** @file | |
| The implementation of the ARP protocol. | |
| Copyright (c) 2006 - 2020, Intel Corporation. All rights reserved.<BR> | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include "ArpImpl.h" | |
| // | |
| // Global variable of EFI ARP Protocol Interface. | |
| // | |
| EFI_ARP_PROTOCOL mEfiArpProtocolTemplate = { | |
| ArpConfigure, | |
| ArpAdd, | |
| ArpFind, | |
| ArpDelete, | |
| ArpFlush, | |
| ArpRequest, | |
| ArpCancel | |
| }; | |
| /** | |
| Initialize the instance context data. | |
| @param[in] ArpService Pointer to the arp service context data this | |
| instance belongs to. | |
| @param[out] Instance Pointer to the instance context data. | |
| @return None. | |
| **/ | |
| VOID | |
| ArpInitInstance ( | |
| IN ARP_SERVICE_DATA *ArpService, | |
| OUT ARP_INSTANCE_DATA *Instance | |
| ) | |
| { | |
| NET_CHECK_SIGNATURE (ArpService, ARP_SERVICE_DATA_SIGNATURE); | |
| Instance->Signature = ARP_INSTANCE_DATA_SIGNATURE; | |
| Instance->ArpService = ArpService; | |
| CopyMem (&Instance->ArpProto, &mEfiArpProtocolTemplate, sizeof (Instance->ArpProto)); | |
| Instance->Configured = FALSE; | |
| Instance->InDestroy = FALSE; | |
| InitializeListHead (&Instance->List); | |
| } | |
| /** | |
| Process the Arp packets received from Mnp, the procedure conforms to RFC826. | |
| @param[in] Context Pointer to the context data registered to the | |
| Event. | |
| @return None. | |
| **/ | |
| VOID | |
| EFIAPI | |
| ArpOnFrameRcvdDpc ( | |
| IN VOID *Context | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| ARP_SERVICE_DATA *ArpService; | |
| EFI_MANAGED_NETWORK_COMPLETION_TOKEN *RxToken; | |
| EFI_MANAGED_NETWORK_RECEIVE_DATA *RxData; | |
| ARP_HEAD *Head; | |
| ARP_ADDRESS ArpAddress; | |
| ARP_CACHE_ENTRY *CacheEntry; | |
| LIST_ENTRY *Entry; | |
| ARP_INSTANCE_DATA *Instance; | |
| EFI_ARP_CONFIG_DATA *ConfigData; | |
| NET_ARP_ADDRESS SenderAddress[2]; | |
| BOOLEAN ProtoMatched; | |
| BOOLEAN IsTarget; | |
| BOOLEAN MergeFlag; | |
| ArpService = (ARP_SERVICE_DATA *)Context; | |
| NET_CHECK_SIGNATURE (ArpService, ARP_SERVICE_DATA_SIGNATURE); | |
| RxToken = &ArpService->RxToken; | |
| if (RxToken->Status == EFI_ABORTED) { | |
| // | |
| // The Token is aborted, possibly by arp itself, just return and the receiving | |
| // process is stopped. | |
| // | |
| return; | |
| } | |
| if (EFI_ERROR (RxToken->Status)) { | |
| // | |
| // Restart the receiving if any other error Status occurs. | |
| // | |
| goto RESTART_RECEIVE; | |
| } | |
| // | |
| // Status is EFI_SUCCESS, process the received frame. | |
| // | |
| RxData = RxToken->Packet.RxData; | |
| // | |
| // Sanity check. | |
| // | |
| if (RxData->DataLength < sizeof (ARP_HEAD)) { | |
| // | |
| // Restart the receiving if packet size is not correct. | |
| // | |
| goto RECYCLE_RXDATA; | |
| } | |
| // | |
| // Convert the byte order of the multi-byte fields. | |
| // | |
| Head = (ARP_HEAD *)RxData->PacketData; | |
| Head->HwType = NTOHS (Head->HwType); | |
| Head->ProtoType = NTOHS (Head->ProtoType); | |
| Head->OpCode = NTOHS (Head->OpCode); | |
| if (RxData->DataLength < (sizeof (ARP_HEAD) + 2 * Head->HwAddrLen + 2 * Head->ProtoAddrLen)) { | |
| goto RECYCLE_RXDATA; | |
| } | |
| if ((Head->HwType != ArpService->SnpMode.IfType) || | |
| (Head->HwAddrLen != ArpService->SnpMode.HwAddressSize) || | |
| (RxData->ProtocolType != ARP_ETHER_PROTO_TYPE)) | |
| { | |
| // | |
| // The hardware type or the hardware address length doesn't match. | |
| // There is a sanity check for the protocol type too. | |
| // | |
| goto RECYCLE_RXDATA; | |
| } | |
| // | |
| // Set the pointers to the addresses contained in the arp packet. | |
| // | |
| ArpAddress.SenderHwAddr = (UINT8 *)(Head + 1); | |
| ArpAddress.SenderProtoAddr = ArpAddress.SenderHwAddr + Head->HwAddrLen; | |
| ArpAddress.TargetHwAddr = ArpAddress.SenderProtoAddr + Head->ProtoAddrLen; | |
| ArpAddress.TargetProtoAddr = ArpAddress.TargetHwAddr + Head->HwAddrLen; | |
| SenderAddress[Hardware].Type = Head->HwType; | |
| SenderAddress[Hardware].Length = Head->HwAddrLen; | |
| SenderAddress[Hardware].AddressPtr = ArpAddress.SenderHwAddr; | |
| SenderAddress[Protocol].Type = Head->ProtoType; | |
| SenderAddress[Protocol].Length = Head->ProtoAddrLen; | |
| SenderAddress[Protocol].AddressPtr = ArpAddress.SenderProtoAddr; | |
| // | |
| // First, check the denied cache table. | |
| // | |
| CacheEntry = ArpFindDeniedCacheEntry ( | |
| ArpService, | |
| &SenderAddress[Protocol], | |
| &SenderAddress[Hardware] | |
| ); | |
| if (CacheEntry != NULL) { | |
| // | |
| // This address (either hardware or protocol address, or both) is configured to | |
| // be a deny entry, silently skip the normal process. | |
| // | |
| goto RECYCLE_RXDATA; | |
| } | |
| ProtoMatched = FALSE; | |
| IsTarget = FALSE; | |
| Instance = NULL; | |
| NET_LIST_FOR_EACH (Entry, &ArpService->ChildrenList) { | |
| // | |
| // Iterate all the children. | |
| // | |
| Instance = NET_LIST_USER_STRUCT (Entry, ARP_INSTANCE_DATA, List); | |
| NET_CHECK_SIGNATURE (Instance, ARP_INSTANCE_DATA_SIGNATURE); | |
| ConfigData = &Instance->ConfigData; | |
| if ((Instance->Configured) && | |
| (Head->ProtoType == ConfigData->SwAddressType) && | |
| (Head->ProtoAddrLen == ConfigData->SwAddressLength)) | |
| { | |
| // | |
| // The protocol type is matched for the received arp packet. | |
| // | |
| ProtoMatched = TRUE; | |
| if (0 == CompareMem ( | |
| (VOID *)ArpAddress.TargetProtoAddr, | |
| ConfigData->StationAddress, | |
| ConfigData->SwAddressLength | |
| )) | |
| { | |
| // | |
| // The arp driver has the target address required by the received arp packet. | |
| // | |
| IsTarget = TRUE; | |
| break; | |
| } | |
| } | |
| } | |
| if (!ProtoMatched) { | |
| // | |
| // Protocol type unmatchable, skip. | |
| // | |
| goto RECYCLE_RXDATA; | |
| } | |
| // | |
| // Check whether the sender's address information is already in the cache. | |
| // | |
| MergeFlag = FALSE; | |
| CacheEntry = ArpFindNextCacheEntryInTable ( | |
| &ArpService->ResolvedCacheTable, | |
| NULL, | |
| ByProtoAddress, | |
| &SenderAddress[Protocol], | |
| NULL | |
| ); | |
| if (CacheEntry != NULL) { | |
| // | |
| // Update the entry with the new information. | |
| // | |
| ArpFillAddressInCacheEntry (CacheEntry, &SenderAddress[Hardware], NULL); | |
| CacheEntry->DecayTime = CacheEntry->DefaultDecayTime; | |
| MergeFlag = TRUE; | |
| } | |
| if (!IsTarget) { | |
| // | |
| // This arp packet isn't targeted to us, skip now. | |
| // | |
| goto RECYCLE_RXDATA; | |
| } | |
| if (!MergeFlag) { | |
| // | |
| // Add the triplet <protocol type, sender protocol address, sender hardware address> | |
| // to the translation table. | |
| // | |
| CacheEntry = ArpFindNextCacheEntryInTable ( | |
| &ArpService->PendingRequestTable, | |
| NULL, | |
| ByProtoAddress, | |
| &SenderAddress[Protocol], | |
| NULL | |
| ); | |
| if (CacheEntry == NULL) { | |
| // | |
| // Allocate a new CacheEntry. | |
| // | |
| CacheEntry = ArpAllocCacheEntry (NULL); | |
| if (CacheEntry == NULL) { | |
| goto RECYCLE_RXDATA; | |
| } | |
| } | |
| if (!IsListEmpty (&CacheEntry->List)) { | |
| RemoveEntryList (&CacheEntry->List); | |
| } | |
| // | |
| // Fill the addresses into the CacheEntry. | |
| // | |
| ArpFillAddressInCacheEntry ( | |
| CacheEntry, | |
| &SenderAddress[Hardware], | |
| &SenderAddress[Protocol] | |
| ); | |
| // | |
| // Inform the user. | |
| // | |
| ArpAddressResolved (CacheEntry, NULL, NULL); | |
| // | |
| // Add this entry into the ResolvedCacheTable | |
| // | |
| InsertHeadList (&ArpService->ResolvedCacheTable, &CacheEntry->List); | |
| } | |
| if (Head->OpCode == ARP_OPCODE_REQUEST) { | |
| // | |
| // Send back the ARP Reply. If we reach here, Instance is not NULL and CacheEntry | |
| // is not NULL. | |
| // | |
| ArpSendFrame (Instance, CacheEntry, ARP_OPCODE_REPLY); | |
| } | |
| RECYCLE_RXDATA: | |
| // | |
| // Signal Mnp to recycle the RxData. | |
| // | |
| gBS->SignalEvent (RxData->RecycleEvent); | |
| RESTART_RECEIVE: | |
| // | |
| // Continue to receive packets from Mnp. | |
| // | |
| Status = ArpService->Mnp->Receive (ArpService->Mnp, RxToken); | |
| DEBUG_CODE_BEGIN (); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "ArpOnFrameRcvd: ArpService->Mnp->Receive " | |
| "failed, %r\n.", | |
| Status | |
| )); | |
| } | |
| DEBUG_CODE_END (); | |
| } | |
| /** | |
| Queue ArpOnFrameRcvdDpc as a DPC at TPL_CALLBACK. | |
| @param[in] Event The Event this notify function registered to. | |
| @param[in] Context Pointer to the context data registered to the | |
| Event. | |
| @return None. | |
| **/ | |
| VOID | |
| EFIAPI | |
| ArpOnFrameRcvd ( | |
| IN EFI_EVENT Event, | |
| IN VOID *Context | |
| ) | |
| { | |
| // | |
| // Request ArpOnFrameRcvdDpc as a DPC at TPL_CALLBACK | |
| // | |
| QueueDpc (TPL_CALLBACK, ArpOnFrameRcvdDpc, Context); | |
| } | |
| /** | |
| Process the already sent arp packets. | |
| @param[in] Context Pointer to the context data registered to the | |
| Event. | |
| @return None. | |
| **/ | |
| VOID | |
| EFIAPI | |
| ArpOnFrameSentDpc ( | |
| IN VOID *Context | |
| ) | |
| { | |
| EFI_MANAGED_NETWORK_COMPLETION_TOKEN *TxToken; | |
| EFI_MANAGED_NETWORK_TRANSMIT_DATA *TxData; | |
| ASSERT (Context != NULL); | |
| TxToken = (EFI_MANAGED_NETWORK_COMPLETION_TOKEN *)Context; | |
| TxData = TxToken->Packet.TxData; | |
| DEBUG_CODE_BEGIN (); | |
| if (EFI_ERROR (TxToken->Status)) { | |
| DEBUG ((DEBUG_ERROR, "ArpOnFrameSent: TxToken->Status, %r.\n", TxToken->Status)); | |
| } | |
| DEBUG_CODE_END (); | |
| // | |
| // Free the allocated memory and close the event. | |
| // | |
| FreePool (TxData->FragmentTable[0].FragmentBuffer); | |
| FreePool (TxData); | |
| gBS->CloseEvent (TxToken->Event); | |
| FreePool (TxToken); | |
| } | |
| /** | |
| Request ArpOnFrameSentDpc as a DPC at TPL_CALLBACK. | |
| @param[in] Event The Event this notify function registered to. | |
| @param[in] Context Pointer to the context data registered to the | |
| Event. | |
| @return None. | |
| **/ | |
| VOID | |
| EFIAPI | |
| ArpOnFrameSent ( | |
| IN EFI_EVENT Event, | |
| IN VOID *Context | |
| ) | |
| { | |
| // | |
| // Request ArpOnFrameSentDpc as a DPC at TPL_CALLBACK | |
| // | |
| QueueDpc (TPL_CALLBACK, ArpOnFrameSentDpc, Context); | |
| } | |
| /** | |
| Process the arp cache olding and drive the retrying arp requests. | |
| @param[in] Event The Event this notify function registered to. | |
| @param[in] Context Pointer to the context data registered to the | |
| Event. | |
| @return None. | |
| **/ | |
| VOID | |
| EFIAPI | |
| ArpTimerHandler ( | |
| IN EFI_EVENT Event, | |
| IN VOID *Context | |
| ) | |
| { | |
| ARP_SERVICE_DATA *ArpService; | |
| LIST_ENTRY *Entry; | |
| LIST_ENTRY *NextEntry; | |
| LIST_ENTRY *ContextEntry; | |
| ARP_CACHE_ENTRY *CacheEntry; | |
| USER_REQUEST_CONTEXT *RequestContext; | |
| ASSERT (Context != NULL); | |
| ArpService = (ARP_SERVICE_DATA *)Context; | |
| // | |
| // Iterate all the pending requests to see whether a retry is needed to send out | |
| // or the request finally fails because the retry time reaches the limitation. | |
| // | |
| NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &ArpService->PendingRequestTable) { | |
| CacheEntry = NET_LIST_USER_STRUCT (Entry, ARP_CACHE_ENTRY, List); | |
| if (CacheEntry->NextRetryTime <= ARP_PERIODIC_TIMER_INTERVAL) { | |
| // | |
| // Timeout, if we can retry more, send out the request again, otherwise abort | |
| // this request. | |
| // | |
| if (CacheEntry->RetryCount == 0) { | |
| // | |
| // Abort this request. | |
| // | |
| ArpAddressResolved (CacheEntry, NULL, NULL); | |
| ASSERT (IsListEmpty (&CacheEntry->UserRequestList)); | |
| RemoveEntryList (&CacheEntry->List); | |
| FreePool (CacheEntry); | |
| } else { | |
| // | |
| // resend the ARP request. | |
| // | |
| ASSERT (!IsListEmpty (&CacheEntry->UserRequestList)); | |
| ContextEntry = CacheEntry->UserRequestList.ForwardLink; | |
| RequestContext = NET_LIST_USER_STRUCT (ContextEntry, USER_REQUEST_CONTEXT, List); | |
| ArpSendFrame (RequestContext->Instance, CacheEntry, ARP_OPCODE_REQUEST); | |
| CacheEntry->RetryCount--; | |
| CacheEntry->NextRetryTime = RequestContext->Instance->ConfigData.RetryTimeOut; | |
| } | |
| } else { | |
| // | |
| // Update the NextRetryTime. | |
| // | |
| CacheEntry->NextRetryTime -= ARP_PERIODIC_TIMER_INTERVAL; | |
| } | |
| } | |
| // | |
| // Check the timeouts for the DeniedCacheTable. | |
| // | |
| NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &ArpService->DeniedCacheTable) { | |
| CacheEntry = NET_LIST_USER_STRUCT (Entry, ARP_CACHE_ENTRY, List); | |
| ASSERT (IsListEmpty (&CacheEntry->UserRequestList)); | |
| if (CacheEntry->DefaultDecayTime == 0) { | |
| // | |
| // It's a static entry, skip it. | |
| // | |
| continue; | |
| } | |
| if (CacheEntry->DecayTime <= ARP_PERIODIC_TIMER_INTERVAL) { | |
| // | |
| // Time out, remove it. | |
| // | |
| RemoveEntryList (&CacheEntry->List); | |
| FreePool (CacheEntry); | |
| } else { | |
| // | |
| // Update the DecayTime. | |
| // | |
| CacheEntry->DecayTime -= ARP_PERIODIC_TIMER_INTERVAL; | |
| } | |
| } | |
| // | |
| // Check the timeouts for the ResolvedCacheTable. | |
| // | |
| NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &ArpService->ResolvedCacheTable) { | |
| CacheEntry = NET_LIST_USER_STRUCT (Entry, ARP_CACHE_ENTRY, List); | |
| ASSERT (IsListEmpty (&CacheEntry->UserRequestList)); | |
| if (CacheEntry->DefaultDecayTime == 0) { | |
| // | |
| // It's a static entry, skip it. | |
| // | |
| continue; | |
| } | |
| if (CacheEntry->DecayTime <= ARP_PERIODIC_TIMER_INTERVAL) { | |
| // | |
| // Time out, remove it. | |
| // | |
| RemoveEntryList (&CacheEntry->List); | |
| FreePool (CacheEntry); | |
| } else { | |
| // | |
| // Update the DecayTime. | |
| // | |
| CacheEntry->DecayTime -= ARP_PERIODIC_TIMER_INTERVAL; | |
| } | |
| } | |
| } | |
| /** | |
| Match the two NET_ARP_ADDRESSes. | |
| @param[in] AddressOne Pointer to the first address to match. | |
| @param[in] AddressTwo Pointer to the second address to match. | |
| @return The two addresses match or not. | |
| **/ | |
| BOOLEAN | |
| ArpMatchAddress ( | |
| IN NET_ARP_ADDRESS *AddressOne, | |
| IN NET_ARP_ADDRESS *AddressTwo | |
| ) | |
| { | |
| ASSERT (AddressOne != NULL && AddressTwo != NULL); | |
| if ((AddressOne->Type != AddressTwo->Type) || | |
| (AddressOne->Length != AddressTwo->Length)) | |
| { | |
| // | |
| // Either Type or Length doesn't match. | |
| // | |
| return FALSE; | |
| } | |
| if ((AddressOne->AddressPtr != NULL) && | |
| (CompareMem ( | |
| AddressOne->AddressPtr, | |
| AddressTwo->AddressPtr, | |
| AddressOne->Length | |
| ) != 0)) | |
| { | |
| // | |
| // The address is not the same. | |
| // | |
| return FALSE; | |
| } | |
| return TRUE; | |
| } | |
| /** | |
| Find the CacheEntry which matches the requirements in the specified CacheTable. | |
| @param[in] CacheTable Pointer to the arp cache table. | |
| @param[in] StartEntry Pointer to the start entry this search begins with | |
| in the cache table. | |
| @param[in] FindOpType The search type. | |
| @param[in] ProtocolAddress Pointer to the protocol address to match. | |
| @param[in] HardwareAddress Pointer to the hardware address to match. | |
| @return Pointer to the matched arp cache entry, if NULL, no match is found. | |
| **/ | |
| ARP_CACHE_ENTRY * | |
| ArpFindNextCacheEntryInTable ( | |
| IN LIST_ENTRY *CacheTable, | |
| IN LIST_ENTRY *StartEntry, | |
| IN FIND_OPTYPE FindOpType, | |
| IN NET_ARP_ADDRESS *ProtocolAddress OPTIONAL, | |
| IN NET_ARP_ADDRESS *HardwareAddress OPTIONAL | |
| ) | |
| { | |
| LIST_ENTRY *Entry; | |
| ARP_CACHE_ENTRY *CacheEntry; | |
| if (StartEntry == NULL) { | |
| // | |
| // Start from the beginning of the table if no StartEntry is specified. | |
| // | |
| StartEntry = CacheTable; | |
| } | |
| for (Entry = StartEntry->ForwardLink; Entry != CacheTable; Entry = Entry->ForwardLink) { | |
| CacheEntry = NET_LIST_USER_STRUCT (Entry, ARP_CACHE_ENTRY, List); | |
| if ((FindOpType & MATCH_SW_ADDRESS) != 0) { | |
| // | |
| // Find by the software address. | |
| // | |
| if (!ArpMatchAddress (ProtocolAddress, &CacheEntry->Addresses[Protocol])) { | |
| // | |
| // The ProtocolAddress doesn't match, continue to the next cache entry. | |
| // | |
| continue; | |
| } | |
| } | |
| if ((FindOpType & MATCH_HW_ADDRESS) != 0) { | |
| // | |
| // Find by the hardware address. | |
| // | |
| if (!ArpMatchAddress (HardwareAddress, &CacheEntry->Addresses[Hardware])) { | |
| // | |
| // The HardwareAddress doesn't match, continue to the next cache entry. | |
| // | |
| continue; | |
| } | |
| } | |
| // | |
| // The CacheEntry meets the requirements now, return this entry. | |
| // | |
| return CacheEntry; | |
| } | |
| // | |
| // No matching. | |
| // | |
| return NULL; | |
| } | |
| /** | |
| Find the CacheEntry, using ProtocolAddress or HardwareAddress or both, as the keyword, | |
| in the DeniedCacheTable. | |
| @param[in] ArpService Pointer to the arp service context data. | |
| @param[in] ProtocolAddress Pointer to the protocol address. | |
| @param[in] HardwareAddress Pointer to the hardware address. | |
| @return Pointer to the matched cache entry, if NULL no match is found. | |
| **/ | |
| ARP_CACHE_ENTRY * | |
| ArpFindDeniedCacheEntry ( | |
| IN ARP_SERVICE_DATA *ArpService, | |
| IN NET_ARP_ADDRESS *ProtocolAddress OPTIONAL, | |
| IN NET_ARP_ADDRESS *HardwareAddress OPTIONAL | |
| ) | |
| { | |
| ARP_CACHE_ENTRY *CacheEntry; | |
| ASSERT ((ProtocolAddress != NULL) || (HardwareAddress != NULL)); | |
| NET_CHECK_SIGNATURE (ArpService, ARP_SERVICE_DATA_SIGNATURE); | |
| CacheEntry = NULL; | |
| if ((ProtocolAddress != NULL) && (ProtocolAddress->AddressPtr != NULL)) { | |
| // | |
| // Find the cache entry in the DeniedCacheTable by the protocol address. | |
| // | |
| CacheEntry = ArpFindNextCacheEntryInTable ( | |
| &ArpService->DeniedCacheTable, | |
| NULL, | |
| ByProtoAddress, | |
| ProtocolAddress, | |
| NULL | |
| ); | |
| if (CacheEntry != NULL) { | |
| // | |
| // There is a match. | |
| // | |
| return CacheEntry; | |
| } | |
| } | |
| if ((HardwareAddress != NULL) && (HardwareAddress->AddressPtr != NULL)) { | |
| // | |
| // Find the cache entry in the DeniedCacheTable by the hardware address. | |
| // | |
| CacheEntry = ArpFindNextCacheEntryInTable ( | |
| &ArpService->DeniedCacheTable, | |
| NULL, | |
| ByHwAddress, | |
| NULL, | |
| HardwareAddress | |
| ); | |
| } | |
| return CacheEntry; | |
| } | |
| /** | |
| Allocate a cache entry and initialize it. | |
| @param[in] Instance Pointer to the instance context data. | |
| @return Pointer to the new created cache entry. | |
| **/ | |
| ARP_CACHE_ENTRY * | |
| ArpAllocCacheEntry ( | |
| IN ARP_INSTANCE_DATA *Instance | |
| ) | |
| { | |
| ARP_CACHE_ENTRY *CacheEntry; | |
| NET_ARP_ADDRESS *Address; | |
| UINT16 Index; | |
| // | |
| // Allocate memory for the cache entry. | |
| // | |
| CacheEntry = AllocatePool (sizeof (ARP_CACHE_ENTRY)); | |
| if (CacheEntry == NULL) { | |
| return NULL; | |
| } | |
| // | |
| // Init the lists. | |
| // | |
| InitializeListHead (&CacheEntry->List); | |
| InitializeListHead (&CacheEntry->UserRequestList); | |
| for (Index = 0; Index < 2; Index++) { | |
| // | |
| // Init the address pointers to point to the concrete buffer. | |
| // | |
| Address = &CacheEntry->Addresses[Index]; | |
| Address->AddressPtr = Address->Buffer.ProtoAddress; | |
| } | |
| // | |
| // Zero the hardware address first. | |
| // | |
| ZeroMem (CacheEntry->Addresses[Hardware].AddressPtr, ARP_MAX_HARDWARE_ADDRESS_LEN); | |
| if (Instance != NULL) { | |
| // | |
| // Inherit the parameters from the instance configuration. | |
| // | |
| CacheEntry->RetryCount = Instance->ConfigData.RetryCount; | |
| CacheEntry->NextRetryTime = Instance->ConfigData.RetryTimeOut; | |
| CacheEntry->DefaultDecayTime = Instance->ConfigData.EntryTimeOut; | |
| CacheEntry->DecayTime = Instance->ConfigData.EntryTimeOut; | |
| } else { | |
| // | |
| // Use the default parameters if this cache entry isn't allocate in a | |
| // instance's scope. | |
| // | |
| CacheEntry->RetryCount = ARP_DEFAULT_RETRY_COUNT; | |
| CacheEntry->NextRetryTime = ARP_DEFAULT_RETRY_INTERVAL; | |
| CacheEntry->DefaultDecayTime = ARP_DEFAULT_TIMEOUT_VALUE; | |
| CacheEntry->DecayTime = ARP_DEFAULT_TIMEOUT_VALUE; | |
| } | |
| return CacheEntry; | |
| } | |
| /** | |
| Turn the CacheEntry into the resolved status. | |
| @param[in] CacheEntry Pointer to the resolved cache entry. | |
| @param[in] Instance Pointer to the instance context data. | |
| @param[in] UserEvent Pointer to the UserEvent to notify. | |
| @return The count of notifications sent to the instance. | |
| **/ | |
| UINTN | |
| ArpAddressResolved ( | |
| IN ARP_CACHE_ENTRY *CacheEntry, | |
| IN ARP_INSTANCE_DATA *Instance OPTIONAL, | |
| IN EFI_EVENT UserEvent OPTIONAL | |
| ) | |
| { | |
| LIST_ENTRY *Entry; | |
| LIST_ENTRY *NextEntry; | |
| USER_REQUEST_CONTEXT *Context; | |
| UINTN Count; | |
| Count = 0; | |
| // | |
| // Iterate all the linked user requests to notify them. | |
| // | |
| NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &CacheEntry->UserRequestList) { | |
| Context = NET_LIST_USER_STRUCT (Entry, USER_REQUEST_CONTEXT, List); | |
| if (((Instance == NULL) || (Context->Instance == Instance)) && | |
| ((UserEvent == NULL) || (Context->UserRequestEvent == UserEvent))) | |
| { | |
| // | |
| // Copy the address to the user-provided buffer and notify the user. | |
| // | |
| CopyMem ( | |
| Context->UserHwAddrBuffer, | |
| CacheEntry->Addresses[Hardware].AddressPtr, | |
| CacheEntry->Addresses[Hardware].Length | |
| ); | |
| gBS->SignalEvent (Context->UserRequestEvent); | |
| // | |
| // Remove this user request and free the context data. | |
| // | |
| RemoveEntryList (&Context->List); | |
| FreePool (Context); | |
| Count++; | |
| } | |
| } | |
| // | |
| // Dispatch the DPCs queued by the NotifyFunction of the Context->UserRequestEvent. | |
| // | |
| DispatchDpc (); | |
| return Count; | |
| } | |
| /** | |
| Fill the addresses in the CacheEntry using the information passed in by | |
| HwAddr and SwAddr. | |
| @param[in] CacheEntry Pointer to the cache entry. | |
| @param[in] HwAddr Pointer to the software address. | |
| @param[in] SwAddr Pointer to the hardware address. | |
| @return None. | |
| **/ | |
| VOID | |
| ArpFillAddressInCacheEntry ( | |
| IN ARP_CACHE_ENTRY *CacheEntry, | |
| IN NET_ARP_ADDRESS *HwAddr OPTIONAL, | |
| IN NET_ARP_ADDRESS *SwAddr OPTIONAL | |
| ) | |
| { | |
| NET_ARP_ADDRESS *Address[2]; | |
| NET_ARP_ADDRESS *CacheAddress; | |
| UINT32 Index; | |
| Address[Hardware] = HwAddr; | |
| Address[Protocol] = SwAddr; | |
| for (Index = 0; Index < 2; Index++) { | |
| if (Address[Index] != NULL) { | |
| // | |
| // Fill the address if the passed in pointer is not NULL. | |
| // | |
| CacheAddress = &CacheEntry->Addresses[Index]; | |
| CacheAddress->Type = Address[Index]->Type; | |
| CacheAddress->Length = Address[Index]->Length; | |
| if (Address[Index]->AddressPtr != NULL) { | |
| // | |
| // Copy it if the AddressPtr points to some buffer. | |
| // | |
| CopyMem ( | |
| CacheAddress->AddressPtr, | |
| Address[Index]->AddressPtr, | |
| CacheAddress->Length | |
| ); | |
| } else { | |
| // | |
| // Zero the corresponding address buffer in the CacheEntry. | |
| // | |
| ZeroMem (CacheAddress->AddressPtr, CacheAddress->Length); | |
| } | |
| } | |
| } | |
| } | |
| /** | |
| Configure the instance using the ConfigData. ConfigData is already validated. | |
| @param[in] Instance Pointer to the instance context data to be | |
| configured. | |
| @param[in] ConfigData Pointer to the configuration data used to | |
| configure the instance. | |
| @retval EFI_SUCCESS The instance is configured with the ConfigData. | |
| @retval EFI_ACCESS_DENIED The instance is already configured and the | |
| ConfigData tries to reset some unchangeable | |
| fields. | |
| @retval EFI_INVALID_PARAMETER The ConfigData provides a non-unicast IPv4 address | |
| when the SwAddressType is IPv4. | |
| @retval EFI_OUT_OF_RESOURCES The instance fails to configure due to memory | |
| limitation. | |
| **/ | |
| EFI_STATUS | |
| ArpConfigureInstance ( | |
| IN ARP_INSTANCE_DATA *Instance, | |
| IN EFI_ARP_CONFIG_DATA *ConfigData OPTIONAL | |
| ) | |
| { | |
| EFI_ARP_CONFIG_DATA *OldConfigData; | |
| IP4_ADDR Ip; | |
| OldConfigData = &Instance->ConfigData; | |
| if (ConfigData != NULL) { | |
| if (Instance->Configured) { | |
| // | |
| // The instance is configured, check the unchangeable fields. | |
| // | |
| if ((OldConfigData->SwAddressType != ConfigData->SwAddressType) || | |
| (OldConfigData->SwAddressLength != ConfigData->SwAddressLength) || | |
| (CompareMem ( | |
| OldConfigData->StationAddress, | |
| ConfigData->StationAddress, | |
| OldConfigData->SwAddressLength | |
| ) != 0)) | |
| { | |
| // | |
| // Deny the unallowed changes. | |
| // | |
| return EFI_ACCESS_DENIED; | |
| } | |
| } else { | |
| // | |
| // The instance is not configured. | |
| // | |
| if (ConfigData->SwAddressType == IPV4_ETHER_PROTO_TYPE) { | |
| CopyMem (&Ip, ConfigData->StationAddress, sizeof (IP4_ADDR)); | |
| if (IP4_IS_UNSPECIFIED (Ip) || IP4_IS_LOCAL_BROADCAST (Ip)) { | |
| // | |
| // The station address should not be zero or broadcast address. | |
| // | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| } | |
| // | |
| // Save the configuration. | |
| // | |
| CopyMem (OldConfigData, ConfigData, sizeof (*OldConfigData)); | |
| OldConfigData->StationAddress = AllocatePool (OldConfigData->SwAddressLength); | |
| if (OldConfigData->StationAddress == NULL) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "ArpConfigInstance: AllocatePool for the StationAddress " | |
| "failed.\n" | |
| )); | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| // | |
| // Save the StationAddress. | |
| // | |
| CopyMem ( | |
| OldConfigData->StationAddress, | |
| ConfigData->StationAddress, | |
| OldConfigData->SwAddressLength | |
| ); | |
| // | |
| // Set the state to configured. | |
| // | |
| Instance->Configured = TRUE; | |
| } | |
| // | |
| // Use the implementation specific values if the following field is zero. | |
| // | |
| OldConfigData->EntryTimeOut = (ConfigData->EntryTimeOut == 0) ? | |
| ARP_DEFAULT_TIMEOUT_VALUE : ConfigData->EntryTimeOut; | |
| OldConfigData->RetryCount = (ConfigData->RetryCount == 0) ? | |
| ARP_DEFAULT_RETRY_COUNT : ConfigData->RetryCount; | |
| OldConfigData->RetryTimeOut = (ConfigData->RetryTimeOut == 0) ? | |
| ARP_DEFAULT_RETRY_INTERVAL : ConfigData->RetryTimeOut; | |
| } else { | |
| // | |
| // Reset the configuration. | |
| // | |
| if (Instance->Configured) { | |
| // | |
| // Cancel the arp requests issued by this instance. | |
| // | |
| Instance->ArpProto.Cancel (&Instance->ArpProto, NULL, NULL); | |
| // | |
| // Free the buffer previously allocated to hold the station address. | |
| // | |
| FreePool (OldConfigData->StationAddress); | |
| } | |
| Instance->Configured = FALSE; | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Send out an arp frame using the CacheEntry and the ArpOpCode. | |
| @param[in] Instance Pointer to the instance context data. | |
| @param[in] CacheEntry Pointer to the configuration data used to | |
| configure the instance. | |
| @param[in] ArpOpCode The opcode used to send out this Arp frame, either | |
| request or reply. | |
| @return None. | |
| **/ | |
| VOID | |
| ArpSendFrame ( | |
| IN ARP_INSTANCE_DATA *Instance, | |
| IN ARP_CACHE_ENTRY *CacheEntry, | |
| IN UINT16 ArpOpCode | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_MANAGED_NETWORK_COMPLETION_TOKEN *TxToken; | |
| EFI_MANAGED_NETWORK_TRANSMIT_DATA *TxData; | |
| UINT32 TotalLength; | |
| UINT8 *Packet; | |
| ARP_SERVICE_DATA *ArpService; | |
| EFI_SIMPLE_NETWORK_MODE *SnpMode; | |
| EFI_ARP_CONFIG_DATA *ConfigData; | |
| UINT8 *TmpPtr; | |
| ARP_HEAD *ArpHead; | |
| ASSERT ((Instance != NULL) && (CacheEntry != NULL)); | |
| // | |
| // Allocate memory for the TxToken. | |
| // | |
| TxToken = AllocatePool (sizeof (EFI_MANAGED_NETWORK_COMPLETION_TOKEN)); | |
| if (TxToken == NULL) { | |
| DEBUG ((DEBUG_ERROR, "ArpSendFrame: Allocate memory for TxToken failed.\n")); | |
| return; | |
| } | |
| TxToken->Event = NULL; | |
| TxData = NULL; | |
| Packet = NULL; | |
| // | |
| // Create the event for this TxToken. | |
| // | |
| Status = gBS->CreateEvent ( | |
| EVT_NOTIFY_SIGNAL, | |
| TPL_NOTIFY, | |
| ArpOnFrameSent, | |
| (VOID *)TxToken, | |
| &TxToken->Event | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "ArpSendFrame: CreateEvent failed for TxToken->Event.\n")); | |
| goto CLEAN_EXIT; | |
| } | |
| // | |
| // Allocate memory for the TxData used in the TxToken. | |
| // | |
| TxData = AllocatePool (sizeof (EFI_MANAGED_NETWORK_TRANSMIT_DATA)); | |
| if (TxData == NULL) { | |
| DEBUG ((DEBUG_ERROR, "ArpSendFrame: Allocate memory for TxData failed.\n")); | |
| goto CLEAN_EXIT; | |
| } | |
| ArpService = Instance->ArpService; | |
| SnpMode = &ArpService->SnpMode; | |
| ConfigData = &Instance->ConfigData; | |
| // | |
| // Calculate the buffer length for this arp frame. | |
| // | |
| TotalLength = SnpMode->MediaHeaderSize + sizeof (ARP_HEAD) + | |
| 2 * (ConfigData->SwAddressLength + SnpMode->HwAddressSize); | |
| // | |
| // Allocate buffer for the arp frame. | |
| // | |
| Packet = AllocatePool (TotalLength); | |
| if (Packet == NULL) { | |
| DEBUG ((DEBUG_ERROR, "ArpSendFrame: Allocate memory for Packet failed.\n")); | |
| ASSERT (Packet != NULL); | |
| } | |
| TmpPtr = Packet; | |
| // | |
| // The destination MAC address. | |
| // | |
| if (ArpOpCode == ARP_OPCODE_REQUEST) { | |
| CopyMem (TmpPtr, &SnpMode->BroadcastAddress, SnpMode->HwAddressSize); | |
| } else { | |
| CopyMem ( | |
| TmpPtr, | |
| CacheEntry->Addresses[Hardware].AddressPtr, | |
| SnpMode->HwAddressSize | |
| ); | |
| } | |
| TmpPtr += SnpMode->HwAddressSize; | |
| // | |
| // The source MAC address. | |
| // | |
| CopyMem (TmpPtr, &SnpMode->CurrentAddress, SnpMode->HwAddressSize); | |
| TmpPtr += SnpMode->HwAddressSize; | |
| // | |
| // The ethernet protocol type. | |
| // | |
| *(UINT16 *)TmpPtr = HTONS (ARP_ETHER_PROTO_TYPE); | |
| TmpPtr += 2; | |
| // | |
| // The ARP Head. | |
| // | |
| ArpHead = (ARP_HEAD *)TmpPtr; | |
| ArpHead->HwType = HTONS ((UINT16)SnpMode->IfType); | |
| ArpHead->ProtoType = HTONS (ConfigData->SwAddressType); | |
| ArpHead->HwAddrLen = (UINT8)SnpMode->HwAddressSize; | |
| ArpHead->ProtoAddrLen = ConfigData->SwAddressLength; | |
| ArpHead->OpCode = HTONS (ArpOpCode); | |
| TmpPtr += sizeof (ARP_HEAD); | |
| // | |
| // The sender hardware address. | |
| // | |
| CopyMem (TmpPtr, &SnpMode->CurrentAddress, SnpMode->HwAddressSize); | |
| TmpPtr += SnpMode->HwAddressSize; | |
| // | |
| // The sender protocol address. | |
| // | |
| CopyMem (TmpPtr, ConfigData->StationAddress, ConfigData->SwAddressLength); | |
| TmpPtr += ConfigData->SwAddressLength; | |
| // | |
| // The target hardware address. | |
| // | |
| CopyMem ( | |
| TmpPtr, | |
| CacheEntry->Addresses[Hardware].AddressPtr, | |
| SnpMode->HwAddressSize | |
| ); | |
| TmpPtr += SnpMode->HwAddressSize; | |
| // | |
| // The target protocol address. | |
| // | |
| CopyMem ( | |
| TmpPtr, | |
| CacheEntry->Addresses[Protocol].AddressPtr, | |
| ConfigData->SwAddressLength | |
| ); | |
| // | |
| // Set all the fields of the TxData. | |
| // | |
| TxData->DestinationAddress = NULL; | |
| TxData->SourceAddress = NULL; | |
| TxData->ProtocolType = 0; | |
| TxData->DataLength = TotalLength - SnpMode->MediaHeaderSize; | |
| TxData->HeaderLength = (UINT16)SnpMode->MediaHeaderSize; | |
| TxData->FragmentCount = 1; | |
| TxData->FragmentTable[0].FragmentBuffer = Packet; | |
| TxData->FragmentTable[0].FragmentLength = TotalLength; | |
| // | |
| // Associate the TxData with the TxToken. | |
| // | |
| TxToken->Packet.TxData = TxData; | |
| TxToken->Status = EFI_NOT_READY; | |
| // | |
| // Send out this arp packet by Mnp. | |
| // | |
| Status = ArpService->Mnp->Transmit (ArpService->Mnp, TxToken); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "Mnp->Transmit failed, %r.\n", Status)); | |
| goto CLEAN_EXIT; | |
| } | |
| return; | |
| CLEAN_EXIT: | |
| if (Packet != NULL) { | |
| FreePool (Packet); | |
| } | |
| if (TxData != NULL) { | |
| FreePool (TxData); | |
| } | |
| if (TxToken->Event != NULL) { | |
| gBS->CloseEvent (TxToken->Event); | |
| } | |
| FreePool (TxToken); | |
| } | |
| /** | |
| Delete the cache entries in the specified CacheTable, using the BySwAddress, | |
| SwAddressType, AddressBuffer combination as the matching key, if Force is TRUE, | |
| the cache is deleted event it's a static entry. | |
| @param[in] CacheTable Pointer to the cache table to do the deletion. | |
| @param[in] BySwAddress Delete the cache entry by software address or by | |
| hardware address. | |
| @param[in] SwAddressType The software address used to do the deletion. | |
| @param[in] AddressBuffer Pointer to the buffer containing the address to | |
| match for the deletion. | |
| @param[in] Force This deletion is forced or not. | |
| @return The count of the deleted cache entries. | |
| **/ | |
| UINTN | |
| ArpDeleteCacheEntryInTable ( | |
| IN LIST_ENTRY *CacheTable, | |
| IN BOOLEAN BySwAddress, | |
| IN UINT16 SwAddressType, | |
| IN UINT8 *AddressBuffer OPTIONAL, | |
| IN BOOLEAN Force | |
| ) | |
| { | |
| LIST_ENTRY *Entry; | |
| LIST_ENTRY *NextEntry; | |
| ARP_CACHE_ENTRY *CacheEntry; | |
| UINTN Count; | |
| Count = 0; | |
| NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, CacheTable) { | |
| CacheEntry = NET_LIST_USER_STRUCT (Entry, ARP_CACHE_ENTRY, List); | |
| if ((CacheEntry->DefaultDecayTime == 0) && !Force) { | |
| // | |
| // It's a static entry and we are not forced to delete it, skip. | |
| // | |
| continue; | |
| } | |
| if (BySwAddress) { | |
| if (SwAddressType == CacheEntry->Addresses[Protocol].Type) { | |
| // | |
| // Protocol address type matched. Check the address. | |
| // | |
| if ((AddressBuffer == NULL) || | |
| (CompareMem ( | |
| AddressBuffer, | |
| CacheEntry->Addresses[Protocol].AddressPtr, | |
| CacheEntry->Addresses[Protocol].Length | |
| ) == 0)) | |
| { | |
| // | |
| // Address matched. | |
| // | |
| goto MATCHED; | |
| } | |
| } | |
| } else { | |
| if ((AddressBuffer == NULL) || | |
| (CompareMem ( | |
| AddressBuffer, | |
| CacheEntry->Addresses[Hardware].AddressPtr, | |
| CacheEntry->Addresses[Hardware].Length | |
| ) == 0)) | |
| { | |
| // | |
| // Address matched. | |
| // | |
| goto MATCHED; | |
| } | |
| } | |
| continue; | |
| MATCHED: | |
| // | |
| // Delete this entry. | |
| // | |
| RemoveEntryList (&CacheEntry->List); | |
| ASSERT (IsListEmpty (&CacheEntry->UserRequestList)); | |
| FreePool (CacheEntry); | |
| Count++; | |
| } | |
| return Count; | |
| } | |
| /** | |
| Delete cache entries in all the cache tables. | |
| @param[in] Instance Pointer to the instance context data. | |
| @param[in] BySwAddress Delete the cache entry by software address or by | |
| hardware address. | |
| @param[in] AddressBuffer Pointer to the buffer containing the address to | |
| match for the deletion. | |
| @param[in] Force This deletion is forced or not. | |
| @return The count of the deleted cache entries. | |
| **/ | |
| UINTN | |
| ArpDeleteCacheEntry ( | |
| IN ARP_INSTANCE_DATA *Instance, | |
| IN BOOLEAN BySwAddress, | |
| IN UINT8 *AddressBuffer OPTIONAL, | |
| IN BOOLEAN Force | |
| ) | |
| { | |
| ARP_SERVICE_DATA *ArpService; | |
| UINTN Count; | |
| NET_CHECK_SIGNATURE (Instance, ARP_INSTANCE_DATA_SIGNATURE); | |
| ArpService = Instance->ArpService; | |
| // | |
| // Delete the cache entries in the DeniedCacheTable. | |
| // | |
| Count = ArpDeleteCacheEntryInTable ( | |
| &ArpService->DeniedCacheTable, | |
| BySwAddress, | |
| Instance->ConfigData.SwAddressType, | |
| AddressBuffer, | |
| Force | |
| ); | |
| // | |
| // Delete the cache entries in the ResolvedCacheTable. | |
| // | |
| Count += ArpDeleteCacheEntryInTable ( | |
| &ArpService->ResolvedCacheTable, | |
| BySwAddress, | |
| Instance->ConfigData.SwAddressType, | |
| AddressBuffer, | |
| Force | |
| ); | |
| return Count; | |
| } | |
| /** | |
| Cancel the arp request. | |
| @param[in] Instance Pointer to the instance context data. | |
| @param[in] TargetSwAddress Pointer to the buffer containing the target | |
| software address to match the arp request. | |
| @param[in] UserEvent The user event used to notify this request | |
| cancellation. | |
| @return The count of the cancelled requests. | |
| **/ | |
| UINTN | |
| ArpCancelRequest ( | |
| IN ARP_INSTANCE_DATA *Instance, | |
| IN VOID *TargetSwAddress OPTIONAL, | |
| IN EFI_EVENT UserEvent OPTIONAL | |
| ) | |
| { | |
| ARP_SERVICE_DATA *ArpService; | |
| LIST_ENTRY *Entry; | |
| LIST_ENTRY *NextEntry; | |
| ARP_CACHE_ENTRY *CacheEntry; | |
| UINTN Count; | |
| NET_CHECK_SIGNATURE (Instance, ARP_INSTANCE_DATA_SIGNATURE); | |
| ArpService = Instance->ArpService; | |
| Count = 0; | |
| NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &ArpService->PendingRequestTable) { | |
| CacheEntry = NET_LIST_USER_STRUCT (Entry, ARP_CACHE_ENTRY, List); | |
| if ((TargetSwAddress == NULL) || | |
| (CompareMem ( | |
| TargetSwAddress, | |
| CacheEntry->Addresses[Protocol].AddressPtr, | |
| CacheEntry->Addresses[Protocol].Length | |
| ) == 0)) | |
| { | |
| // | |
| // This request entry matches the TargetSwAddress or all requests are to be | |
| // cancelled as TargetSwAddress is NULL. | |
| // | |
| Count += ArpAddressResolved (CacheEntry, Instance, UserEvent); | |
| if (IsListEmpty (&CacheEntry->UserRequestList)) { | |
| // | |
| // No user requests any more, remove this request cache entry. | |
| // | |
| RemoveEntryList (&CacheEntry->List); | |
| FreePool (CacheEntry); | |
| } | |
| } | |
| } | |
| return Count; | |
| } | |
| /** | |
| Find the cache entry in the cache table. | |
| @param[in] Instance Pointer to the instance context data. | |
| @param[in] BySwAddress Set to TRUE to look for matching software protocol | |
| addresses. Set to FALSE to look for matching | |
| hardware protocol addresses. | |
| @param[in] AddressBuffer Pointer to address buffer. Set to NULL to match | |
| all addresses. | |
| @param[out] EntryLength The size of an entry in the entries buffer. | |
| @param[out] EntryCount The number of ARP cache entries that are found by | |
| the specified criteria. | |
| @param[out] Entries Pointer to the buffer that will receive the ARP | |
| cache entries. | |
| @param[in] Refresh Set to TRUE to refresh the timeout value of the | |
| matching ARP cache entry. | |
| @retval EFI_SUCCESS The requested ARP cache entries are copied into | |
| the buffer. | |
| @retval EFI_NOT_FOUND No matching entries found. | |
| @retval EFI_OUT_OF_RESOURCE There is a memory allocation failure. | |
| **/ | |
| EFI_STATUS | |
| ArpFindCacheEntry ( | |
| IN ARP_INSTANCE_DATA *Instance, | |
| IN BOOLEAN BySwAddress, | |
| IN VOID *AddressBuffer OPTIONAL, | |
| OUT UINT32 *EntryLength OPTIONAL, | |
| OUT UINT32 *EntryCount OPTIONAL, | |
| OUT EFI_ARP_FIND_DATA **Entries OPTIONAL, | |
| IN BOOLEAN Refresh | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| ARP_SERVICE_DATA *ArpService; | |
| NET_ARP_ADDRESS MatchAddress; | |
| FIND_OPTYPE FindOpType; | |
| LIST_ENTRY *StartEntry; | |
| ARP_CACHE_ENTRY *CacheEntry; | |
| NET_MAP FoundEntries; | |
| UINT32 FoundCount; | |
| EFI_ARP_FIND_DATA *FindData; | |
| LIST_ENTRY *CacheTable; | |
| UINT32 FoundEntryLength; | |
| ArpService = Instance->ArpService; | |
| // | |
| // Init the FoundEntries used to hold the found cache entries. | |
| // | |
| NetMapInit (&FoundEntries); | |
| // | |
| // Set the MatchAddress. | |
| // | |
| if (BySwAddress) { | |
| MatchAddress.Type = Instance->ConfigData.SwAddressType; | |
| MatchAddress.Length = Instance->ConfigData.SwAddressLength; | |
| FindOpType = ByProtoAddress; | |
| } else { | |
| MatchAddress.Type = ArpService->SnpMode.IfType; | |
| MatchAddress.Length = (UINT8)ArpService->SnpMode.HwAddressSize; | |
| FindOpType = ByHwAddress; | |
| } | |
| MatchAddress.AddressPtr = AddressBuffer; | |
| // | |
| // Search the DeniedCacheTable | |
| // | |
| StartEntry = NULL; | |
| while (TRUE) { | |
| // | |
| // Try to find the matched entries in the DeniedCacheTable. | |
| // | |
| CacheEntry = ArpFindNextCacheEntryInTable ( | |
| &ArpService->DeniedCacheTable, | |
| StartEntry, | |
| FindOpType, | |
| &MatchAddress, | |
| &MatchAddress | |
| ); | |
| if (CacheEntry == NULL) { | |
| // | |
| // Once the CacheEntry is NULL, there are no more matches. | |
| // | |
| break; | |
| } | |
| // | |
| // Insert the found entry into the map. | |
| // | |
| NetMapInsertTail ( | |
| &FoundEntries, | |
| (VOID *)CacheEntry, | |
| (VOID *)&ArpService->DeniedCacheTable | |
| ); | |
| // | |
| // Let the next search start from this cache entry. | |
| // | |
| StartEntry = &CacheEntry->List; | |
| if (Refresh) { | |
| // | |
| // Refresh the DecayTime if needed. | |
| // | |
| CacheEntry->DecayTime = CacheEntry->DefaultDecayTime; | |
| } | |
| } | |
| // | |
| // Search the ResolvedCacheTable | |
| // | |
| StartEntry = NULL; | |
| while (TRUE) { | |
| CacheEntry = ArpFindNextCacheEntryInTable ( | |
| &ArpService->ResolvedCacheTable, | |
| StartEntry, | |
| FindOpType, | |
| &MatchAddress, | |
| &MatchAddress | |
| ); | |
| if (CacheEntry == NULL) { | |
| // | |
| // Once the CacheEntry is NULL, there are no more matches. | |
| // | |
| break; | |
| } | |
| // | |
| // Insert the found entry into the map. | |
| // | |
| NetMapInsertTail ( | |
| &FoundEntries, | |
| (VOID *)CacheEntry, | |
| (VOID *)&ArpService->ResolvedCacheTable | |
| ); | |
| // | |
| // Let the next search start from this cache entry. | |
| // | |
| StartEntry = &CacheEntry->List; | |
| if (Refresh) { | |
| // | |
| // Refresh the DecayTime if needed. | |
| // | |
| CacheEntry->DecayTime = CacheEntry->DefaultDecayTime; | |
| } | |
| } | |
| Status = EFI_SUCCESS; | |
| FoundCount = (UINT32)NetMapGetCount (&FoundEntries); | |
| if (FoundCount == 0) { | |
| Status = EFI_NOT_FOUND; | |
| goto CLEAN_EXIT; | |
| } | |
| // | |
| // Found the entry length, make sure its 8 bytes alignment. | |
| // | |
| FoundEntryLength = (((sizeof (EFI_ARP_FIND_DATA) + Instance->ConfigData.SwAddressLength + | |
| ArpService->SnpMode.HwAddressSize) + 3) & ~(0x3)); | |
| if (EntryLength != NULL) { | |
| *EntryLength = FoundEntryLength; | |
| } | |
| if (EntryCount != NULL) { | |
| // | |
| // Return the found entry count. | |
| // | |
| *EntryCount = FoundCount; | |
| } | |
| if (Entries == NULL) { | |
| goto CLEAN_EXIT; | |
| } | |
| // | |
| // Allocate buffer to copy the found entries. | |
| // | |
| FindData = AllocatePool (FoundCount * FoundEntryLength); | |
| if (FindData == NULL) { | |
| DEBUG ((DEBUG_ERROR, "ArpFindCacheEntry: Failed to allocate memory.\n")); | |
| Status = EFI_OUT_OF_RESOURCES; | |
| goto CLEAN_EXIT; | |
| } | |
| // | |
| // Return the address to the user. | |
| // | |
| *Entries = FindData; | |
| // | |
| // Dump the entries. | |
| // | |
| while (!NetMapIsEmpty (&FoundEntries)) { | |
| // | |
| // Get a cache entry from the map. | |
| // | |
| CacheEntry = NetMapRemoveHead (&FoundEntries, (VOID **)&CacheTable); | |
| // | |
| // Set the fields in FindData. | |
| // | |
| FindData->Size = FoundEntryLength; | |
| FindData->DenyFlag = (BOOLEAN)(CacheTable == &ArpService->DeniedCacheTable); | |
| FindData->StaticFlag = (BOOLEAN)(CacheEntry->DefaultDecayTime == 0); | |
| FindData->HwAddressType = ArpService->SnpMode.IfType; | |
| FindData->SwAddressType = Instance->ConfigData.SwAddressType; | |
| FindData->HwAddressLength = (UINT8)ArpService->SnpMode.HwAddressSize; | |
| FindData->SwAddressLength = Instance->ConfigData.SwAddressLength; | |
| // | |
| // Copy the software address. | |
| // | |
| CopyMem ( | |
| FindData + 1, | |
| CacheEntry->Addresses[Protocol].AddressPtr, | |
| FindData->SwAddressLength | |
| ); | |
| // | |
| // Copy the hardware address. | |
| // | |
| CopyMem ( | |
| (UINT8 *)(FindData + 1) + FindData->SwAddressLength, | |
| CacheEntry->Addresses[Hardware].AddressPtr, | |
| FindData->HwAddressLength | |
| ); | |
| // | |
| // Slip to the next FindData. | |
| // | |
| FindData = (EFI_ARP_FIND_DATA *)((UINT8 *)FindData + FoundEntryLength); | |
| } | |
| CLEAN_EXIT: | |
| NetMapClean (&FoundEntries); | |
| return Status; | |
| } |