ip4output.c
来自「EFI BIOS是Intel提出的下一代的BIOS标准。这里上传的Edk源代码是」· C语言 代码 · 共 489 行
C
489 行
/*++
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;
EFI_STATUS
Ip4PrependHead (
IN NET_BUF *Packet,
IN IP4_HEAD *Head,
IN UINT8 *Option,
IN UINT32 OptLen
)
/*++
Routine Description:
Prepend an IP4 head to the Packet. It will copy the options and
build the IP4 header fields. Used for IP4 fragmentation.
Arguments:
Packet - The packet to prepend IP4 header to
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.
Option - The orginal IP4 option to copy from
OptLen - The length of the IP4 option
Returns:
EFI_BAD_BUFFER_SIZE - There is no enought room in the head space of Packet.
EFI_SUCCESS - The IP4 header is successfully added to the packet.
--*/
{
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 = ~NetblockChecksum ((UINT8 *) PacketHead, HeadLen);
Packet->Ip = PacketHead;
return EFI_SUCCESS;
}
IP4_INTERFACE *
Ip4SelectInterface (
IN IP4_SERVICE *IpSb,
IN IP4_ADDR Dst,
IN IP4_ADDR Src
)
/*++
Routine Description:
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.
Arguments:
IpSb - The IP4 service that wants to send the packets.
Dst - The destination of the packet
Src - The source of the packet
Returns:
NULL if no proper interface is found, otherwise the interface that
can be used to send the system packet from.
--*/
{
IP4_INTERFACE *IpIf;
IP4_INTERFACE *Selected;
NET_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;
}
VOID
Ip4SysPacketSent (
IP4_PROTOCOL *Ip4Instance,
NET_BUF *Packet,
EFI_STATUS IoStatus,
UINT32 LinkFlag,
VOID *Context
)
/*++
Routine Description:
The default callback function for system generated packet.
It will free the packet.
Arguments:
Ip4Instance - The IP4 child that issued the transmission.
It most like is NULL.
Packet - The packet that transmitted.
IoStatus - The result of the transmission, succeeded or failed.
LinkFlag - Not used when transmission. check IP4_FRAME_CALLBACK for reference.
Context - The context provided by us
Returns:
None
--*/
{
NetbufFree (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
)
/*++
Routine Description:
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.
Arguments:
IpSb - The IP4 service instance to transmit the packet
IpInstance - The IP4 child that issues the transmission.
It is NULL if the packet is from the system.
Packet - The user data to send, excluding the IP header.
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.
Option - The original option to append to the IP headers
OptLen - The length of the option
GateWay - The next hop address to transmit packet to. 255.255.255.255
means broadcast.
Callback - The callback function to issue when transmission completed.
Context - The opaque context for the callback
Returns:
EFI_NO_MAPPING - There is no interface to the destination.
EFI_NOT_FOUND - There is no route to the destination
EFI_SUCCESS - The packet is successfully transmitted.
Others - Failed to transmit the packet.
--*/
{
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;
}
STATIC
BOOLEAN
Ip4CancelPacketFragments (
IP4_LINK_TX_TOKEN *Frame,
VOID *Context
)
/*++
Routine Description:
The filter function to find a packet and all its fragments.
The packet's fragments have their Context set to the packet.
Arguments:
Frame - The frames hold by the low level interface
Context - Context to the function, which is the packet.
Returns:
TRUE - This is the packet to cancel or its fragments.
FALSE - This is unrelated packet.
--*/
{
if ((Frame->Packet == (NET_BUF *) Context) || (Frame->Context == Context)) {
return TRUE;
}
return FALSE;
}
VOID
Ip4CancelPacket (
IN IP4_INTERFACE *IpIf,
IN NET_BUF *Packet,
IN EFI_STATUS IoStatus
)
/*++
Routine Description:
Cancel the Packet and all its fragments.
Arguments:
IpIf - The interface from which the Packet is sent
Packet - The Packet to cancel
IoStatus - The status returns to the sender.
Returns:
None
--*/
{
Ip4CancelFrames (IpIf, IoStatus, Ip4CancelPacketFragments, Packet);
}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?