Summary

  • TCP simultaneous open lets two endpoints issue active opens toward the same socket pair. Their bare SYNs cross, both enter SYN-RECEIVED, and crossed SYN-ACKs establish one connection—not two competing connections.
  • The mechanism works because each endpoint proves two local facts: it has received the peer's current initial sequence number, and the peer has acknowledged its own. No permanent caller/listener hierarchy is needed.
  • NATs later learned the ordinary client-server packet order as if it were the whole TCP state machine. RFC 5382 had to require them to accept an inbound SYN after an outbound SYN when policy already permitted the connection.

The handshake diagram hid a social assumption

Most explanations of TCP begin with a line moving from a client to a server: SYN, then SYN-ACK, then ACK. The drawing is useful, but it encourages a stronger conclusion than the protocol makes. One side appears born to initiate; the other appears born to wait.

RFC 793 described passive and active OPEN calls, yet it also said that two processes issuing active OPENs to each other at the same time would be correctly connected. The flexibility was important for distributed components acting asynchronously. Neither process needed to win a race and become the official client before TCP could proceed.

The packet sequence was not a collision-recovery trick added later. Simultaneous initiation was part of the original connection-synchronisation design. The ordinary diagram showed one route through the state machine, not a constitutional allocation of roles.

Two SYNs crossed without creating two connections

Suppose peer A chooses initial sequence number 100 and peer B chooses 300. A sends <SEQ=100><SYN> and enters SYN-SENT. B does the same with <SEQ=300><SYN>. The two segments cross in the network.

When A receives B's bare SYN, it does not reject the packet because A already sent one. It records B's initial receive sequence, enters SYN-RECEIVED, and sends a SYN-ACK acknowledging 301. B independently does the mirror operation and acknowledges 101. When the SYN-ACKs cross, each side sees confirmation that the other received its starting number and can enter ESTABLISHED.

The current consolidated specification, RFC 9293, preserves the corrected trace in Figure 7. Both peers travel CLOSEDSYN-SENTSYN-RECEIVEDESTABLISHED. The visual packet order differs from the textbook active/passive handshake, but the logical proof is the same: each side announces a sequence origin, receives the other's, and gets its own acknowledged.

The number after SYN carried the proof

A SYN occupies one position in TCP sequence space. If A sends SYN at 100, B acknowledges 101. The acknowledgment does not merely say “a packet arrived”; it says which sequenced control the receiver is now beyond. An empty ACK itself occupies no sequence space, so TCP does not fall into an infinite obligation to acknowledge acknowledgments.

That arithmetic gives simultaneous open its discipline. The peers do not merge sequence-number spaces. A continues to number its outgoing stream from its own initial value, while B does the same from another. RFC 6528 later strengthened initial sequence selection with a secret pseudorandom-function component tied to the connection four-tuple. Symmetric initiative never meant symmetric or predictable numbers.

The state therefore comes from evidence each endpoint can validate locally. There is no central handshake referee, and no title such as “server” that makes a SYN true. Socket identity, sequence arithmetic and acknowledged history are sufficient.

One socket pair meant one connection

The most tempting implementation mistake was to treat A's attempt and B's attempt as separate objects merely because two applications called connect. RFC 1122 addressed the surprise directly: simultaneous attempts generate one connection instead of two, and this was intentional.

From A's viewpoint the connection is local A socket plus remote B socket. From B's viewpoint the same pair is reversed. The four values still describe one association. Creating two connections would not preserve more information; it would duplicate one identity and leave applications to guess which duplicate should carry data.

This is a useful example of a protocol refusing to elevate local procedure into global reality. Two API calls occurred, but the network object is defined by the shared endpoint pair and synchronised sequence state. A local implementation may have several pending records, yet it must reconcile them before presenting the established association.

The same state could remember two different histories

TCP could not discard history once both peers reached SYN-RECEIVED. RFC 1122 and RFC 9293 require an implementation to remember whether that state followed a passive OPEN or an active OPEN. The distinction matters when a reset arrives or an old duplicate SYN imitates a new attempt.

An endpoint that passively listened may return to listening after a valid reset during synchronisation. An endpoint that actively opened has a different application expectation. The state label alone is therefore not the whole state; the path into it controls recovery.

RFC 793 already warned that an old duplicate SYN can look like simultaneous initiation. The design does not solve this by banning crossed SYNs. It uses acknowledgments, current sequence expectations and reset validation to separate a live rendezvous from debris belonging to an earlier incarnation of the same socket pair.

