Summary

  • TFTP sent the initial RRQ or WRQ to port 69, then used a client-selected and a server-selected transfer identifier as the UDP ports for the accepted exchange.
  • The tuple was deliberately narrow: a wrong source TID produced error 5 without ending the valid transfer, while later fixes and options changed retransmission and throughput without turning the TID into authentication.

Port 69 was a doorbell

TFTP was designed for a small implementation. RFC 783 described a protocol that could read or write a file without directory listing, user authentication or the control vocabulary of FTP. Its economy was especially useful where little software could fit: a machine trying to obtain its first file over a local network did not need a general remote-file session.

Minimality did not remove state. It changed where state lived.

For a read, a client chooses a local transfer identifier, or TID, and sends an RRQ to the server's known TID 69. For a write it sends a WRQ the same way. The positive response does not normally continue from 69. The server chooses its own TID and uses it as the source of DATA block one or ACK block zero. From then on, every datagram carries the client and server TIDs as its destination and source ports.

The well-known port therefore identifies where a new request may be heard. It does not identify the endpoint of every accepted transfer. That distinction lets one listener start several independent exchanges without confusing their DATA and ACK packets. It also means a firewall rule that permits only packets whose source is 69 can admit the request and then break the reply.

The first reply fixed the local authority

RFC 1350 asks each side to choose its TID so immediate reuse is unlikely. Once the first positive reply arrives, the two values are fixed for that transfer. Later packets must come from the expected source TID.

This is not a claim about the identity of a person, machine or file. A TID is a short-lived transport name. It says which datagram conversation this block belongs to. It does not prove that the server was authorized, that the file was genuine or that the source address could not be forged.

Even within that narrow scope, the check matters. Suppose the initial RRQ is duplicated in the network. The server may treat the two copies as two requests and answer them from two different TIDs. The client accepts the first positive response. When the other arrives, it does not merge the conversations and does not sacrifice the one already in progress. It discards the packet, sends error 5—Unknown transfer ID—to its source, and continues the accepted transfer.

RFC 1350 calls an incorrect source port the exceptional error that does not terminate the connection. Most other errors end the exchange and are sent only as a courtesy: ERROR packets are not acknowledged or retransmitted. The wrong-TID rule is different because the error belongs to the stray conversation, not to the valid one.

One block carried the whole frontier

The original transfer rhythm was stop-and-wait. DATA blocks began at one. A sender transmitted one block, kept it available, and waited for the matching ACK before advancing. A block shorter than 512 octets marked the end; if a file length was an exact multiple, a zero-length block supplied the terminal signal.

That one-block frontier provided flow control and avoided reordering inside TFTP. The receiver could distinguish the next block from a duplicate with a small counter. The sender needed to retain only the current block. The final receiver was encouraged to wait briefly after its last ACK so that, if that ACK was lost and the last DATA reappeared, it could acknowledge it again.

The economy had a performance price. Only one block could be in flight. More importantly, the early wording let either side respond to an old duplicate by resending its current datagram. Under the wrong timing, that rule manufactured traffic.

The apprentice duplicated every reply

RFC 1123 gave the failure a memorable name: the Sorcerer's Apprentice Syndrome. A datagram is delayed. One endpoint times out and retransmits. The delayed copy later arrives, prompting another response. If each duplicate response triggers a duplicate of its own, every subsequent DATA and ACK may travel twice.

The file need not be corrupted for the protocol to fail operationally. The extra packets can worsen the congestion that caused the delay, create more timeouts and prevent the transfer from finishing. Correct bytes are not sufficient when the mechanism for proving progress creates its own load.

The required repair was asymmetric. The DATA originator must not resend the current DATA merely because it receives a duplicate ACK. Retransmission remains authorized by the relevant timeout or state transition, not by every stale signal. RFC 1350 superseded RFC 783 with the corrected behavior.

This correction reveals what the block number really did. It was not merely a counter printed beside a chunk. Together with the expected TIDs and the local timer, it decided whether a datagram advanced the exchange, repeated acknowledged state, belonged to another transfer or justified recovery.

Extensions negotiated capacity, not identity

The 512-octet lockstep was attractive for small code and poor for paths that could carry more. RFC 2347 added option negotiation to the RRQ and WRQ. Only the client could ask. A server could acknowledge requested options in an OACK, omit those it did not support, or reject an invalid value under the option's rules. It could not smuggle an unrequested option into the transfer.

For a read, ACK block zero accepts the OACK before DATA begins. For a write, DATA block one accepts it. An option not acknowledged is ignored as if it had never been proposed. The response therefore separates a client's wish from the parameters both endpoints actually agreed to use.

RFC 2348 allowed a different block size. RFC 2349 allowed the timeout interval and transfer size to be negotiated or disclosed. RFC 7440 later allowed a window of consecutive blocks before acknowledgment of the last block in that window. A window of one retained the old stop-and-wait shape; larger windows traded more outstanding state for higher throughput.

None of those changes returned the transfer to port 69. None granted access to a file or authenticated the endpoint. They enlarged the work permitted between acknowledgments while preserving the two-TID conversation and explicit agreement about its parameters.

A small protocol left large duties outside

TFTP's boundary was honest but narrow. It could tell a receiver that a packet came from the TID accepted for this transfer, that a block was new or repeated, that an option was acknowledged, and that a terminal short block had arrived. It could not say whether the requested path was safe, whether a write should be allowed, whether a boot image was signed, or whether a network device should trust what it downloaded.

Those decisions belong to surrounding systems: reachable address scope, file-root restrictions, read and write policy, artifact provenance, firewall or NAT state, and logs that connect the initial request to the dynamic tuple. Treating TID selection as authentication would ask the smallest name in the exchange to carry the largest claim.

The history of TFTP is therefore not just a story of an old, simple transfer tool. It is a compact demonstration that a well-known port can authorize discovery without owning the resulting conversation, that an error may need to protect one exchange from another rather than terminate both, and that performance extensions are safe only when the agreement and identity boundaries survive them.

Sources and limits

The original design is documented in RFC 783, the host requirements and duplicate fix in RFC 1123, and the revised base protocol in RFC 1350. Later negotiation is defined by RFC 2347, RFC 2348, RFC 2349 and RFC 7440. The IANA service registry records port 69. These sources do not measure 2026 deployment and do not make a TID proof of identity, authorization, confidentiality or file integrity.