Skip to content

Commit aeb92b0

Browse files
authored
Merge pull request #1651 from kernelkit/snmp
Add SNMP agent for read-only monitoring
2 parents aafa78f + 1571648 commit aeb92b0

51 files changed

Lines changed: 3748 additions & 5 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎configs/aarch64_defconfig‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ BR2_PACKAGE_MTR=y
8181
BR2_PACKAGE_NETCALC=y
8282
BR2_PACKAGE_NETCAT_OPENBSD=y
8383
BR2_PACKAGE_NETSNMP=y
84+
BR2_PACKAGE_NETSNMP_WITH_MIB_MODULES="host if-mib ucd-snmp/loadave ucd-snmp/memory ucd-snmp/disk_hw agentx"
85+
# BR2_PACKAGE_NETSNMP_ENABLE_MIBS is not set
8486
BR2_PACKAGE_NGINX=y
8587
BR2_PACKAGE_NGINX_HTTP_SSL_MODULE=y
8688
BR2_PACKAGE_NGINX_HTTP_V2_MODULE=y

‎configs/arm_defconfig‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ BR2_PACKAGE_MTR=y
8080
BR2_PACKAGE_NETCALC=y
8181
BR2_PACKAGE_NETCAT_OPENBSD=y
8282
BR2_PACKAGE_NETSNMP=y
83+
BR2_PACKAGE_NETSNMP_WITH_MIB_MODULES="host if-mib ucd-snmp/loadave ucd-snmp/memory ucd-snmp/disk_hw agentx"
84+
# BR2_PACKAGE_NETSNMP_ENABLE_MIBS is not set
8385
BR2_PACKAGE_NGINX=y
8486
BR2_PACKAGE_NGINX_HTTP_SSL_MODULE=y
8587
BR2_PACKAGE_NGINX_HTTP_V2_MODULE=y

‎configs/riscv64_defconfig‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ BR2_PACKAGE_MTR=y
9191
BR2_PACKAGE_NETCALC=y
9292
BR2_PACKAGE_NETCAT_OPENBSD=y
9393
BR2_PACKAGE_NETSNMP=y
94+
BR2_PACKAGE_NETSNMP_WITH_MIB_MODULES="host if-mib ucd-snmp/loadave ucd-snmp/memory ucd-snmp/disk_hw agentx"
95+
# BR2_PACKAGE_NETSNMP_ENABLE_MIBS is not set
9496
BR2_PACKAGE_NGINX=y
9597
BR2_PACKAGE_NGINX_HTTP_SSL_MODULE=y
9698
BR2_PACKAGE_NGINX_HTTP_V2_MODULE=y

‎configs/x86_64_defconfig‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ BR2_PACKAGE_MTR=y
8080
BR2_PACKAGE_NETCALC=y
8181
BR2_PACKAGE_NETCAT_OPENBSD=y
8282
BR2_PACKAGE_NETSNMP=y
83+
BR2_PACKAGE_NETSNMP_WITH_MIB_MODULES="host if-mib ucd-snmp/loadave ucd-snmp/memory ucd-snmp/disk_hw agentx"
84+
# BR2_PACKAGE_NETSNMP_ENABLE_MIBS is not set
8385
BR2_PACKAGE_NGINX=y
8486
BR2_PACKAGE_NGINX_HTTP_SSL_MODULE=y
8587
BR2_PACKAGE_NGINX_HTTP_V2_MODULE=y

‎doc/ChangeLog.md‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,15 @@ All notable changes to the project are documented in this file.
5858
each one, e.g., a VLAN interface and its parent, issue #514
5959
- Add `last-change` to interface operational status: the time the
6060
interface entered its current operational state, issue #514
61+
- Add an SNMP agent for read-only monitoring from existing management
62+
systems. Configured with `ietf-snmp` (RFC 7407): enable
63+
`/snmp/engine` and add a `/snmp/community`, the rest has working
64+
defaults. Serves SNMPv2-MIB, IF-MIB, HOST-RESOURCES-MIB and
65+
UCD-SNMP-MIB, plus LLDP-MIB when LLDP is enabled. Every port is an
66+
interface, so switch ports appear in `ifTable` under the same index
67+
as `/interfaces/interface[name='eth0']/if-index`. SNMP SET, SNMPv3,
68+
notifications and view-based access control are not supported.
69+
Disabled by default, see [SNMP][snmp]
6170

6271
### Fixes
6372

@@ -78,6 +87,7 @@ All notable changes to the project are documented in this file.
7887
when the interface was created
7988

