TCP Segment Format Every Field in the TCP Header

 

TCP Segment Format
Every Field in the TCP Header — Chapter 61, Part 2

← Chapter 61 Index  |  Part 2 of 3

What Is a TCP Segment?

TCP is a stream protocol — from the application’s view, it looks like a continuous pipe of bytes. But underneath, TCP breaks that stream into chunks called segments before handing them to IP. Each segment has a header (control information) followed by optional data (the actual payload bytes).

Understanding the segment format is critical because every TCP behaviour — connection setup, data transfer, error handling, flow control, congestion control — is controlled by fields in this header. When you use Wireshark or an Ellisys analyser, these are the exact bytes you see.

TCP Header Layout

The TCP header is at minimum 20 bytes. With options, it can be up to 60 bytes.

Bit 0 Bit 15 | Bit 16 Bit 31
Source Port (16 bits) Destination Port (16 bits)
Sequence Number (32 bits)
Acknowledgement Number (32 bits)
Hdr Len
(4 bits)
Reserved
(4 bits)
Control Bits
(8 bits: CWR ECE URG ACK PSH RST SYN FIN)
Window Size (16 bits)
TCP Checksum (16 bits) Urgent Pointer (16 bits)
Options (0 – 40 bytes, variable)
Data (payload — 0 or more bytes)

Note: The first 20 bytes (5 rows above the Options row) are always present. The Options field makes the header variable-length — the Header Length field tells the receiver where the data actually starts.

Field-by-Field Breakdown

1. Source Port (16 bits)

The port number of the sender. For a client, this is typically an ephemeral port (automatically assigned by the OS, usually in the range 32768–60999 on Linux). For a server response, this would be a well-known port like 80 (HTTP) or 443 (HTTPS).

2. Destination Port (16 bits)

The port number of the receiver. When a client connects to a web server, the destination port is 80 or 443. Together with the source port and IP addresses, the four-tuple (src IP, src port, dst IP, dst port) uniquely identifies a TCP connection.

3. Sequence Number (32 bits)

This field tells the receiver the position of the first byte of data in this segment within the overall byte stream. TCP assigns every byte a unique sequence number.

For example, if this segment carries bytes 1001 to 1500, the sequence number is 1001. During the initial SYN, the sequence number is the random Initial Sequence Number (ISN) chosen by the sender — not yet data, but the starting point.

4. Acknowledgement Number (32 bits)

This field is only meaningful when the ACK control bit is set. It contains the sequence number of the next byte the receiver expects. It is also the “up to here I have received everything” guarantee. If the ACK number is 1501, it means “I got everything up to byte 1500, now send me starting from 1501.”

5. Header Length (4 bits)

Counts the header length in units of 32-bit (4-byte) words. With 4 bits you can represent 0 to 15, meaning the max header length is 15 × 4 = 60 bytes. The minimum is 5 × 4 = 20 bytes (no options). This field lets the receiving TCP know where the data starts, since the options field varies in size.

6. Reserved (4 bits)

These 4 bits are reserved for future use and must be set to zero. Any packet with non-zero reserved bits was historically dropped, though modern TCP implementations are more lenient.

7. Control Bits (Flags) — 8 bits

This is the most important part of the header. Each bit is an independent flag that changes the meaning of the segment. Multiple flags can be set at the same time.

Flag Full Name What It Means
CWR Congestion Window Reduced Sender has reduced its sending rate in response to a congestion signal. Part of the ECN algorithm.
ECE ECN Echo Receiver is echoing back a congestion signal from the network (ECN-capable). Requires kernel support, enabled via /proc/sys/net/ipv4/tcp_ecn.
URG Urgent When set, the Urgent Pointer field is valid. Signals that part of the data should be prioritised. Rarely used in modern applications.
ACK Acknowledgement The Acknowledgement Number field is valid. This bit is set in almost every segment after the initial SYN.
PSH Push Tells the receiving TCP to deliver this data to the application immediately without waiting to fill a buffer. Useful for interactive applications like telnet.
RST Reset Abruptly terminates the connection. Sent when an error occurs, or when a packet arrives for a port that has no listener.
SYN Synchronise Used during connection establishment (the 3-way handshake). Contains the initial sequence number for this direction.
FIN Finish The sender has no more data to send. Used to gracefully close one direction of the connection.

