Skip to content

mi2428/frridge

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

125 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

frridge

A Linux-first FRR lab runner that creates router containers, wires them together with veth pairs and bridges.

Installation

Build from source

Install Go and Docker with buildx support first, then build and install the binaries with make install. By default, the binaries are installed to ~/.local/bin/frridge and ~/.local/bin/frridge-mp. Set INSTALL_BINDIR if you want to install them somewhere else.

$ git clone git@github.com:mi2428/frridge.git
$ make -C frridge install
$ make -C frridge image

The bundled Dockerfile builds the default frridge-frr:latest companion image from the latest FRR source. You can still point a topology at frrouting/frr:<tag> or another image that already contains FRR and vtysh.

Tip

Prebuilt tarballs are also available from GitHub Releases for macOS and Linux, with amd64 and arm64 builds for each platform. Each archive contains frridge, frridge-mp, and this README. Use frridge on a Linux host, or frridge-mp on macOS when you want to run the lab inside Multipass.

$ curl -L -o frridge.tar.gz https://github.com/mi2428/frridge/releases/download/v0.1.0/frridge-v0.1.0-darwin-arm64.tar.gz
$ tar -xzf ./frridge.tar.gz
$ chmod +x ./frridge-v0.1.0-darwin-arm64/frridge-mp

Usage

frridge talks to Docker and host netlink directly, so the main binary expects a real Linux host.

$ frridge --help

FRR lab runner for container-based network study

Usage:
  frridge [command]

Available Commands:
  completion  Generate the autocompletion script for the specified shell
  console     Open vtysh or a shell inside a running router container
  down        Remove containers and runtime networking for a lab
  help        Help about any command
  ping        Run YAML-defined ping checks and print the raw ping output
  up          Create containers, links, and initial router state

Flags:
  -f, --file string   Path to lab YAML
  -h, --help          help for frridge
  -v, --version       version for frridge

Use "frridge [command] --help" for more information about a command.

Create a lab, enter a router, and tear it down again:

$ sudo frridge up -f lab.yaml
$ sudo frridge ping -f lab.yaml
$ sudo frridge ping -f lab.yaml lf1-host-to-lf2-host
$ sudo frridge console rt1 -f lab.yaml
$ sudo frridge console rt1 -f lab.yaml --shell
$ sudo frridge down -f lab.yaml --purge

up creates containers, wires links, applies optional linux: dataplane objects, runs any remaining shell escape hatches, and then applies first-boot vtysh seed commands. ping runs the named checks from top-level pings: and prints the underlying ping(8) output unchanged. console opens vtysh by default. down --purge also removes generated lab files under lab.workdir.

macOS via Multipass

Use frridge-mp when you want to edit topology files on macOS but execute the lab in a Linux VM.

$ frridge-mp --help

Run frridge labs inside a Multipass Linux VM

Usage:
  frridge-mp [command]

Available Commands:
  completion  Generate the autocompletion script for the specified shell
  console     Open vtysh or a shell inside a running guest-backed router container
  down        Remove containers and runtime networking inside the guest
  exec        Run an arbitrary command inside the guest workspace
  help        Help about any command
  ping        Run YAML-defined ping checks inside the guest
  shell       Open a shell inside the guest at the mounted host workspace
  up          Create containers, links, and initial router state inside the guest

Flags:
      --cpus int          CPU count used when the instance is first launched
      --disk string       Disk size used when the instance is first launched
  -h, --help              help for frridge-mp
      --host-dir string   Host directory mounted into the guest for topology files and lab assets
      --image string      Ubuntu image used when the instance is first launched
      --instance string   Multipass instance name
      --memory string     Memory size used when the instance is first launched
      --repo-dir string   Path to the frridge source tree used to build the guest binary
  -v, --version           version for frridge-mp

Use "frridge-mp [command] --help" for more information about a command.

Every frridge-mp command auto-creates or updates the guest VM, mounts the requested host directory, and refreshes the guest-local frridge binary as needed.

When the repo contains a companion Dockerfile, frridge-mp first tries to reuse host-side Docker cache by building and exporting the image on macOS, then imports that archive into the guest. If host Docker is unavailable, it falls back to building the image inside the guest VM.

The common flow is:

$ frridge-mp up --repo-dir ~/src/frridge --host-dir ../toy-evpn-vxlan --file lab.yaml
$ frridge-mp ping --repo-dir ~/src/frridge --host-dir ../toy-evpn-vxlan --file lab.yaml
$ frridge-mp console --repo-dir ~/src/frridge --host-dir ../toy-evpn-vxlan --file lab.yaml rt1
$ frridge-mp down --repo-dir ~/src/frridge --host-dir ../toy-evpn-vxlan --file lab.yaml --purge

Note

