← Notes

Watching BIRD on OpenWrt: metrics, ubus and a LuCI page

· openwrt, bird, ucode, prometheus, luci

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:

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.