From af1a266670d040d2f4083ff309d732d648afba2a Mon Sep 17 00:00:00 2001 From: Angelos Mouzakitis Date: Tue, 10 Oct 2023 14:33:42 +0000 Subject: Add submodule dependency files Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec --- roms/edk2/NetworkPkg/Udp4Dxe/Udp4Impl.c | 1908 +++++++++++++++++++++++++++++++ 1 file changed, 1908 insertions(+) create mode 100644 roms/edk2/NetworkPkg/Udp4Dxe/Udp4Impl.c (limited to 'roms/edk2/NetworkPkg/Udp4Dxe/Udp4Impl.c') diff --git a/roms/edk2/NetworkPkg/Udp4Dxe/Udp4Impl.c b/roms/edk2/NetworkPkg/Udp4Dxe/Udp4Impl.c new file mode 100644 index 000000000..dc2a324c1 --- /dev/null +++ b/roms/edk2/NetworkPkg/Udp4Dxe/Udp4Impl.c @@ -0,0 +1,1908 @@ +/** @file + The implementation of the Udp4 protocol. + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + + +#include "Udp4Impl.h" + +UINT16 mUdp4RandomPort; + +/** + This function checks and timeouts the I/O datagrams holding by the corresponding + service context. + + @param[in] Event The event this function registered to. + @param[in] Context The context data registered during the creation of + the Event. + +**/ +VOID +EFIAPI +Udp4CheckTimeout ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + This function finds the udp instance by the specified pair. + + @param[in] InstanceList Pointer to the head of the list linking the udp + instances. + @param[in] Address Pointer to the specified IPv4 address. + @param[in] Port The udp port number. + + @retval TRUE The specified pair is found. + @retval FALSE Otherwise. + +**/ +BOOLEAN +Udp4FindInstanceByPort ( + IN LIST_ENTRY *InstanceList, + IN EFI_IPv4_ADDRESS *Address, + IN UINT16 Port + ); + +/** + This function is the packet transmitting notify function registered to the IpIo + interface. It's called to signal the udp TxToken when IpIo layer completes the + transmitting of the udp datagram. + + @param[in] Status The completion status of the output udp datagram. + @param[in] Context Pointer to the context data. + @param[in] Sender Specify a pointer of EFI_IP4_PROTOCOL for sending. + @param[in] NotifyData Pointer to the notify data. + +**/ +VOID +EFIAPI +Udp4DgramSent ( + IN EFI_STATUS Status, + IN VOID *Context, + IN IP_IO_IP_PROTOCOL Sender, + IN VOID *NotifyData + ); + +/** + This function processes the received datagram passed up by the IpIo layer. + + @param[in] Status The status of this udp datagram. + @param[in] IcmpError The IcmpError code, only available when Status is + EFI_ICMP_ERROR. + @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA. + @param[in] Packet Pointer to the NET_BUF containing the received udp + datagram. + @param[in] Context Pointer to the context data. + +**/ +VOID +EFIAPI +Udp4DgramRcvd ( + IN EFI_STATUS Status, + IN UINT8 IcmpError, + IN EFI_NET_SESSION_DATA *NetSession, + IN NET_BUF *Packet, + IN VOID *Context + ); + +/** + This function cancels the token specified by Arg in the Map. This is a callback + used by Udp4InstanceCancelToken(). + + @param[in] Map Pointer to the NET_MAP. + @param[in] Item Pointer to the NET_MAP_ITEM. + @param[in] Arg Pointer to the token to be cancelled, if NULL, + the token specified by Item is cancelled. + + @retval EFI_SUCCESS The token is cancelled if Arg is NULL or the token + is not the same as that in the Item if Arg is not + NULL. + @retval EFI_ABORTED Arg is not NULL, and the token specified by Arg is + cancelled. + +**/ +EFI_STATUS +EFIAPI +Udp4CancelTokens ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Arg OPTIONAL + ); + +/** + This function matches the received udp datagram with the Instance. + + @param[in] Instance Pointer to the udp instance context data. + @param[in] Udp4Session Pointer to the EFI_UDP4_SESSION_DATA abstracted + from the received udp datagram. + + @retval TRUE The udp datagram matches the receiving requirements of the + udp Instance. + @retval FALSE Otherwise. + +**/ +BOOLEAN +Udp4MatchDgram ( + IN UDP4_INSTANCE_DATA *Instance, + IN EFI_UDP4_SESSION_DATA *Udp4Session + ); + +/** + This function removes the Wrap specified by Context and release relevant resources. + + @param[in] Event The Event this notify function registered to. + @param[in] Context Pointer to the context data. + +**/ +VOID +EFIAPI +Udp4RecycleRxDataWrap ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + This function wraps the Packet and the RxData. + + @param[in] Instance Pointer to the instance context data. + @param[in] Packet Pointer to the buffer containing the received + datagram. + @param[in] RxData Pointer to the EFI_UDP4_RECEIVE_DATA of this + datagram. + + @return Pointer to the structure wrapping the RxData and the Packet. + +**/ +UDP4_RXDATA_WRAP * +Udp4WrapRxData ( + IN UDP4_INSTANCE_DATA *Instance, + IN NET_BUF *Packet, + IN EFI_UDP4_RECEIVE_DATA *RxData + ); + +/** + This function enqueues the received datagram into the instances' receiving queues. + + @param[in] Udp4Service Pointer to the udp service context data. + @param[in] Packet Pointer to the buffer containing the received + datagram. + @param[in] RxData Pointer to the EFI_UDP4_RECEIVE_DATA of this + datagram. + + @return The times this datagram is enqueued. + +**/ +UINTN +Udp4EnqueueDgram ( + IN UDP4_SERVICE_DATA *Udp4Service, + IN NET_BUF *Packet, + IN EFI_UDP4_RECEIVE_DATA *RxData + ); + +/** + This function delivers the datagrams enqueued in the instances. + + @param[in] Udp4Service Pointer to the udp service context data. + +**/ +VOID +Udp4DeliverDgram ( + IN UDP4_SERVICE_DATA *Udp4Service + ); + +/** + This function demultiplexes the received udp datagram to the appropriate instances. + + @param[in] Udp4Service Pointer to the udp service context data. + @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA abstracted from + the received datagram. + @param[in] Packet Pointer to the buffer containing the received udp + datagram. + +**/ +VOID +Udp4Demultiplex ( + IN UDP4_SERVICE_DATA *Udp4Service, + IN EFI_NET_SESSION_DATA *NetSession, + IN NET_BUF *Packet + ); + +/** + This function handles the received Icmp Error message and demultiplexes it to the + instance. + + @param[in] Udp4Service Pointer to the udp service context data. + @param[in] IcmpError The icmp error code. + @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA abstracted + from the received Icmp Error packet. + @param[in] Packet Pointer to the Icmp Error packet. + +**/ +VOID +Udp4IcmpHandler ( + IN UDP4_SERVICE_DATA *Udp4Service, + IN UINT8 IcmpError, + IN EFI_NET_SESSION_DATA *NetSession, + IN NET_BUF *Packet + ); + +/** + This function builds and sends out a icmp port unreachable message. + + @param[in] IpIo Pointer to the IP_IO instance. + @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA of the packet + causes this icmp error message. + @param[in] Udp4Header Pointer to the udp header of the datagram causes + this icmp error message. + +**/ +VOID +Udp4SendPortUnreach ( + IN IP_IO *IpIo, + IN EFI_NET_SESSION_DATA *NetSession, + IN VOID *Udp4Header + ); + + +/** + Create the Udp service context data. + + @param[in, out] Udp4Service Pointer to the UDP4_SERVICE_DATA. + @param[in] ImageHandle The image handle of this udp4 driver. + @param[in] ControllerHandle The controller handle this udp4 driver binds on. + + @retval EFI_SUCCESS The udp4 service context data is created and + initialized. + @retval EFI_OUT_OF_RESOURCES Cannot allocate memory. + @retval other Other error occurs. + +**/ +EFI_STATUS +Udp4CreateService ( + IN OUT UDP4_SERVICE_DATA *Udp4Service, + IN EFI_HANDLE ImageHandle, + IN EFI_HANDLE ControllerHandle + ) +{ + EFI_STATUS Status; + IP_IO_OPEN_DATA OpenData; + EFI_IP4_CONFIG_DATA *Ip4ConfigData; + + ZeroMem (Udp4Service, sizeof (UDP4_SERVICE_DATA)); + + Udp4Service->Signature = UDP4_SERVICE_DATA_SIGNATURE; + Udp4Service->ServiceBinding = mUdp4ServiceBinding; + Udp4Service->ImageHandle = ImageHandle; + Udp4Service->ControllerHandle = ControllerHandle; + Udp4Service->ChildrenNumber = 0; + + InitializeListHead (&Udp4Service->ChildrenList); + + // + // Create the IpIo for this service context. + // + Udp4Service->IpIo = IpIoCreate (ImageHandle, ControllerHandle, IP_VERSION_4); + if (Udp4Service->IpIo == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Set the OpenData used to open the IpIo. + // + Ip4ConfigData = &OpenData.IpConfigData.Ip4CfgData; + CopyMem (Ip4ConfigData, &mIp4IoDefaultIpConfigData, sizeof (EFI_IP4_CONFIG_DATA)); + Ip4ConfigData->AcceptBroadcast = TRUE; + OpenData.RcvdContext = (VOID *) Udp4Service; + OpenData.SndContext = NULL; + OpenData.PktRcvdNotify = Udp4DgramRcvd; + OpenData.PktSentNotify = Udp4DgramSent; + + // + // Configure and start the IpIo. + // + Status = IpIoOpen (Udp4Service->IpIo, &OpenData); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Create the event for Udp timeout checking. + // + Status = gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + Udp4CheckTimeout, + Udp4Service, + &Udp4Service->TimeoutEvent + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Start the timeout timer event. + // + Status = gBS->SetTimer ( + Udp4Service->TimeoutEvent, + TimerPeriodic, + UDP4_TIMEOUT_INTERVAL + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + return EFI_SUCCESS; + +ON_ERROR: + + if (Udp4Service->TimeoutEvent != NULL) { + gBS->CloseEvent (Udp4Service->TimeoutEvent); + } + + IpIoDestroy (Udp4Service->IpIo); + + return Status; +} + + +/** + Clean the Udp service context data. + + @param[in] Udp4Service Pointer to the UDP4_SERVICE_DATA. + +**/ +VOID +Udp4CleanService ( + IN UDP4_SERVICE_DATA *Udp4Service + ) +{ + // + // Cancel the TimeoutEvent timer. + // + gBS->SetTimer (Udp4Service->TimeoutEvent, TimerCancel, 0); + + // + // Close the TimeoutEvent timer. + // + gBS->CloseEvent (Udp4Service->TimeoutEvent); + + // + // Destroy the IpIo. + // + IpIoDestroy (Udp4Service->IpIo); +} + + +/** + This function checks and timeouts the I/O datagrams holding by the corresponding + service context. + + @param[in] Event The event this function registered to. + @param[in] Context The context data registered during the creation of + the Event. + +**/ +VOID +EFIAPI +Udp4CheckTimeout ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + UDP4_SERVICE_DATA *Udp4Service; + LIST_ENTRY *Entry; + UDP4_INSTANCE_DATA *Instance; + LIST_ENTRY *WrapEntry; + LIST_ENTRY *NextEntry; + UDP4_RXDATA_WRAP *Wrap; + + Udp4Service = (UDP4_SERVICE_DATA *) Context; + NET_CHECK_SIGNATURE (Udp4Service, UDP4_SERVICE_DATA_SIGNATURE); + + NET_LIST_FOR_EACH (Entry, &Udp4Service->ChildrenList) { + // + // Iterate all the instances belonging to this service context. + // + Instance = NET_LIST_USER_STRUCT (Entry, UDP4_INSTANCE_DATA, Link); + NET_CHECK_SIGNATURE (Instance, UDP4_INSTANCE_DATA_SIGNATURE); + + if (!Instance->Configured || (Instance->ConfigData.ReceiveTimeout == 0)) { + // + // Skip this instance if it's not configured or no receive timeout. + // + continue; + } + + NET_LIST_FOR_EACH_SAFE (WrapEntry, NextEntry, &Instance->RcvdDgramQue) { + // + // Iterate all the rxdatas belonging to this udp instance. + // + Wrap = NET_LIST_USER_STRUCT (WrapEntry, UDP4_RXDATA_WRAP, Link); + + // + // TimeoutTick unit is microsecond, MNP_TIMEOUT_CHECK_INTERVAL unit is 100ns. + // + if (Wrap->TimeoutTick < (UDP4_TIMEOUT_INTERVAL / 10)) { + // + // Remove this RxData if it timeouts. + // + Udp4RecycleRxDataWrap (NULL, (VOID *) Wrap); + } else { + Wrap->TimeoutTick -= (UDP4_TIMEOUT_INTERVAL / 10); + } + } + } +} + + +/** + This function initializes the new created udp instance. + + @param[in] Udp4Service Pointer to the UDP4_SERVICE_DATA. + @param[in, out] Instance Pointer to the un-initialized UDP4_INSTANCE_DATA. + +**/ +VOID +Udp4InitInstance ( + IN UDP4_SERVICE_DATA *Udp4Service, + IN OUT UDP4_INSTANCE_DATA *Instance + ) +{ + // + // Set the signature. + // + Instance->Signature = UDP4_INSTANCE_DATA_SIGNATURE; + + // + // Init the lists. + // + InitializeListHead (&Instance->Link); + InitializeListHead (&Instance->RcvdDgramQue); + InitializeListHead (&Instance->DeliveredDgramQue); + + // + // Init the NET_MAPs. + // + NetMapInit (&Instance->TxTokens); + NetMapInit (&Instance->RxTokens); + NetMapInit (&Instance->McastIps); + + // + // Save the pointer to the UDP4_SERVICE_DATA, and initialize other members. + // + Instance->Udp4Service = Udp4Service; + CopyMem (&Instance->Udp4Proto, &mUdp4Protocol, sizeof (Instance->Udp4Proto)); + Instance->IcmpError = EFI_SUCCESS; + Instance->Configured = FALSE; + Instance->IsNoMapping = FALSE; + Instance->InDestroy = FALSE; +} + + +/** + This function cleans the udp instance. + + @param[in] Instance Pointer to the UDP4_INSTANCE_DATA to clean. + +**/ +VOID +Udp4CleanInstance ( + IN UDP4_INSTANCE_DATA *Instance + ) +{ + NetMapClean (&Instance->McastIps); + NetMapClean (&Instance->RxTokens); + NetMapClean (&Instance->TxTokens); +} + + +/** + This function finds the udp instance by the specified pair. + + @param[in] InstanceList Pointer to the head of the list linking the udp + instances. + @param[in] Address Pointer to the specified IPv4 address. + @param[in] Port The udp port number. + + @retval TRUE The specified pair is found. + @retval FALSE Otherwise. + +**/ +BOOLEAN +Udp4FindInstanceByPort ( + IN LIST_ENTRY *InstanceList, + IN EFI_IPv4_ADDRESS *Address, + IN UINT16 Port + ) +{ + LIST_ENTRY *Entry; + UDP4_INSTANCE_DATA *Instance; + EFI_UDP4_CONFIG_DATA *ConfigData; + + NET_LIST_FOR_EACH (Entry, InstanceList) { + // + // Iterate all the udp instances. + // + Instance = NET_LIST_USER_STRUCT (Entry, UDP4_INSTANCE_DATA, Link); + ConfigData = &Instance->ConfigData; + + if (!Instance->Configured || ConfigData->AcceptAnyPort) { + // + // If the instance is not configured or the configdata of the instance indicates + // this instance accepts any port, skip it. + // + continue; + } + + if (EFI_IP4_EQUAL (&ConfigData->StationAddress, Address) && + (ConfigData->StationPort == Port)) { + // + // if both the address and the port are the same, return TRUE. + // + return TRUE; + } + } + + // + // return FALSE when matching fails. + // + return FALSE; +} + + +/** + This function tries to bind the udp instance according to the configured port + allocation strategy. + + @param[in] InstanceList Pointer to the head of the list linking the udp + instances. + @param[in, out] ConfigData Pointer to the ConfigData of the instance to be + bound. ConfigData->StationPort will be assigned + with an available port value on success. + + @retval EFI_SUCCESS The bound operation is completed successfully. + @retval EFI_ACCESS_DENIED The specified by the ConfigData is + already used by other instance. + @retval EFI_OUT_OF_RESOURCES No available port resources. + +**/ +EFI_STATUS +Udp4Bind ( + IN LIST_ENTRY *InstanceList, + IN OUT EFI_UDP4_CONFIG_DATA *ConfigData + ) +{ + EFI_IPv4_ADDRESS *StationAddress; + UINT16 StartPort; + + if (ConfigData->AcceptAnyPort) { + return EFI_SUCCESS; + } + + StationAddress = &ConfigData->StationAddress; + + if (ConfigData->StationPort != 0) { + + if (!ConfigData->AllowDuplicatePort && + Udp4FindInstanceByPort (InstanceList, StationAddress, ConfigData->StationPort)) { + // + // Do not allow duplicate port and the port is already used by other instance. + // + return EFI_ACCESS_DENIED; + } + } else { + // + // select a random port for this instance; + // + + if (ConfigData->AllowDuplicatePort) { + // + // Just pick up the random port if the instance allows duplicate port. + // + ConfigData->StationPort = mUdp4RandomPort; + } else { + + StartPort = mUdp4RandomPort; + + while (Udp4FindInstanceByPort(InstanceList, StationAddress, mUdp4RandomPort)) { + + mUdp4RandomPort++; + if (mUdp4RandomPort == 0) { + mUdp4RandomPort = UDP4_PORT_KNOWN; + } + + if (mUdp4RandomPort == StartPort) { + // + // No available port. + // + return EFI_OUT_OF_RESOURCES; + } + } + + ConfigData->StationPort = mUdp4RandomPort; + } + + mUdp4RandomPort++; + if (mUdp4RandomPort == 0) { + mUdp4RandomPort = UDP4_PORT_KNOWN; + } + } + + return EFI_SUCCESS; +} + + +/** + This function is used to check whether the NewConfigData has any un-reconfigurable + parameters changed compared to the OldConfigData. + + @param[in] OldConfigData Pointer to the current ConfigData the udp instance + uses. + @param[in] NewConfigData Pointer to the new ConfigData. + + @retval TRUE The instance is reconfigurable. + @retval FALSE Otherwise. + +**/ +BOOLEAN +Udp4IsReconfigurable ( + IN EFI_UDP4_CONFIG_DATA *OldConfigData, + IN EFI_UDP4_CONFIG_DATA *NewConfigData + ) +{ + if ((NewConfigData->AcceptAnyPort != OldConfigData->AcceptAnyPort) || + (NewConfigData->AcceptBroadcast != OldConfigData->AcceptBroadcast) || + (NewConfigData->AcceptPromiscuous != OldConfigData->AcceptPromiscuous) || + (NewConfigData->AllowDuplicatePort != OldConfigData->AllowDuplicatePort) + ) { + // + // The receiving filter parameters cannot be changed. + // + return FALSE; + } + + if ((!NewConfigData->AcceptAnyPort) && + (NewConfigData->StationPort != OldConfigData->StationPort) + ) { + // + // The port is not changeable. + // + return FALSE; + } + + if (!NewConfigData->AcceptPromiscuous) { + + if (NewConfigData->UseDefaultAddress != OldConfigData->UseDefaultAddress) { + // + // The NewConfigData differs to the old one on the UseDefaultAddress. + // + return FALSE; + } + + if (!NewConfigData->UseDefaultAddress && + (!EFI_IP4_EQUAL (&NewConfigData->StationAddress, &OldConfigData->StationAddress) || + !EFI_IP4_EQUAL (&NewConfigData->SubnetMask, &OldConfigData->SubnetMask)) + ) { + // + // If the instance doesn't use the default address, and the new address or + // new subnet mask is different from the old values. + // + return FALSE; + } + } + + if (!EFI_IP4_EQUAL (&NewConfigData->RemoteAddress, &OldConfigData->RemoteAddress)) { + // + // The remoteaddress is not the same. + // + return FALSE; + } + + if (!EFI_IP4_EQUAL (&NewConfigData->RemoteAddress, &mZeroIp4Addr) && + NewConfigData->RemotePort != OldConfigData->RemotePort + ) { + // + // The RemotePort differs if it's designated in the configdata. + // + return FALSE; + } + + // + // All checks pass, return TRUE. + // + return TRUE; +} + + +/** + This function builds the Ip4 configdata from the Udp4ConfigData. + + @param[in] Udp4ConfigData Pointer to the EFI_UDP4_CONFIG_DATA. + @param[in, out] Ip4ConfigData Pointer to the EFI_IP4_CONFIG_DATA. + +**/ +VOID +Udp4BuildIp4ConfigData ( + IN EFI_UDP4_CONFIG_DATA *Udp4ConfigData, + IN OUT EFI_IP4_CONFIG_DATA *Ip4ConfigData + ) +{ + CopyMem (Ip4ConfigData, &mIp4IoDefaultIpConfigData, sizeof (*Ip4ConfigData)); + + Ip4ConfigData->DefaultProtocol = EFI_IP_PROTO_UDP; + Ip4ConfigData->AcceptBroadcast = Udp4ConfigData->AcceptBroadcast; + Ip4ConfigData->AcceptPromiscuous = Udp4ConfigData->AcceptPromiscuous; + Ip4ConfigData->UseDefaultAddress = Udp4ConfigData->UseDefaultAddress; + CopyMem (&Ip4ConfigData->StationAddress, &Udp4ConfigData->StationAddress, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&Ip4ConfigData->SubnetMask, &Udp4ConfigData->SubnetMask, sizeof (EFI_IPv4_ADDRESS)); + + // + // use the -1 magic number to disable the receiving process of the ip instance. + // + Ip4ConfigData->ReceiveTimeout = (UINT32) (-1); +} + + +/** + This function validates the TxToken, it returns the error code according to the spec. + + @param[in] Instance Pointer to the udp instance context data. + @param[in] TxToken Pointer to the token to be checked. + + @retval EFI_SUCCESS The TxToken is valid. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: This is + NULL. Token is NULL. Token.Event is NULL. + Token.Packet.TxData is NULL. + Token.Packet.TxData.FragmentCount is zero. + Token.Packet.TxData.DataLength is not equal to the + sum of fragment lengths. One or more of the + Token.Packet.TxData.FragmentTable[]. + FragmentLength fields is zero. One or more of the + Token.Packet.TxData.FragmentTable[]. + FragmentBuffer fields is NULL. + Token.Packet.TxData. GatewayAddress is not a + unicast IPv4 address if it is not NULL. One or + more IPv4 addresses in Token.Packet.TxData. + UdpSessionData are not valid unicast IPv4 + addresses if the UdpSessionData is not NULL. + @retval EFI_BAD_BUFFER_SIZE The data length is greater than the maximum UDP + packet size. + +**/ +EFI_STATUS +Udp4ValidateTxToken ( + IN UDP4_INSTANCE_DATA *Instance, + IN EFI_UDP4_COMPLETION_TOKEN *TxToken + ) +{ + EFI_UDP4_TRANSMIT_DATA *TxData; + UINT32 Index; + UINT32 TotalLen; + EFI_UDP4_CONFIG_DATA *ConfigData; + EFI_UDP4_SESSION_DATA *UdpSessionData; + IP4_ADDR SourceAddress; + IP4_ADDR GatewayAddress; + + if (TxToken->Event == NULL) { + return EFI_INVALID_PARAMETER; + } + + TxData = TxToken->Packet.TxData; + + if ((TxData == NULL) || (TxData->FragmentCount == 0)) { + return EFI_INVALID_PARAMETER; + } + + TotalLen = 0; + for (Index = 0; Index < TxData->FragmentCount; Index++) { + + if ((TxData->FragmentTable[Index].FragmentBuffer == NULL) || + (TxData->FragmentTable[Index].FragmentLength == 0)) { + // + // if the FragmentBuffer is NULL or the FragmentLeng is zero. + // + return EFI_INVALID_PARAMETER; + } + + TotalLen += TxData->FragmentTable[Index].FragmentLength; + } + + if (TotalLen != TxData->DataLength) { + // + // The TotalLen calculated by adding all the FragmentLeng doesn't equal to the + // DataLength. + // + return EFI_INVALID_PARAMETER; + } + + if (TxData->GatewayAddress != NULL) { + CopyMem (&GatewayAddress, TxData->GatewayAddress, sizeof (IP4_ADDR)); + + if (!Instance->ConfigData.UseDefaultAddress && + (EFI_NTOHL(Instance->ConfigData.SubnetMask) != 0) && + !NetIp4IsUnicast (NTOHL (GatewayAddress), EFI_NTOHL(Instance->ConfigData.SubnetMask))) { + // + // The specified GatewayAddress is not a unicast IPv4 address while it's not 0. + // + return EFI_INVALID_PARAMETER; + } + } + + ConfigData = &Instance->ConfigData; + UdpSessionData = TxData->UdpSessionData; + + if (UdpSessionData != NULL) { + + CopyMem (&SourceAddress, &UdpSessionData->SourceAddress, sizeof (IP4_ADDR)); + + if ((SourceAddress != 0) && + !Instance->ConfigData.UseDefaultAddress && + (EFI_NTOHL(Instance->ConfigData.SubnetMask) != 0) && + !NetIp4IsUnicast (HTONL (SourceAddress), EFI_NTOHL(Instance->ConfigData.SubnetMask))) { + // + // Check whether SourceAddress is a valid IPv4 address in case it's not zero. + // The configured station address is used if SourceAddress is zero. + // + return EFI_INVALID_PARAMETER; + } + + if ((UdpSessionData->DestinationPort == 0) && (ConfigData->RemotePort == 0)) { + // + // Ambiguous, no available DestinationPort for this token. + // + return EFI_INVALID_PARAMETER; + } + + if (EFI_IP4_EQUAL (&UdpSessionData->DestinationAddress, &mZeroIp4Addr)) { + // + // The DestinationAddress specified in the UdpSessionData is 0. + // + return EFI_INVALID_PARAMETER; + } + } else if (EFI_IP4_EQUAL (&ConfigData->RemoteAddress, &mZeroIp4Addr)) { + // + // the configured RemoteAddress is all zero, and the user doesn't override the + // destination address. + // + return EFI_INVALID_PARAMETER; + } + + if (TxData->DataLength > UDP4_MAX_DATA_SIZE) { + return EFI_BAD_BUFFER_SIZE; + } + + return EFI_SUCCESS; +} + + +/** + This function checks whether the specified Token duplicates with the one in the Map. + + @param[in] Map Pointer to the NET_MAP. + @param[in] Item Pointer to the NET_MAP_ITEM contain the pointer to + the Token. + @param[in] Context Pointer to the Token to be checked. + + @retval EFI_SUCCESS The Token specified by Context differs from the + one in the Item. + @retval EFI_ACCESS_DENIED The Token duplicates with the one in the Item. + +**/ +EFI_STATUS +EFIAPI +Udp4TokenExist ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ) +{ + EFI_UDP4_COMPLETION_TOKEN *Token; + EFI_UDP4_COMPLETION_TOKEN *TokenInItem; + + Token = (EFI_UDP4_COMPLETION_TOKEN*) Context; + TokenInItem = (EFI_UDP4_COMPLETION_TOKEN*) Item->Key; + + if ((Token == TokenInItem) || (Token->Event == TokenInItem->Event)) { + // + // The Token duplicates with the TokenInItem in case either the two pointers are the + // same or the Events of these two tokens are the same. + // + return EFI_ACCESS_DENIED; + } + + return EFI_SUCCESS; +} + + +/** + This function calculates the checksum for the Packet, utilizing the pre-calculated + pseudo HeadSum to reduce some overhead. + + @param[in] Packet Pointer to the NET_BUF contains the udp datagram. + @param[in] HeadSum Checksum of the pseudo header except the length + field. + + @retval The 16-bit checksum of this udp datagram. + +**/ +UINT16 +Udp4Checksum ( + IN NET_BUF *Packet, + IN UINT16 HeadSum + ) +{ + UINT16 Checksum; + + Checksum = NetbufChecksum (Packet); + Checksum = NetAddChecksum (Checksum, HeadSum); + + Checksum = NetAddChecksum (Checksum, HTONS ((UINT16) Packet->TotalSize)); + + return (UINT16) ~Checksum; +} + + +/** + This function removes the specified Token from the TokenMap. + + @param[in, out] TokenMap Pointer to the NET_MAP containing the tokens. + @param[in] Token Pointer to the Token to be removed. + + @retval EFI_SUCCESS The specified Token is removed from the TokenMap. + @retval EFI_NOT_FOUND The specified Token is not found in the TokenMap. + +**/ +EFI_STATUS +Udp4RemoveToken ( + IN OUT NET_MAP *TokenMap, + IN EFI_UDP4_COMPLETION_TOKEN *Token + ) +{ + NET_MAP_ITEM *Item; + + // + // Find the Token first. + // + Item = NetMapFindKey (TokenMap, (VOID *) Token); + + if (Item != NULL) { + // + // Remove the token if it's found in the map. + // + NetMapRemoveItem (TokenMap, Item, NULL); + + return EFI_SUCCESS; + } + + return EFI_NOT_FOUND; +} + + +/** + This function is the packet transmitting notify function registered to the IpIo + interface. It's called to signal the udp TxToken when IpIo layer completes the + transmitting of the udp datagram. + + @param[in] Status The completion status of the output udp datagram. + @param[in] Context Pointer to the context data. + @param[in] Sender Specify a pointer of EFI_IP4_PROTOCOL for sending. + @param[in] NotifyData Pointer to the notify data. + +**/ +VOID +EFIAPI +Udp4DgramSent ( + IN EFI_STATUS Status, + IN VOID *Context, + IN IP_IO_IP_PROTOCOL Sender, + IN VOID *NotifyData + ) +{ + UDP4_INSTANCE_DATA *Instance; + EFI_UDP4_COMPLETION_TOKEN *Token; + + Instance = (UDP4_INSTANCE_DATA *) Context; + Token = (EFI_UDP4_COMPLETION_TOKEN *) NotifyData; + + if (Udp4RemoveToken (&Instance->TxTokens, Token) == EFI_SUCCESS) { + // + // The token may be cancelled. Only signal it if the remove operation succeeds. + // + Token->Status = Status; + gBS->SignalEvent (Token->Event); + DispatchDpc (); + } +} + + +/** + This function processes the received datagram passed up by the IpIo layer. + + @param[in] Status The status of this udp datagram. + @param[in] IcmpError The IcmpError code, only available when Status is + EFI_ICMP_ERROR. + @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA. + @param[in] Packet Pointer to the NET_BUF containing the received udp + datagram. + @param[in] Context Pointer to the context data. + +**/ +VOID +EFIAPI +Udp4DgramRcvd ( + IN EFI_STATUS Status, + IN UINT8 IcmpError, + IN EFI_NET_SESSION_DATA *NetSession, + IN NET_BUF *Packet, + IN VOID *Context + ) +{ + NET_CHECK_SIGNATURE (Packet, NET_BUF_SIGNATURE); + + // + // IpIo only passes received packets with Status EFI_SUCCESS or EFI_ICMP_ERROR. + // + if (Status == EFI_SUCCESS) { + // + // Demultiplex the received datagram. + // + Udp4Demultiplex ((UDP4_SERVICE_DATA *) Context, NetSession, Packet); + } else { + // + // Handle the ICMP_ERROR packet. + // + Udp4IcmpHandler ((UDP4_SERVICE_DATA *) Context, IcmpError, NetSession, Packet); + } + + // + // Dispatch the DPC queued by the NotifyFunction of the rx token's events + // which are signaled with received data. + // + DispatchDpc (); +} + + +/** + This function removes the multicast group specified by Arg from the Map. + + @param[in, out] Map Pointer to the NET_MAP. + @param[in] Item Pointer to the NET_MAP_ITEM. + @param[in] Arg Pointer to the Arg, it's the pointer to a + multicast IPv4 Address. + + @retval EFI_SUCCESS The multicast address is removed. + @retval EFI_ABORTED The specified multicast address is removed and the + Arg is not NULL. + +**/ +EFI_STATUS +EFIAPI +Udp4LeaveGroup ( + IN OUT NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Arg OPTIONAL + ) +{ + EFI_IPv4_ADDRESS *McastIp; + + McastIp = Arg; + + if ((McastIp != NULL) && (!EFI_IP4_EQUAL (McastIp, &(Item->Key)))) { + // + // McastIp is not NULL and the multicast address contained in the Item + // is not the same as McastIp. + // + return EFI_SUCCESS; + } + + // + // Remove this Item. + // + NetMapRemoveItem (Map, Item, NULL); + + if (McastIp != NULL) { + // + // Return EFI_ABORTED in case McastIp is not NULL to terminate the iteration. + // + return EFI_ABORTED; + } + + return EFI_SUCCESS; +} + + +/** + This function cancels the token specified by Arg in the Map. This is a callback + used by Udp4InstanceCancelToken(). + + @param[in] Map Pointer to the NET_MAP. + @param[in] Item Pointer to the NET_MAP_ITEM. + @param[in] Arg Pointer to the token to be cancelled, if NULL, + the token specified by Item is cancelled. + + @retval EFI_SUCCESS The token is cancelled if Arg is NULL or the token + is not the same as that in the Item if Arg is not + NULL. + @retval EFI_ABORTED Arg is not NULL, and the token specified by Arg is + cancelled. + +**/ +EFI_STATUS +EFIAPI +Udp4CancelTokens ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Arg OPTIONAL + ) +{ + EFI_UDP4_COMPLETION_TOKEN *TokenToCancel; + NET_BUF *Packet; + IP_IO *IpIo; + + if ((Arg != NULL) && (Item->Key != Arg)) { + return EFI_SUCCESS; + } + + if (Item->Value != NULL) { + // + // If the token is a transmit token, the corresponding Packet is recorded in + // Item->Value, invoke IpIo to cancel this packet first. The IpIoCancelTxToken + // will invoke Udp4DgramSent, the token will be signaled and this Item will + // be removed from the Map there. + // + Packet = (NET_BUF *) (Item->Value); + IpIo = (IP_IO *) (*((UINTN *) &Packet->ProtoData[0])); + + IpIoCancelTxToken (IpIo, Packet); + } else { + // + // The token is a receive token. Abort it and remove it from the Map. + // + TokenToCancel = (EFI_UDP4_COMPLETION_TOKEN *) Item->Key; + NetMapRemoveItem (Map, Item, NULL); + + TokenToCancel->Status = EFI_ABORTED; + gBS->SignalEvent (TokenToCancel->Event); + } + + if (Arg != NULL) { + return EFI_ABORTED; + } + + return EFI_SUCCESS; +} + + +/** + This function removes all the Wrap datas in the RcvdDgramQue. + + @param[in] Instance Pointer to the udp instance context data. + +**/ +VOID +Udp4FlushRcvdDgram ( + IN UDP4_INSTANCE_DATA *Instance + ) +{ + UDP4_RXDATA_WRAP *Wrap; + + while (!IsListEmpty (&Instance->RcvdDgramQue)) { + // + // Iterate all the Wraps in the RcvdDgramQue. + // + Wrap = NET_LIST_HEAD (&Instance->RcvdDgramQue, UDP4_RXDATA_WRAP, Link); + + // + // The Wrap will be removed from the RcvdDgramQue by this function call. + // + Udp4RecycleRxDataWrap (NULL, (VOID *) Wrap); + } +} + + + +/** + Cancel Udp4 tokens from the Udp4 instance. + + @param[in] Instance Pointer to the udp instance context data. + @param[in] Token Pointer to the token to be canceled, if NULL, all + tokens in this instance will be cancelled. + + @retval EFI_SUCCESS The Token is cancelled. + @retval EFI_NOT_FOUND The Token is not found. + +**/ +EFI_STATUS +Udp4InstanceCancelToken ( + IN UDP4_INSTANCE_DATA *Instance, + IN EFI_UDP4_COMPLETION_TOKEN *Token OPTIONAL + ) +{ + EFI_STATUS Status; + + // + // Cancel this token from the TxTokens map. + // + Status = NetMapIterate (&Instance->TxTokens, Udp4CancelTokens, Token); + + if ((Token != NULL) && (Status == EFI_ABORTED)) { + // + // If Token isn't NULL and Status is EFI_ABORTED, the token is cancelled from + // the TxTokens, just return success. + // + return EFI_SUCCESS; + } + + // + // Try to cancel this token from the RxTokens map in condition either the Token + // is NULL or the specified Token is not in TxTokens. + // + Status = NetMapIterate (&Instance->RxTokens, Udp4CancelTokens, Token); + + if ((Token != NULL) && (Status == EFI_SUCCESS)) { + // + // If Token isn't NULL and Status is EFI_SUCCESS, the token is neither in the + // TxTokens nor the RxTokens, or say, it's not found. + // + return EFI_NOT_FOUND; + } + + ASSERT ((Token != NULL) || ((0 == NetMapGetCount (&Instance->TxTokens)) + && (0 == NetMapGetCount (&Instance->RxTokens)))); + + return EFI_SUCCESS; +} + + +/** + This function matches the received udp datagram with the Instance. + + @param[in] Instance Pointer to the udp instance context data. + @param[in] Udp4Session Pointer to the EFI_UDP4_SESSION_DATA abstracted + from the received udp datagram. + + @retval TRUE The udp datagram matches the receiving requirements of the + udp Instance. + @retval FALSE Otherwise. + +**/ +BOOLEAN +Udp4MatchDgram ( + IN UDP4_INSTANCE_DATA *Instance, + IN EFI_UDP4_SESSION_DATA *Udp4Session + ) +{ + EFI_UDP4_CONFIG_DATA *ConfigData; + IP4_ADDR Destination; + + ConfigData = &Instance->ConfigData; + + if (ConfigData->AcceptPromiscuous) { + // + // Always matches if this instance is in the promiscuous state. + // + return TRUE; + } + + if ((!ConfigData->AcceptAnyPort && (Udp4Session->DestinationPort != ConfigData->StationPort)) || + ((ConfigData->RemotePort != 0) && (Udp4Session->SourcePort != ConfigData->RemotePort)) + ) { + // + // The local port or the remote port doesn't match. + // + return FALSE; + } + + if (!EFI_IP4_EQUAL (&ConfigData->RemoteAddress, &mZeroIp4Addr) && + !EFI_IP4_EQUAL (&ConfigData->RemoteAddress, &Udp4Session->SourceAddress) + ) { + // + // This datagram doesn't come from the instance's specified sender. + // + return FALSE; + } + + if (EFI_IP4_EQUAL (&ConfigData->StationAddress, &mZeroIp4Addr) || + EFI_IP4_EQUAL (&Udp4Session->DestinationAddress, &ConfigData->StationAddress) + ) { + // + // The instance is configured to receive datagrams destined to any station IP or + // the destination address of this datagram matches the configured station IP. + // + return TRUE; + } + + CopyMem (&Destination, &Udp4Session->DestinationAddress, sizeof (IP4_ADDR)); + + if (IP4_IS_LOCAL_BROADCAST (Destination) && ConfigData->AcceptBroadcast) { + // + // The instance is configured to receive broadcast and this is a broadcast packet. + // + return TRUE; + } + + if (IP4_IS_MULTICAST (NTOHL (Destination)) && + NetMapFindKey (&Instance->McastIps, (VOID *) (UINTN) Destination) != NULL + ) { + // + // It's a multicast packet and the multicast address is accepted by this instance. + // + return TRUE; + } + + return FALSE; +} + + +/** + This function removes the Wrap specified by Context and release relevant resources. + + @param[in] Event The Event this notify function registered to. + @param[in] Context Pointer to the context data. + +**/ +VOID +EFIAPI +Udp4RecycleRxDataWrap ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + UDP4_RXDATA_WRAP *Wrap; + + Wrap = (UDP4_RXDATA_WRAP *) Context; + + // + // Remove the Wrap from the list it belongs to. + // + RemoveEntryList (&Wrap->Link); + + // + // Free the Packet associated with this Wrap. + // + NetbufFree (Wrap->Packet); + + // + // Close the event. + // + gBS->CloseEvent (Wrap->RxData.RecycleSignal); + + FreePool (Wrap); +} + + +/** + This function wraps the Packet and the RxData. + + @param[in] Instance Pointer to the instance context data. + @param[in] Packet Pointer to the buffer containing the received + datagram. + @param[in] RxData Pointer to the EFI_UDP4_RECEIVE_DATA of this + datagram. + + @return Pointer to the structure wrapping the RxData and the Packet. + +**/ +UDP4_RXDATA_WRAP * +Udp4WrapRxData ( + IN UDP4_INSTANCE_DATA *Instance, + IN NET_BUF *Packet, + IN EFI_UDP4_RECEIVE_DATA *RxData + ) +{ + EFI_STATUS Status; + UDP4_RXDATA_WRAP *Wrap; + + // + // Allocate buffer for the Wrap. + // + Wrap = AllocatePool (sizeof (UDP4_RXDATA_WRAP) + + (Packet->BlockOpNum - 1) * sizeof (EFI_UDP4_FRAGMENT_DATA)); + if (Wrap == NULL) { + return NULL; + } + + InitializeListHead (&Wrap->Link); + + CopyMem (&Wrap->RxData, RxData, sizeof (Wrap->RxData)); + + // + // Create the Recycle event. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + Udp4RecycleRxDataWrap, + Wrap, + &Wrap->RxData.RecycleSignal + ); + if (EFI_ERROR (Status)) { + FreePool (Wrap); + return NULL; + } + + Wrap->Packet = Packet; + Wrap->TimeoutTick = Instance->ConfigData.ReceiveTimeout; + + return Wrap; +} + + +/** + This function enqueues the received datagram into the instances' receiving queues. + + @param[in] Udp4Service Pointer to the udp service context data. + @param[in] Packet Pointer to the buffer containing the received + datagram. + @param[in] RxData Pointer to the EFI_UDP4_RECEIVE_DATA of this + datagram. + + @return The times this datagram is enqueued. + +**/ +UINTN +Udp4EnqueueDgram ( + IN UDP4_SERVICE_DATA *Udp4Service, + IN NET_BUF *Packet, + IN EFI_UDP4_RECEIVE_DATA *RxData + ) +{ + LIST_ENTRY *Entry; + UDP4_INSTANCE_DATA *Instance; + UDP4_RXDATA_WRAP *Wrap; + UINTN Enqueued; + + Enqueued = 0; + + NET_LIST_FOR_EACH (Entry, &Udp4Service->ChildrenList) { + // + // Iterate the instances. + // + Instance = NET_LIST_USER_STRUCT (Entry, UDP4_INSTANCE_DATA, Link); + + if (!Instance->Configured) { + continue; + } + + if (Udp4MatchDgram (Instance, &RxData->UdpSession)) { + // + // Wrap the RxData and put this Wrap into the instances RcvdDgramQue. + // + Wrap = Udp4WrapRxData (Instance, Packet, RxData); + if (Wrap == NULL) { + continue; + } + + NET_GET_REF (Packet); + + InsertTailList (&Instance->RcvdDgramQue, &Wrap->Link); + + Enqueued++; + } + } + + return Enqueued; +} + + +/** + This function delivers the received datagrams for the specified instance. + + @param[in] Instance Pointer to the instance context data. + +**/ +VOID +Udp4InstanceDeliverDgram ( + IN UDP4_INSTANCE_DATA *Instance + ) +{ + UDP4_RXDATA_WRAP *Wrap; + EFI_UDP4_COMPLETION_TOKEN *Token; + NET_BUF *Dup; + EFI_UDP4_RECEIVE_DATA *RxData; + EFI_TPL OldTpl; + + if (!IsListEmpty (&Instance->RcvdDgramQue) && + !NetMapIsEmpty (&Instance->RxTokens)) { + + Wrap = NET_LIST_HEAD (&Instance->RcvdDgramQue, UDP4_RXDATA_WRAP, Link); + + if (NET_BUF_SHARED (Wrap->Packet)) { + // + // Duplicate the Packet if it is shared between instances. + // + Dup = NetbufDuplicate (Wrap->Packet, NULL, 0); + if (Dup == NULL) { + return; + } + + NetbufFree (Wrap->Packet); + + Wrap->Packet = Dup; + } + + NetListRemoveHead (&Instance->RcvdDgramQue); + + Token = (EFI_UDP4_COMPLETION_TOKEN *) NetMapRemoveHead (&Instance->RxTokens, NULL); + + // + // Build the FragmentTable and set the FragmentCount in RxData. + // + RxData = &Wrap->RxData; + RxData->FragmentCount = Wrap->Packet->BlockOpNum; + + NetbufBuildExt ( + Wrap->Packet, + (NET_FRAGMENT *) RxData->FragmentTable, + &RxData->FragmentCount + ); + + Token->Status = EFI_SUCCESS; + Token->Packet.RxData = &Wrap->RxData; + + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + InsertTailList (&Instance->DeliveredDgramQue, &Wrap->Link); + gBS->RestoreTPL (OldTpl); + + gBS->SignalEvent (Token->Event); + } +} + + +/** + This function delivers the datagrams enqueued in the instances. + + @param[in] Udp4Service Pointer to the udp service context data. + +**/ +VOID +Udp4DeliverDgram ( + IN UDP4_SERVICE_DATA *Udp4Service + ) +{ + LIST_ENTRY *Entry; + UDP4_INSTANCE_DATA *Instance; + + NET_LIST_FOR_EACH (Entry, &Udp4Service->ChildrenList) { + // + // Iterate the instances. + // + Instance = NET_LIST_USER_STRUCT (Entry, UDP4_INSTANCE_DATA, Link); + + if (!Instance->Configured) { + continue; + } + + // + // Deliver the datagrams of this instance. + // + Udp4InstanceDeliverDgram (Instance); + } +} + + +/** + This function demultiplexes the received udp datagram to the appropriate instances. + + @param[in] Udp4Service Pointer to the udp service context data. + @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA abstracted from + the received datagram. + @param[in] Packet Pointer to the buffer containing the received udp + datagram. + +**/ +VOID +Udp4Demultiplex ( + IN UDP4_SERVICE_DATA *Udp4Service, + IN EFI_NET_SESSION_DATA *NetSession, + IN NET_BUF *Packet + ) +{ + EFI_UDP_HEADER *Udp4Header; + UINT16 HeadSum; + EFI_UDP4_RECEIVE_DATA RxData; + EFI_UDP4_SESSION_DATA *Udp4Session; + UINTN Enqueued; + + if (Packet->TotalSize < sizeof (EFI_UDP_HEADER)) { + NetbufFree (Packet); + return; + } + + // + // Get the datagram header from the packet buffer. + // + Udp4Header = (EFI_UDP_HEADER *) NetbufGetByte (Packet, 0, NULL); + ASSERT (Udp4Header != NULL); + + if (Udp4Header->Checksum != 0) { + // + // check the checksum. + // + HeadSum = NetPseudoHeadChecksum ( + NetSession->Source.Addr[0], + NetSession->Dest.Addr[0], + EFI_IP_PROTO_UDP, + 0 + ); + + if (Udp4Checksum (Packet, HeadSum) != 0) { + // + // Wrong checksum. + // + NetbufFree (Packet); + return; + } + } + + Udp4Session = &RxData.UdpSession; + Udp4Session->SourcePort = NTOHS (Udp4Header->SrcPort); + Udp4Session->DestinationPort = NTOHS (Udp4Header->DstPort); + + CopyMem (&Udp4Session->SourceAddress, &NetSession->Source, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&Udp4Session->DestinationAddress, &NetSession->Dest, sizeof (EFI_IPv4_ADDRESS)); + + // + // Trim the UDP header. + // + NetbufTrim (Packet, UDP4_HEADER_SIZE, TRUE); + + RxData.DataLength = (UINT32) Packet->TotalSize; + + // + // Try to enqueue this datagram into the instances. + // + Enqueued = Udp4EnqueueDgram (Udp4Service, Packet, &RxData); + + if (Enqueued == 0) { + // + // Send the port unreachable ICMP packet before we free this NET_BUF + // + Udp4SendPortUnreach (Udp4Service->IpIo, NetSession, Udp4Header); + } + + // + // Try to free the packet before deliver it. + // + NetbufFree (Packet); + + if (Enqueued > 0) { + // + // Deliver the datagram. + // + Udp4DeliverDgram (Udp4Service); + } +} + + +/** + This function builds and sends out a icmp port unreachable message. + + @param[in] IpIo Pointer to the IP_IO instance. + @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA of the packet + causes this icmp error message. + @param[in] Udp4Header Pointer to the udp header of the datagram causes + this icmp error message. + +**/ +VOID +Udp4SendPortUnreach ( + IN IP_IO *IpIo, + IN EFI_NET_SESSION_DATA *NetSession, + IN VOID *Udp4Header + ) +{ + NET_BUF *Packet; + UINT32 Len; + IP4_ICMP_ERROR_HEAD *IcmpErrHdr; + EFI_IP4_HEADER *IpHdr; + UINT8 *Ptr; + IP_IO_OVERRIDE Override; + IP_IO_IP_INFO *IpSender; + + IpSender = IpIoFindSender (&IpIo, NetSession->IpVersion, &NetSession->Dest); + if (IpSender == NULL) { + // + // No appropriate sender, since we cannot send out the ICMP message through + // the default zero station address IP instance, abort. + // + return; + } + + IpHdr = NetSession->IpHdr.Ip4Hdr; + + // + // Calculate the required length of the icmp error message. + // + Len = sizeof (IP4_ICMP_ERROR_HEAD) + (EFI_IP4_HEADER_LEN (IpHdr) - + sizeof (IP4_HEAD)) + ICMP_ERROR_PACKET_LENGTH; + + // + // Allocate buffer for the icmp error message. + // + Packet = NetbufAlloc (Len); + if (Packet == NULL) { + return; + } + + // + // Allocate space for the IP4_ICMP_ERROR_HEAD. + // + IcmpErrHdr = (IP4_ICMP_ERROR_HEAD *) NetbufAllocSpace (Packet, Len, FALSE); + ASSERT (IcmpErrHdr != NULL); + + // + // Set the required fields for the icmp port unreachable message. + // + IcmpErrHdr->Head.Type = ICMP_TYPE_UNREACH; + IcmpErrHdr->Head.Code = ICMP_CODE_UNREACH_PORT; + IcmpErrHdr->Head.Checksum = 0; + IcmpErrHdr->Fourth = 0; + + // + // Copy the IP header of the datagram tragged the error. + // + CopyMem (&IcmpErrHdr->IpHead, IpHdr, EFI_IP4_HEADER_LEN (IpHdr)); + + // + // Copy the UDP header. + // + Ptr = (UINT8 *) &IcmpErrHdr->IpHead + EFI_IP4_HEADER_LEN (IpHdr); + CopyMem (Ptr, Udp4Header, ICMP_ERROR_PACKET_LENGTH); + + // + // Calculate the checksum. + // + IcmpErrHdr->Head.Checksum = (UINT16) ~(NetbufChecksum (Packet)); + + // + // Fill the override data. + // + Override.Ip4OverrideData.DoNotFragment = FALSE; + Override.Ip4OverrideData.TypeOfService = 0; + Override.Ip4OverrideData.TimeToLive = 255; + Override.Ip4OverrideData.Protocol = EFI_IP_PROTO_ICMP; + + CopyMem (&Override.Ip4OverrideData.SourceAddress, &NetSession->Dest, sizeof (EFI_IPv4_ADDRESS)); + ZeroMem (&Override.Ip4OverrideData.GatewayAddress, sizeof (EFI_IPv4_ADDRESS)); + + // + // Send out this icmp packet. + // + IpIoSend (IpIo, Packet, IpSender, NULL, NULL, &NetSession->Source, &Override); + + NetbufFree (Packet); +} + + +/** + This function handles the received Icmp Error message and demultiplexes it to the + instance. + + @param[in] Udp4Service Pointer to the udp service context data. + @param[in] IcmpError The icmp error code. + @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA abstracted + from the received Icmp Error packet. + @param[in] Packet Pointer to the Icmp Error packet. + +**/ +VOID +Udp4IcmpHandler ( + IN UDP4_SERVICE_DATA *Udp4Service, + IN UINT8 IcmpError, + IN EFI_NET_SESSION_DATA *NetSession, + IN NET_BUF *Packet + ) +{ + EFI_UDP_HEADER *Udp4Header; + EFI_UDP4_SESSION_DATA Udp4Session; + LIST_ENTRY *Entry; + UDP4_INSTANCE_DATA *Instance; + + if (Packet->TotalSize < sizeof (EFI_UDP_HEADER)) { + NetbufFree (Packet); + return; + } + + Udp4Header = (EFI_UDP_HEADER *) NetbufGetByte (Packet, 0, NULL); + ASSERT (Udp4Header != NULL); + + CopyMem (&Udp4Session.SourceAddress, &NetSession->Source, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&Udp4Session.DestinationAddress, &NetSession->Dest, sizeof (EFI_IPv4_ADDRESS)); + + Udp4Session.SourcePort = NTOHS (Udp4Header->DstPort); + Udp4Session.DestinationPort = NTOHS (Udp4Header->SrcPort); + + NET_LIST_FOR_EACH (Entry, &Udp4Service->ChildrenList) { + // + // Iterate all the instances. + // + Instance = NET_LIST_USER_STRUCT (Entry, UDP4_INSTANCE_DATA, Link); + + if (!Instance->Configured) { + continue; + } + + if (Udp4MatchDgram (Instance, &Udp4Session)) { + // + // Translate the Icmp Error code according to the udp spec. + // + Instance->IcmpError = IpIoGetIcmpErrStatus (IcmpError, IP_VERSION_4, NULL, NULL); + + if (IcmpError > ICMP_ERR_UNREACH_PORT) { + Instance->IcmpError = EFI_ICMP_ERROR; + } + + // + // Notify the instance with the received Icmp Error. + // + Udp4ReportIcmpError (Instance); + + break; + } + } + + NetbufFree (Packet); +} + + +/** + This function reports the received ICMP error. + + @param[in] Instance Pointer to the udp instance context data. + +**/ +VOID +Udp4ReportIcmpError ( + IN UDP4_INSTANCE_DATA *Instance + ) +{ + EFI_UDP4_COMPLETION_TOKEN *Token; + + if (NetMapIsEmpty (&Instance->RxTokens)) { + // + // There are no receive tokens to deliver the ICMP error. + // + return; + } + + if (EFI_ERROR (Instance->IcmpError)) { + // + // Try to get a RxToken from the RxTokens map. + // + Token = (EFI_UDP4_COMPLETION_TOKEN *) NetMapRemoveHead (&Instance->RxTokens, NULL); + + if (Token != NULL) { + // + // Report the error through the Token. + // + Token->Status = Instance->IcmpError; + gBS->SignalEvent (Token->Event); + + // + // Clear the IcmpError. + // + Instance->IcmpError = EFI_SUCCESS; + } + } +} + + +/** + This function is a dummy ext-free function for the NET_BUF created for the output + udp datagram. + + @param[in] Context Pointer to the context data. + +**/ +VOID +EFIAPI +Udp4NetVectorExtFree ( + VOID *Context + ) +{ +} + -- cgit