-
Notifications
You must be signed in to change notification settings - Fork 1
Module FRR
This page describes the FRR module for dot2net.
FRRouting (FRR) is a free and open source Internet routing protocol suite for Linux and Unix platforms. It implements BGP, OSPF, RIP, IS-IS, PIM, LDP, BFD, Babel, PBR, OpenFabric and VRRP.
- Official Website: https://frrouting.org/
- GitHub: https://github.com/FRRouting/frr
- Documentation: https://docs.frrouting.org/
The FRR module generates no output file of its own. It provides a FormatStyle for writing configuration as vtysh commands, and a class that gives a node file logging.
| FormatStyle Name | Purpose | Output Format |
|---|---|---|
FRRVtyshCLI |
vtysh command format | Shell commands for vtysh |
The FRRVtyshCLI FormatStyle formats configuration lines as vtysh commands suitable for shell execution:
vtysh -c "conf t" -c "router ospf" -c "router-id 10.255.0.1"
This is useful for the startup config block when you want to configure FRR via command line rather than configuration files.
nodeclass:
- name: router
use: [frrLogFile]That is the whole of it. The class makes the log file, gives it to the frr
user, names it to a running FRR, and collects it when the lab is destroyed.
It exists because FRR cannot make that file itself: its daemons run as the
frr user and /var/log belongs to root, so a log file named in a
configuration read at boot is one FRR reports it cannot open. Mounting an empty
file in was the older answer, and it does not survive Kathara, which mounts
directories and would have to replace /var/log wholesale.
The commands run from startup, so what is lost is whatever FRR logged before
they run — a handful of lines about reading its configuration. From then on the
log is complete.
| Value | Default | Meaning |
|---|---|---|
frr_log_path |
/var/log/frr.log |
Where the log is written inside the container |
frr_log_level |
informational |
How much is written. FRR's own default is debugging, which floods a lab's log |
nodeclass:
- name: router
use: [frrLogFile]
values:
frr_log_level: debuggingWhile the lab runs, the log is inside the container and nowhere else:
sudo ./containerlab.sh exec r1 tail -f /var/log/frr.logIt is not on the host. Mounting it out would mean giving the container a
writable path into the generated files, and on Kathara it cannot be done at all
— a mount there is a whole directory, so it would replace /var/log.
When the lab is destroyed, the entry script copies it out first:
sudo ./containerlab.sh destroycollected/
├── r1/var/log/frr.log
├── r2/var/log/frr.log
└── ...
collected/<node>/ mirrors the path inside the container, so where a file came
from is readable from where it landed. The files belong to whoever ran the
script, not to root. sudo ./containerlab.sh collect [<dir>] does the same
without destroying the lab, and DOT2NET_COLLECT_DIR moves the destination —
which is what lets one topology be run many times, each run keeping its own
logs.
This needs an entry script (module_config.<module>.generate_scripts: true);
dot2net says so if one is missing. See
Command Reference and
File Output.
The format turns a block of configuration lines into one vtysh invocation,
so it goes on the block, not on startup itself: startup gathers the blocks
and each of them becomes a command.
module:
- containerlab
- frr
nodeclass:
- name: router
interface_policy: [p2p]
params: [lo]
values:
image: quay.io/frrouting/frr:8.5.4
kind: linux
config:
- name: startup # unformatted: it collects the blocks below
depends: [rtr_cmd]
blocks:
after: ["self_rtr_cmd", "interfaces_ospf_cmd"]
- name: rtr_cmd # what the node itself has to say
format: FRRVtyshCLI
template:
- "router ospf"
- "router-id {{ .ip_loopback }}"
interfaceclass:
- name: default
config:
- name: ospf_cmd # and what each of its interfaces has to say
format: FRRVtyshCLI
template:
- "int {{ .name }}"
- "ip addr {{ .ip_addr }}/{{ .ip_plen }}"
- "router ospf"
- "network {{ .ip_net }} area 0"Note where the parameters come from: ip_loopback belongs to the node (hence
params: [lo]), while ip_addr and ip_net belong to an interface — a node
template cannot reach them, which is why the interface has a block of its own.
In topo.yaml exec section:
exec:
- vtysh -c "conf t" -c "router ospf" -c "router-id 10.255.0.1"
- vtysh -c "conf t" -c "int eth0" -c "ip addr 10.0.0.1/30" -c "router ospf" -c "network 10.0.0.0/30 area 0"For more complex configurations, you may prefer using FRR configuration files instead of vtysh commands:
module:
- containerlab
file:
- name: frr.conf
path: /etc/frr/frr.conf
- name: daemons
path: /etc/frr/daemons
- name: vtysh.conf
path: /etc/frr/vtysh.conf
nodeclass:
- name: router
values:
image: quay.io/frrouting/frr:8.5.4
kind: linux
config:
- file: frr.conf
template:
- "hostname {{ .name }}"
- "!"
- "router ospf"
- " router-id {{ .ip_loopback }}"
- "{{ .interfaces_ospf_network }}"
- "!"
- file: daemons
sourcefile: ./daemons
- file: vtysh.conf
sourcefile: ./vtysh.conf
- name: startup
template:
- "vtysh -b" # Load configuration from frr.conf
interfaceclass:
- name: default
config:
# the block frr.conf gathers above, one line per interface
- name: ospf_network
template:
- " network {{ .ip_net }} area 0"| Approach | Pros | Cons |
|---|---|---|
| FRRVtyshCLI (commands) | Simple, no extra files | Hard to debug, limited complexity |
| Config files | Full FRR syntax, easier debugging | Requires file setup (daemons, vtysh.conf) |
Recommendation: Use config files for production topologies; use FRRVtyshCLI for simple tests or quick prototypes.
When using FRR with configuration files, you typically need:
| File | Purpose | Example Content |
|---|---|---|
frr.conf |
Main configuration | Generated by dot2net |
daemons |
Enable routing daemons | ospfd=yes |
vtysh.conf |
vtysh settings | service integrated-vtysh-config |
zebra=yes
bgpd=no
ospfd=yes
ospf6d=no
ripd=no
ripngd=no
isisd=no
pimd=no
ldpd=no
nhrpd=no
eigrpd=no
babeld=no
sharpd=no
staticd=no
pbrd=no
bfdd=no
fabricd=no
service integrated-vtysh-config
This one builds as it stands.
name: ospf_simple
module:
- containerlab
- frr
class_policy:
interface:
default: [default]
layer:
- name: ip
default_connect: true
policy:
- name: p2p
range: 10.0.0.0/16
prefix: 30
- name: lo
type: loopback
range: 10.255.0.0/24
nodeclass:
- name: router
interface_policy: [p2p]
params: [lo]
values:
image: quay.io/frrouting/frr:8.5.4
kind: linux
config:
# startup itself is unformatted: it gathers the blocks below, each of
# which turns into one vtysh invocation
- name: startup
depends: [rtr_cmd]
blocks:
after: ["self_rtr_cmd", "interfaces_ospf_cmd"]
- name: rtr_cmd
format: FRRVtyshCLI
template:
- "router ospf"
- "router-id {{ .ip_loopback }}"
interfaceclass:
- name: default
config:
- name: ospf_cmd
format: FRRVtyshCLI
template:
- "int {{ .name }}"
- "ip addr {{ .ip_addr }}/{{ .ip_plen }}"
- "router ospf"
- "network {{ .ip_net }} area 0"graph {
r1 [class="router"]
r2 [class="router"]
r1 -- r2
}name: ospf_simple
topology:
nodes:
r1:
kind: linux
image: quay.io/frrouting/frr:8.5.4
network-mode: none
exec:
- vtysh -c "conf t" -c "router ospf" -c "router-id 10.255.0.1"
- vtysh -c "conf t" -c "int eth0" -c "ip addr 10.0.0.1/30" -c "router ospf" -c "network 10.0.0.0/30 area 0"
r2:
kind: linux
image: quay.io/frrouting/frr:8.5.4
network-mode: none
exec:
- vtysh -c "conf t" -c "router ospf" -c "router-id 10.255.0.2"
- vtysh -c "conf t" -c "int eth0" -c "ip addr 10.0.0.2/30" -c "router ospf" -c "network 10.0.0.0/30 area 0"
links:
- endpoints: [r1:eth0, r2:eth0]- Module System - How modules work
- Module: Containerlab - Containerlab integration
- Module: TiNET - TiNET integration
- FormatStyle Design - How FormatStyles work