The starter lab
The starter lab puts all three kinds of node in one topology: a native FRR router, an emulated VyOS router and a Linux host, with eBGP between the two routers. It's a template you can grow into any of this site's labs, and every output below comes from running it on this Mac.
FRR (AS 64500) announces its loopback, 198.51.100.1/32. VyOS (AS 64501) announces the host's LAN, 10.0.0.0/24. When BGP is up, the host can reach the FRR loopback through VyOS, so one ping tests everything at once: two Docker networks, the bridge and tap plumbing inside the VM's container, x86 VyOS under emulation, native FRR, and BGP between them.
The VyOS configuration is applied through the console driver, as six set commands and a commit. The memory numbers at the end are the reason to budget carefully: the whole lab uses under 2 GB, and almost all of it is the VM.
- eBGP across the native/emulated boundary. FRR (arm64) and VyOS (x86 under QEMU) peer over the core network; they can't tell the difference.
- One ping tests everything. The host pings FRR's loopback: through VyOS's tap and bridge, the emulated router, BGP's route, and the native one.
# A starter lab for an Apple Silicon Mac: a native FRR router, an x86 VyOS VM
# (QEMU inside a container) and a Linux host, on layer-2-only Docker networks.
# ./get-vyos.sh # once: download the VyOS ISO into ./vyos
# docker compose -f lab-compose.yaml up -d --build
# docker compose -f lab-compose.yaml exec vyos vyos-console.py wait
name: arm-lab
services:
vyos:
build: { context: ., dockerfile: Dockerfile.vyos }
image: ttr-vyos-qemu
cap_add: [NET_ADMIN] # bridges and taps inside the container
devices: ["/dev/net/tun:/dev/net/tun"]
environment: { VYOS_MEM: "1536", VYOS_SMP: "1", VM_ID: "01" }
volumes:
- ./vyos:/vyos:ro # vyos.iso, vmlinuz, initrd.img
- ./vyos-console.py:/usr/local/bin/vyos-console.py:ro
networks: { core: { interface_name: eth0 }, lan: { interface_name: eth1 } }
frr:
image: quay.io/frrouting/frr:10.2.1 # multi-arch: runs natively on arm64
privileged: true
volumes: ["./frr-daemons:/etc/frr/daemons:ro", "./frr.conf:/etc/frr/frr.conf:ro", "./vtysh.conf:/etc/frr/vtysh.conf:ro"]
networks: { core: { interface_name: eth0 } }
host:
image: alpine
privileged: true # lets it set its own address and route
command: sh -c "ip addr add 10.0.0.10/24 dev eth0 && ip route add default via 10.0.0.1 && sleep infinity"
networks: { lan: { interface_name: eth0 } }
# Layer-2-only networks: Docker assigns no IPv4 addresses or routes, so every
# address in the lab is one you configured.
networks:
core: { enable_ipv4: false, enable_ipv6: true, ipam: { config: [{ subnet: "fd00:a1::/64" }] } }
lan: { enable_ipv4: false, enable_ipv6: true, ipam: { config: [{ subnet: "fd00:a2::/64" }] } }- Lines 8–17: The VyOS VM's container: NET_ADMIN and /dev/net/tun for its bridges and taps, 1.5 GB for the VM, one vCPU, and a unique VM_ID.
- Lines 15–16: The ISO files and the console driver, mounted read-only.
- Lines 18–22: FRR, straight from its multi-arch image. The
vtysh.confmount silences a warning on every command. - Lines 23–26: A tiny Alpine host that configures its own address and default route at start-up.
- Lines 30–32: Both lab networks are layer-2-only.
frr version 10.2.1
frr defaults traditional
hostname frr
service integrated-vtysh-config
!
interface eth0
ip address 192.0.2.0/31
exit
!
interface lo
ip address 198.51.100.1/32
exit
!
router bgp 64500
bgp router-id 198.51.100.1
! Lab only: exchange routes without import/export policy.
no bgp ebgp-requires-policy
neighbor 192.0.2.1 remote-as 64501
neighbor 192.0.2.1 description vyos
!
address-family ipv4 unicast
network 198.51.100.1/32
exit-address-family
exit- Lines 14–19: eBGP to VyOS. The lab turns off FRR's eBGP policy requirement to stay short; real sessions need import and export policy (see the BGP lessons).
- Line 22: Announce the loopback.
mac$ time docker compose -f lab-compose.yaml exec -T vyos vyos-console.py conf 'set interfaces ethernet eth0 address 192.0.2.1/31' 'set interfaces ethernet eth1 address 10.0.0.1/24' 'set protocols bgp system-as 64501' 'set protocols bgp neighbor 192.0.2.0 remote-as 64500' 'set protocols bgp neighbor 192.0.2.0 address-family ipv4-unicast' 'set protocols bgp address-family ipv4-unicast network 10.0.0.0/24'
configure
WARNING: You are currently configuring a live-ISO environment, changes will not persist until installed
[edit]
vyos@vyos# set interfaces ethernet eth0 address 192.0.2.1/31
[edit]
vyos@vyos# set interfaces ethernet eth1 address 10.0.0.1/24
[edit]
vyos@vyos# set protocols bgp system-as 64501
[edit]
vyos@vyos# set protocols bgp neighbor 192.0.2.0 remote-as 64500
[edit]
vyos@vyos# set protocols bgp neighbor 192.0.2.0 address-family ipv4-unicast
[edit]
vyos@vyos# set protocols bgp address-family ipv4-unicast network 10.0.0.0/24
[edit]
vyos@vyos# commit
[edit]
vyos@vyos# exit
Warning: configuration changes have not been saved.
exit
vyos@vyos:~$
real 0m30.437s
user 0m0.037s
sys 0m0.022s- Lines 5–15: Six
setlines: two interfaces, and BGP to FRR announcing the LAN. - Line 3: A live-ISO system: changes don't survive a restart, so keep your set commands in a file.
- Line 17: The commit, the slow part under emulation.
- Line 24: Thirty seconds for the whole session.
mac$ docker compose -f lab-compose.yaml exec -T vyos vyos-console.py op 'show ip bgp summary'
show ip bgp summary
IPv4 Unicast Summary:
BGP router identifier 192.0.2.1, local AS number 64501 VRF default vrf-id 0
BGP table version 2
RIB entries 2, using 320 bytes of memory
Peers 1, using 30 KiB of memory
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc
192.0.2.0 4 64500 5 5 2 0 0 00:00:10 1 2 N/A
Total number of neighbors 1
vyos@vyos:~$
mac$ docker compose -f lab-compose.yaml exec -T host ping -c 3 198.51.100.1
PING 198.51.100.1 (198.51.100.1): 56 data bytes
64 bytes from 198.51.100.1: seq=0 ttl=63 time=1.600 ms
64 bytes from 198.51.100.1: seq=1 ttl=63 time=0.653 ms
64 bytes from 198.51.100.1: seq=2 ttl=63 time=0.507 ms
--- 198.51.100.1 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.507/0.920/1.600 ms- Line 11: Established with FRR: 1 prefix received, 2 sent (the LAN, plus FRR's own loopback back to it, which FRR ignores).
- Lines 16–23: The host reaches FRR's loopback through the emulated VyOS router: everything works.
mac$ docker compose -f lab-compose.yaml exec -T frr vtysh -c 'show bgp ipv4 unicast'
BGP table version is 2, local router ID is 198.51.100.1, vrf id 0
Default local pref 100, local AS 64500
Status codes: s suppressed, d damped, h history, u unsorted, * valid, > best, = multipath,
i internal, r RIB-failure, S Stale, R Removed
Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self
Origin codes: i - IGP, e - EGP, ? - incomplete
RPKI validation codes: V valid, I invalid, N Not found
Network Next Hop Metric LocPrf Weight Path
*> 10.0.0.0/24 192.0.2.1 0 0 64501 i
*> 198.51.100.1/32 0.0.0.0 0 32768 i
Displayed 2 routes and 2 total paths- Line 11: The host's LAN, learned from VyOS (AS 64501).
mac$ docker stats --no-stream --format 'table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}' $(docker compose -f lab-compose.yaml ps -q)
NAME CPU % MEM USAGE / LIMIT
arm-lab-frr-1 0.00% 44.36MiB / 7.747GiB
arm-lab-host-1 0.00% 692KiB / 7.747GiB
arm-lab-vyos-1 1.92% 1.71GiB / 7.747GiB- Lines 3–4: The native router and host: 44 MiB and under 1 MiB.
- Line 5: The VyOS VM: 1.71 GiB. Budget for VMs, not containers.