summaryrefslogtreecommitdiffstats
path: root/NetworkPkg/Ip4Dxe/Ip4Output.c
diff options
context:
space:
mode:
Diffstat (limited to 'NetworkPkg/Ip4Dxe/Ip4Output.c')
-rw-r--r--NetworkPkg/Ip4Dxe/Ip4Output.c482
1 files changed, 482 insertions, 0 deletions
diff --git a/NetworkPkg/Ip4Dxe/Ip4Output.c b/NetworkPkg/Ip4Dxe/Ip4Output.c
new file mode 100644
index 0000000000..5eb3814089
--- /dev/null
+++ b/NetworkPkg/Ip4Dxe/Ip4Output.c
@@ -0,0 +1,482 @@
+/** @file
+ Transmit the IP4 packet.
+
+Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#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, IpIf->SubnetMask, TRUE);
+ } else {
+ CacheEntry = Ip4Route (IpInstance->RouteTable, Head->Dst, Head->Src, IpIf->SubnetMask, FALSE);
+ //
+ // 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, IpIf->SubnetMask, TRUE);
+ }
+ }
+
+ 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,
+ IpSb
+ );
+
+ 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, IpSb);
+
+ 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);
+}