--host-dir is the directory mounted into the guest. Keep the topology file and any bind-mounted lab assets below that tree. Runtime .frridge state stays on the guest filesystem rather than on the shared mount.

Supported YAML

This is the supported frridge YAML surface. The comments are the contract.

# Required schema version. This is the only version supported today.
apiVersion: frridge/v1alpha1     # required; the only schema version supported today

# Lab-wide metadata, runtime state location, and optional router-default overrides.
# When omitted, frridge fills in:
#   image: frridge-frr:latest
#   privileged: true
#   sysctls:
#     net.ipv4.ip_forward: "1"
#     net.ipv4.conf.all.rp_filter: "0"
lab:
  name: clos-manual              # required; used in container names and generated state paths
  workdir: .frridge              # optional; defaults to .frridge relative to this YAML file
  defaults:                      # optional; overrides the built-in router defaults above
    image: frrouting/frr:v10.6.1 # optional; use this when the whole lab wants a different image
    privileged: false            # optional; router-local values still win if set
    sysctls:                     # optional; merged onto the built-ins, then merged with router sysctls
      net.ipv4.conf.all.rp_filter: "2"

# Router definitions. Each router becomes one container.
routers:
  rt1:
    hostname: spine1             # optional; defaults to the router key (rt1 here)
    image: frrouting/frr:v10.6.1 # optional router-local override for lab.defaults.image or the built-in image
    command: ["/bin/sh", "-lc", "sleep infinity"] # optional; overrides the image CMD, useful for tool containers
    privileged: true             # optional router-local override for lab.defaults.privileged or the built-in true
    env:                         # optional container environment variables
      ROLE: spine
    loopbacks:                   # optional; each value must be valid CIDR
      - 10.255.0.1/32
      - 2001:db8:ffff::1/128
    mounts:                      # optional additional bind mounts
      - source: ./shared/rt1     # source is relative to this YAML file unless already absolute
        target: /lab             # target must be an absolute path inside the container
        readOnly: false          # optional; defaults to false
    sysctls:                     # optional router-local sysctls merged after built-ins and lab.defaults.sysctls
      net.ipv4.conf.eth1.rp_filter: "0"
    linux:                       # optional router-local Linux dataplane objects built after links and loopbacks
      vrfs:
        - name: tenant           # optional Linux VRF device
          table: 1100
      bonds:
        - name: bond0            # optional Linux bond device
          mode: active-backup
          interfaces: [eth2]     # optional existing router interfaces enslaved to the bond
      interfaces:
        - name: eth3             # optional existing interface to re-home or address
          master: tenant
          addresses: [10.20.20.1/24]
      veths:
        - name: lan0             # optional router-local veth pair
          peer: host0
          master: tenant
          addresses: [10.10.10.1/24]
          namespace:
            name: host
            ifname: eth0
            mac: 02:00:00:00:10:11
            addresses: [10.10.10.11/24]
            defaultVia: 10.10.10.1
      routes:
        - to: 10.255.0.2/32      # optional static routes in the router namespace
          via: 192.0.2.1         # optional; set via, dev, or both
          dev: eth1
      bridges:
        - name: br10             # optional Linux bridge device
          master: tenant
          mac: 02:00:00:00:10:01
          addrgenmode: none
          addresses: [10.10.10.1/24]
          interfaces: [bond0]    # optional existing router interfaces enslaved to the bridge
          vxlans:
            - name: vxlan100
              vni: 100
              local: 10.255.0.1
              nolearning: true
              addrgenmode: none
              bridgeSlave:       # optional bridge_slave settings after enslaving the VXLAN
                neighSuppress: true
                learning: false
          namespaces:
            - name: host
              ifname: eth0
              mac: 02:00:00:00:10:11
              addresses: [10.10.10.11/24]
              defaultVia: 10.10.10.1
    commands:                    # optional post-link startup commands
      - kind: shell              # shell is the escape hatch; it runs on every `frridge up` after `linux:`
        run: |
          ip netns exec host ping -c 1 -W 1 10.10.10.254 >/dev/null 2>&1 || true
      - kind: vtysh              # vtysh commands run once, then persist in /etc/frr/frr.conf
        run: |
          configure terminal
          router bgp 65000
           bgp router-id 10.255.0.1
           no bgp default ipv4-unicast

  rt2:
    loopbacks:
      - 10.255.0.2/32
    commands:
      - kind: vtysh
        run: |
          configure terminal
          router bgp 65000
           bgp router-id 10.255.0.2
           no bgp default ipv4-unicast

  host1: {}                      # empty router entries are valid and inherit the built-ins plus any lab.defaults

