/** @file The implementation of the ARP protocol. Copyright (c) 2006 - 2020, Intel Corporation. All rights reserved.
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 // 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 ( if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "ArpOnFrameRcvd: ArpService->Mnp->Receive " "failed, %r\n.", Status)); } ); } /** 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 ( if (EFI_ERROR (TxToken->Status)) { DEBUG ((EFI_D_ERROR, "ArpOnFrameSent: TxToken->Status, %r.\n", TxToken->Status)); } ); // // 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 ((EFI_D_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 ((EFI_D_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 ((EFI_D_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 ((EFI_D_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 ((EFI_D_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 ((EFI_D_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 ((EFI_D_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; }