Skip to content

Info

License: Free — part of the universal collection tier. SNMP polling is the backbone of network monitoring and the agent ships it without a license key.

SNMP Poll Probe

The snmp_poll probe polls a network device over SNMP v2c or v3 and turns standard MIB objects into typed metrics: system uptime, per-interface traffic, errors, discards, speed and status. Custom OID mappings cover the vendor-specific long tail.

The probe also reads the LLDP neighbor table of each polled device and reports its links. A link is reported when the neighbor advertises a named port (LLDP port ID subtype interface name or locally assigned); a port known only by its MAC address is not linked.

A discovery mode that crawls the network outward from seed devices is accepted in the configuration but not active yet (#953): the discovery block is validated and ignored. The crawl profile is v2c only.

One probe instance polls one device; declare one instance per device.

Quick start

# probes.d/10-snmp-poll.yaml — each file under probes.d/ is a YAML array of probes
- name: core-switch
  type: snmp_poll
  params:
    target: 192.168.1.10
    community: ${secret:core-switch.community}   # OS secret store; inline plaintext is auto-sealed on install
    mibs: [mib-2, if-mib]
    interval: 60

This polls system and interface tables every 60 seconds and emits one series per interface (if_index tag).

Parameters

Parameter Must set Default Description
target Yes - Device address or hostname
port No 161 SNMP UDP port
version In practice v2c SNMP version; v1 is refused. One of v2c, v3, 2c, 3, 2
community If version is v2c, 2c, 2 public Community string (v2c). A secret: reference it with ${secret:…}, ${env:…} or ${file:…} rather than writing it in the file
v3 If version is v3, 3 - USM credentials, required with version v3
v3.username Yes - USM user
v3.auth_protocol No - Authentication protocol; empty for none. One of MD5, SHA, SHA224, SHA256, SHA384, SHA512
v3.auth_passphrase No - Required with auth_protocol. A secret: reference it with ${secret:…}, ${env:…} or ${file:…} rather than writing it in the file
v3.priv_protocol No - Privacy protocol; needs auth_protocol. One of DES, AES, AES192, AES256
v3.priv_passphrase No - Required with priv_protocol. A secret: reference it with ${secret:…}, ${env:…} or ${file:…} rather than writing it in the file
retries No 2 Retries per request
timeout No 5s Per-request timeout
interval No 60s Metric polling cadence
topology_interval No 10m Entity and topology sweep cadence
mibs In practice - Built-in MIB modules to poll; this or custom_mappings is required. One of mib-2, if-mib
mib_paths No - Local MIB files or folders used to name custom mappings
custom_mappings No - OID to metric mappings
custom_mappings[].oid Yes - OID, leading dot optional
custom_mappings[].metric No - Metric name; resolved from mib_paths when omitted
custom_mappings[].type No gauge How the value is reported: a gauge as read, a counter as a monotonic total. One of gauge, counter
custom_mappings[].index_label No - Walk the OID as a table and tag rows with this label
discovery No - Topology crawl from seed devices. Not active yet (#953): the block is validated and ignored
discovery.seeds Yes - Entry-point device addresses
discovery.profile Yes - Credentials for crawled devices (v2c only)
discovery.profile.version No v2c SNMP version used to probe the discovered devices. One of v2c, 2c, 2
discovery.profile.community Yes - Community string used to probe the discovered devices. A secret: reference it with ${secret:…}, ${env:…} or ${file:…} rather than writing it in the file
discovery.allowed_cidrs Yes - The crawl never leaves these ranges
discovery.max_devices No 200 Hard cap on the number of discovered devices
discovery.max_hops No 4 Crawl depth from the seeds
discovery.interval No - Crawl cadence; topology_interval by default
discovery.governance_rules No - Per-device governance by match
discovery.governance_rules[].match No - Conditions a device must all meet; an empty match applies to every device
discovery.governance_rules[].match.cidr No - Address range the polled address must fall in
discovery.governance_rules[].match.vendor No - Vendor name from sysObjectID, compared case-insensitively
discovery.governance_rules[].match.sysname No - Regular expression the device sysName must match
discovery.governance_rules[].governance No - A block of settings
discovery.governance_rules[].governance.owner No - Who owns what this instance observes
discovery.governance_rules[].governance.owner.team No - Owning team
discovery.governance_rules[].governance.owner.contact No - Contact for the team
discovery.governance_rules[].governance.criticality No - Business criticality. One of critical, high, medium, low
discovery.governance_rules[].governance.location No - Where it is
discovery.governance_rules[].governance.location.site No - Site name
discovery.governance_rules[].governance.location.datacenter No - Datacenter name
discovery.governance_rules[].governance.location.rack No - Rack identifier
discovery.governance_rules[].governance.location.room No - Room identifier
discovery.governance_rules[].governance.lifecycle No - active, maintenance, decommissioning or retired
discovery.governance_rules[].governance.labels No - Free-form labels, emitted as entity.label.; use application to name the application chain
governance No - Ownership, criticality and location of the device
governance.owner No - Who owns what this instance observes
governance.owner.team No - Owning team
governance.owner.contact No - Contact for the team
governance.criticality No - Business criticality. One of critical, high, medium, low
governance.location No - Where it is
governance.location.site No - Site name
governance.location.datacenter No - Datacenter name
governance.location.rack No - Rack identifier
governance.location.room No - Room identifier
governance.lifecycle No - active, maintenance, decommissioning or retired
governance.labels No - Free-form labels, emitted as entity.label.; use application to name the application chain

At least one entry under mibs or custom_mappings is required. Configuration errors are accumulated and reported together at startup, not one at a time.

retries matters on a congested link: UDP loses packets, and each attempt costs one timeout before the device counts as unanswered. topology_interval is a slower rail, independent of interval, so a dense topology sweep never delays the traffic counters. mib_paths is read locally; the probe never fetches a MIB over the network.

SNMPv3 (USM)

params:
  target: 192.168.1.10
  version: "3"
  mibs: [mib-2, if-mib]
  v3:
    username: monitoring
    auth_protocol: SHA256
    auth_passphrase: "${file:/etc/senhub-agent/snmp_auth}"
    priv_protocol: AES256
    priv_passphrase: "${file:/etc/senhub-agent/snmp_priv}"

The security level (noAuthNoPriv / authNoPriv / authPriv) is derived from which protocols are set — there is no separate field to contradict it. Unknown protocol names are startup errors, never a silent downgrade. The discovery crawl profile remains v2c-only.

Custom mappings

Map any OID — scalar or table column — to a named metric:

params:
  target: 192.168.1.20
  custom_mappings:
    - oid: .1.3.6.1.4.1.318.1.1.1.2.2.1     # APC UPS battery capacity
      metric: snmp.ups.battery_capacity
      type: gauge
    - oid: .1.3.6.1.2.1.2.2.1.10            # ifInOctets (table walk)
      metric: snmp.interface.in_octets
      type: counter
      index_label: if_index

metric may be omitted only when mib_paths is set: the name is then resolved from your MIB files at startup (for example upsAdvBatteryCapacity), and an OID that cannot be resolved is a startup error, never a silent gap. With index_label, the OID is walked as a table and the row index becomes that tag.

Discovery

Not active yet

The discovery block is parsed and validated but not wired to the poll lifecycle yet — configuring it does nothing today beyond a startup warning. It is documented here because the configuration shape is final. Per-device topology (LLDP neighbors, routes, bridge tables of the polled target) is active and independent of this block. Tracking: #953.

When the crawl ships, a discovery block will make the probe crawl outward from seed devices using LLDP neighbor tables, bounded by CIDR ranges and depth/device caps, and report discovered devices and links on the entity rail:

params:
  target: 192.168.1.10
  mibs: [mib-2, if-mib]
  discovery:
    seeds: [192.168.1.10, 192.168.1.11]
    profile:
      version: 2c
      community: "${env:SNMP_COMMUNITY}"
    allowed_cidrs: [192.168.0.0/16]
    max_devices: 200
    max_hops: 4

max_devices is a hard cap on discovered devices and max_hops bounds the crawl depth from the seeds. governance_rules stamps governance on devices as the crawl finds them (see below).

The governance parameter of this probe and the governance block every probe entry accepts (see Governance per probe) share one vocabulary. Both stamp the polled device; on a key present in both, the probe-level parameter wins, and a matched discovery rule wins over either.

Governance for discovered devices

A crawl finds devices you never listed, so their ownership, criticality and location cannot be written per device. governance_rules states them by match instead: the first rule whose conditions all hold stamps its governance block on the device's entity.

discovery:
  seeds: ["10.0.0.1"]
  allowed_cidrs: ["10.0.0.0/16"]
  governance_rules:
    - match:
        cidr: "10.0.10.0/24"
      governance:
        criticality: critical
        owner:
          team: network
        location:
          site: paris
    - match:
        vendor: "cisco"
        sysname: "^edge-"      # regular expression
      governance:
        criticality: high

match accepts cidr, vendor and sysname — all optional, all must hold for the rule to apply, and a rule with no match applies to every device. The governance block takes the same keys as the per-agent one: criticality, lifecycle, owner, location and free-form labels.

Metrics

One series per device (instance tag); interface metrics add if_index.

Metric Type Description
senhub.snmp.up gauge 1 when the device answered this cycle, 0 when not
senhub.snmp.poll.duration gauge Wall-clock poll time
snmp.sys.uptime gauge Device uptime
snmp.interface.in_octets / out_octets counter Interface traffic
snmp.interface.in_errors / out_errors counter Interface errors
snmp.interface.in_discards / out_discards counter Interface discards
snmp.interface.speed gauge Negotiated interface speed
snmp.interface.admin_status / oper_status gauge Interface status (up=1)

An unreachable device is a measurement (senhub.snmp.up = 0), never a probe failure — the agent keeps polling.

Operational notes

  • v2c and v3. SNMPv1 is rejected at startup (no GETBULK). The SNMP trap receiver accepts v2c and v3 as well.
  • No network MIB fetching. The probe never downloads MIB files at runtime. Built-in modules cover MIB-2 and IF-MIB; everything else goes through custom_mappings.
  • Two rails, two cadences. Metrics poll at interval; topology and entity sweeps run at the slower topology_interval so a dense sweep never delays traffic counters.
  • Counters are raw. in_octets and friends are emitted as counters; compute rates in the backend (rate(snmp_interface_in_octets[5m]) in VictoriaMetrics).

Metric reference

Every metric this probe can emit. Metric is the OpenTelemetry name the OTLP, Prometheus and Zabbix outputs derive theirs from. Name is what a Nagios check and the API metrics= filter match. PRTG channel is the label PRTG shows, placeholders filled from the series' tags.

Metric Name PRTG channel Unit Description
senhub.snmp.up senhub.snmp.up SNMP {instance} Reachability # 1 when the last poll reached the SNMP target, 0 otherwise
senhub.snmp.poll.duration senhub.snmp.poll.duration SNMP {instance} Poll Duration s Wall-clock time taken by the last poll cycle
snmp.sys.uptime snmp.sys.uptime SNMP {instance} Uptime centiseconds sysUpTime — time since the network management portion of the system was last re-initialized (hundredths of a second)
snmp.interface.in_octets snmp.interface.in_octets SNMP {instance} IF{if_index} In Octets bytes ifInOctets — total octets received on the interface
snmp.interface.out_octets snmp.interface.out_octets SNMP {instance} IF{if_index} Out Octets bytes ifOutOctets — total octets transmitted on the interface
snmp.interface.in_errors snmp.interface.in_errors SNMP {instance} IF{if_index} In Errors # ifInErrors — inbound packets containing errors
snmp.interface.out_errors snmp.interface.out_errors SNMP {instance} IF{if_index} Out Errors # ifOutErrors — outbound packets that could not be transmitted due to errors
snmp.interface.in_discards snmp.interface.in_discards SNMP {instance} IF{if_index} In Discards # ifInDiscards — inbound packets discarded though no error was detected
snmp.interface.out_discards snmp.interface.out_discards SNMP {instance} IF{if_index} Out Discards # ifOutDiscards — outbound packets discarded though no error was detected
snmp.interface.speed snmp.interface.speed SNMP {instance} IF{if_index} Speed bits/s ifSpeed — nominal bandwidth of the interface in bits per second
snmp.interface.admin_status snmp.interface.admin_status SNMP {instance} IF{if_index} Admin Status # ifAdminStatus — desired interface state (1=up, 2=down, 3=testing)
snmp.interface.oper_status snmp.interface.oper_status SNMP {instance} IF{if_index} Oper Status # ifOperStatus — current operational interface state (1=up, 2=down, 3=testing, ...)