# Link definitions. Use p2p for point-to-point links and bridge for shared segments.
links:
  - name: rt1-rt2                # required; must be unique within the file
    type: p2p                    # required; supported values are p2p and bridge
    mtu: 1500                    # optional; applies to the host-side veth pair and router interfaces
    members:
      - router: rt1              # router must exist under routers:
        ifname: eth1             # required; must be unique per router across all links
        ipv4: 192.0.2.0/31       # optional; configured with `ip addr replace`
        mac: 02:00:00:00:01:01   # optional; configured before the interface is brought up
      - router: rt2
        ifname: eth1
        ipv4: 192.0.2.1/31

  - name: access
    type: bridge                 # bridge needs at least two members; p2p needs exactly two
    members:
      - router: rt1
        ifname: eth2
      - router: rt2
        ifname: eth2
      - router: host1
        ifname: eth1

# Optional ping checks. These do nothing during `up`; they are only executed
# when `frridge ping` or `frridge-mp ping` is invoked.
pings:
  - name: rt1-to-rt2-loopback    # required; must be unique within the file
    from:
      router: rt1                # required; ping runs inside this router container
      namespace: host            # optional; runs via `ip netns exec <namespace>` when set
      address: 10.255.0.1        # optional; passed to ping(8) as `-I <address>`
    to: 10.255.0.2               # required; target passed to ping(8) as-is
    count: 5                     # optional; defaults to 3 when omitted

Minimal Manual Lab

If you want to practice FRR by typing all routing config yourself, keep the YAML to routers plus links and omit commands entirely. After up, enter routers with frridge console <router> and configure them by hand.

# Minimal manual lab. Create the containers and links, then enter all FRR
# routing config yourself with `frridge console <router>`.
apiVersion: frridge/v1alpha1

# Lab metadata only. The built-in image/sysctl/privileged defaults are enough
# for a plain manual-routing lab.
lab:
  name: clos-manual

# Empty router entries are enough when you want to configure everything by hand.
routers:
  sp1: {}
  sp2: {}
  lf1: {}
  lf2: {}

# Clos fabric wiring only. No shell or vtysh seed commands are provided.
links:
  - name: lf1-sp1
    type: p2p
    members:
      - { router: lf1, ifname: eth1 }
      - { router: sp1, ifname: eth1 }
  - name: lf1-sp2
    type: p2p
    members:
      - { router: lf1, ifname: eth2 }
      - { router: sp2, ifname: eth1 }
  - name: lf2-sp1
    type: p2p
    members:
      - { router: lf2, ifname: eth1 }
      - { router: sp1, ifname: eth2 }
  - name: lf2-sp2
    type: p2p
    members:
      - { router: lf2, ifname: eth2 }
      - { router: sp2, ifname: eth2 }

Development

make release TAG=vX.Y.Z builds four release tarballs, pushes the Git tag, creates or updates the GitHub Release, and uploads the local dist/ artifacts. make mp-verify runs the Multipass-backed smoke test. Before releasing, this repository must have a clean working tree.

$ make

Development
  build            Build host binaries into bin/
  install          Build and install host binaries into INSTALL_BINDIR
  fmt              Format Go sources. Use CHECK_ONLY=1 to check without writing
  lint             Run Go static analysis
  test             Run unit tests
  check            Run formatting, lint, and unit tests
  clean            Remove local build artifacts

Multipass
  mp-shell         Open a shell in the Multipass workspace
  mp-verify        Run the Multipass-backed smoke test
  mp-stop          Stop the Multipass VM
  mp-delete        Delete the Multipass VM and purge local Multipass state
  mp-status        Show Multipass VM status

Distribution
  release          Build dist artifacts and publish them to a GitHub release. Requires TAG=vX.Y.Z
  dist             Build release tarballs into dist/. Use OS=darwin,linux and ARCH=amd64,arm64
  dist-smoke       Smoke-test the host-matching dist tarball
  checksums        Write SHA-256 checksums for dist artifacts

Help
  help             Show this help message

Variables:
  TAG              Release tag for make release, for example v0.1.0
  PACKAGE_VERSION  Build version, defaults to git describe or TAG
  GIT_REMOTE       Release git remote, defaults to origin
  OS               Release OS list for make dist, defaults to darwin,linux
  ARCH             Release arch list for make dist, defaults to amd64,arm64
  INSTALL_BINDIR   Install directory, defaults to /Users/teo/.local/bin
  MP_NAME          Multipass instance name, defaults to frridge-dev
  VERIFY_LAB       Topology used by make mp-verify, defaults to testdata/smoke/lab.yaml

Examples:
  make build                                  # Build host binaries with git-describe version metadata
  make check                                  # Run formatting, lint, and unit tests
  make mp-verify                              # Run the Multipass-backed smoke test
  make dist OS=darwin,linux ARCH=amd64,arm64  # Build release tarballs and checksums
  make release TAG=v0.1.0                     # Publish a GitHub release from local dist artifacts

License

MIT.

About

FRR+Bridge: Build and wire virtual links for containerized FRR labs.

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Contributors