audk/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Output.c

458 lines
15 KiB
C

/** @file
Copyright (c) 2005 - 2006, Intel Corporation
All rights reserved. This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
Module Name:
Ip4Output.c
Abstract:
Transmit the IP4 packet
**/
#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 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 (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 = 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 IpSb The IP4 service that wants to send the packets.
@param Dst The destination of the packet
@param Src The source of the packet
@return NULL if no proper interface is found, otherwise the interface that
@return 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
@return None
**/
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 IpSb The IP4 service instance to transmit the packet
@param IpInstance The IP4 child that issues the transmission. It is
NULL if the packet is from the system.
@param Packet The user data to send, excluding the IP header.
@param 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 Option The original option to append to the IP headers
@param OptLen The length of the option
@param GateWay The next hop address to transmit packet to.
255.255.255.255 means broadcast.
@param Callback The callback function to issue when transmission
completed.
@param 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 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;
//
// 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;
}
//
// Route the packet unless overrided, that is, GateWay isn't zero.
//
if (GateWay == IP4_ALLZERO_ADDRESS) {
Dest = Head->Dst;
if (IP4_IS_BROADCAST (Ip4GetNetCast (Dest, IpIf)) || (Dest == IP4_ALLONE_ADDRESS)) {
//
// Set the gateway to local broadcast if the Dest is
// 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 {
//
// Consult the route table to route the packet
//
if (IpInstance == NULL) {
CacheEntry = Ip4Route (IpSb->DefaultRouteTable, Head->Dst, Head->Src);
} else {
CacheEntry = Ip4Route (IpInstance->RouteTable, Head->Dst, Head->Src);
}
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->SnpMode.MaxPacketSize;
HeadLen = sizeof (IP4_HEAD) + ((OptLen + 3) & (~0x03));
Head->Id = mIp4Id++;
if (Packet->TotalSize + HeadLen > Mtu) {
//
// 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
);
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);
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 Frame The frames hold by the low level interface
@param 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.
**/
STATIC
BOOLEAN
Ip4CancelPacketFragments (
IP4_LINK_TX_TOKEN *Frame,
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.
@return None
**/
VOID
Ip4CancelPacket (
IN IP4_INTERFACE *IpIf,
IN NET_BUF *Packet,
IN EFI_STATUS IoStatus
)
{
Ip4CancelFrames (IpIf, IoStatus, Ip4CancelPacketFragments, Packet);
}