From f215e02bf85f68d3a6106c2a1f4f7f063f819064 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:17:27 +0200 Subject: Adding upstream version 7.0.14-dfsg. Signed-off-by: Daniel Baumann --- .../EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Output.c | 482 +++++++++++++++++++++ 1 file changed, 482 insertions(+) create mode 100644 src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Output.c (limited to 'src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Output.c') diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Output.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Output.c new file mode 100644 index 00000000..c48b4b1c --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Output.c @@ -0,0 +1,482 @@ +/** @file + Transmit the IP4 packet. + +Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.
+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 original IP4 option to copy from + @param OptLen The length of the IP4 option + + @retval EFI_BAD_BUFFER_SIZE There is no enough 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 overridden, 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 disabled 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 original 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 signal 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); +} -- cgit v1.2.3