Skip to content

nonbulk: use GETNEXT instead of GET for the first packet#53

Open
dmueckli wants to merge 1 commit into
NETWAYS:masterfrom
dmueckli:fix/nonbulk-getnext-first-packet
Open

nonbulk: use GETNEXT instead of GET for the first packet#53
dmueckli wants to merge 1 commit into
NETWAYS:masterfrom
dmueckli:fix/nonbulk-getnext-first-packet

Conversation

@dmueckli

@dmueckli dmueckli commented Jul 6, 2026

Copy link
Copy Markdown

Summary

NONBULK mode's very first SNMP request used a plain GET against a
hardcoded instance (e.g. ifDescr.1), while every subsequent packet in the
same walk already used GETNEXT against the base OID. This meant NONBULK
mode silently failed on any device whose first ifIndex is not 1 -
instead of performing the "traditional tree-walk" the mode is documented to
do (see README / -m usage text).

Symptom on affected devices:

  • Under SNMPv1: the agent returns noSuchName for the whole PDU (since v1
    has no per-varbind exception values), and the plugin aborts with
    Error in packet / Reason: (noSuchName) There is no such variable name in this MIB.
  • Under SNMPv2c/v3: the agent can return a per-varbind exception
    (noSuchInstance), which the plugin doesn't recognize as an error, so it
    silently reports zero interfaces found instead of walking the table.

This PR makes the first packet consistent with all following packets:
GETNEXT on the base OID, no hardcoded instance, no GETBULK.

Root cause

snmp_bulkget.c, create_pdu():

if (mode == NONBULK) {
    *pdu = snmp_pdu_create(SNMP_MSG_GET);   // <- only the first packet
}

while the main loop's follow-up packets already do:

if (config.mode == BINTEC || config.mode == NONBULK) {
    pdu = snmp_pdu_create(SNMP_MSG_GETNEXT);
}

check_interfaces.c, main(): because the first packet used a plain
GET, it required a fixed-instance OID list (oid_if_get,
oid_alias_get, oid_names_get - all ending in a hardcoded .1/.0
instance), instead of the base OIDs (oid_if_bulkget etc.) used by
default mode.

Changes

  • snmp_bulkget.c: create_pdu() now creates SNMP_MSG_GETNEXT (not
    SNMP_MSG_GET) for NONBULK, matching the follow-up packets.
  • check_interfaces.c: NONBULK now shares the same base OID arrays as
    default mode (oid_if_bulkget, oid_alias_bulkget,
    oid_names_bulkget) instead of the fixed-instance *_get arrays. The
    choice between GETBULK and GETNEXT is already handled by
    create_pdu()/config.mode, so no further branching is needed here.

What did NOT change

  • Session setup is untouched: start_session() still forces
    SNMP_VERSION_1 for NONBULK on the community-string path;
    start_session_v3() is unchanged and used whenever -u is given,
    independent of mode.
  • default, cisco, and bintec modes are not touched by this diff.
  • create_request() (per-interface counter fetch) is unchanged.
  • No CLI flags were added, removed, or renamed.
  • NONBULK still never issues GETBULK, for either community or SNMPv3
    sessions - this was already true before this patch and remains true
    after it.

Testing

1. Regression check: devices starting at ifIndex 1 (the common case)

Local snmpd (v1), default single-NIC setup, ifIndex starts at 1:

diff <(check_interfaces_old -h 127.0.0.1 -c public -m nonbulk -r '.*') \
     <(check_interfaces_new -h 127.0.0.1 -c public -m nonbulk -r '.*')

→ empty diff, byte-identical output between the pre-patch and post-patch
binary.

2. Bug reproduction: device with a non-1-starting ifIndex range

Simulated with snmpsim (community ifindex-gap, two interfaces at
ifIndex 5 and 6 instead of 1/2):

Before the patch:

$ check_interfaces_old -h 127.0.0.1 --port 16200 -c ifindex-gap -m nonbulk -r '.*'
Error in packet
Reason: (noSuchName) There is no such variable name in this MIB.

After the patch:

$ check_interfaces_new -h 127.0.0.1 --port 16200 -c ifindex-gap -m nonbulk -r '.*'
OK: 2 interfaces found, of which 2 matched the regex | ...
[OK] GigabitEthernet0/1 is up
[OK] GigabitEthernet0/2 is up

Motivation / real-world trigger

Encountered with an Ekinops router whose GETBULK implementation loops
indefinitely by always restarting from index 0 instead of honoring the
requested starting OID. -m nonbulk is the documented workaround for
broken GETBULK implementations, but it turned out to be broken itself
for devices that don't start numbering interfaces at ifIndex 1 - which
is common among devices with buggy SNMP stacks in the first place, and
was indeed the case here.

This fix was verified both against a synthetic snmpsim fixture (see
below) and against the actual Ekinops router in production: with the
patch, -m nonbulk now successfully retrieves the interface list where
it previously returned zero interfaces.

Notes for reviewers

  • Not part of this PR, but noticed during testing: create_request()
    treats SNMP_ERR_NOSUCHNAME (SNMPv1) as success even though the
    returned varbinds are then empty placeholders, which can make a
    device look like all counters are 0/interfaces are "down" if a
    single one of the requested standard OIDs (e.g. ifInDiscards) isn't
    supported by that device. Happy to open a separate issue for this if
    useful - keeping this PR focused on the NONBULK walk fix.

The first packet in NONBULK mode used a plain GET against a hardcoded
instance (ifDescr.1 etc.), while every subsequent packet already used
GETNEXT against the base OID. This meant NONBULK mode silently failed
(noSuchName under SNMPv1, zero interfaces found) on any device whose
first ifIndex is not 1, instead of walking the table as documented.

This aligns the first packet with the GETNEXT-based walk already used
for all following packets. No behavior change for devices that start
at ifIndex 1 (verified against a local snmpd v1 agent - old and new
binary produce byte-identical output); fixes the case of devices with
a non-contiguous or non-1-starting ifIndex range (reproduced with
snmpsim against a simulated agent starting at ifIndex 5).

No change to session setup (community/v1 forcing in start_session(),
SNMPv3 handling in start_session_v3()) or to default/cisco/bintec
modes.
@dmueckli dmueckli force-pushed the fix/nonbulk-getnext-first-packet branch from e016196 to 2cda69e Compare July 6, 2026 15:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants