Search…
Linux from Scratch · Part 9

The Linux networking stack

In this series (15 parts)
  1. What is Linux and how it differs from other OSes
  2. Installing Linux and setting up your environment
  3. The Linux filesystem explained
  4. Users, groups, and permissions
  5. Essential command line tools
  6. Shell scripting fundamentals
  7. Processes and job control
  8. Standard I/O, pipes, and redirection
  9. The Linux networking stack
  10. Package management and software installation
  11. Disk management and filesystems
  12. Logs and system monitoring
  13. SSH and remote access
  14. Cron jobs and task scheduling
  15. 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
  • lo is the loopback interface (127.0.0.1). Used for local-only communication.
  • eth0 is a wired Ethernet connection.
  • wlan0 is 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.

Start typing to search across all content
navigate Enter open Esc close