/** @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); | |
} | |
} | |