Skip to main content

Network Fundamentals: Layers, IP, TCP, UDP and Ports

How a request crosses a network: TCP/IP layers, IP addresses, ports and sockets, TCP vs UDP, HTTP and TLS, traced with curl and tcpdump in a practice lab.

When your application calls an API, opens a database connection or serves a web page, it hands bytes to the operating system and trusts the network to deliver them. Most of the time that works. When it does not, the error message names a layer: "Could not resolve host" is DNS, "Connection refused" is a port with nothing listening, "Connection timed out" is usually a firewall, and "certificate verify failed" is TLS. Knowing which layer each message comes from is most of what makes network problems fast to fix.

In this part you build a small practice network on one Linux machine, send a request through it, and watch every step: the name lookup, the TCP handshake, the HTTP exchange and the TLS handshake. The same lab carries through the whole guide.

Prerequisites

  • A Linux machine where you can use sudo. Use a throwaway VM or cloud server: the lab installs servers (BIND, Unbound, nginx, HAProxy) that you probably do not want on your laptop. Ubuntu 26.04 LTS and 24.04 LTS both work. A local VM from Multipass or UTM is enough; so is a small cloud server. On DigitalOcean, create a Droplet with Ubuntu 26.04 and delete it when you finish the guide. This is a referral link: new accounts get DigitalOcean's standard sign-up credit, and this site gets credit if you later spend on DigitalOcean (referral terms). You need a valid payment method before you can create a Droplet.
  • Comfort with a terminal. No networking knowledge is needed.

Install the tools the lab uses, then stop the server packages from running on the machine itself (the lab starts its own copies inside the practice network):

sudo apt update
sudo apt install -y iproute2 iputils-ping iputils-tracepath iputils-arping traceroute mtr-tiny \
  tcpdump nftables conntrack curl netcat-openbsd openssl python3 ipcalc iperf3 wireguard-tools \
  bind9 bind9-dnsutils unbound nginx haproxy
sudo systemctl disable --now named unbound nginx haproxy

Build the Practice Lab

The lab is one Bash script, netlab.sh. It uses Linux network namespaces: each namespace has its own interfaces, addresses, routing table and firewall, which is exactly how containers get their own network. Nine namespaces play nine machines, connected by virtual cables (veth pairs) to virtual switches (bridges) inside a router:

The addresses 203.0.113.0/24 are reserved for documentation (RFC 5737), so they stand in for the public internet without clashing with any real network. The names shop.example and shop.internal are reserved too. Download the script, read it (it runs as root), and start the lab:

curl -fsSLO https://raw.githubusercontent.com/The-DevOps-Daily/guide-networking-demo-2026-10-27/v1/netlab.sh
less netlab.sh
chmod +x netlab.sh

Run sudo ./netlab.sh down at any time to remove everything, and sudo ./netlab.sh up to get a fresh lab. The parts of this guide change the lab on purpose (delays, firewalls, new configs), so start every part with sudo ./netlab.sh down && sudo ./netlab.sh up, and close any shells you opened inside lab hosts. The script never touches your own interfaces, routes or firewall, and down removes only the namespaces and files that up created.

To run a command on a lab host, prefix it with sudo ip netns exec <host>, or open a root shell on that host:

sudo ip netns exec client bash

In the rest of the guide the prompt shows which host a command runs on: client# means "in a shell on client". The output in this guide comes from the lab on Ubuntu 26.04.1 LTS. Your timings and the random parts (MAC addresses, port numbers) will differ.

The Layers You Actually Use

Networking is built in layers. Each layer does one job and relies on the layer below it, and each adds its own header in front of the data (encapsulation):

The model the internet actually uses is the TCP/IP model with four layers. The OSI model has seven and is the vocabulary people use in conversation ("a layer 4 load balancer", "a layer 7 firewall"), so you need the mapping:

OSI layer Name TCP/IP layer What it does Examples
7 Application Application The data your program cares about HTTP, DNS, SSH, PostgreSQL protocol
6 Presentation Application Encoding, encryption TLS (in practice)
5 Session Application Keeping a conversation going Rarely a separate thing today
4 Transport Transport Ports, reliability, flow control TCP, UDP, QUIC (over UDP)
3 Network Internet Addresses and routing between networks IPv4, IPv6, ICMP
2 Data link Link Delivery on one local network Ethernet, Wi-Fi, ARP
1 Physical Link Bits on a wire or radio Cables, fibre, radio

