Watching BIRD on OpenWrt: metrics, ubus and a LuCI page
Every router in my mesh runs
BIRD. birdc over SSH is fine for one box and
tiring for ten, so I built three small packages that look at BIRD from three
sides: Prometheus, ubus and LuCI. All of them live in
my OpenWrt feed and all of them
are written in ucode.
BIRD’s control socket
BIRD has a simple line protocol on a unix socket — the same one birdc
uses. You connect, read a banner, send a command and read the reply; every
reply line starts with a four-digit code followed by - (more lines follow)
or a space (last line). That is easy to speak from ucode with the socket
module, so none of the packages need birdc or a separate exporter binary.
Metrics: a node-exporter collector
OpenWrt’s stock prometheus-node-exporter-ucode has no BIRD support, so the
feed carries
my fork of it
with a bird collector — install that instead of the stock package. It exports daemon state and uptime, per-protocol
state, imported/exported/filtered prefix counts, BFD sessions and OSPF
details. Metric names and labels mirror the upstream
bird_exporter, so existing
dashboards and alerts work as they are, without a separate exporter
binary on the router.
ubus: rpcd-mod-bird
rpcd-mod-bird
is an rpcd plugin that registers a bird ubus object. It started as a C
plugin and is now plain ucode:
status— a structured snapshot: daemon, protocols, OSPF areas, interfaces and neighbours, BGP sessions, BFD, AS paths;route— look up one address or prefix, with every ECMP next hop;query— raw command passthrough for anything else;set_ospf_cost— change the OSPF cost of one interface at runtime.
ubus call bird status
ubus call bird route '{"prefix": "2001:db8::/48"}'
ubus call bird set_ospf_cost '{"interface": "wg0", "cost": 30}'
set_ospf_cost is the interesting one. It never touches /etc/bird.conf:
it derives a runtime copy in /tmp, edits the cost there, validates it with
BIRD’s own configure check, and only then applies it. Reconfigurations are
serialized with a lock, and costs applied to other links carry over. To go
back to the defaults, delete the runtime copy and run a plain
birdc configure: in BIRD 3 a bare configure (or SIGHUP) always re-reads
the startup config — handy here, but also worth remembering, because it
drops runtime costs.
The output parsers are pure functions, unit-tested against captured BIRD output.
LuCI: luci-app-bird
luci-app-bird
adds Status → BIRD, in the spirit of the WireGuard status page: the
daemon, protocols, OSPF interfaces and neighbours, BGP sessions and BFD. The
page refreshes in place without rebuilding the tables. OSPF costs are
editable inline — that is set_ospf_cost underneath — and a topology graph
is drawn from the BGP AS paths with vis-network.
Being able to nudge a cost from a browser is handy while I don’t yet have the automatic link-quality controller from the mesh note.