A comprehensive deep dive into DoD 4-Layer vs OSI 7-Layer models, TCP packet mechanics, connection establishment & teardown, TCP vs UDP, and kernel sysctl tuning.
The Transmission Control Protocol / Internet Protocol (TCP/IP) suite is the universal foundational communication standard that connects billions of computers, servers, cloud virtual machines, and mobile devices across the global Internet. Developed originally under the auspices of the United States Department of Defense (DARPA), TCP/IP provides reliable, end-to-end data transmission across heterogenous physical networks.
For system administrators, DevOps engineers, and students preparing for university exams or Linux certifications (RHCSA, LFCS), mastering TCP/IP goes far beyond memorizing layer names. It requires understanding how sequence numbers work, how the 3-way handshake builds a session, why the TIME_WAIT socket state protects your server, and how to tune Linux kernel network parameters via sysctl.
While academia commonly teaches the theoretical 7-layer Open Systems Interconnection (OSI) reference model, Linux and the actual Internet implement the practical 4-layer TCP/IP (DoD) architecture. Each layer encapsulates the payload of the layer above it, prepending its own protocol header:
Figure 42.1: The 4-Layer TCP/IP Protocol Stack mapped to Linux commands and utilities
| TCP/IP Layer (DoD) | Equivalent OSI Layers | Protocol Data Unit (PDU) | Core Protocols | Linux Kernel & Userspace Role |
|---|---|---|---|---|
| 4. Application | Application (7), Presentation (6), Session (5) | Data / Message | HTTP, HTTPS, SSH, DNS, FTP, SMTP, DHCP | User-space applications (Nginx, OpenSSH, BIND) interacting via BSD socket APIs (socket(), listen()). |
| 3. Transport | Transport (4) | Segment (TCP) / Datagram (UDP) | TCP, UDP, SCTP, QUIC | Kernel transport layer. Manages port-to-port multiplexing, flow control, reliability, and socket buffers. |
| 2. Internet (Network) | Network (3) | Packet | IPv4, IPv6, ICMP, IGMP, IPsec | Host-to-host packet routing, logical IP addressing, packet fragmentation, and Netfilter firewall hooks. |
| 1. Network Access (Link) | Data Link (2), Physical (1) | Frame (Layer 2) / Bits (Layer 1) | Ethernet (802.3), Wi-Fi (802.11), ARP, VLAN (802.1Q) | Device drivers, Ring buffers (DMA), NAPI polling, and physical NIC Ethernet hardware. |
The Transmission Control Protocol (defined in RFC 793) prepends a 20-byte minimum header (up to 60 bytes with options) to ensure complete transmission reliability over an unreliable IP network:
SYN (Synchronize): Requests connection establishment and synchronizes sequence numbers.ACK (Acknowledgment): Indicates that the Acknowledgment field contains valid data.FIN (Finish): Sender has finished sending data and requests connection closure.RST (Reset): Immediately aborts a malformed or rejected connection.PSH (Push): Directs the receiving kernel to push buffered data immediately to user-space without waiting for a full buffer.URG (Urgent): Flags urgent out-of-band data (rarely used today).Before an application can transmit a single HTTP request or SSH keystroke, TCP guarantees both peers agree to communicate and establish synchronized sequence numbers through the Three-Way Handshake:
Figure 42.2: TCP 3-Way Handshake, Full-Duplex Data Transfer, and 4-Step Connection Teardown
The client generates a cryptographically randomized 32-bit Initial Sequence Number (e.g., ISN = 1000). It sends a packet with the SYN flag set and payload length 0. The client enters the SYN_SENT socket state.
The server receives the SYN on a listening port (LISTEN state). It allocates memory in its SYN backlog queue, chooses its own random Sequence Number (e.g., Seq = 5000), sets Ack = 1001 (client ISN + 1), and flags both SYN and ACK. The server transitions to the SYN_RECV socket state.
The client receives the SYN-ACK, verifies Ack = 1001, and sends back an acknowledgment with Seq = 1001 and Ack = 5001 (server ISN + 1). Both endpoints now transition to the ESTABLISHED state. Application data can now flow in full duplex.
Exam Insight: Why 3 packets instead of 2? A 2-way handshake would leave the server vulnerable. If an old duplicate SYN packet delayed in router buffers arrives at the server after a session closed, a 2-way handshake would force the server to open an orphaned connection and allocate memory without confirming the client actually initiated it.
Because TCP is full-duplex (both directions transmit independently), terminating a session requires closing both channels gracefully:
FIN flag set and enters FIN_WAIT_1.ACK. The client enters FIN_WAIT_2, while the server enters CLOSE_WAIT (notifying the server application that the client has closed its send channel).close(), the server sends its own FIN packet and enters LAST_ACK.ACK, and enters the TIME_WAIT state. The server receives the ACK and immediately enters CLOSED.| Socket State | Endpoint | Description & Operational Meaning |
|---|---|---|
LISTEN |
Server | Server daemon (e.g. Nginx on port 80/443) is waiting for incoming connection requests. |
SYN_SENT |
Client | Client has sent a SYN packet and is awaiting the server's SYN-ACK. |
SYN_RECV |
Server | Server received a SYN, replied with SYN-ACK, and is waiting for client's final ACK. |
ESTABLISHED |
Both | Handshake complete; connection is active and carrying full-duplex payload. |
FIN_WAIT_1 |
Active Closer | Initiator sent its FIN packet and is waiting for ACK or simultaneous FIN. |
FIN_WAIT_2 |
Active Closer | Initiator received ACK for its FIN and is waiting for the remote peer's FIN. |
CLOSE_WAIT |
Passive Closer | Received peer's FIN; waiting for local application code to call close(). |
CLOSING |
Both | Rare state where both peers sent FIN simultaneously before receiving each other's ACKs. |
LAST_ACK |
Passive Closer | Server sent its FIN and is waiting for the client's final ACK before closing completely. |
TIME_WAIT |
Active Closer | Waits for 2*MSL (60 seconds) to ensure final ACK was received and old packets expire. |
CLOSED |
Both | No active connection; socket control block deallocated from kernel memory. |
Both TCP and UDP reside at Layer 4 (Transport Layer), but they serve opposite engineering priorities:
| Architectural Feature | Transmission Control Protocol (TCP) | User Datagram Protocol (UDP) |
|---|---|---|
| Connection Type | Connection-oriented (requires 3-way handshake before data transfer). | Connectionless (fire-and-forget datagrams, no prior handshake). |
| Reliability | Guaranteed: lost packets are automatically detected and retransmitted. | Unreliable: packets may be lost, duplicated, or arrive out of order. |
| Header Overhead | 20 to 60 bytes (complex flags, window scaling, options). | Fixed 8 bytes (Source Port, Dest Port, Length, Checksum). |
| Data Ordering | Sequenced: Kernel reorders received packets into exact byte stream. | No ordering: Datagrams arrive in whatever order routers deliver them. |
| Flow & Congestion Control | Yes: Sliding window algorithms (CUBIC, BBR) adapt to network congestion. | No: Application sends packets as fast as network interfaces can push them. |
| Transmission Speed | Slower due to handshake, ACKs, retransmissions, and flow throttling. | Extremely fast with minimal latency and near-zero CPU overhead. |
| Broadcast / Multicast | Only supports Point-to-Point Unicast (one-to-one). | Supports Unicast, Multicast, and Network Broadcast. |
| Primary Use Cases | Web (HTTP/HTTPS), File transfers (FTP/SFTP), Remote login (SSH), Emails (SMTP). | DNS queries (53), DHCP, VoIP, Zoom, Live video streaming, Online multiplayer gaming. |
Linux allows real-time tuning of the network subsystem via /proc/sys/net/ipv4/ or permanently through /etc/sysctl.d/99-network-tuning.conf. On high-throughput web servers or API gateways, standard kernel defaults can bottleneck connections:
# 1. Protect against SYN Flood DoS attacks (SYN Cookies)
sudo sysctl -w net.ipv4.tcp_syncookies=1
# 2. Increase maximum SYN backlog queue (pending handshakes)
sudo sysctl -w net.ipv4.tcp_max_syn_backlog=4096
# 3. Reduce TIME_WAIT timeout from 60 seconds to 30 seconds
sudo sysctl -w net.ipv4.tcp_fin_timeout=30
# 4. Allow kernel to reuse TIME_WAIT sockets for outgoing connections (vital for reverse proxies)
sudo sysctl -w net.ipv4.tcp_tw_reuse=1
# 5. Expand ephemeral outgoing port range
sudo sysctl -w net.ipv4.ip_local_port_range="10240 65535"
# 6. Apply all sysctl settings permanently from config file
sudo sysctl -p /etc/sysctl.d/99-network-tuning.conf
Open two terminal windows. In Terminal 1, initiate a network capture filtering on port 80 or 443:
# Terminal 1: Capture TCP packets with SYN or ACK flags set
sudo tcpdump -nn -i any 'tcp[tcpflags] & (tcp-syn|tcp-ack) != 0' -c 3
In Terminal 2, trigger an HTTP request to any web server:
# Terminal 2: Make a quick HTTP request
curl -I http://example.com
Terminal 1 will display the exact 3-way packet sequence:
10:14:20.123 IP 192.168.1.100.48292 > 93.184.216.34.80: Flags [S], seq 182749102, win 64240
10:14:20.145 IP 93.184.216.34.80 > 192.168.1.100.48292: Flags [S.], seq 382910492, ack 182749103, win 65535
10:14:20.146 IP 192.168.1.100.48292 > 93.184.216.34.80: Flags [.], ack 382910493, win 64240
# Note: [S] = SYN, [S.] = SYN-ACK, [.] = ACK
Query the Linux kernel's internal socket tables with ss:
# 1. View overall socket statistics summary
ss -s
# 2. View all currently ESTABLISHED TCP connections with process names
ss -tanp state established
# 3. View sockets currently lingering in TIME_WAIT
ss -tan state time-wait | head -n 10
# 4. View all listening sockets on TCP and UDP
ss -tulpn
CLOSE_WAIT means the remote client closed the connection and sent a FIN, but the local application on the Linux server failed to call close() on that socket descriptor. This is almost always an application-level bug (e.g. unhandled exceptions, leaking database connection pools, or missing socket cleanup in Python, Node.js, or Java code).
net.ipv4.tcp_syncookies=1). When the queue fills up, the kernel stops allocating state memory and instead encodes the connection state inside the server's sequence number. If a legitimate client returns the final ACK, the state is reconstructed mathematically on the fly.
sudo ss -tulpn | grep :80 or sudo lsof -i :80. The output explicitly reveals the process name (e.g. nginx, apache2) along with its Process ID (PID).