and also add new UI configuration support in Ip4Dxe driver. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: jiaxinwu <jiaxin.wu@intel.com> Reviewed-by: Ye Ting <ting.ye@intel.com> git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@17853 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			488 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			488 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
|   Transmit the IP4 packet.
 | |
|   
 | |
| Copyright (c) 2005 - 2015, 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"
 | |
| 
 | |
| UINT16  mIp4Id;
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Prepend an IP4 head to the Packet. It will copy the options and
 | |
|   build the IP4 header fields. Used for IP4 fragmentation.
 | |
| 
 | |
|   @param  Packet           The packet to prepend IP4 header to
 | |
|   @param  Head             The caller supplied header. The caller should set
 | |
|                            the following header fields: Tos, TotalLen, Id,
 | |
|                            Fragment, Ttl, Protocol, Src and Dst. All the fields
 | |
|                            are in host byte order. This function will fill in
 | |
|                            the Ver, HeadLen, and checksum.
 | |
|   @param  Option           The orginal IP4 option to copy from
 | |
|   @param  OptLen           The length of the IP4 option
 | |
| 
 | |
|   @retval EFI_BAD_BUFFER_SIZE  There is no enought room in the head space of
 | |
|                                Packet.
 | |
|   @retval EFI_SUCCESS          The IP4 header is successfully added to the packet.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| Ip4PrependHead (
 | |
|   IN OUT NET_BUF                *Packet,
 | |
|   IN     IP4_HEAD               *Head,
 | |
|   IN     UINT8                  *Option,
 | |
|   IN     UINT32                 OptLen
 | |
|   )
 | |
