Skip to content

bird-babel-collector: add new package - #31257

Open
robertfoss wants to merge 1 commit into
openwrt:masterfrom
robertfoss:bird-babel-collector
Open

robertfoss wants to merge 1 commit into
openwrt:masterfrom
robertfoss:bird-babel-collector

Conversation

@robertfoss

@robertfoss robertfoss commented Nov 11, 2026 •

Copy link
Copy Markdown

Package Details

Maintainer: @robertfoss

Related to: openwrt/luci#9237 (luci-app-bird-babel)

Description:

New package: a small ucode daemon that records BIRD's Babel neighbours and
default route selection over up to 24 hours and serves them on ubus as
bird-babel. It is the backend for luci-app-bird-babel (openwrt/luci#9237).

  • Reads BIRD's control socket in restricted (read-only) mode. BIRD 2 and 3,
    IPv4/IPv6 and source-specific (sadr) tables, one or all Babel protocols.
  • Per neighbour: link cost, RTT, and the role of the IPv4/IPv6 default route
    through it (selected, standby, none). Neighbour names from hosts files and
    reverse DNS, models from the neighbour's LuCI board info if it is public.
  • History in RAM as change records in 15 minute blocks with per minute
    summaries: about 0.7 MB for 8 neighbours over 24 hours on stable links,
    under 2 MB on very noisy ones. Ages out after 24 hours.
  • Sampling follows the RAM: every 60 s below 96 MB, 30 s below 192 MB, 15 s
    below 384 MB, else 4 s (BIRD's default hello interval).
  • Route to the gateway: next hop and originator from BIRD; the routers in
    between are traced with traceroute only when the selected route changes to
    one without a recent path, at most every 5 minutes per family.
  • Optional persistence, off by default: written gzip compressed only when the
    collector stops, right before sysupgrade (/lib/upgrade hook), or on
    ubus call bird-babel save.
  • ubus: status, history (with since for cheap polling), events, and
    save (local only). History calls without a login are rate limited.
  • No compiled code; depends on ucode modules and uclient-fetch only.
  • Design notes and measurements in README.md.

Run Testing Details

  • OpenWrt Version: SNAPSHOT r0-8ea847f
  • OpenWrt Target/Subtarget: qualcommbe/ipq53xx
  • OpenWrt Device: GL.iNet GL-BE9300 (Flint 3), a Freifunk Berlin core
    router with BIRD 3.2.4 (Babel patch) and three Babel neighbours

Also tested against recorded control socket sessions of stock BIRD 2 and 3
(shared links, sadr, two Babel protocols, BIRD not running), replayed in an
OpenWrt container, and with a history test (changing replies, query
consistency, save and restart).


LLM use

Written with Claude as a coding assistant, for the code, commit message and this description.
I have reviewed all of it and run it on the device above.

Pings

cc @Noki @PolynomialDivision @pktpls

@robertfoss
robertfoss force-pushed the bird-babel-collector branch 3 times, most recently from 1b0ab4e to 01ec1ab Compare November 11, 2026 21:56
Add a collector that samples the Babel neighbours and default route
selection of BIRD into an in-memory history of up to 24 hours and serves
it on ubus as "bird-babel", for luci-app-bird-babel.

- Talks to BIRD's control socket in restricted (read-only) mode, without
  forking birdc. Works with BIRD 2 and 3, all Babel protocols (or the
  configured one), IPv4 and IPv6 or IPv6 source-specific (sadr) tables.
- Per neighbour it records link cost, RTT and the role of the IPv4 and
  IPv6 default routes through it (selected, standby, none). Stock BIRD
  only puts the best Babel route into the table, so standby routes are
  read from "show babel routes", rarely, and not at all once the tables
  are seen to carry all Babel routes (as patched builds do).
- History is kept per neighbour in 15 minute blocks of 8 byte records,
  one whenever any value changes, plus a summary per minute and a
  histogram per block. Memory follows how often links change rather than
  the sampling interval: about 0.7 MB for 8 neighbours over 24 hours on
  stable links, under 2 MB on very noisy ones. Data older than 24 hours
  ages out by whole blocks, and a memory cap (1/64 of RAM) is never
  expected to be reached.
- The sampling interval follows the RAM: once a minute below 96 MB, 30 s
  below 192 MB, 15 s below 384 MB, else every 4 s, BIRD's default Babel
  hello interval (values only change with hellos).
- ubus methods: status (live, refreshed at most once a second, with the
  routes to the gateways), history (bucketed series, role timelines,
  99th percentiles and the stretches without samples, for a range ending
  now or at a given time; buckets are whole minutes from a minute up and
  aligned to their width, so they line up with the minute summaries and
  refreshes do not reshuffle samples; with since, only the buckets from
  there on and no percentiles, for cheap polling), events, and save
  (local only). History queries are cached briefly. Calls without a
  login (a public status page) are rounded to share cached results, and
  the ones that need computing are rate limited, except polls for the
  last few minutes.
- Names neighbours from the hosts files and reverse DNS, via their IPv4
  address or the address in their router ID, and fetches vendor and
  model from their luci-rpc getBoardJSON where it is open to anonymous
  sessions, over IPv4 or link-local IPv6. Names found are kept for a
  week, so a neighbour that cannot be looked up for a while keeps it.
- Shows the route to the node that originates the selected IPv4 and IPv6
  default routes: BIRD gives the next hop and the originator (its
  address from the router ID); the routers in between are traced with
  traceroute in the background, only when the route switches to one
  without a path from the last hour, and while the gateway was not
  reached, at most every 5 minutes per family. IPv4 targets are the
  address in the originator's router ID, only if private; IPv6 targets
  an address in a prefix the originator announces.
- Detects the router hearing its own hellos (a layer 2 loop between two
  of its interfaces) and reports the MAC addresses involved.
- Optional mesh hints in /etc/config/bird-babel name the mesh AP and
  radio behind a network, for the graph in the web interface.
- History can optionally be saved to flash, gzip compressed, only when
  the collector stops (reboot, shutdown, restart) and right before
  sysupgrade or a config backup takes it (/lib/upgrade hook); "ubus call
  bird-babel save" saves at once. Written to a temporary file and
  renamed. Off by default.

README.md explains the design, the measurements behind it and the
alternatives that were rejected, for further development.

Signed-off-by: Robert Foss <rfoss@kernel.org>
if (!length(m) && !length(a.r4) && !hn) continue;
out[k] = { meta: state.neighbors[k], metric: m, rtt: r, roles4: a.r4, roles6: a.r6 };
if (q.p99) {
let pm = hist_percentile(a.hm, 0.99, 1, INF), pr = hist_percentile(a.hr, 0.99, 1, INF);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clamping to 1..INF instead of the observed range lets the in-bin interpolation overshoot the data: a link with a constant cost of 96 (bin 52) reports a p99 of 99. Clamp to the min/max of the buckets as the previous version did, and skip it when no bucket has data, since whole-block histograms can extend past the range.

Suggested change
let pm = hist_percentile(a.hm, 0.99, 1, INF), pr = hist_percentile(a.hr, 0.99, 1, INF);
let mlo = INF, mhi = 0, rlo = INF, rhi = 0;
for (let b = 0; b < points; b++) {
if (a.n[b]) { mlo = min(mlo, a.mmin[b]); mhi = max(mhi, a.mmax[b]); }
if (a.rn[b]) { rlo = min(rlo, a.rmin[b]); rhi = max(rhi, a.rmax[b]); }
}
let pm = (mlo <= mhi) ? hist_percentile(a.hm, 0.99, mlo, mhi) : null;
let pr = (rlo <= rhi) ? hist_percentile(a.hr, 0.99, rlo, rhi) : null;

Generated by Claude Code

@@ -0,0 +1,12 @@
# Sourced by sysupgrade: save the history right before the config backup
# takes it, instead of using the last periodic save

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: The periodic save is gone (persistence now saves only on stop, on this hook and on ubus call bird-babel save), so this comment is stale.

Suggested change
# takes it, instead of using the last periodic save
# takes it; otherwise the history is only saved when the collector stops

Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants