TTR Networks

Intermediate · Debugging BGP: every session state, reproduced · section 8 of 8

Build the lab yourself

✓ Lab-tested 18 October 2026 on FRR 10.2.1, Docker Desktop (Linux kernel without TCP-MD5)

The lab runs on any machine with Docker. The three routers use under 100 MB of memory between them (about 22 MB each here) and no virtual machines, because FRR runs happily in containers. Every file you need is on this page.

Put the files in one directory: the compose file, the Dockerfile, the daemons file and the three router configs from earlier in the lesson (r1.conf, r2.conf and r3.conf). Then:

docker compose -f lab-compose.yaml up -d --build
docker compose -f lab-compose.yaml exec r1 vtysh

Within about 20 seconds show bgp summary on r1 should show r2 established with 4 prefixes received and 1 sent.

Breaking it

Each scenario in this lesson is one or two changes. Make them in vtysh (configure terminal), watch, then put things back. docker compose -f lab-compose.yaml up -d --force-recreate returns all three routers to the files' configuration in a few seconds.

ScenarioChange
Admin shutdownr1: router bgp 64500 → neighbor 192.0.2.1 shutdown
Max-prefixr1: address-family ipv4 unicast → neighbor 192.0.2.1 maximum-prefix 3
Wrong addressr1: neighbor 192.0.2.9 remote-as 64501
Peer not configuredr2: router bgp 64501 → no neighbor 192.0.2.0
Firewallr2 shell: iptables -A INPUT -i eth0 -p tcp --dport 179 -j DROP and the same with --sport 179
Wrong remote ASr1: neighbor 192.0.2.1 remote-as 64999
Loopbacks without multihopr1: neighbor 198.51.100.243 remote-as 64501, … update-source lo; r3: the same towards 198.51.100.241 with remote-as 64500
Hold timerboth ends: neighbor … timers 5 15, clear, then drop all traffic between r1 and r2 with iptables on r2
Missing policyr1: address-family ipv4 unicast → no neighbor 192.0.2.1 route-map FROM-R2 in and … TO-R2 out
Import filterr1: ip prefix-list FROM-R2 seq 5 deny 198.51.100.64/26, then clear bgp 192.0.2.1 soft in
Unreachable next hopr2: address-family ipv4 unicast → no neighbor 192.0.2.3 next-hop-self

To watch packets, run tcpdump -lnni eth0 tcp port 179 in a second shell on the router (docker compose -f lab-compose.yaml exec r1 sh). To follow the FSM, debug bgp neighbor-events in vtysh together with log file /var/log/frr/frr.log debugging, then read the log.

On macOS and Windows, Docker Desktop's kernel has no TCP-MD5 support, so the password scenario won't fail the way it would on a real router. Most Linux distribution kernels do include TCP-MD5, so on a Linux host it should behave like a real router (we haven't captured that here).

lab-compose.yaml
# BGP debugging lab: r1 (AS 64500) -- r2 (AS 64501) -- r3 (AS 64501).
# Run from this directory:  docker compose -f lab-compose.yaml up -d --build
# Then: docker compose -f lab-compose.yaml exec r1 vtysh
name: bgp-debug-lab
x-router: &router
  build: .
  image: ttr-frr-debug:10.2.1
  privileged: true            # iptables and tcpdump inside the routers
  sysctls: { net.ipv4.ip_forward: 1 }
services:
  r1:
    <<: *router
    hostname: r1
    volumes: ["./daemons:/etc/frr/daemons:ro", "./r1.conf:/etc/frr/frr.conf:ro"]
    networks: { r1r2: { interface_name: eth0 } }
  r2:
    <<: *router
    hostname: r2
    volumes: ["./daemons:/etc/frr/daemons:ro", "./r2.conf:/etc/frr/frr.conf:ro"]
    networks: { r1r2: { interface_name: eth0 }, r2r3: { interface_name: eth1 } }
  r3:
    <<: *router
    hostname: r3
    volumes: ["./daemons:/etc/frr/daemons:ro", "./r3.conf:/etc/frr/frr.conf:ro"]
    networks: { r2r3: { interface_name: eth0 } }
# Layer-2-only links: Docker assigns no IPv4, so FRR's addresses are the only ones.
networks:
  r1r2:
    enable_ipv4: false
    enable_ipv6: true
    ipam: { config: [{ subnet: "fd00:12::/64" }] }
  r2r3:
    enable_ipv4: false
    enable_ipv6: true
    ipam: { config: [{ subnet: "fd00:23::/64" }] }
  • Lines 5–9: One definition shared by all three routers: the FRR image with iptables and tcpdump, privileged so those tools work, and IP forwarding so r2 routes between r1 and r3.
Dockerfile
# FRR 10.2.1 plus the tools the lab uses to break things and watch them break.
FROM quay.io/frrouting/frr:10.2.1
RUN apk add --no-cache iptables tcpdump \
 && mkdir -p /var/log/frr && chown frr:frr /var/log/frr \
 && echo "service integrated-vtysh-config" > /etc/frr/vtysh.conf
daemons
bgpd=yes
ospfd=no
ospf6d=no
ripd=no
ripngd=no
isisd=no
pimd=no
ldpd=no
nhrpd=no
eigrpd=no
babeld=no
sharpd=no
pbrd=no
bfdd=no
fabricd=no
vrrpd=no
pathd=no
staticd=yes
vtysh_enable=yes
zebra_options="  -A 127.0.0.1 -s 90000000"
bgpd_options="   -A 127.0.0.1"
staticd_options="-A 127.0.0.1"

Where next

  • BGP FundamentalsPrerequisite · Intermediate · 75 min

    How the Border Gateway Protocol glues the internet together: autonomous systems, sessions, path attributes, best-path selection, iBGP scaling and a hands-on eBGP lab with real FRR configuration.

  • Packets That Never Come BackAlso covers troubleshooting, tcpdump · Intermediate · 75 min

    Advanced packet capture with tcpdump, and how to find where packets die on a VyOS network: missing NAT and return routes, asymmetric routing through a stateful firewall, and MTU black holes, all diagnosed on a real VyOS router.

  • EVPN and VXLAN: layer 2 over a routed fabricAlso covers BGP · Advanced · 45 min

    Stretch a layer-2 segment across a routed leaf-spine fabric: VXLAN for the data plane, BGP EVPN for the control plane. Built with FRR: the underlay, VNIs, type-2 and type-3 routes, route targets, and the VXLAN packets on the wire.

  • Linux Network ConnectivityAlso covers troubleshooting · 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.