Real protocols do not fit the boxes exactly. TLS sits between TCP and HTTP. QUIC is a transport protocol that runs inside UDP. Treat the model as a checklist for troubleshooting, not as a law: when something fails, you work up from the bottom. Can the packets get there (IP and routing)? Is the port open (TCP)? Does the application answer correctly (HTTP, TLS)?

Follow One Request

Send a request from client to the shop. -v makes curl narrate each step, and -s hides the progress meter:

Read it from the top:

  1. DNS: curl looked up www.shop.example and got 203.0.113.80. Part 3 shows how that answer was found.
  2. TCP: Established connection to www.shop.example (203.0.113.80 port 80) from 203.0.113.10 port ... means the TCP handshake finished. The client used a random source port.
  3. HTTP: the lines that start with > are the request curl sent; < lines are the response headers. 200 OK is the status code.
  4. The body: served by web1 (10.0.2.11). The answer came from web1, a machine with a private address that client never connected to. lb received the request and passed it on: it is a reverse proxy.

Now watch the same request as packets. tcpdump prints every packet that crosses an interface. Open a second terminal, start a capture on lb, and send the request again from client:

The time stamps and TCP options are cut from the lines to keep them short. The flags in square brackets tell the story:

  • [S], [S.], [.]: the three-way handshake. The client sends SYN, the server answers SYN-ACK, the client acknowledges. No data has moved yet, and one full round trip is gone.
  • [P.] with HTTP: GET / HTTP/1.1: the request. The next [P.] is the response. length is the number of payload bytes.
  • [F.]: FIN, each side closing its half of the connection.

Two details are worth noticing. The client address is 203.0.113.10, but the destination is 10.0.1.10, not 203.0.113.80: the router rewrote the destination on the way in (destination NAT, Part 2). And lb saw only this side of the conversation; its own connection to web1 is a second, separate TCP connection.

Addresses, Ports and Sockets

An IP address gets a packet to a machine. A port (a 16-bit number, 0 to 65535) gets it to one program on that machine. A program that waits for connections listens on a port; ss shows which:

