How can I use net namespace as a virtual router on Linux

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
ip netns list

Create one:

bash
sudo ip netns add router1

Delete one:

bash
sudo ip netns delete router1

To execute a command inside a namespace:

bash
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:

[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 interfaces
  • ns2: 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
# Create three namespaces
sudo ip netns add client
sudo ip netns add server
sudo ip netns add router

Verify creation:

bash
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 clientrouter
  • One connecting routerserver
bash
# 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
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
# 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
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
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
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
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
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
sudo ip netns exec client ip route add default via 192.168.1.1

On the server:

bash
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
sudo ip netns exec client ping -c 3 192.168.2.10

✅ If successful, you’ll see:

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:

  1. Client (192.168.1.10) sends ICMP echo request to 192.168.2.10
  2. Since destination is not local, client consults routing table → default gateway = 192.168.1.1
  3. Packet is sent out veth-client → arrives at veth-router1 inside router namespace
  4. Router receives packet on veth-router1, checks destination IP
  5. Finds that 192.168.2.10 is reachable via veth-router2 (directly connected network)
  6. Router forwards packet out veth-router2 → arrives at veth-server in server namespace
  7. 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:

[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
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
# 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
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
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
# 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
sudo ip netns exec client ping -c 4 192.168.2.10

Output might look like:

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
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
sudo ip netns exec router apt-get update && sudo ip netns exec router apt-get install -y iptables

Now apply rules:

bash
# 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
# 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
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
# 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
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
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
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
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
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
# 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:

[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
sudo ip netns add routerb
Step 2: Create veth Pair Between RouterA and RouterB
bash
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
# 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
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
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
# 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
# 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
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
# 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 65001
  • routerb: AS 65002
  • routerc: AS 65003

Configure eBGP between routerb and routerc.

FRR supports BGP natively:

bash
router bgp 65002
neighbor 10.20.20.2 remote-as 65003
network 192.168.2.0/24

And on routerc:

bash
router bgp 65003
neighbor 10.20.20.1 remote-as 65002
network 192.168.3.0/24

After restarting FRR daemons, check:

bash
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
#!/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
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
sudo ip netns exec router ip route show
sudo ip netns exec router ip route show table all

6.2 View ARP Cache

bash
sudo ip netns exec router ip neigh show

If ARP entries are missing, check link layer connectivity.

6.3 Capture Traffic with tcpdump

bash
# 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
sudo dmesg | grep -i "veth\|netns"

Look for errors like “device already exists” or “cannot move device.”

6.5 Use ss and netstat

bash
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
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
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
# 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
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
[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
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
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
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


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

Below Post Ad