The previous chapters gave you the conceptual framework — OSI layers, IP addressing, IPv6. Now it’s time to get your hands dirty with real network configuration on Linux. This chapter covers the tools and concepts you’ll use daily as a network programmer: interfaces, routing, ARP, DNS, and the commands to manage them all.
A network interface is the point of connection between your machine and a network. It can be a physical device (Ethernet card, Wi-Fi adapter) or a virtual construct (loopback, bridge, VLAN interface, VPN tunnel).
# Modern approach (iproute2)
ip link show
# Output example:
# 1: lo: <LOOPBACK,UP> mtu 65536 state UNKNOWN
# link/loopback 00:00:00:00:00:00
# 2: eth0: <BROADCAST,MULTICAST,UP> mtu 1500 state UP
# link/ether aa:bb:cc:dd:ee:ff
# 3: wlan0: <BROADCAST,MULTICAST> mtu 1500 state DOWN
# link/ether 11:22:33:44:55:66
Key interface types:
| Interface | Description |
|---|---|
lo |
Loopback — always 127.0.0.1, used for local-only communication |
eth0 / enp3s0 |
Wired Ethernet (naming depends on system configuration) |
wlan0 / wlp2s0 |
Wireless interface |
docker0 |
Docker bridge network |
br0 |
Network bridge |
veth* |
Virtual Ethernet pair (used in containers) |
tun0 / tap0 |
VPN tunnel interfaces |
Modern Linux uses Predictable Network Interface Names based on firmware, topology, or hardware properties:
en = Ethernet, wl = Wireless, ww = WWANp = PCI bus, s = slot, o = onboardenp3s0 = Ethernet, PCI bus 3, slot 0You can revert to classic naming (eth0) by adding net.ifnames=0 to your kernel boot parameters.
# Bring an interface up
sudo ip link set eth0 up
# Bring an interface down
sudo ip link set eth0 down
# Check interface status
ip link show eth0
# Show all addresses
ip addr show
# Show addresses for a specific interface
ip addr show dev eth0
# Show only IPv4
ip -4 addr show
# Show only IPv6
ip -6 addr show
# Add an IPv4 address
sudo ip addr add 192.168.1.100/24 dev eth0
# Add an IPv6 address
sudo ip addr add 2001:db8::1/64 dev eth0
# Remove an address
sudo ip addr del 192.168.1.100/24 dev eth0
These changes are temporary — they are lost on reboot. For persistent configuration, use your distribution’s network manager.
Most desktop Linux distributions use NetworkManager. The CLI tool is nmcli:
# Show all connections
nmcli connection show
# Show device status
nmcli device status
# Set a static IP
nmcli connection modify "Wired connection 1" \
ipv4.method manual \
ipv4.addresses 192.168.1.100/24 \
ipv4.gateway 192.168.1.1 \
ipv4.dns "8.8.8.8,8.8.4.4"
# Apply changes
nmcli connection up "Wired connection 1"
On servers, systemd-networkd or netplan (Ubuntu) are common alternatives:
# /etc/netplan/01-config.yaml (Ubuntu/netplan)
network:
version: 2
ethernets:
eth0:
addresses:
- 192.168.1.100/24
gateway4: 192.168.1.1
nameservers:
addresses:
- 8.8.8.8
- 8.8.4.4
sudo netplan apply
Routing is the process of determining the path a packet takes from source to destination. Every host has a routing table that tells it where to send packets based on their destination address.
# Show the routing table
ip route show
# Example 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.42
# 172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
Reading the output:
default via 192.168.1.1 — packets with no matching route go to gateway 192.168.1.1 (the default route)192.168.1.0/24 dev eth0 — packets for the local subnet go directly out eth0172.17.0.0/16 dev docker0 — Docker container traffic uses the docker0 bridge# Add a static route
sudo ip route add 10.0.0.0/8 via 192.168.1.254 dev eth0
# Add a default gateway
sudo ip route add default via 192.168.1.1
# Delete a route
sudo ip route del 10.0.0.0/8
# Show the route to a specific destination
ip route get 8.8.8.8
When your machine sends a packet, the kernel follows this process:
ARP maps Layer 3 addresses (IP) to Layer 2 addresses (MAC) on the local network. When your machine wants to send a packet to 192.168.1.1, it needs to know the MAC address of that device.
192.168.1.1 responds with an ARP Reply: “192.168.1.1 is at aa:bb:cc:dd:ee:ff”# Show ARP table
ip neigh show
# Example output:
# 192.168.1.1 dev eth0 lladdr aa:bb:cc:dd:ee:ff REACHABLE
# 192.168.1.5 dev eth0 lladdr 11:22:33:44:55:66 STALE
# Add a static ARP entry
sudo ip neigh add 192.168.1.200 lladdr 00:aa:bb:cc:dd:ee dev eth0
# Delete an ARP entry
sudo ip neigh del 192.168.1.200 dev eth0
# Flush the ARP cache
sudo ip neigh flush all
ARP entries have states: REACHABLE (recently confirmed), STALE (not recently confirmed), DELAY (confirmation pending), FAILED (resolution failed).
The Domain Name System (DNS) translates human-readable hostnames (example.com) to IP addresses (93.184.216.34). As a network programmer, you’ll interact with DNS constantly.
getaddrinfo("example.com")/etc/hosts for a static mapping/etc/resolv.conf.com) → authoritative server# View DNS resolver configuration
cat /etc/resolv.conf
# Example:
# nameserver 8.8.8.8
# nameserver 8.8.4.4
# search mycompany.local
# View static hostname mappings
cat /etc/hosts
# Example:
# 127.0.0.1 localhost
# 192.168.1.10 devserver.local devserver
# Simple lookup
host example.com
# Detailed lookup
dig example.com
# Query a specific DNS server
dig @8.8.8.8 example.com
# Reverse DNS lookup
dig -x 93.184.216.34
# Trace the full resolution path
dig +trace example.com
import socket
# Resolve hostname to IP
result = socket.getaddrinfo("example.com", 80)
for family, socktype, proto, canonname, addr in result:
print(f"{socket.AddressFamily(family).name}: {addr}")
Full example: code/dns_resolver.py
Here’s a quick reference for the essential diagnostic commands you’ll use throughout the book:
| Command | Purpose | Layer |
|---|---|---|
ip link show |
Interface status and MAC addresses | L2 |
ip addr show |
IP addresses on interfaces | L3 |
ip route show |
Routing table | L3 |
ip neigh show |
ARP/NDP neighbor table | L2–L3 |
ping |
Test reachability (ICMP echo) | L3 |
traceroute / tracepath |
Show the path to a destination | L3 |
ss -tuln |
List listening TCP/UDP ports | L4 |
dig / host / nslookup |
DNS queries | L7 |
curl |
HTTP requests | L7 |
tcpdump |
Capture packets | All |
Python gives you programmatic access to most of this information:
import socket
import struct
import fcntl
def get_ip_address(ifname: str) -> str:
"""Get the IPv4 address of a network interface."""
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
return socket.inet_ntoa(fcntl.ioctl(
s.fileno(), 0x8915, # SIOCGIFADDR
struct.pack('256s', ifname.encode()[:15])
)[20:24])
For a more portable approach, the netifaces or psutil libraries are recommended:
import psutil
for name, addrs in psutil.net_if_addrs().items():
for addr in addrs:
if addr.family == socket.AF_INET:
print(f"{name}: {addr.address}/{addr.netmask}")
Full example: code/network_info.py
ip link, ip addr, ip route, and ip neigh to inspect and configure Layer 2–3 settingsnmcli (NetworkManager) and netplan provide persistent network configuration on Linuxsocket module and libraries like psutil let you query network configuration programmatically| ← Previous: IP Addressing and IPv6 | Table of Contents | Next: Switching, Routing, and VLANs → |