Every tutorial on network layers starts with a boring table: "Layer 7 is Application, Layer 6 is Presentation..." and you forget it by next week. This guide is different. We'll learn each layer by doing — capturing packets, debugging real problems, and understanding what happens byte-by-byte when you type curl https://api.example.com.

The Practical Model: TCP/IP (Not OSI)

The OSI model has 7 layers but the real internet uses the TCP/IP model with 4 layers. Every packet you've ever sent uses TCP/IP, not OSI. Let's focus on what actually matters:

TCP/IP Model (What the Internet Actually Uses)
Layer 4: ApplicationHTTP, HTTPS, DNS, gRPC, SMTP, SSH, WebSocket — what your code talks to
Layer 3: TransportTCP (reliable, ordered) or UDP (fast, fire-and-forget) — how data is delivered
Layer 2: Internet (Network)IP addresses, routing, packets crossing networks — where data goes
Layer 1: Network Access (Link + Physical)Ethernet, WiFi, MAC addresses, physical cables — the actual wire/radio

What Happens When You curl a URL?

Let's trace a real request through every layer. This is the single most useful mental model for networking:

Anatomy of: curl https://api.example.com/users
Your Machine(curl)
Network(Internet)
Server(api.example.com)
1 DNS: Resolve api.example.com → 93.184.216.34
2 TCP: 3-way handshake (SYN → SYN-ACK → ACK)
3 TLS: Handshake (certs, key exchange, cipher suite)
4 HTTP: GET /users (encrypted inside TLS)
5 HTTP: 200 OK + JSON body (encrypted)
6 TCP: FIN → ACK (connection close)
# See it yourself! Use curl with verbose output:
curl -v https://api.example.com/users 2>&1

# Output breakdown:
# * Trying 93.184.216.34:443...              ← Layer 2: IP resolution
# * Connected to api.example.com             ← Layer 3: TCP connection
# * SSL connection using TLSv1.3             ← Layer 4: TLS handshake
# > GET /users HTTP/2                        ← Layer 4: HTTP request
# < HTTP/2 200                               ← Layer 4: HTTP response

Layer 1: Network Access (The Physical Wire)

This is the only layer you can physically touch. It handles getting bits from one device to the next device on the same local network.

Real-World: Ethernet & MAC Addresses

# See your network interfaces and MAC addresses
ip link show          # Linux
ifconfig              # macOS
ipconfig /all         # Windows

# Example output:
# eth0: 00:1A:2B:3C:4D:5E  ← 48-bit MAC address (hardware address)
# wlan0: AA:BB:CC:DD:EE:FF  ← WiFi adapter MAC

# See which MAC addresses your machine has talked to recently (ARP table)
arp -a
# ? (192.168.1.1) at 00:11:22:33:44:55 on en0  ← Your router's MAC
# ? (192.168.1.42) at AA:BB:CC:DD:EE:FF on en0  ← Another device

# ARP (Address Resolution Protocol) maps IP → MAC
# "Who has 192.168.1.1? Tell 192.168.1.100"
# "192.168.1.1 is at 00:11:22:33:44:55"

When you'll debug this layer:

  • "My server can't reach the database on the same subnet" → Check if ARP resolution works
  • "Network is slow on this machine" → Check for duplex mismatch, cable issues
  • "VMs can't talk to each other" → Check virtual switch / bridge configuration

Layer 2: Internet Layer (IP — Getting Packets Across Networks)

Layer 1 handles the local network. Layer 2 (IP) handles getting packets from your network to any other network in the world via routing.

Real-World: IP Addresses & Routing

# See your IP addresses
ip addr show          # Linux
ifconfig              # macOS
ipconfig              # Windows

# Public vs Private IPs:
# Private (local network only):
#   10.0.0.0/8        ← Large enterprise networks
#   172.16.0.0/12     ← Medium networks
#   192.168.0.0/16    ← Home/small office (your WiFi is probably here)
# Public (internet-routable):
#   Everything else (e.g., 93.184.216.34)

# What's my public IP?
curl -s https://ifconfig.me
# Output: 203.0.113.42

# Trace the route from your machine to Google's servers
traceroute google.com    # macOS/Linux
tracert google.com       # Windows

# Output:
#  1  192.168.1.1      0.5ms   ← Your home router
#  2  10.0.0.1         2.1ms   ← ISP's first router
#  3  72.14.209.81     5.3ms   ← ISP backbone
#  4  108.170.252.1    8.7ms   ← Google's edge
#  5  142.250.80.46   10.2ms   ← Google's server
# Each hop is a router making a forwarding decision based on the destination IP

