| /** @file | |
| Udp6 driver's whole implementation. | |
| Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR> | |
| This program and the accompanying materials | |
| are licensed and made available under the terms and conditions of the BSD License | |
| which accompanies this distribution. The full text of the license may be found at | |
| http://opensource.org/licenses/bsd-license.php. | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
| **/ | |
| #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. | |
| @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. | |
| @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 cancle 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. | |
| **/ | |
| 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 intializes 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 stragety. | |
| @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 avalaible DestinationPort for this token. | |
| // | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| if (NetIp6IsUnspecifiedAddr (&UdpSessionData->DestinationAddress) && | |
| NetIp6IsUnspecifiedAddr (&ConfigData->RemoteAddress) | |
| ) { | |
| // | |
| // The DestinationAddress is not specificed. | |
| // | |
| 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, execpt 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. | |
| @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; | |
| 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. | |
| @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 | |
| ) | |
| { | |
| 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 cancle 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 matche 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. | |
| **/ | |
| 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 < sizeof (EFI_UDP_HEADER)) { | |
| NetbufFree (Packet); | |
| return; | |
| } | |
| // | |
| // Get the datagram header from the packet buffer. | |
| // | |
| Udp6Header = (EFI_UDP_HEADER *) NetbufGetByte (Packet, 0, NULL); | |
| ASSERT (Udp6Header != NULL); | |
| 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 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); | |
| // | |
| // 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 calcuate 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 < sizeof (EFI_UDP_HEADER)) { | |
| NetbufFree (Packet); | |
| return; | |
| } | |
| Udp6Header = (EFI_UDP_HEADER *) NetbufGetByte (Packet, 0, NULL); | |
| ASSERT (Udp6Header != NULL); | |
| 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; | |
| } | |