/** @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_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;
}