- Get link
- X
- Other Apps
- Get link
- X
- Other Apps
In the world of network engineering, virtualization, and infrastructure automation, the ability to simulate complex topologies — routers, firewalls, gateways, multi-subnet environments — without requiring physical hardware or heavy hypervisors is not just convenient… it’s essential.
Enter Linux network namespaces.
Far from being a niche feature for kernel developers, network namespaces are one of the most powerful, lightweight, and underutilized tools in the Linux system administrator’s toolkit. They allow you to create isolated network stacks — complete with their own interfaces, routing tables, firewall rules, ARP tables, and even PID namespaces — all within a single Linux host.
And here’s the kicker: you can use them to build fully functional virtual routers.
Imagine this:
- You’re designing a new enterprise network topology.
- You need to test OSPF, BGP, NAT, VLAN trunking, or policy-based routing.
- You don’t have access to Cisco/Juniper gear.
- You want to avoid VMware ESXi or Proxmox overhead.
With just a few ip
commands and some basic shell scripting, you can create a multi-router lab environment on your laptop — no VMs, no containers, no cloud credits.
This article is your definitive, 5000-word guide to transforming Linux network namespaces into production-grade virtual routers. We’ll cover everything from the fundamentals of namespace isolation to building a full multi-router topology with dynamic routing protocols, NAT, and traffic inspection — all using only native Linux tools.
By the end, you’ll be able to:
- Create and manage multiple network namespaces
- Connect them via virtual Ethernet pairs (veth)
- Configure IP forwarding and routing tables
- Implement NAT and firewall rules
- Run routing daemons like Quagga or FRRouting inside namespaces
- Simulate real-world WAN/LAN topologies
- Debug and monitor traffic with tcpdump and netstat
Whether you're a network engineer preparing for certification, a DevOps professional automating infrastructure tests, or a student learning networking fundamentals — this guide will equip you with skills that are directly applicable in both academic and industrial environments.
Let’s begin at the beginning.
Section 1: Understanding Linux Network Namespaces — The Foundation
1.1 What Is a Network Namespace?
A network namespace is a Linux kernel feature that provides an isolated instance of the network stack. Each namespace has its own:
- Network interfaces (
lo
,eth0
, etc.) - Routing tables
- Firewall rules (iptables/nftables)
- Socket bindings
- Network device lists
- ARP cache
- ICMP settings
- Sysctl parameters (e.g.,
net.ipv4.ip_forward
)
Processes running inside a namespace see only the network resources assigned to that namespace. They cannot see interfaces or routes defined in other namespaces — unless explicitly connected.
Think of it as having multiple independent Linux machines running on the same physical kernel — each with its own “view” of the network.
🔍 Key Insight: Unlike containers (Docker, Podman), which use namespaces among other things, network namespaces alone give you raw, unadorned control over the network stack — perfect for router simulation.
1.2 How Do Network Namespaces Work Under the Hood?
The Linux kernel maintains a global network namespace by default — the one you interact with when you run ip addr show
or route -n
.
When you create a new network namespace, the kernel clones the current network state and gives it a unique identifier (a file descriptor). Processes attached to this namespace inherit its network configuration.
You can attach processes to a namespace using:
nsenter
unshare
- Or by starting a process inside it via
ip netns exec
Network namespaces are persistent until deleted — they exist even if no process is attached.
You can list them:
bash
1
ip netns list
Create one:
bash
1
sudo ip netns add router1
Delete one:
bash
1
sudo ip netns delete router1
To execute a command inside a namespace:
bash
1
sudo ip netns exec router1 ip addr show
This runs ip addr show
inside the router1
namespace — showing only the interfaces visible there (initially, just lo
).
1.3 Why Not Use Containers or VMs?
VMs | High (full OS) | Medium | Slow | Limited (hypervisor abstraction) |
Containers | Low (shared kernel) | High | Fast | Partial (cgroups, seccomp, apparmor interfere) |
Network Namespaces | Minimal (kernel-only) | Highest | Instant | Full raw access |
For pure network simulation — especially routing, packet filtering, and protocol testing — namespaces win hands down.
You get:
- Full iptables control
- No Docker daemon interference
- Direct access to
/proc/net/
- Ability to run custom kernel modules or patched drivers
- No container runtime dependencies
This makes namespaces ideal for:
- Network certification labs (CCNA, CCNP, JNCIA)
- Testing SD-WAN architectures
- Building IoT gateway simulators
- Developing network monitoring tools
- Academic research on routing protocols
Section 2: Building Your First Virtual Router — Step-by-Step
We’ll now construct a minimal but functional virtual router using two network namespaces: one acting as a “LAN,” another as a “WAN,” and a third as the actual router.
2.1 Setup Overview
Our topology:
1
2
[Client] --(veth pair)-- [Router] --(veth pair)-- [Server]
(ns1) (ns_router) (ns2)
ns1
: Client machine (192.168.1.10/24)ns_router
: Virtual router with two interfacesns2
: Server machine (192.168.2.10/24)
The router will forward packets between the two subnets using IP forwarding and static routes.
2.2 Step 1: Create the Namespaces
Open a terminal (as root or with sudo):
bash
1
2
3
4
# Create three namespaces
sudo ip netns add client
sudo ip netns add server
sudo ip netns add router
Verify creation:
bash
1
2
3
4
5
ip netns list
# Output:
# client
# server
# router
2.3 Step 2: Create Virtual Ethernet Pairs (veth)
A veth (virtual ethernet) pair consists of two endpoints connected like a pipe. Data sent into one end comes out the other.
We need two veth pairs:
- One connecting
client
→router
- One connecting
router
→server
bash
1
2
3
4
5
# Create first pair: client ↔ router
sudo ip link add veth-client type veth peer name veth-router1
# Create second pair: router ↔ server
sudo ip link add veth-server type veth peer name veth-router2
Check created links:
bash
1
2
3
4
5
6
ip link show
# You should see:
# veth-client: <BROADCAST,MULTICAST> mtu 1500 ...
# veth-router1: <BROADCAST,MULTICAST> mtu 1500 ...
# veth-server: <BROADCAST,MULTICAST> mtu 1500 ...
# veth-router2: <BROADCAST,MULTICAST> mtu 1500 ...
2.4 Step 3: Move Veth Endpoints Into Their Respective Namespaces
Now assign each end of the veth pair to its namespace:
bash
1
2
3
4
5
6
7
8
9
# Move client-side veth into client namespace
sudo ip link set veth-client netns client
# Move router-side veths into router namespace
sudo ip link set veth-router1 netns router
sudo ip link set veth-router2 netns router
# Move server-side veth into server namespace
sudo ip link set veth-server netns server
Now, if you run ip link show
in the host, you won't see those interfaces anymore — because they’ve been moved.
2.5 Step 4: Configure IP Addresses and Bring Interfaces Up
We now configure each interface with an IP address and bring it up.
Configure Client (ns1)
bash
1
2
3
sudo ip netns exec client ip addr add 192.168.1.10/24 dev veth-client
sudo ip netns exec client ip link set veth-client up
sudo ip netns exec client ip link set lo up
Configure Server (ns2)
bash
1
2
3
sudo ip netns exec server ip addr add 192.168.2.10/24 dev veth-server
sudo ip netns exec server ip link set veth-server up
sudo ip netns exec server ip link set lo up
Configure Router (ns_router)
The router needs two interfaces:
- One facing the client:
veth-router1
→ 192.168.1.1/24 - One facing the server:
veth-router2
→ 192.168.2.1/24
bash
1
2
3
4
5
6
sudo ip netns exec router ip addr add 192.168.1.1/24 dev veth-router1
sudo ip netns exec router ip addr add 192.168.2.1/24 dev veth-router2
sudo ip netns exec router ip link set veth-router1 up
sudo ip netns exec router ip link set veth-router2 up
sudo ip netns exec router ip link set lo up
2.6 Step 5: Enable IP Forwarding on the Router
By default, Linux does not forward packets between interfaces. For a device to act as a router, we must enable IP forwarding.
Inside the router namespace:
bash
1
sudo ip netns exec router sysctl -w net.ipv4.ip_forward=1
To make it permanent across reboots (if needed), edit /etc/sysctl.conf
on the host:
bash
1
2
echo "net.ipv4.ip_forward = 1" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
But since we’re working in namespaces, this setting applies only to the router’s namespace — perfect!
2.7 Step 6: Set Default Routes on Client and Server
Each endpoint needs to know where to send packets destined outside its local subnet.
On the client:
bash
1
sudo ip netns exec client ip route add default via 192.168.1.1
On the server:
bash
1
sudo ip netns exec server ip route add default via 192.168.2.1
These routes tell both machines: “If I want to reach anything not in my /24, send it to the router.”
2.8 Step 7: Test Connectivity
Now test ping from client to server:
bash
1
sudo ip netns exec client ping -c 3 192.168.2.10
✅ If successful, you’ll see:
1
2
3
4
PING 192.168.2.10 (192.168.2.10) 56(84) bytes of data.
64 bytes from 192.168.2.10: icmp_seq=1 ttl=63 time=0.152 ms
64 bytes from 192.168.2.10: icmp_seq=2 ttl=63 time=0.118 ms
64 bytes from 192.168.2.10: icmp_seq=3 ttl=63 time=0.121 ms
Congratulations! You’ve built your first virtual router using only Linux network namespaces.
2.9 What Just Happened? — The Packet Flow
Let’s trace a packet from client to server:
- Client (
192.168.1.10
) sends ICMP echo request to192.168.2.10
- Since destination is not local, client consults routing table → default gateway =
192.168.1.1
- Packet is sent out
veth-client
→ arrives atveth-router1
inside router namespace - Router receives packet on
veth-router1
, checks destination IP - Finds that
192.168.2.10
is reachable viaveth-router2
(directly connected network) - Router forwards packet out
veth-router2
→ arrives atveth-server
in server namespace - Server replies: packet goes back through reverse path
All this happens without any external software — just kernel routing and veth bridges.
Section 3: Deep Dive — Advanced Router Configuration
Now that we’ve got connectivity, let’s elevate our router from a simple Layer 3 forwarder to a full-fledged network device with advanced features.
3.1 Adding a Third Subnet — Multi-Homed Router
Let’s extend our topology to include a third LAN segment.
New topology:
1
2
3
4
[Client1] --(veth)-- [Router] --(veth)-- [Server]
\ /
\ /
[Client2] -----(veth)--
We’ll add a second client on a different subnet: 10.0.0.0/24
Step 1: Create New Namespace and Veth Pair
bash
1
2
3
4
sudo ip netns add client2
sudo ip link add veth-client2 type veth peer name veth-router3
sudo ip link set veth-client2 netns client2
sudo ip link set veth-router3 netns router
Step 2: Assign IPs
bash
1
2
3
4
5
6
7
8
# Client2
sudo ip netns exec client2 ip addr add 10.0.0.10/24 dev veth-client2
sudo ip netns exec client2 ip link set veth-client2 up
sudo ip netns exec client2 ip link set lo up
# Router side
sudo ip netns exec router ip addr add 10.0.0.1/24 dev veth-router3
sudo ip netns exec router ip link set veth-router3 up
Step 3: Add Route on Client2
bash
1
sudo ip netns exec client2 ip route add default via 10.0.0.1
Step 4: Add Static Route on Router for Client2’s Network
Wait — the router already knows about 10.0.0.0/24
because it’s directly connected. But what if we wanted to reach 192.168.1.0/24
from 10.0.0.0/24
?
It already works — because the router has interfaces on all three networks.
Test ping from client2
to server
:
bash
1
sudo ip netns exec client2 ping -c 2 192.168.2.10
✅ Works! The router acts as a multi-homed gateway — routing between three distinct subnets.
3.2 Simulating a Real-World WAN Link with Delay and Loss
Real networks aren’t perfect. Let’s simulate latency and packet loss on the link between router and server.
We’ll use tc
(traffic control) to shape traffic on veth-router2
inside the router namespace.
bash
1
2
3
# Introduce 100ms delay + 5% packet loss on the WAN-facing interface
sudo ip netns exec router tc qdisc add dev veth-router2 root handle 1: prio
sudo ip netns exec router tc qdisc add dev veth-router2 parent 1:1 handle 10: netem delay 100ms loss 5%
Now test ping from client to server:
bash
1
sudo ip netns exec client ping -c 4 192.168.2.10
Output might look like:
1
2
3
4
64 bytes from 192.168.2.10: icmp_seq=1 ttl=63 time=105 ms
64 bytes from 192.168.2.10: icmp_seq=2 ttl=63 time=104 ms
Request timed out
64 bytes from 192.168.2.10: icmp_seq=4 ttl=63 time=103 ms
You’ve now simulated a slow, unreliable WAN link — useful for testing VoIP apps, backup systems, or SD-WAN failover logic.
To remove the shaping later:
bash
1
sudo ip netns exec router tc qdisc del dev veth-router2 root
3.3 Applying iptables Rules — The Router as a Firewall
Let’s turn our router into a stateful firewall.
We’ll block ICMP from client2
to server
, but allow HTTP traffic.
First, install iptables
inside the router namespace (if not available):
bash
1
sudo ip netns exec router apt-get update && sudo ip netns exec router apt-get install -y iptables
Now apply rules:
bash
1
2
3
4
5
6
7
8
9
10
11
# Block ICMP from client2 subnet (10.0.0.0/24) to server (192.168.2.10)
sudo ip netns exec router iptables -A FORWARD -s 10.0.0.0/24 -d 192.168.2.10 -p icmp -j DROP
# Allow HTTP (port 80) from client2 to server
sudo ip netns exec router iptables -A FORWARD -s 10.0.0.0/24 -d 192.168.2.10 -p tcp --dport 80 -j ACCEPT
# Allow established connections back
sudo ip netns exec router iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
# Default deny on FORWARD chain
sudo ip netns exec router iptables -P FORWARD DROP
Now test:
bash
1
2
3
4
5
6
7
8
# From client2, try to ping server → should fail
sudo ip netns exec client2 ping -c 1 192.168.2.10
# Start a simple web server on server
sudo ip netns exec server python3 -m http.server 80 &
# Try to curl from client2
sudo ip netns exec client2 curl -s --connect-timeout 5 http://192.168.2.10:80
✅ You should see the HTML response — meaning HTTP works, ICMP is blocked.
You’ve just built a stateful firewall with zero hardware — purely in software, using Linux kernel features.
3.4 Configuring NAT (Network Address Translation)
Suppose the server network (192.168.2.0/24
) is your “internal” network, and you want clients on 10.0.0.0/24
to access the internet through the router — but the server doesn’t have a public IP.
We’ll simulate NAT (masquerading) so outbound traffic appears to come from the router’s WAN interface.
Assume the server has a route to the internet via the router’s veth-router2
— which connects to a hypothetical upstream gateway.
Actually, we don’t have an upstream yet. So let’s simulate it.
Step 1: Add a Fourth Namespace — “Internet”
bash
1
2
3
4
sudo ip netns add internet
sudo ip link add veth-internet type veth peer name veth-router4
sudo ip link set veth-internet netns internet
sudo ip link set veth-router4 netns router
Step 2: Assign IPs
bash
1
2
3
4
5
6
7
8
# Internet namespace gets 203.0.113.2/24 (public IP)
sudo ip netns exec internet ip addr add 203.0.113.2/24 dev veth-internet
sudo ip netns exec internet ip link set veth-internet up
sudo ip netns exec internet ip link set lo up
# Router side
sudo ip netns exec router ip addr add 203.0.113.1/24 dev veth-router4
sudo ip netns exec router ip link set veth-router4 up
Step 3: Set Default Route on Router to “Internet”
bash
1
sudo ip netns exec router ip route add default via 203.0.113.2
Step 4: Enable MASQUERADE (NAT)
We want traffic from 10.0.0.0/24
to appear as coming from 203.0.113.1
when leaving via veth-router4
.
bash
1
sudo ip netns exec router iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -o veth-router4 -j MASQUERADE
Step 5: Enable IP Forwarding Again (just in case)
bash
1
sudo ip netns exec router sysctl -w net.ipv4.ip_forward=1
Step 6: Simulate DNS/HTTP on Internet Namespace
Install nginx
or start a Python server in the “internet” namespace:
bash
1
sudo ip netns exec internet python3 -m http.server 80 &
Step 7: Test from Client2 to “Internet”
From client2
(10.0.0.10), try:
bash
1
sudo ip netns exec client2 curl -s http://203.0.113.2
✅ You should receive the HTTP response.
Even though client2
has private IP 10.0.0.10
, the packet reaches 203.0.113.2
because the router translated the source to 203.0.113.1
.
This is SNAT (Source NAT) — exactly how home routers work.
You’ve now built a full NAT-enabled virtual router capable of connecting private LANs to a simulated public internet.
Section 4: Running Routing Protocols — OSPF and BGP Inside Namespaces
Static routes are fine for labs, but real routers use dynamic protocols.
Let’s install FRRouting (FRR) — a modern fork of Quagga — inside our router namespace and configure OSPF.
4.1 Install FRRouting
Since we’re inside a namespace, we need to install packages inside it.
bash
1
2
3
4
5
6
7
8
# Update package list inside router namespace
sudo ip netns exec router apt-get update
# Install FRR
sudo ip netns exec router apt-get install -y frr frr-pythontools
# Enable services (optional for systemd, but we'll start manually)
sudo ip netns exec router systemctl enable frr
💡 Note: Systemd doesn’t work inside namespaces. We’ll start daemons manually.
4.2 Configure OSPF Between Two Routers
We’ll now expand our topology to include two virtual routers, connected via a point-to-point link, running OSPF.
Topology:
1
[Client1] --(192.168.1.0/24)-- [RouterA] --(10.10.10.0/30)-- [RouterB] --(192.168.2.0/24)-- [Client2]
Step 1: Create Second Router Namespace
bash
1
sudo ip netns add routerb
Step 2: Create veth Pair Between RouterA and RouterB
bash
1
2
3
sudo ip link add veth-ra-rb type veth peer name veth-rb-ra
sudo ip link set veth-ra-rb netns router
sudo ip link set veth-rb-ra netns routerb
Step 3: Assign IPs to Point-to-Point Link
Use /30
for p2p links (standard for OSPF):
bash
1
2
3
4
5
6
7
# RouterA side
sudo ip netns exec router ip addr add 10.10.10.1/30 dev veth-ra-rb
sudo ip netns exec router ip link set veth-ra-rb up
# RouterB side
sudo ip netns exec routerb ip addr add 10.10.10.2/30 dev veth-rb-ra
sudo ip netns exec routerb ip link set veth-rb-ra up
Step 4: Configure OSPF on Both Routers
Edit FRR config files inside each namespace.
On RouterA (/etc/frr/frr.conf
inside router ns)
bash
1
2
3
4
5
6
7
8
9
10
11
12
13
sudo ip netns exec router bash -c 'cat > /etc/frr/frr.conf <<EOF
hostname RouterA
password zebra
enable password zebra
router ospf
ospf router-id 1.1.1.1
network 192.168.1.0/24 area 0
network 10.10.10.0/30 area 0
log-adjacency-changes
line vty
EOF'
On RouterB (/etc/frr/frr.conf
inside routerb ns)
bash
1
2
3
4
5
6
7
8
9
10
11
12
13
sudo ip netns exec routerb bash -c 'cat > /etc/frr/frr.conf <<EOF
hostname RouterB
password zebra
enable password zebra
router ospf
ospf router-id 2.2.2.2
network 192.168.2.0/24 area 0
network 10.10.10.0/30 area 0
log-adjacency-changes
line vty
EOF'
Step 5: Start FRR Daemons
Start zebra
and ospfd
manually:
bash
1
2
3
4
5
6
7
# On RouterA
sudo ip netns exec router /usr/lib/frr/zebra -d
sudo ip netns exec router /usr/lib/frr/ospfd -d
# On RouterB
sudo ip netns exec routerb /usr/lib/frr/zebra -d
sudo ip netns exec routerb /usr/lib/frr/ospfd -d
✅ Zebra manages interfaces and kernel routing.
✅ OSPFd handles the OSPF protocol exchange.
Step 6: Verify OSPF Neighbors
Connect to FRR CLI:
bash
1
2
3
4
5
6
7
8
9
10
11
# On RouterA
sudo ip netns exec router vtysh
RouterA# show ip ospf neighbor
# Should show:
# Neighbor ID Pri State Dead Time Address Interface RXmtL RqstL DBsmL
# 2.2.2.2 1 Full/DR 00:00:36 10.10.10.2 veth-ra-rb:10.10.10.1 0 0 0
RouterA# show ip route
# Should show:
# O 192.168.2.0/24 [110/20] via 10.10.10.2, veth-ra-rb, 00:01:23
Same on RouterB:
bash
1
2
3
4
sudo ip netns exec routerb vtysh
RouterB# show ip ospf neighbor
RouterB# show ip route
# Should see: O 192.168.1.0/24 [110/20] via 10.10.10.1
🎉 Now both routers dynamically learn each other’s subnets via OSPF.
Test connectivity from Client1 to Client2:
bash
1
2
# Assuming Client1 is 192.168.1.10, Client2 is 192.168.2.10
sudo ip netns exec client1 ping -c 3 192.168.2.10
✅ It works — without any static routes!
You’ve now implemented a dynamic routing protocol entirely in software using Linux namespaces.
4.3 Bonus: Simulating BGP with Multiple ASes
Want to go further? Add a third router running BGP.
Create routerc
namespace, connect it to routerb
via another /30
link.
Assign AS numbers:
routera
: AS 65001routerb
: AS 65002routerc
: AS 65003
Configure eBGP between routerb
and routerc
.
FRR supports BGP natively:
bash
1
2
3
router bgp 65002
neighbor 10.20.20.2 remote-as 65003
network 192.168.2.0/24
And on routerc
:
bash
1
2
3
router bgp 65003
neighbor 10.20.20.1 remote-as 65002
network 192.168.3.0/24
After restarting FRR daemons, check:
bash
1
2
show ip bgp summary
show ip route
You’ll see routes learned via BGP — mimicking real ISP peering.
This is how network engineers test multi-AS topologies before deploying to production.
Section 5: Automation and Scripting — Turning Lab Topologies Into Reusable Code
Manually typing ip netns exec ...
commands is tedious. Let’s automate.
5.1 Bash Script: Build a Complete Router Lab
Save this as build-virtual-router.sh
:
bash
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#!/bin/bash
echo "Configuring interfaces..."
# Client 1
ip netns exec $NS_CLIENT ip addr add 192.168.1.10/24 dev veth-client
ip netns exec $NS_CLIENT ip link set veth-client up
ip netns exec $NS_CLIENT ip link set lo up
ip netns exec $NS_CLIENT ip route add default via 192.168.1.1
# Client 2
ip netns exec $NS_CLIENT ip addr add 10.0.0.10/24 dev veth-client2
ip netns exec $NS_CLIENT ip link set veth-client2 up
ip netns exec $NS_CLIENT ip route add default via 10.0.0.1
# Server
ip netns exec $NS_SERVER ip addr add 192.168.2.10/24 dev veth-server
ip netns exec $NS_SERVER ip link set veth-server up
ip netns exec $NS_SERVER ip link set lo up
ip netns exec $NS_SERVER ip route add default via 192.168.2.1
# Internet
ip netns exec $NS_INTERNET ip addr add 203.0.113.2/24 dev veth-internet
ip netns exec $NS_INTERNET ip link set veth-internet up
ip netns exec $NS_INTERNET ip link set lo up
# Router
ip netns exec $NS_ROUTER ip addr add 192.168.1.1/24 dev veth-router1
ip netns exec $NS_ROUTER ip addr add 192.168.2.1/24 dev veth-router2
ip netns exec $NS_ROUTER ip addr add 10.0.0.1/24 dev veth-router3
ip netns exec $NS_ROUTER ip addr add 203.0.113.1/24 dev veth-router4
ip netns exec $NS_ROUTER ip link set veth-router1 up
ip netns exec $NS_ROUTER ip link set veth-router2 up
ip netns exec $NS_ROUTER ip link set veth-router3 up
ip netns exec $NS_ROUTER ip link set veth-router4 up
ip netns exec $NS_ROUTER ip link set lo up
# Enable forwarding
ip netns exec $NS_ROUTER sysctl -w net.ipv4.ip_forward=1
# Enable NAT
ip netns exec $NS_ROUTER iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -o veth-router4 -j MASQUERADE
# Block ICMP from 10.0.0.0/24 to server
ip netns exec $NS_ROUTER iptables -A FORWARD -s 10.0.0.0/24 -d 192.168.2.10 -p icmp -j DROP
ip netns exec $NS_ROUTER iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
ip netns exec $NS_ROUTER iptables -P FORWARD DROP
echo "Lab built successfully!"
echo "Starting HTTP server on server..."
ip netns exec $NS_SERVER python3 -m http.server 80 &
echo "Starting HTTP server on internet..."
ip netns exec $NS_INTERNET python3 -m http.server 80 &
echo ""
echo "=== LAB READY ==="
echo "Test connectivity:"
echo " ping from client: sudo ip netns exec client ping 192.168.2.10"
echo " curl from client: sudo ip netns exec client curl http://203.0.113.2"
echo " inspect routes: sudo ip netns exec router ip route show"
echo " view firewall: sudo ip netns exec router iptables -L FORWARD -n -v"
echo ""
echo "To clean up: Ctrl+C or run './build-virtual-router.sh' again"
Make executable:
bash
1
2
chmod +x build-virtual-router.sh
./build-virtual-router.sh
Now you have a reusable, portable, scriptable virtual router lab — perfect for CI/CD pipelines, documentation, or training materials.
5.2 Using Docker Compose? No — Use This Instead
Many people think “I’ll just use Docker.” But containers are overkill.
Your script above uses no external dependencies — only iproute2
, iptables
, and python3
. Runs on any Linux box — even embedded systems.
No Docker daemon. No orchestration. Just pure Linux networking.
That’s power.
Section 6: Monitoring, Debugging, and Troubleshooting
Even the best setups break. Here’s how to debug.
6.1 Inspect Routing Tables
bash
1
2
sudo ip netns exec router ip route show
sudo ip netns exec router ip route show table all
6.2 View ARP Cache
bash
1
sudo ip netns exec router ip neigh show
If ARP entries are missing, check link layer connectivity.
6.3 Capture Traffic with tcpdump
bash
1
2
3
4
5
6
7
8
# On router interface facing client
sudo ip netns exec router tcpdump -i veth-router1 -n
# On server
sudo ip netns exec server tcpdump -i veth-server -n
# On client
sudo ip netns exec client tcpdump -i veth-client -n
Watch for:
- ICMP requests going out
- No replies → check routing/firewall
- No ARP resolution → check link state
6.4 Check Kernel Logs
bash
1
sudo dmesg | grep -i "veth\|netns"
Look for errors like “device already exists” or “cannot move device.”
6.5 Use ss and netstat
bash
1
2
sudo ip netns exec router ss -tuln
sudo ip netns exec router netstat -rn
Confirm services are listening on correct interfaces.
6.6 FRR Debugging
In vtysh
:
bash
1
2
3
4
debug ospf event
debug ospf packet
debug ospf lsa
show ip ospf database
Use these to verify neighbor adjacency, LSA flooding, SPF calculations.
Section 7: Real-World Use Cases — Where This Matters
You might think: “This is cool, but when would I ever use this?”
Here are 10 production-relevant scenarios:
Network Certification Labs | CCNA/CCNP/JNCIA exams require hands-on labs. Namespaces replicate real gear without cost. |
CI/CD for Network Automation | Test Ansible playbooks against virtual routers before deploying to live devices. |
SD-WAN Simulation | Simulate multiple sites, ISPs, and failover policies with dynamic routing. |
IoT Gateway Development | Test MQTT brokers, NAT traversal, and firewall rules on embedded-like topologies. |
Security Research | Isolate malware C2 traffic in a sandboxed namespace. |
Educational Tools | Teach subnetting, routing, NAT, and firewalling visually — students see real packet flow. |
Kubernetes CNI Testing | Validate Calico, Cilium, or Flannel behavior without spinning up clusters. |
Legacy Protocol Testing | Run RIP, EIGRP, or even DECnet in isolated environments. |
Traffic Engineering | Simulate QoS policies, bandwidth throttling, and congestion control. |
Disaster Recovery Drills | Recreate failed network segments locally to test recovery scripts. |
One company reduced their network lab costs from $50k (Cisco hardware) to $0 — using nothing but Ubuntu servers and network namespaces.
Section 8: Limitations and Gotchas
Nothing is perfect. Here’s what you need to know.
8.1 No GUI — It’s All CLI
You won’t get Wireshark GUI inside a namespace. Use tcpdump
+ wireshark
on host to capture.
8.2 No DHCP Server by Default
If you need dynamic IPs, install isc-dhcp-server
inside a namespace and configure scopes.
8.3 No Multicast by Default
Enable multicast support:
bash
1
sudo ip netns exec router sysctl -w net.ipv4.icmp_echo_ignore_broadcasts=0
And ensure IGMP snooping is enabled if bridging.
8.4 Cannot Bind to Host Ports Directly
Services inside namespaces are isolated. To expose port 80 from router
namespace to host:
bash
1
2
# Use port forwarding via iptables DNAT
sudo iptables -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination 172.17.0.2:80
Or use socat
:
bash
1
socat TCP-LISTEN:8080,fork TCP:172.17.0.2:80
8.5 Performance Limits
Namespaces are fast — but not faster than real NICs. Don’t expect 10Gbps throughput on a veth pair unless your host has enough CPU/RAM.
For high-throughput testing, consider SR-IOV or DPDK — but that’s beyond scope.
8.6 Persistent Across Reboot? No
Unless you write init scripts or systemd units, namespaces vanish on reboot.
Solution: Use systemd-nspawn
or wrap your setup in a service file.
Example systemd unit (/etc/systemd/system/virt-router.service
):
ini
1
2
3
4
5
6
7
8
9
10
11
[Unit]
Description=Virtual Router Lab
After=network.target
[Service]
Type=oneshot
ExecStart=/opt/virt-router/build-virtual-router.sh
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
Then:
bash
1
2
sudo systemctl enable virt-router
sudo systemctl start virt-router
Section 9: Going Further — Beyond Basic Routers
Once you master the basics, explore:
9.1 Bridging Namespaces
Instead of point-to-point links, create a bridge inside a namespace to connect multiple clients to one subnet.
bash
1
2
3
4
5
sudo ip netns exec router ip link add br0 type bridge
sudo ip netns exec router ip link set veth-router1 master br0
sudo ip netns exec router ip link set veth-router3 master br0
sudo ip netns exec router ip addr add 192.168.1.1/24 dev br0
sudo ip netns exec router ip link set br0 up
Now both client
and client2
are on the same broadcast domain — perfect for simulating a switch + router combo.
9.2 VLAN Tagging
Use vconfig
or ip link add ... type vlan
inside namespaces to simulate trunk ports.
bash
1
2
sudo ip netns exec router ip link add link veth-router1 name veth-router1.10 type vlan id 10
sudo ip netns exec router ip addr add 192.168.10.1/24 dev veth-router1.10
9.3 Combine with WireGuard
Run WireGuard tunnels inside namespaces to simulate encrypted site-to-site VPNs.
9.4 Integrate with Mininet
Mininet (used for SDN research) uses network namespaces internally. Learn how they do it — then extend it.
9.5 Build a Virtual ISP Network
Simulate Tier 1, Tier 2, and Tier 3 ISPs with BGP, route reflectors, and communities.
Section 10: Conclusion — The Power of Linux Networking
Linux network namespaces are not a gimmick. They are a foundational technology that empowers engineers to recreate, test, and validate entire network infrastructures — without spending a dime on hardware.
You’ve learned:
- How to create isolated network stacks
- How to interconnect them with veth pairs
- How to enable routing and NAT
- How to run dynamic routing protocols (OSPF/BGP)
- How to simulate real-world conditions (latency, loss, firewalls)
- How to automate everything with scripts
- How to troubleshoot like a pro
- And how to apply this knowledge in real jobs, labs, and research
This skill set is rare — and incredibly valuable.
Employers don’t hire people who can click through GUIs. They hire people who understand how packets move through layers — and can build, break, and fix networks from the ground up.
With network namespaces, you’re no longer dependent on vendor equipment. You own the network stack.
You are the operator.
You are the architect.
You are the engineer.
Go build something amazing.
Appendix A: Quick Reference Commands
List namespaces | ip netns list |
Create namespace | ip netns add <name> |
Delete namespace | ip netns delete <name> |
Execute command in ns | ip netns exec <name> <cmd> |
Create veth pair | ip link add <name1> type veth peer name <name2> |
Move interface to ns | ip link set <iface> netns <name> |
Enable IP forwarding | sysctl -w net.ipv4.ip_forward=1 |
Apply NAT | iptables -t nat -A POSTROUTING -s <subnet> -o <iface> -j MASQUERADE |
Block traffic | iptables -A FORWARD -s <src> -d <dst> -j DROP |
Show routes | ip route show |
Show neighbors | ip neigh show |
Capture traffic | tcpdump -i <iface> |
Install FRR | apt install frr |
Start FRR daemon | /usr/lib/frr/zebra -d && /usr/lib/frr/ospfd -d |
Access FRR CLI | vtysh |
Appendix B: Recommended Reading
- Linux Network Namespace Documentation: https://man7.org/linux/man-pages/man7/network_namespaces.7.html
- FRRouting Official Docs: https://docs.frrouting.org/
- Linux Advanced Routing & Traffic Control HOWTO: https://lartc.org/
- “Linux Networking Architecture” by Thomas Graf
- “Computer Networks: A Systems Approach” – Peterson & Davie (Chapter on IP Routing)
Final Word
Network namespaces transform your Linux machine into a laboratory of infinite possibilities. Whether you’re studying for certifications, automating deployments, researching protocols, or simply satisfying curiosity — this tool belongs in your core skillset.
There’s no better way to learn networking than by building it yourself — piece by piece, packet by packet.
So close this article.
Open your terminal.
Run sudo ip netns add lab
.
And start building.
The network is yours to command.
Comments
Post a Comment