/** @file | |
Udp6 driver's whole implementation. | |
Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include "Udp6Impl.h" | |
UINT16 mUdp6RandomPort; | |
/** | |
This function checks and timeouts the I/O datagrams holding by the corresponding | |
service context. | |
@param[in] Event The event this function is registered to. | |
@param[in] Context The context data registered during the creation of | |
the Event. | |
**/ | |
VOID | |
EFIAPI | |
Udp6CheckTimeout ( | |
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 IPv6 address. | |
@param[in] Port The udp port number. | |
@retval TRUE The specified <Address, Port> pair is found. | |
@retval FALSE Otherwise. | |
**/ | |
BOOLEAN | |
Udp6FindInstanceByPort ( | |
IN LIST_ENTRY *InstanceList, | |
IN EFI_IPv6_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 the IpIo layer completes | |
transmitting of the udp datagram. | |
If Context is NULL, then ASSERT(). | |
If NotifyData is NULL, then ASSERT(). | |
@param[in] Status The completion status of the output udp datagram. | |
@param[in] Context Pointer to the context data. | |
@param[in] Sender Specify a EFI_IP6_PROTOCOL for sending. | |
@param[in] NotifyData Pointer to the notify data. | |
**/ | |
VOID | |
EFIAPI | |
Udp6DgramSent ( | |
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. | |
If NetSession is NULL, then ASSERT(). | |
If Packet is NULL, then ASSERT(). | |
If Context is NULL, then ASSERT(). | |
@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 | |
Udp6DgramRcvd ( | |
IN EFI_STATUS Status, | |
IN UINT8 IcmpError, | |
IN EFI_NET_SESSION_DATA *NetSession, | |
IN NET_BUF *Packet, | |
IN VOID *Context | |
); | |
/** | |
This function cancel the token specified by Arg in the Map. | |
@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, all | |
the tokens in this Map will be cancelled. | |
This parameter is optional and may be NULL. | |
@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 | |
Udp6CancelTokens ( | |
IN NET_MAP *Map, | |
IN NET_MAP_ITEM *Item, | |
IN VOID *Arg OPTIONAL | |
); | |
/** | |
This function check if the received udp datagram matches with the Instance. | |
@param[in] Instance Pointer to the udp instance context data. | |
@param[in] Udp6Session Pointer to the EFI_UDP6_SESSION_DATA abstracted | |
from the received udp datagram. | |
@retval TRUE The udp datagram matches the receiving requirements of the Instance. | |
@retval FALSE The udp datagram doe not match the receiving requirements of the Instance. | |
**/ | |
BOOLEAN | |
Udp6MatchDgram ( | |
IN UDP6_INSTANCE_DATA *Instance, | |
IN EFI_UDP6_SESSION_DATA *Udp6Session | |
); | |
/** | |
This function removes the Wrap specified by Context and releases relevant resources. | |
@param[in] Event The Event this notify function is registered to. | |
@param[in] Context Pointer to the context data. | |
**/ | |
VOID | |
EFIAPI | |
Udp6RecycleRxDataWrap ( | |
IN EFI_EVENT Event, | |
IN VOID *Context | |
); | |
/** | |
This function wraps the Packet into 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_UDP6_RECEIVE_DATA of this | |
datagram. | |
@return Pointer to the structure wrapping the RxData and the Packet. NULL will | |
be returned if any error occurs. | |
**/ | |
UDP6_RXDATA_WRAP * | |
Udp6WrapRxData ( | |
IN UDP6_INSTANCE_DATA *Instance, | |
IN NET_BUF *Packet, | |
IN EFI_UDP6_RECEIVE_DATA *RxData | |
); | |
/** | |
This function enqueues the received datagram into the instances' receiving queues. | |
@param[in] Udp6Service 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_UDP6_RECEIVE_DATA of this | |
datagram. | |
@return The times this datagram is enqueued. | |
**/ | |
UINTN | |
Udp6EnqueueDgram ( | |
IN UDP6_SERVICE_DATA *Udp6Service, | |
IN NET_BUF *Packet, | |
IN EFI_UDP6_RECEIVE_DATA *RxData | |
); | |
/** | |
This function delivers the datagrams enqueued in the instances. | |
@param[in] Udp6Service Pointer to the udp service context data. | |
**/ | |
VOID | |
Udp6DeliverDgram ( | |
IN UDP6_SERVICE_DATA *Udp6Service | |
); | |
/** | |
This function demultiplexes the received udp datagram to the appropriate instances. | |
@param[in] Udp6Service 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 | |
Udp6Demultiplex ( | |
IN UDP6_SERVICE_DATA *Udp6Service, | |
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] Udp6Service 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, out] Packet Pointer to the Icmp Error packet. | |
**/ | |
VOID | |
Udp6IcmpHandler ( | |
IN UDP6_SERVICE_DATA *Udp6Service, | |
IN UINT8 IcmpError, | |
IN EFI_NET_SESSION_DATA *NetSession, | |
IN OUT 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] Udp6Header Pointer to the udp header of the datagram causes | |
this icmp error message. | |
**/ | |
VOID | |
Udp6SendPortUnreach ( | |
IN IP_IO *IpIo, | |
IN EFI_NET_SESSION_DATA *NetSession, | |
IN VOID *Udp6Header | |
); | |
/** | |
Find the key in the netmap | |
@param[in] Map The netmap to search within. | |
@param[in] Key The key to search. | |
@return The point to the item contains the Key, or NULL if Key isn't in the map. | |
**/ | |
NET_MAP_ITEM * | |
Udp6MapMultiCastAddr ( | |
IN NET_MAP *Map, | |
IN VOID *Key | |
); | |
/** | |
Create the Udp service context data. | |
@param[in] Udp6Service Pointer to the UDP6_SERVICE_DATA. | |
@param[in] ImageHandle The image handle of this udp6 driver. | |
@param[in] ControllerHandle The controller handle this udp6 driver binds on. | |
@retval EFI_SUCCESS The udp6 service context data was created and | |
initialized. | |
@retval EFI_OUT_OF_RESOURCES Cannot allocate memory. | |
@retval Others An error condition occurred. | |
**/ | |
EFI_STATUS | |
Udp6CreateService ( | |
IN UDP6_SERVICE_DATA *Udp6Service, | |
IN EFI_HANDLE ImageHandle, | |
IN EFI_HANDLE ControllerHandle | |
) | |
{ | |
EFI_STATUS Status; | |
IP_IO_OPEN_DATA OpenData; | |
ZeroMem (Udp6Service, sizeof (UDP6_SERVICE_DATA)); | |
Udp6Service->Signature = UDP6_SERVICE_DATA_SIGNATURE; | |
Udp6Service->ServiceBinding = mUdp6ServiceBinding; | |
Udp6Service->ImageHandle = ImageHandle; | |
Udp6Service->ControllerHandle = ControllerHandle; | |
Udp6Service->ChildrenNumber = 0; | |
InitializeListHead (&Udp6Service->ChildrenList); | |
// | |
// Create the IpIo for this service context. | |
// | |
Udp6Service->IpIo = IpIoCreate (ImageHandle, ControllerHandle, IP_VERSION_6); | |
if (Udp6Service->IpIo == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
// | |
// Set the OpenData used to open the IpIo. | |
// | |
CopyMem ( | |
&OpenData.IpConfigData.Ip6CfgData, | |
&mIp6IoDefaultIpConfigData, | |
sizeof (EFI_IP6_CONFIG_DATA) | |
); | |
OpenData.RcvdContext = (VOID *)Udp6Service; | |
OpenData.SndContext = NULL; | |
OpenData.PktRcvdNotify = Udp6DgramRcvd; | |
OpenData.PktSentNotify = Udp6DgramSent; | |
// | |
// Configure and start the IpIo. | |
// | |
Status = IpIoOpen (Udp6Service->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, | |
Udp6CheckTimeout, | |
Udp6Service, | |
&Udp6Service->TimeoutEvent | |
); | |
if (EFI_ERROR (Status)) { | |
goto ON_ERROR; | |
} | |
// | |
// Start the timeout timer event. | |
// | |
Status = gBS->SetTimer ( | |
Udp6Service->TimeoutEvent, | |
TimerPeriodic, | |
UDP6_TIMEOUT_INTERVAL | |
); | |
if (EFI_ERROR (Status)) { | |
goto ON_ERROR; | |
} | |
return EFI_SUCCESS; | |
ON_ERROR: | |
if (Udp6Service->TimeoutEvent != NULL) { | |
gBS->CloseEvent (Udp6Service->TimeoutEvent); | |
} | |
IpIoDestroy (Udp6Service->IpIo); | |
Udp6Service->IpIo = NULL; | |
return Status; | |
} | |
/** | |
Clean the Udp service context data. | |
@param[in, out] Udp6Service Pointer to the UDP6_SERVICE_DATA. | |
**/ | |
VOID | |
Udp6CleanService ( | |
IN OUT UDP6_SERVICE_DATA *Udp6Service | |
) | |
{ | |
// | |
// Close the TimeoutEvent timer. | |
// | |
gBS->CloseEvent (Udp6Service->TimeoutEvent); | |
// | |
// Destroy the IpIo. | |
// | |
IpIoDestroy (Udp6Service->IpIo); | |
Udp6Service->IpIo = NULL; | |
ZeroMem (Udp6Service, sizeof (UDP6_SERVICE_DATA)); | |
} | |
/** | |
This function checks and times out the I/O datagrams listed in the | |
UDP6_SERVICE_DATA which is specified by the input parameter 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 | |
Udp6CheckTimeout ( | |
IN EFI_EVENT Event, | |
IN VOID *Context | |
) | |
{ | |
UDP6_SERVICE_DATA *Udp6Service; | |
LIST_ENTRY *Entry; | |
UDP6_INSTANCE_DATA *Instance; | |
LIST_ENTRY *WrapEntry; | |
LIST_ENTRY *NextEntry; | |
UDP6_RXDATA_WRAP *Wrap; | |
Udp6Service = (UDP6_SERVICE_DATA *)Context; | |
NET_CHECK_SIGNATURE (Udp6Service, UDP6_SERVICE_DATA_SIGNATURE); | |
NET_LIST_FOR_EACH (Entry, &Udp6Service->ChildrenList) { | |
// | |
// Iterate all the instances belonging to this service context. | |
// | |
Instance = NET_LIST_USER_STRUCT (Entry, UDP6_INSTANCE_DATA, Link); | |
NET_CHECK_SIGNATURE (Instance, UDP6_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, UDP6_RXDATA_WRAP, Link); | |
if (Wrap->TimeoutTick < UDP6_TIMEOUT_INTERVAL / 10) { | |
// | |
// Remove this RxData if it timeouts. | |
// | |
Udp6RecycleRxDataWrap (NULL, (VOID *)Wrap); | |
} else { | |
Wrap->TimeoutTick -= UDP6_TIMEOUT_INTERVAL / 10; | |
} | |
} | |
} | |
} | |
/** | |
This function initializes the new created udp instance. | |
@param[in] Udp6Service Pointer to the UDP6_SERVICE_DATA. | |
@param[in, out] Instance Pointer to the un-initialized UDP6_INSTANCE_DATA. | |
**/ | |
VOID | |
Udp6InitInstance ( | |
IN UDP6_SERVICE_DATA *Udp6Service, | |
IN OUT UDP6_INSTANCE_DATA *Instance | |
) | |
{ | |
// | |
// Set the signature. | |
// | |
Instance->Signature = UDP6_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 UDP6_SERVICE_DATA, and initialize other members. | |
// | |
Instance->Udp6Service = Udp6Service; | |
CopyMem (&Instance->Udp6Proto, &mUdp6Protocol, sizeof (EFI_UDP6_PROTOCOL)); | |
Instance->IcmpError = EFI_SUCCESS; | |
Instance->Configured = FALSE; | |
Instance->IsNoMapping = FALSE; | |
Instance->InDestroy = FALSE; | |
} | |
/** | |
This function cleans the udp instance. | |
@param[in, out] Instance Pointer to the UDP6_INSTANCE_DATA to clean. | |
**/ | |
VOID | |
Udp6CleanInstance ( | |
IN OUT UDP6_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 IPv6 address. | |
@param[in] Port The udp port number. | |
@retval TRUE The specified <Address, Port> pair is found. | |
@retval FALSE Otherwise. | |
**/ | |
BOOLEAN | |
Udp6FindInstanceByPort ( | |
IN LIST_ENTRY *InstanceList, | |
IN EFI_IPv6_ADDRESS *Address, | |
IN UINT16 Port | |
) | |
{ | |
LIST_ENTRY *Entry; | |
UDP6_INSTANCE_DATA *Instance; | |
EFI_UDP6_CONFIG_DATA *ConfigData; | |
NET_LIST_FOR_EACH (Entry, InstanceList) { | |
// | |
// Iterate all the udp instances. | |
// | |
Instance = NET_LIST_USER_STRUCT (Entry, UDP6_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_IP6_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] ConfigData Pointer to the ConfigData of the instance to be | |
bound. | |
@retval EFI_SUCCESS The bound operation 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 | |
Udp6Bind ( | |
IN LIST_ENTRY *InstanceList, | |
IN EFI_UDP6_CONFIG_DATA *ConfigData | |
) | |
{ | |
EFI_IPv6_ADDRESS *StationAddress; | |
UINT16 StartPort; | |
if (ConfigData->AcceptAnyPort) { | |
return EFI_SUCCESS; | |
} | |
StationAddress = &ConfigData->StationAddress; | |
if (ConfigData->StationPort != 0) { | |
if (!ConfigData->AllowDuplicatePort && | |
Udp6FindInstanceByPort (InstanceList, StationAddress, ConfigData->StationPort) | |
) | |
{ | |
// | |
// Do not allow duplicate ports 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 = mUdp6RandomPort; | |
} else { | |
StartPort = mUdp6RandomPort; | |
while (Udp6FindInstanceByPort (InstanceList, StationAddress, mUdp6RandomPort)) { | |
mUdp6RandomPort++; | |
if (mUdp6RandomPort == 0) { | |
mUdp6RandomPort = UDP6_PORT_KNOWN; | |
} | |
if (mUdp6RandomPort == StartPort) { | |
// | |
// No available port. | |
// | |
return EFI_OUT_OF_RESOURCES; | |
} | |
} | |
ConfigData->StationPort = mUdp6RandomPort; | |
} | |
mUdp6RandomPort++; | |
if (mUdp6RandomPort == 0) { | |
mUdp6RandomPort = UDP6_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 according to the NewConfigData. | |
@retval FALSE Otherwise. | |
**/ | |
BOOLEAN | |
Udp6IsReconfigurable ( | |
IN EFI_UDP6_CONFIG_DATA *OldConfigData, | |
IN EFI_UDP6_CONFIG_DATA *NewConfigData | |
) | |
{ | |
if ((NewConfigData->AcceptAnyPort != OldConfigData->AcceptAnyPort) || | |
(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 (!EFI_IP6_EQUAL (&NewConfigData->StationAddress, &OldConfigData->StationAddress)) { | |
// | |
// The StationAddress is not the same. | |
// | |
return FALSE; | |
} | |
if (!EFI_IP6_EQUAL (&NewConfigData->RemoteAddress, &OldConfigData->RemoteAddress)) { | |
// | |
// The remoteaddress is not the same. | |
// | |
return FALSE; | |
} | |
if (!NetIp6IsUnspecifiedAddr (&NewConfigData->RemoteAddress) && | |
(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 Ip6 configdata from the Udp6ConfigData. | |
@param[in] Udp6ConfigData Pointer to the EFI_UDP6_CONFIG_DATA. | |
@param[in, out] Ip6ConfigData Pointer to the EFI_IP6_CONFIG_DATA. | |
**/ | |
VOID | |
Udp6BuildIp6ConfigData ( | |
IN EFI_UDP6_CONFIG_DATA *Udp6ConfigData, | |
IN OUT EFI_IP6_CONFIG_DATA *Ip6ConfigData | |
) | |
{ | |
CopyMem ( | |
Ip6ConfigData, | |
&mIp6IoDefaultIpConfigData, | |
sizeof (EFI_IP6_CONFIG_DATA) | |
); | |
Ip6ConfigData->DefaultProtocol = EFI_IP_PROTO_UDP; | |
Ip6ConfigData->AcceptPromiscuous = Udp6ConfigData->AcceptPromiscuous; | |
IP6_COPY_ADDRESS (&Ip6ConfigData->StationAddress, &Udp6ConfigData->StationAddress); | |
IP6_COPY_ADDRESS (&Ip6ConfigData->DestinationAddress, &Udp6ConfigData->RemoteAddress); | |
// | |
// Use the -1 magic number to disable the receiving process of the ip instance. | |
// | |
Ip6ConfigData->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: | |
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; | |
UdpSessionData.DestinationAddress are not valid | |
unicast IPv6 addresses if the UdpSessionData is | |
not NULL; | |
UdpSessionData.DestinationPort and | |
ConfigData.RemotePort are all zero if the | |
UdpSessionData is not NULL. | |
@retval EFI_BAD_BUFFER_SIZE The data length is greater than the maximum UDP | |
packet size. | |
**/ | |
EFI_STATUS | |
Udp6ValidateTxToken ( | |
IN UDP6_INSTANCE_DATA *Instance, | |
IN EFI_UDP6_COMPLETION_TOKEN *TxToken | |
) | |
{ | |
EFI_UDP6_TRANSMIT_DATA *TxData; | |
UINT32 Index; | |
UINT32 TotalLen; | |
EFI_UDP6_CONFIG_DATA *ConfigData; | |
EFI_UDP6_SESSION_DATA *UdpSessionData; | |
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; | |
} | |
ConfigData = &Instance->ConfigData; | |
UdpSessionData = TxData->UdpSessionData; | |
if (UdpSessionData != NULL) { | |
if ((UdpSessionData->DestinationPort == 0) && (ConfigData->RemotePort == 0)) { | |
// | |
// Ambiguous; no available DestinationPort for this token. | |
// | |
return EFI_INVALID_PARAMETER; | |
} | |
if (NetIp6IsUnspecifiedAddr (&UdpSessionData->DestinationAddress) && | |
NetIp6IsUnspecifiedAddr (&ConfigData->RemoteAddress) | |
) | |
{ | |
// | |
// The DestinationAddress is not specified. | |
// | |
return EFI_INVALID_PARAMETER; | |
} | |
if (!NetIp6IsUnspecifiedAddr (&UdpSessionData->DestinationAddress) && | |
!NetIp6IsUnspecifiedAddr (&ConfigData->RemoteAddress) | |
) | |
{ | |
// | |
// The ConfigData.RemoteAddress is not zero and the UdpSessionData.DestinationAddress | |
// is not zero too. | |
// | |
return EFI_INVALID_PARAMETER; | |
} | |
} else if (NetIp6IsUnspecifiedAddr (&ConfigData->RemoteAddress)) { | |
// | |
// The configured RemoteAddress is all zero, and the user doesn't override the | |
// destination address. | |
// | |
return EFI_INVALID_PARAMETER; | |
} | |
if (TxData->DataLength > UDP6_MAX_DATA_SIZE) { | |
return EFI_BAD_BUFFER_SIZE; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
This function checks whether the specified Token duplicates 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 | |
Udp6TokenExist ( | |
IN NET_MAP *Map, | |
IN NET_MAP_ITEM *Item, | |
IN VOID *Context | |
) | |
{ | |
EFI_UDP6_COMPLETION_TOKEN *Token; | |
EFI_UDP6_COMPLETION_TOKEN *TokenInItem; | |
Token = (EFI_UDP6_COMPLETION_TOKEN *)Context; | |
TokenInItem = (EFI_UDP6_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. | |
@return The 16-bit checksum of this udp datagram. | |
**/ | |
UINT16 | |
Udp6Checksum ( | |
IN NET_BUF *Packet, | |
IN UINT16 HeadSum | |
) | |
{ | |
UINT16 Checksum; | |
Checksum = NetbufChecksum (Packet); | |
Checksum = NetAddChecksum (Checksum, HeadSum); | |
Checksum = NetAddChecksum (Checksum, HTONS ((UINT16)Packet->TotalSize)); | |
Checksum = (UINT16)(~Checksum); | |
return Checksum; | |
} | |
/** | |
This function removes the specified Token from the TokenMap. | |
@param[in] 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 | |
Udp6RemoveToken ( | |
IN NET_MAP *TokenMap, | |
IN EFI_UDP6_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. | |
If Context is NULL, then ASSERT(). | |
If NotifyData is NULL, then ASSERT(). | |
@param[in] Status The completion status of the output udp datagram. | |
@param[in] Context Pointer to the context data. | |
@param[in] Sender Specify a EFI_IP6_PROTOCOL for sending. | |
@param[in] NotifyData Pointer to the notify data. | |
**/ | |
VOID | |
EFIAPI | |
Udp6DgramSent ( | |
IN EFI_STATUS Status, | |
IN VOID *Context, | |
IN IP_IO_IP_PROTOCOL Sender, | |
IN VOID *NotifyData | |
) | |
{ | |
UDP6_INSTANCE_DATA *Instance; | |
EFI_UDP6_COMPLETION_TOKEN *Token; | |
ASSERT (Context != NULL && NotifyData != NULL); | |
Instance = (UDP6_INSTANCE_DATA *)Context; | |
Token = (EFI_UDP6_COMPLETION_TOKEN *)NotifyData; | |
if (Udp6RemoveToken (&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. | |
If NetSession is NULL, then ASSERT(). | |
If Packet is NULL, then ASSERT(). | |
If Context is NULL, then ASSERT(). | |
@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 | |
Udp6DgramRcvd ( | |
IN EFI_STATUS Status, | |
IN UINT8 IcmpError, | |
IN EFI_NET_SESSION_DATA *NetSession, | |
IN NET_BUF *Packet, | |
IN VOID *Context | |
) | |
{ | |
ASSERT (NetSession != NULL && Packet != NULL && Context != NULL); | |
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. | |
// | |
Udp6Demultiplex ((UDP6_SERVICE_DATA *)Context, NetSession, Packet); | |
} else { | |
// | |
// Handle the ICMP6 Error packet. | |
// | |
Udp6IcmpHandler ((UDP6_SERVICE_DATA *)Context, IcmpError, NetSession, Packet); | |
} | |
// | |
// Dispatch the DPC queued by the NotifyFunction of the rx token's events | |
// that are signaled with received data. | |
// | |
DispatchDpc (); | |
} | |
/** | |
This function removes the multicast group specified by Arg from the Map. | |
@param[in] 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 IPv6 Address. This parameter is | |
optional and may be NULL. | |
@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 | |
Udp6LeaveGroup ( | |
IN NET_MAP *Map, | |
IN NET_MAP_ITEM *Item, | |
IN VOID *Arg OPTIONAL | |
) | |
{ | |
EFI_IPv6_ADDRESS *McastIp; | |
McastIp = Arg; | |
if ((McastIp != NULL) && | |
!EFI_IP6_EQUAL (McastIp, ((EFI_IPv6_ADDRESS *)Item->Key)) | |
) | |
{ | |
// | |
// McastIp is not NULL and the multicast address contained in the Item | |
// is not the same as McastIp. | |
// | |
return EFI_SUCCESS; | |
} | |
FreePool (Item->Key); | |
// | |
// 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 cancel the token specified by Arg in the Map. | |
@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, all | |
the tokens in this Map will be cancelled. | |
This parameter is optional and may be NULL. | |
@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 | |
Udp6CancelTokens ( | |
IN NET_MAP *Map, | |
IN NET_MAP_ITEM *Item, | |
IN VOID *Arg OPTIONAL | |
) | |
{ | |
EFI_UDP6_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 Udp6DgramSent, 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_UDP6_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 Udp6 Instance. | |
**/ | |
VOID | |
Udp6FlushRcvdDgram ( | |
IN UDP6_INSTANCE_DATA *Instance | |
) | |
{ | |
UDP6_RXDATA_WRAP *Wrap; | |
while (!IsListEmpty (&Instance->RcvdDgramQue)) { | |
// | |
// Iterate all the Wraps in the RcvdDgramQue. | |
// | |
Wrap = NET_LIST_HEAD (&Instance->RcvdDgramQue, UDP6_RXDATA_WRAP, Link); | |
// | |
// The Wrap will be removed from the RcvdDgramQue by this function call. | |
// | |
Udp6RecycleRxDataWrap (NULL, (VOID *)Wrap); | |
} | |
} | |
/** | |
Cancel Udp6 tokens from the Udp6 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. | |
This parameter is optional and may be NULL. | |
@retval EFI_SUCCESS The Token is cancelled. | |
@retval EFI_NOT_FOUND The Token is not found. | |
**/ | |
EFI_STATUS | |
Udp6InstanceCancelToken ( | |
IN UDP6_INSTANCE_DATA *Instance, | |
IN EFI_UDP6_COMPLETION_TOKEN *Token OPTIONAL | |
) | |
{ | |
EFI_STATUS Status; | |
// | |
// Cancel this token from the TxTokens map. | |
// | |
Status = NetMapIterate (&Instance->TxTokens, Udp6CancelTokens, Token); | |
if ((Token != NULL) && (Status == EFI_ABORTED)) { | |
// | |
// If Token isn't NULL and Status is EFI_ABORTED, the token is cancelled from | |
// the TxTokens and returns 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, Udp6CancelTokens, 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 checks if the received udp datagram matches with the Instance. | |
@param[in] Instance Pointer to the udp instance context data. | |
@param[in] Udp6Session Pointer to the EFI_UDP6_SESSION_DATA abstracted | |
from the received udp datagram. | |
@retval TRUE The udp datagram matches the receiving requirements of the Instance. | |
@retval FALSE The udp datagram does not match the receiving requirements of the Instance. | |
**/ | |
BOOLEAN | |
Udp6MatchDgram ( | |
IN UDP6_INSTANCE_DATA *Instance, | |
IN EFI_UDP6_SESSION_DATA *Udp6Session | |
) | |
{ | |
EFI_UDP6_CONFIG_DATA *ConfigData; | |
EFI_IPv6_ADDRESS Destination; | |
ConfigData = &Instance->ConfigData; | |
if (ConfigData->AcceptPromiscuous) { | |
// | |
// Always matches if this instance is in the promiscuous state. | |
// | |
return TRUE; | |
} | |
if ((!ConfigData->AcceptAnyPort && (Udp6Session->DestinationPort != ConfigData->StationPort)) || | |
((ConfigData->RemotePort != 0) && (Udp6Session->SourcePort != ConfigData->RemotePort)) | |
) | |
{ | |
// | |
// The local port or the remote port doesn't match. | |
// | |
return FALSE; | |
} | |
if (!NetIp6IsUnspecifiedAddr (&ConfigData->RemoteAddress) && | |
!EFI_IP6_EQUAL (&ConfigData->RemoteAddress, &Udp6Session->SourceAddress) | |
) | |
{ | |
// | |
// This datagram doesn't come from the instance's specified sender. | |
// | |
return FALSE; | |
} | |
if (NetIp6IsUnspecifiedAddr (&ConfigData->StationAddress) || | |
EFI_IP6_EQUAL (&Udp6Session->DestinationAddress, &ConfigData->StationAddress) | |
) | |
{ | |
// | |
// The instance is configured to receive datagrams destinated to any station IP or | |
// the destination address of this datagram matches the configured station IP. | |
// | |
return TRUE; | |
} | |
IP6_COPY_ADDRESS (&Destination, &Udp6Session->DestinationAddress); | |
if (IP6_IS_MULTICAST (&Destination) && | |
(NULL != Udp6MapMultiCastAddr (&Instance->McastIps, &Destination)) | |
) | |
{ | |
// | |
// 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 | |
Udp6RecycleRxDataWrap ( | |
IN EFI_EVENT Event, | |
IN VOID *Context | |
) | |
{ | |
UDP6_RXDATA_WRAP *Wrap; | |
Wrap = (UDP6_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 into 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_UDP6_RECEIVE_DATA of this | |
datagram. | |
@return Pointer to the structure wrapping the RxData and the Packet. NULL will | |
be returned if any error occurs. | |
**/ | |
UDP6_RXDATA_WRAP * | |
Udp6WrapRxData ( | |
IN UDP6_INSTANCE_DATA *Instance, | |
IN NET_BUF *Packet, | |
IN EFI_UDP6_RECEIVE_DATA *RxData | |
) | |
{ | |
EFI_STATUS Status; | |
UDP6_RXDATA_WRAP *Wrap; | |
// | |
// Allocate buffer for the Wrap. | |
// | |
Wrap = AllocateZeroPool ( | |
sizeof (UDP6_RXDATA_WRAP) + | |
(Packet->BlockOpNum - 1) * sizeof (EFI_UDP6_FRAGMENT_DATA) | |
); | |
if (Wrap == NULL) { | |
return NULL; | |
} | |
InitializeListHead (&Wrap->Link); | |
CopyMem (&Wrap->RxData, RxData, sizeof (EFI_UDP6_RECEIVE_DATA)); | |
// | |
// Create the Recycle event. | |
// | |
Status = gBS->CreateEvent ( | |
EVT_NOTIFY_SIGNAL, | |
TPL_NOTIFY, | |
Udp6RecycleRxDataWrap, | |
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] Udp6Service 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_UDP6_RECEIVE_DATA of this | |
datagram. | |
@return The times this datagram is enqueued. | |
**/ | |
UINTN | |
Udp6EnqueueDgram ( | |
IN UDP6_SERVICE_DATA *Udp6Service, | |
IN NET_BUF *Packet, | |
IN EFI_UDP6_RECEIVE_DATA *RxData | |
) | |
{ | |
LIST_ENTRY *Entry; | |
UDP6_INSTANCE_DATA *Instance; | |
UDP6_RXDATA_WRAP *Wrap; | |
UINTN Enqueued; | |
Enqueued = 0; | |
NET_LIST_FOR_EACH (Entry, &Udp6Service->ChildrenList) { | |
// | |
// Iterate the instances. | |
// | |
Instance = NET_LIST_USER_STRUCT (Entry, UDP6_INSTANCE_DATA, Link); | |
if (!Instance->Configured) { | |
continue; | |
} | |
if (Udp6MatchDgram (Instance, &RxData->UdpSession)) { | |
// | |
// Wrap the RxData and put this Wrap into the instances RcvdDgramQue. | |
// | |
Wrap = Udp6WrapRxData (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 to the specified instance. | |
@param[in] Instance Pointer to the instance context data. | |
**/ | |
VOID | |
Udp6InstanceDeliverDgram ( | |
IN UDP6_INSTANCE_DATA *Instance | |
) | |
{ | |
UDP6_RXDATA_WRAP *Wrap; | |
EFI_UDP6_COMPLETION_TOKEN *Token; | |
NET_BUF *Dup; | |
EFI_UDP6_RECEIVE_DATA *RxData; | |
EFI_TPL OldTpl; | |
if (!IsListEmpty (&Instance->RcvdDgramQue) && | |
!NetMapIsEmpty (&Instance->RxTokens) | |
) | |
{ | |
Wrap = NET_LIST_HEAD (&Instance->RcvdDgramQue, UDP6_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_UDP6_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] Udp6Service Pointer to the udp service context data. | |
**/ | |
VOID | |
Udp6DeliverDgram ( | |
IN UDP6_SERVICE_DATA *Udp6Service | |
) | |
{ | |
LIST_ENTRY *Entry; | |
UDP6_INSTANCE_DATA *Instance; | |
NET_LIST_FOR_EACH (Entry, &Udp6Service->ChildrenList) { | |
// | |
// Iterate the instances. | |
// | |
Instance = NET_LIST_USER_STRUCT (Entry, UDP6_INSTANCE_DATA, Link); | |
if (!Instance->Configured) { | |
continue; | |
} | |
// | |
// Deliver the datagrams of this instance. | |
// | |
Udp6InstanceDeliverDgram (Instance); | |
} | |
} | |
/** | |
This function demultiplexes the received udp datagram to the appropriate instances. | |
@param[in] Udp6Service 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 | |
Udp6Demultiplex ( | |
IN UDP6_SERVICE_DATA *Udp6Service, | |
IN EFI_NET_SESSION_DATA *NetSession, | |
IN NET_BUF *Packet | |
) | |
{ | |
EFI_UDP_HEADER *Udp6Header; | |
UINT16 HeadSum; | |
EFI_UDP6_RECEIVE_DATA RxData; | |
EFI_UDP6_SESSION_DATA *Udp6Session; | |
UINTN Enqueued; | |
if (Packet->TotalSize < UDP6_HEADER_SIZE) { | |
NetbufFree (Packet); | |
return; | |
} | |
// | |
// Get the datagram header from the packet buffer. | |
// | |
Udp6Header = (EFI_UDP_HEADER *)NetbufGetByte (Packet, 0, NULL); | |
ASSERT (Udp6Header != NULL); | |
if (Udp6Header == NULL) { | |
NetbufFree (Packet); | |
return; | |
} | |
if (Udp6Header->Checksum != 0) { | |
// | |
// check the checksum. | |
// | |
HeadSum = NetIp6PseudoHeadChecksum ( | |
&NetSession->Source.v6, | |
&NetSession->Dest.v6, | |
EFI_IP_PROTO_UDP, | |
0 | |
); | |
if (Udp6Checksum (Packet, HeadSum) != 0) { | |
// | |
// Wrong checksum. | |
// | |
NetbufFree (Packet); | |
return; | |
} | |
} | |
Udp6Session = &RxData.UdpSession; | |
Udp6Session->SourcePort = NTOHS (Udp6Header->SrcPort); | |
Udp6Session->DestinationPort = NTOHS (Udp6Header->DstPort); | |
IP6_COPY_ADDRESS (&Udp6Session->SourceAddress, &NetSession->Source); | |
IP6_COPY_ADDRESS (&Udp6Session->DestinationAddress, &NetSession->Dest); | |
// | |
// Trim the UDP header. | |
// | |
NetbufTrim (Packet, UDP6_HEADER_SIZE, TRUE); | |
RxData.DataLength = (UINT32)Packet->TotalSize; | |
// | |
// Try to enqueue this datagram into the instances. | |
// | |
Enqueued = Udp6EnqueueDgram (Udp6Service, Packet, &RxData); | |
if (Enqueued == 0) { | |
// | |
// Send the port unreachable ICMP packet before we free this NET_BUF | |
// | |
Udp6SendPortUnreach (Udp6Service->IpIo, NetSession, Udp6Header); | |
} | |
// | |
// Try to free the packet before deliver it. | |
// | |
NetbufFree (Packet); | |
if (Enqueued > 0) { | |
// | |
// Deliver the datagram. | |
// | |
Udp6DeliverDgram (Udp6Service); | |
} | |
} | |
/** | |
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] Udp6Header Pointer to the udp header of the datagram causes | |
this icmp error message. | |
**/ | |
VOID | |
Udp6SendPortUnreach ( | |
IN IP_IO *IpIo, | |
IN EFI_NET_SESSION_DATA *NetSession, | |
IN VOID *Udp6Header | |
) | |
{ | |
NET_BUF *Packet; | |
UINT32 Len; | |
IP6_ICMP_ERROR_HEAD *IcmpErrHdr; | |
UINT8 *Ptr; | |
IP_IO_OVERRIDE Override; | |
IP_IO_IP_INFO *IpSender; | |
EFI_IP6_MODE_DATA *Ip6ModeData; | |
EFI_STATUS Status; | |
EFI_IP6_PROTOCOL *Ip6Protocol; | |
Ip6ModeData = NULL; | |
// | |
// An ICMPv6 error message MUST NOT be originated as A packet destined to | |
// 1) an IPv6 multicast address 2) The IPv6 Unspecified Address | |
// | |
if (NetSession->IpVersion == IP_VERSION_6) { | |
if (NetIp6IsUnspecifiedAddr (&NetSession->Dest.v6) || | |
IP6_IS_MULTICAST (&NetSession->Dest.v6) | |
) | |
{ | |
goto EXIT; | |
} | |
} | |
IpSender = IpIoFindSender (&IpIo, NetSession->IpVersion, &NetSession->Dest); | |
// | |
// Get the Ipv6 Mode Data. | |
// | |
Ip6ModeData = AllocateZeroPool (sizeof (EFI_IP6_MODE_DATA)); | |
ASSERT (Ip6ModeData != NULL); | |
if (Ip6ModeData == NULL) { | |
goto EXIT; | |
} | |
// | |
// If not finding the related IpSender use the default IpIo to send out | |
// the port unreachable ICMP message. | |
// | |
if (IpSender == NULL) { | |
Ip6Protocol = IpIo->Ip.Ip6; | |
} else { | |
Ip6Protocol = IpSender->Ip.Ip6; | |
} | |
Status = Ip6Protocol->GetModeData ( | |
Ip6Protocol, | |
Ip6ModeData, | |
NULL, | |
NULL | |
); | |
if (EFI_ERROR (Status)) { | |
goto EXIT; | |
} | |
// | |
// The ICMP6 packet length, includes whole invoking packet and ICMP6 error header. | |
// | |
Len = NetSession->IpHdrLen + | |
NTOHS (((EFI_UDP_HEADER *)Udp6Header)->Length) + | |
sizeof (IP6_ICMP_ERROR_HEAD); | |
// | |
// If the ICMP6 packet length larger than IP MTU, adjust its length to MTU. | |
// | |
if (Ip6ModeData->MaxPacketSize < Len) { | |
Len = Ip6ModeData->MaxPacketSize; | |
} | |
// | |
// Allocate buffer for the icmp error message. | |
// | |
Packet = NetbufAlloc (Len); | |
if (Packet == NULL) { | |
goto EXIT; | |
} | |
// | |
// Allocate space for the IP6_ICMP_ERROR_HEAD. | |
// | |
IcmpErrHdr = (IP6_ICMP_ERROR_HEAD *)NetbufAllocSpace (Packet, Len, FALSE); | |
ASSERT (IcmpErrHdr != NULL); | |
if (IcmpErrHdr == NULL) { | |
goto EXIT; | |
} | |
// | |
// Set the required fields for the icmp port unreachable message. | |
// | |
IcmpErrHdr->Head.Type = ICMP_V6_DEST_UNREACHABLE; | |
IcmpErrHdr->Head.Code = ICMP_V6_PORT_UNREACHABLE; | |
IcmpErrHdr->Head.Checksum = 0; | |
IcmpErrHdr->Fourth = 0; | |
// | |
// Copy as much of invoking Packet as possible without the ICMPv6 packet | |
// exceeding the minimum Ipv6 MTU. The length of IP6_ICMP_ERROR_HEAD contains | |
// the length of EFI_IP6_HEADER, so when using the length of IP6_ICMP_ERROR_HEAD | |
// for pointer movement that fact should be considered. | |
// | |
Ptr = (VOID *)&IcmpErrHdr->Head; | |
Ptr = (UINT8 *)(UINTN)((UINTN)Ptr + sizeof (IP6_ICMP_ERROR_HEAD) - sizeof (EFI_IP6_HEADER)); | |
CopyMem (Ptr, NetSession->IpHdr.Ip6Hdr, NetSession->IpHdrLen); | |
CopyMem ( | |
Ptr + NetSession->IpHdrLen, | |
Udp6Header, | |
Len - NetSession->IpHdrLen - sizeof (IP6_ICMP_ERROR_HEAD) + sizeof (EFI_IP6_HEADER) | |
); | |
// | |
// Set the checksum as zero, and IP6 driver will calculate it with pseudo header. | |
// | |
IcmpErrHdr->Head.Checksum = 0; | |
// | |
// Fill the override data. | |
// | |
Override.Ip6OverrideData.FlowLabel = 0; | |
Override.Ip6OverrideData.HopLimit = 255; | |
Override.Ip6OverrideData.Protocol = IP6_ICMP; | |
// | |
// Send out this icmp packet. | |
// | |
IpIoSend (IpIo, Packet, IpSender, NULL, NULL, &NetSession->Source, &Override); | |
NetbufFree (Packet); | |
EXIT: | |
if (Ip6ModeData != NULL) { | |
FreePool (Ip6ModeData); | |
} | |
} | |
/** | |
This function handles the received Icmp Error message and de-multiplexes it to the | |
instance. | |
@param[in] Udp6Service 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, out] Packet Pointer to the Icmp Error packet. | |
**/ | |
VOID | |
Udp6IcmpHandler ( | |
IN UDP6_SERVICE_DATA *Udp6Service, | |
IN UINT8 IcmpError, | |
IN EFI_NET_SESSION_DATA *NetSession, | |
IN OUT NET_BUF *Packet | |
) | |
{ | |
EFI_UDP_HEADER *Udp6Header; | |
EFI_UDP6_SESSION_DATA Udp6Session; | |
LIST_ENTRY *Entry; | |
UDP6_INSTANCE_DATA *Instance; | |
if (Packet->TotalSize < UDP6_HEADER_SIZE) { | |
NetbufFree (Packet); | |
return; | |
} | |
Udp6Header = (EFI_UDP_HEADER *)NetbufGetByte (Packet, 0, NULL); | |
ASSERT (Udp6Header != NULL); | |
if (Udp6Header == NULL) { | |
NetbufFree (Packet); | |
return; | |
} | |
IP6_COPY_ADDRESS (&Udp6Session.SourceAddress, &NetSession->Source); | |
IP6_COPY_ADDRESS (&Udp6Session.DestinationAddress, &NetSession->Dest); | |
Udp6Session.SourcePort = NTOHS (Udp6Header->DstPort); | |
Udp6Session.DestinationPort = NTOHS (Udp6Header->SrcPort); | |
NET_LIST_FOR_EACH (Entry, &Udp6Service->ChildrenList) { | |
// | |
// Iterate all the instances. | |
// | |
Instance = NET_LIST_USER_STRUCT (Entry, UDP6_INSTANCE_DATA, Link); | |
if (!Instance->Configured) { | |
continue; | |
} | |
if (Udp6MatchDgram (Instance, &Udp6Session)) { | |
// | |
// Translate the Icmp Error code according to the udp spec. | |
// | |
Instance->IcmpError = IpIoGetIcmpErrStatus (IcmpError, IP_VERSION_6, NULL, NULL); | |
if (IcmpError > ICMP_ERR_UNREACH_PORT) { | |
Instance->IcmpError = EFI_ICMP_ERROR; | |
} | |
// | |
// Notify the instance with the received Icmp Error. | |
// | |
Udp6ReportIcmpError (Instance); | |
break; | |
} | |
} | |
NetbufFree (Packet); | |
} | |
/** | |
This function reports the received ICMP error. | |
@param[in] Instance Pointer to the udp instance context data. | |
**/ | |
VOID | |
Udp6ReportIcmpError ( | |
IN UDP6_INSTANCE_DATA *Instance | |
) | |
{ | |
EFI_UDP6_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_UDP6_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 | |
Udp6NetVectorExtFree ( | |
IN VOID *Context | |
) | |
{ | |
} | |
/** | |
Find the key in the netmap. | |
@param[in] Map The netmap to search within. | |
@param[in] Key The key to search. | |
@return The point to the item contains the Key, or NULL, if Key isn't in the map. | |
**/ | |
NET_MAP_ITEM * | |
Udp6MapMultiCastAddr ( | |
IN NET_MAP *Map, | |
IN VOID *Key | |
) | |
{ | |
LIST_ENTRY *Entry; | |
NET_MAP_ITEM *Item; | |
EFI_IPv6_ADDRESS *Addr; | |
ASSERT (Map != NULL); | |
NET_LIST_FOR_EACH (Entry, &Map->Used) { | |
Item = NET_LIST_USER_STRUCT (Entry, NET_MAP_ITEM, Link); | |
Addr = (EFI_IPv6_ADDRESS *)Item->Key; | |
if (EFI_IP6_EQUAL (Addr, Key)) { | |
return Item; | |
} | |
} | |
return NULL; | |
} |