home / blog / docker networks ← packets were harmed in this guide

Docker & Networking

Docker Bridge Subnets: Simulating Routers, Sniffing Traffic, & Debugging Routing Tables

Published: July 12, 2026 • 14 min read · ← Back to Blog

Quick Reference Sheet

What it is for:
Simulating multi-subnet routing configurations, analyzing transit network packets, and managing container firewall boundaries locally.
Who uses it:
Network Engineers, DevOps/SREs, Infrastructure Architects, and Cybersecurity Analysts testing software-defined network (SDN) configurations.
Where it is useful:
Isolating microservice subnets (e.g. front-desk cash terminals vs secure billing databases), auditing network payloads, and troubleshooting routing blocks.
Command Snippet Notice: You can copy configuration code blocks throughout this article using the Copy button. Always replace placeholders with your actual device details:
How to find the parameters for this guide:
  • net-router, net-client-a, net-client-b: These are the container names declared in docker-compose.yml. Run docker ps to see active container names.
  • 10.10.1.254 / 10.10.2.254: These are the static IP gateways allocated to the router interfaces. If you change your subnet ranges in Section 1, make sure to adjust these gateway IPs accordingly.

Docker containerization abstracts away network components. By default, containers are spun up under a shared bridge network where they can connect outwards but remain largely isolated from granular traffic management. In this guide, we build a Software-Defined Networking (SDN) laboratory featuring two separate virtual subnets, a custom dual-homed router node, and implement low-level routing tables, traffic sniffing, and firewall policies.

1. The Network Topology

We declare a virtual topology matching enterprise network zoning (such as keeping public front-desk clients isolated from secure datacenter billing databases):

  • Subnet A (POS Client Network): IP subnet 10.10.1.0/24
  • Subnet B (Secure Database Network): IP subnet 10.10.2.0/24
  • The Gateway Router Node: Dual-homed container acting as a gateway with interfaces in both subnets (Subnet A: 10.10.1.254, Subnet B: 10.10.2.254).

2. Docker Compose Infrastructure Declaration

To run this lab, we build a docker-compose.yml declaring Alpine clients and a privileged gateway router to allow IP forwarding configuration:

services:
  router-node:
    image: alpine:latest
    container_name: net-router
    privileged: true
    networks:
      subnet_a:
        ipv4_address: 10.10.1.254
      subnet_b:
        ipv4_address: 10.10.2.254
    command: sh -c "apk add --no-cache iproute2 iptables tcpdump && sysctl -w net.ipv4.ip_forward=1 && tail -f /dev/null"
    restart: always

  client-a:
    image: alpine:latest
    container_name: net-client-a
    cap_add:
      - NET_ADMIN
    networks:
      subnet_a:
        ipv4_address: 10.10.1.10
    command: sh -c "apk add --no-cache iproute2 tcpdump && ip route del default && ip route add default via 10.10.1.254 && tail -f /dev/null"

  client-b:
    image: alpine:latest
    container_name: net-client-b
    cap_add:
      - NET_ADMIN
    networks:
      subnet_b:
        ipv4_address: 10.10.2.10
    command: sh -c "apk add --no-cache iproute2 tcpdump && ip route del default && ip route add default via 10.10.2.254 && tail -f /dev/null"

networks:
  subnet_a:
    driver: bridge
    ipam:
      config:
        - subnet: 10.10.1.0/24
  subnet_b:
    driver: bridge
    ipam:
      config:
        - subnet: 10.10.2.0/24
Caution: Container Permissions
The privileged: true and cap_add: [NET_ADMIN] parameters grant containers permissions to modify kernel-level configurations (like routing tables and firewall rules). Verify that these nodes are strictly firewalled and isolated in production environments, as container escapes are significantly easier to execute from privileged containers.

3. Activating IP Packet Forwarding

By default, Linux kernels drop packets that are not addressed directly to the local host interface. To turn the router-node into a functional gateway, we enable IP forwarding. This allows the container to receive packets on Subnet A and transmit them outwards to Subnet B:

