summaryrefslogtreecommitdiffstats
path: root/NetworkPkg/Ip4Dxe/Ip4If.c
diff options
context:
space:
mode:
Diffstat (limited to 'NetworkPkg/Ip4Dxe/Ip4If.c')
-rw-r--r--NetworkPkg/Ip4Dxe/Ip4If.c1345
1 files changed, 1345 insertions, 0 deletions
diff --git a/NetworkPkg/Ip4Dxe/Ip4If.c b/NetworkPkg/Ip4Dxe/Ip4If.c
new file mode 100644
index 0000000000..44b8d9fc8f
--- /dev/null
+++ b/NetworkPkg/Ip4Dxe/Ip4If.c
@@ -0,0 +1,1345 @@
+/** @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;
+}