| {
 | |
|   UINT32                    HeadLen;
 | |
|   UINT32                    Len;
 | |
|   IP4_HEAD                  *PacketHead;
 | |
|   BOOLEAN                   FirstFragment;
 | |
| 
 | |
|   //
 | |
|   // Prepend the options: first get the option length, then copy it over.
 | |
|   //
 | |
|   HeadLen       = 0;
 | |
|   FirstFragment = IP4_FIRST_FRAGMENT (Head->Fragment);
 | |
| 
 | |
|   Ip4CopyOption (Option, OptLen, FirstFragment, NULL, &Len);
 | |
| 
 | |
|   HeadLen = IP4_MIN_HEADLEN + Len;
 | |
|   ASSERT (((Len % 4) == 0) && (HeadLen <= IP4_MAX_HEADLEN));
 | |
| 
 | |
|   PacketHead = (IP4_HEAD *) NetbufAllocSpace (Packet, HeadLen, NET_BUF_HEAD);
 | |
| 
 | |
|   if (PacketHead == NULL) {
 | |
|     return EFI_BAD_BUFFER_SIZE;
 | |
|   }
 | |
| 
 | |
|   Ip4CopyOption (Option, OptLen, FirstFragment, (UINT8 *) (PacketHead + 1), &Len);
 | |
| 
 | |
|   //
 | |
|   // Set the head up, convert the host byte order to network byte order
 | |
|   //
 | |
|   PacketHead->Ver       = 4;
 | |
|   PacketHead->HeadLen   = (UINT8) (HeadLen >> 2);
 | |
|   PacketHead->Tos       = Head->Tos;
 | |
|   PacketHead->TotalLen  = HTONS ((UINT16) Packet->TotalSize);
 | |
|   PacketHead->Id        = HTONS (Head->Id);
 | |
|   PacketHead->Fragment  = HTONS (Head->Fragment);
 | |
|   PacketHead->Checksum  = 0;
 | |
|   PacketHead->Ttl       = Head->Ttl;
 | |
|   PacketHead->Protocol  = Head->Protocol;
 | |
|   PacketHead->Src       = HTONL (Head->Src);
 | |
|   PacketHead->Dst       = HTONL (Head->Dst);
 | |
|   PacketHead->Checksum  = (UINT16) (~NetblockChecksum ((UINT8 *) PacketHead, HeadLen));
 | |
| 
 | |
|   Packet->Ip.Ip4        = PacketHead;
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Select an interface to send the packet generated in the IP4 driver
 | |
|   itself, that is, not by the requests of IP4 child's consumer. Such
 | |
|   packets include the ICMP echo replies, and other ICMP error packets.
 | |
| 
 | |
|   @param[in]  IpSb                 The IP4 service that wants to send the packets.
 | |
|   @param[in]  Dst                  The destination of the packet
 | |
|   @param[in]  Src                  The source of the packet
 | |
| 
 | |
|   @return NULL if no proper interface is found, otherwise the interface that
 | |
|           can be used to send the system packet from.
 | |
| 
 | |
| **/
 | |
| IP4_INTERFACE *
 | |
| Ip4SelectInterface (
 | |
|   IN IP4_SERVICE            *IpSb,
 | |
|   IN IP4_ADDR               Dst,
 | |
|   IN IP4_ADDR               Src
 | |
|   )
 | |
| {
 | |
|   IP4_INTERFACE             *IpIf;
 | |
|   IP4_INTERFACE             *Selected;
 | |
|   LIST_ENTRY                *Entry;
 | |
| 
 | |
|   //
 | |
|   // Select the interface the Dst is on if one of the connected
 | |
|   // network. Some IP instance may be configured with 0.0.0.0/0,
 | |
|   // don't select that interface now.
 | |
|   //
 | |
|   IpIf = Ip4FindNet (IpSb, Dst);
 | |
| 
 | |
|   if ((IpIf != NULL) && (IpIf->Ip != IP4_ALLZERO_ADDRESS)) {
 | |
|     return IpIf;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // If source is one of the interface address, select it.
 | |
|   //
 | |
|   IpIf = Ip4FindInterface (IpSb, Src);
 | |
| 
 | |
|   if ((IpIf != NULL) && (IpIf->Ip != IP4_ALLZERO_ADDRESS)) {
 | |
|     return IpIf;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Select a configured interface as the fall back. Always prefer
 | |
|   // an interface with non-zero address.
 | |
|   //
 | |
|   Selected = NULL;
 | |
| 
 | |
|   NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {
 | |
|     IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link);
 | |
| 
 | |
|     if (IpIf->Configured && ((Selected == NULL) || (Selected->Ip == 0))) {
 | |
|       Selected = IpIf;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return Selected;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   The default callback function for system generated packet.
 | |
|   It will free the packet.
 | |
| 
 | |
|   @param  Ip4Instance          The IP4 child that issued the transmission.  It most
 | |
|                                like is NULL.
 | |
|   @param  Packet               The packet that transmitted.
 | |
|   @param  IoStatus             The result of the transmission, succeeded or failed.
 | |
|   @param  LinkFlag             Not used when transmission. check IP4_FRAME_CALLBACK
 | |
|                                for reference.
 | |
|   @param  Context              The context provided by us
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| Ip4SysPacketSent (
 | |
|   IP4_PROTOCOL              *Ip4Instance,
 | |
|   NET_BUF                   *Packet,
 | |
|   EFI_STATUS                IoStatus,
 | |
|   UINT32                    LinkFlag,
 | |
|   VOID                      *Context
 | |
|   )
 | |
| {
 | |
|   NetbufFree (Packet);
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Transmit an IP4 packet. The packet comes either from the IP4
 | |
|   child's consumer (IpInstance != NULL) or the IP4 driver itself
 | |
|   (IpInstance == NULL). It will route the packet, fragment it,
 | |
|   then transmit all the fragments through some interface.
 | |
| 
 | |
|   @param[in]  IpSb             The IP4 service instance to transmit the packet
 | |
|   @param[in]  IpInstance       The IP4 child that issues the transmission.  It is
 | |
|                                NULL if the packet is from the system.
 | |
|   @param[in]  Packet           The user data to send, excluding the IP header.
 | |
|   @param[in]  Head             The caller supplied header. The caller should set
 | |
|                                the following header fields: Tos, TotalLen, Id, tl,
 | |
|                                Fragment, Protocol, Src and Dst. All the fields are
 | |
|                                in host byte  order. This function will fill in the
 | |
|                                Ver, HeadLen,  Fragment, and checksum. The Fragment
 | |
|                                only need to include the DF flag. Ip4Output will
 | |
|                                compute the MF and offset for  you.
 | |
|   @param[in]  Option           The original option to append to the IP headers
 | |
|   @param[in]  OptLen           The length of the option
 | |
|   @param[in]  GateWay          The next hop address to transmit packet to.
 | |
|                                255.255.255.255 means broadcast.
 | |
|   @param[in]  Callback         The callback function to issue when transmission
 | |
|                                completed.
 | |
|   @param[in]  Context          The opaque context for the callback
 | |
| 
 | |
|   @retval EFI_NO_MAPPING       There is no interface to the destination.
 | |
|   @retval EFI_NOT_FOUND        There is no route to the destination
 | |
|   @retval EFI_SUCCESS          The packet is successfully transmitted.
 | |
|   @retval EFI_BAD_BUFFER_SIZE  The length of the IPv4 header + option length +
 | |
|                                total data length is greater than MTU (or greater
 | |
|                                than the maximum packet size if Token.Packet.TxData.
 | |
|                                OverrideData.DoNotFragment is TRUE.)
 | |
|   @retval Others               Failed to transmit the packet.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| Ip4Output (
 | |
|   IN IP4_SERVICE            *IpSb,
 | |
|   IN IP4_PROTOCOL           *IpInstance  OPTIONAL,
 | |
|   IN NET_BUF                *Packet,
 | |
|   IN IP4_HEAD               *Head,
 | |
|   IN UINT8                  *Option,
 | |
|   IN UINT32                 OptLen,
 | |
|   IN IP4_ADDR               GateWay,
 | |
|   IN IP4_FRAME_CALLBACK     Callback,
 | |
|   IN VOID                   *Context
 | |
|   )
 | |
| {
 | |
|   IP4_INTERFACE             *IpIf;
 | |
|   IP4_ROUTE_CACHE_ENTRY     *CacheEntry;
 | |
|   IP4_ADDR                  Dest;
 | |
|   EFI_STATUS                Status;
 | |
|   NET_BUF                   *Fragment;
 | |
|   UINT32                    Index;
 | |
|   UINT32                    HeadLen;
 | |
|   UINT32                    PacketLen;
 | |
|   UINT32                    Offset;
 | |
|   UINT32                    Mtu;
 | |
|   UINT32                    Num;
 | |
|   BOOLEAN                   RawData;
 | |
| 
 | |
|   //
 | |
|   // Select an interface/source for system packet, application
 | |
|   // should select them itself.
 | |
|   //
 | |
|   if (IpInstance == NULL) {
 | |
|     IpIf = Ip4SelectInterface (IpSb, Head->Dst, Head->Src);
 | |
|   } else {
 | |
|     IpIf = IpInstance->Interface;
 | |
|   }
 | |
| 
 | |
|   if (IpIf == NULL) {
 | |
|     return EFI_NO_MAPPING;
 | |
|   }
 | |
| 
 | |
|   if ((Head->Src == IP4_ALLZERO_ADDRESS) && (IpInstance == NULL)) {
 | |
|     Head->Src = IpIf->Ip;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Before IPsec process, prepared the IP head.
 | |
|   // If Ip4Output is transmitting RawData, don't update IPv4 header.
 | |
|   //
 | |
|   HeadLen = sizeof (IP4_HEAD) + ((OptLen + 3) & (~0x03));
 | |
| 
 | |
|   if ((IpInstance != NULL) && IpInstance->ConfigData.RawData) {
 | |
|     RawData        = TRUE;
 | |
|   } else {
 | |
|     Head->HeadLen  = (UINT8) (HeadLen >> 2);
 | |
|     Head->Id       = mIp4Id++;
 | |
|     Head->Ver      = 4;
 | |
|     RawData        = FALSE;
 | |
|   }
 | |
|   
 | |
|   //
 | |
|   // Call IPsec process.
 | |
|   //
 | |
|   Status = Ip4IpSecProcessPacket (
 | |
|              IpSb, 
 | |
|              &Head, 
 | |
|              &Packet, 
 | |
|              &Option, 
 | |
|              &OptLen, 
 | |
|              EfiIPsecOutBound,
 | |
|              Context
 | |
|              );
 | |
| 
 | |
|   if (EFI_ERROR(Status)) {
 | |
|     return Status;
 | |
|   }
 | |
|   
 | |
|   Dest = Head->Dst;
 | |
|   if (IP4_IS_BROADCAST (Ip4GetNetCast (Dest, IpIf)) || (Dest == IP4_ALLONE_ADDRESS)) {
 | |
|     //
 | |
|     // Set the gateway to local broadcast if the Dest is
 | |
|     // the broadcast address for the connected network or
 | |
|     // it is local broadcast.
 | |
|     //
 | |
|     GateWay = IP4_ALLONE_ADDRESS;
 | |
| 
 | |
|   } else if (IP4_IS_MULTICAST (Dest)) {
 | |
|     //
 | |
|     // Set the gateway to the destination if it is an multicast
 | |
|     // address. The IP4_INTERFACE won't consult ARP to send local
 | |
|     // broadcast and multicast.
 | |
|     //
 | |
|     GateWay = Head->Dst;
 | |
| 
 | |
|   } else if (GateWay == IP4_ALLZERO_ADDRESS) {
 | |
|     //
 | |
|     // Route the packet unless overrided, that is, GateWay isn't zero.
 | |
|     //
 | |
|     if (IpInstance == NULL) {
 | |
|       CacheEntry = Ip4Route (IpSb->DefaultRouteTable, Head->Dst, Head->Src);
 | |
|     } else {
 | |
|       CacheEntry = Ip4Route (IpInstance->RouteTable, Head->Dst, Head->Src);
 | |
|       //
 | |
|       // If failed to route the packet by using the instance's route table,
 | |
|       // try to use the default route table.
 | |
|       //
 | |
|       if (CacheEntry == NULL) {
 | |
|         CacheEntry = Ip4Route (IpSb->DefaultRouteTable, Head->Dst, Head->Src);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (CacheEntry == NULL) {
 | |
|       return EFI_NOT_FOUND;
 | |
|     }
 | |
| 
 | |
|     GateWay = CacheEntry->NextHop;
 | |
|     Ip4FreeRouteCacheEntry (CacheEntry);
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // OK, selected the source and route, fragment the packet then send
 | |
|   // them. Tag each fragment other than the first one as spawn from it.
 | |
|   //
 | |
|   Mtu = IpSb->MaxPacketSize + sizeof (IP4_HEAD);  
 | |
| 
 | |
|   if (Packet->TotalSize + HeadLen > Mtu) {
 | |
|     //
 | |
|     // Fragmentation is diabled for RawData mode.
 | |
|     //
 | |
|     if (RawData) {
 | |
|       return EFI_BAD_BUFFER_SIZE;
 | |
|     }
 | |
|     
 | |
|     //
 | |
|     // Packet is fragmented from the tail to the head, that is, the
 | |
|     // first frame sent is the last fragment of the packet. The first
 | |
|     // fragment is NOT sent in this loop. First compute how many
 | |
|     // fragments there are.
 | |
|     //
 | |
|     Mtu       = (Mtu - HeadLen) & (~0x07);
 | |
|     Num       = (Packet->TotalSize + Mtu - 1) / Mtu;
 | |
| 
 | |
|     //
 | |
|     // Initialize the packet length and Offset. Other than the last
 | |
|     // fragment, the packet length equals to MTU. The offset is always
 | |
|     // aligned to MTU.
 | |
|     //
 | |
|     PacketLen = Packet->TotalSize - (Num - 1) * Mtu;
 | |
|     Offset    = Mtu * (Num - 1);
 | |
| 
 | |
|     for (Index = 0; Index < Num - 1; Index++, Offset -= Mtu) {
 | |
|       Fragment = NetbufGetFragment (Packet, Offset, PacketLen, IP4_MAX_HEADLEN);
 | |
| 
 | |
|       if (Fragment == NULL) {
 | |
|         Status = EFI_OUT_OF_RESOURCES;
 | |
|         goto ON_ERROR;
 | |
|       }
 | |
| 
 | |
|       //
 | |
|       // Update the header's fragment. The caller fills the IP4 header
 | |
|       // fields that are required by Ip4PrependHead except the fragment.
 | |
|       //
 | |
|       Head->Fragment = IP4_HEAD_FRAGMENT_FIELD (FALSE, (Index != 0), Offset);
 | |
|       Ip4PrependHead (Fragment, Head, Option, OptLen);
 | |
| 
 | |
|       //
 | |
|       // Transmit the fragments, pass the Packet address as the context.
 | |
|       // So, we can find all the fragments spawned from the Packet by
 | |
|       // compare the NetBuf and Context to the Packet.
 | |
|       //
 | |
|       Status = Ip4SendFrame (
 | |
|                  IpIf,
 | |
|                  IpInstance,
 | |
|                  Fragment,
 | |
|                  GateWay,
 | |
|                  Ip4SysPacketSent,
 | |
|                  Packet
 | |
|                  );
 | |
| 
 | |
|       if (EFI_ERROR (Status)) {
 | |
|         goto ON_ERROR;
 | |
|       }
 | |
| 
 | |
|       PacketLen = Mtu;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Trim the already sent data, then adjust the head's fragment field.
 | |
|     //
 | |
|     NetbufTrim (Packet, Packet->TotalSize - Mtu, FALSE);
 | |
|     Head->Fragment = IP4_HEAD_FRAGMENT_FIELD (FALSE, TRUE, 0);
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Send the first fragment, it is either the orginal packet, or the
 | |
|   // first fragment of a fragmented packet. It seems that there is a subtle
 | |
|   // bug here: what if the caller free the packet in Callback and IpIf (or
 | |
|   // MNP child used by that interface) still holds the fragments and try
 | |
|   // to access the data? The caller can free the packet if it recycles the
 | |
|   // consumer's (such as UDP) data in the Callback. But this can't happen.
 | |
|   // The detailed sequence is:
 | |
|   // 1. for the packets generated by IP4 driver itself:
 | |
|   //    The Callback is Ip4SysPacketSent, which is the same as the
 | |
|   //    fragments' callback. Ip4SysPacketSent simply calls NetbufFree
 | |
|   //    to release its reference to the packet. So, no problem for
 | |
|   //    system packets.
 | |
|   //
 | |
|   // 2. for the upper layer's packets (use UDP as an example):
 | |
|   //    UDP requests the IP layer to transmit some data which is
 | |
|   //    wrapped in an asynchronous token, the token is wrapped
 | |
|   //    in IP4_TXTOKEN_WRAP by IP4. IP4 also wrap the user's data
 | |
|   //    in a net buffer, which is Packet we get here. IP4_TXTOKEN_WRAP
 | |
|   //    is bound with the Packet. It will only be freed when all
 | |
|   //    the references to Packet have been released. Upon then, the
 | |
|   //    Packet's OnFree callback will release the IP4_TXTOKEN_WRAP,
 | |
|   //    and singal the user's recycle event. So, also no problem for
 | |
|   //    upper layer's packets.
 | |
|   //
 | |
|   Ip4PrependHead (Packet, Head, Option, OptLen);
 | |
|   Status = Ip4SendFrame (IpIf, IpInstance, Packet, GateWay, Callback, Context);
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     goto ON_ERROR;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| 
 | |
| ON_ERROR:
 | |
|   Ip4CancelPacket (IpIf, Packet, Status);
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   The filter function to find a packet and all its fragments.
 | |
|   The packet's fragments have their Context set to the packet.
 | |
| 
 | |
|   @param[in]  Frame            The frames hold by the low level interface
 | |
|   @param[in]  Context          Context to the function, which is the packet.
 | |
| 
 | |
|   @retval TRUE                 This is the packet to cancel or its fragments.
 | |
|   @retval FALSE                This is unrelated packet.
 | |
| 
 | |
| **/
 | |
| BOOLEAN
 | |
| Ip4CancelPacketFragments (
 | |
|   IN IP4_LINK_TX_TOKEN   *Frame,
 | |
|   IN VOID                *Context
 | |
|   )
 | |
| {
 | |
|   if ((Frame->Packet == (NET_BUF *) Context) || (Frame->Context == Context)) {
 | |
|     return TRUE;
 | |
|   }
 | |
| 
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Cancel the Packet and all its fragments.
 | |
| 
 | |
|   @param  IpIf                 The interface from which the Packet is sent
 | |
|   @param  Packet               The Packet to cancel
 | |
|   @param  IoStatus             The status returns to the sender.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| Ip4CancelPacket (
 | |
|   IN IP4_INTERFACE    *IpIf,
 | |
|   IN NET_BUF          *Packet,
 | |
|   IN EFI_STATUS       IoStatus
 | |
|   )
 | |
| {
 | |
|   Ip4CancelFrames (IpIf, IoStatus, Ip4CancelPacketFragments, Packet);
 | |
| }
 |