/** @file | |
The implementation of the Udp4 protocol. | |
Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR> | |
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 <Address, Port> 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 <Address, Port> 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 <Address, Port> 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 <Address, Port> 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 <Address, Port> 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 | |
) | |
{ | |
} |