REF: https://bugzilla.tianocore.org/show_bug.cgi?id=1144 Cc: Dandan Bi <dandan.bi@intel.com> Cc: Liming Gao <liming.gao@intel.com> Cc: Jian J Wang <jian.j.wang@intel.com> Cc: Siyuan Fu <siyuan.fu@intel.com> Cc: Jiaxin Wu <jiaxin.wu@intel.com> Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Star Zeng <star.zeng@intel.com> Reviewed-by: Siyuan Fu <siyuan.fu@intel.com> Reviewed-by: Dandan Bi <dandan.bi@intel.com>
		
			
				
	
	
		
			1352 lines
		
	
	
		
			38 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1352 lines
		
	
	
		
			38 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
|   Implement IP4 pesudo interface.
 | |
| 
 | |
| Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.<BR>
 | |
| This program and the accompanying materials
 | |
| are licensed and made available under the terms and conditions of the BSD License
 | |
| which accompanies this distribution.  The full text of the license may be found at
 | |
| http://opensource.org/licenses/bsd-license.php
 | |
| 
 | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
 | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 | |
| 
 | |
| **/
 | |
| 
 | |
| #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;
 | |
| }
 |