2 This is a simple TFTP server application
4 Copyright (c) 2011, 2012, Intel Corporation. All rights reserved.
5 SPDX-License-Identifier: BSD-2-Clause-Patent
9 #include <TftpServer.h>
11 TSDT_TFTP_SERVER mTftpServer
; ///< TFTP server's control structure
12 volatile BOOLEAN mbTftpServerExit
; ///< Set TRUE to cause TFTP server to exit
16 Read file data into a buffer
18 @param [in] pContext Connection context structure address
20 @retval TRUE if a read error occurred
25 IN TSDT_CONNECTION_CONTEXT
* pContext
35 // Use break instead of goto
40 // Determine if there is any work to do
42 LengthInBytes
= DIM ( pContext
->FileData
) >> 1;
43 if (( pContext
->ValidBytes
> LengthInBytes
)
44 || ( 0 == pContext
->BytesRemaining
)) {
49 // Determine the number of bytes to read
51 if ( LengthInBytes
> pContext
->BytesRemaining
) {
52 LengthInBytes
= pContext
->BytesRemaining
;
56 // Read in the next portion of the file
58 BytesRead
= fread ( pContext
->pFill
,
60 (size_t)LengthInBytes
,
62 if ( -1 == BytesRead
) {
68 // Account for the file data read
70 pContext
->BytesRemaining
-= BytesRead
;
71 pContext
->ValidBytes
+= BytesRead
;
72 DEBUG (( DEBUG_FILE_BUFFER
,
73 "0x%08x: Buffer filled with %Ld bytes, %Ld bytes ramaining\r\n",
76 pContext
->BytesRemaining
));
79 // Set the next buffer location
81 pContext
->pFill
+= BytesRead
;
82 if ( pContext
->pEnd
<= pContext
->pFill
) {
83 pContext
->pFill
= &pContext
->FileData
[ 0 ];
87 // Verify that the end of the buffer is reached
89 ASSERT ( 0 == ( DIM ( pContext
->FileData
) & 1 ));
94 // Return the read status
102 Add a connection context to the list of connection contexts.
104 @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure
105 @param [in] SocketFd Socket file descriptor
107 @retval Context structure address, NULL if allocation fails
110 TSDT_CONNECTION_CONTEXT
*
112 IN TSDT_TFTP_SERVER
* pTftpServer
,
116 TSDT_CONNECTION_CONTEXT
* pContext
;
118 TFTP_PACKET
* pPacket
;
123 // Allocate a new context
125 pContext
= (TSDT_CONNECTION_CONTEXT
*)AllocateZeroPool ( sizeof ( *pContext
));
126 if ( NULL
!= pContext
) {
128 // Initialize the context
130 pContext
->SocketFd
= SocketFd
;
131 CopyMem ( &pContext
->RemoteAddress
,
132 &pTftpServer
->RemoteAddress
,
133 sizeof ( pContext
->RemoteAddress
));
134 pContext
->BlockSize
= 512;
139 pContext
->pFill
= &pContext
->FileData
[ 0 ];
140 pContext
->pEnd
= &pContext
->FileData
[ sizeof ( pContext
->FileData
)];
141 pContext
->pBuffer
= pContext
->pFill
;
146 pContext
->MaxTimeout
= MultU64x32 ( PcdGet32 ( Tftp_MaxTimeoutInSec
),
147 2 * 1000 * 1000 * 1000 );
148 pContext
->Rtt2x
= pContext
->MaxTimeout
;
149 pContext
->WindowSize
= MAX_PACKETS
;
150 WindowTimeout ( pContext
);
153 // Place the packets on the free list
155 pPacket
= &pContext
->Tx
[ 0 ];
156 pEnd
= &pPacket
[ DIM ( pContext
->Tx
)];
157 while ( pEnd
> pPacket
) {
158 PacketFree ( pContext
, pPacket
);
163 // Display the new context
165 if ( AF_INET
== pTftpServer
->RemoteAddress
.v4
.sin_family
) {
166 DEBUG (( DEBUG_PORT_WORK
,
167 "0x%08x: Context for %d.%d.%d.%d:%d\r\n",
169 (UINT8
)pTftpServer
->RemoteAddress
.v4
.sin_addr
.s_addr
,
170 (UINT8
)( pTftpServer
->RemoteAddress
.v4
.sin_addr
.s_addr
>> 8 ),
171 (UINT8
)( pTftpServer
->RemoteAddress
.v4
.sin_addr
.s_addr
>> 16 ),
172 (UINT8
)( pTftpServer
->RemoteAddress
.v4
.sin_addr
.s_addr
>> 24 ),
173 htons ( pTftpServer
->RemoteAddress
.v4
.sin_port
)));
176 DEBUG (( DEBUG_PORT_WORK
,
177 "0x%08x: Context for [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n",
179 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 0 ],
180 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 1 ],
181 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 2 ],
182 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 3 ],
183 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 4 ],
184 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 5 ],
185 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 6 ],
186 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 7 ],
187 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 8 ],
188 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 9 ],
189 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 10 ],
190 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 11 ],
191 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 12 ],
192 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 13 ],
193 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 14 ],
194 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 15 ],
195 htons ( pTftpServer
->RemoteAddress
.v6
.sin6_port
)));
199 // Add the context to the context list
201 pContext
->pNext
= pTftpServer
->pContextList
;
202 pTftpServer
->pContextList
= pContext
;
206 // Return the connection context
208 DBG_EXIT_STATUS ( pContext
);
214 Locate a remote connection context.
216 @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure
217 @param [in] pIpAddress The start of the remote IP address in network order
218 @param [in] Port The remote port number
220 @retval Context structure address, NULL if not found
223 TSDT_CONNECTION_CONTEXT
*
225 IN TSDT_TFTP_SERVER
* pTftpServer
228 TSDT_CONNECTION_CONTEXT
* pContext
;
233 // Walk the list of connection contexts
235 pContext
= pTftpServer
->pContextList
;
236 while ( NULL
!= pContext
) {
238 // Attempt to locate the remote network connection
240 if ( 0 == memcmp ( &pTftpServer
->RemoteAddress
,
241 &pContext
->RemoteAddress
,
242 pTftpServer
->RemoteAddress
.v6
.sin6_len
)) {
244 // The connection was found
246 DEBUG (( DEBUG_TFTP_REQUEST
,
247 "0x%08x: pContext found\r\n",
253 // Set the next context
255 pContext
= pContext
->pNext
;
259 // Return the connection context structure address
261 DBG_EXIT_HEX ( pContext
);
267 Remove a context from the list.
269 @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure
270 @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure
275 IN TSDT_TFTP_SERVER
* pTftpServer
,
276 IN TSDT_CONNECTION_CONTEXT
* pContext
279 TSDT_CONNECTION_CONTEXT
* pNextContext
;
280 TSDT_CONNECTION_CONTEXT
* pPreviousContext
;
285 // Attempt to locate the context in the list
287 pPreviousContext
= NULL
;
288 pNextContext
= pTftpServer
->pContextList
;
289 while ( NULL
!= pNextContext
) {
291 // Determine if the context was found
293 if ( pNextContext
== pContext
) {
295 // Remove the context from the list
297 if ( NULL
== pPreviousContext
) {
298 pTftpServer
->pContextList
= pContext
->pNext
;
301 pPreviousContext
->pNext
= pContext
->pNext
;
307 // Set the next context
309 pPreviousContext
= pNextContext
;
310 pNextContext
= pNextContext
->pNext
;
314 // Determine if the context was found
316 if ( NULL
!= pContext
) {
318 // Return the resources
320 gBS
->FreePool ( pContext
);
328 Queue data packets for transmission
330 @param [in] pContext Connection context structure address
332 @retval TRUE if a read error occurred
337 IN TSDT_CONNECTION_CONTEXT
* pContext
341 UINT64 LengthInBytes
;
343 TFTP_PACKET
* pPacket
;
348 // Use break instead of goto
353 // Fill the buffer if necessary
355 bReadError
= BufferFill ( pContext
);
358 // File access mode not supported
360 DEBUG (( DEBUG_ERROR
| DEBUG_TFTP_REQUEST
,
361 "ERROR - File read failure!\r\n" ));
364 // Tell the client of the error
366 SendError ( pContext
,
368 (UINT8
*)"Read failure" );
373 // Determine if any packets can be filled
375 if ( pContext
->bEofSent
376 || ( NULL
== pContext
->pFreeList
)) {
378 // All of the packets are filled
384 // Set the TFTP opcode and block number
386 pPacket
= PacketGet ( pContext
);
387 pBuffer
= &pPacket
->TxBuffer
[ 0 ];
389 *pBuffer
++ = TFTP_OP_DATA
;
390 *pBuffer
++ = (UINT8
)( pContext
->BlockNumber
>> 8 );
391 *pBuffer
++ = (UINT8
)pContext
->BlockNumber
;
394 // Determine how much data needs to be sent
396 LengthInBytes
= pContext
->BlockSize
;
397 if (( pContext
->BytesToSend
< TFTP_MAX_BLOCK_SIZE
)
398 && ( LengthInBytes
> pContext
->BytesToSend
)) {
399 LengthInBytes
= pContext
->BytesToSend
;
400 pContext
->bEofSent
= TRUE
;
402 DEBUG (( DEBUG_TX_PACKET
,
403 "0x%08x: Packet, Block %d filled with %d bytes\r\n",
405 pContext
->BlockNumber
,
406 (UINT32
)LengthInBytes
));
409 // Copy the file data into the packet
411 pPacket
->TxBytes
= (ssize_t
)( 2 + 2 + LengthInBytes
);
412 if ( 0 < LengthInBytes
) {
415 (UINTN
)LengthInBytes
);
416 DEBUG (( DEBUG_FILE_BUFFER
,
417 "0x%08x: Buffer consumed %d bytes of file data\r\n",
422 // Account for the file data consumed
424 pContext
->ValidBytes
-= LengthInBytes
;
425 pContext
->BytesToSend
-= LengthInBytes
;
426 pContext
->pBuffer
+= LengthInBytes
;
427 if ( pContext
->pEnd
<= pContext
->pBuffer
) {
428 pContext
->pBuffer
= &pContext
->FileData
[ 0 ];
433 // Queue the packet for transmission
435 PacketQueue ( pContext
, pPacket
);
439 // Return the read status
449 @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure
450 @param [in] pPacket Address of a ::TFTP_PACKET structure
455 IN TSDT_CONNECTION_CONTEXT
* pContext
,
456 IN TFTP_PACKET
* pPacket
462 // Don't free the error packet
464 if ( pPacket
!= &pContext
->ErrorPacket
) {
466 // Place the packet on the free list
468 pPacket
->pNext
= pContext
->pFreeList
;
469 pContext
->pFreeList
= pPacket
;
470 DEBUG (( DEBUG_TX_PACKET
,
471 "0x%08x: Packet queued to free list\r\n",
480 Get a packet from the free list for transmission
482 @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure
484 @retval Address of a ::TFTP_PACKET structure
489 IN TSDT_CONNECTION_CONTEXT
* pContext
492 TFTP_PACKET
* pPacket
;
497 // Get the next packet from the free list
499 pPacket
= pContext
->pFreeList
;
500 if ( NULL
!= pPacket
) {
501 pContext
->pFreeList
= pPacket
->pNext
;
502 pPacket
->RetryCount
= 0;
503 DEBUG (( DEBUG_TX_PACKET
,
504 "0x%08x: Packet removed from free list\r\n",
511 DBG_EXIT_HEX ( pPacket
);
517 Queue the packet for transmission
519 @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure
520 @param [in] pPacket Address of a ::TFTP_PACKET structure
522 @retval TRUE if a transmission error has occurred
527 IN TSDT_CONNECTION_CONTEXT
* pContext
,
528 IN TFTP_PACKET
* pPacket
531 BOOLEAN bTransmitError
;
538 // Account for this data block
540 pPacket
->BlockNumber
= pContext
->BlockNumber
;
541 pContext
->BlockNumber
+= 1;
544 // Queue the packet for transmission
546 pTail
= pContext
->pTxTail
;
547 if ( NULL
== pTail
) {
548 pContext
->pTxHead
= pPacket
;
551 pTail
->pNext
= pPacket
;
553 pContext
->pTxTail
= pPacket
;
554 pPacket
->pNext
= NULL
;
555 DEBUG (( DEBUG_TX_PACKET
,
556 "0x%08x: Packet queued to TX list\r\n",
560 // Start the transmission if necessary
562 bTransmitError
= FALSE
;
563 if ( pContext
->PacketsInWindow
< pContext
->WindowSize
) {
564 Status
= PacketTx ( pContext
, pPacket
);
565 bTransmitError
= (BOOLEAN
)( EFI_ERROR ( Status
));
569 // Return the transmit status
571 DBG_EXIT_TF ( bTransmitError
);
572 return bTransmitError
;
577 Remove a packet from the transmit queue
579 @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure
584 IN TSDT_CONNECTION_CONTEXT
* pContext
588 TFTP_PACKET
* pPacket
;
593 // Remove a packet from the transmit queue
596 pPacket
= pContext
->pTxHead
;
597 if ( NULL
!= pPacket
) {
598 pNext
= pPacket
->pNext
;
599 pContext
->pTxHead
= pNext
;
600 if ( NULL
== pNext
) {
601 pContext
->pTxTail
= NULL
;
603 DEBUG (( DEBUG_TX_PACKET
,
604 "0x%08x: Packet removed from TX list\r\n",
608 // Remove this packet from the window
610 pContext
->PacketsInWindow
-= 1;
616 DBG_EXIT_HEX ( pPacket
);
624 @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure
625 @param [in] pPacket Address of a ::TFTP_PACKET structure
627 @retval EFI_SUCCESS Message processed successfully
632 IN TSDT_CONNECTION_CONTEXT
* pContext
,
633 IN TFTP_PACKET
* pPacket
636 ssize_t LengthInBytes
;
644 Status
= EFI_SUCCESS
;
647 // Determine if this packet should be transmitted
649 if ( PcdGet32 ( Tftp_MaxRetry
) >= pPacket
->RetryCount
) {
650 pPacket
->RetryCount
+= 1;
653 // Display the operation
655 DEBUG (( DEBUG_TX_PACKET
,
656 "0x%08x: Packet transmiting\r\n",
659 "0x%08x: pContext sending 0x%08x bytes\r\n",
664 // Keep track of when the packet was transmitted
666 if ( PcdGetBool ( Tftp_HighSpeed
)) {
667 pPacket
->TxTime
= GetPerformanceCounter ( );
671 // Send the TFTP packet
673 pContext
->PacketsInWindow
+= 1;
674 LengthInBytes
= sendto ( pContext
->SocketFd
,
675 &pPacket
->TxBuffer
[ 0 ],
678 (struct sockaddr
*)&pContext
->RemoteAddress
,
679 pContext
->RemoteAddress
.sin6_len
);
680 if ( -1 == LengthInBytes
) {
681 DEBUG (( DEBUG_ERROR
| DEBUG_TX
,
682 "ERROR - Transmit failure, errno: 0x%08x\r\n",
684 pContext
->PacketsInWindow
-= 1;
685 Status
= EFI_DEVICE_ERROR
;
692 Status
= EFI_NO_RESPONSE
;
693 DEBUG (( DEBUG_WARN
| DEBUG_WINDOW
,
694 "WARNING - No response from TFTP client\r\n" ));
698 // Return the operation status
700 DBG_EXIT_STATUS ( Status
);
706 Process the work for the sockets.
708 @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure
709 @param [in] pIndex Address of an index into the pollfd array
714 IN TSDT_TFTP_SERVER
* pTftpServer
,
719 TSDT_CONNECTION_CONTEXT
* pContext
;
720 struct pollfd
* pTftpPort
;
721 socklen_t RemoteAddressLength
;
731 pTftpPort
= &pTftpServer
->TftpPort
[ *pIndex
];
734 // Handle input events
736 revents
= pTftpPort
->revents
;
737 pTftpPort
->revents
= 0;
738 if ( 0 != ( revents
& POLLRDNORM
)) {
740 // Receive the message from the remote system
742 RemoteAddressLength
= sizeof ( pTftpServer
->RemoteAddress
);
743 pTftpServer
->RxBytes
= recvfrom ( pTftpPort
->fd
,
744 &pTftpServer
->RxBuffer
[ 0 ],
745 sizeof ( pTftpServer
->RxBuffer
),
747 (struct sockaddr
*) &pTftpServer
->RemoteAddress
,
748 &RemoteAddressLength
);
749 if ( -1 != pTftpServer
->RxBytes
) {
750 if ( PcdGetBool ( Tftp_HighSpeed
)) {
751 pTftpServer
->RxTime
= GetPerformanceCounter ( );
753 if ( AF_INET
== pTftpServer
->RemoteAddress
.v4
.sin_family
) {
754 DEBUG (( DEBUG_TFTP_PORT
,
755 "Received %d bytes from %d.%d.%d.%d:%d\r\n",
756 pTftpServer
->RxBytes
,
757 pTftpServer
->RemoteAddress
.v4
.sin_addr
.s_addr
& 0xff,
758 ( pTftpServer
->RemoteAddress
.v4
.sin_addr
.s_addr
>> 8 ) & 0xff,
759 ( pTftpServer
->RemoteAddress
.v4
.sin_addr
.s_addr
>> 16 ) & 0xff,
760 ( pTftpServer
->RemoteAddress
.v4
.sin_addr
.s_addr
>> 24 ) & 0xff,
761 htons ( pTftpServer
->RemoteAddress
.v4
.sin_port
)));
764 DEBUG (( DEBUG_TFTP_PORT
,
765 "Received %d bytes from [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n",
766 pTftpServer
->RxBytes
,
767 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 0 ],
768 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 1 ],
769 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 2 ],
770 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 3 ],
771 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 4 ],
772 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 5 ],
773 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 6 ],
774 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 7 ],
775 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 8 ],
776 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 9 ],
777 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 10 ],
778 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 11 ],
779 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 12 ],
780 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 13 ],
781 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 14 ],
782 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 15 ],
783 htons ( pTftpServer
->RemoteAddress
.v6
.sin6_port
)));
787 // Lookup connection context using the remote system address and port
788 // to determine if an existing connection to this remote
791 pContext
= ContextFind ( pTftpServer
);
794 // Process the received message
796 TftpProcessRequest ( pTftpServer
, pContext
, pTftpPort
->fd
);
800 // Receive error on the TFTP server port
801 // Close the server socket
803 DEBUG (( DEBUG_ERROR
,
804 "ERROR - Failed receive on TFTP server port, errno: 0x%08x\r\n",
811 // Handle the close event
813 if ( 0 != ( revents
& POLLHUP
)) {
817 close ( pTftpPort
->fd
);
820 pTftpServer
->Entries
-= 1;
821 ASSERT ( 0 <= pTftpServer
->Entries
);
830 Build and send an error packet
832 @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure
833 @param [in] Error Error number for the packet
834 @param [in] pError Zero terminated error string address
836 @retval EFI_SUCCESS Message processed successfully
841 IN TSDT_CONNECTION_CONTEXT
* pContext
,
848 TFTP_PACKET
* pPacket
;
854 // Build the error packet
856 pPacket
= &pContext
->ErrorPacket
;
857 pBuffer
= &pPacket
->TxBuffer
[ 0 ];
859 pBuffer
[ 1 ] = TFTP_OP_ERROR
;
860 pBuffer
[ 2 ] = (UINT8
)( Error
>> 8 );
861 pBuffer
[ 3 ] = (UINT8
)Error
;
864 // Copy the zero terminated string into the buffer
868 Character
= *pError
++;
869 *pBuffer
++ = Character
;
870 } while ( 0 != Character
);
873 // Send the error message
875 pPacket
->TxBytes
= pBuffer
- &pPacket
->TxBuffer
[ 0 ];
876 Status
= PacketTx ( pContext
, pPacket
);
879 // Return the operation status
881 DBG_EXIT_STATUS ( Status
);
887 Scan the list of sockets and process any pending work
889 @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure
894 IN TSDT_TFTP_SERVER
* pTftpServer
899 DEBUG (( DEBUG_SOCKET_POLL
, "Entering SocketPoll\r\n" ));
902 // Determine if any ports are active
904 if ( 0 != pTftpServer
->Entries
) {
905 FDCount
= poll ( &pTftpServer
->TftpPort
[ 0 ],
906 pTftpServer
->Entries
,
912 PortWork ( pTftpServer
, &pTftpServer
->Udpv4Index
);
913 PortWork ( pTftpServer
, &pTftpServer
->Udpv6Index
);
917 DEBUG (( DEBUG_SOCKET_POLL
, "Exiting SocketPoll\r\n" ));
924 @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure
925 @param [in] pContext Connection context structure address
927 @retval TRUE if the context should be closed
932 IN TSDT_TFTP_SERVER
* pTftpServer
,
933 IN TSDT_CONNECTION_CONTEXT
* pContext
937 BOOLEAN bCloseContext
;
940 TFTP_PACKET
* pPacket
;
946 // Use break instead of goto
948 bCloseContext
= FALSE
;
951 // Validate the parameters
953 if ( NULL
== pContext
) {
954 if ( AF_INET
== pTftpServer
->RemoteAddress
.v4
.sin_family
) {
955 DEBUG (( DEBUG_ERROR
,
956 "ERROR - File not open for %d.%d.%d.%d:%d\r\n",
957 (UINT8
)pTftpServer
->RemoteAddress
.v4
.sin_addr
.s_addr
,
958 (UINT8
)( pTftpServer
->RemoteAddress
.v4
.sin_addr
.s_addr
>> 8 ),
959 (UINT8
)( pTftpServer
->RemoteAddress
.v4
.sin_addr
.s_addr
>> 16 ),
960 (UINT8
)( pTftpServer
->RemoteAddress
.v4
.sin_addr
.s_addr
>> 24 ),
961 htons ( pTftpServer
->RemoteAddress
.v4
.sin_port
)));
964 DEBUG (( DEBUG_ERROR
,
965 "ERROR - File not open for [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n",
966 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 0 ],
967 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 1 ],
968 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 2 ],
969 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 3 ],
970 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 4 ],
971 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 5 ],
972 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 6 ],
973 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 7 ],
974 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 8 ],
975 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 9 ],
976 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 10 ],
977 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 11 ],
978 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 12 ],
979 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 13 ],
980 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 14 ],
981 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 15 ],
982 htons ( pTftpServer
->RemoteAddress
.v6
.sin6_port
)));
988 // Verify that the ACK was expected
990 pPacket
= pContext
->pTxHead
;
991 if ( NULL
== pPacket
) {
995 DEBUG (( DEBUG_ERROR
,
996 "ERROR - Expecting data not ACKs for pContext 0x%08x\r\n",
1002 // Get the ACKed block number
1004 pBuffer
= &pTftpServer
->RxBuffer
[ 0 ];
1005 BlockNumber
= HTONS ( *(UINT16
*)&pBuffer
[ 2 ]);
1008 // Determine if this is the correct ACK
1010 DEBUG (( DEBUG_TFTP_ACK
,
1011 "ACK for block 0x%04x received\r\n",
1013 AckNumber
= BlockNumber
- pPacket
->BlockNumber
;
1014 if (( 0 > AckNumber
) || ( AckNumber
>= (INTN
)pContext
->PacketsInWindow
)){
1015 DEBUG (( DEBUG_WARN
| DEBUG_TFTP_ACK
,
1016 "WARNING - Expecting ACK 0x%0x4 not received ACK 0x%08x\r\n",
1017 pPacket
->BlockNumber
,
1023 // Release the ACKed packets
1027 // Remove the packet from the transmit list and window
1029 pPacket
= PacketRemove ( pContext
);
1032 // Get the block number of this packet
1034 AckNumber
= pPacket
->BlockNumber
;
1037 // Increase the size of the transmit window
1039 if ( PcdGetBool ( Tftp_HighSpeed
)
1040 && ( AckNumber
== BlockNumber
)) {
1041 WindowAck ( pTftpServer
, pContext
, pPacket
);
1047 PacketFree ( pContext
, pPacket
);
1048 } while (( NULL
!= pContext
->pTxHead
) && ( AckNumber
!= BlockNumber
));
1051 // Fill the window with packets
1053 pPacket
= pContext
->pTxHead
;
1054 while (( NULL
!= pPacket
)
1055 && ( pContext
->PacketsInWindow
< pContext
->WindowSize
)
1056 && ( !bCloseContext
)) {
1057 Status
= PacketTx ( pContext
, pPacket
);
1058 bCloseContext
= (BOOLEAN
)( EFI_ERROR ( Status
));
1059 pPacket
= pPacket
->pNext
;
1063 // Get more packets ready for transmission
1065 PacketFill ( pContext
);
1068 // Close the context when the last packet is ACKed
1070 if ( 0 == pContext
->PacketsInWindow
) {
1071 bCloseContext
= TRUE
;
1074 // Display the bandwidth
1076 if ( PcdGetBool ( Tftp_Bandwidth
)) {
1083 // Compute the download time
1085 DeltaTime
= GetPerformanceCounter ( );
1086 if ( pTftpServer
->Time2
> pTftpServer
->Time1
) {
1087 DeltaTime
= DeltaTime
- pContext
->TimeStart
;
1090 DeltaTime
= pContext
->TimeStart
- DeltaTime
;
1092 NanoSeconds
= GetTimeInNanoSecond ( DeltaTime
);
1093 Bandwidth
= pContext
->LengthInBytes
;
1094 DEBUG (( DEBUG_WINDOW
,
1095 "File Length %Ld, Transfer Time: %d.%03d Sec\r\n",
1097 DivU64x32 ( NanoSeconds
, 1000 * 1000 * 1000 ),
1098 ((UINT32
)DivU64x32 ( NanoSeconds
, 1000 * 1000 )) % 1000 ));
1101 // Display the round trip time
1103 Bandwidth
= MultU64x32 ( Bandwidth
, 8 * 1000 * 1000 );
1104 Bandwidth
/= NanoSeconds
;
1105 if ( 1000 > Bandwidth
) {
1106 Value
= (UINT32
)Bandwidth
;
1107 Print ( L
"Bandwidth: %d Kbits/Sec\r\n",
1110 else if (( 1000 * 1000 ) > Bandwidth
) {
1111 Value
= (UINT32
)Bandwidth
;
1112 Print ( L
"Bandwidth: %d.%03d Mbits/Sec\r\n",
1117 Value
= (UINT32
)DivU64x32 ( Bandwidth
, 1000 );
1118 Print ( L
"Bandwidth: %d.%03d Gbits/Sec\r\n",
1128 // Return the operation status
1131 return bCloseContext
;
1136 Get the next TFTP option
1138 @param [in] pOption Address of a zero terminated option string
1139 @param [in] pEnd End of buffer address
1140 @param [in] ppNextOption Address to receive the address of the next
1141 zero terminated option string
1143 @retval EFI_SUCCESS Message processed successfully
1150 IN UINT8
** ppNextOption
1153 UINT8
* pNextOption
;
1157 // Locate the end of the option
1159 pNextOption
= pOption
;
1160 while (( pEnd
> pNextOption
) && ( 0 != *pNextOption
)) {
1163 if ( pEnd
<= pNextOption
) {
1165 // Error - end of buffer reached
1167 DEBUG (( DEBUG_ERROR
| DEBUG_TFTP_REQUEST
,
1168 "ERROR - Option without zero termination received!\r\n" ));
1169 Status
= EFI_INVALID_PARAMETER
;
1173 // Zero terminated option found
1178 // Display the zero terminated ASCII option string
1180 DEBUG (( DEBUG_TFTP_REQUEST
,
1183 Status
= EFI_SUCCESS
;
1187 // Return the next option address
1189 *ppNextOption
= pNextOption
;
1192 // Return the operation status
1199 Place an option value into the option acknowledgement
1201 @param [in] pOack Option acknowledgement address
1202 @param [in] Value Value to translate into ASCII decimal
1204 @return Option acknowledgement address
1216 // Determine the next value
1218 NextValue
= Value
/ 10;
1221 // Supress leading zeros
1223 if ( 0 != NextValue
) {
1224 pOack
= TftpOptionSet ( pOack
, NextValue
);
1228 // Output this digit
1230 *pOack
++ = (UINT8
)( Value
- ( NextValue
* 10 ) + '0' );
1233 // Return the next option acknowledgement location
1240 Process the TFTP request
1242 @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure
1243 @param [in] pOption Address of the first zero terminated option string
1244 @param [in] pEnd End of buffer address
1249 IN TSDT_CONNECTION_CONTEXT
* pContext
,
1254 UINT8
* pNextOption
;
1256 TFTP_PACKET
* pPacket
;
1265 pPacket
= PacketGet ( pContext
);
1268 // Start the OACK packet
1269 // Let the OACK handle the parsing errors
1270 // See http://tools.ietf.org/html/rfc2347
1272 pOack
= &pPacket
->TxBuffer
[ 0 ];
1274 *pOack
++ = TFTP_OP_OACK
;
1275 pPacket
->TxBytes
= 2;
1276 pPacket
->BlockNumber
= 0;
1279 // Walk the list of options
1283 // Get the next option, skip junk at end of message
1285 Status
= TftpOptionGet ( pOption
, pEnd
, &pNextOption
);
1286 if ( !EFI_ERROR ( Status
)) {
1288 // Process the option
1292 // blksize - See http://tools.ietf.org/html/rfc2348
1294 pValue
= pNextOption
;
1295 if ( 0 == strcasecmp ((char *)pOption
, "blksize" )) {
1299 Status
= TftpOptionGet ( pValue
, pEnd
, &pNextOption
);
1300 if ( !EFI_ERROR ( Status
)) {
1302 // Validate the block size, skip non-numeric block sizes
1304 Status
= TftpOptionValue ( pValue
, &Value
);
1305 if ( !EFI_ERROR ( Status
)) {
1307 // Propose a smaller block size if necessary
1309 if ( Value
> TFTP_MAX_BLOCK_SIZE
) {
1310 Value
= TFTP_MAX_BLOCK_SIZE
;
1314 // Set the new block size
1316 pContext
->BlockSize
= Value
;
1317 DEBUG (( DEBUG_TFTP_REQUEST
,
1318 "Using block size of %d bytes\r\n",
1319 pContext
->BlockSize
));
1333 pOack
= TftpOptionSet ( pOack
, pContext
->BlockSize
);
1335 pPacket
->TxBytes
+= pOack
- pTemp
;
1341 // timeout - See http://tools.ietf.org/html/rfc2349
1343 else if ( 0 == strcasecmp ((char *)pOption
, "timeout" )) {
1347 Status
= TftpOptionGet ( pValue
, pEnd
, &pNextOption
);
1348 if ( !EFI_ERROR ( Status
)) {
1349 Status
= TftpOptionValue ( pValue
, &Value
);
1350 if ( !EFI_ERROR ( Status
)) {
1352 // Set the timeout value
1354 pContext
->MaxTimeout
= Value
;
1355 DEBUG (( DEBUG_TFTP_REQUEST
,
1356 "Using timeout of %d seconds\r\n",
1357 pContext
->MaxTimeout
));
1371 pOack
= TftpOptionSet ( pOack
, pContext
->MaxTimeout
);
1373 pPacket
->TxBytes
+= pOack
- pTemp
;
1379 // tsize - See http://tools.ietf.org/html/rfc2349
1381 else if ( 0 == strcasecmp ((char *)pOption
, "tsize" )) {
1385 Status
= TftpOptionGet ( pValue
, pEnd
, &pNextOption
);
1386 if ( !EFI_ERROR ( Status
)) {
1387 Status
= TftpOptionValue ( pValue
, &Value
);
1388 if ( !EFI_ERROR ( Status
)) {
1390 // Return the file size
1392 DEBUG (( DEBUG_TFTP_REQUEST
,
1393 "Returning file size of %Ld bytes\r\n",
1394 pContext
->LengthInBytes
));
1406 pOack
= TftpOptionSet ( pOack
, pContext
->LengthInBytes
);
1408 pPacket
->TxBytes
+= pOack
- pTemp
;
1414 // Unknown option - Ignore it
1416 DEBUG (( DEBUG_WARN
| DEBUG_TFTP_REQUEST
,
1417 "WARNING - Skipping unknown option: %a\r\n",
1423 // Set the next option
1425 pOption
= pNextOption
;
1426 } while ( pEnd
> pOption
);
1429 // Transmit the OACK if necessary
1431 if ( 2 < pPacket
->TxBytes
) {
1432 PacketQueue ( pContext
, pPacket
);
1435 PacketFree ( pContext
, pPacket
);
1441 Process the TFTP request
1443 @param [in] pOption Address of the first zero terminated option string
1444 @param [in] pValue Address to receive the value
1446 @retval EFI_SUCCESS Option translated into a value
1462 Status
= EFI_SUCCESS
;
1465 // Walk the characters in the option
1468 while ( 0 != *pOption
) {
1470 // Convert the next digit to binary
1473 if (( '0' <= Digit
) && ( '9' >= Digit
)) {
1475 Value
+= Digit
- '0';
1478 DEBUG (( DEBUG_ERROR
| DEBUG_TFTP_REQUEST
,
1479 "ERROR - Invalid character '0x%02x' in the value\r\n",
1481 Status
= EFI_INVALID_PARAMETER
;
1492 // Return the conversion status
1499 Process the TFTP request
1501 @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure
1502 @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure
1503 @param [in] SocketFd Socket file descriptor
1507 TftpProcessRequest (
1508 IN TSDT_TFTP_SERVER
* pTftpServer
,
1509 IN TSDT_CONNECTION_CONTEXT
* pContext
,
1513 BOOLEAN bCloseContext
;
1521 Opcode
= HTONS ( *(UINT16
*)&pTftpServer
->RxBuffer
[ 0 ]);
1522 DEBUG (( DEBUG_TFTP_REQUEST
,
1523 "TFTP Opcode: 0x%08x\r\n",
1527 // Validate the parameters
1529 bCloseContext
= FALSE
;
1532 DEBUG (( DEBUG_TFTP_REQUEST
,
1533 "ERROR - Unknown TFTP opcode: %d\r\n",
1538 bCloseContext
= TftpAck ( pTftpServer
, pContext
);
1541 case TFTP_OP_READ_REQUEST
:
1542 bCloseContext
= TftpRead ( pTftpServer
, pContext
, SocketFd
);
1549 if ( NULL
== pContext
) {
1550 if ( AF_INET
== pTftpServer
->RemoteAddress
.v4
.sin_family
) {
1551 DEBUG (( DEBUG_ERROR
,
1552 "ERROR - File not open for %d.%d.%d.%d:%d\r\n",
1553 (UINT8
)pTftpServer
->RemoteAddress
.v4
.sin_addr
.s_addr
,
1554 (UINT8
)( pTftpServer
->RemoteAddress
.v4
.sin_addr
.s_addr
>> 8 ),
1555 (UINT8
)( pTftpServer
->RemoteAddress
.v4
.sin_addr
.s_addr
>> 16 ),
1556 (UINT8
)( pTftpServer
->RemoteAddress
.v4
.sin_addr
.s_addr
>> 24 ),
1557 htons ( pTftpServer
->RemoteAddress
.v4
.sin_port
)));
1560 DEBUG (( DEBUG_ERROR
,
1561 "ERROR - File not open for [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n",
1562 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 0 ],
1563 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 1 ],
1564 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 2 ],
1565 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 3 ],
1566 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 4 ],
1567 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 5 ],
1568 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 6 ],
1569 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 7 ],
1570 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 8 ],
1571 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 9 ],
1572 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 10 ],
1573 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 11 ],
1574 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 12 ],
1575 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 13 ],
1576 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 14 ],
1577 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 15 ],
1578 htons ( pTftpServer
->RemoteAddress
.v6
.sin6_port
)));
1582 if ( 0 != pContext
->PacketsInWindow
) {
1583 DEBUG (( DEBUG_ERROR
,
1584 "ERROR - Expecting ACKs not data for pContext 0x%08x\r\n",
1588 if ( pTftpServer
->RxBytes
> (ssize_t
)( pContext
->BlockSize
+ 2 + 2 )) {
1589 DEBUG (( DEBUG_ERROR
,
1590 "ERROR - Receive data length of %d > %d bytes (maximum block size) for pContext 0x%08x\r\n",
1591 pTftpServer
->RxBytes
- 2 - 2,
1592 pContext
->BlockSize
,
1599 if ( NULL
== pContext
) {
1600 if ( AF_INET
== pTftpServer
->RemoteAddress
.v4
.sin_family
) {
1601 DEBUG (( DEBUG_ERROR
,
1602 "ERROR - File not open for %d.%d.%d.%d:%d\r\n",
1603 (UINT8
)pTftpServer
->RemoteAddress
.v4
.sin_addr
.s_addr
,
1604 (UINT8
)( pTftpServer
->RemoteAddress
.v4
.sin_addr
.s_addr
>> 8 ),
1605 (UINT8
)( pTftpServer
->RemoteAddress
.v4
.sin_addr
.s_addr
>> 16 ),
1606 (UINT8
)( pTftpServer
->RemoteAddress
.v4
.sin_addr
.s_addr
>> 24 ),
1607 htons ( pTftpServer
->RemoteAddress
.v4
.sin_port
)));
1610 DEBUG (( DEBUG_ERROR
,
1611 "ERROR - File not open for [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n",
1612 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 0 ],
1613 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 1 ],
1614 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 2 ],
1615 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 3 ],
1616 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 4 ],
1617 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 5 ],
1618 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 6 ],
1619 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 7 ],
1620 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 8 ],
1621 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 9 ],
1622 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 10 ],
1623 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 11 ],
1624 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 12 ],
1625 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 13 ],
1626 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 14 ],
1627 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 15 ],
1628 htons ( pTftpServer
->RemoteAddress
.v6
.sin6_port
)));
1635 // Determine if the context should be closed
1637 if ( bCloseContext
) {
1638 ContextRemove ( pTftpServer
, pContext
);
1646 Process the read request
1648 @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure
1649 @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure
1650 @param [in] SocketFd Socket file descriptor
1652 @retval TRUE if the context should be closed
1657 IN TSDT_TFTP_SERVER
* pTftpServer
,
1658 IN TSDT_CONNECTION_CONTEXT
* pContext
,
1662 BOOLEAN bCloseContext
;
1663 struct stat FileStatus
;
1675 // Log the receive time
1678 if ( PcdGetBool ( Tftp_Bandwidth
)) {
1679 TimeStart
= GetPerformanceCounter ( );
1683 // Close the context if necessary
1685 bCloseContext
= FALSE
;
1686 if ( NULL
!= pContext
) {
1687 ContextRemove ( pTftpServer
, pContext
);
1691 // Use break instead of goto
1695 // Create the connection context
1697 pContext
= ContextAdd ( pTftpServer
, SocketFd
);
1698 if ( NULL
== pContext
) {
1703 // Set the start time
1705 if ( PcdGetBool ( Tftp_Bandwidth
)) {
1706 pContext
->TimeStart
= TimeStart
;
1712 pBuffer
= &pTftpServer
->RxBuffer
[ 0 ];
1713 pEnd
= &pBuffer
[ pTftpServer
->RxBytes
];
1714 pFileName
= &pBuffer
[ 2 ];
1716 while (( pEnd
> pMode
) && ( 0 != *pMode
)) {
1719 if ( pEnd
<= pMode
) {
1723 DEBUG (( DEBUG_ERROR
| DEBUG_RX
,
1724 "ERROR - File mode not found\r\n" ));
1726 // Tell the client of the error
1728 SendError ( pContext
,
1730 (UINT8
*)"File open mode not found" );
1734 DEBUG (( DEBUG_TFTP_REQUEST
,
1735 "TFTP - FileName: %a\r\n",
1739 // Locate the options
1742 while (( pEnd
> pOption
) && ( 0 != *pOption
)) {
1745 if ( pEnd
<= pOption
) {
1747 // End of mode not found
1749 DEBUG (( DEBUG_ERROR
| DEBUG_RX
,
1750 "ERROR - File mode not valid\r\n" ));
1752 // Tell the client of the error
1754 SendError ( pContext
,
1756 (UINT8
*)"File open mode not valid" );
1760 DEBUG (( DEBUG_TFTP_REQUEST
,
1761 "TFTP - Mode: %a\r\n",
1765 // Verify the mode is supported
1768 if ( 0 == strcasecmp ((char *)pMode
, "octet" )) {
1770 // Read the file as binary input
1776 // Determine the file length
1778 pContext
->File
= fopen ((const char *)pFileName
, pReadMode
);
1779 if (( NULL
== pContext
->File
)
1780 || ( -1 == stat ((const char *)pFileName
, &FileStatus
))) {
1784 DEBUG (( DEBUG_ERROR
| DEBUG_TFTP_REQUEST
,
1785 ( NULL
== pContext
->File
)
1786 ? "ERROR - File not found!\r\n"
1787 : "ERROR - Unable to determine file %a size!\r\n",
1791 // Tell the client of the error
1793 SendError ( pContext
,
1794 TFTP_ERROR_NOT_FOUND
,
1795 (UINT8
*)"File not found" );
1798 pContext
->LengthInBytes
= FileStatus
.st_size
;
1799 pContext
->BytesRemaining
= pContext
->LengthInBytes
;
1800 pContext
->BytesToSend
= pContext
->LengthInBytes
;
1803 // Display the file size
1805 DEBUG_CODE_BEGIN ( );
1808 if ( 1024 > pContext
->LengthInBytes
) {
1809 Value
= (UINT32
)pContext
->LengthInBytes
;
1810 DEBUG (( DEBUG_FILE_BUFFER
,
1811 "%a size: %d Bytes\r\n",
1815 else if (( 1024 * 1024 ) > pContext
->LengthInBytes
) {
1816 Value
= (UINT32
)pContext
->LengthInBytes
;
1817 DEBUG (( DEBUG_FILE_BUFFER
,
1818 "%a size: %d.%03d KiBytes (%Ld Bytes)\r\n",
1821 (( Value
% 1024 ) * 1000 ) / 1024,
1822 pContext
->LengthInBytes
));
1824 else if (( 1024 * 1024 * 1024 ) > pContext
->LengthInBytes
) {
1825 Value
= (UINT32
)DivU64x32 ( pContext
->LengthInBytes
, 1024 );
1826 DEBUG (( DEBUG_FILE_BUFFER
,
1827 "%a size: %d.%03d MiBytes (%Ld Bytes)\r\n",
1830 (( Value
% 1024 ) * 1000 ) / 1024,
1831 pContext
->LengthInBytes
));
1834 Value
= (UINT32
)DivU64x32 ( pContext
->LengthInBytes
, 1024 * 1024 );
1835 DEBUG (( DEBUG_FILE_BUFFER
,
1836 "%a size: %d.%03d GiBytes (%Ld Bytes)\r\n",
1839 (( Value
% 1024 ) * 1000 ) / 1024,
1840 pContext
->LengthInBytes
));
1845 // Process the options
1847 if ( pEnd
> pOption
) {
1848 TftpOptions ( pContext
, pOption
, pEnd
);
1852 // Skip the open ACK
1854 pContext
->BlockNumber
= 1;
1858 // Send the first packet (OACK or data block)
1860 bCloseContext
= PacketFill ( pContext
);
1865 // Return the close status
1868 return bCloseContext
;
1873 Create the port for the TFTP server
1875 This routine polls the network layer to create the TFTP port for the
1876 TFTP server. More than one attempt may be necessary since it may take
1877 some time to get the IP address and initialize the upper layers of
1880 @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure
1881 @param [in] AddressFamily The address family to use for the conection.
1882 @param [in] pIndex Address of the index into the port array
1887 IN TSDT_TFTP_SERVER
* pTftpServer
,
1888 IN sa_family_t AddressFamily
,
1893 struct pollfd
* pTftpPort
;
1896 struct sockaddr_in v4
;
1897 struct sockaddr_in6 v6
;
1898 } TftpServerAddress
;
1900 DEBUG (( DEBUG_SERVER_TIMER
, "Entering TftpServerListen\r\n" ));
1903 // Determine if the socket is already initialized
1905 if ( -1 == *pIndex
) {
1907 // Attempt to create the socket for the TFTP server
1909 pTftpPort
= &pTftpServer
->TftpPort
[ pTftpServer
->Entries
];
1910 pTftpPort
->fd
= socket ( AddressFamily
,
1913 if ( -1 != pTftpPort
->fd
) {
1915 // Initialize the poll structure
1917 pTftpPort
->events
= POLLRDNORM
| POLLHUP
;
1918 pTftpPort
->revents
= 0;
1921 // Set the socket address
1924 ZeroMem ( &TftpServerAddress
, sizeof ( TftpServerAddress
));
1925 TftpServerAddress
.v4
.sin_port
= htons ( TftpPort
);
1926 if ( AF_INET
== AddressFamily
) {
1927 TftpServerAddress
.v4
.sin_len
= sizeof ( TftpServerAddress
.v4
);
1928 TftpServerAddress
.v4
.sin_family
= AF_INET
;
1931 TftpServerAddress
.v6
.sin6_len
= sizeof ( TftpServerAddress
.v6
);
1932 TftpServerAddress
.v6
.sin6_family
= AF_INET6
;
1936 // Bind the socket to the TFTP port
1938 SocketStatus
= bind ( pTftpPort
->fd
,
1939 (struct sockaddr
*) &TftpServerAddress
,
1940 TftpServerAddress
.v6
.sin6_len
);
1941 if ( -1 != SocketStatus
) {
1942 DEBUG (( DEBUG_TFTP_PORT
,
1943 "0x%08x: Socket bound to port %d\r\n",
1948 // Account for this connection
1950 *pIndex
= pTftpServer
->Entries
;
1951 pTftpServer
->Entries
+= 1;
1952 ASSERT ( DIM ( pTftpServer
->TftpPort
) >= pTftpServer
->Entries
);
1956 // Release the socket if necessary
1958 if ( -1 == SocketStatus
) {
1959 close ( pTftpPort
->fd
);
1965 DEBUG (( DEBUG_SERVER_TIMER
, "Exiting TftpServerListen\r\n" ));
1970 Update the window due to the ACK
1972 @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure
1973 @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure
1974 @param [in] pPacket Address of a ::TFTP_PACKET structure
1979 IN TSDT_TFTP_SERVER
* pTftpServer
,
1980 IN TSDT_CONNECTION_CONTEXT
* pContext
,
1981 IN TFTP_PACKET
* pPacket
1984 if ( PcdGetBool ( Tftp_HighSpeed
)) {
1991 // Compute the round trip time
1993 if ( pTftpServer
->Time2
> pTftpServer
->Time1
) {
1994 DeltaTime
= pTftpServer
->RxTime
- pPacket
->TxTime
;
1997 DeltaTime
= pPacket
->TxTime
- pTftpServer
->RxTime
;
2001 // Adjust the round trip time
2003 NanoSeconds
= GetTimeInNanoSecond ( DeltaTime
);
2004 DeltaTime
= RShiftU64 ( pContext
->Rtt2x
, ACK_SHIFT
);
2005 pContext
->Rtt2x
+= NanoSeconds
+ NanoSeconds
- DeltaTime
;
2006 if ( pContext
->Rtt2x
> pContext
->MaxTimeout
) {
2007 pContext
->Rtt2x
= pContext
->MaxTimeout
;
2011 // Account for the ACK
2013 if ( pContext
->WindowSize
< MAX_PACKETS
) {
2014 pContext
->AckCount
-= 1;
2015 if ( 0 == pContext
->AckCount
) {
2017 // Increase the window
2019 pContext
->WindowSize
+= 1;
2022 // Set the ACK count
2024 if ( pContext
->WindowSize
< pContext
->Threshold
) {
2025 pContext
->AckCount
= pContext
->WindowSize
* PcdGet32 ( Tftp_AckMultiplier
);
2028 pContext
->AckCount
= PcdGet32 ( Tftp_AckLogBase
) << pContext
->WindowSize
;
2032 // Display the round trip time
2034 DEBUG_CODE_BEGIN ( );
2037 DeltaTime
= RShiftU64 ( pContext
->Rtt2x
, 1 );
2038 if ( 1000 > DeltaTime
) {
2039 DEBUG (( DEBUG_WINDOW
,
2040 "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %Ld nSec\r\n",
2041 pContext
->WindowSize
,
2042 pContext
->Threshold
,
2046 else if (( 1000 * 1000 ) > DeltaTime
) {
2047 Value
= (UINT32
)DeltaTime
;
2048 DEBUG (( DEBUG_WINDOW
,
2049 "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %d.%03d uSec\r\n",
2050 pContext
->WindowSize
,
2051 pContext
->Threshold
,
2056 else if (( 1000 * 1000 * 1000 ) > DeltaTime
) {
2057 Value
= (UINT32
)DivU64x32 ( DeltaTime
, 1000 );
2058 DEBUG (( DEBUG_WINDOW
,
2059 "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %d.%03d mSec\r\n",
2060 pContext
->WindowSize
,
2061 pContext
->Threshold
,
2067 Value
= (UINT32
)DivU64x32 ( DeltaTime
, 1000 * 1000 );
2068 DEBUG (( DEBUG_WINDOW
,
2069 "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %d.%03d Sec\r\n",
2070 pContext
->WindowSize
,
2071 pContext
->Threshold
,
2086 A timeout has occurred, close the window
2088 @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure
2093 IN TSDT_CONNECTION_CONTEXT
* pContext
2096 if ( PcdGetBool ( Tftp_HighSpeed
)) {
2097 TFTP_PACKET
* pPacket
;
2102 // Set the threshold at half the previous window size
2104 pContext
->Threshold
= ( pContext
->WindowSize
+ 1 ) >> 1;
2107 // Close the transmit window
2109 pContext
->WindowSize
= 1;
2110 pContext
->PacketsInWindow
= 0;
2113 // Double the round trip time
2115 pContext
->Rtt2x
= LShiftU64 ( pContext
->Rtt2x
, 1 );
2116 if ( pContext
->Rtt2x
> pContext
->MaxTimeout
) {
2117 pContext
->Rtt2x
= pContext
->MaxTimeout
;
2121 // Set the ACK count
2123 if ( pContext
->WindowSize
< pContext
->Threshold
) {
2124 pContext
->AckCount
= pContext
->WindowSize
* PcdGet32 ( Tftp_AckMultiplier
);
2127 pContext
->AckCount
= PcdGet32 ( Tftp_AckLogBase
) << pContext
->WindowSize
;
2131 // Display the round trip time
2133 DEBUG_CODE_BEGIN ( );
2137 DeltaTime
= RShiftU64 ( pContext
->Rtt2x
, 1 );
2138 if ( 1000 > DeltaTime
) {
2139 DEBUG (( DEBUG_WINDOW
,
2140 "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %Ld nSec\r\n",
2141 pContext
->WindowSize
,
2142 pContext
->Threshold
,
2146 else if (( 1000 * 1000 ) > DeltaTime
) {
2147 Value
= (UINT32
)DeltaTime
;
2148 DEBUG (( DEBUG_WINDOW
,
2149 "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %d.%03d uSec\r\n",
2150 pContext
->WindowSize
,
2151 pContext
->Threshold
,
2156 else if (( 1000 * 1000 * 1000 ) > DeltaTime
) {
2157 Value
= (UINT32
)DivU64x32 ( DeltaTime
, 1000 );
2158 DEBUG (( DEBUG_WINDOW
,
2159 "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %d.%03d mSec\r\n",
2160 pContext
->WindowSize
,
2161 pContext
->Threshold
,
2167 Value
= (UINT32
)DivU64x32 ( DeltaTime
, 1000 * 1000 );
2168 DEBUG (( DEBUG_WINDOW
,
2169 "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %d.%03d Sec\r\n",
2170 pContext
->WindowSize
,
2171 pContext
->Threshold
,
2179 // Retransmit the first packet in the window
2181 pPacket
= pContext
->pTxHead
;
2182 if ( NULL
!= pPacket
) {
2183 PacketTx ( pContext
, pPacket
);
2192 Entry point for the TFTP server application.
2194 @param [in] Argc The number of arguments
2195 @param [in] Argv The argument value array
2197 @retval 0 The application exited normally.
2198 @retval Other An error occurred.
2207 TSDT_TFTP_SERVER
* pTftpServer
;
2212 // Get the performance counter characteristics
2214 pTftpServer
= &mTftpServer
;
2215 if ( PcdGetBool ( Tftp_HighSpeed
)
2216 || PcdGetBool ( Tftp_Bandwidth
)) {
2217 pTftpServer
->ClockFrequency
= GetPerformanceCounterProperties ( &pTftpServer
->Time1
,
2218 &pTftpServer
->Time2
);
2222 // Create a timer event to start TFTP port
2224 Status
= gBS
->CreateEvent ( EVT_TIMER
,
2228 &pTftpServer
->TimerEvent
);
2229 if ( !EFI_ERROR ( Status
)) {
2231 // Compute the poll interval
2233 TriggerTime
= TFTP_PORT_POLL_DELAY
* ( 1000 * 10 );
2234 Status
= gBS
->SetTimer ( pTftpServer
->TimerEvent
,
2237 if ( !EFI_ERROR ( Status
)) {
2238 DEBUG (( DEBUG_TFTP_PORT
, "TFTP port timer started\r\n" ));
2241 // Run the TFTP server forever
2243 pTftpServer
->Udpv4Index
= -1;
2244 pTftpServer
->Udpv6Index
= -1;
2247 // Poll the network layer to create the TFTP port
2248 // for the tftp server. More than one attempt may
2249 // be necessary since it may take some time to get
2250 // the IP address and initialize the upper layers
2251 // of the network stack.
2253 if ( DIM ( pTftpServer
->TftpPort
) != pTftpServer
->Entries
) {
2256 // Wait a while before polling for a connection
2258 if ( EFI_SUCCESS
!= gBS
->CheckEvent ( pTftpServer
->TimerEvent
)) {
2259 if ( 0 == pTftpServer
->Entries
) {
2262 gBS
->WaitForEvent ( 1, &pTftpServer
->TimerEvent
, &Index
);
2266 // Poll for a network connection
2268 TftpServerSocket ( pTftpServer
,
2270 &pTftpServer
->Udpv4Index
);
2271 TftpServerSocket ( pTftpServer
,
2273 &pTftpServer
->Udpv6Index
);
2274 } while ( 0 == pTftpServer
->Entries
);
2278 // Poll the socket for activity
2281 SocketPoll ( pTftpServer
);
2284 // Normal TFTP lets the client request the retransmit by
2285 // sending another ACK for the previous packet
2287 if ( PcdGetBool ( Tftp_HighSpeed
)) {
2290 TSDT_CONNECTION_CONTEXT
* pContext
;
2291 TFTP_PACKET
* pPacket
;
2294 // High speed TFTP uses an agressive retransmit to
2295 // get the TFTP client moving again when the ACK or
2296 // previous data packet was lost.
2298 // Get the current time
2300 CurrentTime
= GetPerformanceCounter ( );
2303 // Walk the list of contexts
2305 pContext
= pTftpServer
->pContextList
;
2306 while ( NULL
!= pContext
)
2309 // Check for a transmit timeout
2311 pPacket
= pContext
->pTxHead
;
2312 if ( NULL
!= pPacket
) {
2314 // Compute the elapsed time
2316 if ( pTftpServer
->Time2
> pTftpServer
->Time1
) {
2317 ElapsedTime
= CurrentTime
- pPacket
->TxTime
;
2320 ElapsedTime
= pPacket
->TxTime
- CurrentTime
;
2322 ElapsedTime
= GetTimeInNanoSecond ( ElapsedTime
);
2325 // Determine if a retransmission is necessary
2327 if ( ElapsedTime
>= pContext
->Rtt2x
) {
2328 DEBUG (( DEBUG_WINDOW
,
2329 "0x%08x: Context TX timeout for packet 0x%08x, Window: %d\r\n",
2332 pContext
->WindowSize
));
2333 WindowTimeout ( pContext
);
2338 // Set the next context
2340 pContext
= pContext
->pNext
;
2343 } while ( DIM ( pTftpServer
->TftpPort
) == pTftpServer
->Entries
);
2344 } while ( !mbTftpServerExit
);
2347 // Done with the timer event
2349 gBS
->SetTimer ( pTftpServer
->TimerEvent
,
2353 gBS
->CloseEvent ( pTftpServer
->TimerEvent
);
2357 // Return the final status
2359 DBG_EXIT_STATUS ( Status
);