From 3bdf9aae5f7f4c5f47fc8f807ae4409dfe3bdd58 Mon Sep 17 00:00:00 2001 From: lpleahy Date: Thu, 9 Feb 2012 19:16:44 +0000 Subject: Merged socket development branch: * Fixed bug report (Duane Voth: Python sockets test application not working) by starting the receive operations when a connection is established! * Increased performance by extending the idle loop into the network stack with the Poll call. * Added support for TCPv6 (SOCK_STREAM) and UDPv6 (SOCK_DGRAM). * Added support for getaddrinfo and getnameinfo calls. * Moved application PCD values into AppPkg Signed-off-by: lpleahy git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@13002 6f19259b-4bc3-4df7-8a09-765794883524 --- StdLib/EfiSocketLib/Tcp6.c | 2329 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2329 insertions(+) create mode 100644 StdLib/EfiSocketLib/Tcp6.c (limited to 'StdLib/EfiSocketLib/Tcp6.c') diff --git a/StdLib/EfiSocketLib/Tcp6.c b/StdLib/EfiSocketLib/Tcp6.c new file mode 100644 index 0000000000..0ee9fb6a64 --- /dev/null +++ b/StdLib/EfiSocketLib/Tcp6.c @@ -0,0 +1,2329 @@ +/** @file + Implement the TCP6 driver support for the socket layer. + + Copyright (c) 2011, Intel Corporation + All rights reserved. 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. + + + \section ConnectionManagement Connection Management + + The ::EslTcp6Listen routine initially places the SOCK_STREAM or + SOCK_SEQPACKET socket into a listen state. When a remote machine + makes a connection to the socket, the TCPv6 network layer calls + ::EslTcp6ListenComplete to complete the connection processing. + EslTcp6ListenComplete manages the connections by placing them in + FIFO order in a queue to be serviced by the application. When the + number of connections exceeds the backlog (ESL_SOCKET::MaxFifoDepth), + the new connection is closed. Eventually, the application indirectly + calls ::EslTcp6Accept to remove the next connection from the queue + and get the associated socket. + +**/ + +#include "Socket.h" + + +/** + Attempt to connect to a remote TCP port + + This routine starts the connection processing for a SOCK_STREAM + or SOCK_SEQPAKCET socket using the TCPv6 network layer. It + configures the local TCPv6 connection point and then attempts to + connect to a remote system. Upon completion, the + ::EslTcp6ConnectComplete routine gets called with the connection + status. + + This routine is called by ::EslSocketConnect to initiate the TCPv6 + network specific connect operations. The connection processing is + initiated by this routine and finished by ::EslTcp6ConnectComplete. + This pair of routines walks through the list of local TCPv6 + connection points until a connection to the remote system is + made. + + @param [in] pSocket Address of an ::ESL_SOCKET structure. + + @retval EFI_SUCCESS The connection was successfully established. + @retval EFI_NOT_READY The connection is in progress, call this routine again. + @retval Others The connection attempt failed. + + **/ +EFI_STATUS +EslTcp6ConnectStart ( + IN ESL_SOCKET * pSocket + ); + + +/** + Process the connection attempt + + A system has initiated a connection attempt with a socket in the + listen state. Attempt to complete the connection. + + The TCPv6 layer calls this routine when a connection is made to + the socket in the listen state. See the + \ref ConnectionManagement section. + + @param [in] Event The listen completion event + + @param [in] pPort Address of an ::ESL_PORT structure. + +**/ +VOID +EslTcp6ListenComplete ( + IN EFI_EVENT Event, + IN ESL_PORT * pPort + ); + + +/** + Accept a network connection. + + This routine waits for a network connection to the socket and + returns the remote network address to the caller if requested. + + This routine is called by ::EslSocketAccept to handle the TCPv6 protocol + specific accept operations for SOCK_STREAM and SOCK_SEQPACKET sockets. + See the \ref ConnectionManagement section. + + @param [in] pSocket Address of an ::ESL_SOCKET structure. + + @param [in] pSockAddr Address of a buffer to receive the remote + network address. + + @param [in, out] pSockAddrLength Length in bytes of the address buffer. + On output specifies the length of the + remote network address. + + @retval EFI_SUCCESS Remote address is available + @retval Others Remote address not available + + **/ +EFI_STATUS +EslTcp6Accept ( + IN ESL_SOCKET * pSocket, + IN struct sockaddr * pSockAddr, + IN OUT socklen_t * pSockAddrLength + ) +{ + ESL_PORT * pPort; + struct sockaddr_in6 * pRemoteAddress; + ESL_TCP6_CONTEXT * pTcp6; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Validate the socket length + // + pRemoteAddress = (struct sockaddr_in6 *) pSockAddr; + if (( NULL == pSockAddrLength ) + || ( sizeof ( *pRemoteAddress ) > *pSockAddrLength )) { + // + // Invalid socket address + // + Status = EFI_INVALID_PARAMETER; + pSocket->errno = EINVAL; + DEBUG (( DEBUG_ACCEPT, + "ERROR - Invalid address length\r\n" )); + } + else { + // + // Assume success + // + Status = EFI_SUCCESS; + + // + // Locate the address context + // + pPort = pSocket->pPortList; + pTcp6 = &pPort->Context.Tcp6; + + // + // Fill-in the remote address structure + // + ZeroMem ( pRemoteAddress, sizeof ( *pRemoteAddress )); + pRemoteAddress->sin6_len = sizeof ( *pRemoteAddress ); + pRemoteAddress->sin6_family = AF_INET6; + pRemoteAddress->sin6_port = SwapBytes16 ( pTcp6->ConfigData.AccessPoint.RemotePort ); + CopyMem ( &pRemoteAddress->sin6_addr.__u6_addr.__u6_addr8 [ 0 ], + &pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[0], + sizeof ( pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr )); + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Process the remote connection completion event. + + This routine handles the completion of a connection attempt. It + releases the port (TCPv6 adapter connection) in the case of an + error and start a connection attempt on the next port. If the + connection attempt was successful then this routine releases all + of the other ports. + + This routine is called by the TCPv6 layer when a connect request + completes. It sets the ESL_SOCKET::bConnected flag to notify the + ::EslTcp6ConnectComplete routine that the connection is available. + The flag is set when the connection is established or no more ports + exist in the list. The connection status is passed via + ESL_SOCKET::ConnectStatus. + + @param [in] Event The connect completion event + + @param [in] pPort Address of an ::ESL_PORT structure. + +**/ +VOID +EslTcp6ConnectComplete ( + IN EFI_EVENT Event, + IN ESL_PORT * pPort + ) +{ + BOOLEAN bRemoveFirstPort; + BOOLEAN bRemovePorts; + ESL_PORT * pNextPort; + ESL_SOCKET * pSocket; + ESL_TCP6_CONTEXT * pTcp6; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Locate the TCP context + // + pSocket = pPort->pSocket; + pTcp6 = &pPort->Context.Tcp6; + + // + // Get the connection status + // + bRemoveFirstPort = FALSE; + bRemovePorts = FALSE; + Status = pTcp6->ConnectToken.CompletionToken.Status; + pSocket->ConnectStatus = Status; + if ( !EFI_ERROR ( Status )) { + // + // The connection was successful + // + DEBUG (( DEBUG_CONNECT, + "0x%08x: Port connected to [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n", + pPort, + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[0], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[1], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[2], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[3], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[4], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[5], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[6], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[7], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[8], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[9], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[10], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[11], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[12], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[13], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[14], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[15], + pTcp6->ConfigData.AccessPoint.RemotePort )); + + // + // Remove the rest of the ports + // + bRemovePorts = TRUE; + } + else { + // + // The connection failed + // + DEBUG (( DEBUG_CONNECT, + "0x%08x: Port connection to [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d failed, Status: %r\r\n", + pPort, + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[0], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[1], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[2], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[3], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[4], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[5], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[6], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[7], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[8], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[9], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[10], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[11], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[12], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[13], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[14], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[15], + pTcp6->ConfigData.AccessPoint.RemotePort, + Status )); + + // + // Close the current port + // + Status = EslSocketPortClose ( pPort ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DEBUG_CONNECT, + "0x%08x: Port closed\r\n", + pPort )); + } + else { + DEBUG (( DEBUG_CONNECT, + "ERROR - Failed to close port 0x%08x, Status: %r\r\n", + pPort, + Status )); + } + + // + // Try to connect using the next port + // + Status = EslTcp6ConnectStart ( pSocket ); + if ( EFI_NOT_READY != Status ) { + pSocket->ConnectStatus = Status; + bRemoveFirstPort = TRUE; + } + } + + // + // Remove the ports if necessary + // + if ( bRemoveFirstPort || bRemovePorts ) { + // + // Remove the first port if necessary + // + pPort = pSocket->pPortList; + if (( !bRemoveFirstPort ) && ( NULL != pPort )) { + pPort = pPort->pLinkSocket; + } + + // + // Remove the rest of the list + // + while ( NULL != pPort ) { + pNextPort = pPort->pLinkSocket; + EslSocketPortClose ( pPort ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DEBUG_CONNECT, + "0x%08x: Port closed\r\n", + pPort )); + } + else { + DEBUG (( DEBUG_CONNECT, + "ERROR - Failed to close port 0x%08x, Status: %r\r\n", + pPort, + Status )); + } + pPort = pNextPort; + } + + // + // Notify the poll routine + // + pSocket->bConnected = TRUE; + } + + DBG_EXIT ( ); +} + + +/** + Poll for completion of the connection attempt. + + This routine polls the ESL_SOCKET::bConnected flag to determine + when the connection attempt is complete. + + This routine is called from ::EslSocketConnect to determine when + the connection is complete. The ESL_SOCKET::bConnected flag is + set by ::EslTcp6ConnectComplete when the TCPv6 layer establishes + a connection or runs out of local network adapters. This routine + gets the connection status from ESL_SOCKET::ConnectStatus. + + @param [in] pSocket Address of an ::ESL_SOCKET structure. + + @retval EFI_SUCCESS The connection was successfully established. + @retval EFI_NOT_READY The connection is in progress, call this routine again. + @retval Others The connection attempt failed. + + **/ +EFI_STATUS +EslTcp6ConnectPoll ( + IN ESL_SOCKET * pSocket + ) +{ + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Determine if the connection is complete + // + if ( !pSocket->bConnected ) { + // + // Not connected + // + pSocket->errno = EAGAIN; + Status = EFI_NOT_READY; + } + else { + // + // The connection processing is complete + // + pSocket->bConnected = FALSE; + + // + // Translate the connection status + // + Status = pSocket->ConnectStatus; + switch ( Status ) { + default: + case EFI_DEVICE_ERROR: + pSocket->errno = EIO; + break; + + case EFI_ABORTED: + pSocket->errno = ECONNREFUSED; + break; + + case EFI_INVALID_PARAMETER: + pSocket->errno = EINVAL; + break; + + case EFI_NO_MAPPING: + case EFI_NO_RESPONSE: + pSocket->errno = EHOSTUNREACH; + break; + + case EFI_NO_MEDIA: + pSocket->errno = ENETDOWN; + break; + + case EFI_OUT_OF_RESOURCES: + pSocket->errno = ENOMEM; + break; + + case EFI_SUCCESS: + pSocket->errno = 0; + pSocket->bConfigured = TRUE; + break; + + case EFI_TIMEOUT: + pSocket->errno = ETIMEDOUT; + break; + + case EFI_UNSUPPORTED: + pSocket->errno = ENOTSUP; + break; + + case 0x80000069: + pSocket->errno = ECONNRESET; + break; + } + } + + // + // Return the initialization status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Attempt to connect to a remote TCP port + + This routine starts the connection processing for a SOCK_STREAM + or SOCK_SEQPAKCET socket using the TCPv6 network layer. It + configures the local TCPv6 connection point and then attempts to + connect to a remote system. Upon completion, the + ::EslTcp6ConnectComplete routine gets called with the connection + status. + + This routine is called by ::EslSocketConnect to initiate the TCPv6 + network specific connect operations. The connection processing is + initiated by this routine and finished by ::EslTcp6ConnectComplete. + This pair of routines walks through the list of local TCPv6 + connection points until a connection to the remote system is + made. + + @param [in] pSocket Address of an ::ESL_SOCKET structure. + + @retval EFI_SUCCESS The connection was successfully established. + @retval EFI_NOT_READY The connection is in progress, call this routine again. + @retval Others The connection attempt failed. + + **/ +EFI_STATUS +EslTcp6ConnectStart ( + IN ESL_SOCKET * pSocket + ) +{ + ESL_PORT * pPort; + ESL_TCP6_CONTEXT * pTcp6; + EFI_TCP6_PROTOCOL * pTcp6Protocol; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Determine if any more local adapters are available + // + pPort = pSocket->pPortList; + if ( NULL != pPort ) { + // + // Configure the port + // + pTcp6 = &pPort->Context.Tcp6; + pTcp6->ConfigData.AccessPoint.ActiveFlag = TRUE; + pTcp6->ConfigData.TrafficClass = 0; + pTcp6->ConfigData.HopLimit = 255; + pTcp6Protocol = pPort->pProtocol.TCPv6; + Status = pTcp6Protocol->Configure ( pTcp6Protocol, + &pTcp6->ConfigData ); + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_CONNECT, + "ERROR - Failed to configure the Tcp6 port, Status: %r\r\n", + Status )); + switch ( Status ) { + case EFI_ACCESS_DENIED: + pSocket->errno = EACCES; + break; + + default: + case EFI_DEVICE_ERROR: + pSocket->errno = EIO; + break; + + case EFI_INVALID_PARAMETER: + pSocket->errno = EADDRNOTAVAIL; + break; + + case EFI_NO_MAPPING: + pSocket->errno = EAFNOSUPPORT; + break; + + case EFI_OUT_OF_RESOURCES: + pSocket->errno = ENOBUFS; + break; + + case EFI_UNSUPPORTED: + pSocket->errno = EOPNOTSUPP; + break; + } + } + else { + DEBUG (( DEBUG_CONNECT, + "0x%08x: Port configured\r\n", + pPort )); + pPort->bConfigured = TRUE; + + // + // Attempt the connection to the remote system + // + Status = pTcp6Protocol->Connect ( pTcp6Protocol, + &pTcp6->ConnectToken ); + if ( !EFI_ERROR ( Status )) { + // + // Connection in progress + // + pSocket->errno = EINPROGRESS; + Status = EFI_NOT_READY; + DEBUG (( DEBUG_CONNECT, + "0x%08x: Port attempting connection to [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n", + pPort, + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[0], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[1], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[2], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[3], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[4], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[5], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[6], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[7], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[8], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[9], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[10], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[11], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[12], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[13], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[14], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[15], + pTcp6->ConfigData.AccessPoint.RemotePort )); + } + else { + // + // Connection error + // + DEBUG (( DEBUG_CONNECT, + "ERROR - Port 0x%08x not connected, Status: %r\r\n", + pPort, + Status )); + // + // Determine the errno value + // + switch ( Status ) { + default: + pSocket->errno = EIO; + break; + + case EFI_OUT_OF_RESOURCES: + pSocket->errno = ENOBUFS; + break; + + case EFI_TIMEOUT: + pSocket->errno = ETIMEDOUT; + break; + + case EFI_NETWORK_UNREACHABLE: + pSocket->errno = ENETDOWN; + break; + + case EFI_HOST_UNREACHABLE: + pSocket->errno = EHOSTUNREACH; + break; + + case EFI_PORT_UNREACHABLE: + case EFI_PROTOCOL_UNREACHABLE: + case EFI_CONNECTION_REFUSED: + pSocket->errno = ECONNREFUSED; + break; + + case EFI_CONNECTION_RESET: + pSocket->errno = ECONNRESET; + break; + } + } + } + } + else { + // + // No more local adapters available + // + pSocket->errno = ENETUNREACH; + Status = EFI_NO_RESPONSE; + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Establish the known port to listen for network connections. + + This routine places the port into a state that enables connection + attempts. + + This routine is called by ::EslSocketListen to handle the network + specifics of the listen operation for SOCK_STREAM and SOCK_SEQPACKET + sockets. See the \ref ConnectionManagement section. + + @param [in] pSocket Address of an ::ESL_SOCKET structure. + + @retval EFI_SUCCESS - Socket successfully created + @retval Other - Failed to enable the socket for listen + +**/ +EFI_STATUS +EslTcp6Listen ( + IN ESL_SOCKET * pSocket + ) +{ + ESL_PORT * pNextPort; + ESL_PORT * pPort; + ESL_TCP6_CONTEXT * pTcp6; + EFI_TCP6_PROTOCOL * pTcp6Protocol; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Verify the socket layer synchronization + // + VERIFY_TPL ( TPL_SOCKETS ); + + // + // Use for/break instead of goto + // + for ( ; ; ) { + // + // Assume no ports are available + // + pSocket->errno = EOPNOTSUPP; + Status = EFI_NOT_READY; + + // + // Walk the list of ports + // + pPort = pSocket->pPortList; + while ( NULL != pPort ) { + // + // Assume success + // + pSocket->errno = 0; + + // + // Use for/break insteak of goto + // + for ( ; ; ) { + // + // Create the listen completion event + // + pTcp6 = &pPort->Context.Tcp6; + Status = gBS->CreateEvent ( EVT_NOTIFY_SIGNAL, + TPL_SOCKETS, + (EFI_EVENT_NOTIFY)EslTcp6ListenComplete, + pPort, + &pTcp6->ListenToken.CompletionToken.Event ); + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_ERROR | DEBUG_LISTEN, + "ERROR - Failed to create the listen completion event, Status: %r\r\n", + Status )); + pSocket->errno = ENOMEM; + break; + } + DEBUG (( DEBUG_POOL, + "0x%08x: Created listen completion event\r\n", + pTcp6->ListenToken.CompletionToken.Event )); + + // + // Configure the port + // + pTcp6Protocol = pPort->pProtocol.TCPv6; + Status = pTcp6Protocol->Configure ( pTcp6Protocol, + &pTcp6->ConfigData ); + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_LISTEN, + "ERROR - Failed to configure the Tcp6 port, Status: %r\r\n", + Status )); + switch ( Status ) { + case EFI_ACCESS_DENIED: + pSocket->errno = EACCES; + break; + + default: + case EFI_DEVICE_ERROR: + pSocket->errno = EIO; + break; + + case EFI_INVALID_PARAMETER: + pSocket->errno = EADDRNOTAVAIL; + break; + + case EFI_NO_MAPPING: + pSocket->errno = EAFNOSUPPORT; + break; + + case EFI_OUT_OF_RESOURCES: + pSocket->errno = ENOBUFS; + break; + + case EFI_UNSUPPORTED: + pSocket->errno = EOPNOTSUPP; + break; + } + break; + } + DEBUG (( DEBUG_LISTEN, + "0x%08x: Port configured\r\n", + pPort )); + pPort->bConfigured = TRUE; + + // + // Start the listen operation on the port + // + Status = pTcp6Protocol->Accept ( pTcp6Protocol, + &pTcp6->ListenToken ); + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_LISTEN, + "ERROR - Failed Tcp6 accept, Status: %r\r\n", + Status )); + switch ( Status ) { + case EFI_ACCESS_DENIED: + pSocket->errno = EACCES; + break; + + default: + case EFI_DEVICE_ERROR: + pSocket->errno = EIO; + break; + + case EFI_INVALID_PARAMETER: + pSocket->errno = EADDRNOTAVAIL; + break; + + case EFI_NOT_STARTED: + pSocket->errno = ENETDOWN; + break; + + case EFI_OUT_OF_RESOURCES: + pSocket->errno = ENOBUFS; + break; + } + break; + } + DEBUG (( DEBUG_LISTEN, + "0x%08x: Listen pending on Port\r\n", + pPort )); + + // + // Listen is pending on this port + // + break; + } + + // + // Get the next port + // + pNextPort = pPort->pLinkSocket; + + // + // Close the port upon error + // + if ( EFI_ERROR ( Status )) { + EslSocketPortCloseStart ( pPort, TRUE, DEBUG_LISTEN ); + } + + // + // Set the next port + // + pPort = pNextPort; + } + + // + // Determine if any ports are in the listen state + // + if ( NULL == pSocket->pPortList ) { + // + // No ports in the listen state + // + pSocket->MaxFifoDepth = 0; + + // + // Return the last error detected + // + break; + } + + // + // Mark the socket as configured + // + pSocket->bConfigured = TRUE; + + // + // All done + // + DEBUG (( DEBUG_LISTEN, + "0x%08x: pSocket - Listen pending on socket\r\n", + pSocket )); + break; + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Process the connection attempt + + A system has initiated a connection attempt with a socket in the + listen state. Attempt to complete the connection. + + The TCPv6 layer calls this routine when a connection is made to + the socket in the listen state. See the + \ref ConnectionManagement section. + + @param [in] Event The listen completion event + + @param [in] pPort Address of an ::ESL_PORT structure. + +**/ +VOID +EslTcp6ListenComplete ( + IN EFI_EVENT Event, + IN ESL_PORT * pPort + ) +{ + EFI_HANDLE ChildHandle; + struct sockaddr_in6 LocalAddress; + EFI_TCP6_CONFIG_DATA * pConfigData; + ESL_LAYER * pLayer; + ESL_PORT * pNewPort; + ESL_SOCKET * pNewSocket; + ESL_SOCKET * pSocket; + ESL_TCP6_CONTEXT * pTcp6; + EFI_TCP6_PROTOCOL * pTcp6Protocol; + EFI_STATUS Status; + EFI_HANDLE TcpPortHandle; + EFI_STATUS TempStatus; + + DBG_ENTER ( ); + VERIFY_AT_TPL ( TPL_SOCKETS ); + + // + // Assume success + // + Status = EFI_SUCCESS; + + // + // Determine if this connection fits into the connection FIFO + // + pSocket = pPort->pSocket; + TcpPortHandle = pPort->Context.Tcp6.ListenToken.NewChildHandle; + if (( SOCKET_STATE_LISTENING == pSocket->State ) + && ( pSocket->MaxFifoDepth > pSocket->FifoDepth )) { + // + // Allocate a socket for this connection + // + ChildHandle = NULL; + pLayer = &mEslLayer; + Status = EslSocketAllocate ( &ChildHandle, + DEBUG_CONNECTION, + &pNewSocket ); + if ( !EFI_ERROR ( Status )) { + // + // Clone the socket parameters + // + pNewSocket->pApi = pSocket->pApi; + pNewSocket->Domain = pSocket->Domain; + pNewSocket->Protocol = pSocket->Protocol; + pNewSocket->Type = pSocket->Type; + + // + // Build the local address + // + pTcp6 = &pPort->Context.Tcp6; + LocalAddress.sin6_len = (uint8_t)pNewSocket->pApi->MinimumAddressLength; + LocalAddress.sin6_family = AF_INET6; + LocalAddress.sin6_port = 0; + CopyMem ( &LocalAddress.sin6_addr.__u6_addr.__u6_addr8 [ 0 ], + &pTcp6->ConfigData.AccessPoint.StationAddress.Addr [ 0 ], + sizeof ( pTcp6->ConfigData.AccessPoint.StationAddress.Addr )); + + // + // Allocate a port for this connection + // Note in this instance Configure may not be called with NULL! + // + Status = EslSocketPortAllocate ( pNewSocket, + pPort->pService, + TcpPortHandle, + (struct sockaddr *)&LocalAddress, + FALSE, + DEBUG_CONNECTION, + &pNewPort ); + if ( !EFI_ERROR ( Status )) { + // + // Restart the listen operation on the port + // + pTcp6Protocol = pPort->pProtocol.TCPv6; + Status = pTcp6Protocol->Accept ( pTcp6Protocol, + &pTcp6->ListenToken ); + + // + // Close the TCP port using SocketClose + // + TcpPortHandle = NULL; + pTcp6 = &pNewPort->Context.Tcp6; + + // + // Check for an accept call error + // + if ( !EFI_ERROR ( Status )) { + // + // Get the port configuration + // + pNewPort->bConfigured = TRUE; + pConfigData = &pTcp6->ConfigData; + pConfigData->ControlOption = &pTcp6->Option; + pTcp6Protocol = pNewPort->pProtocol.TCPv6; + Status = pTcp6Protocol->GetModeData ( pTcp6Protocol, + NULL, + pConfigData, + NULL, + NULL, + NULL ); + if ( !EFI_ERROR ( Status )) { + // + // Add the new socket to the connection FIFO + // + if ( NULL == pSocket->pFifoTail ) { + // + // First connection + // + pSocket->pFifoHead = pNewSocket; + } + else { + // + // Add to end of list. + // + pSocket->pFifoTail->pNextConnection = pNewSocket; + } + pSocket->pFifoTail = pNewSocket; + pSocket->FifoDepth += 1; + + // + // Update the socket state + // + pNewSocket->State = SOCKET_STATE_IN_FIFO; + + // + // Log the connection + // + DEBUG (( DEBUG_CONNECTION | DEBUG_INFO, + "0x%08x: Socket on port [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d connected to [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n", + pNewSocket, + pConfigData->AccessPoint.StationAddress.Addr[0], + pConfigData->AccessPoint.StationAddress.Addr[1], + pConfigData->AccessPoint.StationAddress.Addr[2], + pConfigData->AccessPoint.StationAddress.Addr[3], + pConfigData->AccessPoint.StationAddress.Addr[4], + pConfigData->AccessPoint.StationAddress.Addr[5], + pConfigData->AccessPoint.StationAddress.Addr[6], + pConfigData->AccessPoint.StationAddress.Addr[7], + pConfigData->AccessPoint.StationAddress.Addr[8], + pConfigData->AccessPoint.StationAddress.Addr[9], + pConfigData->AccessPoint.StationAddress.Addr[10], + pConfigData->AccessPoint.StationAddress.Addr[11], + pConfigData->AccessPoint.StationAddress.Addr[12], + pConfigData->AccessPoint.StationAddress.Addr[13], + pConfigData->AccessPoint.StationAddress.Addr[14], + pConfigData->AccessPoint.StationAddress.Addr[15], + pConfigData->AccessPoint.StationPort, + pConfigData->AccessPoint.RemoteAddress.Addr[0], + pConfigData->AccessPoint.RemoteAddress.Addr[1], + pConfigData->AccessPoint.RemoteAddress.Addr[2], + pConfigData->AccessPoint.RemoteAddress.Addr[3], + pConfigData->AccessPoint.RemoteAddress.Addr[4], + pConfigData->AccessPoint.RemoteAddress.Addr[5], + pConfigData->AccessPoint.RemoteAddress.Addr[6], + pConfigData->AccessPoint.RemoteAddress.Addr[7], + pConfigData->AccessPoint.RemoteAddress.Addr[8], + pConfigData->AccessPoint.RemoteAddress.Addr[9], + pConfigData->AccessPoint.RemoteAddress.Addr[10], + pConfigData->AccessPoint.RemoteAddress.Addr[11], + pConfigData->AccessPoint.RemoteAddress.Addr[12], + pConfigData->AccessPoint.RemoteAddress.Addr[13], + pConfigData->AccessPoint.RemoteAddress.Addr[14], + pConfigData->AccessPoint.RemoteAddress.Addr[15], + pConfigData->AccessPoint.RemotePort )); + DEBUG (( DEBUG_CONNECTION | DEBUG_INFO, + "0x%08x: Listen socket adding socket 0x%08x to FIFO, depth: %d\r\n", + pSocket, + pNewSocket, + pSocket->FifoDepth )); + + // + // Start the receive operation + // + EslSocketRxStart ( pNewPort ); + } + else { + DEBUG (( DEBUG_ERROR | DEBUG_CONNECTION | DEBUG_INFO, + "ERROR - GetModeData failed on port 0x%08x, Status: %r\r\n", + pNewPort, + Status )); + } + } + else { + // + // The listen failed on this port + // + DEBUG (( DEBUG_LISTEN | DEBUG_INFO, + "ERROR - Listen failed on port 0x%08x, Status: %r\r\n", + pPort, + Status )); + + // + // Close the listening port + // + EslSocketPortCloseStart ( pPort, TRUE, DEBUG_LISTEN ); + } + } + + // + // Done with the socket if necessary + // + if ( EFI_ERROR ( Status )) { + TempStatus = EslSocketCloseStart ( &pNewSocket->SocketProtocol, + TRUE, + &pSocket->errno ); + ASSERT ( EFI_SUCCESS == TempStatus ); + } + } + } + else { + DEBUG (( DEBUG_CONNECTION, + "0x%08x: Socket FIFO full, connection refused\r\n", + pSocket )); + + // + // The FIFO is full or the socket is in the wrong state + // + Status = EFI_BUFFER_TOO_SMALL; + } + + // + // Close the connection if necessary + // + if (( EFI_ERROR ( Status )) + && ( NULL == TcpPortHandle )) { + // + // TODO: Finish this code path + // The new connection does not fit into the connection FIFO + // + // Process: + // Call close + // Release the resources + + } + + DBG_EXIT ( ); +} + + +/** + Get the local socket address. + + This routine returns the IPv6 address and TCP port number associated + with the local socket. + + This routine is called by ::EslSocketGetLocalAddress to determine the + network address for the SOCK_STREAM or SOCK_SEQPACKET socket. + + @param [in] pPort Address of an ::ESL_PORT structure. + + @param [out] pSockAddr Network address to receive the local system address + +**/ +VOID +EslTcp6LocalAddressGet ( + IN ESL_PORT * pPort, + OUT struct sockaddr * pSockAddr + ) +{ + struct sockaddr_in6 * pLocalAddress; + ESL_TCP6_CONTEXT * pTcp6; + + DBG_ENTER ( ); + + // + // Return the local address + // + pTcp6 = &pPort->Context.Tcp6; + pLocalAddress = (struct sockaddr_in6 *)pSockAddr; + pLocalAddress->sin6_family = AF_INET6; + pLocalAddress->sin6_port = SwapBytes16 ( pTcp6->ConfigData.AccessPoint.StationPort ); + CopyMem ( &pLocalAddress->sin6_addr, + &pTcp6->ConfigData.AccessPoint.StationAddress.Addr[0], + sizeof ( pLocalAddress->sin6_addr )); + + DBG_EXIT ( ); +} + + +/** + Set the local port address. + + This routine sets the local port address. + + This support routine is called by ::EslSocketPortAllocate. + + @param [in] pPort Address of an ESL_PORT structure + @param [in] pSockAddr Address of a sockaddr structure that contains the + connection point on the local machine. An IPv6 address + of INADDR_ANY specifies that the connection is made to + all of the network stacks on the platform. Specifying a + specific IPv6 address restricts the connection to the + network stack supporting that address. Specifying zero + for the port causes the network layer to assign a port + number from the dynamic range. Specifying a specific + port number causes the network layer to use that port. + + @param [in] bBindTest TRUE = run bind testing + + @retval EFI_SUCCESS The operation was successful + + **/ +EFI_STATUS +EslTcp6LocalAddressSet ( + IN ESL_PORT * pPort, + IN CONST struct sockaddr * pSockAddr, + IN BOOLEAN bBindTest + ) +{ + EFI_TCP6_ACCESS_POINT * pAccessPoint; + CONST struct sockaddr_in6 * pIpAddress; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Validate the address + // + pIpAddress = (struct sockaddr_in6 *)pSockAddr; +// +// TODO: Fix the following check +// +/* + if ( INADDR_BROADCAST == pIpAddress->sin6_addr.s_addr ) { + // + // The local address must not be the broadcast address + // + Status = EFI_INVALID_PARAMETER; + pPort->pSocket->errno = EADDRNOTAVAIL; + } + else { +*/ +{ + // + // Set the local address + // + pAccessPoint = &pPort->Context.Tcp6.ConfigData.AccessPoint; + CopyMem ( &pAccessPoint->StationAddress.Addr[0], + &pIpAddress->sin6_addr.__u6_addr.__u6_addr8 [ 0 ], + sizeof ( pIpAddress->sin6_addr.__u6_addr.__u6_addr8 [ 0 ])); + + // + // Validate the IP address + // + pAccessPoint->StationPort = 0; + Status = bBindTest ? EslSocketBindTest ( pPort, EADDRNOTAVAIL ) + : EFI_SUCCESS; + if ( !EFI_ERROR ( Status )) { + // + // Set the port number + // + pAccessPoint->StationPort = SwapBytes16 ( pIpAddress->sin6_port ); + + // + // Display the local address + // + DEBUG (( DEBUG_BIND, + "0x%08x: Port, Local Tcp6 Address: [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n", + pPort, + pAccessPoint->StationAddress.Addr[0], + pAccessPoint->StationAddress.Addr[1], + pAccessPoint->StationAddress.Addr[2], + pAccessPoint->StationAddress.Addr[3], + pAccessPoint->StationAddress.Addr[4], + pAccessPoint->StationAddress.Addr[5], + pAccessPoint->StationAddress.Addr[6], + pAccessPoint->StationAddress.Addr[7], + pAccessPoint->StationAddress.Addr[8], + pAccessPoint->StationAddress.Addr[9], + pAccessPoint->StationAddress.Addr[10], + pAccessPoint->StationAddress.Addr[11], + pAccessPoint->StationAddress.Addr[12], + pAccessPoint->StationAddress.Addr[13], + pAccessPoint->StationAddress.Addr[14], + pAccessPoint->StationAddress.Addr[15], + pAccessPoint->StationPort )); + } + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Free a receive packet + + This routine performs the network specific operations necessary + to free a receive packet. + + This routine is called by ::EslSocketPortCloseTxDone to free a + receive packet. + + @param [in] pPacket Address of an ::ESL_PACKET structure. + @param [in, out] pRxBytes Address of the count of RX bytes + +**/ +VOID +EslTcp6PacketFree ( + IN ESL_PACKET * pPacket, + IN OUT size_t * pRxBytes + ) +{ + DBG_ENTER ( ); + + // + // Account for the receive bytes + // + *pRxBytes -= pPacket->Op.Tcp6Rx.RxData.DataLength; + DBG_EXIT ( ); +} + + +/** + Initialize the network specific portions of an ::ESL_PORT structure. + + This routine initializes the network specific portions of an + ::ESL_PORT structure for use by the socket. + + This support routine is called by ::EslSocketPortAllocate + to connect the socket with the underlying network adapter + running the TCPv6 protocol. + + @param [in] pPort Address of an ESL_PORT structure + @param [in] DebugFlags Flags for debug messages + + @retval EFI_SUCCESS - Socket successfully created + + **/ +EFI_STATUS +EslTcp6PortAllocate ( + IN ESL_PORT * pPort, + IN UINTN DebugFlags + ) +{ + EFI_TCP6_ACCESS_POINT * pAccessPoint; + ESL_SOCKET * pSocket; + ESL_TCP6_CONTEXT * pTcp6; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Use for/break instead of goto + for ( ; ; ) { + // + // Allocate the close event + // + pSocket = pPort->pSocket; + pTcp6 = &pPort->Context.Tcp6; + Status = gBS->CreateEvent ( EVT_NOTIFY_SIGNAL, + TPL_SOCKETS, + (EFI_EVENT_NOTIFY)EslSocketPortCloseComplete, + pPort, + &pTcp6->CloseToken.CompletionToken.Event); + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_ERROR | DebugFlags, + "ERROR - Failed to create the close event, Status: %r\r\n", + Status )); + pSocket->errno = ENOMEM; + break; + } + DEBUG (( DEBUG_CLOSE | DEBUG_POOL, + "0x%08x: Created close event\r\n", + pTcp6->CloseToken.CompletionToken.Event )); + + // + // Allocate the connection event + // + Status = gBS->CreateEvent ( EVT_NOTIFY_SIGNAL, + TPL_SOCKETS, + (EFI_EVENT_NOTIFY)EslTcp6ConnectComplete, + pPort, + &pTcp6->ConnectToken.CompletionToken.Event); + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_ERROR | DebugFlags, + "ERROR - Failed to create the connect event, Status: %r\r\n", + Status )); + pSocket->errno = ENOMEM; + break; + } + DEBUG (( DEBUG_CLOSE | DEBUG_POOL, + "0x%08x: Created connect event\r\n", + pTcp6->ConnectToken.CompletionToken.Event )); + + // + // Initialize the port + // + pSocket->TxPacketOffset = OFFSET_OF ( ESL_PACKET, Op.Tcp6Tx.TxData ); + pSocket->TxTokenEventOffset = OFFSET_OF ( ESL_IO_MGMT, Token.Tcp6Tx.CompletionToken.Event ); + pSocket->TxTokenOffset = OFFSET_OF ( EFI_TCP6_IO_TOKEN, Packet.TxData ); + + // + // Save the cancel, receive and transmit addresses + // pPort->pfnRxCancel = NULL; since the UEFI implementation returns EFI_UNSUPPORTED + // + pPort->pfnConfigure = (PFN_NET_CONFIGURE)pPort->pProtocol.TCPv6->Configure; + pPort->pfnRxPoll = (PFN_NET_POLL)pPort->pProtocol.TCPv6->Poll; + pPort->pfnRxStart = (PFN_NET_IO_START)pPort->pProtocol.TCPv6->Receive; + pPort->pfnTxStart = (PFN_NET_IO_START)pPort->pProtocol.TCPv6->Transmit; + + // + // Set the configuration flags + // + pAccessPoint = &pPort->Context.Tcp6.ConfigData.AccessPoint; + pAccessPoint->ActiveFlag = FALSE; + pTcp6->ConfigData.TrafficClass = 0; + pTcp6->ConfigData.HopLimit = 255; + break; + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Close a Tcp6 port. + + This routine releases the network specific resources allocated by + ::EslTcp6PortAllocate. + + This routine is called by ::EslSocketPortClose. + See the \ref PortCloseStateMachine section. + + @param [in] pPort Address of an ::ESL_PORT structure. + + @retval EFI_SUCCESS The port is closed + @retval other Port close error + +**/ +EFI_STATUS +EslTcp6PortClose ( + IN ESL_PORT * pPort + ) +{ + UINTN DebugFlags; + ESL_TCP6_CONTEXT * pTcp6; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Locate the port in the socket list + // + Status = EFI_SUCCESS; + DebugFlags = pPort->DebugFlags; + pTcp6 = &pPort->Context.Tcp6; + + // + // Done with the connect event + // + if ( NULL != pTcp6->ConnectToken.CompletionToken.Event ) { + Status = gBS->CloseEvent ( pTcp6->ConnectToken.CompletionToken.Event ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DebugFlags | DEBUG_POOL, + "0x%08x: Closed connect event\r\n", + pTcp6->ConnectToken.CompletionToken.Event )); + } + else { + DEBUG (( DEBUG_ERROR | DebugFlags, + "ERROR - Failed to close the connect event, Status: %r\r\n", + Status )); + ASSERT ( EFI_SUCCESS == Status ); + } + } + + // + // Done with the close event + // + if ( NULL != pTcp6->CloseToken.CompletionToken.Event ) { + Status = gBS->CloseEvent ( pTcp6->CloseToken.CompletionToken.Event ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DebugFlags | DEBUG_POOL, + "0x%08x: Closed close event\r\n", + pTcp6->CloseToken.CompletionToken.Event )); + } + else { + DEBUG (( DEBUG_ERROR | DebugFlags, + "ERROR - Failed to close the close event, Status: %r\r\n", + Status )); + ASSERT ( EFI_SUCCESS == Status ); + } + } + + // + // Done with the listen completion event + // + if ( NULL != pTcp6->ListenToken.CompletionToken.Event ) { + Status = gBS->CloseEvent ( pTcp6->ListenToken.CompletionToken.Event ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DebugFlags | DEBUG_POOL, + "0x%08x: Closed listen completion event\r\n", + pTcp6->ListenToken.CompletionToken.Event )); + } + else { + DEBUG (( DEBUG_ERROR | DebugFlags, + "ERROR - Failed to close the listen completion event, Status: %r\r\n", + Status )); + ASSERT ( EFI_SUCCESS == Status ); + } + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Perform the network specific close operation on the port. + + This routine performs a cancel operations on the TCPv6 port to + shutdown the receive operations on the port. + + This routine is called by the ::EslSocketPortCloseTxDone + routine after the port completes all of the transmission. + + @param [in] pPort Address of an ::ESL_PORT structure. + + @retval EFI_SUCCESS The port is closed, not normally returned + @retval EFI_NOT_READY The port is still closing + @retval EFI_ALREADY_STARTED Error, the port is in the wrong state, + most likely the routine was called already. + +**/ +EFI_STATUS +EslTcp6PortCloseOp ( + IN ESL_PORT * pPort + ) +{ + ESL_TCP6_CONTEXT * pTcp6; + EFI_TCP6_PROTOCOL * pTcp6Protocol; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Close the configured port + // + Status = EFI_SUCCESS; + pTcp6 = &pPort->Context.Tcp6; + pTcp6Protocol = pPort->pProtocol.TCPv6; + pTcp6->CloseToken.AbortOnClose = pPort->bCloseNow; + Status = pTcp6Protocol->Close ( pTcp6Protocol, + &pTcp6->CloseToken ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( pPort->DebugFlags | DEBUG_CLOSE | DEBUG_INFO, + "0x%08x: Port close started\r\n", + pPort )); + } + else { + DEBUG (( DEBUG_ERROR | pPort->DebugFlags | DEBUG_CLOSE | DEBUG_INFO, + "ERROR - Close failed on port 0x%08x, Status: %r\r\n", + pPort, + Status )); + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Receive data from a network connection. + + This routine attempts to return buffered data to the caller. The + data is removed from the urgent queue if the message flag MSG_OOB + is specified, otherwise data is removed from the normal queue. + See the \ref ReceiveEngine section. + + This routine is called by ::EslSocketReceive to handle the network + specific receive operation to support SOCK_STREAM and SOCK_SEQPACKET + sockets. + + @param [in] pPort Address of an ::ESL_PORT structure. + + @param [in] pPacket Address of an ::ESL_PACKET structure. + + @param [in] pbConsumePacket Address of a BOOLEAN indicating if the packet is to be consumed + + @param [in] BufferLength Length of the the buffer + + @param [in] pBuffer Address of a buffer to receive the data. + + @param [in] pDataLength Number of received data bytes in the buffer. + + @param [out] pAddress Network address to receive the remote system address + + @param [out] pSkipBytes Address to receive the number of bytes skipped + + @return Returns the address of the next free byte in the buffer. + + **/ +UINT8 * +EslTcp6Receive ( + IN ESL_PORT * pPort, + IN ESL_PACKET * pPacket, + IN BOOLEAN * pbConsumePacket, + IN size_t BufferLength, + IN UINT8 * pBuffer, + OUT size_t * pDataLength, + OUT struct sockaddr * pAddress, + OUT size_t * pSkipBytes + ) +{ + size_t DataLength; + struct sockaddr_in6 * pRemoteAddress; + ESL_TCP6_CONTEXT * pTcp6; + + DBG_ENTER ( ); + + // + // Return the remote system address if requested + // + if ( NULL != pAddress ) { + // + // Build the remote address + // + pTcp6 = &pPort->Context.Tcp6; + DEBUG (( DEBUG_RX, + "Getting packet remote address: [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n", + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[0], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[1], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[2], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[3], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[4], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[5], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[6], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[7], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[8], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[9], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[10], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[11], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[12], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[13], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[14], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[15], + pTcp6->ConfigData.AccessPoint.RemotePort )); + pRemoteAddress = (struct sockaddr_in6 *)pAddress; + CopyMem ( &pRemoteAddress->sin6_addr, + &pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[0], + sizeof ( pRemoteAddress->sin6_addr )); + pRemoteAddress->sin6_port = SwapBytes16 ( pTcp6->ConfigData.AccessPoint.RemotePort ); + } + + // + // Determine the amount of received data + // + DataLength = pPacket->ValidBytes; + if ( BufferLength < DataLength ) { + DataLength = BufferLength; + } + + // + // Move the data into the buffer + // + DEBUG (( DEBUG_RX, + "0x%08x: Port copy packet 0x%08x data into 0x%08x, 0x%08x bytes\r\n", + pPort, + pPacket, + pBuffer, + DataLength )); + CopyMem ( pBuffer, pPacket->pBuffer, DataLength ); + + // + // Determine if the data is being read + // + if ( *pbConsumePacket ) { + // + // Account for the bytes consumed + // + pPacket->pBuffer += DataLength; + pPacket->ValidBytes -= DataLength; + DEBUG (( DEBUG_RX, + "0x%08x: Port account for 0x%08x bytes\r\n", + pPort, + DataLength )); + + // + // Determine if the entire packet was consumed + // + if (( 0 == pPacket->ValidBytes ) + || ( SOCK_STREAM != pPort->pSocket->Type )) { + // + // All done with this packet + // Account for any discarded data + // + *pSkipBytes = pPacket->ValidBytes; + } + else + { + // + // More data to consume later + // + *pbConsumePacket = FALSE; + } + } + + // + // Return the data length and the buffer address + // + *pDataLength = DataLength; + DBG_EXIT_HEX ( pBuffer ); + return pBuffer; +} + + +/** + Get the remote socket address. + + This routine returns the address of the remote connection point + associated with the SOCK_STREAM or SOCK_SEQPACKET socket. + + This routine is called by ::EslSocketGetPeerAddress to detemine + the TCPv6 address and por number associated with the network adapter. + + @param [in] pPort Address of an ::ESL_PORT structure. + + @param [out] pAddress Network address to receive the remote system address + +**/ +VOID +EslTcp6RemoteAddressGet ( + IN ESL_PORT * pPort, + OUT struct sockaddr * pAddress + ) +{ + struct sockaddr_in6 * pRemoteAddress; + ESL_TCP6_CONTEXT * pTcp6; + + DBG_ENTER ( ); + + // + // Return the remote address + // + pTcp6 = &pPort->Context.Tcp6; + pRemoteAddress = (struct sockaddr_in6 *)pAddress; + pRemoteAddress->sin6_family = AF_INET6; + pRemoteAddress->sin6_port = SwapBytes16 ( pTcp6->ConfigData.AccessPoint.RemotePort ); + CopyMem ( &pRemoteAddress->sin6_addr, + &pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[0], + sizeof ( pRemoteAddress->sin6_addr )); + + DBG_EXIT ( ); +} + + +/** + Set the remote address + + This routine sets the remote address in the port. + + This routine is called by ::EslSocketConnect to specify the + remote network address. + + @param [in] pPort Address of an ::ESL_PORT structure. + + @param [in] pSockAddr Network address of the remote system. + + @param [in] SockAddrLength Length in bytes of the network address. + + @retval EFI_SUCCESS The operation was successful + + **/ +EFI_STATUS +EslTcp6RemoteAddressSet ( + IN ESL_PORT * pPort, + IN CONST struct sockaddr * pSockAddr, + IN socklen_t SockAddrLength + ) +{ + CONST struct sockaddr_in6 * pRemoteAddress; + ESL_TCP6_CONTEXT * pTcp6; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Set the remote address + // + pTcp6 = &pPort->Context.Tcp6; + pRemoteAddress = (struct sockaddr_in6 *)pSockAddr; + CopyMem ( &pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr [ 0 ], + &pRemoteAddress->sin6_addr.__u6_addr.__u6_addr8 [ 0 ], + sizeof ( pRemoteAddress->sin6_addr.__u6_addr.__u6_addr8 )); + pTcp6->ConfigData.AccessPoint.RemotePort = SwapBytes16 ( pRemoteAddress->sin6_port ); + Status = EFI_SUCCESS; + +// +// TODO: Fix the following check +// +/* + if ( INADDR_BROADCAST == pRemoteAddress->sin6_addr.s_addr ) { + DEBUG (( DEBUG_CONNECT, + "ERROR - Invalid remote address\r\n" )); + Status = EFI_INVALID_PARAMETER; + pPort->pSocket->errno = EAFNOSUPPORT; + } +*/ + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Process the receive completion + + This routine queues the data in FIFO order in either the urgent + or normal data queues depending upon the type of data received. + See the \ref ReceiveEngine section. + + This routine is called by the TCPv6 driver when some data is + received. + + Buffer the data that was just received. + + @param [in] Event The receive completion event + + @param [in] pIo Address of an ::ESL_IO_MGMT structure + +**/ +VOID +EslTcp6RxComplete ( + IN EFI_EVENT Event, + IN ESL_IO_MGMT * pIo + ) +{ + BOOLEAN bUrgent; + size_t LengthInBytes; + ESL_PACKET * pPacket; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Get the operation status. + // + Status = pIo->Token.Tcp6Rx.CompletionToken.Status; + + // + // +--------------------+ +---------------------------+ + // | ESL_IO_MGMT | | ESL_PACKET | + // | | | | + // | +---------------+ +-----------------------+ | + // | | Token | | EFI_Tcp6_RECEIVE_DATA | | + // | | RxData --> | | | + // | | | +-----------------------+---+ + // | | Event | | Data Buffer | + // +----+---------------+ | | + // | | + // +---------------------------+ + // + // + // Duplicate the buffer address and length for use by the + // buffer handling code in EslTcp6Receive. These fields are + // used when a partial read is done of the data from the + // packet. + // + pPacket = pIo->pPacket; + pPacket->pBuffer = pPacket->Op.Tcp6Rx.RxData.FragmentTable[0].FragmentBuffer; + LengthInBytes = pPacket->Op.Tcp6Rx.RxData.DataLength; + pPacket->ValidBytes = LengthInBytes; + + // + // Get the data type so that it may be linked to the + // correct receive buffer list on the ESL_SOCKET structure + // + bUrgent = pPacket->Op.Tcp6Rx.RxData.UrgentFlag; + + // + // Complete this request + // + EslSocketRxComplete ( pIo, Status, LengthInBytes, bUrgent ); + DBG_EXIT ( ); +} + + +/** + Start a receive operation + + This routine posts a receive buffer to the TCPv6 driver. + See the \ref ReceiveEngine section. + + This support routine is called by EslSocketRxStart. + + @param [in] pPort Address of an ::ESL_PORT structure. + @param [in] pIo Address of an ::ESL_IO_MGMT structure. + + **/ +VOID +EslTcp6RxStart ( + IN ESL_PORT * pPort, + IN ESL_IO_MGMT * pIo + ) +{ + ESL_PACKET * pPacket; + + DBG_ENTER ( ); + + // + // Initialize the buffer for receive + // + pPacket = pIo->pPacket; + pIo->Token.Tcp6Rx.Packet.RxData = &pPacket->Op.Tcp6Rx.RxData; + pPacket->Op.Tcp6Rx.RxData.DataLength = sizeof ( pPacket->Op.Tcp6Rx.Buffer ); + pPacket->Op.Tcp6Rx.RxData.FragmentCount = 1; + pPacket->Op.Tcp6Rx.RxData.FragmentTable[0].FragmentLength = pPacket->Op.Tcp6Rx.RxData.DataLength; + pPacket->Op.Tcp6Rx.RxData.FragmentTable[0].FragmentBuffer = &pPacket->Op.Tcp6Rx.Buffer[0]; + + DBG_EXIT ( ); +} + + +/** + Determine if the socket is configured. + + This routine uses the flag ESL_SOCKET::bConfigured to determine + if the network layer's configuration routine has been called. + + This routine is called by EslSocketIsConfigured to verify + that the socket has been configured. + + @param [in] pSocket Address of an ::ESL_SOCKET structure. + + @retval EFI_SUCCESS - The port is connected + @retval EFI_NOT_STARTED - The port is not connected + + **/ + EFI_STATUS + EslTcp6SocketIsConfigured ( + IN ESL_SOCKET * pSocket + ) +{ + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Determine the socket configuration status + // + Status = pSocket->bConfigured ? EFI_SUCCESS : EFI_NOT_STARTED; + + // + // Return the port connected state. + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Buffer data for transmission over a network connection. + + This routine buffers data for the transmit engine in one of two + queues, one for urgent (out-of-band) data and the other for normal + data. The urgent data is provided to TCP as soon as it is available, + allowing the TCP layer to schedule transmission of the urgent data + between packets of normal data. + + This routine is called by ::EslSocketTransmit to buffer + data for transmission. When the \ref TransmitEngine has resources, + this routine will start the transmission of the next buffer on + the network connection. + + Transmission errors are returned during the next transmission or + during the close operation. Only buffering errors are returned + during the current transmission attempt. + + @param [in] pSocket Address of an ::ESL_SOCKET structure + + @param [in] Flags Message control flags + + @param [in] BufferLength Length of the the buffer + + @param [in] pBuffer Address of a buffer to receive the data. + + @param [in] pDataLength Number of received data bytes in the buffer. + + @param [in] pAddress Network address of the remote system address + + @param [in] AddressLength Length of the remote network address structure + + @retval EFI_SUCCESS - Socket data successfully buffered + + **/ +EFI_STATUS +EslTcp6TxBuffer ( + IN ESL_SOCKET * pSocket, + IN int Flags, + IN size_t BufferLength, + IN CONST UINT8 * pBuffer, + OUT size_t * pDataLength, + IN const struct sockaddr * pAddress, + IN socklen_t AddressLength + ) +{ + BOOLEAN bUrgent; + BOOLEAN bUrgentQueue; + ESL_PACKET * pPacket; + ESL_IO_MGMT ** ppActive; + ESL_IO_MGMT ** ppFree; + ESL_PORT * pPort; + ESL_PACKET ** ppQueueHead; + ESL_PACKET ** ppQueueTail; + ESL_PACKET * pPreviousPacket; + ESL_TCP6_CONTEXT * pTcp6; + size_t * pTxBytes; + EFI_TCP6_TRANSMIT_DATA * pTxData; + EFI_STATUS Status; + EFI_TPL TplPrevious; + + DBG_ENTER ( ); + + // + // Assume failure + // + Status = EFI_UNSUPPORTED; + pSocket->errno = ENOTCONN; + *pDataLength = 0; + + // + // Verify that the socket is connected + // + if ( SOCKET_STATE_CONNECTED == pSocket->State ) { + // + // Locate the port + // + pPort = pSocket->pPortList; + if ( NULL != pPort ) { + // + // Determine the queue head + // + pTcp6 = &pPort->Context.Tcp6; + bUrgent = (BOOLEAN)( 0 != ( Flags & MSG_OOB )); + bUrgentQueue = bUrgent + && ( !pSocket->bOobInLine ) + && pSocket->pApi->bOobSupported; + if ( bUrgentQueue ) { + ppQueueHead = &pSocket->pTxOobPacketListHead; + ppQueueTail = &pSocket->pTxOobPacketListTail; + ppActive = &pPort->pTxOobActive; + ppFree = &pPort->pTxOobFree; + pTxBytes = &pSocket->TxOobBytes; + } + else { + ppQueueHead = &pSocket->pTxPacketListHead; + ppQueueTail = &pSocket->pTxPacketListTail; + ppActive = &pPort->pTxActive; + ppFree = &pPort->pTxFree; + pTxBytes = &pSocket->TxBytes; + } + + // + // Verify that there is enough room to buffer another + // transmit operation + // + if ( pSocket->MaxTxBuf > *pTxBytes ) { + if ( pPort->bTxFlowControl ) { + DEBUG (( DEBUG_TX, + "TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT\r\n0x%08x: pPort, TX flow control released, Max bytes: %d > %d bufferred bytes\r\n", + pPort, + pSocket->MaxTxBuf, + *pTxBytes )); + pPort->bTxFlowControl = FALSE; + } + + // + // Attempt to allocate the packet + // + Status = EslSocketPacketAllocate ( &pPacket, + sizeof ( pPacket->Op.Tcp6Tx ) + - sizeof ( pPacket->Op.Tcp6Tx.Buffer ) + + BufferLength, + 0, + DEBUG_TX ); + if ( !EFI_ERROR ( Status )) { + // + // Initialize the transmit operation + // + pTxData = &pPacket->Op.Tcp6Tx.TxData; + pTxData->Push = TRUE || bUrgent; + pTxData->Urgent = bUrgent; + pTxData->DataLength = (UINT32) BufferLength; + pTxData->FragmentCount = 1; + pTxData->FragmentTable[0].FragmentLength = (UINT32) BufferLength; + pTxData->FragmentTable[0].FragmentBuffer = &pPacket->Op.Tcp6Tx.Buffer[0]; + + // + // Copy the data into the buffer + // + CopyMem ( &pPacket->Op.Tcp6Tx.Buffer[0], + pBuffer, + BufferLength ); + + // + // Synchronize with the socket layer + // + RAISE_TPL ( TplPrevious, TPL_SOCKETS ); + + // + // Stop transmission after an error + // + if ( !EFI_ERROR ( pSocket->TxError )) { + // + // Display the request + // + DEBUG (( DEBUG_TX, + "Send %d %s bytes from 0x%08x\r\n", + BufferLength, + bUrgent ? L"urgent" : L"normal", + pBuffer )); + + // + // Queue the data for transmission + // + pPacket->pNext = NULL; + pPreviousPacket = *ppQueueTail; + if ( NULL == pPreviousPacket ) { + *ppQueueHead = pPacket; + } + else { + pPreviousPacket->pNext = pPacket; + } + *ppQueueTail = pPacket; + DEBUG (( DEBUG_TX, + "0x%08x: Packet on %s transmit list\r\n", + pPacket, + bUrgentQueue ? L"urgent" : L"normal" )); + + // + // Account for the buffered data + // + *pTxBytes += BufferLength; + *pDataLength = BufferLength; + + // + // Start the transmit engine if it is idle + // + if ( NULL != *ppFree ) { + EslSocketTxStart ( pPort, + ppQueueHead, + ppQueueTail, + ppActive, + ppFree ); + } + } + else { + // + // Previous transmit error + // Stop transmission + // + Status = pSocket->TxError; + pSocket->errno = EIO; + + // + // Free the packet + // + EslSocketPacketFree ( pPacket, DEBUG_TX ); + } + + // + // Release the socket layer synchronization + // + RESTORE_TPL ( TplPrevious ); + } + else { + // + // Packet allocation failed + // + pSocket->errno = ENOMEM; + } + } + else { + if ( !pPort->bTxFlowControl ) { + DEBUG (( DEBUG_TX, + "0x%08x: pPort, TX flow control applied, Max bytes %d <= %d bufferred bytes\r\nTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT\r\n", + pPort, + pSocket->MaxTxBuf, + *pTxBytes )); + pPort->bTxFlowControl = TRUE; + } + // + // Not enough buffer space available + // + pSocket->errno = EAGAIN; + Status = EFI_NOT_READY; + } + } + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Process the normal data transmit completion + + This routine use ::EslSocketTxComplete to perform the transmit + completion processing for normal data. + + This routine is called by the TCPv6 network layer when a + normal data transmit request completes. + + @param [in] Event The normal transmit completion event + + @param [in] pIo The ESL_IO_MGMT structure address + +**/ +VOID +EslTcp6TxComplete ( + IN EFI_EVENT Event, + IN ESL_IO_MGMT * pIo + ) +{ + UINT32 LengthInBytes; + ESL_PACKET * pPacket; + ESL_PORT * pPort; + ESL_SOCKET * pSocket; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Locate the active transmit packet + // + pPacket = pIo->pPacket; + pPort = pIo->pPort; + pSocket = pPort->pSocket; + + // + // Get the transmit length and status + // + LengthInBytes = pPacket->Op.Tcp6Tx.TxData.DataLength; + pSocket->TxBytes -= LengthInBytes; + Status = pIo->Token.Tcp6Tx.CompletionToken.Status; + + // + // Complete the transmit operation + // + EslSocketTxComplete ( pIo, + LengthInBytes, + Status, + "Normal ", + &pSocket->pTxPacketListHead, + &pSocket->pTxPacketListTail, + &pPort->pTxActive, + &pPort->pTxFree ); + DBG_EXIT ( ); +} + + +/** + Process the urgent data transmit completion + + This routine use ::EslSocketTxComplete to perform the transmit + completion processing for urgent data. + + This routine is called by the TCPv6 network layer when a + urgent data transmit request completes. + + @param [in] Event The urgent transmit completion event + + @param [in] pIo The ESL_IO_MGMT structure address + +**/ +VOID +EslTcp6TxOobComplete ( + IN EFI_EVENT Event, + IN ESL_IO_MGMT * pIo + ) +{ + UINT32 LengthInBytes; + ESL_PACKET * pPacket; + ESL_PORT * pPort; + ESL_SOCKET * pSocket; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Locate the active transmit packet + // + pPacket = pIo->pPacket; + pPort = pIo->pPort; + pSocket = pPort->pSocket; + + // + // Get the transmit length and status + // + LengthInBytes = pPacket->Op.Tcp6Tx.TxData.DataLength; + pSocket->TxOobBytes -= LengthInBytes; + Status = pIo->Token.Tcp6Tx.CompletionToken.Status; + + // + // Complete the transmit operation + // + EslSocketTxComplete ( pIo, + LengthInBytes, + Status, + "Urgent ", + &pSocket->pTxOobPacketListHead, + &pSocket->pTxOobPacketListTail, + &pPort->pTxOobActive, + &pPort->pTxOobFree ); + DBG_EXIT ( ); +} + + +/** + Interface between the socket layer and the network specific + code that supports SOCK_STREAM and SOCK_SEQPACKET sockets + over TCPv6. +**/ +CONST ESL_PROTOCOL_API cEslTcp6Api = { + "TCPv6", + IPPROTO_TCP, + OFFSET_OF ( ESL_PORT, Context.Tcp6.ConfigData ), + OFFSET_OF ( ESL_LAYER, pTcp6List ), + sizeof ( struct sockaddr_in6 ), + sizeof ( struct sockaddr_in6 ), + AF_INET6, + sizeof (((ESL_PACKET *)0 )->Op.Tcp6Rx ), + OFFSET_OF ( ESL_PACKET, Op.Tcp6Rx.Buffer ) - OFFSET_OF ( ESL_PACKET, Op ), + OFFSET_OF ( ESL_IO_MGMT, Token.Tcp6Rx.Packet.RxData ), + TRUE, + EADDRINUSE, + EslTcp6Accept, + EslTcp6ConnectPoll, + EslTcp6ConnectStart, + EslTcp6SocketIsConfigured, + EslTcp6LocalAddressGet, + EslTcp6LocalAddressSet, + EslTcp6Listen, + NULL, // OptionGet + NULL, // OptionSet + EslTcp6PacketFree, + EslTcp6PortAllocate, + EslTcp6PortClose, + EslTcp6PortCloseOp, + FALSE, + EslTcp6Receive, + EslTcp6RemoteAddressGet, + EslTcp6RemoteAddressSet, + EslTcp6RxComplete, + EslTcp6RxStart, + EslTcp6TxBuffer, + EslTcp6TxComplete, + EslTcp6TxOobComplete +}; -- cgit v1.2.3