The lesson is subtle. Simplifying the state machine by erasing provenance can make implementation diagrams shorter while making the protocol unable to distinguish two events with different consequences.

A middlebox learned only the popular story

For years, the asymmetry in the textbook diagram was harmless because the endpoints owned the state machine. Network address translation changed that. A NAT creates a mapping after an outbound SYN and commonly expects the next inbound packet to be SYN-ACK. During simultaneous open, the next inbound packet is another bare SYN.

RFC 5382 documented NATs that blocked that SYN as unsolicited and others that mishandled the outgoing SYN-ACK. Both endpoints were following TCP, and the NAT might already permit traffic for the mapping, yet the connection failed because the intermediary had implemented one narrative rather than all valid transitions.

The distinction between policy and parsing is essential. A firewall may deny a connection under its filtering policy. But once policy permits a connection in progress, treating a standards-defined transition as malformed is not stricter security; it is protocol narrowing hidden inside state tracking.

RFC 5382 therefore required NATs to handle simultaneous open and other valid TCP sequences for permitted connections. The requirement did not ask the NAT to surrender policy. It asked the NAT not to confuse its most common observation with TCP's entire grammar.

Six seconds exposed the cost of uncertainty

The hardest case arrives when the remote SYN reaches a NAT before the local outbound SYN has created the mapping. The packet appears unsolicited, but delay or loose clock coordination may soon reveal it as half of a simultaneous open. Returning a reset or hard ICMP error immediately can force the remote endpoint to abandon a connection that would otherwise succeed.

RFC 5382 chose a compromise. A NAT should wait at least six seconds before answering that inbound SYN. If the corresponding outbound SYN appears during the interval, the NAT silently drops the early packet and allows retransmission to arrive after the mapping exists. If no matching attempt appears, it can report the error, subject to security policy.

This is not a magical handshake timer. It is an explicit price for incomplete knowledge. Immediate rejection gives faster errors but destroys some valid rendezvous attempts; waiting preserves optionality but delays certainty and holds state. The middlebox cannot know the future, so the specification makes the tradeoff visible instead of pretending ambiguity does not exist.

Peer-to-peer software kept the old symmetry useful

Simultaneous open gained new importance when two endpoints sat behind translators and neither could simply accept an unsolicited inbound connection. Each could create outbound state toward the other; if the timing and mappings aligned, their SYNs could cross through the prepared paths.

RFC 6544 made this an explicit ICE TCP candidate type alongside active and passive candidates. An S-O candidate says each agent will attempt to open toward its peer. The document does not treat that path as universally reliable. It recommends gathering multiple candidate types because operating systems, NATs, firewalls and access networks vary.

That qualification prevents a common historical exaggeration. The existence of simultaneous open did not make peer-to-peer TCP universally traversable. It supplied a valid endpoint mechanism. Success still depended on address discovery, port mapping, timing, filtering behaviour and API support. Relays remained necessary when direct paths failed.

Standards validity and deployment reach are different claims. A mechanism can be mandatory in TCP and still be operationally unavailable because surrounding systems narrow or conceal it.

The durable design was symmetry with evidence

Simultaneous open is a small feature, but it captures an important Internet design choice. The shared layer specifies the minimum facts required for interoperability: one socket pair, two fresh sequence origins, acknowledgments that prove receipt, and state transitions both endpoints can verify. It does not require a permanent authority to declare which peer is caller.

Future decisions remain local. An application may choose active, passive or simultaneous candidates. A firewall may choose whether a path is allowed. An implementation may expose richer or poorer APIs. But a local choice should not be smuggled into the common layer as a false statement that another valid peer transition cannot exist.

The later NAT problem was not that endpoints had too much autonomy. It was that the intermediary silently recast a popular convention—outbound caller, inbound responder—as protocol law. RFC 5382 repaired the boundary by separating permission from faithful state tracking.

When both ends called at once, TCP did not elect a winner. It asked each side to prove what it had sent and received. One connection emerged because verifiable shared state mattered more than the social roles suggested by a diagram.

Sources and evidentiary limits

RFC 793 establishes original simultaneous initiation. RFC 1122 makes support mandatory and clarifies the one-connection outcome. RFC 5382 documents NAT failures and behavioural requirements. RFC 6528 supplies the secure ISN context. RFC 6544 shows later ICE TCP use. RFC 9293 is the current consolidated TCP specification.

These documents do not establish present market share, every OS API behaviour or compliance by every NAT. Packet loss and retransmission may also change an observed trace without changing the logical state transition.