The Linux networking stack
In this series (15 parts)
- What is Linux and how it differs from other OSes
- Installing Linux and setting up your environment
- The Linux filesystem explained
- Users, groups, and permissions
- Essential command line tools
- Shell scripting fundamentals
- Processes and job control
- Standard I/O, pipes, and redirection
- The Linux networking stack
- Package management and software installation
- Disk management and filesystems
- Logs and system monitoring
- SSH and remote access
- Cron jobs and task scheduling
- Linux security basics for sysadmins
Networking on Linux is transparent. You can see every packet, every connection, every routing decision. There are no hidden network settings buried in a GUI. Everything is accessible through commands and configuration files. This visibility is why Linux is the preferred platform for network troubleshooting and security work.
Prerequisites
You should be comfortable with I/O redirection and pipes and the Linux filesystem (you will edit files under /etc).
Network interfaces
A network interface is the software representation of a network connection. It could be a physical Ethernet port, a Wi-Fi adapter, a virtual bridge, or a loopback device.
# List all interfaces
ip link show
Output:
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP
link/ether 52:54:00:ab:cd:ef brd ff:ff:ff:ff:ff:ff
3: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP
link/ether aa:bb:cc:dd:ee:ff brd ff:ff:ff:ff:ff:ff
lois the loopback interface (127.0.0.1). Used for local-only communication.eth0is a wired Ethernet connection.wlan0is a wireless connection.
Viewing IP addresses
ip addr show
Output:
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536
inet 127.0.0.1/8 scope host lo
inet6 ::1/128 scope host
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500
inet 192.168.1.100/24 brd 192.168.1.255 scope global dynamic eth0
inet6 fe80::5054:ff:feab:cdef/64 scope link
The /24 is the subnet mask in CIDR notation. It means the first 24 bits are the network part, leaving 8 bits for host addresses (256 addresses, 254 usable).
Bringing interfaces up and down
# Disable an interface
sudo ip link set eth0 down
# Enable an interface
sudo ip link set eth0 up
# Assign an IP address manually
sudo ip addr add 192.168.1.200/24 dev eth0
The old ifconfig command still works on many systems but ip is the modern replacement.
The routing table
The routing table tells Linux where to send packets based on their destination.
ip route show
Output:
default via 192.168.1.1 dev eth0 proto dhcp metric 100
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.100 metric 100
Reading this:
- default via 192.168.1.1: any packet not matching a specific route goes to 192.168.1.1 (your gateway/router)
- 192.168.1.0/24 dev eth0: packets for the local subnet go directly to eth0
# Add a static route
sudo ip route add 10.0.0.0/8 via 192.168.1.254
# Delete a route
sudo ip route del 10.0.0.0/8
# Show route for a specific destination
ip route get 8.8.8.8
Output:
8.8.8.8 via 192.168.1.1 dev eth0 src 192.168.1.100 uid 1000
DNS resolution
When you type google.com, your system needs to convert that name to an IP address. Here is the resolution order:
graph TD A[Application requests google.com] --> B[Check /etc/hosts] B -->|Found| C[Return IP from hosts file] B -->|Not found| D[Check /etc/resolv.conf for DNS server] D --> E[Query DNS server] E --> F[DNS server returns IP] F --> G[Cache the result] style A fill:#64b5f6,stroke:#1976d2,color:#000 style F fill:#81c784,stroke:#388e3c,color:#000
/etc/hosts
Static hostname-to-IP mappings. Checked before DNS.
cat /etc/hosts
Output:
127.0.0.1 localhost
127.0.1.1 devbox
192.168.1.50 database.local
/etc/resolv.conf
Points to your DNS servers.
cat /etc/resolv.conf
Output:
nameserver 192.168.1.1
nameserver 8.8.8.8
nameserver 8.8.4.4
Testing DNS resolution
# Simple lookup
dig google.com +short
Output:
142.250.80.46
# Full query details
dig google.com
Output:
;; QUESTION SECTION:
;google.com. IN A
;; ANSWER SECTION:
google.com. 300 IN A 142.250.80.46
;; Query time: 12 msec
;; SERVER: 192.168.1.1#53(192.168.1.1) (UDP)
# Reverse lookup (IP to name)
dig -x 142.250.80.46 +short
Output:
lhr25s34-in-f14.1e100.net.
Checking connections with ss
ss (socket statistics) replaces the older netstat command. It shows current network connections.
# All listening TCP ports
ss -tlnp
Output:
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=1230,fd=6))
LISTEN 0 128 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=890,fd=3))
LISTEN 0 244 127.0.0.1:5432 0.0.0.0:* users:(("postgres",pid=1100,fd=5))
Flags explained:
-t= TCP connections-l= listening sockets only-n= show port numbers (not service names)-p= show process using the socket
# All established connections
ss -tnp
Output:
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
ESTAB 0 0 192.168.1.100:22 10.0.0.50:54321 users:(("sshd",pid=2345,fd=4))
ESTAB 0 0 192.168.1.100:80 203.0.113.5:12345 users:(("nginx",pid=1234,fd=8))
# Show all connections for a specific port
ss -tnp sport = :80
# Count connections by state
ss -t state established | wc -l
Firewall basics
Linux has a built-in firewall in the kernel called netfilter. You interact with it through iptables (low-level) or ufw (user-friendly wrapper).
ufw (Uncomplicated Firewall)
# Enable the firewall
sudo ufw enable
# Check status
sudo ufw status verbose
Output:
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
# Allow SSH
sudo ufw allow 22/tcp
# Allow HTTP and HTTPS
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# Allow from a specific IP
sudo ufw allow from 10.0.0.50 to any port 5432
# Deny a specific port
sudo ufw deny 3306/tcp
# Delete a rule
sudo ufw delete allow 3306/tcp
# Check rules with numbers
sudo ufw status numbered
Output:
Status: active
To Action From
-- ------ ----
[ 1] 22/tcp ALLOW IN Anywhere
[ 2] 80/tcp ALLOW IN Anywhere
[ 3] 443/tcp ALLOW IN Anywhere
[ 4] 5432 ALLOW IN 10.0.0.50
iptables (low-level)
iptables gives you full control but is more complex. It works with chains (INPUT, OUTPUT, FORWARD) and rules.
# List all rules
sudo iptables -L -n -v
Output:
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
234 18720 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:22
567 45360 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:80
# Allow incoming SSH
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
# Block an IP address
sudo iptables -A INPUT -s 203.0.113.5 -j DROP
# Allow established connections
sudo iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
For security hardening, you will configure firewall rules as part of a broader defense strategy.
Example 1: Trace a network request step by step
Let’s follow what happens when you run curl https://example.com:
Step 1: DNS lookup
dig example.com +short
Output:
93.184.216.34
Step 2: Check the route
ip route get 93.184.216.34
Output:
93.184.216.34 via 192.168.1.1 dev eth0 src 192.168.1.100 uid 1000
The packet will go through our gateway (192.168.1.1) via eth0.
Step 3: TCP connection (watch with ss)
Open a second terminal and run:
# Watch for new connections
watch -n 0.5 'ss -tnp | grep 93.184'
In the first terminal:
curl -v https://example.com 2>&1 | head -20
Output:
* Trying 93.184.216.34:443...
* Connected to example.com (93.184.216.34) port 443
* ALPN: curl offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
> GET / HTTP/1.1
> Host: example.com
The -v flag shows you: DNS resolution, TCP connection, TLS handshake (covered in cryptography fundamentals), and the HTTP request.
Step 4: Check the connection in ss
ss -tnp | grep 93.184
Output:
ESTAB 0 0 192.168.1.100:54321 93.184.216.34:443 users:(("curl",pid=9999,fd=5))
You can see the established connection from your machine (port 54321) to example.com (port 443).
Example 2: Configure a simple firewall rule
Let’s set up a firewall for a web server that should only accept SSH, HTTP, and HTTPS:
# Reset ufw to default
sudo ufw reset
# Set default policies
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Allow SSH (so you don't lock yourself out!)
sudo ufw allow 22/tcp comment "SSH access"
# Allow HTTP
sudo ufw allow 80/tcp comment "HTTP"
# Allow HTTPS
sudo ufw allow 443/tcp comment "HTTPS"
# Enable the firewall
sudo ufw enable
Output:
Firewall is active and enabled on system startup
Verify:
sudo ufw status verbose
Output:
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
To Action From
-- ------ ----
22/tcp ALLOW IN Anywhere # SSH access
80/tcp ALLOW IN Anywhere # HTTP
443/tcp ALLOW IN Anywhere # HTTPS
Test that other ports are blocked:
# From another machine
nmap -p 3306 192.168.1.100
Output:
PORT STATE SERVICE
3306/tcp filtered mysql
“filtered” means the firewall is silently dropping packets to that port. The attacker does not even get a “connection refused” response.
To restrict SSH to a specific IP (recommended for production):
# Remove the general SSH rule
sudo ufw delete allow 22/tcp
# Allow SSH only from your office IP
sudo ufw allow from 10.0.0.0/24 to any port 22 proto tcp comment "SSH from office"
What comes next
The next article covers Package management and software installation, including building from source and managing repositories.
For a security-focused view of networking, see Networking fundamentals for security in the Cybersecurity from Scratch series.