docker exec -it net-router sysctl -w net.ipv4.ip_forward=1
Troubleshooting: Router does not forward packets
Workaround: Verify that IP packet forwarding is actually enabled by running cat /proc/sys/net/ipv4/ip_forward inside the router container. If it returns 0, the setting was not applied; re-run the sysctl command above to write it to the kernel.

Next, we update the routing table of Client A to send all traffic outside of its local subnet via the router IP gateway address:

docker exec -it net-client-a ip route del default
docker exec -it net-client-a ip route add default via 10.10.1.254

Repeat the gateway declaration for Client B to redirect its outbound traffic back through the router:

docker exec -it net-client-b ip route del default
docker exec -it net-client-b ip route add default via 10.10.2.254

4. Sniffing Transit Network Traffic

To verify that packets are traversing the gateway node, run a tcpdump sniffer inside the router container to capture ICMP (ping) traffic:

docker exec -it net-router tcpdump -i any icmp -n -v

Open a separate host terminal session and trigger a ping packet sequence from Client A to Client B:

docker exec -it net-client-a ping -c 3 10.10.2.10

The sniffer terminal will output the raw transit flow: you will see packets arrive at the router's POS card (eth0) and leave out of the Database card (eth1):

14:25:57.216965 eth0 In  IP 10.10.1.10 > 10.10.2.10: ICMP echo request, id 22, seq 0, length 64
14:25:57.216989 eth1 Out IP 10.10.1.10 > 10.10.2.10: ICMP echo request, id 22, seq 0, length 64
14:25:57.217046 eth1 In  IP 10.10.2.10 > 10.10.1.10: ICMP echo reply, id 22, seq 0, length 64
14:25:57.217055 eth0 Out IP 10.10.2.10 > 10.10.1.10: ICMP echo reply, id 22, seq 0, length 64

Breakdown of fields:

  • In vs Out: Indicates if the packet entered the router's physical boundary or is being pushed back out.
  • eth0 vs eth1: Shows the virtual network card handling the transmission.
  • 10.10.1.10 > 10.10.2.10: The Source IP address sending the data to the Destination IP address.
  • ICMP echo request/reply: The ping protocol query and its corresponding answer.

5. Firewall Rule Filtering (`iptables`)

To simulate a firewall block where the public Client A should be blocked from reaching the database on Client B, open a shell inside the router container from your host terminal, and add an explicit drop rule to its FORWARD chain:

All three commands in this section are executed from your host machine terminal using docker exec to reach the router container's internal Linux kernel. The firewall rules are written into net-router's network namespace, not the host machine.

docker exec -it net-router iptables -A FORWARD -s 10.10.1.10 -d 10.10.2.10 -p icmp -j DROP
Caution: Iptables Evaluation Order
The Linux kernel evaluates iptables rules sequentially from top to bottom. If you place a broad permissive rule above your specific block rule, the block rule will be bypassed completely. To insert a block rule at the absolute top of the chain, use the -I (Insert) flag instead of -A (Append): e.g. docker exec -it net-router iptables -I FORWARD 1 ...

This tells the Linux kernel firewall running inside net-router: If any ICMP packet arrives with source IP 10.10.1.10 and destination IP 10.10.2.10, drop it immediately.

To view your active rules with line numbers, run inside the router container:

docker exec -it net-router iptables -L FORWARD -n -v --line-numbers

To delete the block and restore traffic routing, remove the rule by its index number:

docker exec -it net-router iptables -D FORWARD 1
Troubleshooting: Firewall block rules have no effect
Workaround: Iptables filters sequentially. If you have broad ACCEPT rules defined before your drop rule, the traffic is permitted before it reaches your block. Insert the rule at the top of the chain using the -I flag: docker exec -it net-router iptables -I FORWARD 1 -s 10.10.1.10 -d 10.10.2.10 -p icmp -j DROP.

Related Reads

“The network is the computer.”

— John Gage