Common Flag Combinations

Flags Set When It Appears
SYN First packet from client during 3-way handshake
SYN + ACK Server’s reply during 3-way handshake (acknowledges client SYN, sends its own SYN)
ACK All normal data and acknowledgement segments after connection is established
FIN + ACK Closing a connection — “I am done sending, and I acknowledge your last data”
RST + ACK Aborting a connection abruptly — seen when connecting to a closed port

8. Window Size (16 bits)

This field implements flow control. When a receiver sends an ACK, it also tells the sender how many more bytes it can accept right now (the size of its receive buffer). The sender must not send more than this many bytes beyond the last acknowledged byte.

A window size of 0 means “stop sending — I am full”. This is called a zero window condition. TCP Window Scale options can extend this beyond 65535 bytes for high-speed links.

9. TCP Checksum (16 bits)

A 16-bit error-detection code covering the TCP header, TCP data, and a special 12-byte structure called the TCP pseudoheader.

The pseudoheader is not transmitted — it is constructed in memory for checksum calculation only. It contains:

Source IP Address 4 bytes
Destination IP Address 4 bytes
Zero byte + Protocol (value 6 for TCP) 2 bytes
TCP Segment Length 2 bytes

Why include IP addresses in a TCP checksum? To catch misdelivered packets — if IP routes a packet to the wrong host, the checksum will fail because the destination IP in the pseudoheader will not match what was expected. UDP uses an identical pseudoheader scheme.

10. Urgent Pointer (16 bits)

Only meaningful when the URG flag is set. Points to the last byte of “urgent data” in the segment — data that should be delivered out-of-band to the application as fast as possible, bypassing normal buffering. In practice, URG / urgent data is rarely used in modern software and is considered a legacy feature.

11. Options (0–40 bytes)

Variable-length field for negotiating optional features. Common options include:

  • MSS (Maximum Segment Size) — each side declares the largest segment it will accept. Exchanged during SYN.
  • Window Scale — extends the 16-bit window size field by a scale factor, enabling larger windows for fast links.
  • SACK (Selective ACK) — allows the receiver to acknowledge non-contiguous blocks, reducing retransmission.
  • Timestamps — used to calculate round-trip time and protect against old duplicate packets (PAWS).

Reading TCP Flags in Raw Sockets (C Code)

In a raw socket or packet capture, you can read the TCP flags directly from the header struct. This is useful for tools like a simple port scanner or packet analyser.

#include <stdio.h>
#include <stdint.h>
#include <netinet/tcp.h>   /* struct tcphdr */
#include <netinet/ip.h>    /* struct iphdr */
#include <sys/socket.h>
#include <netpacket/packet.h>
#include <net/ethernet.h>
#include <arpa/inet.h>
#include <string.h>

/* Pretty-print the TCP flags from a tcphdr */
void print_tcp_flags(const struct tcphdr *tcp)
{
    printf("TCP Flags: ");
    if (tcp->syn) printf("SYN ");
    if (tcp->ack) printf("ACK ");
    if (tcp->fin) printf("FIN ");
    if (tcp->rst) printf("RST ");
    if (tcp->psh) printf("PSH ");
    if (tcp->urg) printf("URG ");
    printf("\n");
}

/* Print a summary of a TCP segment */
void print_tcp_segment(const struct tcphdr *tcp)
{
    printf("Src Port  : %u\n", ntohs(tcp->source));
    printf("Dst Port  : %u\n", ntohs(tcp->dest));
    printf("Seq #     : %u\n", ntohl(tcp->seq));
    printf("Ack #     : %u\n", ntohl(tcp->ack_seq));
    printf("Header Len: %u bytes\n", tcp->doff * 4);
    printf("Window    : %u\n", ntohs(tcp->window));
    printf("Checksum  : 0x%04x\n", ntohs(tcp->check));
    print_tcp_flags(tcp);
}

