Linux as a Router
A Linux bridge connects devices within the same Layer 2 network. But real networks are rarely limited to a single subnet. Sooner or later, packets must travel between different networks. That's the job of a router. In this chapter, we'll transform a Linux host into a fully functional IP router and see exactly how packets move between independent networks.
Learning Objectives
By the end of this chapter, you will be able to:
- Understand the difference between switching and routing
- Configure Linux to forward packets
- Build multiple isolated IP networks
- Route packets between subnets
- Observe routing decisions in action
- Understand the role of the default gateway
- Troubleshoot routing problems
- Recognize how Kubernetes nodes, cloud VMs, and physical routers perform the same function
The Problem
In the previous chapter, we built a virtual Ethernet network.
br0
+---------+---------+
Host1 Host2 Host3
192.168.50.11
192.168.50.12
192.168.50.13
All hosts belonged to the same subnet.
192.168.50.0/24
Communication was simple.
Every host could reach every other host directly using ARP.
But what happens when hosts belong to different networks?
Host A
192.168.10.10/24
-------------
Host B
192.168.20.10/24
Host A cannot send Ethernet frames directly to Host B because they are in different IP networks.
Something must connect those networks.
That something is a router.
What Is a Router?
A router is a device that forwards IP packets between different networks.
Unlike a bridge:
- it operates at Layer 3
- it makes decisions using IP addresses
- it maintains a routing table
- it rewrites Ethernet headers for every hop
Every packet that crosses between IP networks passes through a router.
Linux can perform exactly the same job.
The Network We'll Build
We'll create two independent Ethernet networks connected by one Linux router.
Linux Router
left-br right-br
| |
+------+ +------+
| |
router-l router-r
| |
=================== ===================
| |
hostA hostB
| |
Namespace A Namespace B
Network A
192.168.10.0/24
Network B
192.168.20.0/24
The Linux host will connect both networks together.
Step 1 — Create the Namespaces
Create two namespaces.
sudo ip netns add hostA
sudo ip netns add hostB
Enable loopback.
sudo ip netns exec hostA ip link set lo up
sudo ip netns exec hostB ip link set lo up
Step 2 — Create Two Linux Bridges
Each bridge represents an independent Ethernet network.
sudo ip link add left-br type bridge
sudo ip link add right-br type bridge
Bring them up.
sudo ip link set left-br up
sudo ip link set right-br up
Step 3 — Create the veth Pairs
Each namespace needs one Ethernet connection.
sudo ip link add hostA-veth type veth peer name hostA-host
sudo ip link add hostB-veth type veth peer name hostB-host
Move one side into the namespaces.
sudo ip link set hostA-veth netns hostA
sudo ip link set hostB-veth netns hostB
Step 4 — Connect the Hosts to Their Bridges
Attach the host-side interfaces.
sudo ip link set hostA-host master left-br
sudo ip link set hostB-host master right-br
Bring them up.
sudo ip link set hostA-host up
sudo ip link set hostB-host up
Step 5 — Configure the Host Interfaces
Namespace A
sudo ip netns exec hostA ip addr add 192.168.10.10/24 dev hostA-veth
sudo ip netns exec hostA ip link set hostA-veth up
Namespace B
sudo ip netns exec hostB ip addr add 192.168.20.10/24 dev hostB-veth
sudo ip netns exec hostB ip link set hostB-veth up
Current topology
Host A
192.168.10.10
|
left-br
right-br
|
192.168.20.10
Host B
There is still no connection between the two bridges.
Step 6 — Give the Router an Interface on Each Network
Remember that a Linux bridge is itself a network interface.
Assign an IP address to each bridge.
sudo ip addr add 192.168.10.1/24 dev left-br
sudo ip addr add 192.168.20.1/24 dev right-br
The Linux host now belongs to both networks.
Linux Router
left-br right-br
192.168.10.1 192.168.20.1
At this point, Linux has two interfaces connected to two different IP networks.
Step 7 — Test Local Connectivity
Namespace A should reach its local gateway.
sudo ip netns exec hostA ping 192.168.10.1
Namespace B should also reach its gateway.
sudo ip netns exec hostB ping 192.168.20.1
These pings stay inside their local Ethernet networks.
Step 8 — Try to Reach the Other Network
From Namespace A:
sudo ip netns exec hostA ping 192.168.20.10
The result:
Network is unreachable
Why?
Because Namespace A only knows about:
192.168.10.0/24
It has no route for:
192.168.20.0/24
The packet never leaves the namespace.
Step 9 — Configure the Default Gateway
Tell each namespace where to send packets destined for other networks.
Namespace A:
sudo ip netns exec hostA ip route add default via 192.168.10.1
Namespace B:
sudo ip netns exec hostB ip route add default via 192.168.20.1
Verify.
sudo ip netns exec hostA ip route
Example:
default via 192.168.10.1
192.168.10.0/24 dev hostA-veth
Now the packet reaches the Linux router.
But communication still fails.
Why Doesn't It Work Yet?
The packet now arrives at the Linux host.
However, Linux does not forward packets between interfaces by default.
Instead, it silently drops them.
This behavior protects ordinary desktop and server systems from unintentionally acting as routers.
Step 10 — Enable IP Forwarding
Check the current setting.
cat /proc/sys/net/ipv4/ip_forward
Output:
0
Enable forwarding.
sudo sysctl -w net.ipv4.ip_forward=1
Or:
echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward
Verify.
cat /proc/sys/net/ipv4/ip_forward
Output:
1
Linux is now acting as a router.
Step 11 — Test Again
From Namespace A:
sudo ip netns exec hostA ping 192.168.20.10
Now the ping succeeds.
Congratulations.
You have built an IP router using only the Linux kernel.
Following the Packet
Let's follow the packet step by step.
Namespace A creates an ICMP Echo Request.
Destination
192.168.20.10
Namespace A checks its routing table.
Destination not local
↓
Use default gateway
192.168.10.1
An ARP request resolves the gateway's MAC address.
The Ethernet frame is sent.
Destination MAC
Linux Router
The Linux router receives the frame.
Ethernet Header
↓
Removed
Linux now examines the IP packet.
Destination IP
192.168.20.10
Linux searches its routing table.
192.168.20.0/24
↓
right-br
A new Ethernet header is created.
Source MAC
right-br
Destination MAC
Host B
The packet leaves through the second interface.
Notice what changed.
- Ethernet headers were replaced.
- MAC addresses changed.
- The IP packet stayed the same.
This is exactly how every IP router operates.
Observe the Routing Table
Display the router's routing table.
ip route
Example:
192.168.10.0/24 dev left-br
192.168.20.0/24 dev right-br
Linux automatically installed these connected routes when IP addresses were assigned.
No manual routing configuration was required.
Observe TTL Changes
Capture packets.
sudo tcpdump -i right-br icmp
Run:
sudo ip netns exec hostA ping 192.168.20.10
Notice that the packet's TTL has decreased by one.
Every router decrements the TTL before forwarding the packet.
This prevents routing loops from lasting forever.
Observe ARP
Namespace A never learns Host B's MAC address.
Check:
sudo ip netns exec hostA ip neigh
You'll only see:
192.168.10.1
The namespace only knows the MAC address of its default gateway.
Likewise, Host B only knows the router's MAC address.
This is one of the most important concepts in IP networking.
Hosts do not ARP for remote destinations.
They ARP only for the next hop.
Common Routing Problems
IP Forwarding Disabled
Check:
cat /proc/sys/net/ipv4/ip_forward
Must be:
1
Missing Default Route
Verify:
ip netns exec hostA ip route
Expected:
default via 192.168.10.1
Wrong Subnet Mask
Ensure both hosts and router interfaces use compatible prefixes.
Incorrect subnet masks often cause confusing routing behavior.
Interfaces Down
Verify:
ip link
and
ip netns exec hostA ip link
Firewall Rules
Linux may be forwarding packets correctly, but firewall rules can still block them.
Later in this book, we'll study Netfilter, iptables, and nftables in detail.
Real-World Examples
Linux routers are everywhere.
Examples include:
| System | Linux Routing |
|---|---|
| Kubernetes node | Routes Pod traffic |
| Docker host | Routes bridge traffic |
| Cloud VM | Routes VPC traffic |
| Home router (many models) | Linux kernel routing |
| VPN server | Routes encrypted traffic |
| Virtual firewall | Linux routing stack |
Once IP forwarding is enabled, Linux behaves exactly like a dedicated router.
Cleaning Up
Delete the namespaces.
sudo ip netns delete hostA
sudo ip netns delete hostB
Delete the bridges.
sudo ip link delete left-br
sudo ip link delete right-br
Disable forwarding if desired.
sudo sysctl -w net.ipv4.ip_forward=0
Key Takeaways
- A router forwards IP packets between different Layer 3 networks.
- Linux becomes a router when it has interfaces in multiple networks and IP forwarding is enabled.
- Hosts use their routing tables to determine whether a destination is local or remote.
- Remote packets are sent to the default gateway, not directly to the destination host.
- A router removes the incoming Ethernet header, makes a routing decision based on the destination IP address, and creates a new Ethernet header for the outgoing interface.
- The router decrements the packet's TTL before forwarding it.
- Linux automatically installs connected routes for directly attached networks.
Coming Next
So far, every virtual network we've built has used a single Layer 2 broadcast domain per bridge. In production environments, however, a single physical network is often divided into multiple logical networks using VLANs, while technologies like TUN and TAP create virtual interfaces for VPNs, virtualization, and userspace networking.
In the next chapter, we'll explore these additional Linux networking primitives and see how they extend the virtual networking toolbox beyond bridges and veth pairs.