8089
[relsup]: https://github.com/kernelkit/infix/blob/main/doc/releases.md
90+
[snmp]: https://www.kernelkit.org/infix/latest/snmp/
8191

8292

8393
[v26.08.0][] - 2026-09-01

‎doc/snmp.md‎

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
# SNMP
2+
3+
An SNMP agent is available for monitoring the system from an existing network
4+
management system. It is read-only: the agent answers GET and GETNEXT, and
5+
there is no way to configure write access. Changing configuration is done
6+
over NETCONF, RESTCONF or the CLI, where NACM controls who may do what.
7+
8+
The agent is disabled by default and has no community configured, so enabling
9+
it is a deliberate act.
10+
11+
> [!IMPORTANT]
12+
> SNMPv1 and v2c carry the community string in clear text on the wire, and
13+
> anyone who can read it can read everything the community's view allows.
14+
> Restrict access by source address, and prefer a management VLAN or firewall
15+
> zone that is not reachable from the outside. Another alternative is to set
16+
> up a [WireGuard](vpn-wireguard.md) tunnel for management until SNMPv3
17+
> support is added.
18+
19+
## What is served
20+
21+
| MIB | Contents |
22+
|--------------------|--------------------------------------------------------------------------------|
23+
| SNMPv2-MIB | `sysDescr`, `sysObjectID`, `sysUpTime`, `sysName`, `sysContact`, `sysLocation` |
24+
| IF-MIB | `ifTable` and `ifXTable` for every interface |
25+
| HOST-RESOURCES-MIB | storage, processors, running software |
26+
| UCD-SNMP-MIB | load average, memory, disk usage |
27+
| LLDP-MIB | neighbors, when LLDP is enabled |
28+
29+
Every port is an interface, including ports offloaded to a switch fabric, so
30+
they all appear in the `ifTable`. The `ifIndex` an NMS sees is the same as
31+
the `if-index` reported under `/interfaces/interface[name='eth0']/if-index`,
32+
which makes it easy to correlate SNMP data with the operational datastore.
33+
34+
We recommend using `ifName` from `ifXTable` as the stable key for a port, not
35+
`ifIndex`. Interface indices are assigned by the kernel and may shift when
36+
configuration creates or removes VLANs, bridges and other virtual interfaces.
37+
38+
> [!NOTE]
39+
> The counters are the ones the interface itself reports, the same values
40+
> `show interface` shows. On a port whose traffic is forwarded by the switch
41+
> fabric, how much of that traffic the counters account for depends on the
42+
> switch and its driver.
43+
44+
`sysContact` and `sysLocation` come from `/system/contact` and
45+
`/system/location`, and `sysName` follows `/system/hostname`. See
46+
[Contact and Location][contact] for what to put in them.
47+
48+
## Configuration
49+
50+
The data model is [RFC 7407][], `ietf-snmp`. Two things are needed: the
51+
engine enabled, and a community to answer for.
52+
53+
<pre class="cli"><code>admin@example:/> <b>configure</b>
54+
admin@example:/config/> <b>set snmp engine enabled</b>
55+
admin@example:/config/> <b>set snmp community monitor security-name public</b>
56+
admin@example:/config/> <b>leave</b>
57+
</code></pre>
58+
59+
That is the whole thing. `monitor` is just a name for the list entry; the
60+
community string on the wire is `public`, taken from the security name because
61+
no separate `text-name` was given. Set `text-name` when the two should
62+
differ:
63+
64+
<pre class="cli"><code>admin@example:/> <b>set snmp community monitor text-name s3cret</b>
65+
</code></pre>
66+
67+
<pre class="cli"><code>admin@example:/> <b>show snmp</b>
68+
Enabled : yes
69+
Versions : v1, v2c (default)
70+
Listen : 0.0.0.0:161, [::]:161 (default)
71+
72+
COMMUNITY SECURITY NAME SOURCE
73+
public public any
74+
</code></pre>
75+
76+
Leaving `version` or `listen` unset means the agent serves every version it
77+
supports, on every address, port 161. Narrow them when you want something
78+
tighter:
79+
80+
<pre class="cli"><code>admin@example:/> <b>configure</b>
81+
admin@example:/config/> <b>set snmp engine version v2c</b>
82+
admin@example:/config/> <b>edit snmp engine listen mgmt udp</b>
83+
admin@example:/config/snmp/engine/listen/mgmt/udp/> <b>set ip 192.168.1.10</b>
84+
admin@example:/config/snmp/engine/listen/mgmt/udp/> <b>leave</b>
85+
</code></pre>
86+
87+
Every community reads the whole MIB tree, and nothing can write. There is no
88+
view or group to configure. Limit *who* may ask instead, with a source
89+
restriction.
90+
91+
### Restricting by source address
92+
93+
A community with no restriction answers anyone who knows the string. To limit
94+
it, define a target holding the permitted prefix, tag it, and point the
95+
community at that tag:
96+
97+
<pre class="cli"><code>admin@example:/> <b>configure</b>
98+
admin@example:/config/> <b>edit snmp target nms</b>
99+
admin@example:/config/snmp/target/nms/> <b>set udp ip 192.168.1.0</b>
100+
admin@example:/config/snmp/target/nms/> <b>set udp prefix-length 24</b>
101+
admin@example:/config/snmp/target/nms/> <b>set tag nms</b>
102+
admin@example:/config/snmp/target/nms/> <b>set target-params nms</b>
103+
admin@example:/config/snmp/target/nms/> <b>end</b>
104+
admin@example:/config/> <b>set snmp community monitor target-tag nms</b>
105+
admin@example:/config/> <b>leave</b>
106+
</code></pre>
107+
108+
> [!IMPORTANT]
109+
> If no target carries the tag, the community is dropped rather than left
110+
> answering any source. Check `show snmp` after setting it: the community
111+
> disappears from the table if the tag matched nothing.
112+
113+
### Firewall
114+
115+
The agent listens on UDP port 161. Permit the `snmp` service in the zone
116+
facing the management network:
117+
118+
<pre class="cli"><code>admin@example:/> <b>configure</b>
119+
admin@example:/config/> <b>edit firewall zone mgmt</b>
120+
admin@example:/config/firewall/zone/mgmt/> <b>set service snmp</b>
121+
admin@example:/config/firewall/zone/mgmt/> <b>leave</b>
122+
</code></pre>
123+
124+
## Reading the data
125+
126+
From a management host:
127+
128+
<pre class="cli"><code>linux-pc:# <b>snmpwalk -v2c -c public 192.168.1.10 IF-MIB::ifXTable</b>
129+
IF-MIB::ifName.1 = STRING: lo
130+
IF-MIB::ifName.2 = STRING: eth0
131+
IF-MIB::ifName.3 = STRING: eth1
132+
IF-MIB::ifHCInOctets.2 = Counter64: 20748536
133+
IF-MIB::ifHCOutOctets.2 = Counter64: 1332378
134+
</code></pre>
135+
136+
> [!TIP]
137+
> Poll over SNMPv2c, not v1. SNMPv1 has no `Counter64`, so the whole of
138+
> `ifXTable` is unreadable over v1 and only the 32-bit counters in `ifTable`
139+
> remain. Those wrap in well under an hour on a busy gigabit port, which is
140+
> not enough for useful throughput graphs. Set `snmp engine version v2c` to
141+
> refuse v1 outright.
142+
143+
The device itself carries no MIB text files, so the `snmpwalk` and `snmpget`
144+
on it work with numeric OIDs only. That is enough for a quick check from the
145+
console:
146+
147+
<pre class="cli"><code>admin@example:~$ <b>snmpwalk -v2c -c public 127.0.0.1 .1.3.6.1.2.1.31.1.1.1.1</b>
148+
.1.3.6.1.2.1.31.1.1.1.1.1 = STRING: lo
149+
.1.3.6.1.2.1.31.1.1.1.1.2 = STRING: eth0
150+
</code></pre>
151+
152+
## Community strings are secrets
153+
154+
`ietf-snmp` marks the community name and the security name
155+
`nacm:default-deny-all`, which sysrepo honours, so they are unreadable by
156+
default. An operator or guest sees that a community exists but not what it is
157+
called.
158+
159+
## Not yet supported
160+
161+
SNMPv3 (USM), notifications (traps and informs), view-based access control,
162+
the BRIDGE-MIB and the ENTITY-MIB are not implemented.
163+
164+
[contact]: system.md#contact-and-location
165+
[RFC 7407]: https://www.rfc-editor.org/rfc/rfc7407

