From f215e02bf85f68d3a6106c2a1f4f7f063f819064 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:17:27 +0200 Subject: Adding upstream version 7.0.14-dfsg. Signed-off-by: Daniel Baumann --- .../NetworkPkg/Library/DxeIpIoLib/DxeIpIoLib.c | 2291 ++++++++++++++++++++ 1 file changed, 2291 insertions(+) create mode 100644 src/VBox/Devices/EFI/Firmware/NetworkPkg/Library/DxeIpIoLib/DxeIpIoLib.c (limited to 'src/VBox/Devices/EFI/Firmware/NetworkPkg/Library/DxeIpIoLib/DxeIpIoLib.c') diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Library/DxeIpIoLib/DxeIpIoLib.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Library/DxeIpIoLib/DxeIpIoLib.c new file mode 100644 index 00000000..d2fc10c1 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Library/DxeIpIoLib/DxeIpIoLib.c @@ -0,0 +1,2291 @@ +/** @file + IpIo Library. + +(C) Copyright 2014 Hewlett-Packard Development Company, L.P.
+Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + + +GLOBAL_REMOVE_IF_UNREFERENCED LIST_ENTRY mActiveIpIoList = { + &mActiveIpIoList, + &mActiveIpIoList +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_IP4_CONFIG_DATA mIp4IoDefaultIpConfigData = { + EFI_IP_PROTO_UDP, + FALSE, + TRUE, + FALSE, + FALSE, + FALSE, + {{0, 0, 0, 0}}, + {{0, 0, 0, 0}}, + 0, + 255, + FALSE, + FALSE, + 0, + 0 +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_IP6_CONFIG_DATA mIp6IoDefaultIpConfigData = { + EFI_IP_PROTO_UDP, + FALSE, + TRUE, + FALSE, + {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, + {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, + 0, + 255, + 0, + 0, + 0 +}; + +GLOBAL_REMOVE_IF_UNREFERENCED ICMP_ERROR_INFO mIcmpErrMap[10] = { + {FALSE, TRUE }, // ICMP_ERR_UNREACH_NET + {FALSE, TRUE }, // ICMP_ERR_UNREACH_HOST + {TRUE, TRUE }, // ICMP_ERR_UNREACH_PROTOCOL + {TRUE, TRUE }, // ICMP_ERR_UNREACH_PORT + {TRUE, TRUE }, // ICMP_ERR_MSGSIZE + {FALSE, TRUE }, // ICMP_ERR_UNREACH_SRCFAIL + {FALSE, TRUE }, // ICMP_ERR_TIMXCEED_INTRANS + {FALSE, TRUE }, // ICMP_ERR_TIMEXCEED_REASS + {FALSE, FALSE}, // ICMP_ERR_QUENCH + {FALSE, TRUE } // ICMP_ERR_PARAMPROB +}; + +GLOBAL_REMOVE_IF_UNREFERENCED ICMP_ERROR_INFO mIcmp6ErrMap[10] = { + {FALSE, TRUE}, // ICMP6_ERR_UNREACH_NET + {FALSE, TRUE}, // ICMP6_ERR_UNREACH_HOST + {TRUE, TRUE}, // ICMP6_ERR_UNREACH_PROTOCOL + {TRUE, TRUE}, // ICMP6_ERR_UNREACH_PORT + {TRUE, TRUE}, // ICMP6_ERR_PACKAGE_TOOBIG + {FALSE, TRUE}, // ICMP6_ERR_TIMXCEED_HOPLIMIT + {FALSE, TRUE}, // ICMP6_ERR_TIMXCEED_REASS + {FALSE, TRUE}, // ICMP6_ERR_PARAMPROB_HEADER + {FALSE, TRUE}, // ICMP6_ERR_PARAMPROB_NEXHEADER + {FALSE, TRUE} // ICMP6_ERR_PARAMPROB_IPV6OPTION +}; + + +/** + Notify function for IP transmit token. + + @param[in] Context The context passed in by the event notifier. + +**/ +VOID +EFIAPI +IpIoTransmitHandlerDpc ( + IN VOID *Context + ); + + +/** + Notify function for IP transmit token. + + @param[in] Event The event signaled. + @param[in] Context The context passed in by the event notifier. + +**/ +VOID +EFIAPI +IpIoTransmitHandler ( + IN EFI_EVENT Event, + IN VOID *Context + ); + + +/** + This function create an IP child ,open the IP protocol, and return the opened + IP protocol as Interface. + + @param[in] ControllerHandle The controller handle. + @param[in] ImageHandle The image handle. + @param[in] ChildHandle Pointer to the buffer to save the IP child handle. + @param[in] IpVersion The version of the IP protocol to use, either + IPv4 or IPv6. + @param[out] Interface Pointer used to get the IP protocol interface. + + @retval EFI_SUCCESS The IP child is created and the IP protocol + interface is retrieved. + @retval EFI_UNSUPPORTED Unsupported IpVersion. + @retval Others The required operation failed. + +**/ +EFI_STATUS +IpIoCreateIpChildOpenProtocol ( + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ImageHandle, + IN EFI_HANDLE *ChildHandle, + IN UINT8 IpVersion, + OUT VOID **Interface + ) +{ + EFI_STATUS Status; + EFI_GUID *ServiceBindingGuid; + EFI_GUID *IpProtocolGuid; + + if (IpVersion == IP_VERSION_4) { + ServiceBindingGuid = &gEfiIp4ServiceBindingProtocolGuid; + IpProtocolGuid = &gEfiIp4ProtocolGuid; + } else if (IpVersion == IP_VERSION_6){ + ServiceBindingGuid = &gEfiIp6ServiceBindingProtocolGuid; + IpProtocolGuid = &gEfiIp6ProtocolGuid; + } else { + return EFI_UNSUPPORTED; + } + + // + // Create an IP child. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + ImageHandle, + ServiceBindingGuid, + ChildHandle + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Open the IP protocol installed on the *ChildHandle. + // + Status = gBS->OpenProtocol ( + *ChildHandle, + IpProtocolGuid, + Interface, + ImageHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + // + // On failure, destroy the IP child. + // + NetLibDestroyServiceChild ( + ControllerHandle, + ImageHandle, + ServiceBindingGuid, + *ChildHandle + ); + } + + return Status; +} + + +/** + This function close the previously opened IP protocol and destroy the IP child. + + @param[in] ControllerHandle The controller handle. + @param[in] ImageHandle The image handle. + @param[in] ChildHandle The child handle of the IP child. + @param[in] IpVersion The version of the IP protocol to use, either + IPv4 or IPv6. + + @retval EFI_SUCCESS The IP protocol is closed and the relevant IP child + is destroyed. + @retval EFI_UNSUPPORTED Unsupported IpVersion. + @retval Others The required operation failed. + +**/ +EFI_STATUS +IpIoCloseProtocolDestroyIpChild ( + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ImageHandle, + IN EFI_HANDLE ChildHandle, + IN UINT8 IpVersion + ) +{ + EFI_STATUS Status; + EFI_GUID *ServiceBindingGuid; + EFI_GUID *IpProtocolGuid; + + if (IpVersion == IP_VERSION_4) { + ServiceBindingGuid = &gEfiIp4ServiceBindingProtocolGuid; + IpProtocolGuid = &gEfiIp4ProtocolGuid; + } else if (IpVersion == IP_VERSION_6) { + ServiceBindingGuid = &gEfiIp6ServiceBindingProtocolGuid; + IpProtocolGuid = &gEfiIp6ProtocolGuid; + } else { + return EFI_UNSUPPORTED; + } + + // + // Close the previously opened IP protocol. + // + Status = gBS->CloseProtocol ( + ChildHandle, + IpProtocolGuid, + ImageHandle, + ControllerHandle + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Destroy the IP child. + // + return NetLibDestroyServiceChild ( + ControllerHandle, + ImageHandle, + ServiceBindingGuid, + ChildHandle + ); +} + +/** + This function handles ICMPv4 packets. It is the worker function of + IpIoIcmpHandler. + + @param[in] IpIo Pointer to the IP_IO instance. + @param[in, out] Pkt Pointer to the ICMPv4 packet. + @param[in] Session Pointer to the net session of this ICMPv4 packet. + + @retval EFI_SUCCESS The ICMPv4 packet is handled successfully. + @retval EFI_ABORTED This type of ICMPv4 packet is not supported. + +**/ +EFI_STATUS +IpIoIcmpv4Handler ( + IN IP_IO *IpIo, + IN OUT NET_BUF *Pkt, + IN EFI_NET_SESSION_DATA *Session + ) +{ + IP4_ICMP_ERROR_HEAD *IcmpHdr; + EFI_IP4_HEADER *IpHdr; + UINT8 IcmpErr; + UINT8 *PayLoadHdr; + UINT8 Type; + UINT8 Code; + UINT32 TrimBytes; + + ASSERT (IpIo != NULL); + ASSERT (Pkt != NULL); + ASSERT (Session != NULL); + ASSERT (IpIo->IpVersion == IP_VERSION_4); + + // + // Check the ICMP packet length. + // + if (Pkt->TotalSize < sizeof (IP4_ICMP_ERROR_HEAD)) { + return EFI_ABORTED; + } + + IcmpHdr = NET_PROTO_HDR (Pkt, IP4_ICMP_ERROR_HEAD); + IpHdr = (EFI_IP4_HEADER *) (&IcmpHdr->IpHead); + + if (Pkt->TotalSize < ICMP_ERRLEN (IpHdr)) { + + return EFI_ABORTED; + } + + Type = IcmpHdr->Head.Type; + Code = IcmpHdr->Head.Code; + + // + // Analyze the ICMP Error in this ICMP pkt + // + switch (Type) { + case ICMP_TYPE_UNREACH: + switch (Code) { + case ICMP_CODE_UNREACH_NET: + case ICMP_CODE_UNREACH_HOST: + case ICMP_CODE_UNREACH_PROTOCOL: + case ICMP_CODE_UNREACH_PORT: + case ICMP_CODE_UNREACH_SRCFAIL: + IcmpErr = (UINT8) (ICMP_ERR_UNREACH_NET + Code); + + break; + + case ICMP_CODE_UNREACH_NEEDFRAG: + IcmpErr = ICMP_ERR_MSGSIZE; + + break; + + case ICMP_CODE_UNREACH_NET_UNKNOWN: + case ICMP_CODE_UNREACH_NET_PROHIB: + case ICMP_CODE_UNREACH_TOSNET: + IcmpErr = ICMP_ERR_UNREACH_NET; + + break; + + case ICMP_CODE_UNREACH_HOST_UNKNOWN: + case ICMP_CODE_UNREACH_ISOLATED: + case ICMP_CODE_UNREACH_HOST_PROHIB: + case ICMP_CODE_UNREACH_TOSHOST: + IcmpErr = ICMP_ERR_UNREACH_HOST; + + break; + + default: + return EFI_ABORTED; + } + + break; + + case ICMP_TYPE_TIMXCEED: + if (Code > 1) { + return EFI_ABORTED; + } + + IcmpErr = (UINT8) (Code + ICMP_ERR_TIMXCEED_INTRANS); + + break; + + case ICMP_TYPE_PARAMPROB: + if (Code > 1) { + return EFI_ABORTED; + } + + IcmpErr = ICMP_ERR_PARAMPROB; + + break; + + case ICMP_TYPE_SOURCEQUENCH: + if (Code != 0) { + return EFI_ABORTED; + } + + IcmpErr = ICMP_ERR_QUENCH; + + break; + + default: + return EFI_ABORTED; + } + + // + // Notify user the ICMP pkt only containing payload except + // IP and ICMP header + // + PayLoadHdr = (UINT8 *) ((UINT8 *) IpHdr + EFI_IP4_HEADER_LEN (IpHdr)); + TrimBytes = (UINT32) (PayLoadHdr - (UINT8 *) IcmpHdr); + + NetbufTrim (Pkt, TrimBytes, TRUE); + + // + // If the input packet has invalid format, and TrimBytes is larger than + // the packet size, the NetbufTrim might trim the packet to zero. + // + if (Pkt->TotalSize != 0) { + IpIo->PktRcvdNotify (EFI_ICMP_ERROR, IcmpErr, Session, Pkt, IpIo->RcvdContext); + } + + return EFI_SUCCESS; +} + +/** + This function handles ICMPv6 packets. It is the worker function of + IpIoIcmpHandler. + + @param[in] IpIo Pointer to the IP_IO instance. + @param[in, out] Pkt Pointer to the ICMPv6 packet. + @param[in] Session Pointer to the net session of this ICMPv6 packet. + + @retval EFI_SUCCESS The ICMPv6 packet is handled successfully. + @retval EFI_ABORTED This type of ICMPv6 packet is not supported. + +**/ +EFI_STATUS +IpIoIcmpv6Handler ( + IN IP_IO *IpIo, + IN OUT NET_BUF *Pkt, + IN EFI_NET_SESSION_DATA *Session + ) +{ + IP6_ICMP_ERROR_HEAD *IcmpHdr; + EFI_IP6_HEADER *IpHdr; + UINT8 IcmpErr; + UINT8 *PayLoadHdr; + UINT8 Type; + UINT8 Code; + UINT8 NextHeader; + UINT32 TrimBytes; + BOOLEAN Flag; + + ASSERT (IpIo != NULL); + ASSERT (Pkt != NULL); + ASSERT (Session != NULL); + ASSERT (IpIo->IpVersion == IP_VERSION_6); + + // + // Check the ICMPv6 packet length. + // + if (Pkt->TotalSize < sizeof (IP6_ICMP_ERROR_HEAD)) { + + return EFI_ABORTED; + } + + IcmpHdr = NET_PROTO_HDR (Pkt, IP6_ICMP_ERROR_HEAD); + Type = IcmpHdr->Head.Type; + Code = IcmpHdr->Head.Code; + + // + // Analyze the ICMPv6 Error in this ICMPv6 packet + // + switch (Type) { + case ICMP_V6_DEST_UNREACHABLE: + switch (Code) { + case ICMP_V6_NO_ROUTE_TO_DEST: + case ICMP_V6_BEYOND_SCOPE: + case ICMP_V6_ROUTE_REJECTED: + IcmpErr = ICMP6_ERR_UNREACH_NET; + + break; + + case ICMP_V6_COMM_PROHIBITED: + case ICMP_V6_ADDR_UNREACHABLE: + case ICMP_V6_SOURCE_ADDR_FAILED: + IcmpErr = ICMP6_ERR_UNREACH_HOST; + + break; + + case ICMP_V6_PORT_UNREACHABLE: + IcmpErr = ICMP6_ERR_UNREACH_PORT; + + break; + + default: + return EFI_ABORTED; + } + + break; + + case ICMP_V6_PACKET_TOO_BIG: + if (Code >= 1) { + return EFI_ABORTED; + } + + IcmpErr = ICMP6_ERR_PACKAGE_TOOBIG; + + break; + + case ICMP_V6_TIME_EXCEEDED: + if (Code > 1) { + return EFI_ABORTED; + } + + IcmpErr = (UINT8) (ICMP6_ERR_TIMXCEED_HOPLIMIT + Code); + + break; + + case ICMP_V6_PARAMETER_PROBLEM: + if (Code > 3) { + return EFI_ABORTED; + } + + IcmpErr = (UINT8) (ICMP6_ERR_PARAMPROB_HEADER + Code); + + break; + + default: + + return EFI_ABORTED; + } + + // + // Notify user the ICMPv6 packet only containing payload except + // IPv6 basic header, extension header and ICMP header + // + + IpHdr = (EFI_IP6_HEADER *) (&IcmpHdr->IpHead); + NextHeader = IpHdr->NextHeader; + PayLoadHdr = (UINT8 *) ((UINT8 *) IcmpHdr + sizeof (IP6_ICMP_ERROR_HEAD)); + Flag = TRUE; + + do { + switch (NextHeader) { + case EFI_IP_PROTO_UDP: + case EFI_IP_PROTO_TCP: + case EFI_IP_PROTO_ICMP: + case IP6_NO_NEXT_HEADER: + Flag = FALSE; + + break; + + case IP6_HOP_BY_HOP: + case IP6_DESTINATION: + // + // The Hdr Ext Len is 8-bit unsigned integer in 8-octet units, not including + // the first 8 octets. + // + NextHeader = *(PayLoadHdr); + PayLoadHdr = (UINT8 *) (PayLoadHdr + (*(PayLoadHdr + 1) + 1) * 8); + + break; + + case IP6_FRAGMENT: + // + // The Fragment Header Length is 8 octets. + // + NextHeader = *(PayLoadHdr); + PayLoadHdr = (UINT8 *) (PayLoadHdr + 8); + + break; + + default: + + return EFI_ABORTED; + } + } while (Flag); + + TrimBytes = (UINT32) (PayLoadHdr - (UINT8 *) IcmpHdr); + + NetbufTrim (Pkt, TrimBytes, TRUE); + + // + // If the input packet has invalid format, and TrimBytes is larger than + // the packet size, the NetbufTrim might trim the packet to zero. + // + if (Pkt->TotalSize != 0) { + IpIo->PktRcvdNotify (EFI_ICMP_ERROR, IcmpErr, Session, Pkt, IpIo->RcvdContext); + } + + return EFI_SUCCESS; +} + +/** + This function handles ICMP packets. + + @param[in] IpIo Pointer to the IP_IO instance. + @param[in, out] Pkt Pointer to the ICMP packet. + @param[in] Session Pointer to the net session of this ICMP packet. + + @retval EFI_SUCCESS The ICMP packet is handled successfully. + @retval EFI_ABORTED This type of ICMP packet is not supported. + @retval EFI_UNSUPPORTED The IP protocol version in IP_IO is not supported. + +**/ +EFI_STATUS +IpIoIcmpHandler ( + IN IP_IO *IpIo, + IN OUT NET_BUF *Pkt, + IN EFI_NET_SESSION_DATA *Session + ) +{ + + if (IpIo->IpVersion == IP_VERSION_4) { + + return IpIoIcmpv4Handler (IpIo, Pkt, Session); + + } else if (IpIo->IpVersion == IP_VERSION_6) { + + return IpIoIcmpv6Handler (IpIo, Pkt, Session); + + } else { + + return EFI_UNSUPPORTED; + } +} + + +/** + Free function for receive token of IP_IO. It is used to + signal the recycle event to notify IP to recycle the + data buffer. + + @param[in] Event The event to be signaled. + +**/ +VOID +EFIAPI +IpIoExtFree ( + IN VOID *Event + ) +{ + gBS->SignalEvent ((EFI_EVENT) Event); +} + + +/** + Create a send entry to wrap a packet before sending + out it through IP. + + @param[in, out] IpIo Pointer to the IP_IO instance. + @param[in, out] Pkt Pointer to the packet. + @param[in] Sender Pointer to the IP sender. + @param[in] Context Pointer to the context. + @param[in] NotifyData Pointer to the notify data. + @param[in] Dest Pointer to the destination IP address. + @param[in] Override Pointer to the overridden IP_IO data. + + @return Pointer to the data structure created to wrap the packet. If any error occurs, + then return NULL. + +**/ +IP_IO_SEND_ENTRY * +IpIoCreateSndEntry ( + IN OUT IP_IO *IpIo, + IN OUT NET_BUF *Pkt, + IN IP_IO_IP_PROTOCOL Sender, + IN VOID *Context OPTIONAL, + IN VOID *NotifyData OPTIONAL, + IN EFI_IP_ADDRESS *Dest OPTIONAL, + IN IP_IO_OVERRIDE *Override + ) +{ + IP_IO_SEND_ENTRY *SndEntry; + EFI_EVENT Event; + EFI_STATUS Status; + NET_FRAGMENT *ExtFragment; + UINT32 FragmentCount; + IP_IO_OVERRIDE *OverrideData; + IP_IO_IP_TX_DATA *TxData; + EFI_IP4_TRANSMIT_DATA *Ip4TxData; + EFI_IP6_TRANSMIT_DATA *Ip6TxData; + + if ((IpIo->IpVersion != IP_VERSION_4) && (IpIo->IpVersion != IP_VERSION_6)) { + return NULL; + } + + Event = NULL; + TxData = NULL; + OverrideData = NULL; + + // + // Allocate resource for SndEntry + // + SndEntry = AllocatePool (sizeof (IP_IO_SEND_ENTRY)); + if (NULL == SndEntry) { + return NULL; + } + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + IpIoTransmitHandler, + SndEntry, + &Event + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + FragmentCount = Pkt->BlockOpNum; + + // + // Allocate resource for TxData + // + TxData = (IP_IO_IP_TX_DATA *) AllocatePool ( + sizeof (IP_IO_IP_TX_DATA) + sizeof (NET_FRAGMENT) * (FragmentCount - 1) + ); + + if (NULL == TxData) { + goto ON_ERROR; + } + + // + // Build a fragment table to contain the fragments in the packet. + // + if (IpIo->IpVersion == IP_VERSION_4) { + ExtFragment = (NET_FRAGMENT *) TxData->Ip4TxData.FragmentTable; + } else { + ExtFragment = (NET_FRAGMENT *) TxData->Ip6TxData.FragmentTable; + } + + NetbufBuildExt (Pkt, ExtFragment, &FragmentCount); + + + // + // Allocate resource for OverrideData if needed + // + if (NULL != Override) { + + OverrideData = AllocateCopyPool (sizeof (IP_IO_OVERRIDE), Override); + if (NULL == OverrideData) { + goto ON_ERROR; + } + } + + // + // Set other fields of TxData except the fragment table + // + if (IpIo->IpVersion == IP_VERSION_4) { + + Ip4TxData = &TxData->Ip4TxData; + + IP4_COPY_ADDRESS (&Ip4TxData->DestinationAddress, Dest); + + Ip4TxData->OverrideData = &OverrideData->Ip4OverrideData; + Ip4TxData->OptionsLength = 0; + Ip4TxData->OptionsBuffer = NULL; + Ip4TxData->TotalDataLength = Pkt->TotalSize; + Ip4TxData->FragmentCount = FragmentCount; + + // + // Set the fields of SndToken + // + SndEntry->SndToken.Ip4Token.Event = Event; + SndEntry->SndToken.Ip4Token.Packet.TxData = Ip4TxData; + } else { + + Ip6TxData = &TxData->Ip6TxData; + + if (Dest != NULL) { + CopyMem (&Ip6TxData->DestinationAddress, Dest, sizeof (EFI_IPv6_ADDRESS)); + } else { + ZeroMem (&Ip6TxData->DestinationAddress, sizeof (EFI_IPv6_ADDRESS)); + } + + Ip6TxData->OverrideData = &OverrideData->Ip6OverrideData; + Ip6TxData->DataLength = Pkt->TotalSize; + Ip6TxData->FragmentCount = FragmentCount; + Ip6TxData->ExtHdrsLength = 0; + Ip6TxData->ExtHdrs = NULL; + + // + // Set the fields of SndToken + // + SndEntry->SndToken.Ip6Token.Event = Event; + SndEntry->SndToken.Ip6Token.Packet.TxData = Ip6TxData; + } + + // + // Set the fields of SndEntry + // + SndEntry->IpIo = IpIo; + SndEntry->Ip = Sender; + SndEntry->Context = Context; + SndEntry->NotifyData = NotifyData; + + SndEntry->Pkt = Pkt; + NET_GET_REF (Pkt); + + InsertTailList (&IpIo->PendingSndList, &SndEntry->Entry); + + return SndEntry; + +ON_ERROR: + + if (OverrideData != NULL) { + FreePool (OverrideData); + } + + if (TxData != NULL) { + FreePool (TxData); + } + + if (SndEntry != NULL) { + FreePool (SndEntry); + } + + if (Event != NULL) { + gBS->CloseEvent (Event); + } + + return NULL; +} + + +/** + Destroy the SndEntry. + + This function pairs with IpIoCreateSndEntry(). + + @param[in] SndEntry Pointer to the send entry to be destroyed. + +**/ +VOID +IpIoDestroySndEntry ( + IN IP_IO_SEND_ENTRY *SndEntry + ) +{ + EFI_EVENT Event; + IP_IO_IP_TX_DATA *TxData; + IP_IO_OVERRIDE *Override; + + if (SndEntry->IpIo->IpVersion == IP_VERSION_4) { + Event = SndEntry->SndToken.Ip4Token.Event; + TxData = (IP_IO_IP_TX_DATA *) SndEntry->SndToken.Ip4Token.Packet.TxData; + Override = (IP_IO_OVERRIDE *) TxData->Ip4TxData.OverrideData; + } else if (SndEntry->IpIo->IpVersion == IP_VERSION_6) { + Event = SndEntry->SndToken.Ip6Token.Event; + TxData = (IP_IO_IP_TX_DATA *) SndEntry->SndToken.Ip6Token.Packet.TxData; + Override = (IP_IO_OVERRIDE *) TxData->Ip6TxData.OverrideData; + } else { + return ; + } + + gBS->CloseEvent (Event); + + FreePool (TxData); + + if (NULL != Override) { + FreePool (Override); + } + + NetbufFree (SndEntry->Pkt); + + RemoveEntryList (&SndEntry->Entry); + + FreePool (SndEntry); +} + + +/** + Notify function for IP transmit token. + + @param[in] Context The context passed in by the event notifier. + +**/ +VOID +EFIAPI +IpIoTransmitHandlerDpc ( + IN VOID *Context + ) +{ + IP_IO *IpIo; + IP_IO_SEND_ENTRY *SndEntry; + EFI_STATUS Status; + + SndEntry = (IP_IO_SEND_ENTRY *) Context; + + IpIo = SndEntry->IpIo; + + if (IpIo->IpVersion == IP_VERSION_4) { + Status = SndEntry->SndToken.Ip4Token.Status; + } else if (IpIo->IpVersion == IP_VERSION_6){ + Status = SndEntry->SndToken.Ip6Token.Status; + } else { + return ; + } + + if ((IpIo->PktSentNotify != NULL) && (SndEntry->NotifyData != NULL)) { + IpIo->PktSentNotify ( + Status, + SndEntry->Context, + SndEntry->Ip, + SndEntry->NotifyData + ); + } + + IpIoDestroySndEntry (SndEntry); +} + + +/** + Notify function for IP transmit token. + + @param[in] Event The event signaled. + @param[in] Context The context passed in by the event notifier. + +**/ +VOID +EFIAPI +IpIoTransmitHandler ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + // + // Request IpIoTransmitHandlerDpc as a DPC at TPL_CALLBACK + // + QueueDpc (TPL_CALLBACK, IpIoTransmitHandlerDpc, Context); +} + + +/** + The dummy handler for the dummy IP receive token. + + @param[in] Context The context passed in by the event notifier. + +**/ +VOID +EFIAPI +IpIoDummyHandlerDpc ( + IN VOID *Context + ) +{ + IP_IO_IP_INFO *IpInfo; + EFI_STATUS Status; + EFI_EVENT RecycleEvent; + + IpInfo = (IP_IO_IP_INFO *) Context; + + if ((IpInfo->IpVersion != IP_VERSION_4) && (IpInfo->IpVersion != IP_VERSION_6)) { + return ; + } + + RecycleEvent = NULL; + + if (IpInfo->IpVersion == IP_VERSION_4) { + Status = IpInfo->DummyRcvToken.Ip4Token.Status; + + if (IpInfo->DummyRcvToken.Ip4Token.Packet.RxData != NULL) { + RecycleEvent = IpInfo->DummyRcvToken.Ip4Token.Packet.RxData->RecycleSignal; + } + } else { + Status = IpInfo->DummyRcvToken.Ip6Token.Status; + + if (IpInfo->DummyRcvToken.Ip6Token.Packet.RxData != NULL) { + RecycleEvent = IpInfo->DummyRcvToken.Ip6Token.Packet.RxData->RecycleSignal; + } + } + + + + if (EFI_ABORTED == Status) { + // + // The reception is actively aborted by the consumer, directly return. + // + return; + } else if (EFI_SUCCESS == Status) { + // + // Recycle the RxData. + // + ASSERT (RecycleEvent != NULL); + + gBS->SignalEvent (RecycleEvent); + } + + // + // Continue the receive. + // + if (IpInfo->IpVersion == IP_VERSION_4) { + IpInfo->Ip.Ip4->Receive ( + IpInfo->Ip.Ip4, + &IpInfo->DummyRcvToken.Ip4Token + ); + } else { + IpInfo->Ip.Ip6->Receive ( + IpInfo->Ip.Ip6, + &IpInfo->DummyRcvToken.Ip6Token + ); + } +} + + +/** + This function add IpIoDummyHandlerDpc to the end of the DPC queue. + + @param[in] Event The event signaled. + @param[in] Context The context passed in by the event notifier. + +**/ +VOID +EFIAPI +IpIoDummyHandler ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + // + // Request IpIoDummyHandlerDpc as a DPC at TPL_CALLBACK + // + QueueDpc (TPL_CALLBACK, IpIoDummyHandlerDpc, Context); +} + + +/** + Notify function for the IP receive token, used to process + the received IP packets. + + @param[in] Context The context passed in by the event notifier. + +**/ +VOID +EFIAPI +IpIoListenHandlerDpc ( + IN VOID *Context + ) +{ + IP_IO *IpIo; + EFI_STATUS Status; + IP_IO_IP_RX_DATA *RxData; + EFI_NET_SESSION_DATA Session; + NET_BUF *Pkt; + + IpIo = (IP_IO *) Context; + + if (IpIo->IpVersion == IP_VERSION_4) { + Status = IpIo->RcvToken.Ip4Token.Status; + RxData = (IP_IO_IP_RX_DATA *) IpIo->RcvToken.Ip4Token.Packet.RxData; + } else if (IpIo->IpVersion == IP_VERSION_6) { + Status = IpIo->RcvToken.Ip6Token.Status; + RxData = (IP_IO_IP_RX_DATA *) IpIo->RcvToken.Ip6Token.Packet.RxData; + } else { + return; + } + + if (EFI_ABORTED == Status) { + // + // The reception is actively aborted by the consumer, directly return. + // + return; + } + + if ((EFI_SUCCESS != Status) && (EFI_ICMP_ERROR != Status)) { + // + // Only process the normal packets and the icmp error packets. + // + if (RxData != NULL) { + goto CleanUp; + } else { + goto Resume; + } + } + + // + // if RxData is NULL with Status == EFI_SUCCESS or EFI_ICMP_ERROR, this should be a code issue in the low layer (IP). + // + ASSERT (RxData != NULL); + if (RxData == NULL) { + goto Resume; + } + + if (NULL == IpIo->PktRcvdNotify) { + goto CleanUp; + } + + if (IpIo->IpVersion == IP_VERSION_4) { + ASSERT (RxData->Ip4RxData.Header != NULL); + if (IP4_IS_LOCAL_BROADCAST (EFI_IP4 (RxData->Ip4RxData.Header->SourceAddress))) { + // + // The source address is a broadcast address, discard it. + // + goto CleanUp; + } + if ((EFI_IP4 (RxData->Ip4RxData.Header->SourceAddress) != 0) && + (IpIo->SubnetMask != 0) && + IP4_NET_EQUAL (IpIo->StationIp, EFI_NTOHL (((EFI_IP4_RECEIVE_DATA *) RxData)->Header->SourceAddress), IpIo->SubnetMask) && + !NetIp4IsUnicast (EFI_NTOHL (((EFI_IP4_RECEIVE_DATA *) RxData)->Header->SourceAddress), IpIo->SubnetMask)) { + // + // The source address doesn't match StationIp and it's not a unicast IP address, discard it. + // + goto CleanUp; + } + + if (RxData->Ip4RxData.DataLength == 0) { + // + // Discard zero length data payload packet. + // + goto CleanUp; + } + + // + // The fragment should always be valid for non-zero length packet. + // + ASSERT (RxData->Ip4RxData.FragmentCount != 0); + + // + // Create a netbuffer representing IPv4 packet + // + Pkt = NetbufFromExt ( + (NET_FRAGMENT *) RxData->Ip4RxData.FragmentTable, + RxData->Ip4RxData.FragmentCount, + 0, + 0, + IpIoExtFree, + RxData->Ip4RxData.RecycleSignal + ); + if (NULL == Pkt) { + goto CleanUp; + } + + // + // Create a net session + // + Session.Source.Addr[0] = EFI_IP4 (RxData->Ip4RxData.Header->SourceAddress); + Session.Dest.Addr[0] = EFI_IP4 (RxData->Ip4RxData.Header->DestinationAddress); + Session.IpHdr.Ip4Hdr = RxData->Ip4RxData.Header; + Session.IpHdrLen = RxData->Ip4RxData.HeaderLength; + Session.IpVersion = IP_VERSION_4; + } else { + ASSERT (RxData->Ip6RxData.Header != NULL); + if (!NetIp6IsValidUnicast(&RxData->Ip6RxData.Header->SourceAddress)) { + goto CleanUp; + } + + if (RxData->Ip6RxData.DataLength == 0) { + // + // Discard zero length data payload packet. + // + goto CleanUp; + } + + // + // The fragment should always be valid for non-zero length packet. + // + ASSERT (RxData->Ip6RxData.FragmentCount != 0); + + // + // Create a netbuffer representing IPv6 packet + // + Pkt = NetbufFromExt ( + (NET_FRAGMENT *) RxData->Ip6RxData.FragmentTable, + RxData->Ip6RxData.FragmentCount, + 0, + 0, + IpIoExtFree, + RxData->Ip6RxData.RecycleSignal + ); + if (NULL == Pkt) { + goto CleanUp; + } + + // + // Create a net session + // + CopyMem ( + &Session.Source, + &RxData->Ip6RxData.Header->SourceAddress, + sizeof(EFI_IPv6_ADDRESS) + ); + CopyMem ( + &Session.Dest, + &RxData->Ip6RxData.Header->DestinationAddress, + sizeof(EFI_IPv6_ADDRESS) + ); + Session.IpHdr.Ip6Hdr = RxData->Ip6RxData.Header; + Session.IpHdrLen = RxData->Ip6RxData.HeaderLength; + Session.IpVersion = IP_VERSION_6; + } + + if (EFI_SUCCESS == Status) { + + IpIo->PktRcvdNotify (EFI_SUCCESS, 0, &Session, Pkt, IpIo->RcvdContext); + } else { + // + // Status is EFI_ICMP_ERROR + // + Status = IpIoIcmpHandler (IpIo, Pkt, &Session); + if (EFI_ERROR (Status)) { + NetbufFree (Pkt); + } + } + + goto Resume; + +CleanUp: + + if (IpIo->IpVersion == IP_VERSION_4){ + gBS->SignalEvent (RxData->Ip4RxData.RecycleSignal); + } else { + gBS->SignalEvent (RxData->Ip6RxData.RecycleSignal); + } + +Resume: + + if (IpIo->IpVersion == IP_VERSION_4){ + IpIo->Ip.Ip4->Receive (IpIo->Ip.Ip4, &(IpIo->RcvToken.Ip4Token)); + } else { + IpIo->Ip.Ip6->Receive (IpIo->Ip.Ip6, &(IpIo->RcvToken.Ip6Token)); + } +} + +/** + This function add IpIoListenHandlerDpc to the end of the DPC queue. + + @param[in] Event The event signaled. + @param[in] Context The context passed in by the event notifier. + +**/ +VOID +EFIAPI +IpIoListenHandler ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + // + // Request IpIoListenHandlerDpc as a DPC at TPL_CALLBACK + // + QueueDpc (TPL_CALLBACK, IpIoListenHandlerDpc, Context); +} + + +/** + Create a new IP_IO instance. + + If IpVersion is not IP_VERSION_4 or IP_VERSION_6, then ASSERT(). + + This function uses IP4/IP6 service binding protocol in Controller to create + an IP4/IP6 child (aka IP4/IP6 instance). + + @param[in] Image The image handle of the driver or application that + consumes IP_IO. + @param[in] Controller The controller handle that has IP4 or IP6 service + binding protocol installed. + @param[in] IpVersion The version of the IP protocol to use, either + IPv4 or IPv6. + + @return Pointer to a newly created IP_IO instance, or NULL if failed. + +**/ +IP_IO * +EFIAPI +IpIoCreate ( + IN EFI_HANDLE Image, + IN EFI_HANDLE Controller, + IN UINT8 IpVersion + ) +{ + EFI_STATUS Status; + IP_IO *IpIo; + EFI_EVENT Event; + + ASSERT ((IpVersion == IP_VERSION_4) || (IpVersion == IP_VERSION_6)); + + IpIo = AllocateZeroPool (sizeof (IP_IO)); + if (NULL == IpIo) { + return NULL; + } + + InitializeListHead (&(IpIo->PendingSndList)); + InitializeListHead (&(IpIo->IpList)); + IpIo->Controller = Controller; + IpIo->Image = Image; + IpIo->IpVersion = IpVersion; + Event = NULL; + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + IpIoListenHandler, + IpIo, + &Event + ); + if (EFI_ERROR (Status)) { + goto ReleaseIpIo; + } + + if (IpVersion == IP_VERSION_4) { + IpIo->RcvToken.Ip4Token.Event = Event; + } else { + IpIo->RcvToken.Ip6Token.Event = Event; + } + + // + // Create an IP child and open IP protocol + // + Status = IpIoCreateIpChildOpenProtocol ( + Controller, + Image, + &IpIo->ChildHandle, + IpVersion, + (VOID **) & (IpIo->Ip) + ); + if (EFI_ERROR (Status)) { + goto ReleaseIpIo; + } + + return IpIo; + +ReleaseIpIo: + + if (Event != NULL) { + gBS->CloseEvent (Event); + } + + gBS->FreePool (IpIo); + + return NULL; +} + + +/** + Open an IP_IO instance for use. + + If Ip version is not IP_VERSION_4 or IP_VERSION_6, then ASSERT(). + + This function is called after IpIoCreate(). It is used for configuring the IP + instance and register the callbacks and their context data for sending and + receiving IP packets. + + @param[in, out] IpIo Pointer to an IP_IO instance that needs + to open. + @param[in] OpenData The configuration data and callbacks for + the IP_IO instance. + + @retval EFI_SUCCESS The IP_IO instance opened with OpenData + successfully. + @retval EFI_ACCESS_DENIED The IP_IO instance is configured, avoid to + reopen it. + @retval EFI_UNSUPPORTED IPv4 RawData mode is no supported. + @retval EFI_INVALID_PARAMETER Invalid input parameter. + @retval Others Error condition occurred. + +**/ +EFI_STATUS +EFIAPI +IpIoOpen ( + IN OUT IP_IO *IpIo, + IN IP_IO_OPEN_DATA *OpenData + ) +{ + EFI_STATUS Status; + UINT8 IpVersion; + + if (IpIo == NULL || OpenData == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (IpIo->IsConfigured) { + return EFI_ACCESS_DENIED; + } + + IpVersion = IpIo->IpVersion; + + ASSERT ((IpVersion == IP_VERSION_4) || (IpVersion == IP_VERSION_6)); + + // + // configure ip + // + if (IpVersion == IP_VERSION_4){ + // + // RawData mode is no supported. + // + ASSERT (!OpenData->IpConfigData.Ip4CfgData.RawData); + if (OpenData->IpConfigData.Ip4CfgData.RawData) { + return EFI_UNSUPPORTED; + } + + if (!OpenData->IpConfigData.Ip4CfgData.UseDefaultAddress) { + IpIo->StationIp = EFI_NTOHL (OpenData->IpConfigData.Ip4CfgData.StationAddress); + IpIo->SubnetMask = EFI_NTOHL (OpenData->IpConfigData.Ip4CfgData.SubnetMask); + } + + Status = IpIo->Ip.Ip4->Configure ( + IpIo->Ip.Ip4, + &OpenData->IpConfigData.Ip4CfgData + ); + } else { + + Status = IpIo->Ip.Ip6->Configure ( + IpIo->Ip.Ip6, + &OpenData->IpConfigData.Ip6CfgData + ); + } + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // @bug To delete the default route entry in this Ip, if it is: + // @bug (0.0.0.0, 0.0.0.0, 0.0.0.0). Delete this statement if Ip modified + // @bug its code + // + if (IpVersion == IP_VERSION_4){ + Status = IpIo->Ip.Ip4->Routes ( + IpIo->Ip.Ip4, + TRUE, + &mZeroIp4Addr, + &mZeroIp4Addr, + &mZeroIp4Addr + ); + + if (EFI_ERROR (Status) && (EFI_NOT_FOUND != Status)) { + return Status; + } + } + + IpIo->PktRcvdNotify = OpenData->PktRcvdNotify; + IpIo->PktSentNotify = OpenData->PktSentNotify; + + IpIo->RcvdContext = OpenData->RcvdContext; + IpIo->SndContext = OpenData->SndContext; + + if (IpVersion == IP_VERSION_4){ + IpIo->Protocol = OpenData->IpConfigData.Ip4CfgData.DefaultProtocol; + + // + // start to listen incoming packet + // + Status = IpIo->Ip.Ip4->Receive ( + IpIo->Ip.Ip4, + &(IpIo->RcvToken.Ip4Token) + ); + if (EFI_ERROR (Status)) { + IpIo->Ip.Ip4->Configure (IpIo->Ip.Ip4, NULL); + return Status; + } + + } else { + + IpIo->Protocol = OpenData->IpConfigData.Ip6CfgData.DefaultProtocol; + Status = IpIo->Ip.Ip6->Receive ( + IpIo->Ip.Ip6, + &(IpIo->RcvToken.Ip6Token) + ); + if (EFI_ERROR (Status)) { + IpIo->Ip.Ip6->Configure (IpIo->Ip.Ip6, NULL); + return Status; + } + } + + IpIo->IsConfigured = TRUE; + InsertTailList (&mActiveIpIoList, &IpIo->Entry); + + return Status; +} + + +/** + Stop an IP_IO instance. + + If Ip version is not IP_VERSION_4 or IP_VERSION_6, then ASSERT(). + + This function is paired with IpIoOpen(). The IP_IO will be unconfigured and all + the pending send/receive tokens will be canceled. + + @param[in, out] IpIo Pointer to the IP_IO instance that needs to stop. + + @retval EFI_SUCCESS The IP_IO instance stopped successfully. + @retval EFI_INVALID_PARAMETER Invalid input parameter. + @retval Others Error condition occurred. + +**/ +EFI_STATUS +EFIAPI +IpIoStop ( + IN OUT IP_IO *IpIo + ) +{ + EFI_STATUS Status; + IP_IO_IP_INFO *IpInfo; + UINT8 IpVersion; + + if (IpIo == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (!IpIo->IsConfigured) { + return EFI_SUCCESS; + } + + IpVersion = IpIo->IpVersion; + + ASSERT ((IpVersion == IP_VERSION_4) || (IpVersion == IP_VERSION_6)); + + // + // Remove the IpIo from the active IpIo list. + // + RemoveEntryList (&IpIo->Entry); + + // + // Configure NULL Ip + // + if (IpVersion == IP_VERSION_4) { + Status = IpIo->Ip.Ip4->Configure (IpIo->Ip.Ip4, NULL); + } else { + Status = IpIo->Ip.Ip6->Configure (IpIo->Ip.Ip6, NULL); + } + if (EFI_ERROR (Status)) { + return Status; + } + + IpIo->IsConfigured = FALSE; + + // + // Destroy the Ip List used by IpIo + // + + while (!IsListEmpty (&(IpIo->IpList))) { + IpInfo = NET_LIST_HEAD (&(IpIo->IpList), IP_IO_IP_INFO, Entry); + + IpIoRemoveIp (IpIo, IpInfo); + } + + // + // All pending send tokens should be flushed by resetting the IP instances. + // + ASSERT (IsListEmpty (&IpIo->PendingSndList)); + + // + // Close the receive event. + // + if (IpVersion == IP_VERSION_4){ + gBS->CloseEvent (IpIo->RcvToken.Ip4Token.Event); + } else { + gBS->CloseEvent (IpIo->RcvToken.Ip6Token.Event); + } + + return EFI_SUCCESS; +} + + +/** + Destroy an IP_IO instance. + + This function is paired with IpIoCreate(). The IP_IO will be closed first. + Resource will be freed afterwards. See IpIoCloseProtocolDestroyIpChild(). + + @param[in, out] IpIo Pointer to the IP_IO instance that needs to be + destroyed. + + @retval EFI_SUCCESS The IP_IO instance destroyed successfully. + @retval Others Error condition occurred. + +**/ +EFI_STATUS +EFIAPI +IpIoDestroy ( + IN OUT IP_IO *IpIo + ) +{ + EFI_STATUS Status; + + // + // Stop the IpIo. + // + Status = IpIoStop (IpIo); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Close the IP protocol and destroy the child. + // + Status = IpIoCloseProtocolDestroyIpChild ( + IpIo->Controller, + IpIo->Image, + IpIo->ChildHandle, + IpIo->IpVersion + ); + if (EFI_ERROR (Status)) { + return Status; + } + + gBS->FreePool (IpIo); + + return EFI_SUCCESS; +} + + +/** + Send out an IP packet. + + This function is called after IpIoOpen(). The data to be sent is wrapped in + Pkt. The IP instance wrapped in IpIo is used for sending by default but can be + overridden by Sender. Other sending configs, like source address and gateway + address etc., are specified in OverrideData. + + @param[in, out] IpIo Pointer to an IP_IO instance used for sending IP + packet. + @param[in, out] Pkt Pointer to the IP packet to be sent. + @param[in] Sender The IP protocol instance used for sending. + @param[in] Context Optional context data. + @param[in] NotifyData Optional notify data. + @param[in] Dest The destination IP address to send this packet to. + This parameter is optional when using IPv6. + @param[in] OverrideData The data to override some configuration of the IP + instance used for sending. + + @retval EFI_SUCCESS The operation is completed successfully. + @retval EFI_INVALID_PARAMETER The input parameter is not correct. + @retval EFI_NOT_STARTED The IpIo is not configured. + @retval EFI_OUT_OF_RESOURCES Failed due to resource limit. + @retval Others Error condition occurred. + +**/ +EFI_STATUS +EFIAPI +IpIoSend ( + IN OUT IP_IO *IpIo, + IN OUT NET_BUF *Pkt, + IN IP_IO_IP_INFO *Sender OPTIONAL, + IN VOID *Context OPTIONAL, + IN VOID *NotifyData OPTIONAL, + IN EFI_IP_ADDRESS *Dest OPTIONAL, + IN IP_IO_OVERRIDE *OverrideData OPTIONAL + ) +{ + EFI_STATUS Status; + IP_IO_IP_PROTOCOL Ip; + IP_IO_SEND_ENTRY *SndEntry; + + if ((IpIo == NULL) || (Pkt == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if ((IpIo->IpVersion == IP_VERSION_4) && (Dest == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (!IpIo->IsConfigured) { + return EFI_NOT_STARTED; + } + + Ip = (NULL == Sender) ? IpIo->Ip : Sender->Ip; + + // + // create a new SndEntry + // + SndEntry = IpIoCreateSndEntry (IpIo, Pkt, Ip, Context, NotifyData, Dest, OverrideData); + if (NULL == SndEntry) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Send this Packet + // + if (IpIo->IpVersion == IP_VERSION_4){ + Status = Ip.Ip4->Transmit ( + Ip.Ip4, + &SndEntry->SndToken.Ip4Token + ); + } else { + Status = Ip.Ip6->Transmit ( + Ip.Ip6, + &SndEntry->SndToken.Ip6Token + ); + } + + if (EFI_ERROR (Status)) { + IpIoDestroySndEntry (SndEntry); + } + + return Status; +} + + +/** + Cancel the IP transmit token which wraps this Packet. + + If IpIo is NULL, then ASSERT(). + If Packet is NULL, then ASSERT(). + + @param[in] IpIo Pointer to the IP_IO instance. + @param[in] Packet Pointer to the packet of NET_BUF to cancel. + +**/ +VOID +EFIAPI +IpIoCancelTxToken ( + IN IP_IO *IpIo, + IN VOID *Packet + ) +{ + LIST_ENTRY *Node; + IP_IO_SEND_ENTRY *SndEntry; + IP_IO_IP_PROTOCOL Ip; + + ASSERT ((IpIo != NULL) && (Packet != NULL)); + + NET_LIST_FOR_EACH (Node, &IpIo->PendingSndList) { + + SndEntry = NET_LIST_USER_STRUCT (Node, IP_IO_SEND_ENTRY, Entry); + + if (SndEntry->Pkt == Packet) { + + Ip = SndEntry->Ip; + + if (IpIo->IpVersion == IP_VERSION_4) { + Ip.Ip4->Cancel ( + Ip.Ip4, + &SndEntry->SndToken.Ip4Token + ); + } else { + Ip.Ip6->Cancel ( + Ip.Ip6, + &SndEntry->SndToken.Ip6Token + ); + } + + break; + } + } + +} + + +/** + Add a new IP instance for sending data. + + If IpIo is NULL, then ASSERT(). + If Ip version is not IP_VERSION_4 or IP_VERSION_6, then ASSERT(). + + The function is used to add the IP_IO to the IP_IO sending list. The caller + can later use IpIoFindSender() to get the IP_IO and call IpIoSend() to send + data. + + @param[in, out] IpIo Pointer to a IP_IO instance to add a new IP + instance for sending purpose. + + @return Pointer to the created IP_IO_IP_INFO structure, NULL if failed. + +**/ +IP_IO_IP_INFO * +EFIAPI +IpIoAddIp ( + IN OUT IP_IO *IpIo + ) +{ + EFI_STATUS Status; + IP_IO_IP_INFO *IpInfo; + EFI_EVENT Event; + + ASSERT (IpIo != NULL); + ASSERT ((IpIo->IpVersion == IP_VERSION_4) || (IpIo->IpVersion == IP_VERSION_6)); + + IpInfo = AllocatePool (sizeof (IP_IO_IP_INFO)); + if (IpInfo == NULL) { + return NULL; + } + + // + // Init this IpInfo, set the Addr and SubnetMask to 0 before we configure the IP + // instance. + // + InitializeListHead (&IpInfo->Entry); + IpInfo->ChildHandle = NULL; + ZeroMem (&IpInfo->Addr, sizeof (IpInfo->Addr)); + ZeroMem (&IpInfo->PreMask, sizeof (IpInfo->PreMask)); + + IpInfo->RefCnt = 1; + IpInfo->IpVersion = IpIo->IpVersion; + + // + // Create the IP instance and open the IP protocol. + // + Status = IpIoCreateIpChildOpenProtocol ( + IpIo->Controller, + IpIo->Image, + &IpInfo->ChildHandle, + IpInfo->IpVersion, + (VOID **) &IpInfo->Ip + ); + if (EFI_ERROR (Status)) { + goto ReleaseIpInfo; + } + + // + // Create the event for the DummyRcvToken. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + IpIoDummyHandler, + IpInfo, + &Event + ); + if (EFI_ERROR (Status)) { + goto ReleaseIpChild; + } + + if (IpInfo->IpVersion == IP_VERSION_4) { + IpInfo->DummyRcvToken.Ip4Token.Event = Event; + } else { + IpInfo->DummyRcvToken.Ip6Token.Event = Event; + } + + // + // Link this IpInfo into the IpIo. + // + InsertTailList (&IpIo->IpList, &IpInfo->Entry); + + return IpInfo; + +ReleaseIpChild: + + IpIoCloseProtocolDestroyIpChild ( + IpIo->Controller, + IpIo->Image, + IpInfo->ChildHandle, + IpInfo->IpVersion + ); + +ReleaseIpInfo: + + gBS->FreePool (IpInfo); + + return NULL; +} + + +/** + Configure the IP instance of this IpInfo and start the receiving if IpConfigData + is not NULL. + + If IpInfo is NULL, then ASSERT(). + If Ip version is not IP_VERSION_4 or IP_VERSION_6, then ASSERT(). + + @param[in, out] IpInfo Pointer to the IP_IO_IP_INFO instance. + @param[in, out] IpConfigData The IP configure data used to configure the IP + instance, if NULL the IP instance is reset. If + UseDefaultAddress is set to TRUE, and the configure + operation succeeds, the default address information + is written back in this IpConfigData. + + @retval EFI_SUCCESS The IP instance of this IpInfo is configured successfully + or no need to reconfigure it. + @retval Others Configuration fails. + +**/ +EFI_STATUS +EFIAPI +IpIoConfigIp ( + IN OUT IP_IO_IP_INFO *IpInfo, + IN OUT VOID *IpConfigData OPTIONAL + ) +{ + EFI_STATUS Status; + IP_IO_IP_PROTOCOL Ip; + UINT8 IpVersion; + EFI_IP4_MODE_DATA Ip4ModeData; + EFI_IP6_MODE_DATA Ip6ModeData; + + ASSERT (IpInfo != NULL); + + if (IpInfo->RefCnt > 1) { + // + // This IP instance is shared, don't reconfigure it until it has only one + // consumer. Currently, only the tcp children cloned from their passive parent + // will share the same IP. So this cases only happens while IpConfigData is NULL, + // let the last consumer clean the IP instance. + // + return EFI_SUCCESS; + } + + IpVersion = IpInfo->IpVersion; + ASSERT ((IpVersion == IP_VERSION_4) || (IpVersion == IP_VERSION_6)); + + Ip = IpInfo->Ip; + + if (IpInfo->IpVersion == IP_VERSION_4) { + Status = Ip.Ip4->Configure (Ip.Ip4, IpConfigData); + } else { + Status = Ip.Ip6->Configure (Ip.Ip6, IpConfigData); + } + + if (EFI_ERROR (Status)) { + return Status; + } + + if (IpConfigData != NULL) { + if (IpInfo->IpVersion == IP_VERSION_4) { + + if (((EFI_IP4_CONFIG_DATA *) IpConfigData)->UseDefaultAddress) { + Status = Ip.Ip4->GetModeData ( + Ip.Ip4, + &Ip4ModeData, + NULL, + NULL + ); + if (EFI_ERROR (Status)) { + Ip.Ip4->Configure (Ip.Ip4, NULL); + return Status; + } + + IP4_COPY_ADDRESS (&((EFI_IP4_CONFIG_DATA*) IpConfigData)->StationAddress, &Ip4ModeData.ConfigData.StationAddress); + IP4_COPY_ADDRESS (&((EFI_IP4_CONFIG_DATA*) IpConfigData)->SubnetMask, &Ip4ModeData.ConfigData.SubnetMask); + } + + CopyMem ( + &IpInfo->Addr.Addr, + &((EFI_IP4_CONFIG_DATA *) IpConfigData)->StationAddress, + sizeof (IP4_ADDR) + ); + CopyMem ( + &IpInfo->PreMask.SubnetMask, + &((EFI_IP4_CONFIG_DATA *) IpConfigData)->SubnetMask, + sizeof (IP4_ADDR) + ); + + Status = Ip.Ip4->Receive ( + Ip.Ip4, + &IpInfo->DummyRcvToken.Ip4Token + ); + if (EFI_ERROR (Status)) { + Ip.Ip4->Configure (Ip.Ip4, NULL); + } + } else { + Status = Ip.Ip6->GetModeData ( + Ip.Ip6, + &Ip6ModeData, + NULL, + NULL + ); + if (EFI_ERROR (Status)) { + Ip.Ip6->Configure (Ip.Ip6, NULL); + return Status; + } + + if (Ip6ModeData.IsConfigured) { + CopyMem ( + &((EFI_IP6_CONFIG_DATA *) IpConfigData)->StationAddress, + &Ip6ModeData.ConfigData.StationAddress, + sizeof (EFI_IPv6_ADDRESS) + ); + + if (Ip6ModeData.AddressList != NULL) { + FreePool (Ip6ModeData.AddressList); + } + + if (Ip6ModeData.GroupTable != NULL) { + FreePool (Ip6ModeData.GroupTable); + } + + if (Ip6ModeData.RouteTable != NULL) { + FreePool (Ip6ModeData.RouteTable); + } + + if (Ip6ModeData.NeighborCache != NULL) { + FreePool (Ip6ModeData.NeighborCache); + } + + if (Ip6ModeData.PrefixTable != NULL) { + FreePool (Ip6ModeData.PrefixTable); + } + + if (Ip6ModeData.IcmpTypeList != NULL) { + FreePool (Ip6ModeData.IcmpTypeList); + } + + } else { + Status = EFI_NO_MAPPING; + return Status; + } + + CopyMem ( + &IpInfo->Addr, + &Ip6ModeData.ConfigData.StationAddress, + sizeof (EFI_IPv6_ADDRESS) + ); + + Status = Ip.Ip6->Receive ( + Ip.Ip6, + &IpInfo->DummyRcvToken.Ip6Token + ); + if (EFI_ERROR (Status)) { + Ip.Ip6->Configure (Ip.Ip6, NULL); + } + } + } else { + // + // The IP instance is reset, set the stored Addr and SubnetMask to zero. + // + ZeroMem (&IpInfo->Addr, sizeof (IpInfo->Addr)); + ZeroMem (&IpInfo->PreMask, sizeof (IpInfo->PreMask)); + } + + return Status; +} + + +/** + Destroy an IP instance maintained in IpIo->IpList for + sending purpose. + + If Ip version is not IP_VERSION_4 or IP_VERSION_6, then ASSERT(). + + This function pairs with IpIoAddIp(). The IpInfo is previously created by + IpIoAddIp(). The IP_IO_IP_INFO::RefCnt is decremented and the IP instance + will be destroyed if the RefCnt is zero. + + @param[in] IpIo Pointer to the IP_IO instance. + @param[in] IpInfo Pointer to the IpInfo to be removed. + +**/ +VOID +EFIAPI +IpIoRemoveIp ( + IN IP_IO *IpIo, + IN IP_IO_IP_INFO *IpInfo + ) +{ + + UINT8 IpVersion; + + if (IpIo == NULL || IpInfo == NULL) { + return; + } + + ASSERT (IpInfo->RefCnt > 0); + + NET_PUT_REF (IpInfo); + + if (IpInfo->RefCnt > 0) { + + return; + } + + IpVersion = IpIo->IpVersion; + + ASSERT ((IpVersion == IP_VERSION_4) || (IpVersion == IP_VERSION_6)); + + RemoveEntryList (&IpInfo->Entry); + + if (IpVersion == IP_VERSION_4){ + IpInfo->Ip.Ip4->Configure ( + IpInfo->Ip.Ip4, + NULL + ); + IpIoCloseProtocolDestroyIpChild ( + IpIo->Controller, + IpIo->Image, + IpInfo->ChildHandle, + IP_VERSION_4 + ); + + gBS->CloseEvent (IpInfo->DummyRcvToken.Ip4Token.Event); + + } else { + + IpInfo->Ip.Ip6->Configure ( + IpInfo->Ip.Ip6, + NULL + ); + + IpIoCloseProtocolDestroyIpChild ( + IpIo->Controller, + IpIo->Image, + IpInfo->ChildHandle, + IP_VERSION_6 + ); + + gBS->CloseEvent (IpInfo->DummyRcvToken.Ip6Token.Event); + } + + FreePool (IpInfo); +} + + +/** + Find the first IP protocol maintained in IpIo whose local + address is the same as Src. + + This function is called when the caller needs the IpIo to send data to the + specified Src. The IpIo was added previously by IpIoAddIp(). + + @param[in, out] IpIo Pointer to the pointer of the IP_IO instance. + @param[in] IpVersion The version of the IP protocol to use, either + IPv4 or IPv6. + @param[in] Src The local IP address. + + @return Pointer to the IP protocol can be used for sending purpose and its local + address is the same with Src. NULL if failed. + +**/ +IP_IO_IP_INFO * +EFIAPI +IpIoFindSender ( + IN OUT IP_IO **IpIo, + IN UINT8 IpVersion, + IN EFI_IP_ADDRESS *Src + ) +{ + LIST_ENTRY *IpIoEntry; + IP_IO *IpIoPtr; + LIST_ENTRY *IpInfoEntry; + IP_IO_IP_INFO *IpInfo; + + if (IpIo == NULL || Src == NULL) { + return NULL; + } + + if ((IpVersion != IP_VERSION_4) && (IpVersion != IP_VERSION_6)) { + return NULL; + } + + NET_LIST_FOR_EACH (IpIoEntry, &mActiveIpIoList) { + IpIoPtr = NET_LIST_USER_STRUCT (IpIoEntry, IP_IO, Entry); + + if (((*IpIo != NULL) && (*IpIo != IpIoPtr)) || (IpIoPtr->IpVersion != IpVersion)) { + continue; + } + + NET_LIST_FOR_EACH (IpInfoEntry, &IpIoPtr->IpList) { + IpInfo = NET_LIST_USER_STRUCT (IpInfoEntry, IP_IO_IP_INFO, Entry); + if (IpInfo->IpVersion == IP_VERSION_4){ + + if (EFI_IP4_EQUAL (&IpInfo->Addr.v4, &Src->v4)) { + *IpIo = IpIoPtr; + return IpInfo; + } + + } else { + + if (EFI_IP6_EQUAL (&IpInfo->Addr.v6, &Src->v6)) { + *IpIo = IpIoPtr; + return IpInfo; + } + } + } + } + + // + // No match. + // + return NULL; +} + + +/** + Get the ICMP error map information. + + The ErrorStatus will be returned. The IsHard and Notify are optional. If they + are not NULL, this routine will fill them. + + @param[in] IcmpError IcmpError Type. + @param[in] IpVersion The version of the IP protocol to use, + either IPv4 or IPv6. + @param[out] IsHard If TRUE, indicates that it is a hard error. + @param[out] Notify If TRUE, SockError needs to be notified. + + @retval EFI_UNSUPPORTED Unrecognizable ICMP error code. + @return ICMP Error Status, such as EFI_NETWORK_UNREACHABLE. + +**/ +EFI_STATUS +EFIAPI +IpIoGetIcmpErrStatus ( + IN UINT8 IcmpError, + IN UINT8 IpVersion, + OUT BOOLEAN *IsHard OPTIONAL, + OUT BOOLEAN *Notify OPTIONAL + ) +{ + if (IpVersion == IP_VERSION_4 ) { + ASSERT (IcmpError <= ICMP_ERR_PARAMPROB); + + if (IsHard != NULL) { + *IsHard = mIcmpErrMap[IcmpError].IsHard; + } + + if (Notify != NULL) { + *Notify = mIcmpErrMap[IcmpError].Notify; + } + + switch (IcmpError) { + case ICMP_ERR_UNREACH_NET: + return EFI_NETWORK_UNREACHABLE; + + case ICMP_ERR_TIMXCEED_INTRANS: + case ICMP_ERR_TIMXCEED_REASS: + case ICMP_ERR_UNREACH_HOST: + return EFI_HOST_UNREACHABLE; + + case ICMP_ERR_UNREACH_PROTOCOL: + return EFI_PROTOCOL_UNREACHABLE; + + case ICMP_ERR_UNREACH_PORT: + return EFI_PORT_UNREACHABLE; + + case ICMP_ERR_MSGSIZE: + case ICMP_ERR_UNREACH_SRCFAIL: + case ICMP_ERR_QUENCH: + case ICMP_ERR_PARAMPROB: + return EFI_ICMP_ERROR; + + default: + ASSERT (FALSE); + return EFI_UNSUPPORTED; + } + + } else if (IpVersion == IP_VERSION_6) { + + ASSERT (IcmpError <= ICMP6_ERR_PARAMPROB_IPV6OPTION); + + if (IsHard != NULL) { + *IsHard = mIcmp6ErrMap[IcmpError].IsHard; + } + + if (Notify != NULL) { + *Notify = mIcmp6ErrMap[IcmpError].Notify; + } + + switch (IcmpError) { + case ICMP6_ERR_UNREACH_NET: + return EFI_NETWORK_UNREACHABLE; + + case ICMP6_ERR_UNREACH_HOST: + case ICMP6_ERR_TIMXCEED_HOPLIMIT: + case ICMP6_ERR_TIMXCEED_REASS: + return EFI_HOST_UNREACHABLE; + + case ICMP6_ERR_UNREACH_PROTOCOL: + return EFI_PROTOCOL_UNREACHABLE; + + case ICMP6_ERR_UNREACH_PORT: + return EFI_PORT_UNREACHABLE; + + case ICMP6_ERR_PACKAGE_TOOBIG: + case ICMP6_ERR_PARAMPROB_HEADER: + case ICMP6_ERR_PARAMPROB_NEXHEADER: + case ICMP6_ERR_PARAMPROB_IPV6OPTION: + return EFI_ICMP_ERROR; + + default: + ASSERT (FALSE); + return EFI_UNSUPPORTED; + } + + } else { + // + // Should never be here + // + ASSERT (FALSE); + return EFI_UNSUPPORTED; + } +} + + +/** + Refresh the remote peer's Neighbor Cache entries. + + This function is called when the caller needs the IpIo to refresh the existing + IPv6 neighbor cache entries since the neighbor is considered reachable by the + node has recently received a confirmation that packets sent recently to the + neighbor were received by its IP layer. + + @param[in] IpIo Pointer to an IP_IO instance + @param[in] Neighbor The IP address of the neighbor + @param[in] Timeout Time in 100-ns units that this entry will + remain in the neighbor cache. A value of + zero means that the entry is permanent. + A value of non-zero means that the entry is + dynamic and will be deleted after Timeout. + + @retval EFI_SUCCESS The operation is completed successfully. + @retval EFI_NOT_STARTED The IpIo is not configured. + @retval EFI_INVALID_PARAMETER Neighbor Address is invalid. + @retval EFI_NOT_FOUND The neighbor cache entry is not in the + neighbor table. + @retval EFI_UNSUPPORTED IP version is IPv4, which doesn't support neighbor cache refresh. + @retval EFI_OUT_OF_RESOURCES Failed due to resource limit. + +**/ +EFI_STATUS +EFIAPI +IpIoRefreshNeighbor ( + IN IP_IO *IpIo, + IN EFI_IP_ADDRESS *Neighbor, + IN UINT32 Timeout + ) +{ + EFI_IP6_PROTOCOL *Ip; + + if (!IpIo->IsConfigured) { + return EFI_NOT_STARTED; + } + + if (IpIo->IpVersion != IP_VERSION_6) { + return EFI_UNSUPPORTED; + } + + Ip = IpIo->Ip.Ip6; + + return Ip->Neighbors (Ip, FALSE, &Neighbor->v6, NULL, Timeout, TRUE); +} + -- cgit v1.2.3