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