Skip to content

snmpd-openwrt-metrics: add new package - #30547

Open
perceival wants to merge 1 commit into
openwrt:masterfrom
perceival:snmpd-openwrt-metrics-pr
Open

perceival wants to merge 1 commit into
openwrt:masterfrom
perceival:snmpd-openwrt-metrics-pr

Conversation

@perceival

Copy link
Copy Markdown
Contributor

📦 Package Details

Maintainer: @perceival

Description:

Stock OpenWrt reports nothing useful about its wireless side or its sensors over SNMP: the ieee802dot11 MIB module produces no data on mac80211 drivers, and net-snmp's lmSensors module can't be enabled because it still targets the libsensors 2 API that lm-sensors 3 removed.

This adds a small AgentX subagent that serves two subtrees under OpenWrt's own IANA enterprise number (66510) and the standard LM-SENSORS-MIB arc: a per-interface wireless table (clients, frequency, noise floor, tx/rx rate range, SNR range, channel utilisation, tx power, indexed by ifIndex so it joins IF-MIB) plus a per-radio-link table for 802.11be Multi-Link Operation interfaces (which have no single value for those columns), and hwmon/thermal-zone temperature and fan readings. Wireless data comes from libiwinfo, MLO per-link data from a direct nl80211 query (libiwinfo has no MLO awareness), and sensor data from the union of hwmon and thermal zones. No snmpd configuration is required — the AgentX master is on by default.

This is the device-side implementation for LibreNMS PRs librenms/librenms#19347 and librenms/librenms-agent#613, replacing the shell pass_persist script those PRs originally described.


🧪 Run Testing Details

  • OpenWrt Version: SNAPSHOT
  • OpenWrt Target/Subtarget: qualcommbe/ipq53xx (primary hardware target), ipq806x/generic (cross-architecture build verification)
  • OpenWrt Device: GL.iNet GL-BE9300 (Flint 3) ×2 (one an 802.11be multi-link 5GHz+6GHz AP), Netgear Nighthawk X4S R7800

Verified on all three: --version/-V/--help, a full SNMP walk of both MIB tables against each device's real snmpd (via snmpwalk), and (on the two Flint 3 units) that the multi-link interface's per-link table matches iw dev <iface> info exactly while the main interface table correctly reports its radio-scoped columns as absent rather than a misleading single value. On the R7800 (no 802.11be hardware) confirmed the link table is genuinely absent rather than present-but-empty.


✅ Formalities

  • I have reviewed the CONTRIBUTING.md file for detailed contributing guidelines.

If your PR contains a patch:

N/A — this is a new package, not a patch.

@openwrt

openwrt Bot commented Sep 16, 2026

Copy link
Copy Markdown

Formality Check: Suggestions Available

We checked this pull request against the contribution guidelines. Here is what needs your attention:

⚠️ STYLISTIC WARNINGS & SUGGESTIONS

Commit 7c7fa76:

  • ⚠️ Incorrect capitalization of 'OpenWrt' detected: 'OPENWRT'. Please use the correct spelling 'OpenWrt' (or lowercase 'openwrt' where appropriate).

Something broken? Consider reporting an issue.
Running version 059e3de deployed on 2026-09-09 11:53:18 CEST

@openwrt-ai openwrt-ai left a comment

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.

Reviewed 1 new commit.


Generated by Claude Code

#!/bin/sh /etc/rc.common
# SPDX-License-Identifier: GPL-2.0-or-later

START=51

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.

snmpd's own init uses START=99, so at boot this starts long before the AgentX master exists and /var/run/agentx.sock is not there to connect to. Recovery then depends on the subagent's reconnect timer plus procd respawn. Order it after snmpd instead.


Generated by Claude Code

Comment thread net/snmpd-openwrt-metrics/src/mlo.c Outdated
Comment on lines +181 to +182
while (err > 0)
nl_recvmsgs(st.sock, cb);

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.

err is only cleared from a callback, so a local nl_recvmsgs() failure (socket error, ENOBUFS) that invokes no callback spins forever and hangs the agent inside an SNMP request.

Suggested change
while (err > 0)
nl_recvmsgs(st.sock, cb);
while (err > 0) {
if (nl_recvmsgs(st.sock, cb) < 0)
break;
}

Generated by Claude Code

Comment thread net/snmpd-openwrt-metrics/src/mlo.c Outdated
Comment on lines +96 to +97
if (lt[NL80211_ATTR_MAC])
memcpy(l->addr, nla_data(lt[NL80211_ATTR_MAC]), sizeof(l->addr));

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.

nla_parse_nested() is called with a NULL policy, so no attribute length is validated and a short NL80211_ATTR_MAC makes this read past the attribute. Check the length before copying (the nla_get_u32() calls just below have the same exposure).

Suggested change
if (lt[NL80211_ATTR_MAC])
memcpy(l->addr, nla_data(lt[NL80211_ATTR_MAC]), sizeof(l->addr));
if (lt[NL80211_ATTR_MAC] &&
nla_len(lt[NL80211_ATTR_MAC]) >= (int)sizeof(l->addr))
memcpy(l->addr, nla_data(lt[NL80211_ATTR_MAC]), sizeof(l->addr));

Generated by Claude Code

Comment on lines +73 to +81
static bool read_u32(const char *path, uint32_t *out)
{
char buf[32];

if (slurp(path, buf, sizeof(buf)) < 0)
return false;
*out = (uint32_t)strtoul(buf, NULL, 10);
return true;
}

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.

hwmon tempN_input and thermal-zone temp are signed and can be below zero; strtoul("-5000") wraps, so a sub-zero reading is served as ~4294962296 mC. Parse signed and clamp, since lmTempSensorsValue is a Gauge32.

