Skip to content

Bug: Global SNMP singleton causes all devices to use the first device's connection (multi-device timeout issue) #386

@h4cx78

Description

@h4cx78

Summary

When running device-snmp-go with multiple devices, all SNMP requests are sent to the first device that triggered an autoEvent — regardless of which device is actually being polled. This causes persistent Request timeout errors for all subsequent devices.

This is an architectural bug caused by the use of a global singleton for both the SNMP client and the underlying gosnmp connection.


Root Cause

1. Global client singleton in snmpdriver.go (lines 78–82)

mu.Lock()
defer mu.Unlock()
if client == nil {
    client = NewSNMPClient(Address, uint16(port)) // created once for the FIRST device
}

Once client is created for the first device, it is never replaced for subsequent devices. All devices share this single client instance.

2. Global g.Default connection in snmpclient.go (lines 61–69)

if g.Default.Conn == nil {
    g.Default.Target = c.ipAddr
    g.Default.Port = c.ipPort
    g.Default.Community = COMMUNITY_ACCESS
    err := g.Default.Connect()
    ...
}

g.Default is the package-level global GoSNMP instance from the gosnmp library. Once g.Default.Conn is set (for the first device), subsequent calls skip the Connect() block entirely — the target IP/port is never updated for other devices.


Reproduction Scenario

Setup with two devices:

  • Device A (e.g. 192.168.10.200) — unreachable, autoEvents every 5s/10s/15s
  • Device B (e.g. 192.168.84.1) — reachable, autoEvents every 1m

Event sequence:

  1. Device A autoEvent fires first → client = NewSNMPClient("192.168.10.200", 161) — global client created
  2. g.Default.Target = "192.168.10.200"g.Default.Connect() — UDP socket bound to Device A
  3. g.Default.Get(...) → timeout (Device A unreachable)
  4. Device B autoEvent fires → client != nilreused (still points to Device A's config)
  5. g.Default.Conn != nilConnect() is skipped, target is NOT updated to 192.168.84.1
  6. g.Default.Get(...) → packet is sent to 192.168.10.200timeout for Device B too

Debug logs correctly show Device B's address (192.168.84.1) because it is read from the device protocol and printed — but the actual SNMP packet goes to the address hardcoded in g.Default at first connection time.


Expected Behavior

Each device should use its own isolated SNMP connection. When HandleReadCommands is called for a specific device, the SNMP request must be sent to that device's IP and port, not a globally cached connection from a different device.


Suggested Fix

  • Remove the package-level client singleton in snmpdriver.go. Create a per-device client instance keyed by device name or address.
  • In snmpclient.go, stop using g.Default. Instead, create a dedicated gosnmp.GoSNMP struct per client instance and call .Connect() on it directly.
  • Ensure connections are properly closed and recreated when the target address changes.

Related Issues


Environment

  • device-snmp-go latest (main branch / v4.0.1)
  • Multiple devices defined in device.snmp.*.yaml with different autoEvent intervals
  • One or more devices unreachable (causes the first Conn to remain open to wrong target)

Metadata

Metadata

Assignees

No one assigned

    Labels

    help wantedExtra attention is needed

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions