PowerSNMPv3: A New Pure Go SNMP Library with Better Error Handling

Published: (January 27, 2026 at 06:12 PM EST)
3 min read
Source: Dev.to

Source: Dev.to

Introduction

I created a pure Go SNMP v2c/v3 library that is smaller than gosnmp and is based on a slightly modified ASN.1 parser from the Go standard library.
The stdlib ASN.1 parser handles pure DER, but SNMP requires BER for unmarshaling, so I forked it with minimal changes.

Core Features

  • GET multiple OIDs – Returns all successful OIDs even if some fail, along with a partial error describing the failed OID(s).
  • SET multiple OIDs – Follows SNMP spec: the operation is atomic; any failure aborts the whole request.
  • Handles security‑level mismatches gracefully (no unnecessary retries).
  • Automatic recovery for:
    • usmStatsNotInTimeWindows – synchronizes time and retries.
    • usmStatsUnknownEngineIDs – discovers EngineID with key re‑localization and retries.
  • Supported authentication protocols: MD5, SHA, SHA‑224, SHA‑256, SHA‑384, SHA‑512.
  • Supported privacy protocols: DES, AES‑128, AES‑192, AES‑256 (including AGENT++ variants).
  • Key expansion compliant with RFC 3826 (AES‑192/256).
  • Tested with Cisco, Huawei, Moxa, Eltex devices.

Pro Tips

  • Avoid Bulk requests with Moxa devices (BER encoding issues).
  • Use Bulk with Eltex but reduce repetitions to 8 and increase timeouts.

Installation

go get github.com/OlegPowerC/powersnmpv3

Basic Usage

Initializing a Session

package main

import (
    "context"
    "flag"
    "fmt"
    "log"
    "os"
    "time"

    PowerSNMP "github.com/OlegPowerC/powersnmpv3"
)

func main() {
    // Command‑line flags
    Host := flag.String("h", "", "Switch or router IP")
    SNMPVersion := flag.Int("v", 3, "SNMP version (2 or 3), default 3")
    SNMPuser := flag.String("u", "", "SNMP v3 USER")
    SNMPcommunity := flag.String("c", "", "Community name (mandatory for v2)")
    SNMPv3Context := flag.String("context", "", "SNMP v3 context")
    SNMPauthProtocol := flag.String("a", "", "SNMP auth protocol")
    SNMPauthPassword := flag.String("A", "", "SNMP auth password")
    SNMPprivProtocol := flag.String("x", "", "SNMP priv protocol")
    SNMPprivPassword := flag.String("X", "", "SNMP priv password")
    Bulk := flag.Bool("bulk", false, "Use SNMP Bulk")
    DebugLevel := flag.Int("debug", 0, "Debug level")
    StrOid := flag.String("o", "1.3.6", "SNMP OID")
    RawToo := flag.Bool("r", false, "Show raw data")
    flag.Parse()

    // Device definition
    var RouterDev PowerSNMP.NetworkDevice
    RouterDev.IPaddress = *Host
    RouterDev.Port = 161
    RouterDev.SNMPparameters.Username = *SNMPuser
    RouterDev.SNMPparameters.Community = *SNMPcommunity
    RouterDev.SNMPparameters.SNMPversion = *SNMPVersion
    RouterDev.SNMPparameters.AuthProtocol = *SNMPauthProtocol
    RouterDev.SNMPparameters.AuthKey = *SNMPauthPassword
    RouterDev.SNMPparameters.PrivProtocol = *SNMPprivProtocol
    RouterDev.SNMPparameters.PrivKey = *SNMPprivPassword
    RouterDev.SNMPparameters.ContextName = *SNMPv3Context
    RouterDev.SNMPparameters.RetryCount = 5
    RouterDev.SNMPparameters.MaxRepetitions = 50
    RouterDev.SNMPparameters.TimeoutBtwRepeat = 800
    RouterDev.DebugLevel = uint8(*DebugLevel)

    // Create session
    sess, err := PowerSNMP.SNMP_Init(RouterDev)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    defer sess.Close()

    // Convert OID string to internal representation
    iArOID, _ := PowerSNMP.Convert_OID_StringToIntArray_RAW(*StrOid)

    ctx, cancel := context.WithTimeout(context.Background(), 300*time.Second)
    defer cancel()
    ch := make(chan PowerSNMP.ChanDataWErr, 3000)

    if *Bulk {
        go sess.SNMP_BulkWalk_WChan(ctx, iArOID, ch)
    } else {
        go sess.SNMP_Walk_WChan(ctx, iArOID, ch)
    }

    // Process results
    for gdata := range ch {
        if gdata.Error != nil {
            fmt.Println(gdata.Error)
            os.Exit(1)
        }
        if gdata.ValidData {
            if *RawToo {
                fmt.Println(
                    PowerSNMP.Convert_OID_IntArrayToString_RAW(gdata.Data.RSnmpOID),
                    "=",
                    PowerSNMP.Convert_Variable_To_String(gdata.Data.RSnmpVar),
                    ":",
                    PowerSNMP.Convert_ClassTag_to_String(gdata.Data.RSnmpVar),
                    gdata.Data.RSnmpVar.Value,
                )
            } else {
                fmt.Println(
                    PowerSNMP.Convert_OID_IntArrayToString_RAW(gdata.Data.RSnmpOID),
                    "=",
                    PowerSNMP.Convert_Variable_To_String(gdata.Data.RSnmpVar),
                    ":",
                    PowerSNMP.Convert_ClassTag_to_String(gdata.Data.RSnmpVar),
                )
            }
        }
    }
}

Simple GET Example

package main

import (
    "log"

    "github.com/OlegPowerC/powersnmpv3"
)

func main() {
    device := powersnmpv3.NetworkDevice{
        IPaddress: "192.168.1.1",
        Port:      161,
        SNMPparameters: powersnmpv3.SNMPUserParameters{
            SNMPversion:  3,
            Username:     "snmpuser",
            AuthProtocol: "sha",
            AuthKey:      "authpass",
            PrivProtocol: "aes",
            PrivKey:      "privpass",
        },
    }

    sess, err := powersnmpv3.SNMP_Init(device)
    if err != nil {
        log.Fatal(err)
    }
    defer sess.Close()

    oid, _ := powersnmpv3.ParseOID("1.3.6.1.2.1.1.1.0")
    result, err := sess.SNMP_Get(oid)
    if err != nil {
        log.Fatal(err)
    }
    log.Printf("Result: %v", result)
}

Error Handling Example (GET Multi)

results, err := sess.SNMP_GetMulti(oids)
// Even if err is non‑nil, `results` contains successful OIDs
snmpErr, _ := powersnmpv3.ParseError(err)
for _, oidErr := range snmpErr.Oids {
    log.Printf("Failed: %s - %s", oidErr.Failedoid, oidErr.ErrorDescription)
}

Benchmarks

LibraryTime (15,381 OIDs)OIDs/s
PowerSNMPv34.02 s3,827
gosnmp4.43 s3,472
net‑snmp6.43 s2,393

PowerSNMPv3 is ~37 % faster than net‑snmp and reduces packet traffic in mis‑configured environments (4 vs 10 packets).

License

powersnmpv3 is released under the MIT License.

Back to Blog

Related posts

Read more »

Library of Juggling

Article URL: https://libraryofjuggling.com/ Comments URL: https://news.ycombinator.com/item?id=46853552 Points: 7 Comments: 0...