The options are -t TCP, -u UDP, -l listening, -n numeric (no name lookups) and -p the owning process. The Local Address column matters more than anything else on that line:

  • 0.0.0.0:80 (or *:80, or [::]:80 for IPv6): the service accepts connections on every address of the machine.
  • 127.0.0.1:53: only programs on the same machine can connect (here, the router's own lookups). This is the most common reason a service "works on the server but not from outside": it listens on the loopback address.
  • 10.0.1.1:53: one specific address. Unbound on the router listens on each of its addresses, so each subnet can reach it.

How can port 80 on lb serve many clients at once? A TCP connection is identified by four values, the 4-tuple: source address, source port, destination address and destination port. The client picks a random source port for each connection, so every connection is unique even though they all go to port 80. Open three connections that stay open, then look:

All three connections use local port 80; only the client's port differs. The client's operating system picks those ephemeral ports from a fixed range:

That range is also the limit you hit when one machine opens tens of thousands of connections to the same destination: every connection needs its own 4-tuple. The posts Can two sockets share a TCP port? and port vs socket go deeper.

Ports you will meet constantly:

Port Protocol Service
22 TCP SSH
53 UDP and TCP DNS
80, 443 TCP (443 also UDP for HTTP/3) HTTP, HTTPS
3306 TCP MySQL
5432 TCP PostgreSQL
6379 TCP Redis, Valkey
8080, 8000, 3000 TCP Common development and app ports

Ports below 1024 are privileged on Linux: binding to them needs root or the CAP_NET_BIND_SERVICE capability. That is why apps often listen on 8080 behind a proxy on 80.

TCP and UDP

The transport layer offers two main choices, and they make different promises:

TCP UDP
Connection Handshake first (1 round trip) None: just send
Delivery Retransmits lost data, keeps order No retransmission, no ordering
Unit A stream of bytes Separate datagrams
Flow and congestion control Yes No (the application must be careful)
Typical uses HTTP/1.1 and HTTP/2, SSH, databases, email DNS, streaming, games, VoIP, QUIC (HTTP/3)

DNS is the classic UDP protocol. A query is one datagram and the answer is another, with no handshake:

Two packets: the question and the answer. If the answer is lost, the client simply asks again after a timeout. DNS switches to TCP when an answer is too large for one datagram.

"UDP is faster" is true in one specific way: it skips the handshake. On a fast local network you cannot see the difference, so the lab can add delay. tc with the netem queueing discipline makes the router hold every packet to client for 50 ms, which turns the link into a 50 ms round trip, like a server on another continent:

Now time a DNS query, a plain HTTP request and an HTTPS request. curl's -w option prints its timers: time_connect is when the TCP handshake finished, time_appconnect when the TLS handshake finished, and time_starttransfer when the first byte of the response arrived. --resolve pins the address so that no DNS lookup hides in the numbers:

The pattern is the point: each handshake costs one round trip. The DNS answer arrives after one round trip, plain HTTP needs two (TCP handshake, then request and response), and HTTPS needs three (TCP, TLS 1.3, then the request). Seven runs of each, median time to the answer:

That is why connection reuse matters so much: HTTP keep-alive, connection pools for databases, and HTTP/2, which sends many requests over one connection. It is also why QUIC, the transport under HTTP/3, runs over UDP: it combines the transport and TLS handshakes into one round trip, and lets a resumed connection send data in the first packet. UDP itself is not magic; QUIC rebuilds reliability and congestion control on top of it. The posts UDP vs TCP: how much faster is UDP really? and when to use UDP instead of TCP measure this further.

HTTP and TLS

HTTPS is HTTP inside a TLS connection. TLS encrypts the traffic and proves the server's identity with a certificate that a trusted certificate authority (CA) signed. The lab has its own small CA (/etc/netlab/tls/ca.crt); openssl s_client shows the handshake result:

TLSv1.3, Verification: OK and the verified host name are what you want to see. -servername sends SNI (Server Name Indication), the host name the client wants, so that one address can serve certificates for many sites; -verify_hostname checks that the certificate is valid for that name, and -verify_return_error makes s_client stop on any certificate error instead of carrying on, which it does by default. The key exchange group X25519MLKEM768 is a hybrid of classic X25519 and the post-quantum ML-KEM, which OpenSSL 3.5 and current browsers prefer by default. curl negotiated HTTP/2 with the server during the TLS handshake (ALPN), which is why the status line says HTTP/2 200.

When It Fails, Read the Error

Every layer fails with its own message. These are the ones you will see most, produced on purpose in the lab:

  • Could not resolve host: DNS. Check the name and the resolver (Part 3).
  • Network is unreachable or No route to host: routing. Here the router had no path to 198.51.100.0/24 and sent an ICMP error back (Part 2 and Part 8).
  • Connection refused: the machine answered, but nothing listens on that port, or a firewall rejects actively. The host is reachable, so look at the service.
  • Connection timed out: no answer at all. Usually a firewall drops the packets silently, sometimes the machine is down. Part 4 shows both behaviours side by side.
  • SSL certificate ... verify result (older curl: "SSL certificate problem"): TLS. The name, the expiry date or the trust chain is wrong.

Recent curl versions print Could not connect to server for both a refused and an unreachable connection. Add -v to see the reason, or test the port with nc -zv as above.

Common Mistakes

  • Testing reachability with ping only. Many networks block ICMP, and a host that answers ping can still have a closed port. Test the real protocol: nc -zv host 5432, curl -v https://host/.
  • Listening on 127.0.0.1 and wondering why nobody can connect. Check the Local Address column of ss -tlnp.
  • Treating "refused" and "timed out" as the same error. Refused means something answered; timed out means nothing did. They point to different fixes.
  • Opening a new connection for every request. Each new connection pays the handshakes again; at 50 ms round trip that is 100 ms before an HTTPS request even starts.
  • Assuming UDP is always faster or always unreliable. It skips the handshake, and QUIC shows that reliable protocols can be built on it.

Summary

  • Data goes down the layers on the sender (HTTP, TCP, IP, Ethernet) and back up on the receiver; troubleshoot from the bottom up.
  • An IP address finds the machine, a port finds the program, and the 4-tuple identifies one TCP connection, which is how one port serves thousands of clients.
  • TCP spends a round trip on its handshake and TLS 1.3 another; UDP sends at once. Reuse connections to pay those costs once.
  • Error messages name the layer: resolve (DNS), route, refused (port), timed out (often a firewall), certificate (TLS).

Next, Part 2 looks at the addresses themselves: subnets, private ranges, NAT and IPv6.

Keep Learning

Updated:

Tags

NetworkingTCP/IPDNSSubnettingFirewallsLoad BalancingKubernetesDevOps

Found an issue?