Linux Networking for Lab Hosts

Updated

September 4, 2026

Linux Networking for Lab Hosts

Containerlab sits on Linux networking primitives: namespaces, veth pairs, bridges, routes, and basic filtering. If you only drive YAML without understanding the host, debugging feels like magic. This chapter builds the host-side intuition you need before (and during) Containerlab work.

Learning goals

By the end of this chapter you can:

  • Explain how network namespaces isolate stacks
  • Create veth pairs and bridges by hand
  • Add addresses and routes; verify with ip route get
  • Use tcpdump inside a namespace
  • Map Containerlab links to veth/bridge concepts
  • Apply minimal filter awareness (nft/iptables, rp_filter)

Learning goals in practice

You will build a two-namespace router lab without Containerlab, then tear it down. That is the same physics Containerlab orchestrates for you.

Core objects

Object Role
Interface L2 endpoint (physical, veth, bridge, vlan subint)
Network namespace Isolated network stack (interfaces, routes, sockets)
veth pair Virtual cable: two ends, often in different namespaces
Bridge Software switch in the kernel
Route FIB entry: destination → device / next hop
Rule Policy routing (advanced; optional here)
ip -br link
ip -br addr
ip netns list
ip route

Network namespaces

A namespace process sees the root namespace. Lab nodes (containers) each get a namespace.

# Create two namespaces
sudo ip netns add ns1
sudo ip netns add ns2
ip netns list

# Run a command inside
sudo ip netns exec ns1 ip link
sudo ip netns exec ns1 ping -c1 127.0.0.1

Loopback exists per namespace; bring it up if you care about local sockets:

sudo ip netns exec ns1 ip link set lo up

veth pairs: virtual cables

# Create pair veth-a <-> veth-b
sudo ip link add veth-a type veth peer name veth-b

# Move ends into namespaces
sudo ip link set veth-a netns ns1
sudo ip link set veth-b netns ns2

# Name + address inside each ns
sudo ip netns exec ns1 ip link set veth-a name eth0
sudo ip netns exec ns2 ip link set veth-b name eth0

sudo ip netns exec ns1 ip addr add 10.0.0.1/24 dev eth0
sudo ip netns exec ns2 ip addr add 10.0.0.2/24 dev eth0

sudo ip netns exec ns1 ip link set eth0 up
sudo ip netns exec ns2 ip link set eth0 up

# Test L3 on-link
sudo ip netns exec ns1 ping -c 2 10.0.0.2

Predict: ARP resolves; ICMP works; no router required because same subnet.

Observe:

sudo ip netns exec ns1 ip neigh
sudo ip netns exec ns1 tcpdump -ni eth0 -c 10 arp or icmp &
sudo ip netns exec ns1 ping -c 2 10.0.0.2

Bridges: software switches

Connect multiple endpoints as if on a switch:

sudo ip netns add h1
sudo ip netns add h2
sudo ip netns add h3

sudo ip link add br0 type bridge
sudo ip link set br0 up

# veth for each host: host side in ns, peer on host attached to bridge
for n in 1 2 3; do
  sudo ip link add veth-h$n type veth peer name veth-b$n
  sudo ip link set veth-h$n netns h$n
  sudo ip netns exec h$n ip link set veth-h$n name eth0
  sudo ip netns exec h$n ip addr add 10.10.0.$n/24 dev eth0
  sudo ip netns exec h$n ip link set eth0 up
  sudo ip netns exec h$n ip link set lo up
  sudo ip link set veth-b$n master br0
  sudo ip link set veth-b$n up
done

sudo ip netns exec h1 ping -c 2 10.10.0.2
sudo ip netns exec h1 ping -c 2 10.10.0.3

Bridge FDB (MAC table):

bridge fdb show br br0
# or
ip neigh  # not the same; FDB is L2 forward database
bridge link show

After pings, you should see learned MACs on the bridge ports.

Routing between namespaces

Classic: two LANs and a router namespace.

h1 (10.1.0.10/24) -- veth -- r (10.1.0.1 + 10.2.0.1) -- veth -- h2 (10.2.0.10/24)
sudo ip netns add h1
sudo ip netns add h2
sudo ip netns add r

# h1 <-> r
sudo ip link add v1 type veth peer name r1
sudo ip link set v1 netns h1
sudo ip link set r1 netns r
sudo ip netns exec h1 ip link set v1 name eth0
sudo ip netns exec r  ip link set r1 name eth1
sudo ip netns exec h1 ip addr add 10.1.0.10/24 dev eth0
sudo ip netns exec r  ip addr add 10.1.0.1/24  dev eth1
sudo ip netns exec h1 ip link set eth0 up
sudo ip netns exec r  ip link set eth1 up
sudo ip netns exec h1 ip link set lo up
sudo ip netns exec r  ip link set lo up

# h2 <-> r
sudo ip link add v2 type veth peer name r2
sudo ip link set v2 netns h2
sudo ip link set r2 netns r
sudo ip netns exec h2 ip link set v2 name eth0
sudo ip netns exec r  ip link set r2 name eth2
sudo ip netns exec h2 ip addr add 10.2.0.10/24 dev eth0
sudo ip netns exec r  ip addr add 10.2.0.1/24  dev eth2
sudo ip netns exec h2 ip link set eth0 up
sudo ip netns exec r  ip link set eth2 up
sudo ip netns exec h2 ip link set lo up

# Enable forwarding on router ns
sudo ip netns exec r sysctl -w net.ipv4.ip_forward=1

