1086 lines
31 KiB
C
1086 lines
31 KiB
C
|
/** @file
|
||
|
The internal functions and routines to transmit the IP6 packet.
|
||
|
|
||
|
Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.<BR>
|
||
|
|
||
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
||
|
|
||
|
**/
|
||
|
|
||
|
#include "Ip6Impl.h"
|
||
|
|
||
|
UINT32 mIp6Id;
|
||
|
|
||
|
/**
|
||
|
Output all the available source addresses to a list entry head SourceList. The
|
||
|
number of source addresses are also returned.
|
||
|
|
||
|
@param[in] IpSb Points to an IP6 service binding instance.
|
||
|
@param[out] SourceList The list entry head of all source addresses.
|
||
|
It is the caller's responsibility to free the
|
||
|
resources.
|
||
|
@param[out] SourceCount The number of source addresses.
|
||
|
|
||
|
@retval EFI_SUCCESS The source addresses were copied to a list entry head
|
||
|
SourceList.
|
||
|
@retval EFI_OUT_OF_RESOURCES Failed to allocate resources to complete the operation.
|
||
|
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
Ip6CandidateSource (
|
||
|
IN IP6_SERVICE *IpSb,
|
||
|
OUT LIST_ENTRY *SourceList,
|
||
|
OUT UINT32 *SourceCount
|
||
|
)
|
||
|
{
|
||
|
IP6_INTERFACE *IpIf;
|
||
|
LIST_ENTRY *Entry;
|
||
|
LIST_ENTRY *Entry2;
|
||
|
IP6_ADDRESS_INFO *AddrInfo;
|
||
|
IP6_ADDRESS_INFO *Copy;
|
||
|
|
||
|
*SourceCount = 0;
|
||
|
|
||
|
if (IpSb->LinkLocalOk) {
|
||
|
Copy = AllocatePool (sizeof (IP6_ADDRESS_INFO));
|
||
|
if (Copy == NULL) {
|
||
|
return EFI_OUT_OF_RESOURCES;
|
||
|
}
|
||
|
|
||
|
Copy->Signature = IP6_ADDR_INFO_SIGNATURE;
|
||
|
IP6_COPY_ADDRESS (&Copy->Address, &IpSb->LinkLocalAddr);
|
||
|
Copy->IsAnycast = FALSE;
|
||
|
Copy->PrefixLength = IP6_LINK_LOCAL_PREFIX_LENGTH;
|
||
|
Copy->ValidLifetime = (UINT32) IP6_INFINIT_LIFETIME;
|
||
|
Copy->PreferredLifetime = (UINT32) IP6_INFINIT_LIFETIME;
|
||
|
|
||
|
InsertTailList (SourceList, &Copy->Link);
|
||
|
(*SourceCount)++;
|
||
|
}
|
||
|
|
||
|
NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {
|
||
|
IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link);
|
||
|
|
||
|
NET_LIST_FOR_EACH (Entry2, &IpIf->AddressList) {
|
||
|
AddrInfo = NET_LIST_USER_STRUCT_S (Entry2, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE);
|
||
|
|
||
|
if (AddrInfo->IsAnycast) {
|
||
|
//
|
||
|
// Never use an anycast address.
|
||
|
//
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
Copy = AllocateCopyPool (sizeof (IP6_ADDRESS_INFO), AddrInfo);
|
||
|
if (Copy == NULL) {
|
||
|
return EFI_OUT_OF_RESOURCES;
|
||
|
}
|
||
|
|
||
|
InsertTailList (SourceList, &Copy->Link);
|
||
|
(*SourceCount)++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Calculate how many bits are the same between two IPv6 addresses.
|
||
|
|
||
|
@param[in] AddressA Points to an IPv6 address.
|
||
|
@param[in] AddressB Points to another IPv6 address.
|
||
|
|
||
|
@return The common bits of the AddressA and AddressB.
|
||
|
|
||
|
**/
|
||
|
UINT8
|
||
|
Ip6CommonPrefixLen (
|
||
|
IN EFI_IPv6_ADDRESS *AddressA,
|
||
|
IN EFI_IPv6_ADDRESS *AddressB
|
||
|
)
|
||
|
{
|
||
|
UINT8 Count;
|
||
|
UINT8 Index;
|
||
|
UINT8 ByteA;
|
||
|
UINT8 ByteB;
|
||
|
UINT8 NumBits;
|
||
|
|
||
|
Count = 0;
|
||
|
Index = 0;
|
||
|
|
||
|
while (Index < 16) {
|
||
|
ByteA = AddressA->Addr[Index];
|
||
|
ByteB = AddressB->Addr[Index];
|
||
|
|
||
|
if (ByteA == ByteB) {
|
||
|
Count += 8;
|
||
|
Index++;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Check how many bits are common between the two bytes.
|
||
|
//
|
||
|
NumBits = 8;
|
||
|
ByteA = (UINT8) (ByteA ^ ByteB);
|
||
|
|
||
|
while (ByteA != 0) {
|
||
|
NumBits--;
|
||
|
ByteA = (UINT8) (ByteA >> 1);
|
||
|
}
|
||
|
|
||
|
return (UINT8) (Count + NumBits);
|
||
|
}
|
||
|
|
||
|
return Count;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Output all the available source addresses to a list entry head SourceList. The
|
||
|
number of source addresses are also returned.
|
||
|
|
||
|
@param[in] IpSb Points to a IP6 service binding instance.
|
||
|
@param[in] Destination The IPv6 destination address.
|
||
|
@param[out] Source The selected IPv6 source address according to
|
||
|
the Destination.
|
||
|
|
||
|
@retval EFI_SUCCESS The source addresses were copied to a list entry
|
||
|
head SourceList.
|
||
|
@retval EFI_NO_MAPPING The IPv6 stack is not auto configured.
|
||
|
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
Ip6SelectSourceAddress (
|
||
|
IN IP6_SERVICE *IpSb,
|
||
|
IN EFI_IPv6_ADDRESS *Destination,
|
||
|
OUT EFI_IPv6_ADDRESS *Source
|
||
|
)
|
||
|
{
|
||
|
EFI_STATUS Status;
|
||
|
LIST_ENTRY SourceList;
|
||
|
UINT32 SourceCount;
|
||
|
UINT8 ScopeD;
|
||
|
LIST_ENTRY *Entry;
|
||
|
IP6_ADDRESS_INFO *AddrInfo;
|
||
|
IP6_PREFIX_LIST_ENTRY *Prefix;
|
||
|
UINT8 LastCommonLength;
|
||
|
UINT8 CurrentCommonLength;
|
||
|
EFI_IPv6_ADDRESS *TmpAddress;
|
||
|
|
||
|
NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
|
||
|
|
||
|
Status = EFI_SUCCESS;
|
||
|
InitializeListHead (&SourceList);
|
||
|
|
||
|
if (!IpSb->LinkLocalOk) {
|
||
|
return EFI_NO_MAPPING;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Rule 1: Prefer same address.
|
||
|
//
|
||
|
if (Ip6IsOneOfSetAddress (IpSb, Destination, NULL, NULL)) {
|
||
|
IP6_COPY_ADDRESS (Source, Destination);
|
||
|
goto Exit;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Rule 2: Prefer appropriate scope.
|
||
|
//
|
||
|
if (IP6_IS_MULTICAST (Destination)) {
|
||
|
ScopeD = (UINT8) (Destination->Addr[1] >> 4);
|
||
|
} else if (NetIp6IsLinkLocalAddr (Destination)) {
|
||
|
ScopeD = 0x2;
|
||
|
} else {
|
||
|
ScopeD = 0xE;
|
||
|
}
|
||
|
|
||
|
if (ScopeD <= 0x2) {
|
||
|
//
|
||
|
// Return the link-local address if it exists
|
||
|
// One IP6_SERVICE only has one link-local address.
|
||
|
//
|
||
|
IP6_COPY_ADDRESS (Source, &IpSb->LinkLocalAddr);
|
||
|
goto Exit;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// All candidate source addresses are global unicast address.
|
||
|
//
|
||
|
Ip6CandidateSource (IpSb, &SourceList, &SourceCount);
|
||
|
|
||
|
if (SourceCount == 0) {
|
||
|
Status = EFI_NO_MAPPING;
|
||
|
goto Exit;
|
||
|
}
|
||
|
|
||
|
IP6_COPY_ADDRESS (Source, &IpSb->LinkLocalAddr);
|
||
|
|
||
|
if (SourceCount == 1) {
|
||
|
goto Exit;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Rule 3: Avoid deprecated addresses.
|
||
|
// TODO: check the "deprecated" state of the stateful configured address
|
||
|
//
|
||
|
NET_LIST_FOR_EACH (Entry, &IpSb->AutonomousPrefix) {
|
||
|
Prefix = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link);
|
||
|
if (Prefix->PreferredLifetime == 0) {
|
||
|
Ip6RemoveAddr (NULL, &SourceList, &SourceCount, &Prefix->Prefix, Prefix->PrefixLength);
|
||
|
|
||
|
if (SourceCount == 1) {
|
||
|
goto Exit;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// TODO: Rule 4: Prefer home addresses.
|
||
|
// TODO: Rule 5: Prefer outgoing interface.
|
||
|
// TODO: Rule 6: Prefer matching label.
|
||
|
// TODO: Rule 7: Prefer public addresses.
|
||
|
//
|
||
|
|
||
|
//
|
||
|
// Rule 8: Use longest matching prefix.
|
||
|
//
|
||
|
LastCommonLength = Ip6CommonPrefixLen (Source, Destination);
|
||
|
TmpAddress = NULL;
|
||
|
|
||
|
for (Entry = SourceList.ForwardLink; Entry != &SourceList; Entry = Entry->ForwardLink) {
|
||
|
AddrInfo = NET_LIST_USER_STRUCT_S (Entry, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE);
|
||
|
|
||
|
CurrentCommonLength = Ip6CommonPrefixLen (&AddrInfo->Address, Destination);
|
||
|
if (CurrentCommonLength > LastCommonLength) {
|
||
|
LastCommonLength = CurrentCommonLength;
|
||
|
TmpAddress = &AddrInfo->Address;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (TmpAddress != NULL) {
|
||
|
IP6_COPY_ADDRESS (Source, TmpAddress);
|
||
|
}
|
||
|
|
||
|
Exit:
|
||
|
|
||
|
Ip6RemoveAddr (NULL, &SourceList, &SourceCount, NULL, 0);
|
||
|
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Select an interface to send the packet generated in the IP6 driver
|
||
|
itself: that is, not by the requests of the IP6 child's consumer. Such
|
||
|
packets include the ICMPv6 echo replies and other ICMPv6 error packets.
|
||
|
|
||
|
@param[in] IpSb The IP4 service that wants to send the packets.
|
||
|
@param[in] Destination The destination of the packet.
|
||
|
@param[in, out] Source 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.
|
||
|
|
||
|
**/
|
||
|
IP6_INTERFACE *
|
||
|
Ip6SelectInterface (
|
||
|
IN IP6_SERVICE *IpSb,
|
||
|
IN EFI_IPv6_ADDRESS *Destination,
|
||
|
IN OUT EFI_IPv6_ADDRESS *Source
|
||
|
)
|
||
|
{
|
||
|
EFI_STATUS Status;
|
||
|
EFI_IPv6_ADDRESS SelectedSource;
|
||
|
IP6_INTERFACE *IpIf;
|
||
|
BOOLEAN Exist;
|
||
|
|
||
|
NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
|
||
|
ASSERT (Destination != NULL && Source != NULL);
|
||
|
|
||
|
if (NetIp6IsUnspecifiedAddr (Destination)) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
if (!NetIp6IsUnspecifiedAddr (Source)) {
|
||
|
Exist = Ip6IsOneOfSetAddress (IpSb, Source, &IpIf, NULL);
|
||
|
ASSERT (Exist);
|
||
|
|
||
|
return IpIf;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If source is unspecified, select a source according to the destination.
|
||
|
//
|
||
|
Status = Ip6SelectSourceAddress (IpSb, Destination, &SelectedSource);
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
return IpSb->DefaultInterface;
|
||
|
}
|
||
|
|
||
|
Ip6IsOneOfSetAddress (IpSb, &SelectedSource, &IpIf, NULL);
|
||
|
IP6_COPY_ADDRESS (Source, &SelectedSource);
|
||
|
|
||
|
return IpIf;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
The default callback function for the system generated packet.
|
||
|
It will free the packet.
|
||
|
|
||
|
@param[in] Packet The packet that transmitted.
|
||
|
@param[in] IoStatus The result of the transmission, succeeded or failed.
|
||
|
@param[in] LinkFlag Not used when transmitted. Check IP6_FRAME_CALLBACK
|
||
|
for reference.
|
||
|
@param[in] Context The context provided by us.
|
||
|
|
||
|
**/
|
||
|
VOID
|
||
|
Ip6SysPacketSent (
|
||
|
NET_BUF *Packet,
|
||
|
EFI_STATUS IoStatus,
|
||
|
UINT32 LinkFlag,
|
||
|
VOID *Context
|
||
|
)
|
||
|
{
|
||
|
NetbufFree (Packet);
|
||
|
Packet = NULL;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Prefix an IP6 basic head and unfragmentable extension headers and a fragment header
|
||
|
to the Packet. Used for IP6 fragmentation.
|
||
|
|
||
|
@param[in] IpSb The IP6 service instance to transmit the packet.
|
||
|
@param[in] Packet The packet to prefix the IP6 header to.
|
||
|
@param[in] Head The caller supplied header.
|
||
|
@param[in] FragmentOffset The fragment offset of the data following the header.
|
||
|
@param[in] ExtHdrs The length of the original extension header.
|
||
|
@param[in] ExtHdrsLen The length of the extension headers.
|
||
|
@param[in] LastHeader The pointer of next header of last extension header.
|
||
|
@param[in] HeadLen The length of the unfragmented part of the IP6 header.
|
||
|
|
||
|
@retval EFI_BAD_BUFFER_SIZE There is no enough room in the head space of
|
||
|
Packet.
|
||
|
@retval EFI_SUCCESS The operation performed successfully.
|
||
|
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
Ip6PrependHead (
|
||
|
IN IP6_SERVICE *IpSb,
|
||
|
IN NET_BUF *Packet,
|
||
|
IN EFI_IP6_HEADER *Head,
|
||
|
IN UINT16 FragmentOffset,
|
||
|
IN UINT8 *ExtHdrs,
|
||
|
IN UINT32 ExtHdrsLen,
|
||
|
IN UINT8 LastHeader,
|
||
|
IN UINT32 HeadLen
|
||
|
)
|
||
|
{
|
||
|
UINT32 Len;
|
||
|
UINT32 UnFragExtHdrsLen;
|
||
|
EFI_IP6_HEADER *PacketHead;
|
||
|
UINT8 *UpdatedExtHdrs;
|
||
|
EFI_STATUS Status;
|
||
|
UINT8 NextHeader;
|
||
|
|
||
|
UpdatedExtHdrs = NULL;
|
||
|
|
||
|
//
|
||
|
// HeadLen is the length of the fixed part of the sequences of fragments, i.e.
|
||
|
// the unfragment part.
|
||
|
//
|
||
|
PacketHead = (EFI_IP6_HEADER *) NetbufAllocSpace (Packet, HeadLen, NET_BUF_HEAD);
|
||
|
if (PacketHead == NULL) {
|
||
|
return EFI_BAD_BUFFER_SIZE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Set the head up, convert the host byte order to network byte order
|
||
|
//
|
||
|
CopyMem (PacketHead, Head, sizeof (EFI_IP6_HEADER));
|
||
|
PacketHead->PayloadLength = HTONS ((UINT16) (Packet->TotalSize - sizeof (EFI_IP6_HEADER)));
|
||
|
Packet->Ip.Ip6 = PacketHead;
|
||
|
|
||
|
Len = HeadLen - sizeof (EFI_IP6_HEADER);
|
||
|
UnFragExtHdrsLen = Len - sizeof (IP6_FRAGMENT_HEADER);
|
||
|
|
||
|
if (UnFragExtHdrsLen == 0) {
|
||
|
PacketHead->NextHeader = IP6_FRAGMENT;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Append the extension headers: firstly copy the unfragmentable headers, then append
|
||
|
// fragmentation header.
|
||
|
//
|
||
|
if ((FragmentOffset & IP6_FRAGMENT_OFFSET_MASK) == 0) {
|
||
|
NextHeader = Head->NextHeader;
|
||
|
} else {
|
||
|
NextHeader = PacketHead->NextHeader;
|
||
|
}
|
||
|
|
||
|
Status = Ip6FillFragmentHeader (
|
||
|
IpSb,
|
||
|
NextHeader,
|
||
|
LastHeader,
|
||
|
ExtHdrs,
|
||
|
ExtHdrsLen,
|
||
|
FragmentOffset,
|
||
|
&UpdatedExtHdrs
|
||
|
);
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
CopyMem (
|
||
|
(UINT8 *) (PacketHead + 1),
|
||
|
UpdatedExtHdrs,
|
||
|
UnFragExtHdrsLen + sizeof (IP6_FRAGMENT_HEADER)
|
||
|
);
|
||
|
|
||
|
FreePool (UpdatedExtHdrs);
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Transmit an IP6 packet. The packet comes either from the IP6
|
||
|
child's consumer (IpInstance != NULL) or the IP6 driver itself
|
||
|
(IpInstance == NULL). It will route the packet, fragment it,
|
||
|
then transmit all the fragments through an interface.
|
||
|
|
||
|
@param[in] IpSb The IP6 service instance to transmit the packet.
|
||
|
@param[in] Interface The IP6 interface to transmit the packet. Ignored
|
||
|
if NULL.
|
||
|
@param[in] IpInstance The IP6 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: NextHeader, HopLimit,
|
||
|
Src, Dest, FlowLabel, PayloadLength. This function
|
||
|
will fill in the Ver, TrafficClass.
|
||
|
@param[in] ExtHdrs The extension headers to append to the IPv6 basic
|
||
|
header.
|
||
|
@param[in] ExtHdrsLen The length of the extension headers.
|
||
|
@param[in] Callback The callback function to issue when transmission
|
||
|
completed.
|
||
|
@param[in] Context The opaque context for the callback.
|
||
|
|
||
|
@retval EFI_INVALID_PARAMETER Any input parameter or the packet is invalid.
|
||
|
@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 successfully transmitted.
|
||
|
@retval EFI_OUT_OF_RESOURCES Failed to finish the operation due to lack of
|
||
|
resources.
|
||
|
@retval Others Failed to transmit the packet.
|
||
|
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
Ip6Output (
|
||
|
IN IP6_SERVICE *IpSb,
|
||
|
IN IP6_INTERFACE *Interface OPTIONAL,
|
||
|
IN IP6_PROTOCOL *IpInstance OPTIONAL,
|
||
|
IN NET_BUF *Packet,
|
||
|
IN EFI_IP6_HEADER *Head,
|
||
|
IN UINT8 *ExtHdrs,
|
||
|
IN UINT32 ExtHdrsLen,
|
||
|
IN IP6_FRAME_CALLBACK Callback,
|
||
|
IN VOID *Context
|
||
|
)
|
||
|
{
|
||
|
IP6_INTERFACE *IpIf;
|
||
|
EFI_IPv6_ADDRESS NextHop;
|
||
|
IP6_NEIGHBOR_ENTRY *NeighborCache;
|
||
|
IP6_ROUTE_CACHE_ENTRY *RouteCache;
|
||
|
EFI_STATUS Status;
|
||
|
UINT32 Mtu;
|
||
|
UINT32 HeadLen;
|
||
|
UINT16 FragmentOffset;
|
||
|
UINT8 *LastHeader;
|
||
|
UINT32 UnFragmentLen;
|
||
|
UINT32 UnFragmentHdrsLen;
|
||
|
UINT32 FragmentHdrsLen;
|
||
|
UINT16 *Checksum;
|
||
|
UINT16 PacketChecksum;
|
||
|
UINT16 PseudoChecksum;
|
||
|
UINT32 Index;
|
||
|
UINT32 PacketLen;
|
||
|
UINT32 RealExtLen;
|
||
|
UINT32 Offset;
|
||
|
NET_BUF *TmpPacket;
|
||
|
NET_BUF *Fragment;
|
||
|
UINT32 Num;
|
||
|
UINT8 *Buf;
|
||
|
EFI_IP6_HEADER *PacketHead;
|
||
|
IP6_ICMP_HEAD *IcmpHead;
|
||
|
IP6_TXTOKEN_WRAP *Wrap;
|
||
|
IP6_ROUTE_ENTRY *RouteEntry;
|
||
|
UINT8 *UpdatedExtHdrs;
|
||
|
UINT8 NextHeader;
|
||
|
UINT8 LastHeaderBackup;
|
||
|
BOOLEAN FragmentHeadInserted;
|
||
|
UINT8 *ExtHdrsBackup;
|
||
|
UINT8 NextHeaderBackup;
|
||
|
EFI_IPv6_ADDRESS Source;
|
||
|
EFI_IPv6_ADDRESS Destination;
|
||
|
|
||
|
NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
|
||
|
|
||
|
//
|
||
|
// RFC2460: Each extension header is an integer multiple of 8 octets long,
|
||
|
// in order to retain 8-octet alignment for subsequent headers.
|
||
|
//
|
||
|
if ((ExtHdrsLen & 0x7) != 0) {
|
||
|
return EFI_INVALID_PARAMETER;
|
||
|
}
|
||
|
|
||
|
LastHeader = NULL;
|
||
|
|
||
|
Ip6IsExtsValid (
|
||
|
NULL,
|
||
|
NULL,
|
||
|
&Head->NextHeader,
|
||
|
ExtHdrs,
|
||
|
ExtHdrsLen,
|
||
|
FALSE,
|
||
|
NULL,
|
||
|
&LastHeader,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
NULL
|
||
|
);
|
||
|
|
||
|
//
|
||
|
// Select an interface/source for system packet, application
|
||
|
// should select them itself.
|
||
|
//
|
||
|
IpIf = Interface;
|
||
|
if (IpIf == NULL) {
|
||
|
//
|
||
|
// IpInstance->Interface is NULL when IpInstance is configured with both stationaddress
|
||
|
// and destinationaddress is unspecified.
|
||
|
//
|
||
|
if (IpInstance == NULL || IpInstance->Interface == NULL) {
|
||
|
IpIf = Ip6SelectInterface (IpSb, &Head->DestinationAddress, &Head->SourceAddress);
|
||
|
if (IpInstance != NULL) {
|
||
|
IpInstance->Interface = IpIf;
|
||
|
}
|
||
|
} else {
|
||
|
IpIf = IpInstance->Interface;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (IpIf == NULL) {
|
||
|
return EFI_NO_MAPPING;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Update the common field in Head here.
|
||
|
//
|
||
|
Head->Version = 6;
|
||
|
Head->TrafficClassL = 0;
|
||
|
Head->TrafficClassH = 0;
|
||
|
|
||
|
Checksum = NULL;
|
||
|
NextHeader = *LastHeader;
|
||
|
|
||
|
switch (NextHeader) {
|
||
|
case EFI_IP_PROTO_UDP:
|
||
|
Packet->Udp = (EFI_UDP_HEADER *) NetbufGetByte (Packet, 0, NULL);
|
||
|
ASSERT (Packet->Udp != NULL);
|
||
|
if (Packet->Udp->Checksum == 0) {
|
||
|
Checksum = &Packet->Udp->Checksum;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case EFI_IP_PROTO_TCP:
|
||
|
Packet->Tcp = (TCP_HEAD *) NetbufGetByte (Packet, 0, NULL);
|
||
|
ASSERT (Packet->Tcp != NULL);
|
||
|
if (Packet->Tcp->Checksum == 0) {
|
||
|
Checksum = &Packet->Tcp->Checksum;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case IP6_ICMP:
|
||
|
//
|
||
|
// Don't send ICMP packet to an IPv6 anycast address.
|
||
|
//
|
||
|
if (Ip6IsAnycast (IpSb, &Head->DestinationAddress)) {
|
||
|
return EFI_INVALID_PARAMETER;
|
||
|
}
|
||
|
|
||
|
IcmpHead = (IP6_ICMP_HEAD *) NetbufGetByte (Packet, 0, NULL);
|
||
|
ASSERT (IcmpHead != NULL);
|
||
|
if (IcmpHead->Checksum == 0) {
|
||
|
Checksum = &IcmpHead->Checksum;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (Checksum != NULL) {
|
||
|
//
|
||
|
// Calculate the checksum for upper layer protocol if it is not calculated due to lack of
|
||
|
// IPv6 source address.
|
||
|
//
|
||
|
PacketChecksum = NetbufChecksum (Packet);
|
||
|
PseudoChecksum = NetIp6PseudoHeadChecksum (
|
||
|
&Head->SourceAddress,
|
||
|
&Head->DestinationAddress,
|
||
|
NextHeader,
|
||
|
Packet->TotalSize
|
||
|
);
|
||
|
*Checksum = (UINT16) ~NetAddChecksum (PacketChecksum, PseudoChecksum);
|
||
|
}
|
||
|
|
||
|
Status = Ip6IpSecProcessPacket (
|
||
|
IpSb,
|
||
|
&Head,
|
||
|
LastHeader, // no need get the lasthead value for output
|
||
|
&Packet,
|
||
|
&ExtHdrs,
|
||
|
&ExtHdrsLen,
|
||
|
EfiIPsecOutBound,
|
||
|
Context
|
||
|
);
|
||
|
|
||
|
if (EFI_ERROR(Status)) {
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
LastHeader = NULL;
|
||
|
//
|
||
|
// Check incoming parameters.
|
||
|
//
|
||
|
if (!Ip6IsExtsValid (
|
||
|
IpSb,
|
||
|
Packet,
|
||
|
&Head->NextHeader,
|
||
|
ExtHdrs,
|
||
|
ExtHdrsLen,
|
||
|
FALSE,
|
||
|
NULL,
|
||
|
&LastHeader,
|
||
|
&RealExtLen,
|
||
|
&UnFragmentHdrsLen,
|
||
|
NULL
|
||
|
)) {
|
||
|
return EFI_INVALID_PARAMETER;
|
||
|
}
|
||
|
|
||
|
if ((RealExtLen & 0x7) != 0) {
|
||
|
return EFI_INVALID_PARAMETER;
|
||
|
}
|
||
|
|
||
|
LastHeaderBackup = *LastHeader;
|
||
|
|
||
|
//
|
||
|
// Perform next hop determination:
|
||
|
// For multicast packets, the next-hop is always the destination address and
|
||
|
// is considered to be on-link.
|
||
|
//
|
||
|
if (IP6_IS_MULTICAST (&Head->DestinationAddress)) {
|
||
|
IP6_COPY_ADDRESS (&NextHop, &Head->DestinationAddress);
|
||
|
} else {
|
||
|
//
|
||
|
// For unicast packets, use a combination of the Destination Cache, the Prefix List
|
||
|
// and the Default Router List to determine the IP address of the appropriate next hop.
|
||
|
//
|
||
|
|
||
|
NeighborCache = Ip6FindNeighborEntry (IpSb, &Head->DestinationAddress);
|
||
|
if (NeighborCache != NULL) {
|
||
|
//
|
||
|
// Hit Neighbor Cache.
|
||
|
//
|
||
|
IP6_COPY_ADDRESS (&NextHop, &Head->DestinationAddress);
|
||
|
} else {
|
||
|
//
|
||
|
// Not in Neighbor Cache, check Router cache
|
||
|
//
|
||
|
RouteCache = Ip6Route (IpSb, &Head->DestinationAddress, &Head->SourceAddress);
|
||
|
if (RouteCache == NULL) {
|
||
|
return EFI_NOT_FOUND;
|
||
|
}
|
||
|
|
||
|
IP6_COPY_ADDRESS (&NextHop, &RouteCache->NextHop);
|
||
|
Ip6FreeRouteCacheEntry (RouteCache);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Examines the Neighbor Cache for link-layer information about that neighbor.
|
||
|
// DO NOT create neighbor cache if neighbor is itself - when reporting ICMP error.
|
||
|
//
|
||
|
if (!IP6_IS_MULTICAST (&NextHop) && !EFI_IP6_EQUAL (&Head->DestinationAddress, &Head->SourceAddress)) {
|
||
|
NeighborCache = Ip6FindNeighborEntry (IpSb, &NextHop);
|
||
|
if (NeighborCache == NULL) {
|
||
|
NeighborCache = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, &NextHop, NULL);
|
||
|
|
||
|
if (NeighborCache == NULL) {
|
||
|
return EFI_OUT_OF_RESOURCES;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Send out multicast neighbor solicitation for address resolution immediately.
|
||
|
//
|
||
|
Ip6CreateSNMulticastAddr (&NeighborCache->Neighbor, &Destination);
|
||
|
Status = Ip6SelectSourceAddress (IpSb, &NeighborCache->Neighbor, &Source);
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
Status = Ip6SendNeighborSolicit (
|
||
|
IpSb,
|
||
|
&Source,
|
||
|
&Destination,
|
||
|
&NeighborCache->Neighbor,
|
||
|
&IpSb->SnpMode.CurrentAddress
|
||
|
);
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
--NeighborCache->Transmit;
|
||
|
NeighborCache->Ticks = IP6_GET_TICKS (IpSb->RetransTimer) + 1;
|
||
|
}
|
||
|
|
||
|
NeighborCache->Interface = IpIf;
|
||
|
}
|
||
|
|
||
|
UpdatedExtHdrs = NULL;
|
||
|
ExtHdrsBackup = NULL;
|
||
|
NextHeaderBackup = 0;
|
||
|
FragmentHeadInserted = FALSE;
|
||
|
|
||
|
//
|
||
|
// Check whether we received Packet Too Big message for the packet sent to the
|
||
|
// Destination. If yes include a Fragment Header in the subsequent packets.
|
||
|
//
|
||
|
RouteEntry = Ip6FindRouteEntry (
|
||
|
IpSb->RouteTable,
|
||
|
&Head->DestinationAddress,
|
||
|
NULL
|
||
|
);
|
||
|
if (RouteEntry != NULL) {
|
||
|
if ((RouteEntry->Flag & IP6_PACKET_TOO_BIG) == IP6_PACKET_TOO_BIG) {
|
||
|
|
||
|
//
|
||
|
// FragmentHead is inserted after Hop-by-Hop Options header, Destination
|
||
|
// Options header (first occur), Routing header, and before Fragment header,
|
||
|
// Authentication header, Encapsulating Security Payload header, and
|
||
|
// Destination Options header (last occur), and upper-layer header.
|
||
|
//
|
||
|
Status = Ip6FillFragmentHeader (
|
||
|
IpSb,
|
||
|
Head->NextHeader,
|
||
|
LastHeaderBackup,
|
||
|
ExtHdrs,
|
||
|
ExtHdrsLen,
|
||
|
0,
|
||
|
&UpdatedExtHdrs
|
||
|
);
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
if ((ExtHdrs == NULL) && (ExtHdrsLen == 0)) {
|
||
|
NextHeaderBackup = Head->NextHeader;
|
||
|
Head->NextHeader = IP6_FRAGMENT;
|
||
|
}
|
||
|
|
||
|
ExtHdrsBackup = ExtHdrs;
|
||
|
ExtHdrs = UpdatedExtHdrs;
|
||
|
ExtHdrsLen = ExtHdrsLen + sizeof (IP6_FRAGMENT_HEADER);
|
||
|
RealExtLen = RealExtLen + sizeof (IP6_FRAGMENT_HEADER);
|
||
|
|
||
|
mIp6Id++;
|
||
|
|
||
|
FragmentHeadInserted = TRUE;
|
||
|
}
|
||
|
|
||
|
Ip6FreeRouteEntry (RouteEntry);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// OK, selected the source and route, fragment the packet then send
|
||
|
// them. Tag each fragment other than the first one as spawn from it.
|
||
|
// Each extension header is an integer multiple of 8 octets long, in
|
||
|
// order to retain 8-octet alignment for subsequent headers.
|
||
|
//
|
||
|
Mtu = IpSb->MaxPacketSize + sizeof (EFI_IP6_HEADER);
|
||
|
HeadLen = sizeof (EFI_IP6_HEADER) + RealExtLen;
|
||
|
|
||
|
if (Packet->TotalSize + HeadLen > Mtu) {
|
||
|
//
|
||
|
// Remove the inserted Fragment Header since we need fragment the packet.
|
||
|
//
|
||
|
if (FragmentHeadInserted) {
|
||
|
ExtHdrs = ExtHdrsBackup;
|
||
|
ExtHdrsLen = ExtHdrsLen - sizeof (IP6_FRAGMENT_HEADER);
|
||
|
|
||
|
if ((ExtHdrs == NULL) && (ExtHdrsLen == 0)) {
|
||
|
Head->NextHeader = NextHeaderBackup;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
FragmentHdrsLen = ExtHdrsLen - UnFragmentHdrsLen;
|
||
|
|
||
|
//
|
||
|
// The packet is beyond the maximum which can be described through the
|
||
|
// fragment offset field in Fragment header.
|
||
|
//
|
||
|
if ((((Packet->TotalSize + FragmentHdrsLen) >> 3) & (~0x1fff)) != 0) {
|
||
|
Status = EFI_BAD_BUFFER_SIZE;
|
||
|
goto Error;
|
||
|
}
|
||
|
|
||
|
if (FragmentHdrsLen != 0) {
|
||
|
//
|
||
|
// Append the fragmentable extension hdrs before the upper layer payload
|
||
|
// to form a new NET_BUF. This NET_BUF contains all the buffer which will
|
||
|
// be fragmented below.
|
||
|
//
|
||
|
TmpPacket = NetbufGetFragment (Packet, 0, Packet->TotalSize, FragmentHdrsLen);
|
||
|
ASSERT (TmpPacket != NULL);
|
||
|
|
||
|
//
|
||
|
// Allocate the space to contain the fragmentable hdrs and copy the data.
|
||
|
//
|
||
|
Buf = NetbufAllocSpace (TmpPacket, FragmentHdrsLen, TRUE);
|
||
|
ASSERT (Buf != NULL);
|
||
|
CopyMem (Buf, ExtHdrs + UnFragmentHdrsLen, FragmentHdrsLen);
|
||
|
|
||
|
//
|
||
|
// Free the old Packet.
|
||
|
//
|
||
|
NetbufFree (Packet);
|
||
|
Packet = TmpPacket;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// The unfragment part which appears in every fragmented IPv6 packet includes
|
||
|
// the IPv6 header, the unfragmentable extension hdrs and the fragment header.
|
||
|
//
|
||
|
UnFragmentLen = sizeof (EFI_IP6_HEADER) + UnFragmentHdrsLen + sizeof (IP6_FRAGMENT_HEADER);
|
||
|
|
||
|
//
|
||
|
// Mtu now is the length of the fragment part in a full-length fragment.
|
||
|
//
|
||
|
Mtu = (Mtu - UnFragmentLen) & (~0x07);
|
||
|
Num = (Packet->TotalSize + Mtu - 1) / Mtu;
|
||
|
|
||
|
for (Index = 0, Offset = 0, PacketLen = Mtu; Index < Num; Index++) {
|
||
|
//
|
||
|
// Get fragment from the Packet, append UnFragnmentLen spare buffer
|
||
|
// before the fragmented data, the corresponding data is filled in later.
|
||
|
//
|
||
|
Fragment = NetbufGetFragment (Packet, Offset, PacketLen, UnFragmentLen);
|
||
|
if (Fragment == NULL) {
|
||
|
Status = EFI_OUT_OF_RESOURCES;
|
||
|
goto Error;
|
||
|
}
|
||
|
|
||
|
FragmentOffset = (UINT16) ((UINT16) Offset | 0x1);
|
||
|
if (Index == Num - 1){
|
||
|
//
|
||
|
// The last fragment, clear the M flag.
|
||
|
//
|
||
|
FragmentOffset &= (~0x1);
|
||
|
}
|
||
|
|
||
|
Status = Ip6PrependHead (
|
||
|
IpSb,
|
||
|
Fragment,
|
||
|
Head,
|
||
|
FragmentOffset,
|
||
|
ExtHdrs,
|
||
|
ExtHdrsLen,
|
||
|
LastHeaderBackup,
|
||
|
UnFragmentLen
|
||
|
);
|
||
|
ASSERT (Status == EFI_SUCCESS);
|
||
|
|
||
|
Status = Ip6SendFrame (
|
||
|
IpIf,
|
||
|
IpInstance,
|
||
|
Fragment,
|
||
|
&NextHop,
|
||
|
Ip6SysPacketSent,
|
||
|
Packet
|
||
|
);
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
goto Error;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// The last fragment of upper layer packet, update the IP6 token status.
|
||
|
//
|
||
|
if ((Index == Num -1) && (Context != NULL)) {
|
||
|
Wrap = (IP6_TXTOKEN_WRAP *) Context;
|
||
|
Wrap->Token->Status = Status;
|
||
|
}
|
||
|
|
||
|
Offset += PacketLen;
|
||
|
PacketLen = Packet->TotalSize - Offset;
|
||
|
if (PacketLen > Mtu) {
|
||
|
PacketLen = Mtu;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
NetbufFree (Packet);
|
||
|
mIp6Id++;
|
||
|
|
||
|
if (UpdatedExtHdrs != NULL) {
|
||
|
FreePool (UpdatedExtHdrs);
|
||
|
}
|
||
|
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Need not fragment the packet, send it in one frame.
|
||
|
//
|
||
|
PacketHead = (EFI_IP6_HEADER *) NetbufAllocSpace (Packet, HeadLen, NET_BUF_HEAD);
|
||
|
if (PacketHead == NULL) {
|
||
|
Status = EFI_BAD_BUFFER_SIZE;
|
||
|
goto Error;
|
||
|
}
|
||
|
|
||
|
CopyMem (PacketHead, Head, sizeof (EFI_IP6_HEADER));
|
||
|
Packet->Ip.Ip6 = PacketHead;
|
||
|
|
||
|
if (ExtHdrs != NULL) {
|
||
|
Buf = (UINT8 *) (PacketHead + 1);
|
||
|
CopyMem (Buf, ExtHdrs, ExtHdrsLen);
|
||
|
}
|
||
|
|
||
|
if (UpdatedExtHdrs != NULL) {
|
||
|
//
|
||
|
// A Fragment Header is inserted to the packet, update the payload length.
|
||
|
//
|
||
|
PacketHead->PayloadLength = (UINT16) (NTOHS (PacketHead->PayloadLength) +
|
||
|
sizeof (IP6_FRAGMENT_HEADER));
|
||
|
PacketHead->PayloadLength = HTONS (PacketHead->PayloadLength);
|
||
|
FreePool (UpdatedExtHdrs);
|
||
|
}
|
||
|
|
||
|
return Ip6SendFrame (
|
||
|
IpIf,
|
||
|
IpInstance,
|
||
|
Packet,
|
||
|
&NextHop,
|
||
|
Callback,
|
||
|
Context
|
||
|
);
|
||
|
|
||
|
Error:
|
||
|
if (UpdatedExtHdrs != NULL) {
|
||
|
FreePool (UpdatedExtHdrs);
|
||
|
}
|
||
|
Ip6CancelPacket (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 an unrelated packet.
|
||
|
|
||
|
**/
|
||
|
BOOLEAN
|
||
|
Ip6CancelPacketFragments (
|
||
|
IN IP6_LINK_TX_TOKEN *Frame,
|
||
|
IN VOID *Context
|
||
|
)
|
||
|
{
|
||
|
if ((Frame->Packet == (NET_BUF *) Context) || (Frame->Context == Context)) {
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Remove all the frames on the interface that pass the FrameToCancel,
|
||
|
either queued on ARP queues or that have already been delivered to
|
||
|
MNP and not yet recycled.
|
||
|
|
||
|
@param[in] Interface Interface to remove the frames from.
|
||
|
@param[in] IoStatus The transmit status returned to the frames' callback.
|
||
|
@param[in] FrameToCancel Function to select the frame to cancel; NULL to select all.
|
||
|
@param[in] Context Opaque parameters passed to FrameToCancel. Ignored if
|
||
|
FrameToCancel is NULL.
|
||
|
|
||
|
**/
|
||
|
VOID
|
||
|
Ip6CancelFrames (
|
||
|
IN IP6_INTERFACE *Interface,
|
||
|
IN EFI_STATUS IoStatus,
|
||
|
IN IP6_FRAME_TO_CANCEL FrameToCancel OPTIONAL,
|
||
|
IN VOID *Context OPTIONAL
|
||
|
)
|
||
|
{
|
||
|
LIST_ENTRY *Entry;
|
||
|
LIST_ENTRY *Next;
|
||
|
IP6_LINK_TX_TOKEN *Token;
|
||
|
IP6_SERVICE *IpSb;
|
||
|
IP6_NEIGHBOR_ENTRY *ArpQue;
|
||
|
EFI_STATUS Status;
|
||
|
|
||
|
IpSb = Interface->Service;
|
||
|
NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
|
||
|
|
||
|
//
|
||
|
// Cancel all the pending frames on ARP requests
|
||
|
//
|
||
|
NET_LIST_FOR_EACH_SAFE (Entry, Next, &Interface->ArpQues) {
|
||
|
ArpQue = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, ArpList);
|
||
|
|
||
|
Status = Ip6FreeNeighborEntry (
|
||
|
IpSb,
|
||
|
ArpQue,
|
||
|
FALSE,
|
||
|
FALSE,
|
||
|
IoStatus,
|
||
|
FrameToCancel,
|
||
|
Context
|
||
|
);
|
||
|
ASSERT_EFI_ERROR (Status);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Cancel all the frames that have been delivered to MNP
|
||
|
// but not yet recycled.
|
||
|
//
|
||
|
NET_LIST_FOR_EACH_SAFE (Entry, Next, &Interface->SentFrames) {
|
||
|
Token = NET_LIST_USER_STRUCT (Entry, IP6_LINK_TX_TOKEN, Link);
|
||
|
|
||
|
if ((FrameToCancel == NULL) || FrameToCancel (Token, Context)) {
|
||
|
IpSb->Mnp->Cancel (IpSb->Mnp, &Token->MnpToken);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Cancel the Packet and all its fragments.
|
||
|
|
||
|
@param[in] IpIf The interface from which the Packet is sent.
|
||
|
@param[in] Packet The Packet to cancel.
|
||
|
@param[in] IoStatus The status returns to the sender.
|
||
|
|
||
|
**/
|
||
|
VOID
|
||
|
Ip6CancelPacket (
|
||
|
IN IP6_INTERFACE *IpIf,
|
||
|
IN NET_BUF *Packet,
|
||
|
IN EFI_STATUS IoStatus
|
||
|
)
|
||
|
{
|
||
|
Ip6CancelFrames (IpIf, IoStatus, Ip6CancelPacketFragments, Packet);
|
||
|
}
|
||
|
|