# See your machine's routing table
ip route show         # Linux
netstat -rn           # macOS
route print           # Windows

# Key routes:
# default via 192.168.1.1    ← Everything goes to router (gateway)
# 192.168.1.0/24 dev eth0    ← Local network (no routing needed)
# 10.0.0.0/8 via 10.0.0.1   ← VPN or internal network route
How IP Routing Works
💻Your PC192.168.1.100
📶Router192.168.1.1
🌍ISPMultiple hops
🌐InternetBGP routing
🖥Server93.184.216.34

When you'll debug this layer:

  • "Can't reach external services" → Check default route, DNS resolution
  • "High latency to a specific service" → traceroute to find which hop is slow
  • "Packets getting dropped" → ping with different sizes, check MTU
  • "VPN connected but can't reach internal services" → Check route table conflicts

Layer 3: Transport (TCP & UDP — How Data Gets Delivered)

IP gets packets to the right machine. Transport protocols get data to the right application on that machine, using ports.

TCP vs UDP — The Two Transport Protocols
📦 TCP (Transmission Control Protocol)
🤝Connection-oriented (3-way handshake)
Guaranteed delivery (retransmits lost packets)
🔢Ordered (packets arrive in sequence)
📊Flow control (prevents overwhelming receiver)
🐢Higher overhead (headers + handshake)
🎯HTTP, HTTPS, SSH, SMTP, databases
VS
⚡ UDP (User Datagram Protocol)
📨Connectionless (fire and forget)
No delivery guarantee (packets may be lost)
🔀Unordered (packets may arrive shuffled)
🚫No flow control
🚀Minimal overhead (tiny 8-byte header)
🎯DNS, video streaming, gaming, VoIP

Real-World: TCP Deep Dive

# See all active TCP connections on your machine
ss -tuln              # Linux (modern)
netstat -tuln         # Linux/macOS (classic)
netstat -an           # Windows

# Output:
# State     Recv-Q  Send-Q  Local Address:Port   Peer Address:Port
# LISTEN    0       128     0.0.0.0:22           0.0.0.0:*          ← SSH server
# LISTEN    0       511     0.0.0.0:80           0.0.0.0:*          ← Web server
# ESTAB     0       0       10.0.1.5:43210       93.184.216.34:443  ← Active HTTPS
# TIME_WAIT 0       0       10.0.1.5:43211       93.184.216.34:443  ← Closing

# TCP 3-Way Handshake — capture it live with tcpdump
sudo tcpdump -i eth0 -nn 'tcp[tcpflags] & (tcp-syn|tcp-ack) != 0' -c 10
# Output:
# 10:00:01 IP 10.0.1.5.43210 > 93.184.216.34.443: Flags [S]      ← SYN
# 10:00:01 IP 93.184.216.34.443 > 10.0.1.5.43210: Flags [S.]     ← SYN-ACK
# 10:00:01 IP 10.0.1.5.43210 > 93.184.216.34.443: Flags [.]      ← ACK
# Connection established! Took ~1ms (3 packets)

# Common TCP states you'll see:
# LISTEN      ← Server waiting for connections
# ESTABLISHED ← Active connection (data flowing)
# TIME_WAIT   ← Connection closed, waiting for stale packets to expire (2 min)
# CLOSE_WAIT  ← Remote side closed, your app hasn't closed yet (BUG if stuck here)
# SYN_SENT    ← Your machine sent SYN, waiting for SYN-ACK (firewall blocking?)

# 🚨 Debugging: "Too many TIME_WAIT connections"
# This means your app opens and closes tons of short-lived connections.
# Fix: Use connection pooling (requests.Session(), HTTP keep-alive)
ss -s  # Show TCP state summary
# TCP: 2345 (estab 890, closed 1200, time-wait 245)

Real-World: Ports You Must Know

Essential Port Numbers for Developers
Port Protocol Transport What It Does
22SSHTCPRemote shell, SCP, SFTP
53DNSUDP/TCPDomain name resolution
80HTTPTCPWeb traffic (unencrypted)
443HTTPSTCPWeb traffic (encrypted)
5432PostgreSQLTCPDatabase connections
6379RedisTCPCache / message broker
8080HTTP (alt)TCPDev servers, proxies
50051gRPCTCP (HTTP/2)RPC services

Layer 4: Application (HTTP, DNS, TLS — What Your Code Uses)

