Introduction
Trivial File Transfer Protocol (TFTP) is a simple lockstep File Transfer Protocol which allows a client to get a file from or put a file onto a remote host.
TFTP is designed to be small and easy to implement. Therefore, It's a nice protocol to study about networks.
Because of simplicity, TFTP use very small memory footprint. Ideal for embedded systems.
Today, TFTP is virtually unused for Internet transfers, Generally only used on local area networks (LAN)
It is implemented on top of the UDP/IP protocols using well-known port number 69.
Request
The client first requests the server to read or write some data. At well known port 69
.
Request
frame containing the filename, transfer mode and optionally any negotiated option. (We will not discuss those options in this tutorial)
#![allow(unused)] fn main() { pub struct Resource { pub filename: Text, pub mode: Text, } }
Text
is nul-terminated modified version of ascii string.
Data Transfer
Based on request, The server or client sends the data frame to the other endpoint. Data frame contains a block
number and payload.
The payload is sent in a fixed length buffer of 512
bytes by default or the number specified in the block size negotiated option.
The last data block must be less than the negotiated or default blocksize (which is 512
) to signal the end of the transfer.
What happens when the last block is exact block sized ?
If that happens, the endpoint sends a data frame of 0
byte to signal the end of the transfer.
Initially, block
number is 1
. On each transfer, the block number is incremented by one.
What happens when the block number is exhausted ?
According to wikipedia, The original protocol has a transfer file size limit of 512 * 65535
blocks = 32
MB, Today most servers and clients support block number roll-over (block counter going back to 0 or 1 after 65535) which gives an essentially unlimited transfer file size.
If the response is positive, the server create a new socket and all transfers are performed using this new socket.
This significantly simplifies the implementation of overall protocol. As we don't need to track each socket.
Acknowledgement
For various reasons, there might be some packet lost. The sender detect packet loss using a timer and retransmit missing packets. This is known as acknowledgement.
Acknowledgement indicate that the data has been received.
An Endpoint must send an acknowledgement packet for each data packet received. Acknowledgement number must be the same as the block number of the data packet.
It guarantees that all old packets are received and prevents network congestion.
Positive response to a write request is an acknowledgment packet, in this special case the block number will be zero.
Error
Any errors cause termination of the connection. This packet is not acknowledged, and not retransmitted.
#![allow(unused)] fn main() { #[repr(u16)] pub enum ErrorCode { NotDefined, FileNotFound, AccessViolation, DiskFull, IllegalOperation, UnknownTransferID, FileAlreadyExists, NoSuchUser, } }
An error frame contains an error code and reason.
#![allow(unused)] fn main() { pub struct Error { pub code: ErrorCode, pub reason: Text, } }
Frame
In networking, a frame is a fundamental unit of data transmission between two endpoint.
#![allow(unused)] fn main() { #[repr(u16)] pub enum Frame { Read(Resource) = 1, Write(Resource) = 2, Data { block: u16, bytes: Bytes } = 3, Acknowledge(u16) = 4, Error(Error) = 5, } }
Here is the binary format of each packet:
The first 2 bytes of each packet type are known as the opcode
or discriminant, It is just a number that identifies the type of the frame.
Opcode | Frame Type |
---|---|
1 | Read Request |
2 | Write Request |
3 | Data |
4 | Acknowledgment |
5 | Error |
User Datagram Protocol
TFTP uses UDP to transfer it's data.
Protocol Stack |
---|
Local Medium |
Internet Protocol |
UDP |
TFTP Frame |
The UDP header consists of the following fields.
Field | Type |
---|---|
Source Port | u16 |
Destination Port | u16 |
Length | u16 |
Checksum | u16 |
-
Port number: Port numbers as a way to identify a service. Suppose you want to send a mail to your friend, his hotel address is "Main Street, Anytown, USA" and room number is "123", You can think street address as a IP address and room number as a Port number.
-
Length: Length field defines the length of the message in bytes. Maximum value of
u16
bit integer is 65535. However, the actual length of the message is 65,507 bytes* (65,535 − 8 bytes UDP header − 20 bytes IP header) -
Checksum: The checksum field may be used for error-checking of the header and data. This field is optional in IPv4, and mandatory in IPv6.