diff options
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Input.c')
-rw-r--r-- | src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Input.c | 1815 |
1 files changed, 1815 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Input.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Input.c new file mode 100644 index 00000000..91ff89a5 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Input.c @@ -0,0 +1,1815 @@ +/** @file + IP6 internal functions to process the incoming packets. + + Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> + (C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#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 reassembly 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 total length is known + // 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 formatted, 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 excluded. + // + 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 sufficient 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; + } + ASSERT (mIpSec != NULL); + + 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)); + + // + // 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 received 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 transferred 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 implementation. 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; + } + + // + // Enqueue 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, enqueue 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); + } +} + |