🚩 02-02: TCP Flags, Sequence & Acknowledgment Numbers¶
📌 Introduction¶
Every TCP segment carries a header full of control information in addition to any actual data. Two of the most important pieces are the control flags (single-bit switches that tell the receiver what kind of segment this is) and the sequence/acknowledgment numbers (which track exactly which bytes have been sent and received). This lesson covers both in detail, building on the three-way handshake from 02-01: TCP Fundamentals & Handshake.
🚩 The TCP Control Flags¶
The TCP header includes a set of 1-bit flags. Multiple flags can be set simultaneously in the same segment (you already saw this with SYN-ACK in the handshake).
| Flag | Full Name | Meaning |
|---|---|---|
| SYN | Synchronize | Requests a new connection and proposes an initial sequence number |
| ACK | Acknowledge | Confirms receipt of data; the acknowledgment number field is valid |
| FIN | Finish | Sender has no more data to send — begins graceful connection close |
| RST | Reset | Abruptly terminates a connection, usually due to an error or a connection to a port nobody is listening on |
| PSH | Push | Tells the receiver to immediately deliver buffered data to the application, rather than waiting to accumulate more |
| URG | Urgent | Marks part of the segment's data as "urgent," to be prioritized (rarely used in modern applications) |
Flag Deep-Dive¶
SYN — Only set during connection establishment (the first two steps of the handshake). Seeing a bare SYN segment (without ACK) arrive is always the start of a new connection attempt.
ACK — Set on almost every segment after the handshake completes; it's how the receiver continuously confirms "I've received everything up through byte N."
FIN — Graceful closure. Because TCP connections are full-duplex (data flows both directions independently), a proper close involves each side sending its own FIN when it's done sending — a connection can be "half-closed," with one side still sending data after the other has finished.
RST 💡 Security-relevant flag: RST immediately kills a connection with no negotiation — no need to wait for the other side to agree. This makes it a common target for TCP Reset Attacks: if an attacker can guess or sniff the right sequence number and spoof a RST segment claiming to be from one side of an active connection, the other side's OS will simply tear the connection down, believing it to be legitimate. This is a real, historically significant censorship and disruption technique.
PSH — Normally, TCP might buffer small chunks of data waiting to send a more efficient, larger segment. PSH tells the receiving TCP stack "don't wait — hand this to the application right now." Interactive protocols (like SSH or Telnet, where every keystroke matters) rely on this to avoid perceptible lag.
URG — Historically used to mark priority data (e.g., signaling in Telnet), but rarely used in modern protocol design; largely a legacy field today.
🔢 Sequence & Acknowledgment Numbers¶
TCP treats all transmitted data as one continuous stream of bytes, and every single byte in that stream has an implicit position number. The sequence number field in the TCP header identifies the position of the first byte of data in that particular segment.
How It Works¶
- The sequence number (
seq) says: "The data in this segment starts at byte positionseqin my outgoing stream." - The acknowledgment number (
ack) says: "I have successfully received everything up through byteack - 1, so please send me starting from byteacknext."
This is why, during the handshake, the SYN-ACK's ack value was X + 1 — even though no actual data was sent, the SYN flag itself "consumes" one sequence number, so the next expected byte is X + 1.
Worked Numeric Example¶
Let's say a client and server have already completed the handshake with initial sequence numbers X = 1000 (client) and Y = 5000 (server). Now the client sends some actual HTTP data.
Segment 1 — Client sends 100 bytes of data:
seq = 1001 (starts right after the SYN's sequence number)
ack = 5001 (acknowledging the server's SYN)
[100 bytes of HTTP request data]
Segment 2 — Server acknowledges and replies with 300 bytes of data:
seq = 5001 (server's own stream continues from its SYN)
ack = 1101 (1001 + 100 bytes received = next expected byte)
[300 bytes of HTTP response data]
Segment 3 — Client acknowledges the server's data:
seq = 1101 (client's stream continues where it left off)
ack = 5301 (5001 + 300 bytes received = next expected byte)
[no data — this could be a pure ACK]
💡 The pattern: ack = (peer's last seq) + (number of bytes peer just sent). Each side's sequence number only advances when that side sends data (or consumes a SYN/FIN); each side's acknowledgment number only advances when the other side's data is received.
🧮 Why Reliability Falls Out of This Naturally¶
Because every byte has a position, the receiving TCP stack can:
- Detect gaps — if it receives
seq = 1101after already having everything up to1201, it knows something arrived out of order and can reorder before handing data to the application. - Detect loss — if the sender doesn't receive an ACK for data within a timeout window, it assumes the segment was lost and retransmits it.
- Detect duplicates — if the same sequence range arrives twice (e.g., because an ACK was itself lost and the sender retransmitted unnecessarily), the receiver can discard the duplicate.
None of this requires any central coordination — it's entirely encoded in the numbers each side attaches to its own segments.
⚠️ Security Relevance: Predictable Sequence Numbers¶
For an attacker to inject forged data into an active TCP connection (a session hijacking attack, covered in Module 04) or spoof a convincing RST or data segment, they need to know — or correctly guess — the current sequence number the receiving side expects. If sequence numbers were predictable (as they infamously were in some older operating systems), an attacker could inject or reset connections without ever having sniffed the actual traffic.
💡 This is exactly why modern operating systems use randomized initial sequence numbers and randomized source ports as a defensive measure — making sequence numbers hard to guess is a real, load-bearing security control, not just an implementation detail.
📌 Key Takeaways¶
- TCP flags — SYN, ACK, FIN, RST, PSH, URG — tell the receiver what kind of segment it's looking at, and multiple flags can combine (e.g., SYN-ACK).
- RST abruptly tears down a connection with no negotiation, making it the basis of TCP Reset Attacks when an attacker can spoof one convincingly.
- PSH forces immediate delivery to the application instead of buffering — important for interactive, low-latency protocols.
- The sequence number marks the position of the first byte of a segment's data in the sender's byte stream.
- The acknowledgment number tells the peer "I've received everything before this position, send me this next."
- Sequence/ack numbers, not any special "delivery confirmation" mechanism, are what let TCP detect loss, gaps, and duplicates and guarantee ordered, reliable delivery.
- Predictable sequence numbers are a serious vulnerability — modern systems randomize initial sequence numbers specifically to prevent session hijacking and spoofing attacks.