mirror of https://github.com/acidanthera/audk.git
1832 lines
52 KiB
C
1832 lines
52 KiB
C
/** @file
|
|
IP6 internal functions to process the incoming packets.
|
|
|
|
Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>
|
|
(C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR>
|
|
|
|
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.
|
|
|
|
**/
|
|
|
|
#include "Ip6Impl.h"
|
|
|
|
/**
|
|
Create an empty assemble entry for the packet identified by
|
|
(Dst, Src, Id). The default life for the packet is 60 seconds.
|
|
|
|
@param[in] Dst The destination address.
|
|
@param[in] Src The source address.
|
|
@param[in] Id The ID field in the IP header.
|
|
|
|
@return NULL if failed to allocate memory for the entry. Otherwise,
|
|
the pointer to the just created reassemble entry.
|
|
|
|
**/
|
|
IP6_ASSEMBLE_ENTRY *
|
|
Ip6CreateAssembleEntry (
|
|
IN EFI_IPv6_ADDRESS *Dst,
|
|
IN EFI_IPv6_ADDRESS *Src,
|
|
IN UINT32 Id
|
|
)
|
|
{
|
|
IP6_ASSEMBLE_ENTRY *Assemble;
|
|
|
|
Assemble = AllocatePool (sizeof (IP6_ASSEMBLE_ENTRY));
|
|
if (Assemble == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
IP6_COPY_ADDRESS (&Assemble->Dst, Dst);
|
|
IP6_COPY_ADDRESS (&Assemble->Src, Src);
|
|
InitializeListHead (&Assemble->Fragments);
|
|
|
|
Assemble->Id = Id;
|
|
Assemble->Life = IP6_FRAGMENT_LIFE + 1;
|
|
|
|
Assemble->TotalLen = 0;
|
|
Assemble->CurLen = 0;
|
|
Assemble->Head = NULL;
|
|
Assemble->Info = NULL;
|
|
Assemble->Packet = NULL;
|
|
|
|
return Assemble;
|
|
}
|
|
|
|
/**
|
|
Release all the fragments of a packet, then free the assemble entry.
|
|
|
|
@param[in] Assemble The assemble entry to free.
|
|
|
|
**/
|
|
VOID
|
|
Ip6FreeAssembleEntry (
|
|
IN IP6_ASSEMBLE_ENTRY *Assemble
|
|
)
|
|
{
|
|
LIST_ENTRY *Entry;
|
|
LIST_ENTRY *Next;
|
|
NET_BUF *Fragment;
|
|
|
|
NET_LIST_FOR_EACH_SAFE (Entry, Next, &Assemble->Fragments) {
|
|
Fragment = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
|
|
|
|
RemoveEntryList (Entry);
|
|
NetbufFree (Fragment);
|
|
}
|
|
|
|
if (Assemble->Packet != NULL) {
|
|
NetbufFree (Assemble->Packet);
|
|
}
|
|
|
|
FreePool (Assemble);
|
|
}
|
|
|
|
/**
|
|
Release all the fragments of the packet. This is the callback for
|
|
the assembled packet's OnFree. It will free the assemble entry,
|
|
which in turn frees all the fragments of the packet.
|
|
|
|
@param[in] Arg The assemble entry to free.
|
|
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
Ip6OnFreeFragments (
|
|
IN VOID *Arg
|
|
)
|
|
{
|
|
Ip6FreeAssembleEntry ((IP6_ASSEMBLE_ENTRY *) Arg);
|
|
}
|
|
|
|
/**
|
|
Trim the packet to fit in [Start, End), and update per the
|
|
packet information.
|
|
|
|
@param[in, out] Packet Packet to trim.
|
|
@param[in] Start The sequence of the first byte to fit in.
|
|
@param[in] End One beyond the sequence of last byte to fit in.
|
|
|
|
**/
|
|
VOID
|
|
Ip6TrimPacket (
|
|
IN OUT NET_BUF *Packet,
|
|
IN INTN Start,
|
|
IN INTN End
|
|
)
|
|
{
|
|
IP6_CLIP_INFO *Info;
|
|
INTN Len;
|
|
|
|
Info = IP6_GET_CLIP_INFO (Packet);
|
|
|
|
ASSERT (Info->Start + Info->Length == Info->End);
|
|
ASSERT ((Info->Start < End) && (Start < Info->End));
|
|
|
|
if (Info->Start < Start) {
|
|
Len = Start - Info->Start;
|
|
|
|
NetbufTrim (Packet, (UINT32) Len, NET_BUF_HEAD);
|
|
Info->Start = (UINT32) Start;
|
|
Info->Length -= (UINT32) Len;
|
|
}
|
|
|
|
if (End < Info->End) {
|
|
Len = End - Info->End;
|
|
|
|
NetbufTrim (Packet, (UINT32) Len, NET_BUF_TAIL);
|
|
Info->End = (UINT32) End;
|
|
Info->Length -= (UINT32) Len;
|
|
}
|
|
}
|
|
|
|
/**
|
|
Reassemble the IP fragments. If all the fragments of the packet
|
|
have been received, it will wrap the packet in a net buffer then
|
|
return it to caller. If the packet can't be assembled, NULL is
|
|
returned.
|
|
|
|
@param[in, out] Table The assemble table used. A new assemble entry will be created
|
|
if the Packet is from a new chain of fragments.
|
|
@param[in] Packet The fragment to assemble. It might be freed if the fragment
|
|
can't be re-assembled.
|
|
|
|
@return NULL if the packet can't be reassembled. The pointer to the just assembled
|
|
packet if all the fragments of the packet have arrived.
|
|
|
|
**/
|
|
NET_BUF *
|
|
Ip6Reassemble (
|
|
IN OUT IP6_ASSEMBLE_TABLE *Table,
|
|
IN NET_BUF *Packet
|
|
)
|
|
{
|
|
EFI_IP6_HEADER *Head;
|
|
IP6_CLIP_INFO *This;
|
|
IP6_CLIP_INFO *Node;
|
|
IP6_ASSEMBLE_ENTRY *Assemble;
|
|
IP6_ASSEMBLE_ENTRY *Entry;
|
|
LIST_ENTRY *ListHead;
|
|
LIST_ENTRY *Prev;
|
|
LIST_ENTRY *Cur;
|
|
NET_BUF *Fragment;
|
|
NET_BUF *TmpPacket;
|
|
NET_BUF *NewPacket;
|
|
NET_BUF *Duplicate;
|
|
UINT8 *DupHead;
|
|
INTN Index;
|
|
UINT16 UnFragmentLen;
|
|
UINT8 *NextHeader;
|
|
|
|
Head = Packet->Ip.Ip6;
|
|
This = IP6_GET_CLIP_INFO (Packet);
|
|
|
|
ASSERT (Head != NULL);
|
|
|
|
//
|
|
// Find the corresponding assemble entry by (Dst, Src, Id)
|
|
//
|
|
Assemble = NULL;
|
|
Index = IP6_ASSEMBLE_HASH (&Head->DestinationAddress, &Head->SourceAddress, This->Id);
|
|
|
|
NET_LIST_FOR_EACH (Cur, &Table->Bucket[Index]) {
|
|
Entry = NET_LIST_USER_STRUCT (Cur, IP6_ASSEMBLE_ENTRY, Link);
|
|
|
|
if (Entry->Id == This->Id &&
|
|
EFI_IP6_EQUAL (&Entry->Src, &Head->SourceAddress) &&
|
|
EFI_IP6_EQUAL (&Entry->Dst, &Head->DestinationAddress)
|
|
) {
|
|
Assemble = Entry;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Create a new entry if can not find an existing one, insert it to assemble table
|
|
//
|
|
if (Assemble == NULL) {
|
|
Assemble = Ip6CreateAssembleEntry (
|
|
&Head->DestinationAddress,
|
|
&Head->SourceAddress,
|
|
This->Id
|
|
);
|
|
|
|
if (Assemble == NULL) {
|
|
goto Error;
|
|
}
|
|
|
|
InsertHeadList (&Table->Bucket[Index], &Assemble->Link);
|
|
}
|
|
|
|
//
|
|
// Find the point to insert the packet: before the first
|
|
// fragment with THIS.Start < CUR.Start. the previous one
|
|
// has PREV.Start <= THIS.Start < CUR.Start.
|
|
//
|
|
ListHead = &Assemble->Fragments;
|
|
|
|
NET_LIST_FOR_EACH (Cur, ListHead) {
|
|
Fragment = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);
|
|
|
|
if (This->Start < IP6_GET_CLIP_INFO (Fragment)->Start) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check whether the current fragment overlaps with the previous one.
|
|
// It holds that: PREV.Start <= THIS.Start < THIS.End. Only need to
|
|
// check whether THIS.Start < PREV.End for overlap. If two fragments
|
|
// overlaps, trim the overlapped part off THIS fragment.
|
|
//
|
|
if ((Prev = Cur->BackLink) != ListHead) {
|
|
Fragment = NET_LIST_USER_STRUCT (Prev, NET_BUF, List);
|
|
Node = IP6_GET_CLIP_INFO (Fragment);
|
|
|
|
if (This->Start < Node->End) {
|
|
if (This->End <= Node->End) {
|
|
goto Error;
|
|
}
|
|
|
|
//
|
|
// Trim the previous fragment from tail.
|
|
//
|
|
Ip6TrimPacket (Fragment, Node->Start, This->Start);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Insert the fragment into the packet. The fragment may be removed
|
|
// from the list by the following checks.
|
|
//
|
|
NetListInsertBefore (Cur, &Packet->List);
|
|
|
|
//
|
|
// Check the packets after the insert point. It holds that:
|
|
// THIS.Start <= NODE.Start < NODE.End. The equality holds
|
|
// if PREV and NEXT are continuous. THIS fragment may fill
|
|
// several holes. Remove the completely overlapped fragments
|
|
//
|
|
while (Cur != ListHead) {
|
|
Fragment = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);
|
|
Node = IP6_GET_CLIP_INFO (Fragment);
|
|
|
|
//
|
|
// Remove fragments completely overlapped by this fragment
|
|
//
|
|
if (Node->End <= This->End) {
|
|
Cur = Cur->ForwardLink;
|
|
|
|
RemoveEntryList (&Fragment->List);
|
|
Assemble->CurLen -= Node->Length;
|
|
|
|
NetbufFree (Fragment);
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// The conditions are: THIS.Start <= NODE.Start, and THIS.End <
|
|
// NODE.End. Two fragments overlaps if NODE.Start < THIS.End.
|
|
// If two fragments start at the same offset, remove THIS fragment
|
|
// because ((THIS.Start == NODE.Start) && (THIS.End < NODE.End)).
|
|
//
|
|
if (Node->Start < This->End) {
|
|
if (This->Start == Node->Start) {
|
|
RemoveEntryList (&Packet->List);
|
|
goto Error;
|
|
}
|
|
|
|
Ip6TrimPacket (Packet, This->Start, Node->Start);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Update the assemble info: increase the current length. If it is
|
|
// the frist fragment, update the packet's IP head and per packet
|
|
// info. If it is the last fragment, update the total length.
|
|
//
|
|
Assemble->CurLen += This->Length;
|
|
|
|
if (This->Start == 0) {
|
|
//
|
|
// Once the first fragment is enqueued, it can't be removed
|
|
// from the fragment list. So, Assemble->Head always point
|
|
// to valid memory area.
|
|
//
|
|
if ((Assemble->Head != NULL) || (Assemble->Packet != NULL)) {
|
|
goto Error;
|
|
}
|
|
|
|
//
|
|
// Backup the first fragment in case the reasembly of that packet fail.
|
|
//
|
|
Duplicate = NetbufDuplicate (Packet, NULL, sizeof (EFI_IP6_HEADER));
|
|
if (Duplicate == NULL) {
|
|
goto Error;
|
|
}
|
|
|
|
//
|
|
// Revert IP head to network order.
|
|
//
|
|
DupHead = NetbufGetByte (Duplicate, 0, NULL);
|
|
ASSERT (DupHead != NULL);
|
|
Duplicate->Ip.Ip6 = Ip6NtohHead ((EFI_IP6_HEADER *) DupHead);
|
|
Assemble->Packet = Duplicate;
|
|
|
|
//
|
|
// Adjust the unfragmentable part in first fragment
|
|
//
|
|
UnFragmentLen = (UINT16) (This->HeadLen - sizeof (EFI_IP6_HEADER));
|
|
if (UnFragmentLen == 0) {
|
|
//
|
|
// There is not any unfragmentable extension header.
|
|
//
|
|
ASSERT (Head->NextHeader == IP6_FRAGMENT);
|
|
Head->NextHeader = This->NextHeader;
|
|
} else {
|
|
NextHeader = NetbufGetByte (
|
|
Packet,
|
|
This->FormerNextHeader + sizeof (EFI_IP6_HEADER),
|
|
0
|
|
);
|
|
if (NextHeader == NULL) {
|
|
goto Error;
|
|
}
|
|
|
|
*NextHeader = This->NextHeader;
|
|
}
|
|
|
|
Assemble->Head = Head;
|
|
Assemble->Info = IP6_GET_CLIP_INFO (Packet);
|
|
}
|
|
|
|
//
|
|
// Don't update the length more than once.
|
|
//
|
|
if ((This->LastFrag != 0) && (Assemble->TotalLen == 0)) {
|
|
Assemble->TotalLen = This->End;
|
|
}
|
|
|
|
//
|
|
// Deliver the whole packet if all the fragments received.
|
|
// All fragments received if:
|
|
// 1. received the last one, so, the totoal length is know
|
|
// 2. received all the data. If the last fragment on the
|
|
// queue ends at the total length, all data is received.
|
|
//
|
|
if ((Assemble->TotalLen != 0) && (Assemble->CurLen >= Assemble->TotalLen)) {
|
|
|
|
RemoveEntryList (&Assemble->Link);
|
|
|
|
//
|
|
// If the packet is properly formated, the last fragment's End
|
|
// equals to the packet's total length. Otherwise, the packet
|
|
// is a fake, drop it now.
|
|
//
|
|
Fragment = NET_LIST_USER_STRUCT (ListHead->BackLink, NET_BUF, List);
|
|
if (IP6_GET_CLIP_INFO (Fragment)->End != (INTN) Assemble->TotalLen) {
|
|
Ip6FreeAssembleEntry (Assemble);
|
|
goto Error;
|
|
}
|
|
|
|
Fragment = NET_LIST_HEAD (ListHead, NET_BUF, List);
|
|
This = Assemble->Info;
|
|
|
|
//
|
|
// This TmpPacket is used to hold the unfragmentable part, i.e.,
|
|
// the IPv6 header and the unfragmentable extension headers. Be noted that
|
|
// the Fragment Header is exluded.
|
|
//
|
|
TmpPacket = NetbufGetFragment (Fragment, 0, This->HeadLen, 0);
|
|
ASSERT (TmpPacket != NULL);
|
|
|
|
NET_LIST_FOR_EACH (Cur, ListHead) {
|
|
//
|
|
// Trim off the unfragment part plus the fragment header from all fragments.
|
|
//
|
|
Fragment = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);
|
|
NetbufTrim (Fragment, This->HeadLen + sizeof (IP6_FRAGMENT_HEADER), TRUE);
|
|
}
|
|
|
|
InsertHeadList (ListHead, &TmpPacket->List);
|
|
|
|
//
|
|
// Wrap the packet in a net buffer then deliver it up
|
|
//
|
|
NewPacket = NetbufFromBufList (
|
|
&Assemble->Fragments,
|
|
0,
|
|
0,
|
|
Ip6OnFreeFragments,
|
|
Assemble
|
|
);
|
|
|
|
if (NewPacket == NULL) {
|
|
Ip6FreeAssembleEntry (Assemble);
|
|
goto Error;
|
|
}
|
|
|
|
NewPacket->Ip.Ip6 = Assemble->Head;
|
|
|
|
CopyMem (IP6_GET_CLIP_INFO (NewPacket), Assemble->Info, sizeof (IP6_CLIP_INFO));
|
|
|
|
return NewPacket;
|
|
}
|
|
|
|
return NULL;
|
|
|
|
Error:
|
|
NetbufFree (Packet);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/**
|
|
The callback function for the net buffer that wraps the packet processed by
|
|
IPsec. It releases the wrap packet and also signals IPsec to free the resources.
|
|
|
|
@param[in] Arg The wrap context.
|
|
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
Ip6IpSecFree (
|
|
IN VOID *Arg
|
|
)
|
|
{
|
|
IP6_IPSEC_WRAP *Wrap;
|
|
|
|
Wrap = (IP6_IPSEC_WRAP *) Arg;
|
|
|
|
if (Wrap->IpSecRecycleSignal != NULL) {
|
|
gBS->SignalEvent (Wrap->IpSecRecycleSignal);
|
|
}
|
|
|
|
NetbufFree (Wrap->Packet);
|
|
|
|
FreePool (Wrap);
|
|
|
|
return;
|
|
}
|
|
|
|
/**
|
|
The work function to locate the IPsec protocol to process the inbound or
|
|
outbound IP packets. The process routine handles the packet with the following
|
|
actions: bypass the packet, discard the packet, or protect the packet.
|
|
|
|
@param[in] IpSb The IP6 service instance.
|
|
@param[in, out] Head The caller-supplied IP6 header.
|
|
@param[in, out] LastHead The next header field of last IP header.
|
|
@param[in, out] Netbuf The IP6 packet to be processed by IPsec.
|
|
@param[in, out] ExtHdrs The caller-supplied options.
|
|
@param[in, out] ExtHdrsLen The length of the option.
|
|
@param[in] Direction The directionality in an SPD entry,
|
|
EfiIPsecInBound, or EfiIPsecOutBound.
|
|
@param[in] Context The token's wrap.
|
|
|
|
@retval EFI_SUCCESS The IPsec protocol is not available or disabled.
|
|
@retval EFI_SUCCESS The packet was bypassed, and all buffers remain the same.
|
|
@retval EFI_SUCCESS The packet was protected.
|
|
@retval EFI_ACCESS_DENIED The packet was discarded.
|
|
@retval EFI_OUT_OF_RESOURCES There are not suffcient resources to complete the operation.
|
|
@retval EFI_BUFFER_TOO_SMALL The number of non-empty blocks is bigger than the
|
|
number of input data blocks when building a fragment table.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
Ip6IpSecProcessPacket (
|
|
IN IP6_SERVICE *IpSb,
|
|
IN OUT EFI_IP6_HEADER **Head,
|
|
IN OUT UINT8 *LastHead,
|
|
IN OUT NET_BUF **Netbuf,
|
|
IN OUT UINT8 **ExtHdrs,
|
|
IN OUT UINT32 *ExtHdrsLen,
|
|
IN EFI_IPSEC_TRAFFIC_DIR Direction,
|
|
IN VOID *Context
|
|
)
|
|
{
|
|
NET_FRAGMENT *FragmentTable;
|
|
NET_FRAGMENT *OriginalFragmentTable;
|
|
UINT32 FragmentCount;
|
|
UINT32 OriginalFragmentCount;
|
|
EFI_EVENT RecycleEvent;
|
|
NET_BUF *Packet;
|
|
IP6_TXTOKEN_WRAP *TxWrap;
|
|
IP6_IPSEC_WRAP *IpSecWrap;
|
|
EFI_STATUS Status;
|
|
EFI_IP6_HEADER *PacketHead;
|
|
UINT8 *Buf;
|
|
EFI_IP6_HEADER ZeroHead;
|
|
|
|
Status = EFI_SUCCESS;
|
|
|
|
if (!mIpSec2Installed) {
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
Packet = *Netbuf;
|
|
RecycleEvent = NULL;
|
|
IpSecWrap = NULL;
|
|
FragmentTable = NULL;
|
|
PacketHead = NULL;
|
|
Buf = NULL;
|
|
TxWrap = (IP6_TXTOKEN_WRAP *) Context;
|
|
FragmentCount = Packet->BlockOpNum;
|
|
ZeroMem (&ZeroHead, sizeof (EFI_IP6_HEADER));
|
|
|
|
if (mIpSec == NULL) {
|
|
gBS->LocateProtocol (&gEfiIpSec2ProtocolGuid, NULL, (VOID **) &mIpSec);
|
|
|
|
//
|
|
// Check whether the ipsec protocol is available.
|
|
//
|
|
if (mIpSec == NULL) {
|
|
goto ON_EXIT;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check whether the ipsec enable variable is set.
|
|
//
|
|
if (mIpSec->DisabledFlag) {
|
|
//
|
|
// If IPsec is disabled, restore the original MTU
|
|
//
|
|
IpSb->MaxPacketSize = IpSb->OldMaxPacketSize;
|
|
goto ON_EXIT;
|
|
} else {
|
|
//
|
|
// If IPsec is enabled, use the MTU which reduce the IPsec header length.
|
|
//
|
|
IpSb->MaxPacketSize = IpSb->OldMaxPacketSize - IP6_MAX_IPSEC_HEADLEN;
|
|
}
|
|
|
|
|
|
//
|
|
// Bypass all multicast inbound or outbound traffic.
|
|
//
|
|
if (IP6_IS_MULTICAST (&(*Head)->DestinationAddress) || IP6_IS_MULTICAST (&(*Head)->SourceAddress)) {
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
//
|
|
// Rebuild fragment table from netbuf to ease ipsec process.
|
|
//
|
|
FragmentTable = AllocateZeroPool (FragmentCount * sizeof (NET_FRAGMENT));
|
|
|
|
if (FragmentTable == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
Status = NetbufBuildExt (Packet, FragmentTable, &FragmentCount);
|
|
OriginalFragmentTable = FragmentTable;
|
|
OriginalFragmentCount = FragmentCount;
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
FreePool (FragmentTable);
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
//
|
|
// Convert host byte order to network byte order
|
|
//
|
|
Ip6NtohHead (*Head);
|
|
|
|
Status = mIpSec->ProcessExt (
|
|
mIpSec,
|
|
IpSb->Controller,
|
|
IP_VERSION_6,
|
|
(VOID *) (*Head),
|
|
LastHead,
|
|
(VOID **) ExtHdrs,
|
|
ExtHdrsLen,
|
|
(EFI_IPSEC_FRAGMENT_DATA **) (&FragmentTable),
|
|
&FragmentCount,
|
|
Direction,
|
|
&RecycleEvent
|
|
);
|
|
//
|
|
// Convert back to host byte order
|
|
//
|
|
Ip6NtohHead (*Head);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
FreePool (OriginalFragmentTable);
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
if (OriginalFragmentCount == FragmentCount && OriginalFragmentTable == FragmentTable) {
|
|
//
|
|
// For ByPass Packet
|
|
//
|
|
FreePool (FragmentTable);
|
|
goto ON_EXIT;
|
|
} else {
|
|
//
|
|
// Free the FragmentTable which allocated before calling the IPsec.
|
|
//
|
|
FreePool (OriginalFragmentTable);
|
|
}
|
|
|
|
if (Direction == EfiIPsecOutBound && TxWrap != NULL) {
|
|
TxWrap->IpSecRecycleSignal = RecycleEvent;
|
|
TxWrap->Packet = NetbufFromExt (
|
|
FragmentTable,
|
|
FragmentCount,
|
|
IP6_MAX_HEADLEN,
|
|
0,
|
|
Ip6FreeTxToken,
|
|
TxWrap
|
|
);
|
|
if (TxWrap->Packet == NULL) {
|
|
TxWrap->Packet = *Netbuf;
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
CopyMem (
|
|
IP6_GET_CLIP_INFO (TxWrap->Packet),
|
|
IP6_GET_CLIP_INFO (Packet),
|
|
sizeof (IP6_CLIP_INFO)
|
|
);
|
|
|
|
NetIpSecNetbufFree(Packet);
|
|
*Netbuf = TxWrap->Packet;
|
|
|
|
} else {
|
|
|
|
IpSecWrap = AllocateZeroPool (sizeof (IP6_IPSEC_WRAP));
|
|
|
|
if (IpSecWrap == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
gBS->SignalEvent (RecycleEvent);
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
IpSecWrap->IpSecRecycleSignal = RecycleEvent;
|
|
IpSecWrap->Packet = Packet;
|
|
Packet = NetbufFromExt (
|
|
FragmentTable,
|
|
FragmentCount,
|
|
IP6_MAX_HEADLEN,
|
|
0,
|
|
Ip6IpSecFree,
|
|
IpSecWrap
|
|
);
|
|
|
|
if (Packet == NULL) {
|
|
Packet = IpSecWrap->Packet;
|
|
gBS->SignalEvent (RecycleEvent);
|
|
FreePool (IpSecWrap);
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
if (Direction == EfiIPsecInBound && 0 != CompareMem (&ZeroHead, *Head, sizeof (EFI_IP6_HEADER))) {
|
|
|
|
PacketHead = (EFI_IP6_HEADER *) NetbufAllocSpace (
|
|
Packet,
|
|
sizeof (EFI_IP6_HEADER) + *ExtHdrsLen,
|
|
NET_BUF_HEAD
|
|
);
|
|
if (PacketHead == NULL) {
|
|
*Netbuf = Packet;
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
CopyMem (PacketHead, *Head, sizeof (EFI_IP6_HEADER));
|
|
*Head = PacketHead;
|
|
Packet->Ip.Ip6 = PacketHead;
|
|
|
|
if (*ExtHdrs != NULL) {
|
|
Buf = (UINT8 *) (PacketHead + 1);
|
|
CopyMem (Buf, *ExtHdrs, *ExtHdrsLen);
|
|
}
|
|
|
|
NetbufTrim (Packet, sizeof (EFI_IP6_HEADER) + *ExtHdrsLen, TRUE);
|
|
CopyMem (
|
|
IP6_GET_CLIP_INFO (Packet),
|
|
IP6_GET_CLIP_INFO (IpSecWrap->Packet),
|
|
sizeof (IP6_CLIP_INFO)
|
|
);
|
|
}
|
|
*Netbuf = Packet;
|
|
}
|
|
|
|
ON_EXIT:
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Pre-process the IPv6 packet. First validates the IPv6 packet, and
|
|
then reassembles packet if it is necessary.
|
|
|
|
@param[in] IpSb The IP6 service instance.
|
|
@param[in, out] Packet The received IP6 packet to be processed.
|
|
@param[in] Flag The link layer flag for the packet received, such
|
|
as multicast.
|
|
@param[out] Payload The pointer to the payload of the recieved packet.
|
|
it starts from the first byte of the extension header.
|
|
@param[out] LastHead The pointer of NextHeader of the last extension
|
|
header processed by IP6.
|
|
@param[out] ExtHdrsLen The length of the whole option.
|
|
@param[out] UnFragmentLen The length of unfragmented length of extension headers.
|
|
@param[out] Fragmented Indicate whether the packet is fragmented.
|
|
@param[out] Head The pointer to the EFI_IP6_Header.
|
|
|
|
@retval EFI_SUCCESS The received packet is well format.
|
|
@retval EFI_INVALID_PARAMETER The received packet is malformed.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
Ip6PreProcessPacket (
|
|
IN IP6_SERVICE *IpSb,
|
|
IN OUT NET_BUF **Packet,
|
|
IN UINT32 Flag,
|
|
OUT UINT8 **Payload,
|
|
OUT UINT8 **LastHead,
|
|
OUT UINT32 *ExtHdrsLen,
|
|
OUT UINT32 *UnFragmentLen,
|
|
OUT BOOLEAN *Fragmented,
|
|
OUT EFI_IP6_HEADER **Head
|
|
)
|
|
{
|
|
UINT16 PayloadLen;
|
|
UINT16 TotalLen;
|
|
UINT32 FormerHeadOffset;
|
|
UINT32 HeadLen;
|
|
IP6_FRAGMENT_HEADER *FragmentHead;
|
|
UINT16 FragmentOffset;
|
|
IP6_CLIP_INFO *Info;
|
|
EFI_IPv6_ADDRESS Loopback;
|
|
|
|
HeadLen = 0;
|
|
PayloadLen = 0;
|
|
//
|
|
// Check whether the input packet is a valid packet
|
|
//
|
|
if ((*Packet)->TotalSize < IP6_MIN_HEADLEN) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Get header information of the packet.
|
|
//
|
|
*Head = (EFI_IP6_HEADER *) NetbufGetByte (*Packet, 0, NULL);
|
|
if (*Head == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Multicast addresses must not be used as source addresses in IPv6 packets.
|
|
//
|
|
if (((*Head)->Version != 6) || (IP6_IS_MULTICAST (&(*Head)->SourceAddress))) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// A packet with a destination address of loopback ::1/128 or unspecified must be dropped.
|
|
//
|
|
ZeroMem (&Loopback, sizeof (EFI_IPv6_ADDRESS));
|
|
Loopback.Addr[15] = 0x1;
|
|
if ((CompareMem (&Loopback, &(*Head)->DestinationAddress, sizeof (EFI_IPv6_ADDRESS)) == 0) ||
|
|
(NetIp6IsUnspecifiedAddr (&(*Head)->DestinationAddress))) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Convert the IP header to host byte order.
|
|
//
|
|
(*Packet)->Ip.Ip6 = Ip6NtohHead (*Head);
|
|
|
|
//
|
|
// Get the per packet info.
|
|
//
|
|
Info = IP6_GET_CLIP_INFO (*Packet);
|
|
Info->LinkFlag = Flag;
|
|
Info->CastType = 0;
|
|
|
|
if (IpSb->MnpConfigData.EnablePromiscuousReceive) {
|
|
Info->CastType = Ip6Promiscuous;
|
|
}
|
|
|
|
if (Ip6IsOneOfSetAddress (IpSb, &(*Head)->DestinationAddress, NULL, NULL)) {
|
|
Info->CastType = Ip6Unicast;
|
|
} else if (IP6_IS_MULTICAST (&(*Head)->DestinationAddress)) {
|
|
if (Ip6FindMldEntry (IpSb, &(*Head)->DestinationAddress) != NULL) {
|
|
Info->CastType = Ip6Multicast;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Drop the packet that is not delivered to us.
|
|
//
|
|
if (Info->CastType == 0) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
|
|
PayloadLen = (*Head)->PayloadLength;
|
|
|
|
Info->Start = 0;
|
|
Info->Length = PayloadLen;
|
|
Info->End = Info->Start + Info->Length;
|
|
Info->HeadLen = (UINT16) sizeof (EFI_IP6_HEADER);
|
|
Info->Status = EFI_SUCCESS;
|
|
Info->LastFrag = FALSE;
|
|
|
|
TotalLen = (UINT16) (PayloadLen + sizeof (EFI_IP6_HEADER));
|
|
|
|
//
|
|
// Mnp may deliver frame trailer sequence up, trim it off.
|
|
//
|
|
if (TotalLen < (*Packet)->TotalSize) {
|
|
NetbufTrim (*Packet, (*Packet)->TotalSize - TotalLen, FALSE);
|
|
}
|
|
|
|
if (TotalLen != (*Packet)->TotalSize) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Check the extension headers, if exist validate them
|
|
//
|
|
if (PayloadLen != 0) {
|
|
*Payload = AllocatePool ((UINTN) PayloadLen);
|
|
if (*Payload == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
NetbufCopy (*Packet, sizeof (EFI_IP6_HEADER), PayloadLen, *Payload);
|
|
}
|
|
|
|
if (!Ip6IsExtsValid (
|
|
IpSb,
|
|
*Packet,
|
|
&(*Head)->NextHeader,
|
|
*Payload,
|
|
(UINT32) PayloadLen,
|
|
TRUE,
|
|
&FormerHeadOffset,
|
|
LastHead,
|
|
ExtHdrsLen,
|
|
UnFragmentLen,
|
|
Fragmented
|
|
)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
HeadLen = sizeof (EFI_IP6_HEADER) + *UnFragmentLen;
|
|
|
|
if (*Fragmented) {
|
|
//
|
|
// Get the fragment offset from the Fragment header
|
|
//
|
|
FragmentHead = (IP6_FRAGMENT_HEADER *) NetbufGetByte (*Packet, HeadLen, NULL);
|
|
if (FragmentHead == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
FragmentOffset = NTOHS (FragmentHead->FragmentOffset);
|
|
|
|
if ((FragmentOffset & 0x1) == 0) {
|
|
Info->LastFrag = TRUE;
|
|
}
|
|
|
|
FragmentOffset &= (~0x1);
|
|
|
|
//
|
|
// This is the first fragment of the packet
|
|
//
|
|
if (FragmentOffset == 0) {
|
|
Info->NextHeader = FragmentHead->NextHeader;
|
|
}
|
|
|
|
Info->HeadLen = (UINT16) HeadLen;
|
|
HeadLen += sizeof (IP6_FRAGMENT_HEADER);
|
|
Info->Start = FragmentOffset;
|
|
Info->Length = TotalLen - (UINT16) HeadLen;
|
|
Info->End = Info->Start + Info->Length;
|
|
Info->Id = FragmentHead->Identification;
|
|
Info->FormerNextHeader = FormerHeadOffset;
|
|
|
|
//
|
|
// Fragments should in the unit of 8 octets long except the last one.
|
|
//
|
|
if ((Info->LastFrag == 0) && (Info->Length % 8 != 0)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Reassemble the packet.
|
|
//
|
|
*Packet = Ip6Reassemble (&IpSb->Assemble, *Packet);
|
|
if (*Packet == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Re-check the assembled packet to get the right values.
|
|
//
|
|
*Head = (*Packet)->Ip.Ip6;
|
|
PayloadLen = (*Head)->PayloadLength;
|
|
if (PayloadLen != 0) {
|
|
if (*Payload != NULL) {
|
|
FreePool (*Payload);
|
|
}
|
|
|
|
*Payload = AllocatePool ((UINTN) PayloadLen);
|
|
if (*Payload == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
NetbufCopy (*Packet, sizeof (EFI_IP6_HEADER), PayloadLen, *Payload);
|
|
}
|
|
|
|
if (!Ip6IsExtsValid (
|
|
IpSb,
|
|
*Packet,
|
|
&(*Head)->NextHeader,
|
|
*Payload,
|
|
(UINT32) PayloadLen,
|
|
TRUE,
|
|
NULL,
|
|
LastHead,
|
|
ExtHdrsLen,
|
|
UnFragmentLen,
|
|
Fragmented
|
|
)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Trim the head off, after this point, the packet is headless.
|
|
// and Packet->TotalLen == Info->Length.
|
|
//
|
|
NetbufTrim (*Packet, sizeof (EFI_IP6_HEADER) + *ExtHdrsLen, TRUE);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
The IP6 input routine. It is called by the IP6_INTERFACE when an
|
|
IP6 fragment is received from MNP.
|
|
|
|
@param[in] Packet The IP6 packet received.
|
|
@param[in] IoStatus The return status of receive request.
|
|
@param[in] Flag The link layer flag for the packet received, such
|
|
as multicast.
|
|
@param[in] Context The IP6 service instance that owns the MNP.
|
|
|
|
**/
|
|
VOID
|
|
Ip6AcceptFrame (
|
|
IN NET_BUF *Packet,
|
|
IN EFI_STATUS IoStatus,
|
|
IN UINT32 Flag,
|
|
IN VOID *Context
|
|
)
|
|
{
|
|
IP6_SERVICE *IpSb;
|
|
EFI_IP6_HEADER *Head;
|
|
UINT8 *Payload;
|
|
UINT8 *LastHead;
|
|
UINT32 UnFragmentLen;
|
|
UINT32 ExtHdrsLen;
|
|
BOOLEAN Fragmented;
|
|
EFI_STATUS Status;
|
|
EFI_IP6_HEADER ZeroHead;
|
|
|
|
IpSb = (IP6_SERVICE *) Context;
|
|
NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
|
|
|
|
Payload = NULL;
|
|
LastHead = NULL;
|
|
|
|
//
|
|
// Check input parameters
|
|
//
|
|
if (EFI_ERROR (IoStatus) || (IpSb->State == IP6_SERVICE_DESTROY)) {
|
|
goto Drop;
|
|
}
|
|
|
|
//
|
|
// Pre-Process the Ipv6 Packet and then reassemble if it is necessary.
|
|
//
|
|
Status = Ip6PreProcessPacket (
|
|
IpSb,
|
|
&Packet,
|
|
Flag,
|
|
&Payload,
|
|
&LastHead,
|
|
&ExtHdrsLen,
|
|
&UnFragmentLen,
|
|
&Fragmented,
|
|
&Head
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Restart;
|
|
}
|
|
//
|
|
// After trim off, the packet is a esp/ah/udp/tcp/icmp6 net buffer,
|
|
// and no need consider any other ahead ext headers.
|
|
//
|
|
Status = Ip6IpSecProcessPacket (
|
|
IpSb,
|
|
&Head,
|
|
LastHead, // need get the lasthead value for input
|
|
&Packet,
|
|
&Payload,
|
|
&ExtHdrsLen,
|
|
EfiIPsecInBound,
|
|
NULL
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
goto Restart;
|
|
}
|
|
|
|
//
|
|
// If the packet is protected by IPsec Tunnel Mode, Check the Inner Ip Packet.
|
|
//
|
|
ZeroMem (&ZeroHead, sizeof (EFI_IP6_HEADER));
|
|
if (0 == CompareMem (Head, &ZeroHead, sizeof (EFI_IP6_HEADER))) {
|
|
Status = Ip6PreProcessPacket (
|
|
IpSb,
|
|
&Packet,
|
|
Flag,
|
|
&Payload,
|
|
&LastHead,
|
|
&ExtHdrsLen,
|
|
&UnFragmentLen,
|
|
&Fragmented,
|
|
&Head
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Restart;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check the Packet again.
|
|
//
|
|
if (Packet == NULL) {
|
|
goto Restart;
|
|
}
|
|
|
|
//
|
|
// Packet may have been changed. The ownership of the packet
|
|
// is transfered to the packet process logic.
|
|
//
|
|
Head = Packet->Ip.Ip6;
|
|
IP6_GET_CLIP_INFO (Packet)->Status = EFI_SUCCESS;
|
|
|
|
switch (*LastHead) {
|
|
case IP6_ICMP:
|
|
Ip6IcmpHandle (IpSb, Head, Packet);
|
|
break;
|
|
default:
|
|
Ip6Demultiplex (IpSb, Head, Packet);
|
|
}
|
|
|
|
Packet = NULL;
|
|
|
|
//
|
|
// Dispatch the DPCs queued by the NotifyFunction of the rx token's events
|
|
// which are signaled with received data.
|
|
//
|
|
DispatchDpc ();
|
|
|
|
Restart:
|
|
if (Payload != NULL) {
|
|
FreePool (Payload);
|
|
}
|
|
|
|
Ip6ReceiveFrame (Ip6AcceptFrame, IpSb);
|
|
|
|
Drop:
|
|
if (Packet != NULL) {
|
|
NetbufFree (Packet);
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
/**
|
|
Initialize an already allocated assemble table. This is generally
|
|
the assemble table embedded in the IP6 service instance.
|
|
|
|
@param[in, out] Table The assemble table to initialize.
|
|
|
|
**/
|
|
VOID
|
|
Ip6CreateAssembleTable (
|
|
IN OUT IP6_ASSEMBLE_TABLE *Table
|
|
)
|
|
{
|
|
UINT32 Index;
|
|
|
|
for (Index = 0; Index < IP6_ASSEMLE_HASH_SIZE; Index++) {
|
|
InitializeListHead (&Table->Bucket[Index]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
Clean up the assemble table by removing all of the fragments
|
|
and assemble entries.
|
|
|
|
@param[in, out] Table The assemble table to clean up.
|
|
|
|
**/
|
|
VOID
|
|
Ip6CleanAssembleTable (
|
|
IN OUT IP6_ASSEMBLE_TABLE *Table
|
|
)
|
|
{
|
|
LIST_ENTRY *Entry;
|
|
LIST_ENTRY *Next;
|
|
IP6_ASSEMBLE_ENTRY *Assemble;
|
|
UINT32 Index;
|
|
|
|
for (Index = 0; Index < IP6_ASSEMLE_HASH_SIZE; Index++) {
|
|
NET_LIST_FOR_EACH_SAFE (Entry, Next, &Table->Bucket[Index]) {
|
|
Assemble = NET_LIST_USER_STRUCT (Entry, IP6_ASSEMBLE_ENTRY, Link);
|
|
|
|
RemoveEntryList (Entry);
|
|
Ip6FreeAssembleEntry (Assemble);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
The signal handle of IP6's recycle event. It is called back
|
|
when the upper layer releases the packet.
|
|
|
|
@param[in] Event The IP6's recycle event.
|
|
@param[in] Context The context of the handle, which is a IP6_RXDATA_WRAP.
|
|
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
Ip6OnRecyclePacket (
|
|
IN EFI_EVENT Event,
|
|
IN VOID *Context
|
|
)
|
|
{
|
|
IP6_RXDATA_WRAP *Wrap;
|
|
|
|
Wrap = (IP6_RXDATA_WRAP *) Context;
|
|
|
|
EfiAcquireLockOrFail (&Wrap->IpInstance->RecycleLock);
|
|
RemoveEntryList (&Wrap->Link);
|
|
EfiReleaseLock (&Wrap->IpInstance->RecycleLock);
|
|
|
|
ASSERT (!NET_BUF_SHARED (Wrap->Packet));
|
|
NetbufFree (Wrap->Packet);
|
|
|
|
gBS->CloseEvent (Wrap->RxData.RecycleSignal);
|
|
FreePool (Wrap);
|
|
}
|
|
|
|
/**
|
|
Wrap the received packet to a IP6_RXDATA_WRAP, which will be
|
|
delivered to the upper layer. Each IP6 child that accepts the
|
|
packet will get a not-shared copy of the packet which is wrapped
|
|
in the IP6_RXDATA_WRAP. The IP6_RXDATA_WRAP->RxData is passed
|
|
to the upper layer. The upper layer will signal the recycle event in
|
|
it when it is done with the packet.
|
|
|
|
@param[in] IpInstance The IP6 child to receive the packet.
|
|
@param[in] Packet The packet to deliver up.
|
|
|
|
@return NULL if it failed to wrap the packet; otherwise, the wrapper.
|
|
|
|
**/
|
|
IP6_RXDATA_WRAP *
|
|
Ip6WrapRxData (
|
|
IN IP6_PROTOCOL *IpInstance,
|
|
IN NET_BUF *Packet
|
|
)
|
|
{
|
|
IP6_RXDATA_WRAP *Wrap;
|
|
EFI_IP6_RECEIVE_DATA *RxData;
|
|
EFI_STATUS Status;
|
|
|
|
Wrap = AllocatePool (IP6_RXDATA_WRAP_SIZE (Packet->BlockOpNum));
|
|
|
|
if (Wrap == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
InitializeListHead (&Wrap->Link);
|
|
|
|
Wrap->IpInstance = IpInstance;
|
|
Wrap->Packet = Packet;
|
|
RxData = &Wrap->RxData;
|
|
|
|
ZeroMem (&RxData->TimeStamp, sizeof (EFI_TIME));
|
|
|
|
Status = gBS->CreateEvent (
|
|
EVT_NOTIFY_SIGNAL,
|
|
TPL_NOTIFY,
|
|
Ip6OnRecyclePacket,
|
|
Wrap,
|
|
&RxData->RecycleSignal
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
FreePool (Wrap);
|
|
return NULL;
|
|
}
|
|
|
|
ASSERT (Packet->Ip.Ip6 != NULL);
|
|
|
|
//
|
|
// The application expects a network byte order header.
|
|
//
|
|
RxData->HeaderLength = sizeof (EFI_IP6_HEADER);
|
|
RxData->Header = (EFI_IP6_HEADER *) Ip6NtohHead (Packet->Ip.Ip6);
|
|
RxData->DataLength = Packet->TotalSize;
|
|
|
|
//
|
|
// Build the fragment table to be delivered up.
|
|
//
|
|
RxData->FragmentCount = Packet->BlockOpNum;
|
|
NetbufBuildExt (Packet, (NET_FRAGMENT *) RxData->FragmentTable, &RxData->FragmentCount);
|
|
|
|
return Wrap;
|
|
}
|
|
|
|
/**
|
|
Check whether this IP child accepts the packet.
|
|
|
|
@param[in] IpInstance The IP child to check.
|
|
@param[in] Head The IP header of the packet.
|
|
@param[in] Packet The data of the packet.
|
|
|
|
@retval TRUE The child wants to receive the packet.
|
|
@retval FALSE The child does not want to receive the packet.
|
|
|
|
**/
|
|
BOOLEAN
|
|
Ip6InstanceFrameAcceptable (
|
|
IN IP6_PROTOCOL *IpInstance,
|
|
IN EFI_IP6_HEADER *Head,
|
|
IN NET_BUF *Packet
|
|
)
|
|
{
|
|
IP6_ICMP_ERROR_HEAD Icmp;
|
|
EFI_IP6_CONFIG_DATA *Config;
|
|
IP6_CLIP_INFO *Info;
|
|
UINT8 *Proto;
|
|
UINT32 Index;
|
|
UINT8 *ExtHdrs;
|
|
UINT16 ErrMsgPayloadLen;
|
|
UINT8 *ErrMsgPayload;
|
|
|
|
Config = &IpInstance->ConfigData;
|
|
Proto = NULL;
|
|
|
|
//
|
|
// Dirty trick for the Tiano UEFI network stack implmentation. If
|
|
// ReceiveTimeout == -1, the receive of the packet for this instance
|
|
// is disabled. The UEFI spec don't have such captibility. We add
|
|
// this to improve the performance because IP will make a copy of
|
|
// the received packet for each accepting instance. Some IP instances
|
|
// used by UDP/TCP only send packets, they don't wants to receive.
|
|
//
|
|
if (Config->ReceiveTimeout == (UINT32)(-1)) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (Config->AcceptPromiscuous) {
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Check whether the protocol is acceptable.
|
|
//
|
|
ExtHdrs = NetbufGetByte (Packet, 0, NULL);
|
|
|
|
if (!Ip6IsExtsValid (
|
|
IpInstance->Service,
|
|
Packet,
|
|
&Head->NextHeader,
|
|
ExtHdrs,
|
|
(UINT32) Head->PayloadLength,
|
|
TRUE,
|
|
NULL,
|
|
&Proto,
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
)) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// The upper layer driver may want to receive the ICMPv6 error packet
|
|
// invoked by its packet, like UDP.
|
|
//
|
|
if ((*Proto == IP6_ICMP) && (!Config->AcceptAnyProtocol) && (*Proto != Config->DefaultProtocol)) {
|
|
NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);
|
|
|
|
if (Icmp.Head.Type <= ICMP_V6_ERROR_MAX) {
|
|
if (!Config->AcceptIcmpErrors) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Get the protocol of the invoking packet of ICMPv6 error packet.
|
|
//
|
|
ErrMsgPayloadLen = NTOHS (Icmp.IpHead.PayloadLength);
|
|
ErrMsgPayload = NetbufGetByte (Packet, sizeof (Icmp), NULL);
|
|
|
|
if (!Ip6IsExtsValid (
|
|
NULL,
|
|
NULL,
|
|
&Icmp.IpHead.NextHeader,
|
|
ErrMsgPayload,
|
|
ErrMsgPayloadLen,
|
|
TRUE,
|
|
NULL,
|
|
&Proto,
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
)) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Match the protocol
|
|
//
|
|
if (!Config->AcceptAnyProtocol && (*Proto != Config->DefaultProtocol)) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Check for broadcast, the caller has computed the packet's
|
|
// cast type for this child's interface.
|
|
//
|
|
Info = IP6_GET_CLIP_INFO (Packet);
|
|
|
|
//
|
|
// If it is a multicast packet, check whether we are in the group.
|
|
//
|
|
if (Info->CastType == Ip6Multicast) {
|
|
//
|
|
// Receive the multicast if the instance wants to receive all packets.
|
|
//
|
|
if (NetIp6IsUnspecifiedAddr (&IpInstance->ConfigData.StationAddress)) {
|
|
return TRUE;
|
|
}
|
|
|
|
for (Index = 0; Index < IpInstance->GroupCount; Index++) {
|
|
if (EFI_IP6_EQUAL (IpInstance->GroupList + Index, &Head->DestinationAddress)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return (BOOLEAN)(Index < IpInstance->GroupCount);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
Enqueue a shared copy of the packet to the IP6 child if the
|
|
packet is acceptable to it. Here the data of the packet is
|
|
shared, but the net buffer isn't.
|
|
|
|
@param IpInstance The IP6 child to enqueue the packet to.
|
|
@param Head The IP header of the received packet.
|
|
@param Packet The data of the received packet.
|
|
|
|
@retval EFI_NOT_STARTED The IP child hasn't been configured.
|
|
@retval EFI_INVALID_PARAMETER The child doesn't want to receive the packet.
|
|
@retval EFI_OUT_OF_RESOURCES Failed to allocate some resources
|
|
@retval EFI_SUCCESS A shared copy the packet is enqueued to the child.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
Ip6InstanceEnquePacket (
|
|
IN IP6_PROTOCOL *IpInstance,
|
|
IN EFI_IP6_HEADER *Head,
|
|
IN NET_BUF *Packet
|
|
)
|
|
{
|
|
IP6_CLIP_INFO *Info;
|
|
NET_BUF *Clone;
|
|
|
|
//
|
|
// Check whether the packet is acceptable to this instance.
|
|
//
|
|
if (IpInstance->State != IP6_STATE_CONFIGED) {
|
|
return EFI_NOT_STARTED;
|
|
}
|
|
|
|
if (!Ip6InstanceFrameAcceptable (IpInstance, Head, Packet)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Enque a shared copy of the packet.
|
|
//
|
|
Clone = NetbufClone (Packet);
|
|
|
|
if (Clone == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Set the receive time out for the assembled packet. If it expires,
|
|
// packet will be removed from the queue.
|
|
//
|
|
Info = IP6_GET_CLIP_INFO (Clone);
|
|
Info->Life = IP6_US_TO_SEC (IpInstance->ConfigData.ReceiveTimeout);
|
|
|
|
InsertTailList (&IpInstance->Received, &Clone->List);
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Deliver the received packets to the upper layer if there are both received
|
|
requests and enqueued packets. If the enqueued packet is shared, it will
|
|
duplicate it to a non-shared packet, release the shared packet, then
|
|
deliver the non-shared packet up.
|
|
|
|
@param[in] IpInstance The IP child to deliver the packet up.
|
|
|
|
@retval EFI_OUT_OF_RESOURCES Failed to allocate resources to deliver the
|
|
packets.
|
|
@retval EFI_SUCCESS All the enqueued packets that can be delivered
|
|
are delivered up.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
Ip6InstanceDeliverPacket (
|
|
IN IP6_PROTOCOL *IpInstance
|
|
)
|
|
{
|
|
EFI_IP6_COMPLETION_TOKEN *Token;
|
|
IP6_RXDATA_WRAP *Wrap;
|
|
NET_BUF *Packet;
|
|
NET_BUF *Dup;
|
|
UINT8 *Head;
|
|
|
|
//
|
|
// Deliver a packet if there are both a packet and a receive token.
|
|
//
|
|
while (!IsListEmpty (&IpInstance->Received) && !NetMapIsEmpty (&IpInstance->RxTokens)) {
|
|
|
|
Packet = NET_LIST_HEAD (&IpInstance->Received, NET_BUF, List);
|
|
|
|
if (!NET_BUF_SHARED (Packet)) {
|
|
//
|
|
// If this is the only instance that wants the packet, wrap it up.
|
|
//
|
|
Wrap = Ip6WrapRxData (IpInstance, Packet);
|
|
|
|
if (Wrap == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
RemoveEntryList (&Packet->List);
|
|
|
|
} else {
|
|
//
|
|
// Create a duplicated packet if this packet is shared
|
|
//
|
|
Dup = NetbufDuplicate (Packet, NULL, sizeof (EFI_IP6_HEADER));
|
|
|
|
if (Dup == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Copy the IP head over. The packet to deliver up is
|
|
// headless. Trim the head off after copy. The IP head
|
|
// may be not continuous before the data.
|
|
//
|
|
Head = NetbufAllocSpace (Dup, sizeof (EFI_IP6_HEADER), NET_BUF_HEAD);
|
|
ASSERT (Head != NULL);
|
|
Dup->Ip.Ip6 = (EFI_IP6_HEADER *) Head;
|
|
|
|
CopyMem (Head, Packet->Ip.Ip6, sizeof (EFI_IP6_HEADER));
|
|
NetbufTrim (Dup, sizeof (EFI_IP6_HEADER), TRUE);
|
|
|
|
Wrap = Ip6WrapRxData (IpInstance, Dup);
|
|
|
|
if (Wrap == NULL) {
|
|
NetbufFree (Dup);
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
RemoveEntryList (&Packet->List);
|
|
NetbufFree (Packet);
|
|
|
|
Packet = Dup;
|
|
}
|
|
|
|
//
|
|
// Insert it into the delivered packet, then get a user's
|
|
// receive token, pass the wrapped packet up.
|
|
//
|
|
EfiAcquireLockOrFail (&IpInstance->RecycleLock);
|
|
InsertHeadList (&IpInstance->Delivered, &Wrap->Link);
|
|
EfiReleaseLock (&IpInstance->RecycleLock);
|
|
|
|
Token = NetMapRemoveHead (&IpInstance->RxTokens, NULL);
|
|
Token->Status = IP6_GET_CLIP_INFO (Packet)->Status;
|
|
Token->Packet.RxData = &Wrap->RxData;
|
|
|
|
gBS->SignalEvent (Token->Event);
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Enqueue a received packet to all the IP children that share
|
|
the same interface.
|
|
|
|
@param[in] IpSb The IP6 service instance that receive the packet.
|
|
@param[in] Head The header of the received packet.
|
|
@param[in] Packet The data of the received packet.
|
|
@param[in] IpIf The interface to enqueue the packet to.
|
|
|
|
@return The number of the IP6 children that accepts the packet.
|
|
|
|
**/
|
|
INTN
|
|
Ip6InterfaceEnquePacket (
|
|
IN IP6_SERVICE *IpSb,
|
|
IN EFI_IP6_HEADER *Head,
|
|
IN NET_BUF *Packet,
|
|
IN IP6_INTERFACE *IpIf
|
|
)
|
|
{
|
|
IP6_PROTOCOL *IpInstance;
|
|
IP6_CLIP_INFO *Info;
|
|
LIST_ENTRY *Entry;
|
|
INTN Enqueued;
|
|
INTN LocalType;
|
|
INTN SavedType;
|
|
|
|
//
|
|
// First, check that the packet is acceptable to this interface
|
|
// and find the local cast type for the interface.
|
|
//
|
|
LocalType = 0;
|
|
Info = IP6_GET_CLIP_INFO (Packet);
|
|
|
|
if (IpIf->PromiscRecv) {
|
|
LocalType = Ip6Promiscuous;
|
|
} else {
|
|
LocalType = Info->CastType;
|
|
}
|
|
|
|
//
|
|
// Iterate through the ip instances on the interface, enqueue
|
|
// the packet if filter passed. Save the original cast type,
|
|
// and pass the local cast type to the IP children on the
|
|
// interface. The global cast type will be restored later.
|
|
//
|
|
SavedType = Info->CastType;
|
|
Info->CastType = (UINT32) LocalType;
|
|
|
|
Enqueued = 0;
|
|
|
|
NET_LIST_FOR_EACH (Entry, &IpIf->IpInstances) {
|
|
IpInstance = NET_LIST_USER_STRUCT (Entry, IP6_PROTOCOL, AddrLink);
|
|
NET_CHECK_SIGNATURE (IpInstance, IP6_PROTOCOL_SIGNATURE);
|
|
|
|
if (Ip6InstanceEnquePacket (IpInstance, Head, Packet) == EFI_SUCCESS) {
|
|
Enqueued++;
|
|
}
|
|
}
|
|
|
|
Info->CastType = (UINT32) SavedType;
|
|
return Enqueued;
|
|
}
|
|
|
|
/**
|
|
Deliver the packet for each IP6 child on the interface.
|
|
|
|
@param[in] IpSb The IP6 service instance that received the packet.
|
|
@param[in] IpIf The IP6 interface to deliver the packet.
|
|
|
|
**/
|
|
VOID
|
|
Ip6InterfaceDeliverPacket (
|
|
IN IP6_SERVICE *IpSb,
|
|
IN IP6_INTERFACE *IpIf
|
|
)
|
|
{
|
|
IP6_PROTOCOL *IpInstance;
|
|
LIST_ENTRY *Entry;
|
|
|
|
NET_LIST_FOR_EACH (Entry, &IpIf->IpInstances) {
|
|
IpInstance = NET_LIST_USER_STRUCT (Entry, IP6_PROTOCOL, AddrLink);
|
|
Ip6InstanceDeliverPacket (IpInstance);
|
|
}
|
|
}
|
|
|
|
/**
|
|
De-multiplex the packet. the packet delivery is processed in two
|
|
passes. The first pass will enqueue a shared copy of the packet
|
|
to each IP6 child that accepts the packet. The second pass will
|
|
deliver a non-shared copy of the packet to each IP6 child that
|
|
has pending receive requests. Data is copied if more than one
|
|
child wants to consume the packet, because each IP child needs
|
|
its own copy of the packet to make changes.
|
|
|
|
@param[in] IpSb The IP6 service instance that received the packet.
|
|
@param[in] Head The header of the received packet.
|
|
@param[in] Packet The data of the received packet.
|
|
|
|
@retval EFI_NOT_FOUND No IP child accepts the packet.
|
|
@retval EFI_SUCCESS The packet is enqueued or delivered to some IP
|
|
children.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
Ip6Demultiplex (
|
|
IN IP6_SERVICE *IpSb,
|
|
IN EFI_IP6_HEADER *Head,
|
|
IN NET_BUF *Packet
|
|
)
|
|
{
|
|
|
|
LIST_ENTRY *Entry;
|
|
IP6_INTERFACE *IpIf;
|
|
INTN Enqueued;
|
|
|
|
//
|
|
// Two pass delivery: first, enque a shared copy of the packet
|
|
// to each instance that accept the packet.
|
|
//
|
|
Enqueued = 0;
|
|
|
|
NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {
|
|
IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link);
|
|
|
|
if (IpIf->Configured) {
|
|
Enqueued += Ip6InterfaceEnquePacket (IpSb, Head, Packet, IpIf);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Second: deliver a duplicate of the packet to each instance.
|
|
// Release the local reference first, so that the last instance
|
|
// getting the packet will not copy the data.
|
|
//
|
|
NetbufFree (Packet);
|
|
Packet = NULL;
|
|
|
|
if (Enqueued == 0) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {
|
|
IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link);
|
|
|
|
if (IpIf->Configured) {
|
|
Ip6InterfaceDeliverPacket (IpSb, IpIf);
|
|
}
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Decrease the life of the transmitted packets. If it is
|
|
decreased to zero, cancel the packet. This function is
|
|
called by Ip6packetTimerTicking that provides timeout for both the
|
|
received-but-not-delivered and transmitted-but-not-recycle
|
|
packets.
|
|
|
|
@param[in] Map The IP6 child's transmit map.
|
|
@param[in] Item Current transmitted packet.
|
|
@param[in] Context Not used.
|
|
|
|
@retval EFI_SUCCESS Always returns EFI_SUCCESS.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
Ip6SentPacketTicking (
|
|
IN NET_MAP *Map,
|
|
IN NET_MAP_ITEM *Item,
|
|
IN VOID *Context
|
|
)
|
|
{
|
|
IP6_TXTOKEN_WRAP *Wrap;
|
|
|
|
Wrap = (IP6_TXTOKEN_WRAP *) Item->Value;
|
|
ASSERT (Wrap != NULL);
|
|
|
|
if ((Wrap->Life > 0) && (--Wrap->Life == 0)) {
|
|
Ip6CancelPacket (Wrap->IpInstance->Interface, Wrap->Packet, EFI_ABORTED);
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Timeout the fragments, and the enqueued, and transmitted packets.
|
|
|
|
@param[in] IpSb The IP6 service instance to timeout.
|
|
|
|
**/
|
|
VOID
|
|
Ip6PacketTimerTicking (
|
|
IN IP6_SERVICE *IpSb
|
|
)
|
|
{
|
|
LIST_ENTRY *InstanceEntry;
|
|
LIST_ENTRY *Entry;
|
|
LIST_ENTRY *Next;
|
|
IP6_PROTOCOL *IpInstance;
|
|
IP6_ASSEMBLE_ENTRY *Assemble;
|
|
NET_BUF *Packet;
|
|
IP6_CLIP_INFO *Info;
|
|
UINT32 Index;
|
|
|
|
//
|
|
// First, time out the fragments. The packet's life is counting down
|
|
// once the first-arriving fragment of that packet was received.
|
|
//
|
|
for (Index = 0; Index < IP6_ASSEMLE_HASH_SIZE; Index++) {
|
|
NET_LIST_FOR_EACH_SAFE (Entry, Next, &(IpSb->Assemble.Bucket[Index])) {
|
|
Assemble = NET_LIST_USER_STRUCT (Entry, IP6_ASSEMBLE_ENTRY, Link);
|
|
|
|
if ((Assemble->Life > 0) && (--Assemble->Life == 0)) {
|
|
//
|
|
// If the first fragment (the one with a Fragment Offset of zero)
|
|
// has been received, an ICMP Time Exceeded - Fragment Reassembly
|
|
// Time Exceeded message should be sent to the source of that fragment.
|
|
//
|
|
if ((Assemble->Packet != NULL) &&
|
|
!IP6_IS_MULTICAST (&Assemble->Head->DestinationAddress)) {
|
|
Ip6SendIcmpError (
|
|
IpSb,
|
|
Assemble->Packet,
|
|
NULL,
|
|
&Assemble->Head->SourceAddress,
|
|
ICMP_V6_TIME_EXCEEDED,
|
|
ICMP_V6_TIMEOUT_REASSEMBLE,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
//
|
|
// If reassembly of a packet is not completed within 60 seconds of
|
|
// the reception of the first-arriving fragment of that packet, the
|
|
// reassembly must be abandoned and all the fragments that have been
|
|
// received for that packet must be discarded.
|
|
//
|
|
RemoveEntryList (Entry);
|
|
Ip6FreeAssembleEntry (Assemble);
|
|
}
|
|
}
|
|
}
|
|
|
|
NET_LIST_FOR_EACH (InstanceEntry, &IpSb->Children) {
|
|
IpInstance = NET_LIST_USER_STRUCT (InstanceEntry, IP6_PROTOCOL, Link);
|
|
|
|
//
|
|
// Second, time out the assembled packets enqueued on each IP child.
|
|
//
|
|
NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpInstance->Received) {
|
|
Packet = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
|
|
Info = IP6_GET_CLIP_INFO (Packet);
|
|
|
|
if ((Info->Life > 0) && (--Info->Life == 0)) {
|
|
RemoveEntryList (Entry);
|
|
NetbufFree (Packet);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Third: time out the transmitted packets.
|
|
//
|
|
NetMapIterate (&IpInstance->TxTokens, Ip6SentPacketTicking, NULL);
|
|
}
|
|
}
|
|
|