/** @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 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); | |
} |