| /** @file | |
| IP4 input process. | |
| Copyright (c) 2005 - 2020, Intel Corporation. All rights reserved.<BR> | |
| (C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR> | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include "Ip4Impl.h" | |
| /** | |
| Create an empty assemble entry for the packet identified by | |
| (Dst, Src, Id, Protocol). The default life for the packet is | |
| 120 seconds. | |
| @param[in] Dst The destination address | |
| @param[in] Src The source address | |
| @param[in] Id The ID field in IP header | |
| @param[in] Protocol The protocol field in IP header | |
| @return NULL if failed to allocate memory for the entry, otherwise | |
| the point to just created reassemble entry. | |
| **/ | |
| IP4_ASSEMBLE_ENTRY * | |
| Ip4CreateAssembleEntry ( | |
| IN IP4_ADDR Dst, | |
| IN IP4_ADDR Src, | |
| IN UINT16 Id, | |
| IN UINT8 Protocol | |
| ) | |
| { | |
| IP4_ASSEMBLE_ENTRY *Assemble; | |
| Assemble = AllocatePool (sizeof (IP4_ASSEMBLE_ENTRY)); | |
| if (Assemble == NULL) { | |
| return NULL; | |
| } | |
| InitializeListHead (&Assemble->Link); | |
| InitializeListHead (&Assemble->Fragments); | |
| Assemble->Dst = Dst; | |
| Assemble->Src = Src; | |
| Assemble->Id = Id; | |
| Assemble->Protocol = Protocol; | |
| Assemble->TotalLen = 0; | |
| Assemble->CurLen = 0; | |
| Assemble->Head = NULL; | |
| Assemble->Info = NULL; | |
| Assemble->Life = IP4_FRAGMENT_LIFE; | |
| return Assemble; | |
| } | |
| /** | |
| Release all the fragments of a packet, then free the assemble entry. | |
| @param[in] Assemble The assemble entry to free | |
| **/ | |
| VOID | |
| Ip4FreeAssembleEntry ( | |
| IN IP4_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); | |
| } | |
| FreePool (Assemble); | |
| } | |
| /** | |
| Initialize an already allocated assemble table. This is generally | |
| the assemble table embedded in the IP4 service instance. | |
| @param[in, out] Table The assemble table to initialize. | |
| **/ | |
| VOID | |
| Ip4InitAssembleTable ( | |
| IN OUT IP4_ASSEMBLE_TABLE *Table | |
| ) | |
| { | |
| UINT32 Index; | |
| for (Index = 0; Index < IP4_ASSEMLE_HASH_SIZE; Index++) { | |
| InitializeListHead (&Table->Bucket[Index]); | |
| } | |
| } | |
| /** | |
| Clean up the assemble table: remove all the fragments | |
| and assemble entries. | |
| @param[in] Table The assemble table to clean up | |
| **/ | |
| VOID | |
| Ip4CleanAssembleTable ( | |
| IN IP4_ASSEMBLE_TABLE *Table | |
| ) | |
| { | |
| LIST_ENTRY *Entry; | |
| LIST_ENTRY *Next; | |
| IP4_ASSEMBLE_ENTRY *Assemble; | |
| UINT32 Index; | |
| for (Index = 0; Index < IP4_ASSEMLE_HASH_SIZE; Index++) { | |
| NET_LIST_FOR_EACH_SAFE (Entry, Next, &Table->Bucket[Index]) { | |
| Assemble = NET_LIST_USER_STRUCT (Entry, IP4_ASSEMBLE_ENTRY, Link); | |
| RemoveEntryList (Entry); | |
| Ip4FreeAssembleEntry (Assemble); | |
| } | |
| } | |
| } | |
| /** | |
| Trim the packet to fit in [Start, End), and update the per | |
| packet information. | |
| @param Packet Packet to trim | |
| @param Start The sequence of the first byte to fit in | |
| @param End One beyond the sequence of last byte to fit in. | |
| **/ | |
| VOID | |
| Ip4TrimPacket ( | |
| IN OUT NET_BUF *Packet, | |
| IN INTN Start, | |
| IN INTN End | |
| ) | |
| { | |
| IP4_CLIP_INFO *Info; | |
| INTN Len; | |
| Info = IP4_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 = Start; | |
| Info->Length -= Len; | |
| } | |
| if (End < Info->End) { | |
| Len = End - Info->End; | |
| NetbufTrim (Packet, (UINT32)Len, NET_BUF_TAIL); | |
| Info->End = End; | |
| Info->Length -= Len; | |
| } | |
| } | |
| /** | |
| 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 will free all the fragments of the packet. | |
| @param[in] Arg The assemble entry to free | |
| **/ | |
| VOID | |
| EFIAPI | |
| Ip4OnFreeFragments ( | |
| IN VOID *Arg | |
| ) | |
| { | |
| Ip4FreeAssembleEntry ((IP4_ASSEMBLE_ENTRY *)Arg); | |
| } | |
| /** | |
| 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 | |
| return. | |
| @param Table The assemble table used. New assemble entry will be created | |
| if the Packet is from a new chain of fragments. | |
| @param 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 reassemble. The point to just assembled | |
| packet if all the fragments of the packet have arrived. | |
| **/ | |
| NET_BUF * | |
| Ip4Reassemble ( | |
| IN OUT IP4_ASSEMBLE_TABLE *Table, | |
| IN OUT NET_BUF *Packet | |
| ) | |
| { | |
| IP4_HEAD *IpHead; | |
| IP4_CLIP_INFO *This; | |
| IP4_CLIP_INFO *Node; | |
| IP4_ASSEMBLE_ENTRY *Assemble; | |
| LIST_ENTRY *Head; | |
| LIST_ENTRY *Prev; | |
| LIST_ENTRY *Cur; | |
| NET_BUF *Fragment; | |
| NET_BUF *NewPacket; | |
| INTN Index; | |
| IpHead = Packet->Ip.Ip4; | |
| This = IP4_GET_CLIP_INFO (Packet); | |
| ASSERT (IpHead != NULL); | |
| // | |
| // First: find the related assemble entry | |
| // | |
| Assemble = NULL; | |
| Index = IP4_ASSEMBLE_HASH (IpHead->Dst, IpHead->Src, IpHead->Id, IpHead->Protocol); | |
| NET_LIST_FOR_EACH (Cur, &Table->Bucket[Index]) { | |
| Assemble = NET_LIST_USER_STRUCT (Cur, IP4_ASSEMBLE_ENTRY, Link); | |
| if ((Assemble->Dst == IpHead->Dst) && (Assemble->Src == IpHead->Src) && | |
| (Assemble->Id == IpHead->Id) && (Assemble->Protocol == IpHead->Protocol)) | |
| { | |
| break; | |
| } | |
| } | |
| // | |
| // Create a new assemble entry if no assemble entry is related to this packet | |
| // | |
| if (Cur == &Table->Bucket[Index]) { | |
| Assemble = Ip4CreateAssembleEntry ( | |
| IpHead->Dst, | |
| IpHead->Src, | |
| IpHead->Id, | |
| IpHead->Protocol | |
| ); | |
| if (Assemble == NULL) { | |
| goto DROP; | |
| } | |
| InsertHeadList (&Table->Bucket[Index], &Assemble->Link); | |
| } | |
| // | |
| // Assemble shouldn't be NULL here | |
| // | |
| ASSERT (Assemble != NULL); | |
| // | |
| // 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. | |
| // | |
| Head = &Assemble->Fragments; | |
| NET_LIST_FOR_EACH (Cur, Head) { | |
| Fragment = NET_LIST_USER_STRUCT (Cur, NET_BUF, List); | |
| if (This->Start < IP4_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) != Head) { | |
| Fragment = NET_LIST_USER_STRUCT (Prev, NET_BUF, List); | |
| Node = IP4_GET_CLIP_INFO (Fragment); | |
| if (This->Start < Node->End) { | |
| if (This->End <= Node->End) { | |
| NetbufFree (Packet); | |
| return NULL; | |
| } | |
| Ip4TrimPacket (Packet, Node->End, This->End); | |
| } | |
| } | |
| // | |
| // 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 != Head) { | |
| Fragment = NET_LIST_USER_STRUCT (Cur, NET_BUF, List); | |
| Node = IP4_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 DROP; | |
| } | |
| Ip4TrimPacket (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. | |
| // | |
| ASSERT (Assemble->Head == NULL); | |
| Assemble->Head = IpHead; | |
| Assemble->Info = IP4_GET_CLIP_INFO (Packet); | |
| } | |
| // | |
| // Don't update the length more than once. | |
| // | |
| if (IP4_LAST_FRAGMENT (IpHead->Fragment) && (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 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 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 (Head->BackLink, NET_BUF, List); | |
| if (IP4_GET_CLIP_INFO (Fragment)->End != Assemble->TotalLen) { | |
| Ip4FreeAssembleEntry (Assemble); | |
| return NULL; | |
| } | |
| // | |
| // Wrap the packet in a net buffer then deliver it up | |
| // | |
| NewPacket = NetbufFromBufList ( | |
| &Assemble->Fragments, | |
| 0, | |
| 0, | |
| Ip4OnFreeFragments, | |
| Assemble | |
| ); | |
| if (NewPacket == NULL) { | |
| Ip4FreeAssembleEntry (Assemble); | |
| return NULL; | |
| } | |
| NewPacket->Ip.Ip4 = Assemble->Head; | |
| ASSERT (Assemble->Info != NULL); | |
| CopyMem ( | |
| IP4_GET_CLIP_INFO (NewPacket), | |
| Assemble->Info, | |
| sizeof (*IP4_GET_CLIP_INFO (NewPacket)) | |
| ); | |
| return NewPacket; | |
| } | |
| return NULL; | |
| DROP: | |
| NetbufFree (Packet); | |
| return NULL; | |
| } | |
| /** | |
| The callback function for the net buffer which 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 | |
| Ip4IpSecFree ( | |
| IN VOID *Arg | |
| ) | |
| { | |
| IP4_IPSEC_WRAP *Wrap; | |
| Wrap = (IP4_IPSEC_WRAP *)Arg; | |
| if (Wrap->IpSecRecycleSignal != NULL) { | |
| gBS->SignalEvent (Wrap->IpSecRecycleSignal); | |
| } | |
| NetbufFree (Wrap->Packet); | |
| FreePool (Wrap); | |
| return; | |
| } | |
| /** | |
| The work function to locate IPsec protocol to process the inbound or | |
| outbound IP packets. The process routine handls the packet with following | |
| actions: bypass the packet, discard the packet, or protect the packet. | |
| @param[in] IpSb The IP4 service instance. | |
| @param[in, out] Head The caller supplied IP4 header. | |
| @param[in, out] Netbuf The IP4 packet to be processed by IPsec. | |
| @param[in, out] Options The caller supplied options. | |
| @param[in, out] OptionsLen 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 is no sufficient resource to complete the operation. | |
| @retval EFI_BUFFER_TOO_SMALL The number of non-empty block is bigger than the | |
| number of input data blocks when build a fragment table. | |
| **/ | |
| EFI_STATUS | |
| Ip4IpSecProcessPacket ( | |
| IN IP4_SERVICE *IpSb, | |
| IN OUT IP4_HEAD **Head, | |
| IN OUT NET_BUF **Netbuf, | |
| IN OUT UINT8 **Options, | |
| IN OUT UINT32 *OptionsLen, | |
| 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; | |
| IP4_TXTOKEN_WRAP *TxWrap; | |
| IP4_IPSEC_WRAP *IpSecWrap; | |
| EFI_STATUS Status; | |
| IP4_HEAD ZeroHead; | |
| Status = EFI_SUCCESS; | |
| if (!mIpSec2Installed) { | |
| goto ON_EXIT; | |
| } | |
| ASSERT (mIpSec != NULL); | |
| Packet = *Netbuf; | |
| RecycleEvent = NULL; | |
| IpSecWrap = NULL; | |
| FragmentTable = NULL; | |
| TxWrap = (IP4_TXTOKEN_WRAP *)Context; | |
| FragmentCount = Packet->BlockOpNum; | |
| ZeroMem (&ZeroHead, sizeof (IP4_HEAD)); | |
| // | |
| // 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 - IP4_MAX_IPSEC_HEADLEN; | |
| } | |
| // | |
| // 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); | |
| // | |
| // Record the original FragmentTable and count. | |
| // | |
| OriginalFragmentTable = FragmentTable; | |
| OriginalFragmentCount = FragmentCount; | |
| if (EFI_ERROR (Status)) { | |
| FreePool (FragmentTable); | |
| goto ON_EXIT; | |
| } | |
| // | |
| // Convert host byte order to network byte order | |
| // | |
| Ip4NtohHead (*Head); | |
| Status = mIpSec->ProcessExt ( | |
| mIpSec, | |
| IpSb->Controller, | |
| IP_VERSION_4, | |
| (VOID *)(*Head), | |
| &(*Head)->Protocol, | |
| (VOID **)Options, | |
| OptionsLen, | |
| (EFI_IPSEC_FRAGMENT_DATA **)(&FragmentTable), | |
| &FragmentCount, | |
| Direction, | |
| &RecycleEvent | |
| ); | |
| // | |
| // Convert back to host byte order | |
| // | |
| Ip4NtohHead (*Head); | |
| if (EFI_ERROR (Status)) { | |
| FreePool (OriginalFragmentTable); | |
| goto ON_EXIT; | |
| } | |
| if ((OriginalFragmentTable == FragmentTable) && (OriginalFragmentCount == FragmentCount)) { | |
| // | |
| // 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, | |
| IP4_MAX_HEADLEN, | |
| 0, | |
| Ip4FreeTxToken, | |
| TxWrap | |
| ); | |
| if (TxWrap->Packet == NULL) { | |
| // | |
| // Recover the TxWrap->Packet, if meet a error, and the caller will free | |
| // the TxWrap. | |
| // | |
| TxWrap->Packet = *Netbuf; | |
| Status = EFI_OUT_OF_RESOURCES; | |
| goto ON_EXIT; | |
| } | |
| // | |
| // Free original Netbuf. | |
| // | |
| NetIpSecNetbufFree (*Netbuf); | |
| *Netbuf = TxWrap->Packet; | |
| } else { | |
| IpSecWrap = AllocateZeroPool (sizeof (IP4_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, | |
| IP4_MAX_HEADLEN, | |
| 0, | |
| Ip4IpSecFree, | |
| 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 (*Head, &ZeroHead, sizeof (IP4_HEAD)))) { | |
| Ip4PrependHead (Packet, *Head, *Options, *OptionsLen); | |
| Ip4NtohHead (Packet->Ip.Ip4); | |
| NetbufTrim (Packet, ((*Head)->HeadLen << 2), TRUE); | |
| CopyMem ( | |
| IP4_GET_CLIP_INFO (Packet), | |
| IP4_GET_CLIP_INFO (IpSecWrap->Packet), | |
| sizeof (IP4_CLIP_INFO) | |
| ); | |
| } | |
| *Netbuf = Packet; | |
| } | |
| ON_EXIT: | |
| return Status; | |
| } | |
| /** | |
| Pre-process the IPv4 packet. First validates the IPv4 packet, and | |
| then reassembles packet if it is necessary. | |
| @param[in] IpSb Pointer to IP4_SERVICE. | |
| @param[in, out] Packet Pointer to the Packet to be processed. | |
| @param[in] Head Pointer to the IP4_HEAD. | |
| @param[in] Option Pointer to a buffer which contains the IPv4 option. | |
| @param[in] OptionLen The length of Option in bytes. | |
| @param[in] Flag The link layer flag for the packet received, such | |
| as multicast. | |
| @retval EFI_SUCCESS The received packet is in well form. | |
| @retval EFI_INVALID_PARAMETER The received packet is malformed. | |
| **/ | |
| EFI_STATUS | |
| Ip4PreProcessPacket ( | |
| IN IP4_SERVICE *IpSb, | |
| IN OUT NET_BUF **Packet, | |
| IN IP4_HEAD *Head, | |
| IN UINT8 *Option, | |
| IN UINT32 OptionLen, | |
| IN UINT32 Flag | |
| ) | |
| { | |
| IP4_CLIP_INFO *Info; | |
| UINT32 HeadLen; | |
| UINT32 TotalLen; | |
| UINT16 Checksum; | |
| // | |
| // Check if the IP4 header is correctly formatted. | |
| // | |
| HeadLen = (Head->HeadLen << 2); | |
| TotalLen = NTOHS (Head->TotalLen); | |
| // | |
| // Mnp may deliver frame trailer sequence up, trim it off. | |
| // | |
| if (TotalLen < (*Packet)->TotalSize) { | |
| NetbufTrim (*Packet, (*Packet)->TotalSize - TotalLen, FALSE); | |
| } | |
| if ((Head->Ver != 4) || (HeadLen < IP4_MIN_HEADLEN) || | |
| (TotalLen < HeadLen) || (TotalLen != (*Packet)->TotalSize)) | |
| { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| // | |
| // Some OS may send IP packets without checksum. | |
| // | |
| Checksum = (UINT16)(~NetblockChecksum ((UINT8 *)Head, HeadLen)); | |
| if ((Head->Checksum != 0) && (Checksum != 0)) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| // | |
| // Convert the IP header to host byte order, then get the per packet info. | |
| // | |
| (*Packet)->Ip.Ip4 = Ip4NtohHead (Head); | |
| Info = IP4_GET_CLIP_INFO (*Packet); | |
| Info->LinkFlag = Flag; | |
| Info->CastType = Ip4GetHostCast (IpSb, Head->Dst, Head->Src); | |
| Info->Start = (Head->Fragment & IP4_HEAD_OFFSET_MASK) << 3; | |
| Info->Length = Head->TotalLen - HeadLen; | |
| Info->End = Info->Start + Info->Length; | |
| Info->Status = EFI_SUCCESS; | |
| // | |
| // The packet is destinated to us if the CastType is non-zero. | |
| // | |
| if ((Info->CastType == 0) || (Info->End > IP4_MAX_PACKET_SIZE)) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| // | |
| // Validate the options. Don't call the Ip4OptionIsValid if | |
| // there is no option to save some CPU process. | |
| // | |
| if ((OptionLen > 0) && !Ip4OptionIsValid (Option, OptionLen, TRUE)) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| // | |
| // Trim the head off, after this point, the packet is headless, | |
| // and Packet->TotalLen == Info->Length. | |
| // | |
| NetbufTrim (*Packet, HeadLen, TRUE); | |
| // | |
| // Reassemble the packet if this is a fragment. The packet is a | |
| // fragment if its head has MF (more fragment) set, or it starts | |
| // at non-zero byte. | |
| // | |
| if (((Head->Fragment & IP4_HEAD_MF_MASK) != 0) || (Info->Start != 0)) { | |
| // | |
| // Drop the fragment if DF is set but it is fragmented. Gateway | |
| // need to send a type 4 destination unreache ICMP message here. | |
| // | |
| if ((Head->Fragment & IP4_HEAD_DF_MASK) != 0) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| // | |
| // The length of all but the last fragments is in the unit of 8 bytes. | |
| // | |
| if (((Head->Fragment & IP4_HEAD_MF_MASK) != 0) && (Info->Length % 8 != 0)) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| *Packet = Ip4Reassemble (&IpSb->Assemble, *Packet); | |
| // | |
| // Packet assembly isn't complete, start receive more packet. | |
| // | |
| if (*Packet == NULL) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| This function checks the IPv4 packet length. | |
| @param[in] Packet Pointer to the IPv4 Packet to be checked. | |
| @retval TRUE The input IPv4 packet length is valid. | |
| @retval FALSE The input IPv4 packet length is invalid. | |
| **/ | |
| BOOLEAN | |
| Ip4IsValidPacketLength ( | |
| IN NET_BUF *Packet | |
| ) | |
| { | |
| // | |
| // Check the IP4 packet length. | |
| // | |
| if (Packet->TotalSize < IP4_MIN_HEADLEN) { | |
| return FALSE; | |
| } | |
| return TRUE; | |
| } | |
| /** | |
| The IP4 input routine. It is called by the IP4_INTERFACE when a | |
| IP4 fragment is received from MNP. | |
| @param[in] Ip4Instance The IP4 child that request the receive, most like | |
| it is NULL. | |
| @param[in] Packet The IP4 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 IP4 service instance that own the MNP. | |
| **/ | |
| VOID | |
| Ip4AccpetFrame ( | |
| IN IP4_PROTOCOL *Ip4Instance, | |
| IN NET_BUF *Packet, | |
| IN EFI_STATUS IoStatus, | |
| IN UINT32 Flag, | |
| IN VOID *Context | |
| ) | |
| { | |
| IP4_SERVICE *IpSb; | |
| IP4_HEAD *Head; | |
| EFI_STATUS Status; | |
| IP4_HEAD ZeroHead; | |
| UINT8 *Option; | |
| UINT32 OptionLen; | |
| IpSb = (IP4_SERVICE *)Context; | |
| Option = NULL; | |
| if (EFI_ERROR (IoStatus) || (IpSb->State == IP4_SERVICE_DESTROY)) { | |
| goto DROP; | |
| } | |
| if (!Ip4IsValidPacketLength (Packet)) { | |
| goto RESTART; | |
| } | |
| Head = (IP4_HEAD *)NetbufGetByte (Packet, 0, NULL); | |
| ASSERT (Head != NULL); | |
| OptionLen = (Head->HeadLen << 2) - IP4_MIN_HEADLEN; | |
| if (OptionLen > 0) { | |
| Option = (UINT8 *)(Head + 1); | |
| } | |
| // | |
| // Validate packet format and reassemble packet if it is necessary. | |
| // | |
| Status = Ip4PreProcessPacket ( | |
| IpSb, | |
| &Packet, | |
| Head, | |
| Option, | |
| OptionLen, | |
| Flag | |
| ); | |
| 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 = Ip4IpSecProcessPacket ( | |
| IpSb, | |
| &Head, | |
| &Packet, | |
| &Option, | |
| &OptionLen, | |
| EfiIPsecInBound, | |
| NULL | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| goto RESTART; | |
| } | |
| // | |
| // If the packet is protected by tunnel mode, parse the inner Ip Packet. | |
| // | |
| ZeroMem (&ZeroHead, sizeof (IP4_HEAD)); | |
| if (0 == CompareMem (Head, &ZeroHead, sizeof (IP4_HEAD))) { | |
| // Packet may have been changed. Head, HeadLen, TotalLen, and | |
| // info must be reloaded before use. The ownership of the packet | |
| // is transferred to the packet process logic. | |
| // | |
| if (!Ip4IsValidPacketLength (Packet)) { | |
| goto RESTART; | |
| } | |
| Head = (IP4_HEAD *)NetbufGetByte (Packet, 0, NULL); | |
| ASSERT (Head != NULL); | |
| Status = Ip4PreProcessPacket ( | |
| IpSb, | |
| &Packet, | |
| Head, | |
| Option, | |
| OptionLen, | |
| Flag | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| goto RESTART; | |
| } | |
| } | |
| ASSERT (Packet != NULL); | |
| Head = Packet->Ip.Ip4; | |
| IP4_GET_CLIP_INFO (Packet)->Status = EFI_SUCCESS; | |
| switch (Head->Protocol) { | |
| case EFI_IP_PROTO_ICMP: | |
| Ip4IcmpHandle (IpSb, Head, Packet); | |
| break; | |
| case IP4_PROTO_IGMP: | |
| Ip4IgmpHandle (IpSb, Head, Packet); | |
| break; | |
| default: | |
| Ip4Demultiplex (IpSb, Head, Packet, Option, OptionLen); | |
| } | |
| Packet = NULL; | |
| // | |
| // Dispatch the DPCs queued by the NotifyFunction of the rx token's events | |
| // which are signaled with received data. | |
| // | |
| DispatchDpc (); | |
| RESTART: | |
| Ip4ReceiveFrame (IpSb->DefaultInterface, NULL, Ip4AccpetFrame, IpSb); | |
| DROP: | |
| if (Packet != NULL) { | |
| NetbufFree (Packet); | |
| } | |
| return; | |
| } | |
| /** | |
| 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 If the child wants to receive the packet. | |
| @retval FALSE Otherwise. | |
| **/ | |
| BOOLEAN | |
| Ip4InstanceFrameAcceptable ( | |
| IN IP4_PROTOCOL *IpInstance, | |
| IN IP4_HEAD *Head, | |
| IN NET_BUF *Packet | |
| ) | |
| { | |
| IP4_ICMP_ERROR_HEAD Icmp; | |
| EFI_IP4_CONFIG_DATA *Config; | |
| IP4_CLIP_INFO *Info; | |
| UINT16 Proto; | |
| UINT32 Index; | |
| Config = &IpInstance->ConfigData; | |
| // | |
| // 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 capability. 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; | |
| } | |
| // | |
| // Use protocol from the IP header embedded in the ICMP error | |
| // message to filter, instead of ICMP itself. ICMP handle will | |
| // call Ip4Demultiplex to deliver ICMP errors. | |
| // | |
| Proto = Head->Protocol; | |
| if ((Proto == EFI_IP_PROTO_ICMP) && (!Config->AcceptAnyProtocol) && (Proto != Config->DefaultProtocol)) { | |
| NetbufCopy (Packet, 0, sizeof (Icmp.Head), (UINT8 *)&Icmp.Head); | |
| if (mIcmpClass[Icmp.Head.Type].IcmpClass == ICMP_ERROR_MESSAGE) { | |
| if (!Config->AcceptIcmpErrors) { | |
| return FALSE; | |
| } | |
| NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *)&Icmp); | |
| Proto = Icmp.IpHead.Protocol; | |
| } | |
| } | |
| // | |
| // 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 = IP4_GET_CLIP_INFO (Packet); | |
| if (IP4_IS_BROADCAST (Info->CastType)) { | |
| return Config->AcceptBroadcast; | |
| } | |
| // | |
| // If it is a multicast packet, check whether we are in the group. | |
| // | |
| if (Info->CastType == IP4_MULTICAST) { | |
| // | |
| // Receive the multicast if the instance wants to receive all packets. | |
| // | |
| if (!IpInstance->ConfigData.UseDefaultAddress && (IpInstance->Interface->Ip == 0)) { | |
| return TRUE; | |
| } | |
| for (Index = 0; Index < IpInstance->GroupCount; Index++) { | |
| if (IpInstance->Groups[Index] == HTONL (Head->Dst)) { | |
| break; | |
| } | |
| } | |
| return (BOOLEAN)(Index < IpInstance->GroupCount); | |
| } | |
| return TRUE; | |
| } | |
| /** | |
| Enqueue a shared copy of the packet to the IP4 child if the | |
| packet is acceptable to it. Here the data of the packet is | |
| shared, but the net buffer isn't. | |
| @param[in] IpInstance The IP4 child to enqueue the packet to | |
| @param[in] Head The IP header of the received packet | |
| @param[in] 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 resource | |
| @retval EFI_SUCCESS A shared copy the packet is enqueued to the child. | |
| **/ | |
| EFI_STATUS | |
| Ip4InstanceEnquePacket ( | |
| IN IP4_PROTOCOL *IpInstance, | |
| IN IP4_HEAD *Head, | |
| IN NET_BUF *Packet | |
| ) | |
| { | |
| IP4_CLIP_INFO *Info; | |
| NET_BUF *Clone; | |
| // | |
| // Check whether the packet is acceptable to this instance. | |
| // | |
| if (IpInstance->State != IP4_STATE_CONFIGED) { | |
| return EFI_NOT_STARTED; | |
| } | |
| if (!Ip4InstanceFrameAcceptable (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 = IP4_GET_CLIP_INFO (Clone); | |
| Info->Life = IP4_US_TO_SEC (IpInstance->ConfigData.ReceiveTimeout); | |
| InsertTailList (&IpInstance->Received, &Clone->List); | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| The signal handle of IP4's recycle event. It is called back | |
| when the upper layer release the packet. | |
| @param Event The IP4's recycle event. | |
| @param Context The context of the handle, which is a | |
| IP4_RXDATA_WRAP | |
| **/ | |
| VOID | |
| EFIAPI | |
| Ip4OnRecyclePacket ( | |
| IN EFI_EVENT Event, | |
| IN VOID *Context | |
| ) | |
| { | |
| IP4_RXDATA_WRAP *Wrap; | |
| Wrap = (IP4_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 IP4_RXDATA_WRAP, which will be | |
| delivered to the upper layer. Each IP4 child that accepts the | |
| packet will get a not-shared copy of the packet which is wrapped | |
| in the IP4_RXDATA_WRAP. The IP4_RXDATA_WRAP->RxData is passed | |
| to the upper layer. Upper layer will signal the recycle event in | |
| it when it is done with the packet. | |
| @param[in] IpInstance The IP4 child to receive the packet. | |
| @param[in] Packet The packet to deliver up. | |
| @retval Wrap if warp the packet succeed. | |
| @retval NULL failed to wrap the packet . | |
| **/ | |
| IP4_RXDATA_WRAP * | |
| Ip4WrapRxData ( | |
| IN IP4_PROTOCOL *IpInstance, | |
| IN NET_BUF *Packet | |
| ) | |
| { | |
| IP4_RXDATA_WRAP *Wrap; | |
| EFI_IP4_RECEIVE_DATA *RxData; | |
| EFI_STATUS Status; | |
| BOOLEAN RawData; | |
| Wrap = AllocatePool (IP4_RXDATA_WRAP_SIZE (Packet->BlockOpNum)); | |
| if (Wrap == NULL) { | |
| return NULL; | |
| } | |
| InitializeListHead (&Wrap->Link); | |
| Wrap->IpInstance = IpInstance; | |
| Wrap->Packet = Packet; | |
| RxData = &Wrap->RxData; | |
| ZeroMem (RxData, sizeof (EFI_IP4_RECEIVE_DATA)); | |
| Status = gBS->CreateEvent ( | |
| EVT_NOTIFY_SIGNAL, | |
| TPL_NOTIFY, | |
| Ip4OnRecyclePacket, | |
| Wrap, | |
| &RxData->RecycleSignal | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| FreePool (Wrap); | |
| return NULL; | |
| } | |
| ASSERT (Packet->Ip.Ip4 != NULL); | |
| ASSERT (IpInstance != NULL); | |
| RawData = IpInstance->ConfigData.RawData; | |
| // | |
| // The application expects a network byte order header. | |
| // | |
| if (!RawData) { | |
| RxData->HeaderLength = (Packet->Ip.Ip4->HeadLen << 2); | |
| RxData->Header = (EFI_IP4_HEADER *)Ip4NtohHead (Packet->Ip.Ip4); | |
| RxData->OptionsLength = RxData->HeaderLength - IP4_MIN_HEADLEN; | |
| RxData->Options = NULL; | |
| if (RxData->OptionsLength != 0) { | |
| RxData->Options = (VOID *)(RxData->Header + 1); | |
| } | |
| } | |
| 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; | |
| } | |
| /** | |
| Deliver the received packets to 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 | |
| Ip4InstanceDeliverPacket ( | |
| IN IP4_PROTOCOL *IpInstance | |
| ) | |
| { | |
| EFI_IP4_COMPLETION_TOKEN *Token; | |
| IP4_RXDATA_WRAP *Wrap; | |
| NET_BUF *Packet; | |
| NET_BUF *Dup; | |
| UINT8 *Head; | |
| UINT32 HeadLen; | |
| // | |
| // 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 = Ip4WrapRxData (IpInstance, Packet); | |
| if (Wrap == NULL) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| RemoveEntryList (&Packet->List); | |
| } else { | |
| // | |
| // Create a duplicated packet if this packet is shared | |
| // | |
| if (IpInstance->ConfigData.RawData) { | |
| HeadLen = 0; | |
| } else { | |
| HeadLen = IP4_MAX_HEADLEN; | |
| } | |
| Dup = NetbufDuplicate (Packet, NULL, HeadLen); | |
| if (Dup == NULL) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| if (!IpInstance->ConfigData.RawData) { | |
| // | |
| // 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, IP4_MAX_HEADLEN, NET_BUF_HEAD); | |
| ASSERT (Head != NULL); | |
| Dup->Ip.Ip4 = (IP4_HEAD *)Head; | |
| CopyMem (Head, Packet->Ip.Ip4, Packet->Ip.Ip4->HeadLen << 2); | |
| NetbufTrim (Dup, IP4_MAX_HEADLEN, TRUE); | |
| } | |
| Wrap = Ip4WrapRxData (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 = IP4_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 IP4 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] Option Point to the IP4 packet header options. | |
| @param[in] OptionLen Length of the IP4 packet header options. | |
| @param[in] IpIf The interface to enqueue the packet to. | |
| @return The number of the IP4 children that accepts the packet | |
| **/ | |
| INTN | |
| Ip4InterfaceEnquePacket ( | |
| IN IP4_SERVICE *IpSb, | |
| IN IP4_HEAD *Head, | |
| IN NET_BUF *Packet, | |
| IN UINT8 *Option, | |
| IN UINT32 OptionLen, | |
| IN IP4_INTERFACE *IpIf | |
| ) | |
| { | |
| IP4_PROTOCOL *IpInstance; | |
| IP4_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. A packet sent | |
| // to say 192.168.1.1 should NOT be deliver to 10.0.0.1 unless | |
| // promiscuous receiving. | |
| // | |
| LocalType = 0; | |
| Info = IP4_GET_CLIP_INFO (Packet); | |
| if ((Info->CastType == IP4_MULTICAST) || (Info->CastType == IP4_LOCAL_BROADCAST)) { | |
| // | |
| // If the CastType is multicast, don't need to filter against | |
| // the group address here, Ip4InstanceFrameAcceptable will do | |
| // that later. | |
| // | |
| LocalType = Info->CastType; | |
| } else { | |
| // | |
| // Check the destination against local IP. If the station | |
| // address is 0.0.0.0, it means receiving all the IP destined | |
| // to local non-zero IP. Otherwise, it is necessary to compare | |
| // the destination to the interface's IP address. | |
| // | |
| if (IpIf->Ip == IP4_ALLZERO_ADDRESS) { | |
| LocalType = IP4_LOCAL_HOST; | |
| } else { | |
| LocalType = Ip4GetNetCast (Head->Dst, IpIf); | |
| if ((LocalType == 0) && IpIf->PromiscRecv) { | |
| LocalType = IP4_PROMISCUOUS; | |
| } | |
| } | |
| } | |
| if (LocalType == 0) { | |
| return 0; | |
| } | |
| // | |
| // 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 = LocalType; | |
| Enqueued = 0; | |
| NET_LIST_FOR_EACH (Entry, &IpIf->IpInstances) { | |
| IpInstance = NET_LIST_USER_STRUCT (Entry, IP4_PROTOCOL, AddrLink); | |
| NET_CHECK_SIGNATURE (IpInstance, IP4_PROTOCOL_SIGNATURE); | |
| // | |
| // In RawData mode, add IPv4 headers and options back to packet. | |
| // | |
| if ((IpInstance->ConfigData.RawData) && (Option != NULL) && (OptionLen != 0)) { | |
| Ip4PrependHead (Packet, Head, Option, OptionLen); | |
| } | |
| if (Ip4InstanceEnquePacket (IpInstance, Head, Packet) == EFI_SUCCESS) { | |
| Enqueued++; | |
| } | |
| } | |
| Info->CastType = SavedType; | |
| return Enqueued; | |
| } | |
| /** | |
| Deliver the packet for each IP4 child on the interface. | |
| @param[in] IpSb The IP4 service instance that received the packet | |
| @param[in] IpIf The IP4 interface to deliver the packet. | |
| @retval EFI_SUCCESS It always returns EFI_SUCCESS now | |
| **/ | |
| EFI_STATUS | |
| Ip4InterfaceDeliverPacket ( | |
| IN IP4_SERVICE *IpSb, | |
| IN IP4_INTERFACE *IpIf | |
| ) | |
| { | |
| IP4_PROTOCOL *Ip4Instance; | |
| LIST_ENTRY *Entry; | |
| NET_LIST_FOR_EACH (Entry, &IpIf->IpInstances) { | |
| Ip4Instance = NET_LIST_USER_STRUCT (Entry, IP4_PROTOCOL, AddrLink); | |
| Ip4InstanceDeliverPacket (Ip4Instance); | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Demultiple the packet. the packet delivery is processed in two | |
| passes. The first pass will enqueue a shared copy of the packet | |
| to each IP4 child that accepts the packet. The second pass will | |
| deliver a non-shared copy of the packet to each IP4 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 IP4 service instance that received the packet. | |
| @param[in] Head The header of the received packet. | |
| @param[in] Packet The data of the received packet. | |
| @param[in] Option Point to the IP4 packet header options. | |
| @param[in] OptionLen Length of the IP4 packet header options. | |
| @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 | |
| Ip4Demultiplex ( | |
| IN IP4_SERVICE *IpSb, | |
| IN IP4_HEAD *Head, | |
| IN NET_BUF *Packet, | |
| IN UINT8 *Option, | |
| IN UINT32 OptionLen | |
| ) | |
| { | |
| LIST_ENTRY *Entry; | |
| IP4_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, IP4_INTERFACE, Link); | |
| if (IpIf->Configured) { | |
| Enqueued += Ip4InterfaceEnquePacket ( | |
| IpSb, | |
| Head, | |
| Packet, | |
| Option, | |
| OptionLen, | |
| 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); | |
| if (Enqueued == 0) { | |
| return EFI_NOT_FOUND; | |
| } | |
| NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { | |
| IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link); | |
| if (IpIf->Configured) { | |
| Ip4InterfaceDeliverPacket (IpSb, IpIf); | |
| } | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Timeout the fragment and enqueued packets. | |
| @param[in] IpSb The IP4 service instance to timeout | |
| **/ | |
| VOID | |
| Ip4PacketTimerTicking ( | |
| IN IP4_SERVICE *IpSb | |
| ) | |
| { | |
| LIST_ENTRY *InstanceEntry; | |
| LIST_ENTRY *Entry; | |
| LIST_ENTRY *Next; | |
| IP4_PROTOCOL *IpInstance; | |
| IP4_ASSEMBLE_ENTRY *Assemble; | |
| NET_BUF *Packet; | |
| IP4_CLIP_INFO *Info; | |
| UINT32 Index; | |
| // | |
| // First, time out the fragments. The packet's life is counting down | |
| // once the first-arrived fragment was received. | |
| // | |
| for (Index = 0; Index < IP4_ASSEMLE_HASH_SIZE; Index++) { | |
| NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->Assemble.Bucket[Index]) { | |
| Assemble = NET_LIST_USER_STRUCT (Entry, IP4_ASSEMBLE_ENTRY, Link); | |
| if ((Assemble->Life > 0) && (--Assemble->Life == 0)) { | |
| RemoveEntryList (Entry); | |
| Ip4FreeAssembleEntry (Assemble); | |
| } | |
| } | |
| } | |
| NET_LIST_FOR_EACH (InstanceEntry, &IpSb->Children) { | |
| IpInstance = NET_LIST_USER_STRUCT (InstanceEntry, IP4_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 = IP4_GET_CLIP_INFO (Packet); | |
| if ((Info->Life > 0) && (--Info->Life == 0)) { | |
| RemoveEntryList (Entry); | |
| NetbufFree (Packet); | |
| } | |
| } | |
| // | |
| // Third: time out the transmitted packets. | |
| // | |
| NetMapIterate (&IpInstance->TxTokens, Ip4SentPacketTicking, NULL); | |
| } | |
| } |