This is the layer developers interact with most. Every API call, database query, and web page uses application-layer protocols built on top of TCP/UDP.

DNS — The Internet's Phone Book

# How DNS resolution works (step by step):
# 1. You type: curl api.example.com
# 2. OS checks /etc/hosts file first (local override)
# 3. OS checks local DNS cache
# 4. Asks your configured DNS server (e.g., 8.8.8.8)
# 5. DNS server resolves recursively:
#    Root server (.com) → TLD server (example.com) → Authoritative server
# 6. Returns: api.example.com → 93.184.216.34

# Query DNS manually
dig api.example.com
# ;; ANSWER SECTION:
# api.example.com.  300  IN  A  93.184.216.34
# TTL=300 means this answer is cached for 5 minutes

# Query specific record types
dig example.com MX          # Mail servers
dig example.com NS          # Name servers
dig example.com TXT         # SPF, DKIM, verification records
dig example.com AAAA        # IPv6 address
dig example.com CNAME       # Alias to another domain

# Trace the full DNS resolution path
dig +trace api.example.com
# Shows: root → .com → example.com → api.example.com

# Check what DNS server you're using
cat /etc/resolv.conf        # Linux
scutil --dns | head -20     # macOS

# 🚨 Debugging DNS:
# "Can't resolve hostname" → dig @8.8.8.8 hostname (bypass local DNS)
# "Works from one machine, not another" → Different DNS servers, stale cache
# "Intermittent failures" → DNS TTL too low, server overloaded
# Clear DNS cache:
sudo systemd-resolve --flush-caches   # Linux (systemd)
sudo dscacheutil -flushcache          # macOS

HTTP/HTTPS — How the Web Works

# HTTP is a text-based request-response protocol on top of TCP

# Raw HTTP request (what curl sends):
# GET /api/users HTTP/1.1
# Host: api.example.com
# Accept: application/json
# Authorization: Bearer eyJ...
#
# (empty line = end of headers)

# Raw HTTP response (what the server returns):
# HTTP/1.1 200 OK
# Content-Type: application/json
# Content-Length: 128
# Cache-Control: max-age=300
#
# {"users": [{"id": 1, "name": "Alice"}]}

# See the full request/response exchange:
curl -v https://api.example.com/users 2>&1 | head -30
# Lines starting with > are the REQUEST
# Lines starting with < are the RESPONSE

# HTTP/2 vs HTTP/1.1:
# HTTP/1.1: One request per TCP connection (or keep-alive pipelining)
# HTTP/2: Multiplexed — many requests share one connection (used by gRPC)
# HTTP/3: Uses QUIC (UDP-based) — faster handshake, no head-of-line blocking

# Check which HTTP version a server supports:
curl -v --http2 https://api.example.com 2>&1 | grep "< HTTP"
# < HTTP/2 200

Packet Capture with tcpdump & Wireshark

The ultimate debugging tool. tcpdump captures raw packets on any interface — the network equivalent of a debugger.

# Capture all traffic on eth0
sudo tcpdump -i eth0 -nn

# Capture only HTTP traffic (port 80)
sudo tcpdump -i eth0 -nn port 80

# Capture traffic to/from a specific IP
sudo tcpdump -i eth0 -nn host 93.184.216.34

# Capture DNS queries (port 53)
sudo tcpdump -i eth0 -nn port 53
# Output:
# 10:00:01 IP 10.0.1.5.52341 > 8.8.8.8.53: A? api.example.com
# 10:00:01 IP 8.8.8.8.53 > 10.0.1.5.52341: A 93.184.216.34

# Capture only TCP SYN packets (new connections)
sudo tcpdump -i eth0 -nn 'tcp[tcpflags] == tcp-syn'

# Save capture to file (analyze in Wireshark later)
sudo tcpdump -i eth0 -nn -w capture.pcap -c 1000

# Wireshark: Open capture.pcap for visual analysis
# - Filter: http.request.method == "GET"
# - Filter: tcp.flags.syn == 1
# - Filter: dns.qry.name contains "example"
# - Right-click any packet → Follow TCP Stream (see full conversation)

Production Debugging Scenarios

Here are real problems you'll face and which layer to investigate:

