mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-11-23 11:35:19 +01:00
1346 lines
38 KiB
C
1346 lines
38 KiB
C
|
/** @file
|
||
|
Implement IP4 pesudo interface.
|
||
|
|
||
|
Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.<BR>
|
||
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
||
|
|
||
|
**/
|
||
|
|
||
|
#include "Ip4Impl.h"
|
||
|
|
||
|
//
|
||
|
// Mac address with all zero, used to determine whethter the ARP
|
||
|
// resolve succeeded. Failed ARP requests zero the MAC address buffer.
|
||
|
//
|
||
|
EFI_MAC_ADDRESS mZeroMacAddress;
|
||
|
|
||
|
/**
|
||
|
Callback funtion when frame transmission is finished. It will
|
||
|
call the frame owner's callback function to tell it the result.
|
||
|
|
||
|
@param[in] Context Context which is point to the token.
|
||
|
|
||
|
**/
|
||
|
VOID
|
||
|
EFIAPI
|
||
|
Ip4OnFrameSentDpc (
|
||
|
IN VOID *Context
|
||
|
);
|
||
|
|
||
|
/**
|
||
|
Request Ip4OnFrameSentDpc as a DPC at TPL_CALLBACK.
|
||
|
|
||
|
@param[in] Event The transmit token's event.
|
||
|
@param[in] Context Context which is point to the token.
|
||
|
|
||
|
**/
|
||
|
VOID
|
||
|
EFIAPI
|
||
|
Ip4OnFrameSent (
|
||
|
IN EFI_EVENT Event,
|
||
|
IN VOID *Context
|
||
|
);
|
||
|
|
||
|
/**
|
||
|
Callback function when ARP request are finished. It will cancelled
|
||
|
all the queued frame if the ARP requests failed. Or transmit them
|
||
|
if the request succeed.
|
||
|
|
||
|
@param[in] Context The context of the callback, a point to the ARP
|
||
|
queue
|
||
|
|
||
|
**/
|
||
|
VOID
|
||
|
EFIAPI
|
||
|
Ip4OnArpResolvedDpc (
|
||
|
IN VOID *Context
|
||
|
);
|
||
|
|
||
|
/**
|
||
|
Request Ip4OnArpResolvedDpc as a DPC at TPL_CALLBACK.
|
||
|
|
||
|
@param Event The Arp request event.
|
||
|
@param Context The context of the callback, a point to the ARP
|
||
|
queue.
|
||
|
|
||
|
**/
|
||
|
VOID
|
||
|
EFIAPI
|
||
|
Ip4OnArpResolved (
|
||
|
IN EFI_EVENT Event,
|
||
|
IN VOID *Context
|
||
|
);
|
||
|
|
||
|
/**
|
||
|
Received a frame from MNP, wrap it in net buffer then deliver
|
||
|
it to IP's input function. The ownship of the packet also
|
||
|
transferred to IP. When Ip is finished with this packet, it
|
||
|
will call NetbufFree to release the packet, NetbufFree will
|
||
|
again call the Ip4RecycleFrame to signal MNP's event and free
|
||
|
the token used.
|
||
|
|
||
|
@param Context Context for the callback.
|
||
|
|
||
|
**/
|
||
|
VOID
|
||
|
EFIAPI
|
||
|
Ip4OnFrameReceivedDpc (
|
||
|
IN VOID *Context
|
||
|
);
|
||
|
|
||
|
/**
|
||
|
Request Ip4OnFrameReceivedDpc as a DPC at TPL_CALLBACK.
|
||
|
|
||
|
@param Event The receive event delivered to MNP for receive.
|
||
|
@param Context Context for the callback.
|
||
|
|
||
|
**/
|
||
|
VOID
|
||
|
EFIAPI
|
||
|
Ip4OnFrameReceived (
|
||
|
IN EFI_EVENT Event,
|
||
|
IN VOID *Context
|
||
|
);
|
||
|
|
||
|
/**
|
||
|
Remove all the frames on the ARP queue that pass the FrameToCancel,
|
||
|
that is, either FrameToCancel is NULL or it returns true for the frame.
|
||
|
|
||
|
@param[in] ArpQue ARP frame to remove the frames from.
|
||
|
@param[in] IoStatus The status returned to the cancelled frames'
|
||
|
callback function.
|
||
|
@param[in] FrameToCancel Function to select which frame to cancel.
|
||
|
@param[in] Context Opaque parameter to the FrameToCancel.
|
||
|
|
||
|
**/
|
||
|
VOID
|
||
|
Ip4CancelFrameArp (
|
||
|
IN IP4_ARP_QUE *ArpQue,
|
||
|
IN EFI_STATUS IoStatus,
|
||
|
IN IP4_FRAME_TO_CANCEL FrameToCancel OPTIONAL,
|
||
|
IN VOID *Context
|
||
|
);
|
||
|
|
||
|
|
||
|
/**
|
||
|
Wrap a transmit request into a newly allocated IP4_LINK_TX_TOKEN.
|
||
|
|
||
|
@param[in] Interface The interface to send out to.
|
||
|
@param[in] IpInstance The IpInstance that transmit the packet. NULL if
|
||
|
the packet is sent by the IP4 driver itself.
|
||
|
@param[in] Packet The packet to transmit
|
||
|
@param[in] CallBack Call back function to execute if transmission
|
||
|
finished.
|
||
|
@param[in] Context Opaque parameter to the call back.
|
||
|
@param[in] IpSb The pointer to the IP4 service binding instance.
|
||
|
|
||
|
@retval Token The wrapped token if succeed
|
||
|
@retval NULL The wrapped token if NULL
|
||
|
|
||
|
**/
|
||
|
IP4_LINK_TX_TOKEN *
|
||
|
Ip4WrapLinkTxToken (
|
||
|
IN IP4_INTERFACE *Interface,
|
||
|
IN IP4_PROTOCOL *IpInstance OPTIONAL,
|
||
|
IN NET_BUF *Packet,
|
||
|
IN IP4_FRAME_CALLBACK CallBack,
|
||
|
IN VOID *Context,
|
||
|
IN IP4_SERVICE *IpSb
|
||
|
)
|
||
|
{
|
||
|
EFI_MANAGED_NETWORK_COMPLETION_TOKEN *MnpToken;
|
||
|
EFI_MANAGED_NETWORK_TRANSMIT_DATA *MnpTxData;
|
||
|
IP4_LINK_TX_TOKEN *Token;
|
||
|
EFI_STATUS Status;
|
||
|
UINT32 Count;
|
||
|
|
||
|
Token = AllocatePool (sizeof (IP4_LINK_TX_TOKEN) + \
|
||
|
(Packet->BlockOpNum - 1) * sizeof (EFI_MANAGED_NETWORK_FRAGMENT_DATA));
|
||
|
|
||
|
if (Token == NULL) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
Token->Signature = IP4_FRAME_TX_SIGNATURE;
|
||
|
InitializeListHead (&Token->Link);
|
||
|
|
||
|
Token->Interface = Interface;
|
||
|
Token->IpInstance = IpInstance;
|
||
|
Token->IpSb = IpSb;
|
||
|
Token->CallBack = CallBack;
|
||
|
Token->Packet = Packet;
|
||
|
Token->Context = Context;
|
||
|
CopyMem (&Token->DstMac, &mZeroMacAddress, sizeof (Token->DstMac));
|
||
|
CopyMem (&Token->SrcMac, &Interface->Mac, sizeof (Token->SrcMac));
|
||
|
|
||
|
MnpToken = &(Token->MnpToken);
|
||
|
MnpToken->Status = EFI_NOT_READY;
|
||
|
|
||
|
Status = gBS->CreateEvent (
|
||
|
EVT_NOTIFY_SIGNAL,
|
||
|
TPL_NOTIFY,
|
||
|
Ip4OnFrameSent,
|
||
|
Token,
|
||
|
&MnpToken->Event
|
||
|
);
|
||
|
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
FreePool (Token);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
MnpTxData = &Token->MnpTxData;
|
||
|
MnpToken->Packet.TxData = MnpTxData;
|
||
|
|
||
|
MnpTxData->DestinationAddress = &Token->DstMac;
|
||
|
MnpTxData->SourceAddress = &Token->SrcMac;
|
||
|
MnpTxData->ProtocolType = IP4_ETHER_PROTO;
|
||
|
MnpTxData->DataLength = Packet->TotalSize;
|
||
|
MnpTxData->HeaderLength = 0;
|
||
|
|
||
|
Count = Packet->BlockOpNum;
|
||
|
|
||
|
NetbufBuildExt (Packet, (NET_FRAGMENT *) MnpTxData->FragmentTable, &Count);
|
||
|
MnpTxData->FragmentCount = (UINT16)Count;
|
||
|
|
||
|
return Token;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
Free the link layer transmit token. It will close the event
|
||
|
then free the memory used.
|
||
|
|
||
|
@param[in] Token Token to free
|
||
|
|
||
|
**/
|
||
|
VOID
|
||
|
Ip4FreeLinkTxToken (
|
||
|
IN IP4_LINK_TX_TOKEN *Token
|
||
|
)
|
||
|
{
|
||
|
NET_CHECK_SIGNATURE (Token, IP4_FRAME_TX_SIGNATURE);
|
||
|
|
||
|
gBS->CloseEvent (Token->MnpToken.Event);
|
||
|
FreePool (Token);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
Create an IP_ARP_QUE structure to request ARP service.
|
||
|
|
||
|
@param[in] Interface The interface to send ARP from.
|
||
|
@param[in] DestIp The destination IP (host byte order) to request MAC
|
||
|
for
|
||
|
|
||
|
@return Point to newly created IP4_ARP_QUE if succeed, otherwise NULL.
|
||
|
|
||
|
**/
|
||
|
IP4_ARP_QUE *
|
||
|
Ip4CreateArpQue (
|
||
|
IN IP4_INTERFACE *Interface,
|
||
|
IN IP4_ADDR DestIp
|
||
|
)
|
||
|
{
|
||
|
IP4_ARP_QUE *ArpQue;
|
||
|
EFI_STATUS Status;
|
||
|
|
||
|
ArpQue = AllocatePool (sizeof (IP4_ARP_QUE));
|
||
|
|
||
|
if (ArpQue == NULL) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
ArpQue->Signature = IP4_FRAME_ARP_SIGNATURE;
|
||
|
InitializeListHead (&ArpQue->Link);
|
||
|
|
||
|
InitializeListHead (&ArpQue->Frames);
|
||
|
ArpQue->Interface = Interface;
|
||
|
|
||
|
Status = gBS->CreateEvent (
|
||
|
EVT_NOTIFY_SIGNAL,
|
||
|
TPL_NOTIFY,
|
||
|
Ip4OnArpResolved,
|
||
|
ArpQue,
|
||
|
&ArpQue->OnResolved
|
||
|
);
|
||
|
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
FreePool (ArpQue);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
ArpQue->Ip = DestIp;
|
||
|
CopyMem (&ArpQue->Mac, &mZeroMacAddress, sizeof (ArpQue->Mac));
|
||
|
|
||
|
return ArpQue;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
Remove all the transmit requests queued on the ARP queue, then free it.
|
||
|
|
||
|
@param[in] ArpQue Arp queue to free
|
||
|
@param[in] IoStatus The transmit status returned to transmit requests'
|
||
|
callback.
|
||
|
|
||
|
**/
|
||
|
VOID
|
||
|
Ip4FreeArpQue (
|
||
|
IN IP4_ARP_QUE *ArpQue,
|
||
|
IN EFI_STATUS IoStatus
|
||
|
)
|
||
|
{
|
||
|
NET_CHECK_SIGNATURE (ArpQue, IP4_FRAME_ARP_SIGNATURE);
|
||
|
|
||
|
//
|
||
|
// Remove all the frame waiting the ARP response
|
||
|
//
|
||
|
Ip4CancelFrameArp (ArpQue, IoStatus, NULL, NULL);
|
||
|
|
||
|
gBS->CloseEvent (ArpQue->OnResolved);
|
||
|
FreePool (ArpQue);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
Create a link layer receive token to wrap the receive request
|
||
|
|
||
|
@param[in] Interface The interface to receive from
|
||
|
@param[in] IpInstance The instance that request the receive (NULL for IP4
|
||
|
driver itself)
|
||
|
@param[in] CallBack Call back function to execute when finished.
|
||
|
@param[in] Context Opaque parameters to the callback
|
||
|
|
||
|
@return Point to created IP4_LINK_RX_TOKEN if succeed, otherwise NULL.
|
||
|
|
||
|
**/
|
||
|
IP4_LINK_RX_TOKEN *
|
||
|
Ip4CreateLinkRxToken (
|
||
|
IN IP4_INTERFACE *Interface,
|
||
|
IN IP4_PROTOCOL *IpInstance,
|
||
|
IN IP4_FRAME_CALLBACK CallBack,
|
||
|
IN VOID *Context
|
||
|
)
|
||
|
{
|
||
|
EFI_MANAGED_NETWORK_COMPLETION_TOKEN *MnpToken;
|
||
|
IP4_LINK_RX_TOKEN *Token;
|
||
|
EFI_STATUS Status;
|
||
|
|
||
|
Token = AllocatePool (sizeof (IP4_LINK_RX_TOKEN));
|
||
|
if (Token == NULL) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
Token->Signature = IP4_FRAME_RX_SIGNATURE;
|
||
|
Token->Interface = Interface;
|
||
|
Token->IpInstance = IpInstance;
|
||
|
Token->CallBack = CallBack;
|
||
|
Token->Context = Context;
|
||
|
|
||
|
MnpToken = &Token->MnpToken;
|
||
|
MnpToken->Status = EFI_NOT_READY;
|
||
|
|
||
|
Status = gBS->CreateEvent (
|
||
|
EVT_NOTIFY_SIGNAL,
|
||
|
TPL_NOTIFY,
|
||
|
Ip4OnFrameReceived,
|
||
|
Token,
|
||
|
&MnpToken->Event
|
||
|
);
|
||
|
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
FreePool (Token);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
MnpToken->Packet.RxData = NULL;
|
||
|
return Token;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
Free the link layer request token. It will close the event
|
||
|
then free the memory used.
|
||
|
|
||
|
@param[in] Token Request token to free.
|
||
|
|
||
|
**/
|
||
|
VOID
|
||
|
Ip4FreeFrameRxToken (
|
||
|
IN IP4_LINK_RX_TOKEN *Token
|
||
|
)
|
||
|
{
|
||
|
|
||
|
NET_CHECK_SIGNATURE (Token, IP4_FRAME_RX_SIGNATURE);
|
||
|
|
||
|
gBS->CloseEvent (Token->MnpToken.Event);
|
||
|
FreePool (Token);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
Remove all the frames on the ARP queue that pass the FrameToCancel,
|
||
|
that is, either FrameToCancel is NULL or it returns true for the frame.
|
||
|
|
||
|
@param[in] ArpQue ARP frame to remove the frames from.
|
||
|
@param[in] IoStatus The status returned to the cancelled frames'
|
||
|
callback function.
|
||
|
@param[in] FrameToCancel Function to select which frame to cancel.
|
||
|
@param[in] Context Opaque parameter to the FrameToCancel.
|
||
|
|
||
|
**/
|
||
|
VOID
|
||
|
Ip4CancelFrameArp (
|
||
|
IN IP4_ARP_QUE *ArpQue,
|
||
|
IN EFI_STATUS IoStatus,
|
||
|
IN IP4_FRAME_TO_CANCEL FrameToCancel OPTIONAL,
|
||
|
IN VOID *Context
|
||
|
)
|
||
|
{
|
||
|
LIST_ENTRY *Entry;
|
||
|
LIST_ENTRY *Next;
|
||
|
IP4_LINK_TX_TOKEN *Token;
|
||
|
|
||
|
NET_LIST_FOR_EACH_SAFE (Entry, Next, &ArpQue->Frames) {
|
||
|
Token = NET_LIST_USER_STRUCT (Entry, IP4_LINK_TX_TOKEN, Link);
|
||
|
|
||
|
if ((FrameToCancel == NULL) || FrameToCancel (Token, Context)) {
|
||
|
RemoveEntryList (Entry);
|
||
|
|
||
|
Token->CallBack (Token->IpInstance, Token->Packet, IoStatus, 0, Token->Context);
|
||
|
Ip4FreeLinkTxToken (Token);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
Remove all the frames on the interface that pass the FrameToCancel,
|
||
|
either queued on ARP queues or that have already been delivered to
|
||
|
MNP and not yet recycled.
|
||
|
|
||
|
@param[in] Interface Interface to remove the frames from.
|
||
|
@param[in] IoStatus The transmit status returned to the frames'
|
||
|
callback.
|
||
|
@param[in] FrameToCancel Function to select the frame to cancel, NULL to
|
||
|
select all.
|
||
|
@param[in] Context Opaque parameters passed to FrameToCancel.
|
||
|
|
||
|
**/
|
||
|
VOID
|
||
|
Ip4CancelFrames (
|
||
|
IN IP4_INTERFACE *Interface,
|
||
|
IN EFI_STATUS IoStatus,
|
||
|
IN IP4_FRAME_TO_CANCEL FrameToCancel OPTIONAL,
|
||
|
IN VOID *Context
|
||
|
)
|
||
|
{
|
||
|
LIST_ENTRY *Entry;
|
||
|
LIST_ENTRY *Next;
|
||
|
IP4_ARP_QUE *ArpQue;
|
||
|
IP4_LINK_TX_TOKEN *Token;
|
||
|
|
||
|
//
|
||
|
// Cancel all the pending frames on ARP requests
|
||
|
//
|
||
|
NET_LIST_FOR_EACH_SAFE (Entry, Next, &Interface->ArpQues) {
|
||
|
ArpQue = NET_LIST_USER_STRUCT (Entry, IP4_ARP_QUE, Link);
|
||
|
|
||
|
Ip4CancelFrameArp (ArpQue, IoStatus, FrameToCancel, Context);
|
||
|
|
||
|
if (IsListEmpty (&ArpQue->Frames)) {
|
||
|
Interface->Arp->Cancel (Interface->Arp, &ArpQue->Ip, ArpQue->OnResolved);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Cancel all the frames that have been delivered to MNP
|
||
|
// but not yet recycled.
|
||
|
//
|
||
|
NET_LIST_FOR_EACH_SAFE (Entry, Next, &Interface->SentFrames) {
|
||
|
Token = NET_LIST_USER_STRUCT (Entry, IP4_LINK_TX_TOKEN, Link);
|
||
|
|
||
|
if ((FrameToCancel == NULL) || FrameToCancel (Token, Context)) {
|
||
|
Interface->Mnp->Cancel (Interface->Mnp, &Token->MnpToken);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
Create an IP4_INTERFACE. Delay the creation of ARP instance until
|
||
|
the interface is configured.
|
||
|
|
||
|
@param[in] Mnp The shared MNP child of this IP4 service binding
|
||
|
instance.
|
||
|
@param[in] Controller The controller this IP4 service binding instance
|
||
|
is installed. Most like the UNDI handle.
|
||
|
@param[in] ImageHandle This driver's image handle.
|
||
|
|
||
|
@return Point to the created IP4_INTERFACE, otherwise NULL.
|
||
|
|
||
|
**/
|
||
|
IP4_INTERFACE *
|
||
|
Ip4CreateInterface (
|
||
|
IN EFI_MANAGED_NETWORK_PROTOCOL *Mnp,
|
||
|
IN EFI_HANDLE Controller,
|
||
|
IN EFI_HANDLE ImageHandle
|
||
|
)
|
||
|
{
|
||
|
IP4_INTERFACE *Interface;
|
||
|
EFI_SIMPLE_NETWORK_MODE SnpMode;
|
||
|
|
||
|
Interface = AllocatePool (sizeof (IP4_INTERFACE));
|
||
|
|
||
|
if ((Interface == NULL) || (Mnp == NULL)) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
Interface->Signature = IP4_INTERFACE_SIGNATURE;
|
||
|
InitializeListHead (&Interface->Link);
|
||
|
Interface->RefCnt = 1;
|
||
|
|
||
|
Interface->Ip = IP4_ALLZERO_ADDRESS;
|
||
|
Interface->SubnetMask = IP4_ALLZERO_ADDRESS;
|
||
|
Interface->Configured = FALSE;
|
||
|
|
||
|
Interface->Controller = Controller;
|
||
|
Interface->Image = ImageHandle;
|
||
|
Interface->Mnp = Mnp;
|
||
|
Interface->Arp = NULL;
|
||
|
Interface->ArpHandle = NULL;
|
||
|
|
||
|
InitializeListHead (&Interface->ArpQues);
|
||
|
InitializeListHead (&Interface->SentFrames);
|
||
|
|
||
|
Interface->RecvRequest = NULL;
|
||
|
|
||
|
//
|
||
|
// Get the interface's Mac address and broadcast mac address from SNP
|
||
|
//
|
||
|
if (EFI_ERROR (Mnp->GetModeData (Mnp, NULL, &SnpMode))) {
|
||
|
FreePool (Interface);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
CopyMem (&Interface->Mac, &SnpMode.CurrentAddress, sizeof (Interface->Mac));
|
||
|
CopyMem (&Interface->BroadcastMac, &SnpMode.BroadcastAddress, sizeof (Interface->BroadcastMac));
|
||
|
Interface->HwaddrLen = SnpMode.HwAddressSize;
|
||
|
|
||
|
InitializeListHead (&Interface->IpInstances);
|
||
|
Interface->PromiscRecv = FALSE;
|
||
|
|
||
|
return Interface;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
Set the interface's address, create and configure
|
||
|
the ARP child if necessary.
|
||
|
|
||
|
@param Interface The interface to set the address.
|
||
|
@param IpAddr The interface's IP address.
|
||
|
@param SubnetMask The interface's netmask.
|
||
|
|
||
|
@retval EFI_SUCCESS The interface is configured with Ip/netmask pair,
|
||
|
and a ARP is created for it.
|
||
|
@retval Others Failed to set the interface's address.
|
||
|
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
Ip4SetAddress (
|
||
|
IN OUT IP4_INTERFACE *Interface,
|
||
|
IN IP4_ADDR IpAddr,
|
||
|
IN IP4_ADDR SubnetMask
|
||
|
)
|
||
|
{
|
||
|
EFI_ARP_CONFIG_DATA ArpConfig;
|
||
|
EFI_STATUS Status;
|
||
|
|
||
|
NET_CHECK_SIGNATURE (Interface, IP4_INTERFACE_SIGNATURE);
|
||
|
|
||
|
//
|
||
|
// Set the ip/netmask, then compute the subnet broadcast
|
||
|
// and network broadcast for easy access. When computing
|
||
|
// nework broadcast, the subnet mask is most like longer
|
||
|
// than the default netmask (not subneted) as defined in
|
||
|
// RFC793. If that isn't the case, we are aggregating the
|
||
|
// networks, use the subnet's mask instead.
|
||
|
//
|
||
|
Interface->Ip = IpAddr;
|
||
|
Interface->SubnetMask = SubnetMask;
|
||
|
Interface->SubnetBrdcast = (IpAddr | ~SubnetMask);
|
||
|
Interface->NetBrdcast = (IpAddr | ~SubnetMask);
|
||
|
|
||
|
//
|
||
|
// Do clean up for Arp child
|
||
|
//
|
||
|
if (Interface->ArpHandle != NULL) {
|
||
|
if (Interface->Arp != NULL) {
|
||
|
gBS->CloseProtocol (
|
||
|
Interface->ArpHandle,
|
||
|
&gEfiArpProtocolGuid,
|
||
|
Interface->Image,
|
||
|
Interface->Controller
|
||
|
);
|
||
|
|
||
|
Interface->Arp = NULL;
|
||
|
}
|
||
|
|
||
|
NetLibDestroyServiceChild (
|
||
|
Interface->Controller,
|
||
|
Interface->Image,
|
||
|
&gEfiArpServiceBindingProtocolGuid,
|
||
|
&Interface->ArpHandle
|
||
|
);
|
||
|
|
||
|
Interface->ArpHandle = NULL;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If the address is NOT all zero, create then configure an ARP child.
|
||
|
// Pay attention: DHCP configures its station address as 0.0.0.0/0
|
||
|
//
|
||
|
if (IpAddr != IP4_ALLZERO_ADDRESS) {
|
||
|
Status = NetLibCreateServiceChild (
|
||
|
Interface->Controller,
|
||
|
Interface->Image,
|
||
|
&gEfiArpServiceBindingProtocolGuid,
|
||
|
&Interface->ArpHandle
|
||
|
);
|
||
|
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
Status = gBS->OpenProtocol (
|
||
|
Interface->ArpHandle,
|
||
|
&gEfiArpProtocolGuid,
|
||
|
(VOID **) &Interface->Arp,
|
||
|
Interface->Image,
|
||
|
Interface->Controller,
|
||
|
EFI_OPEN_PROTOCOL_BY_DRIVER
|
||
|
);
|
||
|
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
goto ON_ERROR;
|
||
|
}
|
||
|
|
||
|
IpAddr = HTONL (IpAddr);
|
||
|
ArpConfig.SwAddressType = IP4_ETHER_PROTO;
|
||
|
ArpConfig.SwAddressLength = 4;
|
||
|
ArpConfig.StationAddress = &IpAddr;
|
||
|
ArpConfig.EntryTimeOut = 0;
|
||
|
ArpConfig.RetryCount = 0;
|
||
|
ArpConfig.RetryTimeOut = 0;
|
||
|
|
||
|
Status = Interface->Arp->Configure (Interface->Arp, &ArpConfig);
|
||
|
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
gBS->CloseProtocol (
|
||
|
Interface->ArpHandle,
|
||
|
&gEfiArpProtocolGuid,
|
||
|
Interface->Image,
|
||
|
Interface->Controller
|
||
|
);
|
||
|
|
||
|
goto ON_ERROR;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Interface->Configured = TRUE;
|
||
|
return EFI_SUCCESS;
|
||
|
|
||
|
ON_ERROR:
|
||
|
NetLibDestroyServiceChild (
|
||
|
Interface->Controller,
|
||
|
Interface->Image,
|
||
|
&gEfiArpServiceBindingProtocolGuid,
|
||
|
&Interface->ArpHandle
|
||
|
);
|
||
|
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
Filter function to cancel all the frame related to an IP instance.
|
||
|
|
||
|
@param[in] Frame The transmit request to test whether to cancel
|
||
|
@param[in] Context The context which is the Ip instance that issued
|
||
|
the transmit.
|
||
|
|
||
|
@retval TRUE The frame belongs to this instance and is to be
|
||
|
removed
|
||
|
@retval FALSE The frame doesn't belong to this instance.
|
||
|
|
||
|
**/
|
||
|
BOOLEAN
|
||
|
Ip4CancelInstanceFrame (
|
||
|
IN IP4_LINK_TX_TOKEN *Frame,
|
||
|
IN VOID *Context
|
||
|
)
|
||
|
{
|
||
|
if (Frame->IpInstance == (IP4_PROTOCOL *) Context) {
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/**
|
||
|
If there is a pending receive request, cancel it. Don't call
|
||
|
the receive request's callback because this function can be only
|
||
|
called if the instance or driver is tearing itself down. It
|
||
|
doesn't make sense to call it back. But it is necessary to call
|
||
|
the transmit token's callback to give it a chance to free the
|
||
|
packet and update the upper layer's transmit request status, say
|
||
|
that from the UDP.
|
||
|
|
||
|
@param[in] Interface The interface used by the IpInstance
|
||
|
|
||
|
**/
|
||
|
VOID
|
||
|
Ip4CancelReceive (
|
||
|
IN IP4_INTERFACE *Interface
|
||
|
)
|
||
|
{
|
||
|
EFI_TPL OldTpl;
|
||
|
IP4_LINK_RX_TOKEN *Token;
|
||
|
|
||
|
if ((Token = Interface->RecvRequest) != NULL) {
|
||
|
OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
|
||
|
|
||
|
Interface->RecvRequest = NULL;
|
||
|
Interface->Mnp->Cancel (Interface->Mnp, &Token->MnpToken);
|
||
|
|
||
|
gBS->RestoreTPL (OldTpl);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
Free the interface used by IpInstance. All the IP instance with
|
||
|
the same Ip/Netmask pair share the same interface. It is reference
|
||
|
counted. All the frames haven't been sent will be cancelled.
|
||
|
Because the IpInstance is optional, the caller must remove
|
||
|
IpInstance from the interface's instance list itself.
|
||
|
|
||
|
@param[in] Interface The interface used by the IpInstance.
|
||
|
@param[in] IpInstance The Ip instance that free the interface. NULL if
|
||
|
the Ip driver is releasing the default interface.
|
||
|
|
||
|
@retval EFI_SUCCESS The interface use IpInstance is freed.
|
||
|
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
Ip4FreeInterface (
|
||
|
IN IP4_INTERFACE *Interface,
|
||
|
IN IP4_PROTOCOL *IpInstance OPTIONAL
|
||
|
)
|
||
|
{
|
||
|
NET_CHECK_SIGNATURE (Interface, IP4_INTERFACE_SIGNATURE);
|
||
|
ASSERT (Interface->RefCnt > 0);
|
||
|
|
||
|
//
|
||
|
// Remove all the pending transmit token related to this IP instance.
|
||
|
//
|
||
|
Ip4CancelFrames (Interface, EFI_ABORTED, Ip4CancelInstanceFrame, IpInstance);
|
||
|
|
||
|
if (--Interface->RefCnt > 0) {
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Destroy the interface if this is the last IP instance that
|
||
|
// has the address. Remove all the system transmitted packets
|
||
|
// from this interface, cancel the receive request if there is
|
||
|
// one, and destroy the ARP requests.
|
||
|
//
|
||
|
Ip4CancelFrames (Interface, EFI_ABORTED, Ip4CancelInstanceFrame, NULL);
|
||
|
Ip4CancelReceive (Interface);
|
||
|
|
||
|
ASSERT (IsListEmpty (&Interface->IpInstances));
|
||
|
ASSERT (IsListEmpty (&Interface->ArpQues));
|
||
|
ASSERT (IsListEmpty (&Interface->SentFrames));
|
||
|
|
||
|
if (Interface->Arp != NULL) {
|
||
|
gBS->CloseProtocol (
|
||
|
Interface->ArpHandle,
|
||
|
&gEfiArpProtocolGuid,
|
||
|
Interface->Image,
|
||
|
Interface->Controller
|
||
|
);
|
||
|
|
||
|
NetLibDestroyServiceChild (
|
||
|
Interface->Controller,
|
||
|
Interface->Image,
|
||
|
&gEfiArpServiceBindingProtocolGuid,
|
||
|
Interface->ArpHandle
|
||
|
);
|
||
|
}
|
||
|
|
||
|
RemoveEntryList (&Interface->Link);
|
||
|
FreePool (Interface);
|
||
|
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
This function tries to send all the queued frames in ArpQue to the default gateway if
|
||
|
the ARP resolve for direct destination address is failed when using /32 subnet mask.
|
||
|
|
||
|
@param[in] ArpQue The ARP queue of a failed request.
|
||
|
|
||
|
@retval EFI_SUCCESS All the queued frames have been send to the default route.
|
||
|
@retval Others Failed to send the queued frames.
|
||
|
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
Ip4SendFrameToDefaultRoute (
|
||
|
IN IP4_ARP_QUE *ArpQue
|
||
|
)
|
||
|
{
|
||
|
LIST_ENTRY *Entry;
|
||
|
LIST_ENTRY *Next;
|
||
|
IP4_ROUTE_CACHE_ENTRY *RtCacheEntry;
|
||
|
IP4_LINK_TX_TOKEN *Token;
|
||
|
IP4_ADDR Gateway;
|
||
|
EFI_STATUS Status;
|
||
|
IP4_ROUTE_ENTRY *DefaultRoute;
|
||
|
|
||
|
//
|
||
|
// ARP resolve failed when using /32 subnet mask.
|
||
|
//
|
||
|
NET_LIST_FOR_EACH_SAFE (Entry, Next, &ArpQue->Frames) {
|
||
|
RemoveEntryList (Entry);
|
||
|
Token = NET_LIST_USER_STRUCT (Entry, IP4_LINK_TX_TOKEN, Link);
|
||
|
ASSERT (Token->Interface->SubnetMask == IP4_ALLONE_ADDRESS);
|
||
|
//
|
||
|
// Find the default gateway IP address. The default route was saved to the RtCacheEntry->Tag in Ip4Route().
|
||
|
//
|
||
|
RtCacheEntry = NULL;
|
||
|
if (Token->IpInstance != NULL) {
|
||
|
RtCacheEntry = Ip4FindRouteCache (Token->IpInstance->RouteTable, NTOHL (ArpQue->Ip), Token->Interface->Ip);
|
||
|
}
|
||
|
if (RtCacheEntry == NULL) {
|
||
|
RtCacheEntry = Ip4FindRouteCache (Token->IpSb->DefaultRouteTable, NTOHL (ArpQue->Ip), Token->Interface->Ip);
|
||
|
}
|
||
|
if (RtCacheEntry == NULL) {
|
||
|
Status= EFI_NO_MAPPING;
|
||
|
goto ON_ERROR;
|
||
|
}
|
||
|
DefaultRoute = (IP4_ROUTE_ENTRY*)RtCacheEntry->Tag;
|
||
|
if (DefaultRoute == NULL) {
|
||
|
Status= EFI_NO_MAPPING;
|
||
|
goto ON_ERROR;
|
||
|
}
|
||
|
//
|
||
|
// Try to send the frame to the default route.
|
||
|
//
|
||
|
Gateway = DefaultRoute->NextHop;
|
||
|
if (ArpQue->Ip == Gateway) {
|
||
|
//
|
||
|
// ARP resolve for the default route is failed, return error to caller.
|
||
|
//
|
||
|
Status= EFI_NO_MAPPING;
|
||
|
goto ON_ERROR;
|
||
|
}
|
||
|
RtCacheEntry->NextHop = Gateway;
|
||
|
Status = Ip4SendFrame (Token->Interface,Token->IpInstance,Token->Packet,Gateway,Token->CallBack,Token->Context,Token->IpSb);
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
Status= EFI_NO_MAPPING;
|
||
|
goto ON_ERROR;
|
||
|
}
|
||
|
Ip4FreeRouteCacheEntry (RtCacheEntry);
|
||
|
}
|
||
|
|
||
|
return EFI_SUCCESS;
|
||
|
|
||
|
ON_ERROR:
|
||
|
if (RtCacheEntry != NULL) {
|
||
|
Ip4FreeRouteCacheEntry (RtCacheEntry);
|
||
|
}
|
||
|
Token->CallBack (Token->IpInstance, Token->Packet, Status, 0, Token->Context);
|
||
|
Ip4FreeLinkTxToken (Token);
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
Callback function when ARP request are finished. It will cancel
|
||
|
all the queued frame if the ARP requests failed. Or transmit them
|
||
|
if the request succeed.
|
||
|
|
||
|
@param[in] Context The context of the callback, a point to the ARP
|
||
|
queue
|
||
|
|
||
|
**/
|
||
|
VOID
|
||
|
EFIAPI
|
||
|
Ip4OnArpResolvedDpc (
|
||
|
IN VOID *Context
|
||
|
)
|
||
|
{
|
||
|
LIST_ENTRY *Entry;
|
||
|
LIST_ENTRY *Next;
|
||
|
IP4_ARP_QUE *ArpQue;
|
||
|
IP4_INTERFACE *Interface;
|
||
|
IP4_LINK_TX_TOKEN *Token;
|
||
|
EFI_STATUS Status;
|
||
|
EFI_STATUS IoStatus;
|
||
|
|
||
|
ArpQue = (IP4_ARP_QUE *) Context;
|
||
|
NET_CHECK_SIGNATURE (ArpQue, IP4_FRAME_ARP_SIGNATURE);
|
||
|
|
||
|
RemoveEntryList (&ArpQue->Link);
|
||
|
|
||
|
//
|
||
|
// ARP resolve failed for some reason.
|
||
|
//
|
||
|
if (NET_MAC_EQUAL (&ArpQue->Mac, &mZeroMacAddress, ArpQue->Interface->HwaddrLen)) {
|
||
|
if (ArpQue->Interface->SubnetMask != IP4_ALLONE_ADDRESS) {
|
||
|
//
|
||
|
// Release all the frame and ARP queue itself. Ip4FreeArpQue will call the frame's
|
||
|
// owner back.
|
||
|
//
|
||
|
IoStatus = EFI_NO_MAPPING;
|
||
|
} else {
|
||
|
//
|
||
|
// ARP resolve failed when using 32bit subnet mask, try to send the packets to the
|
||
|
// default route.
|
||
|
//
|
||
|
IoStatus = Ip4SendFrameToDefaultRoute (ArpQue);
|
||
|
}
|
||
|
goto ON_EXIT;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// ARP resolve succeeded, Transmit all the frame. Release the ARP
|
||
|
// queue. It isn't necessary for us to cache the ARP binding because
|
||
|
// we always check the ARP cache first before transmit.
|
||
|
//
|
||
|
IoStatus = EFI_SUCCESS;
|
||
|
Interface = ArpQue->Interface;
|
||
|
|
||
|
NET_LIST_FOR_EACH_SAFE (Entry, Next, &ArpQue->Frames) {
|
||
|
RemoveEntryList (Entry);
|
||
|
|
||
|
Token = NET_LIST_USER_STRUCT (Entry, IP4_LINK_TX_TOKEN, Link);
|
||
|
CopyMem (&Token->DstMac, &ArpQue->Mac, sizeof (Token->DstMac));
|
||
|
|
||
|
//
|
||
|
// Insert the tx token before transmitting it via MNP as the FrameSentDpc
|
||
|
// may be called before Mnp->Transmit returns which will remove this tx
|
||
|
// token from the SentFrames list. Remove it from the list if the returned
|
||
|
// Status of Mnp->Transmit is not EFI_SUCCESS as in this case the
|
||
|
// FrameSentDpc won't be queued.
|
||
|
//
|
||
|
InsertTailList (&Interface->SentFrames, &Token->Link);
|
||
|
|
||
|
Status = Interface->Mnp->Transmit (Interface->Mnp, &Token->MnpToken);
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
RemoveEntryList (&Token->Link);
|
||
|
Token->CallBack (Token->IpInstance, Token->Packet, Status, 0, Token->Context);
|
||
|
|
||
|
Ip4FreeLinkTxToken (Token);
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ON_EXIT:
|
||
|
Ip4FreeArpQue (ArpQue, IoStatus);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Request Ip4OnArpResolvedDpc as a DPC at TPL_CALLBACK.
|
||
|
|
||
|
@param Event The Arp request event.
|
||
|
@param Context The context of the callback, a point to the ARP
|
||
|
queue.
|
||
|
|
||
|
**/
|
||
|
VOID
|
||
|
EFIAPI
|
||
|
Ip4OnArpResolved (
|
||
|
IN EFI_EVENT Event,
|
||
|
IN VOID *Context
|
||
|
)
|
||
|
{
|
||
|
//
|
||
|
// Request Ip4OnArpResolvedDpc as a DPC at TPL_CALLBACK
|
||
|
//
|
||
|
QueueDpc (TPL_CALLBACK, Ip4OnArpResolvedDpc, Context);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/**
|
||
|
Callback funtion when frame transmission is finished. It will
|
||
|
call the frame owner's callback function to tell it the result.
|
||
|
|
||
|
@param[in] Context Context which is point to the token.
|
||
|
|
||
|
**/
|
||
|
VOID
|
||
|
EFIAPI
|
||
|
Ip4OnFrameSentDpc (
|
||
|
IN VOID *Context
|
||
|
)
|
||
|
{
|
||
|
IP4_LINK_TX_TOKEN *Token;
|
||
|
|
||
|
Token = (IP4_LINK_TX_TOKEN *) Context;
|
||
|
NET_CHECK_SIGNATURE (Token, IP4_FRAME_TX_SIGNATURE);
|
||
|
|
||
|
RemoveEntryList (&Token->Link);
|
||
|
|
||
|
Token->CallBack (
|
||
|
Token->IpInstance,
|
||
|
Token->Packet,
|
||
|
Token->MnpToken.Status,
|
||
|
0,
|
||
|
Token->Context
|
||
|
);
|
||
|
|
||
|
Ip4FreeLinkTxToken (Token);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Request Ip4OnFrameSentDpc as a DPC at TPL_CALLBACK.
|
||
|
|
||
|
@param[in] Event The transmit token's event.
|
||
|
@param[in] Context Context which is point to the token.
|
||
|
|
||
|
**/
|
||
|
VOID
|
||
|
EFIAPI
|
||
|
Ip4OnFrameSent (
|
||
|
IN EFI_EVENT Event,
|
||
|
IN VOID *Context
|
||
|
)
|
||
|
{
|
||
|
//
|
||
|
// Request Ip4OnFrameSentDpc as a DPC at TPL_CALLBACK
|
||
|
//
|
||
|
QueueDpc (TPL_CALLBACK, Ip4OnFrameSentDpc, Context);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/**
|
||
|
Send a frame from the interface. If the next hop is broadcast or
|
||
|
multicast address, it is transmitted immediately. If the next hop
|
||
|
is a unicast, it will consult ARP to resolve the NextHop's MAC.
|
||
|
If some error happened, the CallBack won't be called. So, the caller
|
||
|
must test the return value, and take action when there is an error.
|
||
|
|
||
|
@param[in] Interface The interface to send the frame from
|
||
|
@param[in] IpInstance The IP child that request the transmission. NULL
|
||
|
if it is the IP4 driver itself.
|
||
|
@param[in] Packet The packet to transmit.
|
||
|
@param[in] NextHop The immediate destination to transmit the packet
|
||
|
to.
|
||
|
@param[in] CallBack Function to call back when transmit finished.
|
||
|
@param[in] Context Opaque parameter to the call back.
|
||
|
@param[in] IpSb The pointer to the IP4 service binding instance.
|
||
|
|
||
|
@retval EFI_OUT_OF_RESOURCES Failed to allocate resource to send the frame
|
||
|
@retval EFI_NO_MAPPING Can't resolve the MAC for the nexthop
|
||
|
@retval EFI_SUCCESS The packet is successfully transmitted.
|
||
|
@retval other Other error occurs.
|
||
|
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
Ip4SendFrame (
|
||
|
IN IP4_INTERFACE *Interface,
|
||
|
IN IP4_PROTOCOL *IpInstance OPTIONAL,
|
||
|
IN NET_BUF *Packet,
|
||
|
IN IP4_ADDR NextHop,
|
||
|
IN IP4_FRAME_CALLBACK CallBack,
|
||
|
IN VOID *Context,
|
||
|
IN IP4_SERVICE *IpSb
|
||
|
)
|
||
|
{
|
||
|
IP4_LINK_TX_TOKEN *Token;
|
||
|
LIST_ENTRY *Entry;
|
||
|
IP4_ARP_QUE *ArpQue;
|
||
|
EFI_ARP_PROTOCOL *Arp;
|
||
|
EFI_STATUS Status;
|
||
|
|
||
|
ASSERT (Interface->Configured);
|
||
|
|
||
|
Token = Ip4WrapLinkTxToken (Interface, IpInstance, Packet, CallBack, Context, IpSb);
|
||
|
|
||
|
if (Token == NULL) {
|
||
|
return EFI_OUT_OF_RESOURCES;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Get the destination MAC address for multicast and broadcasts.
|
||
|
// Don't depend on ARP to solve the address since there maybe no
|
||
|
// ARP at all. Ip4Output has set NextHop to 255.255.255.255 for
|
||
|
// all the broadcasts.
|
||
|
//
|
||
|
if (NextHop == IP4_ALLONE_ADDRESS) {
|
||
|
CopyMem (&Token->DstMac, &Interface->BroadcastMac, sizeof (Token->DstMac));
|
||
|
goto SEND_NOW;
|
||
|
|
||
|
} else if (IP4_IS_MULTICAST (NextHop)) {
|
||
|
|
||
|
Status = Ip4GetMulticastMac (Interface->Mnp, NextHop, &Token->DstMac);
|
||
|
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
goto ON_ERROR;
|
||
|
}
|
||
|
|
||
|
goto SEND_NOW;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Can only send out multicast/broadcast if the IP address is zero
|
||
|
//
|
||
|
if ((Arp = Interface->Arp) == NULL) {
|
||
|
Status = EFI_NO_MAPPING;
|
||
|
goto ON_ERROR;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// First check whether this binding is in the ARP cache.
|
||
|
//
|
||
|
NextHop = HTONL (NextHop);
|
||
|
Status = Arp->Request (Arp, &NextHop, NULL, &Token->DstMac);
|
||
|
|
||
|
if (Status == EFI_SUCCESS) {
|
||
|
goto SEND_NOW;
|
||
|
|
||
|
} else if (Status != EFI_NOT_READY) {
|
||
|
goto ON_ERROR;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Have to do asynchronous ARP resolution. First check
|
||
|
// whether there is already a pending request.
|
||
|
//
|
||
|
ArpQue = NULL;
|
||
|
|
||
|
NET_LIST_FOR_EACH (Entry, &Interface->ArpQues) {
|
||
|
ArpQue = NET_LIST_USER_STRUCT (Entry, IP4_ARP_QUE, Link);
|
||
|
|
||
|
if (ArpQue->Ip == NextHop) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Found a pending ARP request, enqueue the frame then return
|
||
|
//
|
||
|
if (Entry != &Interface->ArpQues) {
|
||
|
InsertTailList (&ArpQue->Frames, &Token->Link);
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// First frame to NextHop, issue an asynchronous ARP requests
|
||
|
//
|
||
|
ArpQue = Ip4CreateArpQue (Interface, NextHop);
|
||
|
|
||
|
if (ArpQue == NULL) {
|
||
|
Status = EFI_OUT_OF_RESOURCES;
|
||
|
goto ON_ERROR;
|
||
|
}
|
||
|
|
||
|
Status = Arp->Request (Arp, &ArpQue->Ip, ArpQue->OnResolved, ArpQue->Mac.Addr);
|
||
|
|
||
|
if (EFI_ERROR (Status) && (Status != EFI_NOT_READY)) {
|
||
|
Ip4FreeArpQue (ArpQue, EFI_NO_MAPPING);
|
||
|
goto ON_ERROR;
|
||
|
}
|
||
|
|
||
|
InsertHeadList (&ArpQue->Frames, &Token->Link);
|
||
|
InsertHeadList (&Interface->ArpQues, &ArpQue->Link);
|
||
|
return EFI_SUCCESS;
|
||
|
|
||
|
SEND_NOW:
|
||
|
//
|
||
|
// Insert the tx token into the SentFrames list before calling Mnp->Transmit.
|
||
|
// Remove it if the returned status is not EFI_SUCCESS.
|
||
|
//
|
||
|
InsertTailList (&Interface->SentFrames, &Token->Link);
|
||
|
Status = Interface->Mnp->Transmit (Interface->Mnp, &Token->MnpToken);
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
RemoveEntryList (&Token->Link);
|
||
|
goto ON_ERROR;
|
||
|
}
|
||
|
|
||
|
return EFI_SUCCESS;
|
||
|
|
||
|
ON_ERROR:
|
||
|
Ip4FreeLinkTxToken (Token);
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
Call back function when the received packet is freed.
|
||
|
Check Ip4OnFrameReceived for information.
|
||
|
|
||
|
@param Context Context, which is the IP4_LINK_RX_TOKEN.
|
||
|
|
||
|
**/
|
||
|
VOID
|
||
|
EFIAPI
|
||
|
Ip4RecycleFrame (
|
||
|
IN VOID *Context
|
||
|
)
|
||
|
{
|
||
|
IP4_LINK_RX_TOKEN *Frame;
|
||
|
|
||
|
Frame = (IP4_LINK_RX_TOKEN *) Context;
|
||
|
NET_CHECK_SIGNATURE (Frame, IP4_FRAME_RX_SIGNATURE);
|
||
|
|
||
|
gBS->SignalEvent (Frame->MnpToken.Packet.RxData->RecycleEvent);
|
||
|
Ip4FreeFrameRxToken (Frame);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
Received a frame from MNP, wrap it in net buffer then deliver
|
||
|
it to IP's input function. The ownship of the packet also
|
||
|
transferred to IP. When Ip is finished with this packet, it
|
||
|
will call NetbufFree to release the packet, NetbufFree will
|
||
|
again call the Ip4RecycleFrame to signal MNP's event and free
|
||
|
the token used.
|
||
|
|
||
|
@param Context Context for the callback.
|
||
|
|
||
|
**/
|
||
|
VOID
|
||
|
EFIAPI
|
||
|
Ip4OnFrameReceivedDpc (
|
||
|
IN VOID *Context
|
||
|
)
|
||
|
{
|
||
|
EFI_MANAGED_NETWORK_COMPLETION_TOKEN *MnpToken;
|
||
|
EFI_MANAGED_NETWORK_RECEIVE_DATA *MnpRxData;
|
||
|
IP4_LINK_RX_TOKEN *Token;
|
||
|
NET_FRAGMENT Netfrag;
|
||
|
NET_BUF *Packet;
|
||
|
UINT32 Flag;
|
||
|
|
||
|
Token = (IP4_LINK_RX_TOKEN *) Context;
|
||
|
NET_CHECK_SIGNATURE (Token, IP4_FRAME_RX_SIGNATURE);
|
||
|
|
||
|
//
|
||
|
// First clear the interface's receive request in case the
|
||
|
// caller wants to call Ip4ReceiveFrame in the callback.
|
||
|
//
|
||
|
Token->Interface->RecvRequest = NULL;
|
||
|
|
||
|
MnpToken = &Token->MnpToken;
|
||
|
MnpRxData = MnpToken->Packet.RxData;
|
||
|
|
||
|
if (EFI_ERROR (MnpToken->Status) || (MnpRxData == NULL)) {
|
||
|
Token->CallBack (Token->IpInstance, NULL, MnpToken->Status, 0, Token->Context);
|
||
|
Ip4FreeFrameRxToken (Token);
|
||
|
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Wrap the frame in a net buffer then deliever it to IP input.
|
||
|
// IP will reassemble the packet, and deliver it to upper layer
|
||
|
//
|
||
|
Netfrag.Len = MnpRxData->DataLength;
|
||
|
Netfrag.Bulk = MnpRxData->PacketData;
|
||
|
|
||
|
Packet = NetbufFromExt (&Netfrag, 1, 0, IP4_MAX_HEADLEN, Ip4RecycleFrame, Token);
|
||
|
|
||
|
if (Packet == NULL) {
|
||
|
gBS->SignalEvent (MnpRxData->RecycleEvent);
|
||
|
|
||
|
Token->CallBack (Token->IpInstance, NULL, EFI_OUT_OF_RESOURCES, 0, Token->Context);
|
||
|
Ip4FreeFrameRxToken (Token);
|
||
|
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
Flag = (MnpRxData->BroadcastFlag ? IP4_LINK_BROADCAST : 0);
|
||
|
Flag |= (MnpRxData->MulticastFlag ? IP4_LINK_MULTICAST : 0);
|
||
|
Flag |= (MnpRxData->PromiscuousFlag ? IP4_LINK_PROMISC : 0);
|
||
|
|
||
|
Token->CallBack (Token->IpInstance, Packet, EFI_SUCCESS, Flag, Token->Context);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Request Ip4OnFrameReceivedDpc as a DPC at TPL_CALLBACK.
|
||
|
|
||
|
@param Event The receive event delivered to MNP for receive.
|
||
|
@param Context Context for the callback.
|
||
|
|
||
|
**/
|
||
|
VOID
|
||
|
EFIAPI
|
||
|
Ip4OnFrameReceived (
|
||
|
IN EFI_EVENT Event,
|
||
|
IN VOID *Context
|
||
|
)
|
||
|
{
|
||
|
//
|
||
|
// Request Ip4OnFrameReceivedDpc as a DPC at TPL_CALLBACK
|
||
|
//
|
||
|
QueueDpc (TPL_CALLBACK, Ip4OnFrameReceivedDpc, Context);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
Request to receive the packet from the interface.
|
||
|
|
||
|
@param[in] Interface The interface to receive the frames from.
|
||
|
@param[in] IpInstance The instance that requests the receive. NULL for
|
||
|
the driver itself.
|
||
|
@param[in] CallBack Function to call when receive finished.
|
||
|
@param[in] Context Opaque parameter to the callback.
|
||
|
|
||
|
@retval EFI_ALREADY_STARTED There is already a pending receive request.
|
||
|
@retval EFI_OUT_OF_RESOURCES Failed to allocate resource to receive.
|
||
|
@retval EFI_SUCCESS The recieve request has been started.
|
||
|
@retval other Other error occurs.
|
||
|
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
Ip4ReceiveFrame (
|
||
|
IN IP4_INTERFACE *Interface,
|
||
|
IN IP4_PROTOCOL *IpInstance OPTIONAL,
|
||
|
IN IP4_FRAME_CALLBACK CallBack,
|
||
|
IN VOID *Context
|
||
|
)
|
||
|
{
|
||
|
IP4_LINK_RX_TOKEN *Token;
|
||
|
EFI_STATUS Status;
|
||
|
|
||
|
NET_CHECK_SIGNATURE (Interface, IP4_INTERFACE_SIGNATURE);
|
||
|
|
||
|
if (Interface->RecvRequest != NULL) {
|
||
|
return EFI_ALREADY_STARTED;
|
||
|
}
|
||
|
|
||
|
Token = Ip4CreateLinkRxToken (Interface, IpInstance, CallBack, Context);
|
||
|
|
||
|
if (Token == NULL) {
|
||
|
return EFI_OUT_OF_RESOURCES;
|
||
|
}
|
||
|
|
||
|
Interface->RecvRequest = Token;
|
||
|
Status = Interface->Mnp->Receive (Interface->Mnp, &Token->MnpToken);
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
Interface->RecvRequest = NULL;
|
||
|
Ip4FreeFrameRxToken (Token);
|
||
|
return Status;
|
||
|
}
|
||
|
return EFI_SUCCESS;
|
||
|
}
|