483 lines
15 KiB
C
483 lines
15 KiB
C
|
/** @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);
|
||
|
}
|