int main(void)
{
    /* Raw socket to capture all IP packets on this machine */
    int sockfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_IP));
    if (sockfd == -1) {
        perror("socket (need root)");
        return 1;
    }

    unsigned char buffer[65536];
    struct iphdr  *ip;
    struct tcphdr *tcp;

    printf("Listening for TCP segments (Ctrl-C to stop)...\n\n");

    while (1) {
        ssize_t n = recv(sockfd, buffer, sizeof(buffer), 0);
        if (n < 0) break;

        ip = (struct iphdr *)(buffer + 14);  /* skip Ethernet header */

        /* Only process TCP packets */
        if (ip->protocol != 6) continue;

        int ip_hlen = ip->ihl * 4;
        tcp = (struct tcphdr *)((unsigned char *)ip + ip_hlen);

        printf("--- TCP Segment ---\n");
        printf("IP: %s ", inet_ntoa(*(struct in_addr *)&ip->saddr));
        printf("→ %s\n",  inet_ntoa(*(struct in_addr *)&ip->daddr));
        print_tcp_segment(tcp);
        printf("\n");
    }

    return 0;
}

Compile and run (requires root):

gcc -o tcp_sniff tcp_sniff.c
sudo ./tcp_sniff

ECN: Explicit Congestion Notification

CWR and ECE are used together as part of ECN (RFC 3168). ECN is a way to signal congestion without dropping packets. Normally, when a router is congested, it simply drops packets. With ECN, the router can mark packets instead, and the receiver echoes this back to the sender using the ECE flag. The sender then reduces its rate and confirms with CWR.

Check and enable ECN on Linux:

# Check current setting (0 = disabled, 1 = enabled, 2 = enabled for outgoing only)
cat /proc/sys/net/ipv4/tcp_ecn

# Enable ECN
echo 1 | sudo tee /proc/sys/net/ipv4/tcp_ecn

Interview Questions and Answers

Q1. How many bytes is the minimum TCP header? What makes it variable?
The minimum TCP header is 20 bytes (when there are no options). The Options field can add up to 40 bytes, making the maximum header size 60 bytes. The Header Length field (4 bits, counts 32-bit words) tells the receiver how long the header actually is.
Q2. What is the TCP pseudoheader and why is it used in the checksum?
The pseudoheader is a 12-byte virtual structure (source IP, dest IP, protocol number, TCP length) that is constructed in memory and included in the checksum calculation but not transmitted. Its purpose is to detect misdelivered packets — if IP accidentally routes a packet to the wrong host or passes it to the wrong upper-layer protocol, the checksum will fail because the pseudoheader values will not match.
Q3. What is the difference between RST and FIN?
FIN is a graceful close — it signals “I have finished sending data” but the connection remains half-open until both sides send FIN. Data already in flight is still delivered. RST is an abrupt close — it immediately tears down the connection, any unread data is discarded, and the peer gets a connection reset error. RST is also sent when a packet arrives for a port with no listener.
Q4. What does the Window Size field control?
Flow control. The receiver advertises how many bytes of buffer space it has available. The sender is not allowed to send more unacknowledged bytes than this window allows. A window of 0 means stop. This prevents a fast sender from overwhelming a slow receiver.
Q5. Can two flags be set at the same time? Give an example.
Yes. During the TCP 3-way handshake, the server’s response has both SYN and ACK set. SYN means “here is my initial sequence number” and ACK means “I acknowledge your SYN”. Similarly, FIN + ACK is common when closing a connection — “I am done sending AND I acknowledge what you just sent.”
Q6. What is the purpose of the PSH flag?
PSH (push) tells the receiving TCP stack to deliver the buffered data to the application immediately, without waiting to accumulate more data. It is commonly set on the last segment of a write, so interactive applications (SSH, telnet) get responses without waiting for a buffer to fill.
Q7. What is ECN and which TCP flags are involved?
ECN (Explicit Congestion Notification) allows network routers to signal congestion by marking packets instead of dropping them. The ECE flag is set by the receiver to echo the congestion signal back to the sender. The sender then reduces its window and sets the CWR flag to confirm it has slowed down. ECN requires support from both endpoints and the network routers, and is enabled on Linux via /proc/sys/net/ipv4/tcp_ecn.

Leave a Reply

Your email address will not be published. Required fields are marked *