+/** @file\r
+\r
+Copyright (c) 2006 - 2007, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution. The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+Module Name:\r
+\r
+ Udp4Impl.c\r
+\r
+Abstract:\r
+\r
+ The implementation of the Udp4 protocol.\r
+\r
+\r
+**/\r
+\r
+\r
+#include "Udp4Impl.h"\r
+\r
+UINT16 mUdp4RandomPort;\r
+\r
+STATIC\r
+VOID\r
+EFIAPI\r
+Udp4CheckTimeout (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ );\r
+\r
+STATIC\r
+BOOLEAN\r
+Udp4FindInstanceByPort (\r
+ IN NET_LIST_ENTRY *InstanceList,\r
+ IN EFI_IPv4_ADDRESS *Address,\r
+ IN UINT16 Port\r
+ );\r
+\r
+STATIC\r
+VOID\r
+Udp4DgramSent (\r
+ IN EFI_STATUS Status,\r
+ IN VOID *Context,\r
+ IN VOID *Sender,\r
+ IN VOID *NotifyData\r
+ );\r
+\r
+STATIC\r
+VOID\r
+Udp4DgramRcvd (\r
+ IN EFI_STATUS Status,\r
+ IN ICMP_ERROR IcmpError,\r
+ IN EFI_NET_SESSION_DATA *NetSession,\r
+ IN NET_BUF *Packet,\r
+ IN VOID *Context\r
+ );\r
+\r
+STATIC\r
+EFI_STATUS\r
+Udp4CancelTokens (\r
+ IN NET_MAP *Map,\r
+ IN NET_MAP_ITEM *Item,\r
+ IN VOID *Arg OPTIONAL\r
+ );\r
+\r
+STATIC\r
+BOOLEAN\r
+Udp4MatchDgram (\r
+ IN UDP4_INSTANCE_DATA *Instance,\r
+ IN EFI_UDP4_SESSION_DATA *Udp4Session\r
+ );\r
+\r
+STATIC\r
+VOID\r
+EFIAPI\r
+Udp4RecycleRxDataWrap (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ );\r
+\r
+STATIC\r
+UDP4_RXDATA_WRAP *\r
+Udp4WrapRxData (\r
+ IN UDP4_INSTANCE_DATA *Instance,\r
+ IN NET_BUF *Packet,\r
+ IN EFI_UDP4_RECEIVE_DATA *RxData\r
+ );\r
+\r
+STATIC\r
+UINTN\r
+Udp4EnqueueDgram (\r
+ IN UDP4_SERVICE_DATA *Udp4Service,\r
+ IN NET_BUF *Packet,\r
+ IN EFI_UDP4_RECEIVE_DATA *RxData\r
+ );\r
+\r
+STATIC\r
+VOID\r
+Udp4DeliverDgram (\r
+ IN UDP4_SERVICE_DATA *Udp4Service\r
+ );\r
+\r
+STATIC\r
+VOID\r
+Udp4Demultiplex (\r
+ IN UDP4_SERVICE_DATA *Udp4Service,\r
+ IN EFI_NET_SESSION_DATA *NetSession,\r
+ IN NET_BUF *Packet\r
+ );\r
+\r
+STATIC\r
+VOID\r
+Udp4IcmpHandler (\r
+ IN UDP4_SERVICE_DATA *Udp4Service,\r
+ IN ICMP_ERROR IcmpError,\r
+ IN EFI_NET_SESSION_DATA *NetSession,\r
+ IN NET_BUF *Packet\r
+ );\r
+\r
+STATIC\r
+VOID\r
+Udp4SendPortUnreach (\r
+ IN IP_IO *IpIo,\r
+ IN EFI_NET_SESSION_DATA *NetSession,\r
+ IN VOID *Udp4Header\r
+ );\r
+\r
+\r
+/**\r
+ Create the Udp service context data.\r
+\r
+ @param Udp4Service Pointer to the UDP4_SERVICE_DATA.\r
+ @param ImageHandle The image handle of this udp4 driver.\r
+ @param ControllerHandle The controller handle this udp4 driver binds on.\r
+\r
+ @retval EFI_SUCCESS The udp4 service context data is created and\r
+ initialized.\r
+ @retval EFI_OUT_OF_RESOURCES Cannot allocate memory.\r
+\r
+**/\r
+EFI_STATUS\r
+Udp4CreateService (\r
+ IN UDP4_SERVICE_DATA *Udp4Service,\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_HANDLE ControllerHandle\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ IP_IO_OPEN_DATA OpenData;\r
+\r
+ Udp4Service->Signature = UDP4_SERVICE_DATA_SIGNATURE;\r
+ Udp4Service->ServiceBinding = mUdp4ServiceBinding;\r
+ Udp4Service->ImageHandle = ImageHandle;\r
+ Udp4Service->ControllerHandle = ControllerHandle;\r
+ Udp4Service->ChildrenNumber = 0;\r
+\r
+ NetListInit (&Udp4Service->ChildrenList);\r
+\r
+ //\r
+ // Create the IpIo for this service context.\r
+ //\r
+ Udp4Service->IpIo = IpIoCreate (ImageHandle, ControllerHandle);\r
+ if (Udp4Service->IpIo == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ //\r
+ // Set the OpenData used to open the IpIo.\r
+ //\r
+ OpenData.IpConfigData = mIpIoDefaultIpConfigData;\r
+ OpenData.IpConfigData.AcceptBroadcast = TRUE;\r
+ OpenData.RcvdContext = (VOID *) Udp4Service;\r
+ OpenData.SndContext = NULL;\r
+ OpenData.PktRcvdNotify = Udp4DgramRcvd;\r
+ OpenData.PktSentNotify = Udp4DgramSent;\r
+\r
+ //\r
+ // Configure and start the IpIo.\r
+ //\r
+ Status = IpIoOpen (Udp4Service->IpIo, &OpenData);\r
+ if (EFI_ERROR (Status)) {\r
+ goto RELEASE_IPIO;\r
+ }\r
+\r
+ //\r
+ // Create the event for Udp timeout checking.\r
+ //\r
+ Status = gBS->CreateEvent (\r
+ EVT_TIMER | EVT_NOTIFY_SIGNAL,\r
+ NET_TPL_FAST_TIMER,\r
+ Udp4CheckTimeout,\r
+ Udp4Service,\r
+ &Udp4Service->TimeoutEvent\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto RELEASE_IPIO;\r
+ }\r
+\r
+ //\r
+ // Start the timeout timer event.\r
+ //\r
+ Status = gBS->SetTimer (\r
+ Udp4Service->TimeoutEvent,\r
+ TimerPeriodic,\r
+ UDP4_TIMEOUT_INTERVAL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto RELEASE_ALL;\r
+ }\r
+\r
+ Udp4Service->MacString = NULL;\r
+\r
+ return EFI_SUCCESS;\r
+\r
+RELEASE_ALL:\r
+\r
+ gBS->CloseEvent (Udp4Service->TimeoutEvent);\r
+\r
+RELEASE_IPIO:\r
+\r
+ IpIoDestroy (Udp4Service->IpIo);\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Clean the Udp service context data.\r
+\r
+ @param Udp4Service Pointer to the UDP4_SERVICE_DATA.\r
+\r
+ @return None.\r
+\r
+**/\r
+VOID\r
+Udp4CleanService (\r
+ IN UDP4_SERVICE_DATA *Udp4Service\r
+ )\r
+{\r
+ //\r
+ // Cancel the TimeoutEvent timer.\r
+ //\r
+ gBS->SetTimer (Udp4Service->TimeoutEvent, TimerCancel, 0);\r
+\r
+ //\r
+ // Close the TimeoutEvent timer.\r
+ //\r
+ gBS->CloseEvent (Udp4Service->TimeoutEvent);\r
+\r
+ //\r
+ // Destroy the IpIo.\r
+ //\r
+ IpIoDestroy (Udp4Service->IpIo);\r
+}\r
+\r
+\r
+/**\r
+ This function checks and timeouts the I/O datagrams holding by the corresponding\r
+ service context.\r
+\r
+ @param Event The event this function registered to.\r
+ @param Conext The context data registered during the creation of\r
+ the Event.\r
+\r
+ @return None.\r
+\r
+**/\r
+STATIC\r
+VOID\r
+EFIAPI\r
+Udp4CheckTimeout (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ UDP4_SERVICE_DATA *Udp4Service;\r
+ NET_LIST_ENTRY *Entry;\r
+ UDP4_INSTANCE_DATA *Instance;\r
+ NET_LIST_ENTRY *WrapEntry;\r
+ NET_LIST_ENTRY *NextEntry;\r
+ UDP4_RXDATA_WRAP *Wrap;\r
+\r
+ Udp4Service = (UDP4_SERVICE_DATA *) Context;\r
+ NET_CHECK_SIGNATURE (Udp4Service, UDP4_SERVICE_DATA_SIGNATURE);\r
+\r
+ NET_LIST_FOR_EACH (Entry, &Udp4Service->ChildrenList) {\r
+ //\r
+ // Iterate all the instances belonging to this service context.\r
+ //\r
+ Instance = NET_LIST_USER_STRUCT (Entry, UDP4_INSTANCE_DATA, Link);\r
+ NET_CHECK_SIGNATURE (Instance, UDP4_INSTANCE_DATA_SIGNATURE);\r
+\r
+ if (!Instance->Configured || (Instance->ConfigData.ReceiveTimeout == 0)) {\r
+ //\r
+ // Skip this instance if it's not configured or no receive timeout.\r
+ //\r
+ continue;\r
+ }\r
+\r
+ NET_LIST_FOR_EACH_SAFE (WrapEntry, NextEntry, &Instance->RcvdDgramQue) {\r
+ //\r
+ // Iterate all the rxdatas belonging to this udp instance.\r
+ //\r
+ Wrap = NET_LIST_USER_STRUCT (Entry, UDP4_RXDATA_WRAP, Link);\r
+\r
+ if (Wrap->TimeoutTick <= UDP4_TIMEOUT_INTERVAL / 1000) {\r
+ //\r
+ // Remove this RxData if it timeouts.\r
+ //\r
+ Udp4RecycleRxDataWrap (NULL, (VOID *) Wrap);\r
+ } else {\r
+ Wrap->TimeoutTick -= UDP4_TIMEOUT_INTERVAL / 1000;\r
+ }\r
+ }\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ This function intializes the new created udp instance.\r
+\r
+ @param Udp4Service Pointer to the UDP4_SERVICE_DATA.\r
+ @param Instance Pointer to the un-initialized UDP4_INSTANCE_DATA.\r
+\r
+ @return None.\r
+\r
+**/\r
+VOID\r
+Udp4InitInstance (\r
+ IN UDP4_SERVICE_DATA *Udp4Service,\r
+ IN UDP4_INSTANCE_DATA *Instance\r
+ )\r
+{\r
+ //\r
+ // Set the signature.\r
+ //\r
+ Instance->Signature = UDP4_INSTANCE_DATA_SIGNATURE;\r
+\r
+ //\r
+ // Init the lists.\r
+ //\r
+ NetListInit (&Instance->Link);\r
+ NetListInit (&Instance->RcvdDgramQue);\r
+ NetListInit (&Instance->DeliveredDgramQue);\r
+\r
+ //\r
+ // Init the NET_MAPs.\r
+ //\r
+ NetMapInit (&Instance->TxTokens);\r
+ NetMapInit (&Instance->RxTokens);\r
+ NetMapInit (&Instance->McastIps);\r
+\r
+ //\r
+ // Save the pointer to the UDP4_SERVICE_DATA, and initialize other members.\r
+ //\r
+ Instance->Udp4Service = Udp4Service;\r
+ Instance->Udp4Proto = mUdp4Protocol;\r
+ Instance->IcmpError = EFI_SUCCESS;\r
+ Instance->Configured = FALSE;\r
+ Instance->IsNoMapping = FALSE;\r
+ Instance->Destroyed = FALSE;\r
+}\r
+\r
+\r
+/**\r
+ This function cleans the udp instance.\r
+\r
+ @param Instance Pointer to the UDP4_INSTANCE_DATA to clean.\r
+\r
+ @return None.\r
+\r
+**/\r
+VOID\r
+Udp4CleanInstance (\r
+ IN UDP4_INSTANCE_DATA *Instance\r
+ )\r
+{\r
+ NetMapClean (&Instance->McastIps);\r
+ NetMapClean (&Instance->RxTokens);\r
+ NetMapClean (&Instance->TxTokens);\r
+}\r
+\r
+\r
+/**\r
+ This function finds the udp instance by the specified <Address, Port> pair.\r
+\r
+ @param InstanceList Pointer to the head of the list linking the udp\r
+ instances.\r
+ @param Address Pointer to the specified IPv4 address.\r
+ @param Port The udp port number.\r
+\r
+ @return Is the specified <Address, Port> pair found or not.\r
+\r
+**/\r
+STATIC\r
+BOOLEAN\r
+Udp4FindInstanceByPort (\r
+ IN NET_LIST_ENTRY *InstanceList,\r
+ IN EFI_IPv4_ADDRESS *Address,\r
+ IN UINT16 Port\r
+ )\r
+{\r
+ NET_LIST_ENTRY *Entry;\r
+ UDP4_INSTANCE_DATA *Instance;\r
+ EFI_UDP4_CONFIG_DATA *ConfigData;\r
+\r
+ NET_LIST_FOR_EACH (Entry, InstanceList) {\r
+ //\r
+ // Iterate all the udp instances.\r
+ //\r
+ Instance = NET_LIST_USER_STRUCT (Entry, UDP4_INSTANCE_DATA, Link);\r
+ ConfigData = &Instance->ConfigData;\r
+\r
+ if (!Instance->Configured || ConfigData->AcceptAnyPort) {\r
+ //\r
+ // If the instance is not configured or the configdata of the instance indicates\r
+ // this instance accepts any port, skip it.\r
+ //\r
+ continue;\r
+ }\r
+\r
+ if (EFI_IP_EQUAL (ConfigData->StationAddress, *Address) &&\r
+ (ConfigData->StationPort == Port)) {\r
+ //\r
+ // if both the address and the port are the same, return TRUE.\r
+ //\r
+ return TRUE;\r
+ }\r
+ }\r
+\r
+ //\r
+ // return FALSE when matching fails.\r
+ //\r
+ return FALSE;\r
+}\r
+\r
+\r
+/**\r
+ This function tries to bind the udp instance according to the configured port\r
+ allocation stragety.\r
+\r
+ @param InstanceList Pointer to the head of the list linking the udp\r
+ instances.\r
+ @param ConfigData Pointer to the ConfigData of the instance to be\r
+ bound.\r
+\r
+ @retval EFI_SUCCESS The bound operation is completed successfully.\r
+ @retval EFI_ACCESS_DENIED The <Address, Port> specified by the ConfigData is\r
+ already used by other instance.\r
+ @retval EFI_OUT_OF_RESOURCES No available port resources.\r
+\r
+**/\r
+EFI_STATUS\r
+Udp4Bind (\r
+ IN NET_LIST_ENTRY *InstanceList,\r
+ IN EFI_UDP4_CONFIG_DATA *ConfigData\r
+ )\r
+{\r
+ EFI_IPv4_ADDRESS *StationAddress;\r
+ UINT16 StartPort;\r
+\r
+ if (ConfigData->AcceptAnyPort) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ StationAddress = &ConfigData->StationAddress;\r
+\r
+ if (ConfigData->StationPort != 0) {\r
+\r
+ if (!ConfigData->AllowDuplicatePort &&\r
+ Udp4FindInstanceByPort (InstanceList, StationAddress, ConfigData->StationPort)) {\r
+ //\r
+ // Do not allow duplicate port and the port is already used by other instance.\r
+ //\r
+ return EFI_ACCESS_DENIED;\r
+ }\r
+ } else {\r
+ //\r
+ // select a random port for this instance;\r
+ //\r
+\r
+ if (ConfigData->AllowDuplicatePort) {\r
+ //\r
+ // Just pick up the random port if the instance allows duplicate port.\r
+ //\r
+ ConfigData->StationPort = mUdp4RandomPort;\r
+ } else {\r
+\r
+ StartPort = mUdp4RandomPort;\r
+\r
+ while (Udp4FindInstanceByPort(InstanceList, StationAddress, mUdp4RandomPort)) {\r
+\r
+ mUdp4RandomPort++;\r
+ if (mUdp4RandomPort == 0) {\r
+ mUdp4RandomPort = UDP4_PORT_KNOWN;\r
+ }\r
+\r
+ if (mUdp4RandomPort == StartPort) {\r
+ //\r
+ // No available port.\r
+ //\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ }\r
+\r
+ ConfigData->StationPort = mUdp4RandomPort;\r
+ }\r
+\r
+ mUdp4RandomPort++;\r
+ if (mUdp4RandomPort == 0) {\r
+ mUdp4RandomPort = UDP4_PORT_KNOWN;\r
+ }\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ This function is used to check whether the NewConfigData has any un-reconfigurable\r
+ parameters changed compared to the OldConfigData.\r
+\r
+ @param OldConfigData Pointer to the current ConfigData the udp instance\r
+ uses.\r
+ @param NewConfigData Pointer to the new ConfigData.\r
+\r
+ @return The instance is reconfigurable or not according to the NewConfigData.\r
+\r
+**/\r
+BOOLEAN\r
+Udp4IsReconfigurable (\r
+ IN EFI_UDP4_CONFIG_DATA *OldConfigData,\r
+ IN EFI_UDP4_CONFIG_DATA *NewConfigData\r
+ )\r
+{\r
+ if ((NewConfigData->AcceptAnyPort != OldConfigData->AcceptAnyPort) ||\r
+ (NewConfigData->AcceptBroadcast != OldConfigData->AcceptBroadcast) ||\r
+ (NewConfigData->AcceptPromiscuous != OldConfigData->AcceptPromiscuous) ||\r
+ (NewConfigData->AllowDuplicatePort != OldConfigData->AllowDuplicatePort)) {\r
+ //\r
+ // The receiving filter parameters cannot be changed.\r
+ //\r
+ return FALSE;\r
+ }\r
+\r
+ if ((!NewConfigData->AcceptAnyPort) &&\r
+ (NewConfigData->StationPort != OldConfigData->StationPort)) {\r
+ //\r
+ // The port is not changeable.\r
+ //\r
+ return FALSE;\r
+ }\r
+\r
+ if (!NewConfigData->AcceptPromiscuous) {\r
+\r
+ if (NewConfigData->UseDefaultAddress != OldConfigData->UseDefaultAddress) {\r
+ //\r
+ // The NewConfigData differs to the old one on the UseDefaultAddress.\r
+ //\r
+ return FALSE;\r
+ }\r
+\r
+ if (!NewConfigData->UseDefaultAddress &&\r
+ (!EFI_IP_EQUAL (NewConfigData->StationAddress, OldConfigData->StationAddress) ||\r
+ !EFI_IP_EQUAL (NewConfigData->SubnetMask, OldConfigData->SubnetMask))) {\r
+ //\r
+ // If the instance doesn't use the default address, and the new address or\r
+ // new subnet mask is different from the old values.\r
+ //\r
+ return FALSE;\r
+ }\r
+ }\r
+\r
+ if (!EFI_IP_EQUAL (NewConfigData->RemoteAddress, OldConfigData->RemoteAddress)) {\r
+ //\r
+ // The remoteaddress is not the same.\r
+ //\r
+ return FALSE;\r
+ }\r
+\r
+ if ((EFI_IP4 (NewConfigData->RemoteAddress) != 0) &&\r
+ (NewConfigData->RemotePort != OldConfigData->RemotePort)) {\r
+ //\r
+ // The RemotePort differs if it's designated in the configdata.\r
+ //\r
+ return FALSE;\r
+ }\r
+\r
+ //\r
+ // All checks pass, return TRUE.\r
+ //\r
+ return TRUE;\r
+}\r
+\r
+\r
+/**\r
+ This function builds the Ip4 configdata from the Udp4ConfigData.\r
+\r
+ @param Udp4ConfigData Pointer to the EFI_UDP4_CONFIG_DATA.\r
+ @param Ip4ConfigData Pointer to the EFI_IP4_CONFIG_DATA.\r
+\r
+ @return None.\r
+\r
+**/\r
+VOID\r
+Udp4BuildIp4ConfigData (\r
+ IN EFI_UDP4_CONFIG_DATA *Udp4ConfigData,\r
+ IN EFI_IP4_CONFIG_DATA *Ip4ConfigData\r
+ )\r
+{\r
+ *Ip4ConfigData = mIpIoDefaultIpConfigData;\r
+ Ip4ConfigData->DefaultProtocol = EFI_IP_PROTO_UDP;\r
+ Ip4ConfigData->AcceptBroadcast = Udp4ConfigData->AcceptBroadcast;\r
+ Ip4ConfigData->AcceptPromiscuous = Udp4ConfigData->AcceptPromiscuous;\r
+ Ip4ConfigData->UseDefaultAddress = Udp4ConfigData->UseDefaultAddress;\r
+ Ip4ConfigData->StationAddress = Udp4ConfigData->StationAddress;\r
+ Ip4ConfigData->SubnetMask = Udp4ConfigData->SubnetMask;\r
+\r
+ //\r
+ // use the -1 magic number to disable the receiving process of the ip instance.\r
+ //\r
+ Ip4ConfigData->ReceiveTimeout = (UINT32) (-1);\r
+}\r
+\r
+\r
+/**\r
+ This function validates the TxToken, it returns the error code according to the spec.\r
+\r
+ @param Instance Pointer to the udp instance context data.\r
+ @param TxToken Pointer to the token to be checked.\r
+\r
+ @retval EFI_SUCCESS The TxToken is valid.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: This is\r
+ NULL. Token is NULL. Token.Event is NULL.\r
+ Token.Packet.TxData is NULL.\r
+ Token.Packet.TxData.FragmentCount is zero.\r
+ Token.Packet.TxData.DataLength is not equal to the\r
+ sum of fragment lengths. One or more of the\r
+ Token.Packet.TxData.FragmentTable[].\r
+ FragmentLength fields is zero. One or more of the\r
+ Token.Packet.TxData.FragmentTable[].\r
+ FragmentBuffer fields is NULL.\r
+ Token.Packet.TxData. GatewayAddress is not a\r
+ unicast IPv4 address if it is not NULL. One or\r
+ more IPv4 addresses in Token.Packet.TxData.\r
+ UdpSessionData are not valid unicast IPv4\r
+ addresses if the UdpSessionData is not NULL.\r
+ @retval EFI_BAD_BUFFER_SIZE The data length is greater than the maximum UDP\r
+ packet size.\r
+\r
+**/\r
+EFI_STATUS\r
+Udp4ValidateTxToken (\r
+ IN UDP4_INSTANCE_DATA *Instance,\r
+ IN EFI_UDP4_COMPLETION_TOKEN *TxToken\r
+ )\r
+{\r
+ EFI_UDP4_TRANSMIT_DATA *TxData;\r
+ UINT32 Index;\r
+ UINT32 TotalLen;\r
+ EFI_UDP4_CONFIG_DATA *ConfigData;\r
+ EFI_UDP4_SESSION_DATA *UdpSessionData;\r
+ IP4_ADDR SourceAddress;\r
+\r
+ if (TxToken->Event == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ TxData = TxToken->Packet.TxData;\r
+\r
+ if ((TxData == NULL) || (TxData->FragmentCount == 0)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ TotalLen = 0;\r
+ for (Index = 0; Index < TxData->FragmentCount; Index++) {\r
+\r
+ if ((TxData->FragmentTable[Index].FragmentBuffer == NULL) ||\r
+ (TxData->FragmentTable[Index].FragmentLength == 0)) {\r
+ //\r
+ // if the FragmentBuffer is NULL or the FragmentLeng is zero.\r
+ //\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ TotalLen += TxData->FragmentTable[Index].FragmentLength;\r
+ }\r
+\r
+ if (TotalLen != TxData->DataLength) {\r
+ //\r
+ // The TotalLen calculated by adding all the FragmentLeng doesn't equal to the\r
+ // DataLength.\r
+ //\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if ((TxData->GatewayAddress != NULL) &&\r
+ !Ip4IsUnicast(EFI_NTOHL (*(TxData->GatewayAddress)), 0)) {\r
+ //\r
+ // The specified GatewayAddress is not a unicast IPv4 address while it's not 0.\r
+ //\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ ConfigData = &Instance->ConfigData;\r
+ UdpSessionData = TxData->UdpSessionData;\r
+\r
+ if (UdpSessionData != NULL) {\r
+\r
+ SourceAddress = EFI_NTOHL (UdpSessionData->SourceAddress);\r
+\r
+ if ((SourceAddress != 0) && !Ip4IsUnicast (SourceAddress, 0)) {\r
+ //\r
+ // Check whether SourceAddress is a valid IPv4 address in case it's not zero.\r
+ // The configured station address is used if SourceAddress is zero.\r
+ //\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if ((UdpSessionData->DestinationPort == 0) && (ConfigData->RemotePort == 0)) {\r
+ //\r
+ // Ambiguous, no avalaible DestinationPort for this token.\r
+ //\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (EFI_IP4 (UdpSessionData->DestinationAddress) == 0) {\r
+ //\r
+ // The DestinationAddress specified in the UdpSessionData is 0.\r
+ //\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ } else if (EFI_IP4 (ConfigData->RemoteAddress) == 0) {\r
+ //\r
+ // the configured RemoteAddress is all zero, and the user doens't override the\r
+ // destination address.\r
+ //\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (TxData->DataLength > UDP4_MAX_DATA_SIZE) {\r
+ return EFI_BAD_BUFFER_SIZE;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ This function checks whether the specified Token duplicates with the one in the Map.\r
+\r
+ @param Map Pointer to the NET_MAP.\r
+ @param Item Pointer to the NET_MAP_ITEM contain the pointer to\r
+ the Token.\r
+ @param Context Pointer to the Token to be checked.\r
+\r
+ @retval EFI_SUCCESS The Token specified by Context differs from the\r
+ one in the Item.\r
+ @retval EFI_ACCESS_DENIED The Token duplicates with the one in the Item.\r
+\r
+**/\r
+EFI_STATUS\r
+Udp4TokenExist (\r
+ IN NET_MAP *Map,\r
+ IN NET_MAP_ITEM *Item,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ EFI_UDP4_COMPLETION_TOKEN *Token;\r
+ EFI_UDP4_COMPLETION_TOKEN *TokenInItem;\r
+\r
+ Token = (EFI_UDP4_COMPLETION_TOKEN*) Context;\r
+ TokenInItem = (EFI_UDP4_COMPLETION_TOKEN*) Item->Key;\r
+\r
+ if ((Token == TokenInItem) || (Token->Event == TokenInItem->Event)) {\r
+ //\r
+ // The Token duplicates with the TokenInItem in case either the two pointers are the\r
+ // same or the Events of these two tokens are the same.\r
+ //\r
+ return EFI_ACCESS_DENIED;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ This function calculates the checksum for the Packet, utilizing the pre-calculated\r
+ pseudo HeadSum to reduce some overhead.\r
+\r
+ @param Packet Pointer to the NET_BUF contains the udp datagram.\r
+ @param HeadSum Checksum of the pseudo header execpt the length\r
+ field.\r
+\r
+ @return The 16-bit checksum of this udp datagram.\r
+\r
+**/\r
+UINT16\r
+Udp4Checksum (\r
+ IN NET_BUF *Packet,\r
+ IN UINT16 HeadSum\r
+ )\r
+{\r
+ UINT16 Checksum;\r
+\r
+ Checksum = NetbufChecksum (Packet);\r
+ Checksum = NetAddChecksum (Checksum, HeadSum);\r
+\r
+ Checksum = NetAddChecksum (Checksum, HTONS ((UINT16) Packet->TotalSize));\r
+\r
+ return ~Checksum;\r
+}\r
+\r
+\r
+/**\r
+ This function removes the specified Token from the TokenMap.\r
+\r
+ @param TokenMap Pointer to the NET_MAP containing the tokens.\r
+ @param Token Pointer to the Token to be removed.\r
+\r
+ @retval EFI_SUCCESS The specified Token is removed from the TokenMap.\r
+ @retval EFI_NOT_FOUND The specified Token is not found in the TokenMap.\r
+\r
+**/\r
+EFI_STATUS\r
+Udp4RemoveToken (\r
+ IN NET_MAP *TokenMap,\r
+ IN EFI_UDP4_COMPLETION_TOKEN *Token\r
+ )\r
+{\r
+ NET_MAP_ITEM *Item;\r
+\r
+ //\r
+ // Find the Token first.\r
+ //\r
+ Item = NetMapFindKey (TokenMap, (VOID *) Token);\r
+\r
+ if (Item != NULL) {\r
+ //\r
+ // Remove the token if it's found in the map.\r
+ //\r
+ NetMapRemoveItem (TokenMap, Item, NULL);\r
+\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ return EFI_NOT_FOUND;\r
+}\r
+\r
+\r
+/**\r
+ This function is the packet transmitting notify function registered to the IpIo\r
+ interface. It's called to signal the udp TxToken when IpIo layer completes the\r
+ transmitting of the udp datagram.\r
+\r
+ @param Status The completion status of the output udp datagram.\r
+ @param Context Pointer to the context data.\r
+ @param Sender Pointer to the Ip sender of the udp datagram.\r
+ @param NotifyData Pointer to the notify data.\r
+\r
+ @return None.\r
+\r
+**/\r
+STATIC\r
+VOID\r
+Udp4DgramSent (\r
+ IN EFI_STATUS Status,\r
+ IN VOID *Context,\r
+ IN VOID *Sender,\r
+ IN VOID *NotifyData\r
+ )\r
+{\r
+ UDP4_INSTANCE_DATA *Instance;\r
+ EFI_UDP4_COMPLETION_TOKEN *Token;\r
+\r
+ Instance = (UDP4_INSTANCE_DATA *) Context;\r
+ Token = (EFI_UDP4_COMPLETION_TOKEN *) NotifyData;\r
+\r
+ if (Udp4RemoveToken (&Instance->TxTokens, Token) == EFI_SUCCESS) {\r
+ //\r
+ // The token may be cancelled. Only signal it if the remove operation succeeds.\r
+ //\r
+ Token->Status = Status;\r
+ gBS->SignalEvent (Token->Event);\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ This function processes the received datagram passed up by the IpIo layer.\r
+\r
+ @param Status The status of this udp datagram.\r
+ @param IcmpError The IcmpError code, only available when Status is\r
+ EFI_ICMP_ERROR.\r
+ @param NetSession Pointer to the EFI_NET_SESSION_DATA.\r
+ @param Packet Pointer to the NET_BUF containing the received udp\r
+ datagram.\r
+ @param Context Pointer to the context data.\r
+\r
+ @return None.\r
+\r
+**/\r
+STATIC\r
+VOID\r
+Udp4DgramRcvd (\r
+ IN EFI_STATUS Status,\r
+ IN ICMP_ERROR IcmpError,\r
+ IN EFI_NET_SESSION_DATA *NetSession,\r
+ IN NET_BUF *Packet,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ NET_CHECK_SIGNATURE (Packet, NET_BUF_SIGNATURE);\r
+\r
+ //\r
+ // IpIo only passes received packets with Status EFI_SUCCESS or EFI_ICMP_ERROR.\r
+ //\r
+ if (Status == EFI_SUCCESS) {\r
+ //\r
+ // Demultiplex the received datagram.\r
+ //\r
+ Udp4Demultiplex ((UDP4_SERVICE_DATA *) Context, NetSession, Packet);\r
+ } else {\r
+ //\r
+ // Handle the ICMP_ERROR packet.\r
+ //\r
+ Udp4IcmpHandler ((UDP4_SERVICE_DATA *) Context, IcmpError, NetSession, Packet);\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ This function removes the multicast group specified by Arg from the Map.\r
+\r
+ @param Map Pointer to the NET_MAP.\r
+ @param Item Pointer to the NET_MAP_ITEM.\r
+ @param Arg Pointer to the Arg, it's the pointer to a\r
+ multicast IPv4 Address.\r
+\r
+ @retval EFI_SUCCESS The multicast address is removed.\r
+ @retval EFI_ABORTED The specified multicast address is removed and the\r
+ Arg is not NULL.\r
+\r
+**/\r
+EFI_STATUS\r
+Udp4LeaveGroup (\r
+ IN NET_MAP *Map,\r
+ IN NET_MAP_ITEM *Item,\r
+ IN VOID *Arg OPTIONAL\r
+ )\r
+{\r
+ EFI_IPv4_ADDRESS *McastIp;\r
+\r
+ McastIp = Arg;\r
+\r
+ if ((McastIp != NULL) && ((UINTN) EFI_IP4 (*McastIp) != (UINTN) (Item->Key))) {\r
+ //\r
+ // McastIp is not NULL and the multicast address contained in the Item\r
+ // is not the same as McastIp.\r
+ //\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ //\r
+ // Remove this Item.\r
+ //\r
+ NetMapRemoveItem (Map, Item, NULL);\r
+\r
+ if (McastIp != NULL) {\r
+ //\r
+ // Return EFI_ABORTED in case McastIp is not NULL to terminate the iteration.\r
+ //\r
+ return EFI_ABORTED;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ This function cancle the token specified by Arg in the Map.\r
+\r
+ @param Map Pointer to the NET_MAP.\r
+ @param Item Pointer to the NET_MAP_ITEM.\r
+ @param Arg Pointer to the token to be cancelled, if NULL, all\r
+ the tokens in this Map will be cancelled.\r
+\r
+ @retval EFI_SUCCESS The token is cancelled if Arg is NULL or the token\r
+ is not the same as that in the Item if Arg is not\r
+ NULL.\r
+ @retval EFI_ABORTED Arg is not NULL, and the token specified by Arg is\r
+ cancelled.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+Udp4CancelTokens (\r
+ IN NET_MAP *Map,\r
+ IN NET_MAP_ITEM *Item,\r
+ IN VOID *Arg OPTIONAL\r
+ )\r
+{\r
+ EFI_UDP4_COMPLETION_TOKEN *TokenToCancel;\r
+ NET_BUF *Packet;\r
+ IP_IO *IpIo;\r
+\r
+ if ((Arg != NULL) && (Item->Key != Arg)) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ if (Item->Value != NULL) {\r
+ //\r
+ // If the token is a transmit token, the corresponding Packet is recorded in\r
+ // Item->Value, invoke IpIo to cancel this packet first. The IpIoCancelTxToken\r
+ // will invoke Udp4DgramSent, the token will be signaled and this Item will\r
+ // be removed from the Map there.\r
+ //\r
+ Packet = (NET_BUF *) (Item->Value);\r
+ IpIo = (IP_IO *) (*((UINTN *) &Packet->ProtoData[0]));\r
+\r
+ IpIoCancelTxToken (IpIo, Packet);\r
+ } else {\r
+ //\r
+ // The token is a receive token. Abort it and remove it from the Map.\r
+ //\r
+ TokenToCancel = (EFI_UDP4_COMPLETION_TOKEN *) Item->Key;\r
+\r
+ TokenToCancel->Status = EFI_ABORTED;\r
+ gBS->SignalEvent (TokenToCancel->Event);\r
+\r
+ NetMapRemoveItem (Map, Item, NULL);\r
+ }\r
+\r
+ if (Arg != NULL) {\r
+ return EFI_ABORTED;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ This function removes all the Wrap datas in the RcvdDgramQue.\r
+\r
+ @param RcvdDgramQue Pointer to the list containing all the Wrap datas.\r
+\r
+ @return None.\r
+\r
+**/\r
+VOID\r
+Udp4FlushRxData (\r
+ IN NET_LIST_ENTRY *RcvdDgramQue\r
+ )\r
+{\r
+ UDP4_RXDATA_WRAP *Wrap;\r
+ EFI_TPL OldTpl;\r
+\r
+ OldTpl = NET_RAISE_TPL (NET_TPL_RECYCLE);\r
+\r
+ while (!NetListIsEmpty (RcvdDgramQue)) {\r
+ //\r
+ // Iterate all the Wraps in the RcvdDgramQue.\r
+ //\r
+ Wrap = NET_LIST_HEAD (RcvdDgramQue, UDP4_RXDATA_WRAP, Link);\r
+\r
+ //\r
+ // The Wrap will be removed from the RcvdDgramQue by this function call.\r
+ //\r
+ Udp4RecycleRxDataWrap (NULL, (VOID *) Wrap);\r
+ }\r
+\r
+ NET_RESTORE_TPL (OldTpl);\r
+}\r
+\r
+\r
+\r
+/**\r
+\r
+ @param Instance Pointer to the udp instance context data.\r
+ @param Token Pointer to the token to be canceled, if NULL, all\r
+ tokens in this instance will be cancelled.\r
+\r
+ @retval EFI_SUCCESS The Token is cancelled.\r
+ @retval EFI_NOT_FOUND The Token is not found.\r
+\r
+**/\r
+EFI_STATUS\r
+Udp4InstanceCancelToken (\r
+ IN UDP4_INSTANCE_DATA *Instance,\r
+ IN EFI_UDP4_COMPLETION_TOKEN *Token OPTIONAL\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ //\r
+ // Cancle this token from the TxTokens map.\r
+ //\r
+ Status = NetMapIterate (&Instance->TxTokens, Udp4CancelTokens, Token);\r
+\r
+ if ((Token != NULL) && (Status == EFI_ABORTED)) {\r
+ //\r
+ // If Token isn't NULL and Status is EFI_ABORTED, the token is cancelled from\r
+ // the TxTokens, just return success.\r
+ //\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ //\r
+ // Try to cancel this token from the RxTokens map in condition either the Token\r
+ // is NULL or the specified Token is not in TxTokens.\r
+ //\r
+ Status = NetMapIterate (&Instance->RxTokens, Udp4CancelTokens, Token);\r
+\r
+ if ((Token != NULL) && (Status == EFI_SUCCESS)) {\r
+ //\r
+ // If Token isn't NULL and Status is EFI_SUCCESS, the token is neither in the\r
+ // TxTokens nor the RxTokens, or say, it's not found.\r
+ //\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ ASSERT ((Token != NULL) || ((0 == NetMapGetCount (&Instance->TxTokens))\r
+ && (0 == NetMapGetCount (&Instance->RxTokens))));\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ This function matches the received udp datagram with the Instance.\r
+\r
+ @param Instance Pointer to the udp instance context data.\r
+ @param Udp4Session Pointer to the EFI_UDP4_SESSION_DATA abstracted\r
+ from the received udp datagram.\r
+\r
+ @return The udp datagram matches the receiving requirments of the Instance or not.\r
+\r
+**/\r
+STATIC\r
+BOOLEAN\r
+Udp4MatchDgram (\r
+ IN UDP4_INSTANCE_DATA *Instance,\r
+ IN EFI_UDP4_SESSION_DATA *Udp4Session\r
+ )\r
+{\r
+ EFI_UDP4_CONFIG_DATA *ConfigData;\r
+ IP4_ADDR Destination;\r
+\r
+ ConfigData = &Instance->ConfigData;\r
+\r
+ if (ConfigData->AcceptPromiscuous) {\r
+ //\r
+ // Always matches if this instance is in the promiscuous state.\r
+ //\r
+ return TRUE;\r
+ }\r
+\r
+ if ((!ConfigData->AcceptAnyPort && (Udp4Session->DestinationPort != ConfigData->StationPort)) ||\r
+ ((ConfigData->RemotePort != 0) && (Udp4Session->SourcePort != ConfigData->RemotePort))) {\r
+ //\r
+ // The local port or the remote port doesn't match.\r
+ //\r
+ return FALSE;\r
+ }\r
+\r
+ if ((EFI_IP4 (ConfigData->RemoteAddress) != 0) &&\r
+ !EFI_IP_EQUAL (ConfigData->RemoteAddress, Udp4Session->SourceAddress)) {\r
+ //\r
+ // This datagram doesn't come from the instance's specified sender.\r
+ //\r
+ return FALSE;\r
+ }\r
+\r
+ if ((EFI_IP4 (ConfigData->StationAddress) == 0) ||\r
+ EFI_IP_EQUAL (Udp4Session->DestinationAddress, ConfigData->StationAddress)) {\r
+ //\r
+ // The instance is configured to receive datagrams destinated to any station IP or\r
+ // the destination address of this datagram matches the configured station IP.\r
+ //\r
+ return TRUE;\r
+ }\r
+\r
+ Destination = EFI_IP4 (Udp4Session->DestinationAddress);\r
+\r
+ if (IP4_IS_LOCAL_BROADCAST (Destination) && ConfigData->AcceptBroadcast) {\r
+ //\r
+ // The instance is configured to receive broadcast and this is a broadcast packet.\r
+ //\r
+ return TRUE;\r
+ }\r
+\r
+ if (IP4_IS_MULTICAST (NTOHL (Destination)) &&\r
+ (NULL != NetMapFindKey (&Instance->McastIps, (VOID *) (UINTN) Destination))) {\r
+ //\r
+ // It's a multicast packet and the multicast address is accepted by this instance.\r
+ //\r
+ return TRUE;\r
+ }\r
+\r
+ return FALSE;\r
+}\r
+\r
+\r
+/**\r
+ This function removes the Wrap specified by Context and release relevant resources.\r
+\r
+ @param Event The Event this notify function registered to.\r
+ @param Context Pointer to the context data.\r
+\r
+ @return None.\r
+\r
+**/\r
+STATIC\r
+VOID\r
+EFIAPI\r
+Udp4RecycleRxDataWrap (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ UDP4_RXDATA_WRAP *Wrap;\r
+\r
+ Wrap = (UDP4_RXDATA_WRAP *) Context;\r
+\r
+ //\r
+ // Remove the Wrap from the list it belongs to.\r
+ //\r
+ NetListRemoveEntry (&Wrap->Link);\r
+\r
+ //\r
+ // Free the Packet associated with this Wrap.\r
+ //\r
+ NetbufFree (Wrap->Packet);\r
+\r
+ //\r
+ // Close the event.\r
+ //\r
+ gBS->CloseEvent (Wrap->RxData.RecycleSignal);\r
+\r
+ NetFreePool (Wrap);\r
+}\r
+\r
+\r
+/**\r
+ This function wraps the Packet and the RxData.\r
+\r
+ @param Instance Pointer to the instance context data.\r
+ @param Packet Pointer to the buffer containing the received\r
+ datagram.\r
+ @param RxData Pointer to the EFI_UDP4_RECEIVE_DATA of this\r
+ datagram.\r
+\r
+ @return Pointer to the structure wrapping the RxData and the Packet.\r
+\r
+**/\r
+STATIC\r
+UDP4_RXDATA_WRAP *\r
+Udp4WrapRxData (\r
+ IN UDP4_INSTANCE_DATA *Instance,\r
+ IN NET_BUF *Packet,\r
+ IN EFI_UDP4_RECEIVE_DATA *RxData\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UDP4_RXDATA_WRAP *Wrap;\r
+\r
+ //\r
+ // Allocate buffer for the Wrap.\r
+ //\r
+ Wrap = NetAllocatePool (sizeof (UDP4_RXDATA_WRAP) +\r
+ (Packet->BlockOpNum - 1) * sizeof (EFI_UDP4_FRAGMENT_DATA));\r
+ if (Wrap == NULL) {\r
+ return NULL;\r
+ }\r
+\r
+ NetListInit (&Wrap->Link);\r
+\r
+ Wrap->RxData = *RxData;\r
+\r
+ //\r
+ // Create the Recycle event.\r
+ //\r
+ Status = gBS->CreateEvent (\r
+ EVT_NOTIFY_SIGNAL,\r
+ NET_TPL_RECYCLE,\r
+ Udp4RecycleRxDataWrap,\r
+ Wrap,\r
+ &Wrap->RxData.RecycleSignal\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ NetFreePool (Wrap);\r
+ return NULL;\r
+ }\r
+\r
+ Wrap->Packet = Packet;\r
+ Wrap->TimeoutTick = Instance->ConfigData.ReceiveTimeout;\r
+\r
+ return Wrap;\r
+}\r
+\r
+\r
+/**\r
+ This function enqueues the received datagram into the instances' receiving queues.\r
+\r
+ @param Udp4Service Pointer to the udp service context data.\r
+ @param Packet Pointer to the buffer containing the received\r
+ datagram.\r
+ @param RxData Pointer to the EFI_UDP4_RECEIVE_DATA of this\r
+ datagram.\r
+\r
+ @return The times this datagram is enqueued.\r
+\r
+**/\r
+STATIC\r
+UINTN\r
+Udp4EnqueueDgram (\r
+ IN UDP4_SERVICE_DATA *Udp4Service,\r
+ IN NET_BUF *Packet,\r
+ IN EFI_UDP4_RECEIVE_DATA *RxData\r
+ )\r
+{\r
+ NET_LIST_ENTRY *Entry;\r
+ UDP4_INSTANCE_DATA *Instance;\r
+ UDP4_RXDATA_WRAP *Wrap;\r
+ UINTN Enqueued;\r
+\r
+ Enqueued = 0;\r
+\r
+ NET_LIST_FOR_EACH (Entry, &Udp4Service->ChildrenList) {\r
+ //\r
+ // Iterate the instances.\r
+ //\r
+ Instance = NET_LIST_USER_STRUCT (Entry, UDP4_INSTANCE_DATA, Link);\r
+\r
+ if (!Instance->Configured) {\r
+ continue;\r
+ }\r
+\r
+ if (Udp4MatchDgram (Instance, &RxData->UdpSession)) {\r
+ //\r
+ // Wrap the RxData and put this Wrap into the instances RcvdDgramQue.\r
+ //\r
+ Wrap = Udp4WrapRxData (Instance, Packet, RxData);\r
+ if (Wrap == NULL) {\r
+ continue;\r
+ }\r
+\r
+ NET_GET_REF (Packet);\r
+\r
+ NetListInsertTail (&Instance->RcvdDgramQue, &Wrap->Link);\r
+\r
+ Enqueued++;\r
+ }\r
+ }\r
+\r
+ return Enqueued;\r
+}\r
+\r
+\r
+/**\r
+ This function delivers the received datagrams for the specified instance.\r
+\r
+ @param Instance Pointer to the instance context data.\r
+\r
+ @return None.\r
+\r
+**/\r
+VOID\r
+Udp4InstanceDeliverDgram (\r
+ IN UDP4_INSTANCE_DATA *Instance\r
+ )\r
+{\r
+ UDP4_RXDATA_WRAP *Wrap;\r
+ EFI_UDP4_COMPLETION_TOKEN *Token;\r
+ NET_BUF *Dup;\r
+ EFI_UDP4_RECEIVE_DATA *RxData;\r
+\r
+ if (!NetListIsEmpty (&Instance->RcvdDgramQue) &&\r
+ !NetMapIsEmpty (&Instance->RxTokens)) {\r
+\r
+ Wrap = NET_LIST_HEAD (&Instance->RcvdDgramQue, UDP4_RXDATA_WRAP, Link);\r
+\r
+ if (NET_BUF_SHARED (Wrap->Packet)) {\r
+ //\r
+ // Duplicate the Packet if it is shared between instances.\r
+ //\r
+ Dup = NetbufDuplicate (Wrap->Packet, NULL, 0);\r
+ if (Dup == NULL) {\r
+ return;\r
+ }\r
+\r
+ NetbufFree (Wrap->Packet);\r
+\r
+ Wrap->Packet = Dup;\r
+ }\r
+\r
+ NetListRemoveHead (&Instance->RcvdDgramQue);\r
+\r
+ Token = (EFI_UDP4_COMPLETION_TOKEN *) NetMapRemoveHead (&Instance->RxTokens, NULL);\r
+\r
+ //\r
+ // Build the FragmentTable and set the FragmentCount in RxData.\r
+ //\r
+ RxData = &Wrap->RxData;\r
+ RxData->FragmentCount = Wrap->Packet->BlockOpNum;\r
+\r
+ NetbufBuildExt (\r
+ Wrap->Packet,\r
+ (NET_FRAGMENT *) RxData->FragmentTable,\r
+ &RxData->FragmentCount\r
+ );\r
+\r
+ Token->Status = EFI_SUCCESS;\r
+ Token->Packet.RxData = &Wrap->RxData;\r
+\r
+ gBS->SignalEvent (Token->Event);\r
+\r
+ NetListInsertTail (&Instance->DeliveredDgramQue, &Wrap->Link);\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ This function delivers the datagrams enqueued in the instances.\r
+\r
+ @param Udp4Service Pointer to the udp service context data.\r
+\r
+ @return None.\r
+\r
+**/\r
+STATIC\r
+VOID\r
+Udp4DeliverDgram (\r
+ IN UDP4_SERVICE_DATA *Udp4Service\r
+ )\r
+{\r
+ NET_LIST_ENTRY *Entry;\r
+ UDP4_INSTANCE_DATA *Instance;\r
+\r
+ NET_LIST_FOR_EACH (Entry, &Udp4Service->ChildrenList) {\r
+ //\r
+ // Iterate the instances.\r
+ //\r
+ Instance = NET_LIST_USER_STRUCT (Entry, UDP4_INSTANCE_DATA, Link);\r
+\r
+ if (!Instance->Configured) {\r
+ continue;\r
+ }\r
+\r
+ //\r
+ // Deliver the datagrams of this instance.\r
+ //\r
+ Udp4InstanceDeliverDgram (Instance);\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ This function demultiplexes the received udp datagram to the apropriate instances.\r
+\r
+ @param Udp4Service Pointer to the udp service context data.\r
+ @param NetSession Pointer to the EFI_NET_SESSION_DATA abstrated from\r
+ the received datagram.\r
+ @param Packet Pointer to the buffer containing the received udp\r
+ datagram.\r
+\r
+ @return None.\r
+\r
+**/\r
+STATIC\r
+VOID\r
+Udp4Demultiplex (\r
+ IN UDP4_SERVICE_DATA *Udp4Service,\r
+ IN EFI_NET_SESSION_DATA *NetSession,\r
+ IN NET_BUF *Packet\r
+ )\r
+{\r
+ EFI_UDP4_HEADER *Udp4Header;\r
+ UINT16 HeadSum;\r
+ EFI_UDP4_RECEIVE_DATA RxData;\r
+ EFI_UDP4_SESSION_DATA *Udp4Session;\r
+ UINTN Enqueued;\r
+\r
+ //\r
+ // Get the datagram header from the packet buffer.\r
+ //\r
+ Udp4Header = (EFI_UDP4_HEADER *) NetbufGetByte (Packet, 0, NULL);\r
+\r
+ if (Udp4Header->Checksum != 0) {\r
+ //\r
+ // check the checksum.\r
+ //\r
+ HeadSum = NetPseudoHeadChecksum (\r
+ NetSession->Source,\r
+ NetSession->Dest,\r
+ EFI_IP_PROTO_UDP,\r
+ 0\r
+ );\r
+\r
+ if (Udp4Checksum (Packet, HeadSum) != 0) {\r
+ //\r
+ // Wrong checksum.\r
+ //\r
+ return;\r
+ }\r
+ }\r
+\r
+ gRT->GetTime (&RxData.TimeStamp, NULL);\r
+\r
+ Udp4Session = &RxData.UdpSession;\r
+ EFI_IP4 (Udp4Session->SourceAddress) = NetSession->Source;\r
+ EFI_IP4 (Udp4Session->DestinationAddress) = NetSession->Dest;\r
+ Udp4Session->SourcePort = NTOHS (Udp4Header->SrcPort);\r
+ Udp4Session->DestinationPort = NTOHS (Udp4Header->DstPort);\r
+\r
+ //\r
+ // Trim the UDP header.\r
+ //\r
+ NetbufTrim (Packet, UDP4_HEADER_SIZE, TRUE);\r
+\r
+ RxData.DataLength = (UINT32) Packet->TotalSize;\r
+\r
+ //\r
+ // Try to enqueue this datagram into the instances.\r
+ //\r
+ Enqueued = Udp4EnqueueDgram (Udp4Service, Packet, &RxData);\r
+\r
+ if (Enqueued == 0) {\r
+ //\r
+ // Send the port unreachable ICMP packet before we free this NET_BUF\r
+ //\r
+ Udp4SendPortUnreach (Udp4Service->IpIo, NetSession, Udp4Header);\r
+ }\r
+\r
+ //\r
+ // Try to free the packet before deliver it.\r
+ //\r
+ NetbufFree (Packet);\r
+\r
+ if (Enqueued > 0) {\r
+ //\r
+ // Deliver the datagram.\r
+ //\r
+ Udp4DeliverDgram (Udp4Service);\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ This function builds and sends out a icmp port unreachable message.\r
+\r
+ @param IpIo Pointer to the IP_IO instance.\r
+ @param NetSession Pointer to the EFI_NET_SESSION_DATA of the packet\r
+ causes this icmp error message.\r
+ @param Udp4Header Pointer to the udp header of the datagram causes\r
+ this icmp error message.\r
+\r
+ @return None.\r
+\r
+**/\r
+STATIC\r
+VOID\r
+Udp4SendPortUnreach (\r
+ IN IP_IO *IpIo,\r
+ IN EFI_NET_SESSION_DATA *NetSession,\r
+ IN VOID *Udp4Header\r
+ )\r
+{\r
+ NET_BUF *Packet;\r
+ UINT32 Len;\r
+ IP4_ICMP_ERROR_HEAD *IcmpErrHdr;\r
+ EFI_IP4_HEADER *IpHdr;\r
+ UINT8 *Ptr;\r
+ IP_IO_OVERRIDE Override;\r
+ IP_IO_IP_INFO *IpSender;\r
+\r
+ IpSender = IpIoFindSender (&IpIo, NetSession->Dest);\r
+ if (IpSender == NULL) {\r
+ //\r
+ // No apropriate sender, since we cannot send out the ICMP message through\r
+ // the default zero station address IP instance, abort.\r
+ //\r
+ return;\r
+ }\r
+\r
+ IpHdr = NetSession->IpHdr;\r
+\r
+ //\r
+ // Calculate the requried length of the icmp error message.\r
+ //\r
+ Len = sizeof (IP4_ICMP_ERROR_HEAD) + (EFI_IP4_HEADER_LEN (IpHdr) -\r
+ sizeof (IP4_HEAD)) + ICMP_ERROR_PACKET_LENGTH;\r
+\r
+ //\r
+ // Allocate buffer for the icmp error message.\r
+ //\r
+ Packet = NetbufAlloc (Len);\r
+ if (Packet == NULL) {\r
+ return;\r
+ }\r
+\r
+ //\r
+ // Allocate space for the IP4_ICMP_ERROR_HEAD.\r
+ //\r
+ IcmpErrHdr = (IP4_ICMP_ERROR_HEAD *) NetbufAllocSpace (Packet, Len, FALSE);\r
+\r
+ //\r
+ // Set the required fields for the icmp port unreachable message.\r
+ //\r
+ IcmpErrHdr->Head.Type = ICMP_TYPE_UNREACH;\r
+ IcmpErrHdr->Head.Code = ICMP_CODE_UNREACH_PORT;\r
+ IcmpErrHdr->Head.Checksum = 0;\r
+ IcmpErrHdr->Fourth = 0;\r
+\r
+ //\r
+ // Copy the IP header of the datagram tragged the error.\r
+ //\r
+ NetCopyMem (&IcmpErrHdr->IpHead, IpHdr, EFI_IP4_HEADER_LEN (IpHdr));\r
+\r
+ //\r
+ // Copy the UDP header.\r
+ //\r
+ Ptr = (UINT8 *) &IcmpErrHdr->IpHead + EFI_IP4_HEADER_LEN (IpHdr);\r
+ NetCopyMem (Ptr, Udp4Header, ICMP_ERROR_PACKET_LENGTH);\r
+\r
+ //\r
+ // Calculate the checksum.\r
+ //\r
+ IcmpErrHdr->Head.Checksum = ~(NetbufChecksum (Packet));\r
+\r
+ //\r
+ // Fill the override data.\r
+ //\r
+ Override.DoNotFragment = FALSE;\r
+ Override.TypeOfService = 0;\r
+ Override.TimeToLive = 255;\r
+ Override.Protocol = EFI_IP_PROTO_ICMP;\r
+ EFI_IP4 (Override.SourceAddress) = NetSession->Dest;\r
+ EFI_IP4 (Override.GatewayAddress) = 0;\r
+\r
+ //\r
+ // Send out this icmp packet.\r
+ //\r
+ IpIoSend (IpIo, Packet, IpSender, NULL, NULL, NetSession->Source, &Override);\r
+\r
+ NetbufFree (Packet);\r
+}\r
+\r
+\r
+/**\r
+ This function handles the received Icmp Error message and demultiplexes it to the\r
+ instance.\r
+\r
+ @param Udp4Service Pointer to the udp service context data.\r
+ @param IcmpError The icmp error code.\r
+ @param NetSession Pointer to the EFI_NET_SESSION_DATA abstracted\r
+ from the received Icmp Error packet.\r
+ @param Packet Pointer to the Icmp Error packet.\r
+\r
+ @return None.\r
+\r
+**/\r
+STATIC\r
+VOID\r
+Udp4IcmpHandler (\r
+ IN UDP4_SERVICE_DATA *Udp4Service,\r
+ IN ICMP_ERROR IcmpError,\r
+ IN EFI_NET_SESSION_DATA *NetSession,\r
+ IN NET_BUF *Packet\r
+ )\r
+{\r
+ EFI_UDP4_HEADER *Udp4Header;\r
+ EFI_UDP4_SESSION_DATA Udp4Session;\r
+ NET_LIST_ENTRY *Entry;\r
+ UDP4_INSTANCE_DATA *Instance;\r
+\r
+ Udp4Header = (EFI_UDP4_HEADER *) NetbufGetByte (Packet, 0, NULL);\r
+\r
+ EFI_IP4 (Udp4Session.SourceAddress) = NetSession->Source;\r
+ EFI_IP4 (Udp4Session.DestinationAddress) = NetSession->Dest;\r
+ Udp4Session.SourcePort = NTOHS (Udp4Header->DstPort);\r
+ Udp4Session.DestinationPort = NTOHS (Udp4Header->SrcPort);\r
+\r
+ NET_LIST_FOR_EACH (Entry, &Udp4Service->ChildrenList) {\r
+ //\r
+ // Iterate all the instances.\r
+ //\r
+ Instance = NET_LIST_USER_STRUCT (Entry, UDP4_INSTANCE_DATA, Link);\r
+\r
+ if (!Instance->Configured ||\r
+ Instance->ConfigData.AcceptPromiscuous ||\r
+ Instance->ConfigData.AcceptAnyPort ||\r
+ (EFI_IP4 (Instance->ConfigData.StationAddress) == 0)) {\r
+ //\r
+ // Don't try to deliver the ICMP error to this instance if it is not configured,\r
+ // or it's configured to be promiscuous or accept any port or accept all the\r
+ // datagrams.\r
+ //\r
+ continue;\r
+ }\r
+\r
+ if (Udp4MatchDgram (Instance, &Udp4Session)) {\r
+ //\r
+ // Translate the Icmp Error code according to the udp spec.\r
+ //\r
+ Instance->IcmpError = IpIoGetIcmpErrStatus (IcmpError, NULL, NULL);\r
+\r
+ if (IcmpError > ICMP_ERR_UNREACH_PORT) {\r
+ Instance->IcmpError = EFI_ICMP_ERROR;\r
+ }\r
+\r
+ //\r
+ // Notify the instance with the received Icmp Error.\r
+ //\r
+ Udp4ReportIcmpError (Instance);\r
+\r
+ break;\r
+ }\r
+ }\r
+\r
+ NetbufFree (Packet);\r
+}\r
+\r
+\r
+/**\r
+ This function reports the received ICMP error.\r
+\r
+ @param Instance Pointer to the udp instance context data.\r
+\r
+ @return None.\r
+\r
+**/\r
+VOID\r
+Udp4ReportIcmpError (\r
+ IN UDP4_INSTANCE_DATA *Instance\r
+ )\r
+{\r
+ EFI_UDP4_COMPLETION_TOKEN *Token;\r
+\r
+ if (NetMapIsEmpty (&Instance->RxTokens)) {\r
+ //\r
+ // There are no receive tokens to deliver the ICMP error.\r
+ //\r
+ return;\r
+ }\r
+\r
+ if (EFI_ERROR (Instance->IcmpError)) {\r
+ //\r
+ // Try to get a RxToken from the RxTokens map.\r
+ //\r
+ Token = (EFI_UDP4_COMPLETION_TOKEN *) NetMapRemoveHead (&Instance->RxTokens, NULL);\r
+\r
+ if (Token != NULL) {\r
+ //\r
+ // Report the error through the Token.\r
+ //\r
+ Token->Status = Instance->IcmpError;\r
+ gBS->SignalEvent (Token->Event);\r
+\r
+ //\r
+ // Clear the IcmpError.\r
+ //\r
+ Instance->IcmpError = EFI_SUCCESS;\r
+ }\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ This function is a dummy ext-free function for the NET_BUF created for the output\r
+ udp datagram.\r
+\r
+ @param Context Pointer to the context data.\r
+\r
+ @return None.\r
+\r
+**/\r
+VOID\r
+Udp4NetVectorExtFree (\r
+ VOID *Context\r
+ )\r
+{\r
+}\r
+\r
+\r
+/**\r
+ Set the Udp4 variable data.\r
+\r
+ @param Udp4Service Udp4 service data.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources to set the\r
+ variable.\r
+ @retval other Set variable failed.\r
+\r
+**/\r
+EFI_STATUS\r
+Udp4SetVariableData (\r
+ IN UDP4_SERVICE_DATA *Udp4Service\r
+ )\r
+{\r
+ UINT32 NumConfiguredInstance;\r
+ NET_LIST_ENTRY *Entry;\r
+ UINTN VariableDataSize;\r
+ EFI_UDP4_VARIABLE_DATA *Udp4VariableData;\r
+ EFI_UDP4_SERVICE_POINT *Udp4ServicePoint;\r
+ UDP4_INSTANCE_DATA *Udp4Instance;\r
+ CHAR16 *NewMacString;\r
+ EFI_STATUS Status;\r
+\r
+ NumConfiguredInstance = 0;\r
+\r
+ //\r
+ // Go through the children list to count the configured children.\r
+ //\r
+ NET_LIST_FOR_EACH (Entry, &Udp4Service->ChildrenList) {\r
+ Udp4Instance = NET_LIST_USER_STRUCT_S (\r
+ Entry,\r
+ UDP4_INSTANCE_DATA,\r
+ Link,\r
+ UDP4_INSTANCE_DATA_SIGNATURE\r
+ );\r
+\r
+ if (Udp4Instance->Configured) {\r
+ NumConfiguredInstance++;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Calculate the size of the Udp4VariableData. As there may be no Udp4 child,\r
+ // we should add extra buffer for the service points only if the number of configured\r
+ // children is more than 1.\r
+ //\r
+ VariableDataSize = sizeof (EFI_UDP4_VARIABLE_DATA);\r
+\r
+ if (NumConfiguredInstance > 1) {\r
+ VariableDataSize += sizeof (EFI_UDP4_SERVICE_POINT) * (NumConfiguredInstance - 1);\r
+ }\r
+\r
+ Udp4VariableData = NetAllocatePool (VariableDataSize);\r
+ if (Udp4VariableData == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Udp4VariableData->DriverHandle = Udp4Service->ImageHandle;\r
+ Udp4VariableData->ServiceCount = NumConfiguredInstance;\r
+\r
+ Udp4ServicePoint = &Udp4VariableData->Services[0];\r
+\r
+ //\r
+ // Go through the children list to fill the configured children's address pairs.\r
+ //\r
+ NET_LIST_FOR_EACH (Entry, &Udp4Service->ChildrenList) {\r
+ Udp4Instance = NET_LIST_USER_STRUCT_S (\r
+ Entry,\r
+ UDP4_INSTANCE_DATA,\r
+ Link,\r
+ UDP4_INSTANCE_DATA_SIGNATURE\r
+ );\r
+\r
+ if (Udp4Instance->Configured) {\r
+ Udp4ServicePoint->InstanceHandle = Udp4Instance->ChildHandle;\r
+ Udp4ServicePoint->LocalAddress = Udp4Instance->ConfigData.StationAddress;\r
+ Udp4ServicePoint->LocalPort = Udp4Instance->ConfigData.StationPort;\r
+ Udp4ServicePoint->RemoteAddress = Udp4Instance->ConfigData.RemoteAddress;\r
+ Udp4ServicePoint->RemotePort = Udp4Instance->ConfigData.RemotePort;\r
+\r
+ Udp4ServicePoint++;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Get the mac string.\r
+ //\r
+ Status = NetLibGetMacString (\r
+ Udp4Service->ControllerHandle,\r
+ Udp4Service->ImageHandle,\r
+ &NewMacString\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ if (Udp4Service->MacString != NULL) {\r
+ //\r
+ // The variable is set already, we're going to update it.\r
+ //\r
+ if (StrCmp (Udp4Service->MacString, NewMacString) != 0) {\r
+ //\r
+ // The mac address is changed, delete the previous variable first.\r
+ //\r
+ gRT->SetVariable (\r
+ Udp4Service->MacString,\r
+ &gEfiUdp4ServiceBindingProtocolGuid,\r
+ EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
+ 0,\r
+ NULL\r
+ );\r
+ }\r
+\r
+ NetFreePool (Udp4Service->MacString);\r
+ }\r
+\r
+ Udp4Service->MacString = NewMacString;\r
+\r
+ Status = gRT->SetVariable (\r
+ Udp4Service->MacString,\r
+ &gEfiUdp4ServiceBindingProtocolGuid,\r
+ EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
+ VariableDataSize,\r
+ (VOID *) Udp4VariableData\r
+ );\r
+\r
+ON_ERROR:\r
+\r
+ NetFreePool (Udp4VariableData);\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Clear the variable and free the resource.\r
+\r
+ @param Udp4Service Udp4 service data.\r
+\r
+ @return None.\r
+\r
+**/\r
+VOID\r
+Udp4ClearVariableData (\r
+ IN UDP4_SERVICE_DATA *Udp4Service\r
+ )\r
+{\r
+ ASSERT (Udp4Service->MacString != NULL);\r
+\r
+ gRT->SetVariable (\r
+ Udp4Service->MacString,\r
+ &gEfiUdp4ServiceBindingProtocolGuid,\r
+ EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
+ 0,\r
+ NULL\r
+ );\r
+\r
+ NetFreePool (Udp4Service->MacString);\r
+ Udp4Service->MacString = NULL;\r
+}\r