Step 1 of 5 · Reading · ~3 min
Read
Linux Namespaces
veth + Bridge + NAT
A network namespace on its own is a machine with one unplugged loopback port. Everything that makes docker run -p 8080:80 work is built on top, from three primitives you can point at in ip link output on any host running Docker.
The three pieces
eth0 (host, public IP)
|
[NAT: MASQUERADE] iptables rewrites the source address
|
docker0 (172.17.0.1) the bridge: a virtual L2 switch
/ \
veth1 veth2 host ends of two cables
| |
eth0 in eth0 in container ends, one per netns
ns container_a ns container_b
172.17.0.2 172.17.0.3
- A bridge —
docker0, usually172.17.0.1/16. It behaves like an unmanaged switch: frames in on one port go out the others. - A veth pair per container — two virtual NICs created as a unit and permanently joined. One end is attached to the bridge; the other is moved into the container's network namespace and renamed
eth0. - NAT — an iptables
MASQUERADErule that rewrites the source address of packets leaving172.17.0.0/16to the host's own address.
Each piece is useless alone, and it is worth being able to say why: a veth pair with no bridge connects one container to one host interface and to no siblings; a bridge with no veth has nothing plugged into it; NAT with neither has no packets to rewrite.
The routing decision, which is the whole exercise
Inside the container the table is two lines, and every outbound packet is decided by them:
172.17.0.0/16 dev eth0 # on the bridge: deliver directly
default via 172.17.0.1 dev eth0 # everything else: hand to the gateway
A packet to 172.17.0.3 matches the first line: it goes out eth0, across the bridge, into the sibling — no NAT, no host routing, and the sibling sees the real source 172.17.0.2. A packet to 8.8.8.8 matches only the default route, reaches the host, and the MASQUERADE rule rewrites its source to the host's address — because 172.17.0.2 is private space that no router on the internet would know how to answer.
That is also the honest answer to "are containers on one bridge isolated from each other?" They are not. They share an L2 segment by construction; --network and firewall rules are what separate them, not namespaces.
Why the exercise is a simulator
Creating a bridge, a veth pair, or an iptables rule all require CAP_NET_ADMIN, and the sandbox that runs your code has it dropped. What survives the removal is exactly the interesting half: the decision procedure. Given a namespace, a destination, and a routing table, is this packet delivered directly, source-rewritten via a gateway, or dropped for want of a route?
Your exercise: veth + Bridge + NAT Router
| The kernel does | Your simulator does |
|---|---|
ip link add br0 type bridge; ip addr add 172.17.0.1 dev br0 | BRIDGE <name> <gw_ip> |
ip netns add c1 | NETNS <name> |
ip link add ... type veth peer ...; ip link set ... netns c1 | VETH <bridge> <netns> <ns_ip> — records both ends at once |
ip route add <cidr> dev eth0 | ROUTE <netns> <cidr> |
the FIB lookup, then the MASQUERADE rule, then ENETUNREACH | SEND <netns> <dst> — DIRECT, NAT, or NO ROUTE |
bridge link / brctl show | SHOW-BRIDGE <name> |
Match the prefix properly: /0 matches everything and /32 matches one address, so compare the destination and the network under a mask built from the prefix length rather than comparing strings. A namespace with no veth has no source address and no gateway — NO ROUTE, before any route matching happens.
Reference: man 8 ip-netns, man 8 bridge.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…