‎doc/system.md‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,31 @@ to clients, it is up to the clients to request the domain name *option*.
390390
> the hostname, are restarted when the hostname is changed.
391391
392392

393+
## Contact and Location
394+
395+
Two free-form settings record who looks after the device and where it
396+
is. Neither changes how the system behaves, they exist so that whoever
397+
finds the device, or finds it in an inventory, can tell.
398+
399+
<pre class="cli"><code>admin@example:/> <b>configure</b>
400+
admin@example:/config/> <b>edit system</b>
401+
admin@example:/config/system/> <b>set contact "Ops &lt;ops@example.com&gt;"</b>
402+
admin@example:/config/system/> <b>set location "Substation 12, cabinet B"</b>
403+
admin@example:/config/system/> <b>leave</b>
404+
</code></pre>
405+
406+
They come into their own once something reads them off the device
407+
instead of out of a spreadsheet. A monitoring system polling [SNMP][]
408+
reads them as `sysContact` and `sysLocation`, so an alarm can say which
409+
cabinet to walk to and who to call, without anyone maintaining that
410+
mapping by hand. They are equally available over NETCONF and RESTCONF
411+
under `/system/contact` and `/system/location`.
412+
413+
Location is worth a convention agreed up front, since its value is in
414+
being consistent across every device: site, room, rack, unit, in that
415+
order, is a good starting point.
416+
417+
393418
## Changing Login Banner
394419

