TTR Networks

Beginner · Office gateway: DHCP, DNS and VRRP on VyOS · section 6 of 6

Build the lab yourself

✓ Lab-tested 18 October 2026 on VyOS 2026.10.16-0024-rolling

The whole lab runs in one container with no Docker networking: two VyOS VMs, a LAN bridge and an ISP bridge, two PCs and the ISP as network namespaces (the ISP runs dnsmasq as its resolver and a small web server). Put ./vyos (from the Packets That Never Come Back lesson's get-vyos.sh) next to these files and run docker compose up -d --build. Apply each router's stages in order (R2 uses priority 100, the range .150–.199 and its own address .3). To fail a router, kill its VM: docker compose exec lab pkill -f console-r1.sock.

lab-compose.yaml
# Needs ./vyos (from the tcpdump-and-vyos lesson's get-vyos.sh). Then: docker compose up -d --build
# Consoles:   docker compose exec -it lab socat -,raw,echo=0,escape=0x1d UNIX-CONNECT:/run/console-r1.sock   (or console-r2)
# PCs:        docker compose exec lab ip netns exec pc1 sh        (pc2, isp likewise)
name: vyos-gateway-lab
services:
  lab:
    build:
      context: .
      dockerfile_inline: |
        FROM debian:bookworm-slim
        RUN apt-get update && apt-get install -y --no-install-recommends \
          qemu-system-x86 iproute2 python3 socat procps tcpdump iputils-ping dnsmasq isc-dhcp-client dnsutils curl
        COPY start-gwlab.sh /usr/local/bin/start-gwlab.sh
        CMD ["/usr/local/bin/start-gwlab.sh"]
    hostname: lab
    privileged: true
    network_mode: none
    volumes: ["./vyos:/vyos:ro"]
  • Lines 2–3: How to reach the consoles and the PCs.
  • Line 17: No Docker networking: everything is built inside the container.
start-gwlab.sh
#!/bin/sh
# One container, no Docker networking. brlan: the office LAN (two PCs, both routers' eth0).
# brwan: the ISP segment (an ISP namespace, both routers' eth1). Two VyOS VMs, r1 and r2.
set -e
sysctl -qw net.ipv6.conf.all.disable_ipv6=1 net.ipv6.conf.default.disable_ipv6=1
for br in brlan brwan; do ip link add "$br" type bridge; ip link set "$br" up; done
for n in 1 2; do
  ip netns add "pc$n"; mkdir -p "/etc/netns/pc$n"; : > "/etc/netns/pc$n/resolv.conf"
  ip link add "pc$n-lan" type veth peer name eth0 netns "pc$n"
  ip -n "pc$n" link set eth0 address "02:00:00:00:00:0$n"
  ip netns exec "pc$n" sysctl -qw net.ipv6.conf.all.disable_ipv6=1
  ip -n "pc$n" link set lo up; ip -n "pc$n" link set eth0 up
  ip link set "pc$n-lan" master brlan up
done
# The ISP: a gateway on the WAN segment, a DNS resolver (192.0.2.53) and a web server (192.0.2.80)
ip netns add isp
ip link add isp-wan type veth peer name eth0 netns isp
ip link set isp-wan master brwan up
ip netns exec isp sh -c 'sysctl -qw net.ipv6.conf.all.disable_ipv6=1 net.ipv4.ip_forward=1
  ip link set lo up; ip link set eth0 up
  ip addr add 203.0.113.1/29 dev eth0
  ip addr add 192.0.2.53/32 dev lo; ip addr add 192.0.2.80/32 dev lo
  ip route add 10.0.0.0/24 via 203.0.113.4'
ip netns exec isp dnsmasq --listen-address=192.0.2.53 --bind-interfaces --no-resolv --no-hosts \
  --address=/server.example.net/192.0.2.80 --log-queries --log-facility=/var/log/dnsmasq.log
mkdir -p /srv/www && echo 'hello from server.example.net' > /srv/www/index.html
ip netns exec isp sh -c 'cd /srv/www && exec python3 -m http.server --bind 192.0.2.80 80 >/var/log/http.log 2>&1' &
for vm in 1 2; do
  for side in lan wan; do ip tuntap add "r$vm-$side" mode tap; ip link set "r$vm-$side" master "br$side" up; done
  qemu-system-x86_64 -m 1280 -smp 1 -cpu max -accel tcg,thread=multi \
    -uuid "00000000-0000-4000-8000-00000000000$vm" \
    -kernel /vyos/vmlinuz -initrd /vyos/initrd.img \
    -append "boot=live components hostname=vyos username=live nopersistence noautologin nonetworking union=overlay console=ttyS0,115200 net.ifnames=0 biosdevname=0" \
    -cdrom /vyos/vyos.iso \
    -netdev tap,id=lan,ifname="r$vm-lan",script=no,downscript=no -device virtio-net-pci,netdev=lan,mac="52:54:00:0$vm:00:01" \
    -netdev tap,id=wan,ifname="r$vm-wan",script=no,downscript=no -device virtio-net-pci,netdev=wan,mac="52:54:00:0$vm:01:01" \
    -display none -serial "unix:/run/console-r$vm.sock,server=on,wait=off" -monitor none &
done
wait
  • Line 6: The LAN and ISP segments.
  • Lines 7–14: Two PCs on the LAN, each with its own resolv.conf for the DHCP client.
  • Lines 16–27: The ISP: gateway, resolver and web server.
  • Lines 28–38: Two VyOS VMs, each with a LAN and a WAN NIC.

Where next

  • Setting up VyOS with NATPrerequisite · Beginner · 45 min

    A VyOS router with a NAT interface for a private LAN and a public interface for a routed public subnet: plain routing, source NAT, port forwarding and the forward firewall that NAT isn't. Every output comes from a real VyOS lab.

  • Switching basics: layer 2, VLANs and IP addressingPrerequisite · Beginner · 40 min

    How a switch learns MAC addresses and floods broadcasts, how IP subnets and default gateways decide where packets go, how VLANs split a switch into separate networks, and how trunks and a router connect them. Built on a real switch, three PCs and a VyOS router.

  • Linux Network ConnectivityAlso covers DNS · Beginner · 80 min

    Find out why a Linux machine can't reach something, layer by layer: interfaces and addresses, routes and ip route get, ARP and the neighbour table, ping and traceroute, DNS, and listening sockets — with real output from a three-host lab.

  • Zone-based firewall on VyOSAlso in Office & branch networks · Intermediate · 35 min

    Rebuild the NAT lesson's router with a zone-based firewall: WAN, LAN, a DMZ and the router itself, with one ruleset per direction. Includes the missing-return-traffic mistake, post-NAT matching, and a full test matrix.