Suggested change
static bool read_u32(const char *path, uint32_t *out)
{
char buf[32];
if (slurp(path, buf, sizeof(buf)) < 0)
return false;
*out = (uint32_t)strtoul(buf, NULL, 10);
return true;
}
static bool read_u32(const char *path, uint32_t *out)
{
char buf[32];
long v;
if (slurp(path, buf, sizeof(buf)) < 0)
return false;
v = strtol(buf, NULL, 10);
*out = (v < 0) ? 0 : (uint32_t)v;
return true;
}

Generated by Claude Code

Comment thread net/snmpd-openwrt-metrics/src/collect.c Outdated
Comment on lines +289 to +292
if (seen_n < sizeof(seen) / sizeof(seen[0]) &&
!mac_seen(seen, seen_n, e->mac)) {
memcpy(seen[seen_n].a, e->mac, 6);
seen_n++;

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.

openwrtWirelessClientCount is documented as excluding STA-mode uplinks ("this device's own uplink is not one of its clients"), but every phy80211 netdev is walked here with no mode check, so a wireless backhaul's upstream AP lands in seen[] and is counted as a client. iw->mode is already available (it is used in collect_dump()); gate the dedup on AP/master mode, or drop the exclusion claim from the MIB.


Generated by Claude Code

Comment thread net/snmpd-openwrt-metrics/src/collect.c Outdated
Comment on lines +249 to +252
if (iw->noise && !iw->noise(de->d_name, &val))
w->noise = val;
val = 0;
if (iw->txpower && !iw->txpower(de->d_name, &val))

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.

When noise/txpower fail or are unsupported, the field stays 0 and is still served as a real dBm value — exactly the "misleading zero" the MLD columns go out of their way to avoid. Track them the way has_chanutil is tracked and return noSuchInstance for columns 6 and 17 instead.


Generated by Claude Code

Comment on lines +151 to +153
"Display label for the wireless interface, normally the SSID. If
multiple interfaces share an SSID, the agent appends a band suffix
such as 24, 5 or 6 to keep labels unique."

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: no band suffix is ever appended — collect_wireless() uses the raw iwinfo SSID and falls back to the interface name, so two VAPs sharing an SSID get identical labels. Either implement the disambiguation or drop the sentence.


Generated by Claude Code

-- OPENWRT-MIB
--
-- Base registration module for OpenWrt metrics exported by the
-- LibreNMS OpenWrt SNMP agent (a single net-snmp pass_persist handler).

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: this ships an AgentX subagent, not a pass_persist handler — the commit message says the pass_persist script is what it replaces. Same wording in OPENWRT-WIRELESS-MIB lines 6 and 43.

Suggested change
-- LibreNMS OpenWrt SNMP agent (a single net-snmp pass_persist handler).
-- OpenWrt SNMP metrics agent (a net-snmp AgentX subagent).

Generated by Claude Code

@perceival

Copy link
Copy Markdown
Contributor Author

The build succeeds on every architecture here. The two failing CI jobs (x86_64, arm_cortex-a15_neon-vfpv4) are dying in the post-build Docker runtime smoke test, at apk update inside the container — wget: exited with error 8 fetching https://downloads.openwrt.org/snapshots/targets/x86/64/kmods/.../packages.adb (note the slash between x86 and 64, should be x86_64 like every other URL in the same log). Same two jobs are failing the same way right now on #30544, which has nothing to do with this package, so it looks like a CI infrastructure issue rather than something in this PR.

@openwrt-ai openwrt-ai left a comment

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.

Reviewed 1 new commit; no new issues found.


Generated by Claude Code

Stock OpenWrt reports nothing useful about its wireless side or its
sensors over SNMP. The ieee802dot11 MIB module is compiled into net-snmp
but produces no data on mac80211 drivers, and net-snmp's lmSensors module
cannot be enabled because it still targets the libsensors 2 API that
lm-sensors 3 removed.

Add a small AgentX subagent serving two subtrees:

  .1.3.6.1.4.1.66510.1.10  OPENWRT-WIRELESS-MIB, under OpenWrt's own IANA
                           enterprise number, indexed by ifIndex so rows
                           join IF-MIB: clients, frequency, noise floor,
                           tx/rx rate range, SNR range, channel
                           utilisation, tx power, and (for 802.11be
                           Multi-Link Operation interfaces, which have no
                           single value for those columns) a separate
                           per-radio-link table
  .1.3.6.1.4.1.2021.13.16  LM-SENSORS-MIB temperature and fan tables

Wireless data comes from libiwinfo, MLO per-link data from a direct
nl80211 query (libiwinfo has no MLO awareness), and sensor data from
hwmon and thermal zones, taking the union of the two so a sensor with no
thermal zone (an MDIO PHY die sensor, for example) is still reported.

snmpd enables the AgentX master by default and listens on a known
socket, so the package needs no snmpd configuration at all: no pass
registration, no uci-defaults, no removal hook. Radios, VAPs and sensors
are discovered at request time, so there is nothing per-interface to
maintain either.

Tested on a GL-BE9300 (qualcommbe/ipq53xx), including its 802.11be
multi-link (5GHz+6GHz) AP, and cross-checked against `iwinfo`/`iw`/sysfs
output and a live AgentX registration against snmpd with `snmpwalk`.

Signed-off-by: Kamil Bienkiewicz <perceivalpercy@gmail.com>
@perceival
perceival force-pushed the snmpd-openwrt-metrics-pr branch from ea6f205 to 7c7fa76 Compare September 18, 2026 08:03
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