1574 lines
38 KiB
Go
1574 lines
38 KiB
Go
package snmp_trap
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/gosnmp/gosnmp"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/influxdata/telegraf"
|
|
"github.com/influxdata/telegraf/config"
|
|
"github.com/influxdata/telegraf/internal/snmp"
|
|
"github.com/influxdata/telegraf/metric"
|
|
"github.com/influxdata/telegraf/testutil"
|
|
)
|
|
|
|
func TestReceiveTrapV1(t *testing.T) {
|
|
now := uint32(123123123)
|
|
|
|
// If the first pdu isn't type TimeTicks, gosnmp.SendTrap() will
|
|
// prepend one with time.Now()
|
|
var tests = []struct {
|
|
name string
|
|
|
|
// send
|
|
trap gosnmp.SnmpTrap // include pdus
|
|
|
|
// receive
|
|
entries []entry
|
|
expected []telegraf.Metric
|
|
}{
|
|
{
|
|
name: "trap enterprise",
|
|
trap: gosnmp.SnmpTrap{
|
|
Variables: []gosnmp.SnmpPDU{
|
|
{
|
|
Name: ".1.2.3.4.5",
|
|
Type: gosnmp.OctetString,
|
|
Value: "payload",
|
|
},
|
|
},
|
|
Enterprise: ".1.2.3",
|
|
AgentAddress: "10.20.30.40",
|
|
GenericTrap: 6, // enterpriseSpecific
|
|
SpecificTrap: 55,
|
|
Timestamp: uint(now),
|
|
},
|
|
entries: []entry{
|
|
{
|
|
".1.2.3.4.5",
|
|
snmp.MibEntry{
|
|
MibName: "valueMIB",
|
|
OidText: "valueOID",
|
|
},
|
|
},
|
|
{
|
|
".1.2.3.0.55",
|
|
snmp.MibEntry{
|
|
MibName: "enterpriseMIB",
|
|
OidText: "enterpriseOID",
|
|
},
|
|
},
|
|
},
|
|
expected: []telegraf.Metric{
|
|
metric.New(
|
|
"snmp_trap",
|
|
map[string]string{
|
|
"oid": ".1.2.3.0.55",
|
|
"name": "enterpriseOID",
|
|
"mib": "enterpriseMIB",
|
|
"version": "1",
|
|
"source": "127.0.0.1",
|
|
"agent_address": "10.20.30.40",
|
|
"community": "public",
|
|
},
|
|
map[string]interface{}{
|
|
"sysUpTimeInstance": uint(now),
|
|
"valueOID": "payload",
|
|
},
|
|
time.Unix(0, 0),
|
|
),
|
|
},
|
|
},
|
|
// v1 generic trap
|
|
{
|
|
name: "trap generic",
|
|
trap: gosnmp.SnmpTrap{
|
|
Variables: []gosnmp.SnmpPDU{
|
|
{
|
|
Name: ".1.2.3.4.5",
|
|
Type: gosnmp.OctetString,
|
|
Value: "payload",
|
|
},
|
|
{
|
|
Name: ".1.2.3.4.6",
|
|
Type: gosnmp.OctetString,
|
|
Value: []byte{0x7, 0xe8, 0x1, 0x4, 0xe, 0x2, 0x19, 0x0, 0x0, 0xe, 0x2},
|
|
},
|
|
},
|
|
Enterprise: ".1.2.3",
|
|
AgentAddress: "10.20.30.40",
|
|
GenericTrap: 0, // coldStart
|
|
SpecificTrap: 0,
|
|
Timestamp: uint(now),
|
|
},
|
|
entries: []entry{
|
|
{
|
|
".1.2.3.4.5",
|
|
snmp.MibEntry{
|
|
MibName: "valueMIB",
|
|
OidText: "valueOID",
|
|
},
|
|
},
|
|
{
|
|
".1.2.3.4.6",
|
|
snmp.MibEntry{
|
|
MibName: "valueMIB",
|
|
OidText: "valueHexOID",
|
|
},
|
|
},
|
|
{
|
|
".1.3.6.1.6.3.1.1.5.1",
|
|
snmp.MibEntry{
|
|
MibName: "coldStartMIB",
|
|
OidText: "coldStartOID",
|
|
},
|
|
},
|
|
},
|
|
expected: []telegraf.Metric{
|
|
metric.New(
|
|
"snmp_trap",
|
|
map[string]string{
|
|
"oid": ".1.3.6.1.6.3.1.1.5.1",
|
|
"name": "coldStartOID",
|
|
"mib": "coldStartMIB",
|
|
"version": "1",
|
|
"source": "127.0.0.1",
|
|
"agent_address": "10.20.30.40",
|
|
"community": "public",
|
|
},
|
|
map[string]interface{}{
|
|
"sysUpTimeInstance": uint(now),
|
|
"valueOID": "payload",
|
|
"valueHexOID": "07e801040e021900000e02",
|
|
},
|
|
time.Unix(0, 0),
|
|
),
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// We would prefer to specify port 0 and let the network
|
|
// stack choose an unused port for us but TrapListener
|
|
// doesn't have a way to return the autoselected port.
|
|
// Instead, we'll use an unusual port and hope it's
|
|
// unused.
|
|
const port = 12399
|
|
|
|
// Set up the service input plugin
|
|
plugin := &SnmpTrap{
|
|
ServiceAddress: "udp://:" + strconv.Itoa(port),
|
|
Version: "1",
|
|
Log: testutil.Logger{},
|
|
transl: &testTranslator{entries: tt.entries},
|
|
}
|
|
require.NoError(t, plugin.Init())
|
|
|
|
// Start the plugin
|
|
var acc testutil.Accumulator
|
|
require.NoError(t, plugin.Start(&acc))
|
|
defer plugin.Stop()
|
|
|
|
// Create a v1 client and send the trap
|
|
client := &gosnmp.GoSNMP{
|
|
Port: port,
|
|
Version: gosnmp.Version1,
|
|
Timeout: 2 * time.Second,
|
|
Retries: 1,
|
|
MaxOids: gosnmp.MaxOids,
|
|
Target: "127.0.0.1",
|
|
Community: "public",
|
|
}
|
|
require.NoError(t, client.Connect(), "connecting failed")
|
|
defer client.Conn.Close()
|
|
_, err := client.SendTrap(tt.trap)
|
|
require.NoError(t, err, "sending failed")
|
|
require.NoError(t, client.Conn.Close(), "closing failed")
|
|
|
|
// Wait for trap to be received
|
|
require.Eventually(t, func() bool {
|
|
return acc.NMetrics() >= uint64(len(tt.expected))
|
|
}, 3*time.Second, 100*time.Millisecond, "timed out waiting for trap to be received")
|
|
|
|
// Verify plugin output
|
|
testutil.RequireMetricsEqual(t, tt.expected, acc.GetTelegrafMetrics(), testutil.IgnoreTime())
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestReceiveTrapV2c(t *testing.T) {
|
|
now := uint32(123123123)
|
|
|
|
// If the first pdu isn't type TimeTicks, gosnmp.SendTrap() will
|
|
// prepend one with time.Now()
|
|
var tests = []struct {
|
|
name string
|
|
|
|
// send
|
|
trap gosnmp.SnmpTrap // include pdus
|
|
|
|
// receive
|
|
entries []entry
|
|
expected []telegraf.Metric
|
|
}{
|
|
// ordinary v2c coldStart trap
|
|
{
|
|
name: "v2c coldStart",
|
|
trap: gosnmp.SnmpTrap{
|
|
Variables: []gosnmp.SnmpPDU{
|
|
{
|
|
Name: ".1.3.6.1.2.1.1.3.0",
|
|
Type: gosnmp.TimeTicks,
|
|
Value: now,
|
|
},
|
|
{
|
|
Name: ".1.3.6.1.6.3.1.1.4.1.0", // SNMPv2-MIB::snmpTrapOID.0
|
|
Type: gosnmp.ObjectIdentifier,
|
|
Value: ".1.3.6.1.6.3.1.1.5.1", // coldStart
|
|
},
|
|
},
|
|
},
|
|
entries: []entry{
|
|
{
|
|
oid: ".1.3.6.1.6.3.1.1.4.1.0",
|
|
e: snmp.MibEntry{
|
|
MibName: "SNMPv2-MIB",
|
|
OidText: "snmpTrapOID.0",
|
|
},
|
|
},
|
|
{
|
|
oid: ".1.3.6.1.6.3.1.1.5.1",
|
|
e: snmp.MibEntry{
|
|
MibName: "SNMPv2-MIB",
|
|
OidText: "coldStart",
|
|
},
|
|
},
|
|
{
|
|
oid: ".1.3.6.1.2.1.1.3.0",
|
|
e: snmp.MibEntry{
|
|
MibName: "UNUSED_MIB_NAME",
|
|
OidText: "sysUpTimeInstance",
|
|
},
|
|
},
|
|
},
|
|
expected: []telegraf.Metric{
|
|
metric.New(
|
|
"snmp_trap", // name
|
|
map[string]string{ // tags
|
|
"oid": ".1.3.6.1.6.3.1.1.5.1",
|
|
"name": "coldStart",
|
|
"mib": "SNMPv2-MIB",
|
|
"version": "2c",
|
|
"source": "127.0.0.1",
|
|
"community": "public",
|
|
},
|
|
map[string]interface{}{ // fields
|
|
"sysUpTimeInstance": now,
|
|
},
|
|
time.Unix(0, 0),
|
|
),
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// We would prefer to specify port 0 and let the network
|
|
// stack choose an unused port for us but TrapListener
|
|
// doesn't have a way to return the autoselected port.
|
|
// Instead, we'll use an unusual port and hope it's
|
|
// unused.
|
|
const port = 12399
|
|
|
|
// Set up the service input plugin
|
|
plugin := &SnmpTrap{
|
|
ServiceAddress: "udp://:" + strconv.Itoa(port),
|
|
Version: "2c",
|
|
Log: testutil.Logger{},
|
|
transl: &testTranslator{entries: tt.entries},
|
|
}
|
|
require.NoError(t, plugin.Init())
|
|
|
|
var acc testutil.Accumulator
|
|
require.NoError(t, plugin.Start(&acc))
|
|
defer plugin.Stop()
|
|
|
|
// Create a v1 client and send the trap
|
|
client := &gosnmp.GoSNMP{
|
|
Port: port,
|
|
Version: gosnmp.Version2c,
|
|
Timeout: 2 * time.Second,
|
|
Retries: 1,
|
|
MaxOids: gosnmp.MaxOids,
|
|
Target: "127.0.0.1",
|
|
Community: "public",
|
|
}
|
|
require.NoError(t, client.Connect(), "connecting failed")
|
|
defer client.Conn.Close()
|
|
_, err := client.SendTrap(tt.trap)
|
|
require.NoError(t, err, "sending failed")
|
|
require.NoError(t, client.Conn.Close(), "closing failed")
|
|
|
|
// Wait for trap to be received
|
|
require.Eventually(t, func() bool {
|
|
return acc.NMetrics() >= uint64(len(tt.expected))
|
|
}, 3*time.Second, 100*time.Millisecond, "timed out waiting for trap to be received")
|
|
|
|
// Verify plugin output
|
|
testutil.RequireMetricsEqual(t, tt.expected, acc.GetTelegrafMetrics(), testutil.IgnoreTime())
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestReceiveTrapV3(t *testing.T) {
|
|
now := uint32(123123123)
|
|
|
|
// If the first pdu isn't type TimeTicks, gosnmp.SendTrap() will
|
|
// prepend one with time.Now()
|
|
var tests = []struct {
|
|
name string
|
|
|
|
// send
|
|
trap gosnmp.SnmpTrap // include pdus
|
|
|
|
// auth and priv parameters
|
|
secName string // v3 username
|
|
secLevel string // v3 security level
|
|
authProto string // Auth protocol: "", MD5 or SHA
|
|
authPass string // Auth passphrase
|
|
privProto string // Priv protocol: "", DES or AES
|
|
privPass string // Priv passphrase
|
|
|
|
// sender context
|
|
contextName string
|
|
engineID string
|
|
|
|
// receive
|
|
entries []entry
|
|
expected []telegraf.Metric
|
|
}{
|
|
// ordinary v3 coldStart trap no auth and no priv
|
|
{
|
|
name: "noAuthNoPriv",
|
|
secName: "peter",
|
|
secLevel: "noAuthNoPriv",
|
|
contextName: "foo_context_name",
|
|
engineID: "bar_engine_id",
|
|
trap: gosnmp.SnmpTrap{
|
|
Variables: []gosnmp.SnmpPDU{
|
|
{
|
|
Name: ".1.3.6.1.2.1.1.3.0",
|
|
Type: gosnmp.TimeTicks,
|
|
Value: now,
|
|
},
|
|
{
|
|
Name: ".1.3.6.1.6.3.1.1.4.1.0", // SNMPv2-MIB::snmpTrapOID.0
|
|
Type: gosnmp.ObjectIdentifier,
|
|
Value: ".1.3.6.1.6.3.1.1.5.1", // coldStart
|
|
},
|
|
},
|
|
},
|
|
entries: []entry{
|
|
{
|
|
oid: ".1.3.6.1.6.3.1.1.4.1.0",
|
|
e: snmp.MibEntry{
|
|
MibName: "SNMPv2-MIB",
|
|
OidText: "snmpTrapOID.0",
|
|
},
|
|
},
|
|
{
|
|
oid: ".1.3.6.1.6.3.1.1.5.1",
|
|
e: snmp.MibEntry{
|
|
MibName: "SNMPv2-MIB",
|
|
OidText: "coldStart",
|
|
},
|
|
},
|
|
{
|
|
oid: ".1.3.6.1.2.1.1.3.0",
|
|
e: snmp.MibEntry{
|
|
MibName: "UNUSED_MIB_NAME",
|
|
OidText: "sysUpTimeInstance",
|
|
},
|
|
},
|
|
},
|
|
expected: []telegraf.Metric{
|
|
metric.New(
|
|
"snmp_trap", // name
|
|
map[string]string{ // tags
|
|
"oid": ".1.3.6.1.6.3.1.1.5.1",
|
|
"name": "coldStart",
|
|
"mib": "SNMPv2-MIB",
|
|
"version": "3",
|
|
"source": "127.0.0.1",
|
|
"context_name": "foo_context_name",
|
|
"engine_id": "6261725f656e67696e655f6964",
|
|
},
|
|
map[string]interface{}{ // fields
|
|
"sysUpTimeInstance": now,
|
|
},
|
|
time.Unix(0, 0),
|
|
),
|
|
},
|
|
},
|
|
// ordinary v3 coldstart trap SHA auth and no priv
|
|
{
|
|
name: "authNoPriv SHA",
|
|
secName: "peter",
|
|
secLevel: "authNoPriv",
|
|
authProto: "SHA",
|
|
authPass: "passpass",
|
|
trap: gosnmp.SnmpTrap{
|
|
Variables: []gosnmp.SnmpPDU{
|
|
{
|
|
Name: ".1.3.6.1.2.1.1.3.0",
|
|
Type: gosnmp.TimeTicks,
|
|
Value: now,
|
|
},
|
|
{
|
|
Name: ".1.3.6.1.6.3.1.1.4.1.0", // SNMPv2-MIB::snmpTrapOID.0
|
|
Type: gosnmp.ObjectIdentifier,
|
|
Value: ".1.3.6.1.6.3.1.1.5.1", // coldStart
|
|
},
|
|
},
|
|
},
|
|
entries: []entry{
|
|
{
|
|
oid: ".1.3.6.1.6.3.1.1.4.1.0",
|
|
e: snmp.MibEntry{
|
|
MibName: "SNMPv2-MIB",
|
|
OidText: "snmpTrapOID.0",
|
|
},
|
|
},
|
|
{
|
|
oid: ".1.3.6.1.6.3.1.1.5.1",
|
|
e: snmp.MibEntry{
|
|
MibName: "SNMPv2-MIB",
|
|
OidText: "coldStart",
|
|
},
|
|
},
|
|
{
|
|
oid: ".1.3.6.1.2.1.1.3.0",
|
|
e: snmp.MibEntry{
|
|
MibName: "UNUSED_MIB_NAME",
|
|
OidText: "sysUpTimeInstance",
|
|
},
|
|
},
|
|
},
|
|
expected: []telegraf.Metric{
|
|
metric.New(
|
|
"snmp_trap", // name
|
|
map[string]string{ // tags
|
|
"oid": ".1.3.6.1.6.3.1.1.5.1",
|
|
"name": "coldStart",
|
|
"mib": "SNMPv2-MIB",
|
|
"version": "3",
|
|
"source": "127.0.0.1",
|
|
},
|
|
map[string]interface{}{ // fields
|
|
"sysUpTimeInstance": now,
|
|
},
|
|
time.Unix(0, 0),
|
|
),
|
|
},
|
|
},
|
|
// ordinary v3 coldstart trap SHA224 auth and no priv
|
|
{
|
|
name: "authNoPriv SHA224",
|
|
secName: "peter",
|
|
secLevel: "authNoPriv",
|
|
authProto: "SHA224",
|
|
authPass: "passpass",
|
|
trap: gosnmp.SnmpTrap{
|
|
Variables: []gosnmp.SnmpPDU{
|
|
{
|
|
Name: ".1.3.6.1.2.1.1.3.0",
|
|
Type: gosnmp.TimeTicks,
|
|
Value: now,
|
|
},
|
|
{
|
|
Name: ".1.3.6.1.6.3.1.1.4.1.0", // SNMPv2-MIB::snmpTrapOID.0
|
|
Type: gosnmp.ObjectIdentifier,
|
|
Value: ".1.3.6.1.6.3.1.1.5.1", // coldStart
|
|
},
|
|
},
|
|
},
|
|
entries: []entry{
|
|
{
|
|
oid: ".1.3.6.1.6.3.1.1.4.1.0",
|
|
e: snmp.MibEntry{
|
|
MibName: "SNMPv2-MIB",
|
|
OidText: "snmpTrapOID.0",
|
|
},
|
|
},
|
|
{
|
|
oid: ".1.3.6.1.6.3.1.1.5.1",
|
|
e: snmp.MibEntry{
|
|
MibName: "SNMPv2-MIB",
|
|
OidText: "coldStart",
|
|
},
|
|
},
|
|
{
|
|
oid: ".1.3.6.1.2.1.1.3.0",
|
|
e: snmp.MibEntry{
|
|
MibName: "UNUSED_MIB_NAME",
|
|
OidText: "sysUpTimeInstance",
|
|
},
|
|
},
|
|
},
|
|
expected: []telegraf.Metric{
|
|
metric.New(
|
|
"snmp_trap", // name
|
|
map[string]string{ // tags
|
|
"oid": ".1.3.6.1.6.3.1.1.5.1",
|
|
"name": "coldStart",
|
|
"mib": "SNMPv2-MIB",
|
|
"version": "3",
|
|
"source": "127.0.0.1",
|
|
},
|
|
map[string]interface{}{ // fields
|
|
"sysUpTimeInstance": now,
|
|
},
|
|
time.Unix(0, 0),
|
|
),
|
|
},
|
|
},
|
|
// ordinary v3 coldstart trap SHA256 auth and no priv
|
|
{
|
|
name: "authNoPriv SHA256",
|
|
secName: "peter",
|
|
secLevel: "authNoPriv",
|
|
authProto: "SHA256",
|
|
authPass: "passpass",
|
|
trap: gosnmp.SnmpTrap{
|
|
Variables: []gosnmp.SnmpPDU{
|
|
{
|
|
Name: ".1.3.6.1.2.1.1.3.0",
|
|
Type: gosnmp.TimeTicks,
|
|
Value: now,
|
|
},
|
|
{
|
|
Name: ".1.3.6.1.6.3.1.1.4.1.0", // SNMPv2-MIB::snmpTrapOID.0
|
|
Type: gosnmp.ObjectIdentifier,
|
|
Value: ".1.3.6.1.6.3.1.1.5.1", // coldStart
|
|
},
|
|
},
|
|
},
|
|
entries: []entry{
|
|
{
|
|
oid: ".1.3.6.1.6.3.1.1.4.1.0",
|
|
e: snmp.MibEntry{
|
|
MibName: "SNMPv2-MIB",
|
|
OidText: "snmpTrapOID.0",
|
|
},
|
|
},
|
|
{
|
|
oid: ".1.3.6.1.6.3.1.1.5.1",
|
|
e: snmp.MibEntry{
|
|
MibName: "SNMPv2-MIB",
|
|
OidText: "coldStart",
|
|
},
|
|
},
|
|
{
|
|
oid: ".1.3.6.1.2.1.1.3.0",
|
|
e: snmp.MibEntry{
|
|
MibName: "UNUSED_MIB_NAME",
|
|
OidText: "sysUpTimeInstance",
|
|
},
|
|
},
|
|
},
|
|
expected: []telegraf.Metric{
|
|
metric.New(
|
|
"snmp_trap", // name
|
|
map[string]string{ // tags
|
|
"oid": ".1.3.6.1.6.3.1.1.5.1",
|
|
"name": "coldStart",
|
|
"mib": "SNMPv2-MIB",
|
|
"version": "3",
|
|
"source": "127.0.0.1",
|
|
},
|
|
map[string]interface{}{ // fields
|
|
"sysUpTimeInstance": now,
|
|
},
|
|
time.Unix(0, 0),
|
|
),
|
|
},
|
|
},
|
|
// ordinary v3 coldstart trap SHA384 auth and no priv
|
|
{
|
|
name: "authNoPriv SHA384",
|
|
secName: "peter",
|
|
secLevel: "authNoPriv",
|
|
authProto: "SHA384",
|
|
authPass: "passpass",
|
|
trap: gosnmp.SnmpTrap{
|
|
Variables: []gosnmp.SnmpPDU{
|
|
{
|
|
Name: ".1.3.6.1.2.1.1.3.0",
|
|
Type: gosnmp.TimeTicks,
|
|
Value: now,
|
|
},
|
|
{
|
|
Name: ".1.3.6.1.6.3.1.1.4.1.0", // SNMPv2-MIB::snmpTrapOID.0
|
|
Type: gosnmp.ObjectIdentifier,
|
|
Value: ".1.3.6.1.6.3.1.1.5.1", // coldStart
|
|
},
|
|
},
|
|
},
|
|
entries: []entry{
|
|
{
|
|
oid: ".1.3.6.1.6.3.1.1.4.1.0",
|
|
e: snmp.MibEntry{
|
|
MibName: "SNMPv2-MIB",
|
|
OidText: "snmpTrapOID.0",
|
|
},
|
|
},
|
|
{
|
|
oid: ".1.3.6.1.6.3.1.1.5.1",
|
|
e: snmp.MibEntry{
|
|
MibName: "SNMPv2-MIB",
|
|
OidText: "coldStart",
|
|
},
|
|
},
|
|
{
|
|
oid: ".1.3.6.1.2.1.1.3.0",
|
|
e: snmp.MibEntry{
|
|
MibName: "UNUSED_MIB_NAME",
|
|
OidText: "sysUpTimeInstance",
|
|
},
|
|
},
|
|
},
|
|
expected: []telegraf.Metric{
|
|
metric.New(
|
|
"snmp_trap", // name
|
|
map[string]string{ // tags
|
|
"oid": ".1.3.6.1.6.3.1.1.5.1",
|
|
"name": "coldStart",
|
|
"mib": "SNMPv2-MIB",
|
|
"version": "3",
|
|
"source": "127.0.0.1",
|
|
},
|
|
map[string]interface{}{ // fields
|
|
"sysUpTimeInstance": now,
|
|
},
|
|
time.Unix(0, 0),
|
|
),
|
|
},
|
|
},
|
|
// ordinary v3 coldstart trap SHA512 auth and no priv
|
|
{
|
|
name: "authNoPriv SHA512",
|
|
secName: "peter",
|
|
secLevel: "authNoPriv",
|
|
authProto: "SHA512",
|
|
authPass: "passpass",
|
|
trap: gosnmp.SnmpTrap{
|
|
Variables: []gosnmp.SnmpPDU{
|
|
{
|
|
Name: ".1.3.6.1.2.1.1.3.0",
|
|
Type: gosnmp.TimeTicks,
|
|
Value: now,
|
|
},
|
|
{
|
|
Name: ".1.3.6.1.6.3.1.1.4.1.0", // SNMPv2-MIB::snmpTrapOID.0
|
|
Type: gosnmp.ObjectIdentifier,
|
|
Value: ".1.3.6.1.6.3.1.1.5.1", // coldStart
|
|
},
|
|
},
|
|
},
|
|
entries: []entry{
|
|
{
|
|
oid: ".1.3.6.1.6.3.1.1.4.1.0",
|
|
e: snmp.MibEntry{
|
|
MibName: "SNMPv2-MIB",
|
|
OidText: "snmpTrapOID.0",
|
|
},
|
|
},
|
|
{
|
|
oid: ".1.3.6.1.6.3.1.1.5.1",
|
|
e: snmp.MibEntry{
|
|
MibName: "SNMPv2-MIB",
|
|
OidText: "coldStart",
|
|
},
|
|
},
|
|
{
|
|
oid: ".1.3.6.1.2.1.1.3.0",
|
|
e: snmp.MibEntry{
|
|
MibName: "UNUSED_MIB_NAME",
|
|
OidText: "sysUpTimeInstance",
|
|
},
|
|
},
|
|
},
|
|
expected: []telegraf.Metric{
|
|
metric.New(
|
|
"snmp_trap", // name
|
|
map[string]string{ // tags
|
|
"oid": ".1.3.6.1.6.3.1.1.5.1",
|
|
"name": "coldStart",
|
|
"mib": "SNMPv2-MIB",
|
|
"version": "3",
|
|
"source": "127.0.0.1",
|
|
},
|
|
map[string]interface{}{ // fields
|
|
"sysUpTimeInstance": now,
|
|
},
|
|
time.Unix(0, 0),
|
|
),
|
|
},
|
|
},
|
|
// ordinary v3 coldstart trap MD5 auth and no priv
|
|
{
|
|
name: "authNoPriv MD5",
|
|
secName: "peter",
|
|
secLevel: "authNoPriv",
|
|
authProto: "MD5",
|
|
authPass: "passpass",
|
|
trap: gosnmp.SnmpTrap{
|
|
Variables: []gosnmp.SnmpPDU{
|
|
{
|
|
Name: ".1.3.6.1.2.1.1.3.0",
|
|
Type: gosnmp.TimeTicks,
|
|
Value: now,
|
|
},
|
|
{
|
|
Name: ".1.3.6.1.6.3.1.1.4.1.0", // SNMPv2-MIB::snmpTrapOID.0
|
|
Type: gosnmp.ObjectIdentifier,
|
|
Value: ".1.3.6.1.6.3.1.1.5.1", // coldStart
|
|
},
|
|
},
|
|
},
|
|
entries: []entry{
|
|
{
|
|
oid: ".1.3.6.1.6.3.1.1.4.1.0",
|
|
e: snmp.MibEntry{
|
|
MibName: "SNMPv2-MIB",
|
|
OidText: "snmpTrapOID.0",
|
|
},
|
|
},
|
|
{
|
|
oid: ".1.3.6.1.6.3.1.1.5.1",
|
|
e: snmp.MibEntry{
|
|
MibName: "SNMPv2-MIB",
|
|
OidText: "coldStart",
|
|
},
|
|
},
|
|
{
|
|
oid: ".1.3.6.1.2.1.1.3.0",
|
|
e: snmp.MibEntry{
|
|
MibName: "UNUSED_MIB_NAME",
|
|
OidText: "sysUpTimeInstance",
|
|
},
|
|
},
|
|
},
|
|
expected: []telegraf.Metric{
|
|
metric.New(
|
|
"snmp_trap", // name
|
|
map[string]string{ // tags
|
|
"oid": ".1.3.6.1.6.3.1.1.5.1",
|
|
"name": "coldStart",
|
|
"mib": "SNMPv2-MIB",
|
|
"version": "3",
|
|
"source": "127.0.0.1",
|
|
},
|
|
map[string]interface{}{ // fields
|
|
"sysUpTimeInstance": now,
|
|
},
|
|
time.Unix(0, 0),
|
|
),
|
|
},
|
|
},
|
|
// ordinary v3 coldStart SHA trap auth and AES priv
|
|
{
|
|
name: "authPriv SHA-AES",
|
|
secName: "peter",
|
|
secLevel: "authPriv",
|
|
authProto: "SHA",
|
|
authPass: "passpass",
|
|
privProto: "AES",
|
|
privPass: "passpass",
|
|
trap: gosnmp.SnmpTrap{
|
|
Variables: []gosnmp.SnmpPDU{
|
|
{
|
|
Name: ".1.3.6.1.2.1.1.3.0",
|
|
Type: gosnmp.TimeTicks,
|
|
Value: now,
|
|
},
|
|
{
|
|
Name: ".1.3.6.1.6.3.1.1.4.1.0", // SNMPv2-MIB::snmpTrapOID.0
|
|
Type: gosnmp.ObjectIdentifier,
|
|
Value: ".1.3.6.1.6.3.1.1.5.1", // coldStart
|
|
},
|
|
},
|
|
},
|
|
entries: []entry{
|
|
{
|
|
oid: ".1.3.6.1.6.3.1.1.4.1.0",
|
|
e: snmp.MibEntry{
|
|
MibName: "SNMPv2-MIB",
|
|
OidText: "snmpTrapOID.0",
|
|
},
|
|
},
|
|
{
|
|
oid: ".1.3.6.1.6.3.1.1.5.1",
|
|
e: snmp.MibEntry{
|
|
MibName: "SNMPv2-MIB",
|
|
OidText: "coldStart",
|
|
},
|
|
},
|
|
{
|
|
oid: ".1.3.6.1.2.1.1.3.0",
|
|
e: snmp.MibEntry{
|
|
MibName: "UNUSED_MIB_NAME",
|
|
OidText: "sysUpTimeInstance",
|
|
},
|
|
},
|
|
},
|
|
expected: []telegraf.Metric{
|
|
metric.New(
|
|
"snmp_trap", // name
|
|
map[string]string{ // tags
|
|
"oid": ".1.3.6.1.6.3.1.1.5.1",
|
|
"name": "coldStart",
|
|
"mib": "SNMPv2-MIB",
|
|
"version": "3",
|
|
"source": "127.0.0.1",
|
|
},
|
|
map[string]interface{}{ // fields
|
|
"sysUpTimeInstance": now,
|
|
},
|
|
time.Unix(0, 0),
|
|
),
|
|
},
|
|
},
|
|
// ordinary v3 coldStart SHA trap auth and DES priv
|
|
{
|
|
name: "authPriv SHA-DES",
|
|
secName: "peter",
|
|
secLevel: "authPriv",
|
|
authProto: "SHA",
|
|
authPass: "passpass",
|
|
privProto: "DES",
|
|
privPass: "passpass",
|
|
trap: gosnmp.SnmpTrap{
|
|
Variables: []gosnmp.SnmpPDU{
|
|
{
|
|
Name: ".1.3.6.1.2.1.1.3.0",
|
|
Type: gosnmp.TimeTicks,
|
|
Value: now,
|
|
},
|
|
{
|
|
Name: ".1.3.6.1.6.3.1.1.4.1.0", // SNMPv2-MIB::snmpTrapOID.0
|
|
Type: gosnmp.ObjectIdentifier,
|
|
Value: ".1.3.6.1.6.3.1.1.5.1", // coldStart
|
|
},
|
|
},
|
|
},
|
|
entries: []entry{
|
|
{
|
|
oid: ".1.3.6.1.6.3.1.1.4.1.0",
|
|
e: snmp.MibEntry{
|
|
MibName: "SNMPv2-MIB",
|
|
OidText: "snmpTrapOID.0",
|
|
},
|
|
},
|
|
{
|
|
oid: ".1.3.6.1.6.3.1.1.5.1",
|
|
e: snmp.MibEntry{
|
|
MibName: "SNMPv2-MIB",
|
|
OidText: "coldStart",
|
|
},
|
|
},
|
|
{
|
|
oid: ".1.3.6.1.2.1.1.3.0",
|
|
e: snmp.MibEntry{
|
|
MibName: "UNUSED_MIB_NAME",
|
|
OidText: "sysUpTimeInstance",
|
|
},
|
|
},
|
|
},
|
|
expected: []telegraf.Metric{
|
|
metric.New(
|
|
"snmp_trap", // name
|
|
map[string]string{ // tags
|
|
"oid": ".1.3.6.1.6.3.1.1.5.1",
|
|
"name": "coldStart",
|
|
"mib": "SNMPv2-MIB",
|
|
"version": "3",
|
|
"source": "127.0.0.1",
|
|
},
|
|
map[string]interface{}{ // fields
|
|
"sysUpTimeInstance": now,
|
|
},
|
|
time.Unix(0, 0),
|
|
),
|
|
},
|
|
},
|
|
// ordinary v3 coldStart SHA trap auth and AES192 priv
|
|
{
|
|
name: "authPriv SHA-AES192",
|
|
secName: "peter",
|
|
secLevel: "authPriv",
|
|
authProto: "SHA",
|
|
authPass: "passpass",
|
|
privProto: "AES192",
|
|
privPass: "passpass",
|
|
trap: gosnmp.SnmpTrap{
|
|
Variables: []gosnmp.SnmpPDU{
|
|
{
|
|
Name: ".1.3.6.1.2.1.1.3.0",
|
|
Type: gosnmp.TimeTicks,
|
|
Value: now,
|
|
},
|
|
{
|
|
Name: ".1.3.6.1.6.3.1.1.4.1.0", // SNMPv2-MIB::snmpTrapOID.0
|
|
Type: gosnmp.ObjectIdentifier,
|
|
Value: ".1.3.6.1.6.3.1.1.5.1", // coldStart
|
|
},
|
|
},
|
|
},
|
|
entries: []entry{
|
|
{
|
|
oid: ".1.3.6.1.6.3.1.1.4.1.0",
|
|
e: snmp.MibEntry{
|
|
MibName: "SNMPv2-MIB",
|
|
OidText: "snmpTrapOID.0",
|
|
},
|
|
},
|
|
{
|
|
oid: ".1.3.6.1.6.3.1.1.5.1",
|
|
e: snmp.MibEntry{
|
|
MibName: "SNMPv2-MIB",
|
|
OidText: "coldStart",
|
|
},
|
|
},
|
|
{
|
|
oid: ".1.3.6.1.2.1.1.3.0",
|
|
e: snmp.MibEntry{
|
|
MibName: "UNUSED_MIB_NAME",
|
|
OidText: "sysUpTimeInstance",
|
|
},
|
|
},
|
|
},
|
|
expected: []telegraf.Metric{
|
|
metric.New(
|
|
"snmp_trap", // name
|
|
map[string]string{ // tags
|
|
"oid": ".1.3.6.1.6.3.1.1.5.1",
|
|
"name": "coldStart",
|
|
"mib": "SNMPv2-MIB",
|
|
"version": "3",
|
|
"source": "127.0.0.1",
|
|
},
|
|
map[string]interface{}{ // fields
|
|
"sysUpTimeInstance": now,
|
|
},
|
|
time.Unix(0, 0),
|
|
),
|
|
},
|
|
},
|
|
// ordinary v3 coldStart SHA trap auth and AES192C priv
|
|
{
|
|
name: "authPriv SHA-AES192C",
|
|
secName: "peter",
|
|
secLevel: "authPriv",
|
|
authProto: "SHA",
|
|
authPass: "passpass",
|
|
privProto: "AES192C",
|
|
privPass: "passpass",
|
|
trap: gosnmp.SnmpTrap{
|
|
Variables: []gosnmp.SnmpPDU{
|
|
{
|
|
Name: ".1.3.6.1.2.1.1.3.0",
|
|
Type: gosnmp.TimeTicks,
|
|
Value: now,
|
|
},
|
|
{
|
|
Name: ".1.3.6.1.6.3.1.1.4.1.0", // SNMPv2-MIB::snmpTrapOID.0
|
|
Type: gosnmp.ObjectIdentifier,
|
|
Value: ".1.3.6.1.6.3.1.1.5.1", // coldStart
|
|
},
|
|
},
|
|
},
|
|
entries: []entry{
|
|
{
|
|
oid: ".1.3.6.1.6.3.1.1.4.1.0",
|
|
e: snmp.MibEntry{
|
|
MibName: "SNMPv2-MIB",
|
|
OidText: "snmpTrapOID.0",
|
|
},
|
|
},
|
|
{
|
|
oid: ".1.3.6.1.6.3.1.1.5.1",
|
|
e: snmp.MibEntry{
|
|
MibName: "SNMPv2-MIB",
|
|
OidText: "coldStart",
|
|
},
|
|
},
|
|
{
|
|
oid: ".1.3.6.1.2.1.1.3.0",
|
|
e: snmp.MibEntry{
|
|
MibName: "UNUSED_MIB_NAME",
|
|
OidText: "sysUpTimeInstance",
|
|
},
|
|
},
|
|
},
|
|
expected: []telegraf.Metric{
|
|
metric.New(
|
|
"snmp_trap", // name
|
|
map[string]string{ // tags
|
|
"oid": ".1.3.6.1.6.3.1.1.5.1",
|
|
"name": "coldStart",
|
|
"mib": "SNMPv2-MIB",
|
|
"version": "3",
|
|
"source": "127.0.0.1",
|
|
},
|
|
map[string]interface{}{ // fields
|
|
"sysUpTimeInstance": now,
|
|
},
|
|
time.Unix(0, 0),
|
|
),
|
|
},
|
|
},
|
|
// ordinary v3 coldStart SHA trap auth and AES256 priv
|
|
{
|
|
name: "authPriv SHA-AES256",
|
|
secName: "peter",
|
|
secLevel: "authPriv",
|
|
authProto: "SHA",
|
|
authPass: "passpass",
|
|
privProto: "AES256",
|
|
privPass: "passpass",
|
|
trap: gosnmp.SnmpTrap{
|
|
Variables: []gosnmp.SnmpPDU{
|
|
{
|
|
Name: ".1.3.6.1.2.1.1.3.0",
|
|
Type: gosnmp.TimeTicks,
|
|
Value: now,
|
|
},
|
|
{
|
|
Name: ".1.3.6.1.6.3.1.1.4.1.0", // SNMPv2-MIB::snmpTrapOID.0
|
|
Type: gosnmp.ObjectIdentifier,
|
|
Value: ".1.3.6.1.6.3.1.1.5.1", // coldStart
|
|
},
|
|
},
|
|
},
|
|
entries: []entry{
|
|
{
|
|
oid: ".1.3.6.1.6.3.1.1.4.1.0",
|
|
e: snmp.MibEntry{
|
|
MibName: "SNMPv2-MIB",
|
|
OidText: "snmpTrapOID.0",
|
|
},
|
|
},
|
|
{
|
|
oid: ".1.3.6.1.6.3.1.1.5.1",
|
|
e: snmp.MibEntry{
|
|
MibName: "SNMPv2-MIB",
|
|
OidText: "coldStart",
|
|
},
|
|
},
|
|
{
|
|
oid: ".1.3.6.1.2.1.1.3.0",
|
|
e: snmp.MibEntry{
|
|
MibName: "UNUSED_MIB_NAME",
|
|
OidText: "sysUpTimeInstance",
|
|
},
|
|
},
|
|
},
|
|
expected: []telegraf.Metric{
|
|
metric.New(
|
|
"snmp_trap", // name
|
|
map[string]string{ // tags
|
|
"oid": ".1.3.6.1.6.3.1.1.5.1",
|
|
"name": "coldStart",
|
|
"mib": "SNMPv2-MIB",
|
|
"version": "3",
|
|
"source": "127.0.0.1",
|
|
},
|
|
map[string]interface{}{ // fields
|
|
"sysUpTimeInstance": now,
|
|
},
|
|
time.Unix(0, 0),
|
|
),
|
|
},
|
|
},
|
|
// ordinary v3 coldStart SHA trap auth and AES256C priv
|
|
{
|
|
name: "authPriv SHA-AES256C",
|
|
secName: "peter",
|
|
secLevel: "authPriv",
|
|
authProto: "SHA",
|
|
authPass: "passpass",
|
|
privProto: "AES256C",
|
|
privPass: "passpass",
|
|
trap: gosnmp.SnmpTrap{
|
|
Variables: []gosnmp.SnmpPDU{
|
|
{
|
|
Name: ".1.3.6.1.2.1.1.3.0",
|
|
Type: gosnmp.TimeTicks,
|
|
Value: now,
|
|
},
|
|
{
|
|
Name: ".1.3.6.1.6.3.1.1.4.1.0", // SNMPv2-MIB::snmpTrapOID.0
|
|
Type: gosnmp.ObjectIdentifier,
|
|
Value: ".1.3.6.1.6.3.1.1.5.1", // coldStart
|
|
},
|
|
},
|
|
},
|
|
entries: []entry{
|
|
{
|
|
oid: ".1.3.6.1.6.3.1.1.4.1.0",
|
|
e: snmp.MibEntry{
|
|
MibName: "SNMPv2-MIB",
|
|
OidText: "snmpTrapOID.0",
|
|
},
|
|
},
|
|
{
|
|
oid: ".1.3.6.1.6.3.1.1.5.1",
|
|
e: snmp.MibEntry{
|
|
MibName: "SNMPv2-MIB",
|
|
OidText: "coldStart",
|
|
},
|
|
},
|
|
{
|
|
oid: ".1.3.6.1.2.1.1.3.0",
|
|
e: snmp.MibEntry{
|
|
MibName: "UNUSED_MIB_NAME",
|
|
OidText: "sysUpTimeInstance",
|
|
},
|
|
},
|
|
},
|
|
expected: []telegraf.Metric{
|
|
metric.New(
|
|
"snmp_trap", // name
|
|
map[string]string{ // tags
|
|
"oid": ".1.3.6.1.6.3.1.1.5.1",
|
|
"name": "coldStart",
|
|
"mib": "SNMPv2-MIB",
|
|
"version": "3",
|
|
"source": "127.0.0.1",
|
|
},
|
|
map[string]interface{}{ // fields
|
|
"sysUpTimeInstance": now,
|
|
},
|
|
time.Unix(0, 0),
|
|
),
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// We would prefer to specify port 0 and let the network
|
|
// stack choose an unused port for us but TrapListener
|
|
// doesn't have a way to return the autoselected port.
|
|
// Instead, we'll use an unusual port and hope it's
|
|
// unused.
|
|
const port = 12399
|
|
|
|
// Set up the service input plugin
|
|
plugin := &SnmpTrap{
|
|
ServiceAddress: "udp://:" + strconv.Itoa(port),
|
|
Version: "3",
|
|
SecName: config.NewSecret([]byte(tt.secName)),
|
|
SecLevel: tt.secLevel,
|
|
AuthProtocol: tt.authProto,
|
|
AuthPassword: config.NewSecret([]byte(tt.authPass)),
|
|
PrivProtocol: tt.privProto,
|
|
PrivPassword: config.NewSecret([]byte(tt.privPass)),
|
|
Log: testutil.Logger{},
|
|
transl: &testTranslator{entries: tt.entries},
|
|
}
|
|
require.NoError(t, plugin.Init())
|
|
|
|
var acc testutil.Accumulator
|
|
require.NoError(t, plugin.Start(&acc))
|
|
defer plugin.Stop()
|
|
|
|
// Create a v3 client and send the trap
|
|
var msgFlags gosnmp.SnmpV3MsgFlags
|
|
switch strings.ToLower(tt.secLevel) {
|
|
case "noauthnopriv", "":
|
|
msgFlags = gosnmp.NoAuthNoPriv
|
|
case "authnopriv":
|
|
msgFlags = gosnmp.AuthNoPriv
|
|
case "authpriv":
|
|
msgFlags = gosnmp.AuthPriv
|
|
default:
|
|
require.FailNowf(t, "unknown security level %q", tt.secLevel)
|
|
}
|
|
security := createSecurityParameters(tt.authProto, tt.privProto, tt.secName, tt.privPass, tt.authPass)
|
|
|
|
client := &gosnmp.GoSNMP{
|
|
Port: port,
|
|
Version: gosnmp.Version3,
|
|
Timeout: 2 * time.Second,
|
|
Retries: 1,
|
|
MaxOids: gosnmp.MaxOids,
|
|
Target: "127.0.0.1",
|
|
SecurityParameters: security,
|
|
SecurityModel: gosnmp.UserSecurityModel,
|
|
MsgFlags: msgFlags,
|
|
ContextName: tt.contextName,
|
|
ContextEngineID: tt.engineID,
|
|
}
|
|
require.NoError(t, client.Connect(), "connecting failed")
|
|
defer client.Conn.Close()
|
|
_, err := client.SendTrap(tt.trap)
|
|
require.NoError(t, err, "sending failed")
|
|
require.NoError(t, client.Conn.Close(), "closing failed")
|
|
|
|
// Wait for trap to be received
|
|
require.Eventually(t, func() bool {
|
|
return acc.NMetrics() >= uint64(len(tt.expected))
|
|
}, 3*time.Second, 100*time.Millisecond, "timed out waiting for trap to be received")
|
|
|
|
// Verify plugin output
|
|
testutil.RequireMetricsEqual(t, tt.expected, acc.GetTelegrafMetrics(), testutil.IgnoreTime())
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestOidLookupFail(t *testing.T) {
|
|
now := uint32(123123123)
|
|
|
|
// Check that we're not running snmptranslate to look up oids
|
|
// when we shouldn't. This sends and receives a valid trap
|
|
// but metric production should fail because the oids aren't in
|
|
// the cache and oid lookup is intentionally mocked to fail.
|
|
trap := gosnmp.SnmpTrap{
|
|
Variables: []gosnmp.SnmpPDU{
|
|
{
|
|
Name: ".1.3.6.1.2.1.1.3.0",
|
|
Type: gosnmp.TimeTicks,
|
|
Value: now,
|
|
},
|
|
{
|
|
Name: ".1.3.6.1.6.3.1.1.4.1.0", // SNMPv2-MIB::snmpTrapOID.0
|
|
Type: gosnmp.ObjectIdentifier,
|
|
Value: ".1.3.6.1.6.3.1.1.5.1", // coldStart
|
|
},
|
|
},
|
|
}
|
|
|
|
// We would prefer to specify port 0 and let the network
|
|
// stack choose an unused port for us but TrapListener
|
|
// doesn't have a way to return the autoselected port.
|
|
// Instead, we'll use an unusual port and hope it's
|
|
// unused.
|
|
const port = 12399
|
|
|
|
// Set up the service input plugin
|
|
logger := &testutil.CaptureLogger{}
|
|
fail := make(chan bool, 1)
|
|
plugin := &SnmpTrap{
|
|
ServiceAddress: "udp://:" + strconv.Itoa(port),
|
|
Version: "2c",
|
|
Log: logger,
|
|
transl: &testTranslator{fail: fail},
|
|
}
|
|
require.NoError(t, plugin.Init())
|
|
|
|
var acc testutil.Accumulator
|
|
require.NoError(t, plugin.Start(&acc))
|
|
defer plugin.Stop()
|
|
|
|
// Create a v1 client and send the trap
|
|
client := &gosnmp.GoSNMP{
|
|
Port: port,
|
|
Version: gosnmp.Version2c,
|
|
Timeout: 2 * time.Second,
|
|
Retries: 1,
|
|
MaxOids: gosnmp.MaxOids,
|
|
Target: "127.0.0.1",
|
|
Community: "public",
|
|
}
|
|
require.NoError(t, client.Connect(), "connecting failed")
|
|
defer client.Conn.Close()
|
|
_, err := client.SendTrap(trap)
|
|
require.NoError(t, err, "sending failed")
|
|
require.NoError(t, client.Conn.Close(), "closing failed")
|
|
|
|
// Wait for lookup to fail
|
|
select {
|
|
case <-fail:
|
|
case <-time.After(time.Second):
|
|
t.Log("timeout waiting for failing OID lookup")
|
|
t.Fail()
|
|
}
|
|
|
|
// Verify plugin output
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
var found bool
|
|
for _, msg := range logger.Errors() {
|
|
if found = strings.Contains(msg, "unexpected oid"); found {
|
|
break
|
|
}
|
|
}
|
|
require.True(t, found, "did not receive expected error message")
|
|
}
|
|
|
|
func TestInvalidAuth(t *testing.T) {
|
|
now := uint32(time.Now().Unix())
|
|
trap := gosnmp.SnmpTrap{
|
|
Variables: []gosnmp.SnmpPDU{
|
|
{
|
|
Name: ".1.3.6.1.2.1.1.3.0",
|
|
Type: gosnmp.TimeTicks,
|
|
Value: now,
|
|
},
|
|
{
|
|
Name: ".1.3.6.1.6.3.1.1.4.1.0", // SNMPv2-MIB::snmpTrapOID.0
|
|
Type: gosnmp.ObjectIdentifier,
|
|
Value: ".1.3.6.1.6.3.1.1.5.1", // coldStart
|
|
},
|
|
},
|
|
}
|
|
translator := &testTranslator{entries: []entry{
|
|
{
|
|
oid: ".1.3.6.1.6.3.1.1.4.1.0",
|
|
e: snmp.MibEntry{
|
|
MibName: "SNMPv2-MIB",
|
|
OidText: "snmpTrapOID.0",
|
|
},
|
|
},
|
|
{
|
|
oid: ".1.3.6.1.6.3.1.1.5.1",
|
|
e: snmp.MibEntry{
|
|
MibName: "SNMPv2-MIB",
|
|
OidText: "coldStart",
|
|
},
|
|
},
|
|
{
|
|
oid: ".1.3.6.1.2.1.1.3.0",
|
|
e: snmp.MibEntry{
|
|
MibName: "UNUSED_MIB_NAME",
|
|
OidText: "sysUpTimeInstance",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
// If the first pdu isn't type TimeTicks, gosnmp.SendTrap() will
|
|
// prepend one with time.Now()
|
|
var tests = []struct {
|
|
name string
|
|
user string // v3 username
|
|
secLevel string // v3 security level
|
|
authProto string // Auth protocol: "", MD5 or SHA
|
|
authPass string // Auth passphrase
|
|
privProto string // Priv protocol: "", DES or AES
|
|
privPass string // Priv passphrase
|
|
expected string
|
|
}{
|
|
{
|
|
name: "no authentication",
|
|
user: "franz",
|
|
secLevel: "NoAuthNoPriv",
|
|
},
|
|
{
|
|
name: "wrong username",
|
|
user: "foo",
|
|
secLevel: "authPriv",
|
|
authProto: "sha",
|
|
authPass: "what a nice day",
|
|
privProto: "aes",
|
|
privPass: "for my privacy",
|
|
},
|
|
{
|
|
name: "wrong password",
|
|
user: "franz",
|
|
secLevel: "authPriv",
|
|
authProto: "sha",
|
|
authPass: "passpass",
|
|
privProto: "aes",
|
|
privPass: "for my privacy",
|
|
},
|
|
{
|
|
name: "wrong auth protocol",
|
|
user: "franz",
|
|
secLevel: "authPriv",
|
|
authProto: "md5",
|
|
authPass: "what a nice day",
|
|
privProto: "aes",
|
|
privPass: "for my privacy",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// We would prefer to specify port 0 and let the network
|
|
// stack choose an unused port for us but TrapListener
|
|
// doesn't have a way to return the autoselected port.
|
|
// Instead, we'll use an unusual port and hope it's
|
|
// unused.
|
|
const port = 12399
|
|
|
|
// Set up the service input plugin
|
|
plugin := &SnmpTrap{
|
|
ServiceAddress: "udp://:" + strconv.Itoa(port),
|
|
Version: "3",
|
|
SecName: config.NewSecret([]byte("franz")),
|
|
SecLevel: "authPriv",
|
|
AuthProtocol: "sha",
|
|
AuthPassword: config.NewSecret([]byte("what a nice day")),
|
|
PrivProtocol: "aes",
|
|
PrivPassword: config.NewSecret([]byte("for my privacy")),
|
|
Log: testutil.Logger{},
|
|
transl: translator,
|
|
}
|
|
require.NoError(t, plugin.Init())
|
|
|
|
// Inject special logger
|
|
found := make(chan bool, 1)
|
|
plugin.listener.Params.Logger = gosnmp.NewLogger(
|
|
&testLogger{matcher: "incoming packet is not authentic", out: found},
|
|
)
|
|
|
|
var acc testutil.Accumulator
|
|
require.NoError(t, plugin.Start(&acc))
|
|
defer plugin.Stop()
|
|
|
|
// Create a v3 client and send the trap
|
|
var msgFlags gosnmp.SnmpV3MsgFlags
|
|
switch strings.ToLower(tt.secLevel) {
|
|
case "noauthnopriv", "":
|
|
msgFlags = gosnmp.NoAuthNoPriv
|
|
case "authnopriv":
|
|
msgFlags = gosnmp.AuthNoPriv
|
|
case "authpriv":
|
|
msgFlags = gosnmp.AuthPriv
|
|
default:
|
|
require.FailNowf(t, "unknown security level %q", tt.secLevel)
|
|
}
|
|
security := createSecurityParameters(tt.authProto, tt.privProto, tt.user, tt.privPass, tt.authPass)
|
|
|
|
client := &gosnmp.GoSNMP{
|
|
Port: port,
|
|
Version: gosnmp.Version3,
|
|
Timeout: 2 * time.Second,
|
|
Retries: 1,
|
|
MaxOids: gosnmp.MaxOids,
|
|
Target: "127.0.0.1",
|
|
SecurityParameters: security,
|
|
SecurityModel: gosnmp.UserSecurityModel,
|
|
MsgFlags: msgFlags,
|
|
ContextName: "context-name",
|
|
ContextEngineID: "engine no 5",
|
|
}
|
|
require.NoError(t, client.Connect(), "connecting failed")
|
|
defer client.Conn.Close()
|
|
_, err := client.SendTrap(trap)
|
|
require.NoError(t, err)
|
|
require.NoError(t, client.Conn.Close(), "closing failed")
|
|
|
|
// Wait for error to be logged
|
|
select {
|
|
case <-found:
|
|
case <-time.After(3 * time.Second):
|
|
t.Fatal("timed out waiting for unauthenticated log")
|
|
}
|
|
|
|
// Verify plugin output
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
})
|
|
}
|
|
}
|
|
|
|
func createSecurityParameters(authProto, privProto, username, privPass, authPass string) *gosnmp.UsmSecurityParameters {
|
|
var authenticationProtocol gosnmp.SnmpV3AuthProtocol
|
|
switch strings.ToLower(authProto) {
|
|
case "md5":
|
|
authenticationProtocol = gosnmp.MD5
|
|
case "sha":
|
|
authenticationProtocol = gosnmp.SHA
|
|
case "sha224":
|
|
authenticationProtocol = gosnmp.SHA224
|
|
case "sha256":
|
|
authenticationProtocol = gosnmp.SHA256
|
|
case "sha384":
|
|
authenticationProtocol = gosnmp.SHA384
|
|
case "sha512":
|
|
authenticationProtocol = gosnmp.SHA512
|
|
case "":
|
|
authenticationProtocol = gosnmp.NoAuth
|
|
default:
|
|
authenticationProtocol = gosnmp.NoAuth
|
|
}
|
|
|
|
var privacyProtocol gosnmp.SnmpV3PrivProtocol
|
|
switch strings.ToLower(privProto) {
|
|
case "aes":
|
|
privacyProtocol = gosnmp.AES
|
|
case "des":
|
|
privacyProtocol = gosnmp.DES
|
|
case "aes192":
|
|
privacyProtocol = gosnmp.AES192
|
|
case "aes192c":
|
|
privacyProtocol = gosnmp.AES192C
|
|
case "aes256":
|
|
privacyProtocol = gosnmp.AES256
|
|
case "aes256c":
|
|
privacyProtocol = gosnmp.AES256C
|
|
case "":
|
|
privacyProtocol = gosnmp.NoPriv
|
|
default:
|
|
privacyProtocol = gosnmp.NoPriv
|
|
}
|
|
|
|
return &gosnmp.UsmSecurityParameters{
|
|
AuthoritativeEngineID: "deadbeef", // has to be between 5 & 32 chars
|
|
AuthoritativeEngineBoots: 1,
|
|
AuthoritativeEngineTime: 1,
|
|
UserName: username,
|
|
PrivacyProtocol: privacyProtocol,
|
|
PrivacyPassphrase: privPass,
|
|
AuthenticationPassphrase: authPass,
|
|
AuthenticationProtocol: authenticationProtocol,
|
|
}
|
|
}
|
|
|
|
type entry struct {
|
|
oid string
|
|
e snmp.MibEntry
|
|
}
|
|
|
|
type testTranslator struct {
|
|
entries []entry
|
|
fail chan bool
|
|
}
|
|
|
|
func (t *testTranslator) lookup(input string) (snmp.MibEntry, error) {
|
|
for _, entry := range t.entries {
|
|
if input == entry.oid {
|
|
return snmp.MibEntry{MibName: entry.e.MibName, OidText: entry.e.OidText}, nil
|
|
}
|
|
}
|
|
if t.fail != nil {
|
|
t.fail <- true
|
|
}
|
|
return snmp.MibEntry{}, errors.New("unexpected oid")
|
|
}
|
|
|
|
type testLogger struct {
|
|
matcher string
|
|
out chan bool
|
|
}
|
|
|
|
func (l *testLogger) Print(v ...interface{}) {
|
|
if strings.Contains(fmt.Sprint(v...), l.matcher) {
|
|
l.out <- true
|
|
}
|
|
}
|
|
|
|
func (l *testLogger) Printf(format string, v ...interface{}) {
|
|
if strings.Contains(fmt.Sprintf(format, v...), l.matcher) {
|
|
l.out <- true
|
|
}
|
|
}
|