# Default routes on hosts
sudo ip netns exec h1 ip route add default via 10.1.0.1
sudo ip netns exec h2 ip route add default via 10.2.0.1

# Test
sudo ip netns exec h1 ping -c 3 10.2.0.10
sudo ip netns exec h1 traceroute -n 10.2.0.10

Predict → observe:

sudo ip netns exec h1 ip route get 10.2.0.10
sudo ip netns exec r  ip route get 10.2.0.10 from 10.1.0.10 iif eth1
sudo ip netns exec r  ip -s link show eth1

Failure drill: disable forwarding

sudo ip netns exec r sysctl -w net.ipv4.ip_forward=0
sudo ip netns exec h1 ping -c 2 10.2.0.10   # expect fail
sudo ip netns exec r sysctl -w net.ipv4.ip_forward=1

Harden note: container routers must allow forwarding; images often set this, but verify.

Failure drill: missing return route

If the “router” only had a connected route on one side (misconfigured static elsewhere), one-way traffic appears. With connected routes on both interfaces above, both directions work. Later static chapters amplify return-path bugs.

VLAN subinterfaces (preview of trunks)

# On a namespace with eth0
sudo ip netns exec h1 ip link add link eth0 name eth0.10 type vlan id 10
sudo ip netns exec h1 ip addr add 10.10.10.1/24 dev eth0.10
sudo ip netns exec h1 ip link set eth0.10 up

Both ends need matching VLAN IDs. Bridges can be VLAN-aware (vlan_filtering); Containerlab L2 labs often use OVS or NOS switches—concepts transfer.

Policy leftovers that break labs

rp_filter (reverse path filtering)

Strict reverse-path checks drop asymmetric legitimate lab traffic.

sysctl net.ipv4.conf.all.rp_filter
sysctl net.ipv4.conf.default.rp_filter
# Per-iface:
# sysctl net.ipv4.conf.eth0.rp_filter

If mysterious one-way drops appear on Linux routers, check rp_filter and counters.

Forwarding and firewall

# nftables quick view
sudo nft list ruleset
# legacy
sudo iptables -L -n -v
sudo iptables -t nat -L -n -v

Host firewalls (especially Docker’s chains) can interact with lab bridges. When a lab “should work” and does not, inspect host filter tables—not only node configs.

Disable offloads when captures look weird (optional)

Checksum offload can make tcpdump show wrong checksums on local veth. Prefer understanding this over panicking:

sudo ethtool -k veth0 | grep sum || true

Mapping to Containerlab

You did by hand Containerlab roughly does
ip netns add Creates container network namespaces
veth pairs Links between nodes
bridges / OVS Multipoint wiring, mgmt network
addressing From topology + image startup
ip_forward Inside router nodes

When Containerlab deploy fails or links are dark, drop to:

ip netns list
# Containerlab often names netsns like clab-<lab>-<node>
sudo ip netns exec clab-mylab-r1 ip -br a

Exact naming depends on lab name and version; containerlab inspect helps.

sudo containerlab inspect -t topology.clab.yml

Clean teardown habits

Namespaces and veths litter a host if you exit mid-lab.

# Example cleanup for the objects you created
sudo ip netns del h1 2>/dev/null || true
sudo ip netns del h2 2>/dev/null || true
sudo ip netns del r  2>/dev/null || true
sudo ip netns del ns1 2>/dev/null || true
sudo ip netns del ns2 2>/dev/null || true
sudo ip link del br0 2>/dev/null || true
# dangling veths usually die with netns; if not:
ip -br link | grep veth || true

Prefer scripted up.sh / down.sh even for hand labs.

Example down.sh

#!/usr/bin/env bash
set -euo pipefail
for ns in h1 h2 r ns1 ns2; do
  sudo ip netns del "$ns" 2>/dev/null || true
done
sudo ip link del br0 2>/dev/null || true
echo "cleaned"

Predict → observe → fix drill (checklist)

Lab: three hosts on br0 as above.

  1. Predict: h1→h3 uses bridge flood then learn; FDB populates.
  2. Observe: bridge fdb show, tcpdump on veth-b1.
  3. Fix inject: sudo ip link set veth-b3 down — h3 unreachable; FDB ages/fails.
  4. Harden: document “access port down vs cable unplug” equivalence in labs.

Useful one-liners cheatsheet

# Watch neigh events
ip monitor neigh

# Route decision
ip route get 1.1.1.1 from 10.1.0.10 iif eth0

# Stats
ip -s -s link show eth0

# All addresses every ns (rough)
for ns in $(ip netns list | awk '{print $1}'); do
  echo "===== $ns ====="
  sudo ip netns exec "$ns" ip -br a
done

Security note for shared hosts

Namespace labs are powerful. On a shared machine:

  • Do not bridge lab interfaces onto trusted production networks without filters
  • Clean up after sessions
  • Avoid enabling ip_forward on the root namespace unless you intend the host to route

Containerlab management networks still need isolation discipline on corporate laptops.

Summary

  • Namespaces isolate full network stacks—foundation of container networking
  • veth pairs are virtual cables; bridges are virtual switches
  • Router namespaces need addressing, up state, forwarding, and routes both ways
  • Host firewall and rp_filter are common “invisible” lab breakers
  • Hand-building once makes Containerlab YAML readable and debuggable

Next: Containerlab fundamentals—topology YAML, kinds, links, lifecycle—on top of these primitives.