Skip to content
Lesson 7 of 19

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
  1. A bridgedocker0, usually 172.17.0.1/16. It behaves like an unmanaged switch: frames in on one port go out the others.
  2. 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.
  3. NAT — an iptables MASQUERADE rule that rewrites the source address of packets leaving 172.17.0.0/16 to 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 doesYour simulator does
ip link add br0 type bridge; ip addr add 172.17.0.1 dev br0BRIDGE <name> <gw_ip>
ip netns add c1NETNS <name>
ip link add ... type veth peer ...; ip link set ... netns c1VETH <bridge> <netns> <ns_ip> — records both ends at once
ip route add <cidr> dev eth0ROUTE <netns> <cidr>
the FIB lookup, then the MASQUERADE rule, then ENETUNREACHSEND <netns> <dst>DIRECT, NAT, or NO ROUTE
bridge link / brctl showSHOW-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.

Up nextcgroups: Memory Limitscgroups & Resource Limits

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…