Network Debugging: Which Layer Is the Problem?
Symptom Layer Debug With Likely Cause
Can't resolve hostnameApplication (DNS)dig, nslookupDNS server down, wrong /etc/resolv.conf
Connection refusedTransport (TCP)telnet, nc, ssService not running, wrong port
Connection timeoutInternet (IP)ping, tracerouteFirewall blocking, routing issue
TLS handshake failedApplication (TLS)openssl s_clientExpired cert, wrong hostname, cipher mismatch
HTTP 502 Bad GatewayApplication (HTTP)curl -v, access logsBackend crashed, proxy misconfigured
Slow responsesTransport (TCP)tcpdump, ssPacket loss, TCP retransmissions, small window
Network unreachableLink (Physical)ip link, ethtoolCable unplugged, NIC down, VLAN wrong

The Essential Networking Toolkit

Your Network Debugging Toolkit
📡pingIs host alive?
🗺tracerouteWhere is it slow?
🔍digDNS working?
🔗curl -vHTTP working?
📦tcpdumpWhat's on the wire?
📊ss / netstatConnection state?
# Debugging flowchart (use this every time):

# Step 1: Can I reach the host at all?
ping 93.184.216.34
# If NO → Layer 2/3 issue (routing, firewall, host down)

# Step 2: Can I resolve the hostname?
dig api.example.com
# If NO → DNS issue (Layer 4: Application)

# Step 3: Can I open a TCP connection?
nc -zv api.example.com 443
# or: telnet api.example.com 443
# If "Connection refused" → Service not listening on that port
# If "Connection timed out" → Firewall blocking the port

# Step 4: Is TLS working?
openssl s_client -connect api.example.com:443 -servername api.example.com
# Shows certificate chain, TLS version, cipher suite
# If error → Cert expired, hostname mismatch, protocol mismatch

# Step 5: Is HTTP working?
curl -v https://api.example.com/health
# If 5xx → Server-side bug
# If timeout → Back to step 1-3

# Step 6: Capture packets for deep analysis
sudo tcpdump -i eth0 -nn host 93.184.216.34 -w debug.pcap
# Open in Wireshark for visual analysis

Network Layers in Kubernetes

If you work with Kubernetes, here's how the layers map:

Network Layers in Kubernetes
L4: Application — Ingress (HTTP routing, TLS termination, path-based routing)nginx-ingress, traefik, istio gateway → routes external traffic to Services
L3: Transport — Service (ClusterIP, NodePort, LoadBalancer)kube-proxy / iptables / eBPF → load-balances TCP/UDP to pod endpoints
L2: Internet — Pod Network (CNI plugin: Calico, Cilium, Flannel)Every pod gets a unique IP, pods communicate across nodes via overlay/BGP
L1: Link — Node Network (AWS VPC, GCP VPC, bare metal)Physical/virtual NICs, VPC subnets, security groups
# Debugging networking in Kubernetes:

# What IP did my pod get?
kubectl get pod my-app -o wide
# NAME    READY   STATUS   IP           NODE
# my-app  1/1     Running  10.244.1.15  node-2

# Can my pod reach another service?
kubectl exec -it my-app -- curl -v http://other-service:8080/health

# DNS resolution inside a pod:
kubectl exec -it my-app -- nslookup other-service.default.svc.cluster.local
# Server: 10.96.0.10 (CoreDNS)
# Address: 10.96.0.10#53
# Name: other-service.default.svc.cluster.local  Address: 10.96.45.123

# See Service endpoints (which pods back a Service?)
kubectl get endpoints other-service
# NAME            ENDPOINTS
# other-service   10.244.1.15:8080,10.244.2.23:8080

# Debug with a network tools pod:
kubectl run debug --image=nicolaka/netshoot -it --rm -- bash
# Inside: ping, traceroute, dig, curl, tcpdump all available

Mastery Checklist

Network Mastery Roadmap
Level 1: Know your tools
ping, dig, curl -v, traceroute, ss, nc — use them daily until they're muscle memory
Level 2: Understand TCP
Handshake, states, retransmissions, window size. Read tcpdump output fluently.
Level 3: Master DNS
Record types, TTL, caching layers, split-horizon DNS, CoreDNS in K8s
Level 4: Know TLS deeply
Certificate chains, mTLS, cipher suites. Debug with openssl s_client.
Level 5: Packet analysis
Wireshark fluency. Capture and analyze any protocol. Spot retransmissions, resets, fragmentation.
Level 6: Network design
VPC architecture, subnet design, load balancer types, service mesh, eBPF

Networking isn't about memorizing layer numbers — it's about knowing which tool to reach for when something breaks at 3 AM. Start by running every command in this guide on your own machine. Then break things intentionally in a lab (block ports with iptables, poison DNS, drop packets with tc) and practice fixing them. That's how you master network layers — not by reading, but by debugging.