summaryrefslogtreecommitdiff
path: root/AppPkg/Applications/Sockets/TftpServer
diff options
context:
space:
mode:
Diffstat (limited to 'AppPkg/Applications/Sockets/TftpServer')
-rw-r--r--AppPkg/Applications/Sockets/TftpServer/TftpServer.c2203
-rw-r--r--AppPkg/Applications/Sockets/TftpServer/TftpServer.h284
-rw-r--r--AppPkg/Applications/Sockets/TftpServer/TftpServer.inf13
3 files changed, 1727 insertions, 773 deletions
diff --git a/AppPkg/Applications/Sockets/TftpServer/TftpServer.c b/AppPkg/Applications/Sockets/TftpServer/TftpServer.c
index 1da3c250d5..e9ab0dee5f 100644
--- a/AppPkg/Applications/Sockets/TftpServer/TftpServer.c
+++ b/AppPkg/Applications/Sockets/TftpServer/TftpServer.c
@@ -1,106 +1,211 @@
-/*++
- This file contains an 'Intel UEFI Application' and is
- licensed for Intel CPUs and chipsets under the terms of your
- license agreement with Intel or your vendor. This file may
- be modified by the user, subject to additional terms of the
- license agreement
---*/
-/*++
-
-Copyright (c) 2011 Intel Corporation. All rights reserved
-This software and associated documentation (if any) is furnished
-under a license and may only be used or copied in accordance
-with the terms of the license. Except as permitted by such
-license, no part of this software or documentation may be
-reproduced, stored in a retrieval system, or transmitted in any
-form or by any means without the express written consent of
-Intel Corporation.
-
---*/
-
/** @file
This is a simple TFTP server application
+ Copyright (c) 2011, 2012, 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.
+
**/
#include <TftpServer.h>
-TSDT_TFTP_SERVER mTftpServer; ///< TFTP server's control structure
+TSDT_TFTP_SERVER mTftpServer; ///< TFTP server's control structure
+volatile BOOLEAN mbTftpServerExit; ///< Set TRUE to cause TFTP server to exit
/**
- Add a connection context to the list of connection contexts.
+ Read file data into a buffer
- @param [in] pTftpServer The TFTP server control structure address.
+ @param [in] pContext Connection context structure address
- @retval Context structure address, NULL if allocation fails
+ @retval TRUE if a read error occurred
**/
-TSDT_CONNECTION_CONTEXT *
-ContextAdd (
- IN TSDT_TFTP_SERVER * pTftpServer
+BOOLEAN
+BufferFill (
+ IN TSDT_CONNECTION_CONTEXT * pContext
)
{
- size_t LengthInBytes;
- TSDT_CONNECTION_CONTEXT * pContext;
- EFI_STATUS Status;
+ BOOLEAN bReadError;
+ size_t BytesRead;
+ UINT64 LengthInBytes;
DBG_ENTER ( );
//
- // Use for/break instead of goto
+ // Use break instead of goto
//
+ bReadError = FALSE;
for ( ; ; ) {
//
- // Allocate a new context
- //
- LengthInBytes = sizeof ( *pContext );
- Status = gBS->AllocatePool ( EfiRuntimeServicesData,
- LengthInBytes,
- (VOID **)&pContext );
- if ( EFI_ERROR ( Status )) {
- DEBUG (( DEBUG_ERROR | DEBUG_POOL,
- "ERROR - Failed to allocate the context, Status: %r\r\n",
- Status ));
- pContext = NULL;
+ // Determine if there is any work to do
+ //
+ LengthInBytes = DIM ( pContext->FileData ) >> 1;
+ if (( pContext->ValidBytes > LengthInBytes )
+ || ( 0 == pContext->BytesRemaining )) {
break;
}
//
+ // Determine the number of bytes to read
+ //
+ if ( LengthInBytes > pContext->BytesRemaining ) {
+ LengthInBytes = pContext->BytesRemaining;
+ }
+
+ //
+ // Read in the next portion of the file
+ //
+ BytesRead = fread ( pContext->pFill,
+ 1,
+ (size_t)LengthInBytes,
+ pContext->File );
+ if ( -1 == BytesRead ) {
+ bReadError = TRUE;
+ break;
+ }
+
+ //
+ // Account for the file data read
+ //
+ pContext->BytesRemaining -= BytesRead;
+ pContext->ValidBytes += BytesRead;
+ DEBUG (( DEBUG_FILE_BUFFER,
+ "0x%08x: Buffer filled with %Ld bytes, %Ld bytes ramaining\r\n",
+ pContext->pFill,
+ BytesRead,
+ pContext->BytesRemaining ));
+
+ //
+ // Set the next buffer location
+ //
+ pContext->pFill += BytesRead;
+ if ( pContext->pEnd <= pContext->pFill ) {
+ pContext->pFill = &pContext->FileData[ 0 ];
+ }
+
+ //
+ // Verify that the end of the buffer is reached
+ //
+ ASSERT ( 0 == ( DIM ( pContext->FileData ) & 1 ));
+ break;
+ }
+
+ //
+ // Return the read status
+ //
+ DBG_EXIT ( );
+ return bReadError;
+}
+
+
+/**
+ Add a connection context to the list of connection contexts.
+
+ @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure
+ @param [in] SocketFd Socket file descriptor
+
+ @retval Context structure address, NULL if allocation fails
+
+**/
+TSDT_CONNECTION_CONTEXT *
+ContextAdd (
+ IN TSDT_TFTP_SERVER * pTftpServer,
+ IN int SocketFd
+ )
+{
+ TSDT_CONNECTION_CONTEXT * pContext;
+ TFTP_PACKET * pEnd;
+ TFTP_PACKET * pPacket;
+
+ DBG_ENTER ( );
+
+ //
+ // Allocate a new context
+ //
+ pContext = (TSDT_CONNECTION_CONTEXT *)AllocateZeroPool ( sizeof ( *pContext ));
+ if ( NULL != pContext ) {
+ //
// Initialize the context
//
- ZeroMem ( pContext, LengthInBytes );
+ pContext->SocketFd = SocketFd;
CopyMem ( &pContext->RemoteAddress,
&pTftpServer->RemoteAddress,
sizeof ( pContext->RemoteAddress ));
- pContext->BlockSize = TFTP_MAX_BLOCK_SIZE;
- pContext->pBuffer = &pContext->FileData[0];
- pContext->pEnd = &pContext->pBuffer[sizeof ( pContext->pBuffer )];
- pContext->MaxTransferSize = 0;
- pContext->MaxTransferSize -= 1;
+ pContext->BlockSize = 512;
+
+ //
+ // Buffer management
+ //
+ pContext->pFill = &pContext->FileData[ 0 ];
+ pContext->pEnd = &pContext->FileData[ sizeof ( pContext->FileData )];
+ pContext->pBuffer = pContext->pFill;
+
+ //
+ // Window management
+ //
+ pContext->MaxTimeout = MultU64x32 ( PcdGet32 ( Tftp_MaxTimeoutInSec ),
+ 2 * 1000 * 1000 * 1000 );
+ pContext->Rtt2x = pContext->MaxTimeout;
+ pContext->WindowSize = MAX_PACKETS;
+ WindowTimeout ( pContext );
+
+ //
+ // Place the packets on the free list
+ //
+ pPacket = &pContext->Tx[ 0 ];
+ pEnd = &pPacket[ DIM ( pContext->Tx )];
+ while ( pEnd > pPacket ) {
+ PacketFree ( pContext, pPacket );
+ pPacket += 1;
+ }
//
// Display the new context
//
- DEBUG (( DEBUG_PORT_WORK | DEBUG_INFO,
- "0x%08x: Context for %d.%d.%d.%d:%d\r\n",
- pContext,
- (UINT8)pContext->RemoteAddress.sin_addr.s_addr,
- (UINT8)( pContext->RemoteAddress.sin_addr.s_addr >> 8 ),
- (UINT8)( pContext->RemoteAddress.sin_addr.s_addr >> 16 ),
- (UINT8)( pContext->RemoteAddress.sin_addr.s_addr >> 24 ),
- htons ( pContext->RemoteAddress.sin_port )));
+ if ( AF_INET == pTftpServer->RemoteAddress.v4.sin_family ) {
+ DEBUG (( DEBUG_PORT_WORK,
+ "0x%08x: Context for %d.%d.%d.%d:%d\r\n",
+ pContext,
+ (UINT8)pTftpServer->RemoteAddress.v4.sin_addr.s_addr,
+ (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 8 ),
+ (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 16 ),
+ (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 24 ),
+ htons ( pTftpServer->RemoteAddress.v4.sin_port )));
+ }
+ else {
+ DEBUG (( DEBUG_PORT_WORK,
+ "0x%08x: Context for [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n",
+ pContext,
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 0 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 1 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 2 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 3 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 4 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 5 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 6 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 7 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 8 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 9 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 10 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 11 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 12 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 13 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 14 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 15 ],
+ htons ( pTftpServer->RemoteAddress.v6.sin6_port )));
+ }
//
// Add the context to the context list
//
pContext->pNext = pTftpServer->pContextList;
pTftpServer->pContextList = pContext;
-
- //
- // All done
- //
- break;
}
//
@@ -114,10 +219,8 @@ ContextAdd (
/**
Locate a remote connection context.
- @param [in] pTftpServer The TFTP server control structure address.
-
+ @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure
@param [in] pIpAddress The start of the remote IP address in network order
-
@param [in] Port The remote port number
@retval Context structure address, NULL if not found
@@ -140,8 +243,9 @@ ContextFind (
//
// Attempt to locate the remote network connection
//
- if (( pTftpServer->RemoteAddress.sin_addr.s_addr == pContext->RemoteAddress.sin_addr.s_addr )
- && ( pTftpServer->RemoteAddress.sin_port == pContext->RemoteAddress.sin_port )) {
+ if ( 0 == memcmp ( &pTftpServer->RemoteAddress,
+ &pContext->RemoteAddress,
+ pTftpServer->RemoteAddress.v6.sin6_len )) {
//
// The connection was found
//
@@ -168,9 +272,8 @@ ContextFind (
/**
Remove a context from the list.
- @param [in] pTftpServer The TFTP server control structure address.
-
- @param [in] pContext The context structure address.
+ @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure
+ @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure
**/
VOID
@@ -228,79 +331,151 @@ ContextRemove (
/**
- Process the work for the sockets.
+ Queue data packets for transmission
- @param [in] pTftpServer The TFTP server control structure address.
+ @param [in] pContext Connection context structure address
+
+ @retval TRUE if a read error occurred
**/
-VOID
-PortWork (
- IN TSDT_TFTP_SERVER * pTftpServer
+BOOLEAN
+PacketFill (
+ IN TSDT_CONNECTION_CONTEXT * pContext
)
{
- TSDT_CONNECTION_CONTEXT * pContext;
- socklen_t RemoteAddressLength;
+ BOOLEAN bReadError;
+ UINT64 LengthInBytes;
+ UINT8 * pBuffer;
+ TFTP_PACKET * pPacket;
DBG_ENTER ( );
//
- // Handle input events
+ // Use break instead of goto
//
- if ( 0 != ( pTftpServer->TftpPort.revents & POLLRDNORM )) {
+ bReadError = FALSE;
+ for ( ; ; ) {
//
- // Receive the message from the remote system
+ // Fill the buffer if necessary
//
- RemoteAddressLength = sizeof ( pTftpServer->RemoteAddress );
- pTftpServer->RxBytes = recvfrom ( pTftpServer->TftpPort.fd,
- &pTftpServer->RxBuffer[0],
- sizeof ( pTftpServer->RxBuffer ),
- 0,
- (struct sockaddr *) &pTftpServer->RemoteAddress,
- &RemoteAddressLength );
- if ( -1 != pTftpServer->RxBytes ) {
- pTftpServer->RemoteAddress.sin_len = (UINT8) RemoteAddressLength;
- DEBUG (( DEBUG_TFTP_PORT,
- "Received %d bytes from %d.%d.%d.%d:%d\r\n",
- pTftpServer->RxBytes,
- pTftpServer->RemoteAddress.sin_addr.s_addr & 0xff,
- ( pTftpServer->RemoteAddress.sin_addr.s_addr >> 8 ) & 0xff,
- ( pTftpServer->RemoteAddress.sin_addr.s_addr >> 16 ) & 0xff,
- ( pTftpServer->RemoteAddress.sin_addr.s_addr >> 24 ) & 0xff,
- htons ( pTftpServer->RemoteAddress.sin_port )));
+ bReadError = BufferFill ( pContext );
+ if ( bReadError ) {
+ //
+ // File access mode not supported
+ //
+ DEBUG (( DEBUG_ERROR | DEBUG_TFTP_REQUEST,
+ "ERROR - File read failure!\r\n" ));
//
- // Lookup connection context using the remote system address and port
- // to determine if an existing connection to this remote
- // system exists
+ // Tell the client of the error
//
- pContext = ContextFind ( pTftpServer );
+ SendError ( pContext,
+ TFTP_ERROR_SEE_MSG,
+ (UINT8 *)"Read failure" );
+ break;
+ }
+ //
+ // Determine if any packets can be filled
+ //
+ if ( pContext->bEofSent
+ || ( NULL == pContext->pFreeList )) {
//
- // Process the received message
+ // All of the packets are filled
//
- TftpProcessRequest ( pTftpServer, pContext );
+ break;
}
- else {
+
+ //
+ // Set the TFTP opcode and block number
+ //
+ pPacket = PacketGet ( pContext );
+ pBuffer = &pPacket->TxBuffer[ 0 ];
+ *pBuffer++ = 0;
+ *pBuffer++ = TFTP_OP_DATA;
+ *pBuffer++ = (UINT8)( pContext->BlockNumber >> 8 );
+ *pBuffer++ = (UINT8)pContext->BlockNumber;
+
+ //
+ // Determine how much data needs to be sent
+ //
+ LengthInBytes = pContext->BlockSize;
+ if (( pContext->BytesToSend < TFTP_MAX_BLOCK_SIZE )
+ && ( LengthInBytes > pContext->BytesToSend )) {
+ LengthInBytes = pContext->BytesToSend;
+ pContext->bEofSent = TRUE;
+ }
+ DEBUG (( DEBUG_TX_PACKET,
+ "0x%08x: Packet, Block %d filled with %d bytes\r\n",
+ pPacket,
+ pContext->BlockNumber,
+ (UINT32)LengthInBytes ));
+
+ //
+ // Copy the file data into the packet
+ //
+ pPacket->TxBytes = (ssize_t)( 2 + 2 + LengthInBytes );
+ if ( 0 < LengthInBytes ) {
+ CopyMem ( pBuffer,
+ pContext->pBuffer,
+ (UINTN)LengthInBytes );
+ DEBUG (( DEBUG_FILE_BUFFER,
+ "0x%08x: Buffer consumed %d bytes of file data\r\n",
+ pContext->pBuffer,
+ LengthInBytes ));
+
//
- // Receive error on the TFTP server port
- // Close the server socket
+ // Account for the file data consumed
//
- DEBUG (( DEBUG_ERROR,
- "ERROR - Failed receive on TFTP server port, errno: 0x%08x\r\n",
- errno ));
- pTftpServer->TftpPort.revents |= POLLHUP;
+ pContext->ValidBytes -= LengthInBytes;
+ pContext->BytesToSend -= LengthInBytes;
+ pContext->pBuffer += LengthInBytes;
+ if ( pContext->pEnd <= pContext->pBuffer ) {
+ pContext->pBuffer = &pContext->FileData[ 0 ];
+ }
}
+
+ //
+ // Queue the packet for transmission
+ //
+ PacketQueue ( pContext, pPacket );
}
//
- // Handle the close event
+ // Return the read status
//
- if ( 0 != ( pTftpServer->TftpPort.revents & POLLHUP )) {
+ DBG_EXIT ( );
+ return bReadError;
+}
+
+
+/**
+ Free the packet
+
+ @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure
+ @param [in] pPacket Address of a ::TFTP_PACKET structure
+
+**/
+VOID
+PacketFree(
+ IN TSDT_CONNECTION_CONTEXT * pContext,
+ IN TFTP_PACKET * pPacket
+ )
+{
+ DBG_ENTER ( );
+
+ //
+ // Don't free the error packet
+ //
+ if ( pPacket != &pContext->ErrorPacket ) {
//
- // Close the port
+ // Place the packet on the free list
//
- close ( pTftpServer->TftpPort.fd );
- pTftpServer->TftpPort.fd = -1;
+ pPacket->pNext = pContext->pFreeList;
+ pContext->pFreeList = pPacket;
+ DEBUG (( DEBUG_TX_PACKET,
+ "0x%08x: Packet queued to free list\r\n",
+ pPacket ));
}
DBG_EXIT ( );
@@ -308,127 +483,658 @@ PortWork (
/**
- Scan the list of sockets and process any pending work
+ Get a packet from the free list for transmission
+
+ @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure
- @param [in] pTftpServer The TFTP server control structure address.
+ @retval Address of a ::TFTP_PACKET structure
**/
-VOID
-SocketPoll (
- IN TSDT_TFTP_SERVER * pTftpServer
+TFTP_PACKET *
+PacketGet (
+ IN TSDT_CONNECTION_CONTEXT * pContext
)
{
- int FDCount;
+ TFTP_PACKET * pPacket;
- DEBUG (( DEBUG_SOCKET_POLL, "Entering SocketPoll\r\n" ));
+ DBG_ENTER ( );
//
- // Determine if any ports are active
+ // Get the next packet from the free list
+ //
+ pPacket = pContext->pFreeList;
+ if ( NULL != pPacket ) {
+ pContext->pFreeList = pPacket->pNext;
+ pPacket->RetryCount = 0;
+ DEBUG (( DEBUG_TX_PACKET,
+ "0x%08x: Packet removed from free list\r\n",
+ pPacket ));
+ }
+
+ //
+ // Return the packet
+ //
+ DBG_EXIT_HEX ( pPacket );
+ return pPacket;
+}
+
+
+/**
+ Queue the packet for transmission
+
+ @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure
+ @param [in] pPacket Address of a ::TFTP_PACKET structure
+
+ @retval TRUE if a transmission error has occurred
+
+**/
+BOOLEAN
+PacketQueue (
+ IN TSDT_CONNECTION_CONTEXT * pContext,
+ IN TFTP_PACKET * pPacket
+ )
+{
+ BOOLEAN bTransmitError;
+ TFTP_PACKET * pTail;
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Account for this data block
+ //
+ pPacket->BlockNumber = pContext->BlockNumber;
+ pContext->BlockNumber += 1;
+
+ //
+ // Queue the packet for transmission
+ //
+ pTail = pContext->pTxTail;
+ if ( NULL == pTail ) {
+ pContext->pTxHead = pPacket;
+ }
+ else {
+ pTail->pNext = pPacket;
+ }
+ pContext->pTxTail = pPacket;
+ pPacket->pNext = NULL;
+ DEBUG (( DEBUG_TX_PACKET,
+ "0x%08x: Packet queued to TX list\r\n",
+ pPacket ));
+
+ //
+ // Start the transmission if necessary
//
- FDCount = poll ( &pTftpServer->TftpPort,
- 1,
- CLIENT_POLL_DELAY );
- if ( -1 == FDCount ) {
- DEBUG (( DEBUG_ERROR | DEBUG_SOCKET_POLL,
- "ERROR - errno: %d\r\n",
- errno ));
+ bTransmitError = FALSE;
+ if ( pContext->PacketsInWindow < pContext->WindowSize ) {
+ Status = PacketTx ( pContext, pPacket );
+ bTransmitError = (BOOLEAN)( EFI_ERROR ( Status ));
}
- if ( 0 < FDCount ) {
+ //
+ // Return the transmit status
+ //
+ DBG_EXIT_TF ( bTransmitError );
+ return bTransmitError;
+}
+
+
+/**
+ Remove a packet from the transmit queue
+
+ @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure
+
+**/
+TFTP_PACKET *
+PacketRemove(
+ IN TSDT_CONNECTION_CONTEXT * pContext
+ )
+{
+ TFTP_PACKET * pNext;
+ TFTP_PACKET * pPacket;
+
+ DBG_ENTER ( );
+
+ //
+ // Remove a packet from the transmit queue
+ //
+ //
+ pPacket = pContext->pTxHead;
+ if ( NULL != pPacket ) {
+ pNext = pPacket->pNext;
+ pContext->pTxHead = pNext;
+ if ( NULL == pNext ) {
+ pContext->pTxTail = NULL;
+ }
+ DEBUG (( DEBUG_TX_PACKET,
+ "0x%08x: Packet removed from TX list\r\n",
+ pPacket ));
+
//
- // Process this port
+ // Remove this packet from the window
//
- PortWork ( pTftpServer );
- pTftpServer->TftpPort.revents = 0;
+ pContext->PacketsInWindow -= 1;
}
- DEBUG (( DEBUG_SOCKET_POLL, "Exiting SocketPoll\r\n" ));
+ //
+ // Return the packet
+ //
+ DBG_EXIT_HEX ( pPacket );
+ return pPacket;
}
/**
- Convert a character to lower case
+ Transmit the packet
- @param [in] Character The character to convert
+ @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure
+ @param [in] pPacket Address of a ::TFTP_PACKET structure
- @return The lower case equivalent of the character
+ @retval EFI_SUCCESS Message processed successfully
**/
-int
-tolower (
- int Character
+EFI_STATUS
+PacketTx (
+ IN TSDT_CONNECTION_CONTEXT * pContext,
+ IN TFTP_PACKET * pPacket
)
{
+ ssize_t LengthInBytes;
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
//
- // Determine if the character is upper case
+ // Assume success
+ //
+ Status = EFI_SUCCESS;
+
//
- if (( 'A' <= Character ) && ( 'Z' >= Character )) {
+ // Determine if this packet should be transmitted
+ //
+ if ( PcdGet32 ( Tftp_MaxRetry ) >= pPacket->RetryCount ) {
+ pPacket->RetryCount += 1;
+
+ //
+ // Display the operation
+ //
+ DEBUG (( DEBUG_TX_PACKET,
+ "0x%08x: Packet transmiting\r\n",
+ pPacket ));
+ DEBUG (( DEBUG_TX,
+ "0x%08x: pContext sending 0x%08x bytes\r\n",
+ pContext,
+ pPacket->TxBytes ));
+
//
- // Convert the character to lower caes
+ // Keep track of when the packet was transmitted
//
- Character += 'a' - 'A';
+ if ( PcdGetBool ( Tftp_HighSpeed )) {
+ pPacket->TxTime = GetPerformanceCounter ( );
+ }
+
+ //
+ // Send the TFTP packet
+ //
+ pContext->PacketsInWindow += 1;
+ LengthInBytes = sendto ( pContext->SocketFd,
+ &pPacket->TxBuffer[ 0 ],
+ pPacket->TxBytes,
+ 0,
+ (struct sockaddr *)&pContext->RemoteAddress,
+ pContext->RemoteAddress.sin6_len );
+ if ( -1 == LengthInBytes ) {
+ DEBUG (( DEBUG_ERROR | DEBUG_TX,
+ "ERROR - Transmit failure, errno: 0x%08x\r\n",
+ errno ));
+ pContext->PacketsInWindow -= 1;
+ Status = EFI_DEVICE_ERROR;
+ }
+ }
+ else {
+ //
+ // Too many retries
+ //
+ Status = EFI_NO_RESPONSE;
+ DEBUG (( DEBUG_WARN | DEBUG_WINDOW,
+ "WARNING - No response from TFTP client\r\n" ));
}
//
- // Return the converted character
+ // Return the operation status
//
- return Character;
+ DBG_EXIT_STATUS ( Status );
+ return Status;
}
/**
- Case independent string comparison
+ Process the work for the sockets.
+
+ @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure
+ @param [in] pIndex Address of an index into the pollfd array
+
+**/
+VOID
+PortWork (
+ IN TSDT_TFTP_SERVER * pTftpServer,
+ IN int * pIndex
+ )
+{
+ int Index;
+ TSDT_CONNECTION_CONTEXT * pContext;
+ struct pollfd * pTftpPort;
+ socklen_t RemoteAddressLength;
+ int revents;
+
+ DBG_ENTER ( );
+
+ //
+ // Locate the port
+ //
+ Index = *pIndex;
+ if ( -1 != Index ) {
+ pTftpPort = &pTftpServer->TftpPort[ *pIndex ];
- @param [in] pString1 Zero terminated string address
- @param [in] pString2 Zero terminated string address
+ //
+ // Handle input events
+ //
+ revents = pTftpPort->revents;
+ pTftpPort->revents = 0;
+ if ( 0 != ( revents & POLLRDNORM )) {
+ //
+ // Receive the message from the remote system
+ //
+ RemoteAddressLength = sizeof ( pTftpServer->RemoteAddress );
+ pTftpServer->RxBytes = recvfrom ( pTftpPort->fd,
+ &pTftpServer->RxBuffer[ 0 ],
+ sizeof ( pTftpServer->RxBuffer ),
+ 0,
+ (struct sockaddr *) &pTftpServer->RemoteAddress,
+ &RemoteAddressLength );
+ if ( -1 != pTftpServer->RxBytes ) {
+ if ( PcdGetBool ( Tftp_HighSpeed )) {
+ pTftpServer->RxTime = GetPerformanceCounter ( );
+ }
+ if ( AF_INET == pTftpServer->RemoteAddress.v4.sin_family ) {
+ DEBUG (( DEBUG_TFTP_PORT,
+ "Received %d bytes from %d.%d.%d.%d:%d\r\n",
+ pTftpServer->RxBytes,
+ pTftpServer->RemoteAddress.v4.sin_addr.s_addr & 0xff,
+ ( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 8 ) & 0xff,
+ ( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 16 ) & 0xff,
+ ( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 24 ) & 0xff,
+ htons ( pTftpServer->RemoteAddress.v4.sin_port )));
+ }
+ else {
+ DEBUG (( DEBUG_TFTP_PORT,
+ "Received %d bytes from [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n",
+ pTftpServer->RxBytes,
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 0 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 1 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 2 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 3 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 4 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 5 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 6 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 7 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 8 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 9 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 10 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 11 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 12 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 13 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 14 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 15 ],
+ htons ( pTftpServer->RemoteAddress.v6.sin6_port )));
+ }
- @return Returns the first character difference between string 1
- and string 2.
+ //
+ // Lookup connection context using the remote system address and port
+ // to determine if an existing connection to this remote
+ // system exists
+ //
+ pContext = ContextFind ( pTftpServer );
+
+ //
+ // Process the received message
+ //
+ TftpProcessRequest ( pTftpServer, pContext, pTftpPort->fd );
+ }
+ else {
+ //
+ // Receive error on the TFTP server port
+ // Close the server socket
+ //
+ DEBUG (( DEBUG_ERROR,
+ "ERROR - Failed receive on TFTP server port, errno: 0x%08x\r\n",
+ errno ));
+ revents |= POLLHUP;
+ }
+ }
+
+ //
+ // Handle the close event
+ //
+ if ( 0 != ( revents & POLLHUP )) {
+ //
+ // Close the port
+ //
+ close ( pTftpPort->fd );
+ pTftpPort->fd = -1;
+ *pIndex = -1;
+ pTftpServer->Entries -= 1;
+ ASSERT ( 0 <= pTftpServer->Entries );
+ }
+ }
+
+ DBG_EXIT ( );
+}
+
+
+/**
+ Build and send an error packet
+
+ @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure
+ @param [in] Error Error number for the packet
+ @param [in] pError Zero terminated error string address
+
+ @retval EFI_SUCCESS Message processed successfully
**/
-int
-stricmp (
- char * pString1,
- char * pString2
+EFI_STATUS
+SendError (
+ IN TSDT_CONNECTION_CONTEXT * pContext,
+ IN UINT16 Error,
+ IN UINT8 * pError
)
{
- int Char1;
- int Char2;
- int Difference;
+ UINT8 Character;
+ UINT8 * pBuffer;
+ TFTP_PACKET * pPacket;
+ EFI_STATUS Status;
+ DBG_ENTER ( );
+
+ //
+ // Build the error packet
//
- // Walk the length of the strings
+ pPacket = &pContext->ErrorPacket;
+ pBuffer = &pPacket->TxBuffer[ 0 ];
+ pBuffer[ 0 ] = 0;
+ pBuffer[ 1 ] = TFTP_OP_ERROR;
+ pBuffer[ 2 ] = (UINT8)( Error >> 8 );
+ pBuffer[ 3 ] = (UINT8)Error;
+
//
+ // Copy the zero terminated string into the buffer
+ //
+ pBuffer += 4;
do {
+ Character = *pError++;
+ *pBuffer++ = Character;
+ } while ( 0 != Character );
+
+ //
+ // Send the error message
+ //
+ pPacket->TxBytes = pBuffer - &pPacket->TxBuffer[ 0 ];
+ Status = PacketTx ( pContext, pPacket );
+
+ //
+ // Return the operation status
+ //
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ Scan the list of sockets and process any pending work
+
+ @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure
+
+**/
+VOID
+SocketPoll (
+ IN TSDT_TFTP_SERVER * pTftpServer
+ )
+{
+ int FDCount;
+
+ DEBUG (( DEBUG_SOCKET_POLL, "Entering SocketPoll\r\n" ));
+
+ //
+ // Determine if any ports are active
+ //
+ if ( 0 != pTftpServer->Entries ) {
+ FDCount = poll ( &pTftpServer->TftpPort[ 0 ],
+ pTftpServer->Entries,
+ CLIENT_POLL_DELAY );
+ if ( 0 < FDCount ) {
+ //
+ // Process this port
+ //
+ PortWork ( pTftpServer, &pTftpServer->Udpv4Index );
+ PortWork ( pTftpServer, &pTftpServer->Udpv6Index );
+ }
+ }
+
+ DEBUG (( DEBUG_SOCKET_POLL, "Exiting SocketPoll\r\n" ));
+}
+
+
+/**
+ Process the ACK
+
+ @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure
+ @param [in] pContext Connection context structure address
+
+ @retval TRUE if the context should be closed
+
+**/
+BOOLEAN
+TftpAck (
+ IN TSDT_TFTP_SERVER * pTftpServer,
+ IN TSDT_CONNECTION_CONTEXT * pContext
+ )
+{
+ INTN AckNumber;
+ BOOLEAN bCloseContext;
+ UINT16 BlockNumber;
+ UINT8 * pBuffer;
+ TFTP_PACKET * pPacket;
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Use break instead of goto
+ //
+ bCloseContext = FALSE;
+ for ( ; ; ) {
+ //
+ // Validate the parameters
//
- // Get the next characters
+ if ( NULL == pContext ) {
+ if ( AF_INET == pTftpServer->RemoteAddress.v4.sin_family ) {
+ DEBUG (( DEBUG_ERROR,
+ "ERROR - File not open for %d.%d.%d.%d:%d\r\n",
+ (UINT8)pTftpServer->RemoteAddress.v4.sin_addr.s_addr,
+ (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 8 ),
+ (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 16 ),
+ (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 24 ),
+ htons ( pTftpServer->RemoteAddress.v4.sin_port )));
+ }
+ else {
+ DEBUG (( DEBUG_ERROR,
+ "ERROR - File not open for [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n",
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 0 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 1 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 2 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 3 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 4 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 5 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 6 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 7 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 8 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 9 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 10 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 11 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 12 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 13 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 14 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 15 ],
+ htons ( pTftpServer->RemoteAddress.v6.sin6_port )));
+ }
+ break;
+ }
+
//
- Char1 = (UINT8)*pString1++;
- Char2 = (UINT8)*pString2++;
+ // Verify that the ACK was expected
+ //
+ pPacket = pContext->pTxHead;
+ if ( NULL == pPacket ) {
+ //
+ // ACK not expected!
+ //
+ DEBUG (( DEBUG_ERROR,
+ "ERROR - Expecting data not ACKs for pContext 0x%08x\r\n",
+ pContext ));
+ break;
+ }
//
- // Convert them to lower case
+ // Get the ACKed block number
//
- Char1 = tolower ( Char1 );
- Char2 = tolower ( Char2 );
+ pBuffer = &pTftpServer->RxBuffer[ 0 ];
+ BlockNumber = HTONS ( *(UINT16 *)&pBuffer[ 2 ]);
//
- // Done when the characters differ
+ // Determine if this is the correct ACK
//
- Difference = Char1 - Char2;
- if ( 0 != Difference ) {
+ DEBUG (( DEBUG_TFTP_ACK,
+ "ACK for block 0x%04x received\r\n",
+ BlockNumber ));
+ AckNumber = BlockNumber - pPacket->BlockNumber;
+ if (( 0 > AckNumber ) || ( AckNumber >= (INTN)pContext->PacketsInWindow )){
+ DEBUG (( DEBUG_WARN | DEBUG_TFTP_ACK,
+ "WARNING - Expecting ACK 0x%0x4 not received ACK 0x%08x\r\n",
+ pPacket->BlockNumber,
+ BlockNumber ));
break;
}
//
- // Done at the end of the string
+ // Release the ACKed packets
//
- } while ( 0 != Char1 );
+ do {
+ //
+ // Remove the packet from the transmit list and window
+ //
+ pPacket = PacketRemove ( pContext );
+
+ //
+ // Get the block number of this packet
+ //
+ AckNumber = pPacket->BlockNumber;
+
+ //
+ // Increase the size of the transmit window
+ //
+ if ( PcdGetBool ( Tftp_HighSpeed )
+ && ( AckNumber == BlockNumber )) {
+ WindowAck ( pTftpServer, pContext, pPacket );
+ }
+
+ //
+ // Free this packet
+ //
+ PacketFree ( pContext, pPacket );
+ } while (( NULL != pContext->pTxHead ) && ( AckNumber != BlockNumber ));
+
+ //
+ // Fill the window with packets
+ //
+ pPacket = pContext->pTxHead;
+ while (( NULL != pPacket )
+ && ( pContext->PacketsInWindow < pContext->WindowSize )
+ && ( !bCloseContext )) {
+ Status = PacketTx ( pContext, pPacket );
+ bCloseContext = (BOOLEAN)( EFI_ERROR ( Status ));
+ pPacket = pPacket->pNext;
+ }
+
+ //
+ // Get more packets ready for transmission
+ //
+ PacketFill ( pContext );
+
+ //
+ // Close the context when the last packet is ACKed
+ //
+ if ( 0 == pContext->PacketsInWindow ) {
+ bCloseContext = TRUE;
+
+ //
+ // Display the bandwidth
+ //
+ if ( PcdGetBool ( Tftp_Bandwidth )) {
+ UINT64 Bandwidth;
+ UINT64 DeltaTime;
+ UINT64 NanoSeconds;
+ UINT32 Value;
+
+ //
+ // Compute the download time
+ //
+ DeltaTime = GetPerformanceCounter ( );
+ if ( pTftpServer->Time2 > pTftpServer->Time1 ) {
+ DeltaTime = DeltaTime - pContext->TimeStart;
+ }
+ else {
+ DeltaTime = pContext->TimeStart - DeltaTime;
+ }
+ NanoSeconds = GetTimeInNanoSecond ( DeltaTime );
+ Bandwidth = pContext->LengthInBytes;
+ DEBUG (( DEBUG_WINDOW,
+ "File Length %Ld, Transfer Time: %d.%03d Sec\r\n",
+ Bandwidth,
+ DivU64x32 ( NanoSeconds, 1000 * 1000 * 1000 ),
+ ((UINT32)DivU64x32 ( NanoSeconds, 1000 * 1000 )) % 1000 ));
+
+ //
+ // Display the round trip time
+ //
+ Bandwidth = MultU64x32 ( Bandwidth, 8 * 1000 * 1000 );
+ Bandwidth /= NanoSeconds;
+ if ( 1000 > Bandwidth ) {
+ Value = (UINT32)Bandwidth;
+ Print ( L"Bandwidth: %d Kbits/Sec\r\n",
+ Value );
+ }
+ else if (( 1000 * 1000 ) > Bandwidth ) {
+ Value = (UINT32)Bandwidth;
+ Print ( L"Bandwidth: %d.%03d Mbits/Sec\r\n",
+ Value / 1000,
+ Value % 1000 );
+ }
+ else {
+ Value = (UINT32)DivU64x32 ( Bandwidth, 1000 );
+ Print ( L"Bandwidth: %d.%03d Gbits/Sec\r\n",
+ Value / 1000,
+ Value % 1000 );
+ }
+ }
+ }
+ break;
+ }
//
- // Return the difference
+ // Return the operation status
//
- return Difference;
+ DBG_EXIT ( );
+ return bCloseContext;
}
@@ -539,7 +1245,7 @@ TftpOptionSet (
/**
Process the TFTP request
- @param [in] pContext The context structure address.
+ @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure
@param [in] pOption Address of the first zero terminated option string
@param [in] pEnd End of buffer address
@@ -553,20 +1259,27 @@ TftpOptions (
{
UINT8 * pNextOption;
UINT8 * pOack;
+ TFTP_PACKET * pPacket;
UINT8 * pTemp;
UINT8 * pValue;
EFI_STATUS Status;
INT32 Value;
//
+ // Get a packet
+ //
+ pPacket = PacketGet ( pContext );
+
+ //
// Start the OACK packet
// Let the OACK handle the parsing errors
// See http://tools.ietf.org/html/rfc2347
//
- pOack = &pContext->TxBuffer[0];
+ pOack = &pPacket->TxBuffer[ 0 ];
*pOack++ = 0;
*pOack++ = TFTP_OP_OACK;
- pContext->TxBytes = 2;
+ pPacket->TxBytes = 2;
+ pPacket->BlockNumber = 0;
//
// Walk the list of options
@@ -585,7 +1298,7 @@ TftpOptions (
// blksize - See http://tools.ietf.org/html/rfc2348
//
pValue = pNextOption;
- if ( 0 == stricmp ((char *)pOption, "blksize" )) {
+ if ( 0 == strcasecmp ((char *)pOption, "blksize" )) {
//
// Get the value
//
@@ -625,7 +1338,7 @@ TftpOptions (
*pOack++ = 0;
pOack = TftpOptionSet ( pOack, pContext->BlockSize );
*pOack++ = 0;
- pContext->TxBytes += pOack - pTemp;
+ pPacket->TxBytes += pOack - pTemp;
}
}
}
@@ -633,7 +1346,7 @@ TftpOptions (
//
// timeout - See http://tools.ietf.org/html/rfc2349
//
- else if ( 0 == stricmp ((char *)pOption, "timeout" )) {
+ else if ( 0 == strcasecmp ((char *)pOption, "timeout" )) {
//
// Get the value
//
@@ -644,10 +1357,10 @@ TftpOptions (
//
// Set the timeout value
//
- pContext->Timeout = Value;
+ pContext->MaxTimeout = Value;
DEBUG (( DEBUG_TFTP_REQUEST,
"Using timeout of %d seconds\r\n",
- pContext->Timeout ));
+ pContext->MaxTimeout ));
//
// Update the OACK
@@ -661,9 +1374,9 @@ TftpOptions (
*pOack++ = 'u';
*pOack++ = 't';
*pOack++ = 0;
- pOack = TftpOptionSet ( pOack, pContext->Timeout );
+ pOack = TftpOptionSet ( pOack, pContext->MaxTimeout );
*pOack++ = 0;
- pContext->TxBytes += pOack - pTemp;
+ pPacket->TxBytes += pOack - pTemp;
}
}
}
@@ -671,7 +1384,7 @@ TftpOptions (
//
// tsize - See http://tools.ietf.org/html/rfc2349
//
- else if ( 0 == stricmp ((char *)pOption, "tsize" )) {
+ else if ( 0 == strcasecmp ((char *)pOption, "tsize" )) {
//
// Get the value
//
@@ -698,7 +1411,7 @@ TftpOptions (
*pOack++ = 0;
pOack = TftpOptionSet ( pOack, pContext->LengthInBytes );
*pOack++ = 0;
- pContext->TxBytes += pOack - pTemp;
+ pPacket->TxBytes += pOack - pTemp;
}
}
}
@@ -717,6 +1430,16 @@ TftpOptions (
//
pOption = pNextOption;
} while ( pEnd > pOption );
+
+ //
+ // Transmit the OACK if necessary
+ //
+ if ( 2 < pPacket->TxBytes ) {
+ PacketQueue ( pContext, pPacket );
+ }
+ else {
+ PacketFree ( pContext, pPacket );
+ }
}
@@ -781,69 +1504,91 @@ TftpOptionValue (
/**
Process the TFTP request
- @param [in] pTftpServer The TFTP server control structure address.
- @param [in] pContext Connection context structure address
+ @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure
+ @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure
+ @param [in] SocketFd Socket file descriptor
**/
VOID
TftpProcessRequest (
IN TSDT_TFTP_SERVER * pTftpServer,
- IN TSDT_CONNECTION_CONTEXT * pContext
+ IN TSDT_CONNECTION_CONTEXT * pContext,
+ IN int SocketFd
)
{
BOOLEAN bCloseContext;
- BOOLEAN bIgnorePacket;
- UINT16 BlockNumber;
UINT16 Opcode;
- UINT8 * pBuffer;
- UINT8 * pEnd;
- UINT8 * pFileName;
- UINT8 * pMode;
- UINT8 * pOption;
- EFI_STATUS Status;
DBG_ENTER ( );
//
// Get the opcode
//
- pBuffer = &pTftpServer->RxBuffer[0];
- Opcode = HTONS ( *(UINT16 *)&pBuffer[0]);
-Print ( L"TFTP Opcode: 0x%08x\r\n", Opcode );
+ Opcode = HTONS ( *(UINT16 *)&pTftpServer->RxBuffer[ 0 ]);
+ DEBUG (( DEBUG_TFTP_REQUEST,
+ "TFTP Opcode: 0x%08x\r\n",
+ Opcode ));
//
// Validate the parameters
//
bCloseContext = FALSE;
- bIgnorePacket = FALSE;
switch ( Opcode ) {
default:
DEBUG (( DEBUG_TFTP_REQUEST,
"ERROR - Unknown TFTP opcode: %d\r\n",
Opcode ));
- bIgnorePacket = TRUE;
+ break;
+
+ case TFTP_OP_ACK:
+ bCloseContext = TftpAck ( pTftpServer, pContext );
break;
case TFTP_OP_READ_REQUEST:
+ bCloseContext = TftpRead ( pTftpServer, pContext, SocketFd );
break;
+
+
+
case TFTP_OP_DATA:
if ( NULL == pContext ) {
- DEBUG (( DEBUG_ERROR,
- "ERROR - File not open for %d.%d.%d.%d:%d\r\n",
- (UINT8)pTftpServer->RemoteAddress.sin_addr.s_addr,
- (UINT8)( pTftpServer->RemoteAddress.sin_addr.s_addr >> 8 ),
- (UINT8)( pTftpServer->RemoteAddress.sin_addr.s_addr >> 16 ),
- (UINT8)( pTftpServer->RemoteAddress.sin_addr.s_addr >> 24 ),
- htons ( pTftpServer->RemoteAddress.sin_port )));
- bIgnorePacket = TRUE;
+ if ( AF_INET == pTftpServer->RemoteAddress.v4.sin_family ) {
+ DEBUG (( DEBUG_ERROR,
+ "ERROR - File not open for %d.%d.%d.%d:%d\r\n",
+ (UINT8)pTftpServer->RemoteAddress.v4.sin_addr.s_addr,
+ (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 8 ),
+ (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 16 ),
+ (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 24 ),
+ htons ( pTftpServer->RemoteAddress.v4.sin_port )));
+ }
+ else {
+ DEBUG (( DEBUG_ERROR,
+ "ERROR - File not open for [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n",
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 0 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 1 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 2 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 3 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 4 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 5 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 6 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 7 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 8 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 9 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 10 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 11 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 12 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 13 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 14 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 15 ],
+ htons ( pTftpServer->RemoteAddress.v6.sin6_port )));
+ }
break;
}
- if ( pContext->bExpectAck ) {
+ if ( 0 != pContext->PacketsInWindow ) {
DEBUG (( DEBUG_ERROR,
"ERROR - Expecting ACKs not data for pContext 0x%08x\r\n",
pContext ));
- bIgnorePacket = TRUE;
break;
}
if ( pTftpServer->RxBytes > (ssize_t)( pContext->BlockSize + 2 + 2 )) {
@@ -852,229 +1597,44 @@ Print ( L"TFTP Opcode: 0x%08x\r\n", Opcode );
pTftpServer->RxBytes - 2 - 2,
pContext->BlockSize,
pContext ));
- bIgnorePacket = TRUE;
- break;
- }
- break;
-
- case TFTP_OP_ACK:
- if ( NULL == pContext ) {
- DEBUG (( DEBUG_ERROR,
- "ERROR - File not open for %d.%d.%d.%d:%d\r\n",
- (UINT8)pTftpServer->RemoteAddress.sin_addr.s_addr,
- (UINT8)( pTftpServer->RemoteAddress.sin_addr.s_addr >> 8 ),
- (UINT8)( pTftpServer->RemoteAddress.sin_addr.s_addr >> 16 ),
- (UINT8)( pTftpServer->RemoteAddress.sin_addr.s_addr >> 24 ),
- htons ( pTftpServer->RemoteAddress.sin_port )));
- bIgnorePacket = TRUE;
- }
- if ( !pContext->bExpectAck ) {
- DEBUG (( DEBUG_ERROR,
- "ERROR - Expecting data not ACKs for pContext 0x%08x\r\n",
- pContext ));
- bIgnorePacket = TRUE;
break;
}
break;
case TFTP_OP_ERROR:
if ( NULL == pContext ) {
- DEBUG (( DEBUG_ERROR,
- "ERROR - File not open for %d.%d.%d.%d:%d\r\n",
- (UINT8)pTftpServer->RemoteAddress.sin_addr.s_addr,
- (UINT8)( pTftpServer->RemoteAddress.sin_addr.s_addr >> 8 ),
- (UINT8)( pTftpServer->RemoteAddress.sin_addr.s_addr >> 16 ),
- (UINT8)( pTftpServer->RemoteAddress.sin_addr.s_addr >> 24 ),
- htons ( pTftpServer->RemoteAddress.sin_port )));
- bIgnorePacket = TRUE;
- }
- break;
- }
- if ( !bIgnorePacket ) {
- //
- // Process the request
- //
- switch ( Opcode ) {
- default:
- DEBUG (( DEBUG_TFTP_REQUEST,
- "ERROR - Unable to process TFTP opcode: %d\r\n",
- Opcode ));
- break;
-
- case TFTP_OP_READ_REQUEST:
-
- //
- // Close the context if necessary
- //
- if ( NULL != pContext ) {
- ContextRemove ( pTftpServer, pContext );
- }
-
- //
- // Create the connection context
- //
- pContext = ContextAdd ( pTftpServer );
- if ( NULL == pContext ) {
- break;
- }
-
- //
- // Locate the mode
- //
- pFileName = &pBuffer[2];
- pEnd = &pBuffer[pTftpServer->RxBytes];
- pMode = pFileName;
- while (( pEnd > pMode ) && ( 0 != *pMode )) {
- pMode += 1;
- }
- if ( pEnd <= pMode ) {
- //
- // Mode not found
- //
- DEBUG (( DEBUG_ERROR | DEBUG_RX,
- "ERROR - File mode not found\r\n" ));
- //
- // Tell the client of the error
- //
- TftpSendError ( pTftpServer,
- pContext,
- 0,
- (UINT8 *)"File open mode not found" );
- break;
- }
- pMode += 1;
- DEBUG (( DEBUG_TFTP_REQUEST,
- "TFTP - FileName: %a\n",
- pFileName ));
-
- //
- // Locate the options
- //
- pOption = pMode;
- while (( pEnd > pOption ) && ( 0 != *pOption )) {
- pOption += 1;
- }
- if ( pEnd <= pOption ) {
- //
- // End of mode not found
- //
- DEBUG (( DEBUG_ERROR | DEBUG_RX,
- "ERROR - File mode not valid\r\n" ));
- //
- // Tell the client of the error
- //
- TftpSendError ( pTftpServer,
- pContext,
- 0,
- (UINT8 *)"File open mode not valid" );
- break;
- }
- pOption += 1;
- DEBUG (( DEBUG_TFTP_REQUEST,
- "TFTP - Mode: %a\r\n",
- pMode ));
-
- //
- // Verify the mode is supported
- //
- if ( 0 != stricmp ((char *)pMode, "octet" )) {
- //
- // File access mode not supported
- //
- DEBUG (( DEBUG_ERROR | DEBUG_TFTP_REQUEST,
- "ERROR - File mode %a not supported\r\n",
- pMode ));
-
- //
- // Tell the client of the error
- //
- TftpSendError ( pTftpServer,
- pContext,
- 0,
- (UINT8 *)"File open mode not supported" );
- break;
- }
-
- //
- // Open the file, close the context on error
- //
-// TODO: Remove the following line
-pContext->File = (EFI_HANDLE)1;
-
- //
- // Determine the file length
- //
-//fstat
-
- //
- // Process the options
- //
- TftpOptions ( pContext, pOption, pEnd );
-
- //
- // Read in the first portion of the file
- //
-
- //
- // Send the first block
- //
- pContext->bExpectAck = TRUE;
- if ( 2 < pContext->TxBytes ) {
- //
- // Send the OACK
- //
- Status = TftpTxPacket ( pTftpServer, pContext );
+ if ( AF_INET == pTftpServer->RemoteAddress.v4.sin_family ) {
+ DEBUG (( DEBUG_ERROR,
+ "ERROR - File not open for %d.%d.%d.%d:%d\r\n",
+ (UINT8)pTftpServer->RemoteAddress.v4.sin_addr.s_addr,
+ (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 8 ),
+ (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 16 ),
+ (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 24 ),
+ htons ( pTftpServer->RemoteAddress.v4.sin_port )));
}
else {
- //
- // Send the first block of data
- //
- Status = TftpSendNextBlock ( pTftpServer, pContext );
+ DEBUG (( DEBUG_ERROR,
+ "ERROR - File not open for [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n",
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 0 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 1 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 2 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 3 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 4 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 5 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 6 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 7 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 8 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 9 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 10 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 11 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 12 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 13 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 14 ],
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 15 ],
+ htons ( pTftpServer->RemoteAddress.v6.sin6_port )));
}
- break;
-
- case TFTP_OP_ACK:
- //
- // Get the block number that is being ACKed
- //
- BlockNumber = pTftpServer->RxBuffer[2];
- BlockNumber <<= 8;
- BlockNumber |= pTftpServer->RxBuffer[3];
-
- //
- // Determine if this is the correct ACK
- //
- DEBUG (( DEBUG_TFTP_ACK,
- "ACK for block 0x%04x received\r\n",
- BlockNumber ));
- if (( !pContext->bExpectAck )
- || ( BlockNumber != pContext->AckNext )) {
- DEBUG (( DEBUG_WARN | DEBUG_TFTP_ACK,
- "WARNING - Expecting ACK 0x%0x4 not received ACK 0x%08x\r\n",
- pContext->AckNext,
- BlockNumber ));
- }
- else {
- //
- // Process the expected ACK
- //
- if ( pContext->bEofSent ) {
- bCloseContext = TRUE;
- }
- else {
- //
- // Set the next expected ACK
- //
- pContext->AckNext += 1;
-
- //
- // Send the next packet of data
- //
- Status = TftpSendNextBlock ( pTftpServer, pContext );
- }
- }
- break;
}
+ break;
}
//
@@ -1089,119 +1649,229 @@ pContext->File = (EFI_HANDLE)1;
/**
- Build and send an error packet
+ Process the read request
- @param [in] pTftpServer The TFTP server control structure address.
- @param [in] pContext The context structure address.
- @param [in] Error Error number for the packet
- @param [in] pError Zero terminated error string address
+ @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure
+ @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure
+ @param [in] SocketFd Socket file descriptor
- @retval EFI_SUCCESS Message processed successfully
+ @retval TRUE if the context should be closed
**/
-EFI_STATUS
-TftpSendError (
+BOOLEAN
+TftpRead (
IN TSDT_TFTP_SERVER * pTftpServer,
IN TSDT_CONNECTION_CONTEXT * pContext,
- IN UINT16 Error,
- IN UINT8 * pError
+ IN int SocketFd
)
{
- UINT8 Character;
+ BOOLEAN bCloseContext;
+ struct stat FileStatus;
UINT8 * pBuffer;
- EFI_STATUS Status;
+ UINT8 * pEnd;
+ UINT8 * pFileName;
+ UINT8 * pMode;
+ UINT8 * pOption;
+ CHAR8 * pReadMode;
+ UINT64 TimeStart;
DBG_ENTER ( );
//
- // Build the error packet
+ // Log the receive time
//
- pBuffer = &pContext->TxBuffer[0];
- pBuffer[0] = 0;
- pBuffer[1] = TFTP_OP_ERROR;
- pBuffer[2] = (UINT8)( Error >> 8 );
- pBuffer[3] = (UINT8)Error;
+ TimeStart = 0;
+ if ( PcdGetBool ( Tftp_Bandwidth )) {
+ TimeStart = GetPerformanceCounter ( );
+ }
//
- // Copy the zero terminated string into the buffer
+ // Close the context if necessary
//
- pBuffer += 4;
- do {
- Character = *pError++;
- *pBuffer++ = Character;
- } while ( 0 != Character );
+ bCloseContext = FALSE;
+ if ( NULL != pContext ) {
+ ContextRemove ( pTftpServer, pContext );
+ }
//
- // Send the error message
+ // Use break instead of goto
//
- pContext->TxBytes = pBuffer - &pContext->TxBuffer[0];
- Status = TftpTxPacket ( pTftpServer, pContext );
+ for ( ; ; ) {
+ //
+ // Create the connection context
+ //
+ pContext = ContextAdd ( pTftpServer, SocketFd );
+ if ( NULL == pContext ) {
+ break;
+ }
- //
- // Return the operation status
- //
- DBG_EXIT_STATUS ( Status );
- return Status;
-}
+ //
+ // Set the start time
+ //
+ if ( PcdGetBool ( Tftp_Bandwidth )) {
+ pContext->TimeStart = TimeStart;
+ }
+ //
+ // Locate the mode
+ //
+ pBuffer = &pTftpServer->RxBuffer[ 0 ];
+ pEnd = &pBuffer[ pTftpServer->RxBytes ];
+ pFileName = &pBuffer[ 2 ];
+ pMode = pFileName;
+ while (( pEnd > pMode ) && ( 0 != *pMode )) {
+ pMode += 1;
+ }
+ if ( pEnd <= pMode ) {
+ //
+ // Mode not found
+ //
+ DEBUG (( DEBUG_ERROR | DEBUG_RX,
+ "ERROR - File mode not found\r\n" ));
+ //
+ // Tell the client of the error
+ //
+ SendError ( pContext,
+ TFTP_ERROR_SEE_MSG,
+ (UINT8 *)"File open mode not found" );
+ break;
+ }
+ pMode += 1;
+ DEBUG (( DEBUG_TFTP_REQUEST,
+ "TFTP - FileName: %a\r\n",
+ pFileName ));
-/**
- Send the next block of file system data
+ //
+ // Locate the options
+ //
+ pOption = pMode;
+ while (( pEnd > pOption ) && ( 0 != *pOption )) {
+ pOption += 1;
+ }
+ if ( pEnd <= pOption ) {
+ //
+ // End of mode not found
+ //
+ DEBUG (( DEBUG_ERROR | DEBUG_RX,
+ "ERROR - File mode not valid\r\n" ));
+ //
+ // Tell the client of the error
+ //
+ SendError ( pContext,
+ TFTP_ERROR_SEE_MSG,
+ (UINT8 *)"File open mode not valid" );
+ break;
+ }
+ pOption += 1;
+ DEBUG (( DEBUG_TFTP_REQUEST,
+ "TFTP - Mode: %a\r\n",
+ pMode ));
- @param [in] pTftpServer The TFTP server control structure address.
- @param [in] pContext The context structure address.
+ //
+ // Verify the mode is supported
+ //
+ pReadMode = "r";
+ if ( 0 == strcasecmp ((char *)pMode, "octet" )) {
+ //
+ // Read the file as binary input
+ //
+ pReadMode = "rb";
+ }
- @retval EFI_SUCCESS Message processed successfully
+ //
+ // Determine the file length
+ //
+ pContext->File = fopen ( pFileName, pReadMode );
+ if (( NULL == pContext->File )
+ || ( -1 == stat ( pFileName, &FileStatus ))) {
+ //
+ // File not found
+ //
+ DEBUG (( DEBUG_ERROR | DEBUG_TFTP_REQUEST,
+ ( NULL == pContext->File )
+ ? "ERROR - File not found!\r\n"
+ : "ERROR - Unable to determine file %a size!\r\n",
+ pFileName ));
-**/
-EFI_STATUS
-TftpSendNextBlock (
- IN TSDT_TFTP_SERVER * pTftpServer,
- IN TSDT_CONNECTION_CONTEXT * pContext
- )
-{
- ssize_t LengthInBytes;
- UINT8 * pBuffer;
- EFI_STATUS Status;
+ //
+ // Tell the client of the error
+ //
+ SendError ( pContext,
+ TFTP_ERROR_NOT_FOUND,
+ (UINT8 *)"File not found" );
+ break;
+ }
+ pContext->LengthInBytes = FileStatus.st_size;
+ pContext->BytesRemaining = pContext->LengthInBytes;
+ pContext->BytesToSend = pContext->LengthInBytes;
- //
- // Determine how much data needs to be sent
- //
- LengthInBytes = pContext->BlockSize;
- if (( pContext->LengthInBytes < TFTP_MAX_BLOCK_SIZE )
- || ( LengthInBytes > (ssize_t)pContext->LengthInBytes )) {
- LengthInBytes = (ssize_t)pContext->LengthInBytes;
- pContext->bEofSent = TRUE;
- }
+ //
+ // Display the file size
+ //
+ DEBUG_CODE_BEGIN ( );
+ UINT32 Value;
+
+ if ( 1024 > pContext->LengthInBytes ) {
+ Value = (UINT32)pContext->LengthInBytes;
+ DEBUG (( DEBUG_FILE_BUFFER,
+ "%a size: %d Bytes\r\n",
+ pFileName,
+ Value ));
+ }
+ else if (( 1024 * 1024 ) > pContext->LengthInBytes ) {
+ Value = (UINT32)pContext->LengthInBytes;
+ DEBUG (( DEBUG_FILE_BUFFER,
+ "%a size: %d.%03d KiBytes (%Ld Bytes)\r\n",
+ pFileName,
+ Value / 1024,
+ (( Value % 1024 ) * 1000 ) / 1024,
+ pContext->LengthInBytes ));
+ }
+ else if (( 1024 * 1024 * 1024 ) > pContext->LengthInBytes ) {
+ Value = (UINT32)DivU64x32 ( pContext->LengthInBytes, 1024 );
+ DEBUG (( DEBUG_FILE_BUFFER,
+ "%a size: %d.%03d MiBytes (%Ld Bytes)\r\n",
+ pFileName,
+ Value / 1024,
+ (( Value % 1024 ) * 1000 ) / 1024,
+ pContext->LengthInBytes ));
+ }
+ else {
+ Value = (UINT32)DivU64x32 ( pContext->LengthInBytes, 1024 * 1024 );
+ DEBUG (( DEBUG_FILE_BUFFER,
+ "%a size: %d.%03d GiBytes (%Ld Bytes)\r\n",
+ pFileName,
+ Value / 1024,
+ (( Value % 1024 ) * 1000 ) / 1024,
+ pContext->LengthInBytes ));
+ }
+ DEBUG_CODE_END ( );
- //
- // Set the TFTP opcode and block number
- //
- pBuffer = &pContext->TxBuffer[0];
- *pBuffer++ = 0;
- *pBuffer++ = TFTP_OP_DATA;
- *pBuffer++ = (UINT8)( pContext->AckNext >> 8 );
- *pBuffer++ = (UINT8)pContext->AckNext;
+ //
+ // Process the options
+ //
+ if ( pEnd > pOption ) {
+ TftpOptions ( pContext, pOption, pEnd );
+ }
+ else {
+ //
+ // Skip the open ACK
+ //
+ pContext->BlockNumber = 1;
+ }
- //
- // Copy the file data into the transmit buffer
- //
- pContext->TxBytes = 2 + 2 + LengthInBytes;
- if ( 0 < LengthInBytes ) {
- CopyMem ( &pBuffer,
- pContext->pBuffer,
- LengthInBytes );
+ //
+ // Send the first packet (OACK or data block)
+ //
+ bCloseContext = PacketFill ( pContext );
+ break;
}
//
- // Send the next block
+ // Return the close status
//
- Status = TftpTxPacket ( pTftpServer, pContext );
-
- //
- // Return the operation status
- //
- return Status;
+ DBG_EXIT ( );
+ return bCloseContext;
}
@@ -1213,241 +1883,314 @@ TftpSendNextBlock (
some time to get the IP address and initialize the upper layers of
the network stack.
- @param [in] pTftpServer The TFTP server control structure address.
+ @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure
+ @param [in] AddressFamily The address family to use for the conection.
+ @param [in] pIndex Address of the index into the port array
**/
VOID
-TftpServerTimer (
- IN TSDT_TFTP_SERVER * pTftpServer
+TftpServerSocket (
+ IN TSDT_TFTP_SERVER * pTftpServer,
+ IN sa_family_t AddressFamily,
+ IN int * pIndex
)
{
- UINT16 TftpPort;
int SocketStatus;
- EFI_STATUS Status;
+ struct pollfd * pTftpPort;
+ UINT16 TftpPort;
+ union {
+ struct sockaddr_in v4;
+ struct sockaddr_in6 v6;
+ } TftpServerAddress;
- DEBUG (( DEBUG_SERVER_TIMER, "Entering TftpServerTimer\r\n" ));
+ DEBUG (( DEBUG_SERVER_TIMER, "Entering TftpServerListen\r\n" ));
//
- // Open the TFTP port on the server
+ // Determine if the socket is already initialized
//
- do {
- do {
- //
- // Wait for a while
- //
- Status = gBS->CheckEvent ( pTftpServer->TimerEvent );
- } while ( EFI_SUCCESS != Status );
-
+ if ( -1 == *pIndex ) {
//
// Attempt to create the socket for the TFTP server
//
- pTftpServer->TftpPort.events = POLLRDNORM | POLLHUP;
- pTftpServer->TftpPort.revents = 0;
- pTftpServer->TftpPort.fd = socket ( AF_INET,
- SOCK_DGRAM,
- IPPROTO_UDP );
- if ( -1 != pTftpServer->TftpPort.fd ) {
+ pTftpPort = &pTftpServer->TftpPort[ pTftpServer->Entries ];
+ pTftpPort->fd = socket ( AddressFamily,
+ SOCK_DGRAM,
+ IPPROTO_UDP );
+ if ( -1 != pTftpPort->fd ) {
+ //
+ // Initialize the poll structure
+ //
+ pTftpPort->events = POLLRDNORM | POLLHUP;
+ pTftpPort->revents = 0;
+
//
// Set the socket address
//
- ZeroMem ( &pTftpServer->TftpServerAddress,
- sizeof ( pTftpServer->TftpServerAddress ));
TftpPort = 69;
- DEBUG (( DEBUG_TFTP_PORT,
- "TFTP Port: %d\r\n",
- TftpPort ));
- pTftpServer->TftpServerAddress.sin_len = sizeof ( pTftpServer->TftpServerAddress );
- pTftpServer->TftpServerAddress.sin_family = AF_INET;
- pTftpServer->TftpServerAddress.sin_addr.s_addr = INADDR_ANY;
- pTftpServer->TftpServerAddress.sin_port = htons ( TftpPort );
+ ZeroMem ( &TftpServerAddress, sizeof ( TftpServerAddress ));
+ TftpServerAddress.v4.sin_port = htons ( TftpPort );
+ if ( AF_INET == AddressFamily ) {
+ TftpServerAddress.v4.sin_len = sizeof ( TftpServerAddress.v4 );
+ TftpServerAddress.v4.sin_family = AF_INET;
+ }
+ else {
+ TftpServerAddress.v6.sin6_len = sizeof ( TftpServerAddress.v6 );
+ TftpServerAddress.v6.sin6_family = AF_INET6;
+ }
//
// Bind the socket to the TFTP port
//
- SocketStatus = bind ( pTftpServer->TftpPort.fd,
- (struct sockaddr *) &pTftpServer->TftpServerAddress,
- pTftpServer->TftpServerAddress.sin_len );
+ SocketStatus = bind ( pTftpPort->fd,
+ (struct sockaddr *) &TftpServerAddress,
+ TftpServerAddress.v6.sin6_len );
if ( -1 != SocketStatus ) {
DEBUG (( DEBUG_TFTP_PORT,
"0x%08x: Socket bound to port %d\r\n",
- pTftpServer->TftpPort.fd,
+ pTftpPort->fd,
TftpPort ));
+
+ //
+ // Account for this connection
+ //
+ *pIndex = pTftpServer->Entries;
+ pTftpServer->Entries += 1;
+ ASSERT ( DIM ( pTftpServer->TftpPort ) >= pTftpServer->Entries );
}
//
// Release the socket if necessary
//
if ( -1 == SocketStatus ) {
- close ( pTftpServer->TftpPort.fd );
- pTftpServer->TftpPort.fd = -1;
+ close ( pTftpPort->fd );
+ pTftpPort->fd = -1;
}
}
+ }
- //
- // Wait until the socket is open
- //
- }while ( -1 == pTftpServer->TftpPort.fd );
-
- DEBUG (( DEBUG_SERVER_TIMER, "Exiting TftpServerTimer\r\n" ));
+ DEBUG (( DEBUG_SERVER_TIMER, "Exiting TftpServerListen\r\n" ));
}
/**
- Start the TFTP server port creation timer
-
- @param [in] pTftpServer The TFTP server control structure address.
+ Update the window due to the ACK
- @retval EFI_SUCCESS The timer was successfully started.
- @retval EFI_ALREADY_STARTED The timer is already running.
- @retval Other The timer failed to start.
+ @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure
+ @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure
+ @param [in] pPacket Address of a ::TFTP_PACKET structure
**/
-EFI_STATUS
-TftpServerTimerStart (
- IN TSDT_TFTP_SERVER * pTftpServer
+VOID
+WindowAck (
+ IN TSDT_TFTP_SERVER * pTftpServer,
+ IN TSDT_CONNECTION_CONTEXT * pContext,
+ IN TFTP_PACKET * pPacket
)
{
- EFI_STATUS Status;
- UINT64 TriggerTime;
+ if ( PcdGetBool ( Tftp_HighSpeed )) {
+ UINT64 DeltaTime;
+ UINT64 NanoSeconds;
- DBG_ENTER ( );
+ DBG_ENTER ( );
- //
- // Assume the timer is already running
- //
- Status = EFI_ALREADY_STARTED;
- if ( !pTftpServer->bTimerRunning ) {
//
- // Compute the poll interval
+ // Compute the round trip time
//
- TriggerTime = TFTP_PORT_POLL_DELAY * ( 1000 * 10 );
- Status = gBS->SetTimer ( pTftpServer->TimerEvent,
- TimerPeriodic,
- TriggerTime );
- if ( !EFI_ERROR ( Status )) {
- DEBUG (( DEBUG_TFTP_PORT, "TFTP port timer started\r\n" ));
-
- //
- // Mark the timer running
- //
- pTftpServer->bTimerRunning = TRUE;
+ if ( pTftpServer->Time2 > pTftpServer->Time1 ) {
+ DeltaTime = pTftpServer->RxTime - pPacket->TxTime;
}
else {
- DEBUG (( DEBUG_ERROR | DEBUG_TFTP_PORT,
- "ERROR - Failed to start TFTP port timer, Status: %r\r\n",
- Status ));
+ DeltaTime = pPacket->TxTime - pTftpServer->RxTime;
}
- }
- //
- // Return the operation status
- //
- DBG_EXIT_STATUS ( Status );
- return Status;
+ //
+ // Adjust the round trip time
+ //
+ NanoSeconds = GetTimeInNanoSecond ( DeltaTime );
+ DeltaTime = RShiftU64 ( pContext->Rtt2x, ACK_SHIFT );
+ pContext->Rtt2x += NanoSeconds + NanoSeconds - DeltaTime;
+ if ( pContext->Rtt2x > pContext->MaxTimeout ) {
+ pContext->Rtt2x = pContext->MaxTimeout;
+ }
+
+ //
+ // Account for the ACK
+ //
+ if ( pContext->WindowSize < MAX_PACKETS ) {
+ pContext->AckCount -= 1;
+ if ( 0 == pContext->AckCount ) {
+ //
+ // Increase the window
+ //
+ pContext->WindowSize += 1;
+
+ //
+ // Set the ACK count
+ //
+ if ( pContext->WindowSize < pContext->Threshold ) {
+ pContext->AckCount = pContext->WindowSize * PcdGet32 ( Tftp_AckMultiplier );
+ }
+ else {
+ pContext->AckCount = PcdGet32 ( Tftp_AckLogBase ) << pContext->WindowSize;
+ }
+
+ //
+ // Display the round trip time
+ //
+ DEBUG_CODE_BEGIN ( );
+ UINT32 Value;
+
+ DeltaTime = RShiftU64 ( pContext->Rtt2x, 1 );
+ if ( 1000 > DeltaTime ) {
+ DEBUG (( DEBUG_WINDOW,
+ "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %Ld nSec\r\n",
+ pContext->WindowSize,
+ pContext->Threshold,
+ pContext->AckCount,
+ DeltaTime ));
+ }
+ else if (( 1000 * 1000 ) > DeltaTime ) {
+ Value = (UINT32)DeltaTime;
+ DEBUG (( DEBUG_WINDOW,
+ "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %d.%03d uSec\r\n",
+ pContext->WindowSize,
+ pContext->Threshold,
+ pContext->AckCount,
+ Value / 1000,
+ Value % 1000 ));
+ }
+ else if (( 1000 * 1000 * 1000 ) > DeltaTime ) {
+ Value = (UINT32)DivU64x32 ( DeltaTime, 1000 );
+ DEBUG (( DEBUG_WINDOW,
+ "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %d.%03d mSec\r\n",
+ pContext->WindowSize,
+ pContext->Threshold,
+ pContext->AckCount,
+ Value / 1000,
+ Value % 1000 ));
+ }
+ else {
+ Value = (UINT32)DivU64x32 ( DeltaTime, 1000 * 1000 );
+ DEBUG (( DEBUG_WINDOW,
+ "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %d.%03d Sec\r\n",
+ pContext->WindowSize,
+ pContext->Threshold,
+ pContext->AckCount,
+ Value / 1000,
+ Value % 1000 ));
+ }
+ DEBUG_CODE_END ( );
+ }
+ }
+
+ DBG_EXIT ( );
+ }
}
/**
- Stop the TFTP server port creation timer
-
- @param [in] pTftpServer The TFTP server control structure address.
+ A timeout has occurred, close the window
- @retval EFI_SUCCESS The TFTP port timer is stopped
- @retval Other Failed to stop the TFTP port timer
+ @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure
**/
-EFI_STATUS
-TftpServerTimerStop (
- IN TSDT_TFTP_SERVER * pTftpServer
+VOID
+WindowTimeout (
+ IN TSDT_CONNECTION_CONTEXT * pContext
)
{
- EFI_STATUS Status;
+ if ( PcdGetBool ( Tftp_HighSpeed )) {
+ TFTP_PACKET * pPacket;
- DBG_ENTER ( );
+ DBG_ENTER ( );
- //
- // Assume the timer is stopped
- //
- Status = EFI_SUCCESS;
- if ( pTftpServer->bTimerRunning ) {
//
- // Stop the port creation polling
+ // Set the threshold at half the previous window size
//
- Status = gBS->SetTimer ( pTftpServer->TimerEvent,
- TimerCancel,
- 0 );
- if ( !EFI_ERROR ( Status )) {
- DEBUG (( DEBUG_TFTP_PORT, "TFT[ port timer stopped\r\n" ));
+ pContext->Threshold = ( pContext->WindowSize + 1 ) >> 1;
- //
- // Mark the timer stopped
- //
- pTftpServer->bTimerRunning = FALSE;
+ //
+ // Close the transmit window
+ //
+ pContext->WindowSize = 1;
+ pContext->PacketsInWindow = 0;
+
+ //
+ // Double the round trip time
+ //
+ pContext->Rtt2x = LShiftU64 ( pContext->Rtt2x, 1 );
+ if ( pContext->Rtt2x > pContext->MaxTimeout ) {
+ pContext->Rtt2x = pContext->MaxTimeout;
+ }
+
+ //
+ // Set the ACK count
+ //
+ if ( pContext->WindowSize < pContext->Threshold ) {
+ pContext->AckCount = pContext->WindowSize * PcdGet32 ( Tftp_AckMultiplier );
}
else {
- DEBUG (( DEBUG_ERROR | DEBUG_TFTP_PORT,
- "ERROR - Failed to stop TFT[ port timer, Status: %r\r\n",
- Status ));
+ pContext->AckCount = PcdGet32 ( Tftp_AckLogBase ) << pContext->WindowSize;
}
- }
- //
- // Return the operation status
- //
- DBG_EXIT_STATUS ( Status );
- return Status;
-}
-
-/**
- Send the next TFTP packet
-
- @param [in] pTftpServer The TFTP server control structure address.
- @param [in] pContext The context structure address.
-
- @retval EFI_SUCCESS Message processed successfully
-
-**/
-EFI_STATUS
-TftpTxPacket (
- IN TSDT_TFTP_SERVER * pTftpServer,
- IN TSDT_CONNECTION_CONTEXT * pContext
- )
-{
- ssize_t LengthInBytes;
- EFI_STATUS Status;
-
- DBG_ENTER ( );
-
- //
- // Assume success
- //
- Status = EFI_SUCCESS;
+ //
+ // Display the round trip time
+ //
+ DEBUG_CODE_BEGIN ( );
+ UINT64 DeltaTime;
+ UINT32 Value;
+
+ DeltaTime = RShiftU64 ( pContext->Rtt2x, 1 );
+ if ( 1000 > DeltaTime ) {
+ DEBUG (( DEBUG_WINDOW,
+ "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %Ld nSec\r\n",
+ pContext->WindowSize,
+ pContext->Threshold,
+ pContext->AckCount,
+ DeltaTime ));
+ }
+ else if (( 1000 * 1000 ) > DeltaTime ) {
+ Value = (UINT32)DeltaTime;
+ DEBUG (( DEBUG_WINDOW,
+ "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %d.%03d uSec\r\n",
+ pContext->WindowSize,
+ pContext->Threshold,
+ pContext->AckCount,
+ Value / 1000,
+ Value % 1000 ));
+ }
+ else if (( 1000 * 1000 * 1000 ) > DeltaTime ) {
+ Value = (UINT32)DivU64x32 ( DeltaTime, 1000 );
+ DEBUG (( DEBUG_WINDOW,
+ "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %d.%03d mSec\r\n",
+ pContext->WindowSize,
+ pContext->Threshold,
+ pContext->AckCount,
+ Value / 1000,
+ Value % 1000 ));
+ }
+ else {
+ Value = (UINT32)DivU64x32 ( DeltaTime, 1000 * 1000 );
+ DEBUG (( DEBUG_WINDOW,
+ "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %d.%03d Sec\r\n",
+ pContext->WindowSize,
+ pContext->Threshold,
+ pContext->AckCount,
+ Value / 1000,
+ Value % 1000 ));
+ }
+ DEBUG_CODE_END ( );
- //
- // Send the TFTP packet
- //
- DEBUG (( DEBUG_TX,
- "0x%08x: pContext sending 0x%08x bytes\r\n",
- pContext,
- pContext->TxBytes ));
- LengthInBytes = sendto ( pTftpServer->TftpPort.fd,
- &pContext->TxBuffer[0],
- pContext->TxBytes,
- 0,
- (struct sockaddr *)&pContext->RemoteAddress,
- pContext->RemoteAddress.sin_len );
- if ( -1 == LengthInBytes ) {
- DEBUG (( DEBUG_ERROR | DEBUG_TX,
- "ERROR - Transmit failure, errno: 0x%08x\r\n",
- errno ));
- Status = EFI_DEVICE_ERROR;
+ //
+ // Retransmit the first packet in the window
+ //
+ pPacket = pContext->pTxHead;
+ if ( NULL != pPacket ) {
+ PacketTx ( pContext, pPacket );
+ }
+
+ DBG_EXIT ( );
}
-
- //
- // Return the operation status
- //
- DBG_EXIT_STATUS ( Status );
- return Status;
}
@@ -1466,25 +2209,46 @@ main (
IN char **Argv
)
{
+ UINTN Index;
TSDT_TFTP_SERVER * pTftpServer;
EFI_STATUS Status;
+ UINT64 TriggerTime;
//
- // Create a timer event to start TFTP port
+ // Get the performance counter characteristics
//
pTftpServer = &mTftpServer;
+ if ( PcdGetBool ( Tftp_HighSpeed )
+ || PcdGetBool ( Tftp_Bandwidth )) {
+ pTftpServer->ClockFrequency = GetPerformanceCounterProperties ( &pTftpServer->Time1,
+ &pTftpServer->Time2 );
+ }
+
+ //
+ // Create a timer event to start TFTP port
+ //
Status = gBS->CreateEvent ( EVT_TIMER,
TPL_TFTP_SERVER,
NULL,
NULL,
&pTftpServer->TimerEvent );
if ( !EFI_ERROR ( Status )) {
- Status = TftpServerTimerStart ( pTftpServer );
+ //
+ // Compute the poll interval
+ //
+ TriggerTime = TFTP_PORT_POLL_DELAY * ( 1000 * 10 );
+ Status = gBS->SetTimer ( pTftpServer->TimerEvent,
+ TimerPeriodic,
+ TriggerTime );
if ( !EFI_ERROR ( Status )) {
+ DEBUG (( DEBUG_TFTP_PORT, "TFTP port timer started\r\n" ));
+
//
// Run the TFTP server forever
//
- for ( ; ; ) {
+ pTftpServer->Udpv4Index = -1;
+ pTftpServer->Udpv6Index = -1;
+ do {
//
// Poll the network layer to create the TFTP port
// for the tftp server. More than one attempt may
@@ -1492,28 +2256,107 @@ main (
// the IP address and initialize the upper layers
// of the network stack.
//
- TftpServerTimer ( pTftpServer );
+ if ( DIM ( pTftpServer->TftpPort ) != pTftpServer->Entries ) {
+ do {
+ //
+ // Wait a while before polling for a connection
+ //
+ if ( EFI_SUCCESS != gBS->CheckEvent ( pTftpServer->TimerEvent )) {
+ if ( 0 == pTftpServer->Entries ) {
+ break;
+ }
+ gBS->WaitForEvent ( 1, &pTftpServer->TimerEvent, &Index );
+ }
+
+ //
+ // Poll for a network connection
+ //
+ TftpServerSocket ( pTftpServer,
+ AF_INET,
+ &pTftpServer->Udpv4Index );
+ TftpServerSocket ( pTftpServer,
+ AF_INET6,
+ &pTftpServer->Udpv6Index );
+ } while ( 0 == pTftpServer->Entries );
+ }
//
// Poll the socket for activity
//
do {
SocketPoll ( pTftpServer );
- } while ( -1 != pTftpServer->TftpPort.fd );
-//
-// TODO: Remove the following test code
-// Exit when the network connection is broken
-//
-break;
- }
+ //
+ // Normal TFTP lets the client request the retransmit by
+ // sending another ACK for the previous packet
+ //
+ if ( PcdGetBool ( Tftp_HighSpeed )) {
+ UINT64 CurrentTime;
+ UINT64 ElapsedTime;
+ TSDT_CONNECTION_CONTEXT * pContext;
+ TFTP_PACKET * pPacket;
+
+ //
+ // High speed TFTP uses an agressive retransmit to
+ // get the TFTP client moving again when the ACK or
+ // previous data packet was lost.
+ //
+ // Get the current time
+ //
+ CurrentTime = GetPerformanceCounter ( );
+
+ //
+ // Walk the list of contexts
+ //
+ pContext = pTftpServer->pContextList;
+ while ( NULL != pContext )
+ {
+ //
+ // Check for a transmit timeout
+ //
+ pPacket = pContext->pTxHead;
+ if ( NULL != pPacket ) {
+ //
+ // Compute the elapsed time
+ //
+ if ( pTftpServer->Time2 > pTftpServer->Time1 ) {
+ ElapsedTime = CurrentTime - pPacket->TxTime;
+ }
+ else {
+ ElapsedTime = pPacket->TxTime - CurrentTime;
+ }
+ ElapsedTime = GetTimeInNanoSecond ( ElapsedTime );
+
+ //
+ // Determine if a retransmission is necessary
+ //
+ if ( ElapsedTime >= pContext->Rtt2x ) {
+ DEBUG (( DEBUG_WINDOW,
+ "0x%08x: Context TX timeout for packet 0x%08x, Window: %d\r\n",
+ pContext,
+ pPacket,
+ pContext->WindowSize ));
+ WindowTimeout ( pContext );
+ }
+ }
+
+ //
+ // Set the next context
+ //
+ pContext = pContext->pNext;
+ }
+ }
+ } while ( DIM ( pTftpServer->TftpPort ) == pTftpServer->Entries );
+ } while ( !mbTftpServerExit );
//
// Done with the timer event
//
- TftpServerTimerStop ( pTftpServer );
- Status = gBS->CloseEvent ( pTftpServer->TimerEvent );
+ gBS->SetTimer ( pTftpServer->TimerEvent,
+ TimerCancel,
+ 0 );
}
+ gBS->CloseEvent ( pTftpServer->TimerEvent );
}
//
diff --git a/AppPkg/Applications/Sockets/TftpServer/TftpServer.h b/AppPkg/Applications/Sockets/TftpServer/TftpServer.h
index b1e9f83ce5..79b64c2820 100644
--- a/AppPkg/Applications/Sockets/TftpServer/TftpServer.h
+++ b/AppPkg/Applications/Sockets/TftpServer/TftpServer.h
@@ -1,7 +1,7 @@
/** @file
Definitions for the TFTP server.
- Copyright (c) 2011, Intel Corporation
+ Copyright (c) 2011, 2012, 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
@@ -16,6 +16,7 @@
#define _TFTP_SERVER_H_
#include <errno.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Uefi.h>
@@ -24,29 +25,33 @@
#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
#include <Library/PcdLib.h>
+#include <Library/TimerLib.h>
#include <Library/UefiApplicationEntryPoint.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>
#include <Protocol/BlockIo.h>
#include <netinet/in.h>
+#include <netinet6/in6.h>
#include <sys/EfiSysCall.h>
#include <sys/poll.h>
#include <sys/socket.h>
+#include <sys/Stat.h>
//------------------------------------------------------------------------------
// Macros
//------------------------------------------------------------------------------
#if defined(_MSC_VER) /* Handle Microsoft VC++ compiler specifics. */
-#define DBG_ENTER() DEBUG (( DEBUG_INFO, "Entering " __FUNCTION__ "\n" )) ///< Display routine entry
-#define DBG_EXIT() DEBUG (( DEBUG_INFO, "Exiting " __FUNCTION__ "\n" )) ///< Display routine exit
-#define DBG_EXIT_DEC(Status) DEBUG (( DEBUG_INFO, "Exiting " __FUNCTION__ ", Status: %d\n", Status )) ///< Display routine exit with decimal value
-#define DBG_EXIT_HEX(Status) DEBUG (( DEBUG_INFO, "Exiting " __FUNCTION__ ", Status: 0x%08x\n", Status )) ///< Display routine exit with hex value
-#define DBG_EXIT_STATUS(Status) DEBUG (( DEBUG_INFO, "Exiting " __FUNCTION__ ", Status: %r\n", Status )) ///< Display routine exit with status value
-#define DBG_EXIT_TF(Status) DEBUG (( DEBUG_INFO, "Exiting " __FUNCTION__ ", returning %s\n", (FALSE == Status) ? L"FALSE" : L"TRUE" )) ///< Display routine with TRUE/FALSE value
+#define DBG_ENTER() DEBUG (( DEBUG_ENTER_EXIT, "Entering " __FUNCTION__ "\n" )) ///< Display routine entry
+#define DBG_EXIT() DEBUG (( DEBUG_ENTER_EXIT, "Exiting " __FUNCTION__ "\n" )) ///< Display routine exit
+#define DBG_EXIT_DEC(Status) DEBUG (( DEBUG_ENTER_EXIT, "Exiting " __FUNCTION__ ", Status: %d\n", Status )) ///< Display routine exit with decimal value
+#define DBG_EXIT_HEX(Status) DEBUG (( DEBUG_ENTER_EXIT, "Exiting " __FUNCTION__ ", Status: 0x%08x\n", Status )) ///< Display routine exit with hex value
+#define DBG_EXIT_STATUS(Status) DEBUG (( DEBUG_ENTER_EXIT, "Exiting " __FUNCTION__ ", Status: %r\n", Status )) ///< Display routine exit with status value
+#define DBG_EXIT_TF(Status) DEBUG (( DEBUG_ENTER_EXIT, "Exiting " __FUNCTION__ ", returning %s\n", (FALSE == Status) ? L"FALSE" : L"TRUE" )) ///< Display routine with TRUE/FALSE value
#else // _MSC_VER
#define DBG_ENTER()
#define DBG_EXIT()
@@ -62,14 +67,22 @@
// Constants
//------------------------------------------------------------------------------
-#define DEBUG_PORT_WORK 0x40000000 ///< Display the port work messages
-#define DEBUG_SERVER_TIMER 0x20000000 ///< Display the socket poll messages
-#define DEBUG_TFTP_PORT 0x10000000 ///< Display the TFTP port messages
-#define DEBUG_TFTP_REQUEST 0x08000000 ///< Display the TFTP request messages
-#define DEBUG_TX 0x04000000 ///< Display transmit messages
-#define DEBUG_SOCKET_POLL 0x02000000 ///< Display the socket poll messages
-#define DEBUG_RX 0x01000000 ///< Display receive messages
-#define DEBUG_TFTP_ACK 0x00800000 ///< Display the TFTP ACK messages
+#define ACK_SHIFT 4 ///< Number of samples in ACK average
+
+#define DEBUG_WINDOW 0x00000001 ///< Display the window messages
+#define DEBUG_TX_PACKET 0x00000002 ///< Display the transmit packet messages
+#define DEBUG_FILE_BUFFER 0x00000004 ///< Display the file buffer messages
+#define DEBUG_SERVER_TIMER 0x00000008 ///< Display the socket poll messages
+#define DEBUG_TFTP_REQUEST 0x00000010 ///< Display the TFTP request messages
+#define DEBUG_PORT_WORK 0x00000020 ///< Display the port work messages
+#define DEBUG_SOCKET_POLL 0x00000040 ///< Display the socket poll messages
+#define DEBUG_TFTP_PORT 0x00000080 ///< Display the TFTP port messages
+#define DEBUG_TX 0x00000100 ///< Display transmit messages
+#define DEBUG_RX 0x00000200 ///< Display receive messages
+#define DEBUG_TFTP_ACK 0x00000400 ///< Display the TFTP ACK messages
+#define DEBUG_ENTER_EXIT 0x00000800 ///< Display entry and exit messages
+
+#define MAX_PACKETS 8 ///< Maximum number of packets in the window
#define TFTP_PORT_POLL_DELAY ( 2 * 1000 ) ///< Delay in milliseconds for attempts to open the TFTP port
#define CLIENT_POLL_DELAY 50 ///< Delay in milliseconds between client polls
@@ -123,11 +136,33 @@
#define TFTP_MAX_BLOCK_SIZE 4096 ///< Maximum block size
+#define TFTP_ERROR_SEE_MSG 0 ///< See the error message
+#define TFTP_ERROR_NOT_FOUND 1 ///< File not found
+#define TFTP_ERROR_ACCESS_VIOLATION 2 ///< Access violation
+#define TFTP_ERROR_DISK_FULL 3 ///< Disk full
+#define TFTP_ERROR_ILLEGAL_OP 4 ///< Illegal operation
+#define TFTP_ERROR_UNKNOWN_XFER_ID 5 ///< Unknown transfer ID
+#define TFTP_ERROR_FILE_EXISTS 6 ///< File already exists
+#define TFTP_ERROR_NO_SUCH_USER 7 ///< No such user
+
//------------------------------------------------------------------------------
// Data Types
//------------------------------------------------------------------------------
/**
+ Packet structure
+**/
+typedef struct _TFTP_PACKET TFTP_PACKET;
+typedef struct _TFTP_PACKET {
+ TFTP_PACKET * pNext; ///< Next packet in list
+ UINT64 TxTime; ///< Time the transmit was performed
+ ssize_t TxBytes; ///< Bytes in the TX buffer
+ UINT32 RetryCount; ///< Number of transmissions
+ UINT16 BlockNumber; ///< Block number of this packet
+ UINT8 TxBuffer[ 2 + 2 + TFTP_MAX_BLOCK_SIZE ]; ///< Transmit buffer
+} GCC_TFTP_PACKET;
+
+/**
Port control structure
**/
typedef struct _TSDT_CONNECTION_CONTEXT TSDT_CONNECTION_CONTEXT;
@@ -135,33 +170,49 @@ typedef struct _TSDT_CONNECTION_CONTEXT {
//
// Remote connection management
//
- TSDT_CONNECTION_CONTEXT * pNext; ///< Next context in the connection list
- struct sockaddr_in RemoteAddress; ///< Remote address
-
- //
- // TFTP management parameters
- //
- UINT16 AckNext; ///< Next block to be received
- BOOLEAN bExpectAck; ///< TRUE for read, FALSE for write
- UINT32 BlockSize; ///< Negotiated block size
- UINT32 Timeout; ///< Number of seconds to wait before retransmission
+ TSDT_CONNECTION_CONTEXT * pNext; ///< Next context in the connection list
+ struct sockaddr_in6 RemoteAddress; ///< Remote address
+ int SocketFd; ///< Socket file descriptor
//
// File management parameters
//
- EFI_HANDLE File; ///< NULL while file is closed
+ FILE * File; ///< NULL while file is closed
UINT64 LengthInBytes; ///< Size of the file
- UINT64 MaxTransferSize; ///< Maximum transfer size
+ UINT64 BytesRemaining; ///< Number of bytes remaining to be sent
+ UINT64 BytesToSend; ///< Number of bytes to send
+ UINT64 ValidBytes; ///< Number of valid bytes in the buffer
BOOLEAN bEofSent; ///< End of file sent
+ UINT8 * pFill; ///< Next portion of the buffer to fill
UINT8 * pBuffer; ///< Pointer into the file data
UINT8 * pEnd; ///< End of the file data
- UINT8 FileData[ 64 * TFTP_MAX_BLOCK_SIZE ]; ///< File data to send
+ UINT8 FileData[ 2 * MAX_PACKETS * TFTP_MAX_BLOCK_SIZE ]; ///< File data to send
+ UINT64 TimeStart; ///< Start of file transfer
+
+ //
+ // TFTP management parameters
+ //
+ UINT16 BlockNumber; ///< Next block to be transmitted
+ UINT32 BlockSize; ///< Negotiated block size
+
+ //
+ // Window management
+ //
+ UINT32 AckCount; ///< Number of ACKs to receive before increasing the window
+ UINT32 PacketsInWindow; ///< Number of packets in the window
+ UINT32 Threshold; ///< Size of window when ACK count becomes logrithmic
+ UINT32 WindowSize; ///< Size of the transmit window
+ UINT64 MaxTimeout; ///< Maximum number of seconds to wait before retransmission
+ UINT64 Rtt2x; ///< Twice the average round trip time in nanoseconds
//
// Buffer management
//
- ssize_t TxBytes; ///< Bytes in the TX buffer
- UINT8 TxBuffer[ 2 + 2 + TFTP_MAX_BLOCK_SIZE ]; ///< Transmit buffer
+ TFTP_PACKET * pFreeList; ///< List of free packets
+ TFTP_PACKET * pTxHead; ///< First packet in the list of packets for transmission
+ TFTP_PACKET * pTxTail; ///< Last packet in the list of packets for transmission
+ TFTP_PACKET ErrorPacket; ///< Error packet
+ TFTP_PACKET Tx[ MAX_PACKETS ];///< Transmit packets
}GCC_TSDT_CONNECTION_CONTEXT;
/**
@@ -176,19 +227,31 @@ typedef struct {
EFI_HANDLE ImageHandle; ///< Image handle
//
+ // Performance management
+ //
+ UINT64 ClockFrequency; ///< Frequency of the clock
+ UINT64 Time1; ///< Clock value after rollover
+ UINT64 Time2; ///< Clock value before rollover
+ UINT64 RxTime; ///< Time when the packet was recevied
+
+ //
// TFTP port management
//
- BOOLEAN bTimerRunning; ///< Port creation timer status
EFI_EVENT TimerEvent; ///< Timer to open TFTP port
- struct pollfd TftpPort; ///< Poll descriptor for the TFTP port
- struct sockaddr_in TftpServerAddress; ///< Address of the local TFTP server
+ int Udpv4Index; ///< Entry for UDPv4
+ int Udpv6Index; ///< Entry for UDPv6
+ int Entries; ///< Number of TFTP ports
+ struct pollfd TftpPort [ 2 ]; ///< Poll descriptor for the TFTP ports (UDP4, UDP6)
//
// Request management
//
- struct sockaddr_in RemoteAddress; ///< Remote address
- ssize_t RxBytes; ///< Receive data length in bytes
- UINT8 RxBuffer[ 2 + 2 + TFTP_MAX_BLOCK_SIZE ]; ///< Receive buffer
+ union {
+ struct sockaddr_in v4; ///< UDP4 address
+ struct sockaddr_in6 v6; ///< UDP6 address
+ } RemoteAddress; ///< Remote address
+ ssize_t RxBytes; ///< Receive data length in bytes
+ UINT8 RxBuffer[ 2 + 2 + TFTP_MAX_BLOCK_SIZE ]; ///< Receive buffer
//
// Client port management
@@ -205,37 +268,77 @@ extern TSDT_TFTP_SERVER mTftpServer;
//------------------------------------------------------------------------------
/**
- Process the TFTP request
+ Queue data packets for transmission
- @param [in] pOption Address of the first zero terminated option string
- @param [in] pValue Address to receive the value
+ @param [in] pContext Connection context structure address
- @retval EFI_SUCCESS Option translated into a value
+ @retval TRUE if a read error occurred
**/
-EFI_STATUS
-TftpOptionValue (
- IN UINT8 * pOption,
- IN INT32 * pValue
+BOOLEAN
+PacketFill (
+ IN TSDT_CONNECTION_CONTEXT * pContext
);
/**
- Process the TFTP request
+ Free the packet
- @param [in] pTftpServer The TFTP server control structure address.
- @param [in] pContext Connection context structure address
+ @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure
+ @param [in] pPacket Address of a ::TFTP_PACKET structure
**/
VOID
-TftpProcessRequest (
- IN TSDT_TFTP_SERVER * pTftpServer,
+PacketFree(
+ IN TSDT_CONNECTION_CONTEXT * pContext,
+ IN TFTP_PACKET * pPacket
+ );
+
+/**
+ Get a packet for transmission
+
+ @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure
+
+ @retval Address of a ::TFTP_PACKET structure
+
+**/
+TFTP_PACKET *
+PacketGet (
IN TSDT_CONNECTION_CONTEXT * pContext
);
/**
+ Queue the packet for transmission
+
+ @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure
+ @param [in] pPacket Address of a ::TFTP_PACKET structure
+
+ @retval TRUE if a transmission error has occurred
+
+**/
+BOOLEAN
+PacketQueue (
+ IN TSDT_CONNECTION_CONTEXT * pContext,
+ IN TFTP_PACKET * pPacket
+ );
+
+/**
+ Transmit the packet
+
+ @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure
+ @param [in] pPacket Address of a ::TFTP_PACKET structure
+
+ @retval EFI_SUCCESS Message processed successfully
+
+**/
+EFI_STATUS
+PacketTx (
+ IN TSDT_CONNECTION_CONTEXT * pContext,
+ IN TFTP_PACKET * pPacket
+ );
+
+/**
Build and send an error packet
- @param [in] pTftpServer The TFTP server control structure address.
@param [in] pContext The context structure address.
@param [in] Error Error number for the packet
@param [in] pError Zero terminated error string address
@@ -244,85 +347,82 @@ TftpProcessRequest (
**/
EFI_STATUS
-TftpSendError (
- IN TSDT_TFTP_SERVER * pTftpServer,
+SendError (
IN TSDT_CONNECTION_CONTEXT * pContext,
IN UINT16 Error,
IN UINT8 * pError
);
/**
- Send the next block of file system data
+ Process the TFTP request
- @param [in] pTftpServer The TFTP server control structure address.
- @param [in] pContext The context structure address.
+ @param [in] pOption Address of the first zero terminated option string
+ @param [in] pValue Address to receive the value
- @retval EFI_SUCCESS Message processed successfully
+ @retval EFI_SUCCESS Option translated into a value
**/
EFI_STATUS
-TftpSendNextBlock (
- IN TSDT_TFTP_SERVER * pTftpServer,
- IN TSDT_CONNECTION_CONTEXT * pContext
+TftpOptionValue (
+ IN UINT8 * pOption,
+ IN INT32 * pValue
);
/**
- TFTP port creation timer routine
-
- This routine polls the socket layer waiting for the initial network connection
- which will enable the creation of the TFTP port. The socket layer will manage
- the coming and going of the network connections after that until the last network
- connection is broken.
+ Process the TFTP request
- @param [in] pTftpServer The TFTP server control structure address.
+ @param [in] pTftpServer The TFTP server control structure address.
+ @param [in] pContext Connection context structure address
+ @param [in] SocketFd Socket file descriptor
**/
VOID
-TftpServerTimer (
- IN TSDT_TFTP_SERVER * pTftpServer
+TftpProcessRequest (
+ IN TSDT_TFTP_SERVER * pTftpServer,
+ IN TSDT_CONNECTION_CONTEXT * pContext,
+ IN int SocketFd
);
/**
- Start the TFTP server port creation timer
+ Process the read request
- @param [in] pTftpServer The TFTP server control structure address.
+ @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure
+ @param [in] pContext Connection context structure address
+ @param [in] SocketFd Socket file descriptor
- @retval EFI_SUCCESS The timer was successfully started.
- @retval EFI_ALREADY_STARTED The timer is already running.
- @retval Other The timer failed to start.
+ @retval TRUE if the context should be closed
**/
-EFI_STATUS
-TftpServerTimerStart (
- IN TSDT_TFTP_SERVER * pTftpServer
+BOOLEAN
+TftpRead (
+ IN TSDT_TFTP_SERVER * pTftpServer,
+ IN TSDT_CONNECTION_CONTEXT * pContext,
+ IN int SocketFd
);
/**
- Stop the TFTP server port creation timer
-
- @param [in] pTftpServer The TFTP server control structure address.
+ Update the window due to the ACK
- @retval EFI_SUCCESS The TFTP port timer is stopped
- @retval Other Failed to stop the TFTP port timer
+ @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure
+ @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure
+ @param [in] pPacket Address of a ::TFTP_PACKET structure
**/
-EFI_STATUS
-TftpServerTimerStop (
- IN TSDT_TFTP_SERVER * pTftpServer
+VOID
+WindowAck (
+ IN TSDT_TFTP_SERVER * pTftpServer,
+ IN TSDT_CONNECTION_CONTEXT * pContext,
+ IN TFTP_PACKET * pPacket
);
/**
- Send the next TFTP packet
-
- @param [in] pTftpServer The TFTP server control structure address.
- @param [in] pContext The context structure address.
+ A timeout has occurred, close the window
- @retval EFI_SUCCESS Message processed successfully
+ @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure
**/
-EFI_STATUS
-TftpTxPacket (
- IN TSDT_TFTP_SERVER * pTftpServer,
+VOID
+WindowTimeout (
IN TSDT_CONNECTION_CONTEXT * pContext
);
diff --git a/AppPkg/Applications/Sockets/TftpServer/TftpServer.inf b/AppPkg/Applications/Sockets/TftpServer/TftpServer.inf
index f48724289f..d7cbfaa303 100644
--- a/AppPkg/Applications/Sockets/TftpServer/TftpServer.inf
+++ b/AppPkg/Applications/Sockets/TftpServer/TftpServer.inf
@@ -8,7 +8,7 @@
# license agreement
#
#
-# Copyright (c) 20011 Intel Corporation. All rights reserved
+# Copyright (c) 2011 Intel Corporation. All rights reserved
# This software and associated documentation (if any) is furnished
# under a license and may only be used or copied in accordance
# with the terms of the license. Except as permitted by such
@@ -39,7 +39,17 @@
TftpServer.c
+[Pcd]
+ gAppPkgTokenSpaceGuid.Tftp_AckLogBase
+ gAppPkgTokenSpaceGuid.Tftp_AckMultiplier
+ gAppPkgTokenSpaceGuid.Tftp_Bandwidth
+ gAppPkgTokenSpaceGuid.Tftp_HighSpeed
+ gAppPkgTokenSpaceGuid.Tftp_MaxRetry
+ gAppPkgTokenSpaceGuid.Tftp_MaxTimeoutInSec
+
+
[Packages]
+ AppPkg/AppPkg.dec
MdePkg/MdePkg.dec
ShellPkg/ShellPkg.dec
StdLib/StdLib.dec
@@ -54,6 +64,7 @@
LibC
ShellLib
ShellCEntryLib
+ TimerLib
UefiBootServicesTableLib
UefiLib
UefiRuntimeServicesTableLib