395420
The `motd-banner` setting is an Infix augment and an example of a
@@ -618,3 +643,5 @@ entry is disabled or removed.
618643
[3]: https://www.rfc-editor.org/rfc/rfc8341
619644
[4]: https://chrony-project.org/doc/4.6.1/chronyc.html
620645
[5]: https://linux.die.net/man/1/mkpasswd
646+
647+
[SNMP]: snmp.md

‎mkdocs.yml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ nav:
4949
- DHCP Server: dhcp.md
5050
- NTP Server: ntp.md
5151
- PTP (IEEE 1588/802.1AS): ptp.md
52+
- SNMP: snmp.md
5253
- System:
5354
- Boot Procedure: boot.md
5455
- Configuration: system.md

‎package/confd/confd.mk‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ CONFD_CONF_OPTS += --enable-webui
5050
else
5151
CONFD_CONF_OPTS += --disable-webui
5252
endif
53+
ifeq ($(BR2_PACKAGE_NETSNMP),y)
54+
CONFD_CONF_OPTS += --enable-snmp
55+
else
56+
CONFD_CONF_OPTS += --disable-snmp
57+
endif
5358
define CONFD_INSTALL_EXTRA
5459
for fn in confd.conf crond.conf rcd.conf resolvconf.conf; do \
5560
cp $(CONFD_PKGDIR)/$$fn $(FINIT_D)/available/; \
@@ -114,6 +119,12 @@ define CONFD_INSTALL_YANG_MODULES_WEBUI
114119
$(BR2_EXTERNAL_INFIX_PATH)/utils/srload $(@D)/yang/web.inc
115120
endef
116121
endif
122+
ifeq ($(BR2_PACKAGE_NETSNMP),y)
123+
define CONFD_INSTALL_YANG_MODULES_SNMP
124+
$(COMMON_SYSREPO_ENV) \
125+
$(BR2_EXTERNAL_INFIX_PATH)/utils/srload $(@D)/yang/snmp.inc
126+
endef
127+
endif
117128

118129
# PER_PACKAGE_DIR
119130
# Since the last package in the dependency chain that runs sysrepoctl is confd, we need to
@@ -145,6 +156,7 @@ CONFD_POST_INSTALL_TARGET_HOOKS += CONFD_INSTALL_YANG_MODULES_CONTAINERS
145156
CONFD_POST_INSTALL_TARGET_HOOKS += CONFD_INSTALL_YANG_MODULES_WIFI
146157
CONFD_POST_INSTALL_TARGET_HOOKS += CONFD_INSTALL_YANG_MODULES_GPS
147158
CONFD_POST_INSTALL_TARGET_HOOKS += CONFD_INSTALL_YANG_MODULES_WEBUI
159+
CONFD_POST_INSTALL_TARGET_HOOKS += CONFD_INSTALL_YANG_MODULES_SNMP
148160
CONFD_POST_INSTALL_TARGET_HOOKS += CONFD_INSTALL_IN_ROMFS
149161
CONFD_TARGET_FINALIZE_HOOKS += CONFD_CLEANUP
150162

‎package/klish-plugin-infix/klish-plugin-infix.mk‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ else
2323
KLISH_PLUGIN_INFIX_CONF_OPTS += --disable-containers
2424
endif
2525

26+
ifeq ($(BR2_PACKAGE_NETSNMP),y)
27+
KLISH_PLUGIN_INFIX_CONF_OPTS += --enable-snmp
28+
else
29+
KLISH_PLUGIN_INFIX_CONF_OPTS += --disable-snmp
30+
endif
31+
2632
ifeq ($(BR2_PACKAGE_KLISH_PLUGIN_INFIX_SHELL),y)
2733
KLISH_PLUGIN_INFIX_CONF_OPTS += --enable-shell
2834
else

0 commit comments

Comments
 (0)