fix(inputs.snmp_trap): Copy GoSNMP global defaults to prevent side-effects (#13542)
This commit is contained in:
parent
77b153e50f
commit
ae163536e6
2
go.mod
2
go.mod
|
|
@ -94,7 +94,7 @@ require (
|
|||
github.com/gophercloud/gophercloud v1.2.0
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
github.com/gosnmp/gosnmp v1.35.0
|
||||
github.com/gosnmp/gosnmp v1.35.1-0.20230602062452-f30602b8dad6
|
||||
github.com/grid-x/modbus v0.0.0-20211113184042-7f2251c342c9
|
||||
github.com/gwos/tcg/sdk v0.0.0-20220621192633-df0eac0a1a4c
|
||||
github.com/harlow/kinesis-consumer v0.3.6-0.20211204214318-c2b9f79d7ab6
|
||||
|
|
|
|||
4
go.sum
4
go.sum
|
|
@ -761,8 +761,8 @@ github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/z
|
|||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gosnmp/gosnmp v1.35.0 h1:EuWWNPxTCdAUx2/NbQcSa3WdNxjzpy4Phv57b4MWpJM=
|
||||
github.com/gosnmp/gosnmp v1.35.0/go.mod h1:2AvKZ3n9aEl5TJEo/fFmf/FGO4Nj4cVeEc5yuk88CYc=
|
||||
github.com/gosnmp/gosnmp v1.35.1-0.20230602062452-f30602b8dad6 h1:pzIZ9ij5bf6vL8aSAoFoksiT7pZXyzBOhDdRlZUT89Q=
|
||||
github.com/gosnmp/gosnmp v1.35.1-0.20230602062452-f30602b8dad6/go.mod h1:V8wQurBU216WENrmFCZVFV4bVcMWIb9ZmPVI5PoH80A=
|
||||
github.com/grid-x/modbus v0.0.0-20211113184042-7f2251c342c9 h1:Q7e9kXS3sRbTjsNDKazbcbDSGAKjFdk096M3qYbwNpE=
|
||||
github.com/grid-x/modbus v0.0.0-20211113184042-7f2251c342c9/go.mod h1:qVX2WhsI5xyAoM6I/MV1bXSKBPdLAjp7pCvieO/S0AY=
|
||||
github.com/grid-x/serial v0.0.0-20191104121038-e24bc9bf6f08/go.mod h1:kdOd86/VGFWRrtkNwf1MPk0u1gIjc4Y7R2j7nhwc7Rk=
|
||||
|
|
|
|||
|
|
@ -26,6 +26,18 @@ type translator interface {
|
|||
lookup(oid string) (snmp.MibEntry, error)
|
||||
}
|
||||
|
||||
type wrapLog struct {
|
||||
telegraf.Logger
|
||||
}
|
||||
|
||||
func (l wrapLog) Printf(format string, args ...interface{}) {
|
||||
l.Debugf(format, args...)
|
||||
}
|
||||
|
||||
func (l wrapLog) Print(args ...interface{}) {
|
||||
l.Debug(args...)
|
||||
}
|
||||
|
||||
type SnmpTrap struct {
|
||||
ServiceAddress string `toml:"service_address"`
|
||||
Timeout config.Duration `toml:"timeout" deprecated:"1.20.0;unused option"`
|
||||
|
|
@ -104,7 +116,12 @@ func (s *SnmpTrap) Start(acc telegraf.Accumulator) error {
|
|||
s.acc = acc
|
||||
s.listener = gosnmp.NewTrapListener()
|
||||
s.listener.OnNewTrap = makeTrapHandler(s)
|
||||
s.listener.Params = gosnmp.Default
|
||||
|
||||
// gosnmp.Default is a pointer, using this more than once
|
||||
// has side effects
|
||||
defaults := *gosnmp.Default
|
||||
s.listener.Params = &defaults
|
||||
s.listener.Params.Logger = gosnmp.NewLogger(wrapLog{s.Log})
|
||||
|
||||
switch s.Version {
|
||||
case "3":
|
||||
|
|
|
|||
|
|
@ -1319,3 +1319,384 @@ func TestReceiveTrap(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
func TestReceiveTrapMultipleConfig(t *testing.T) {
|
||||
now := uint32(123123123)
|
||||
fakeTime := time.Unix(456456456, 456)
|
||||
|
||||
// If the first pdu isn't type TimeTicks, gosnmp.SendTrap() will
|
||||
// prepend one with time.Now()
|
||||
var tests = []struct {
|
||||
name string
|
||||
|
||||
// send
|
||||
version gosnmp.SnmpVersion
|
||||
trap gosnmp.SnmpTrap // include pdus
|
||||
// V3 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
|
||||
|
||||
// V3 sender context
|
||||
contextName string
|
||||
engineID string
|
||||
|
||||
// receive
|
||||
entries []entry
|
||||
metrics []telegraf.Metric
|
||||
}{
|
||||
//ordinary v3 coldStart SHA trap auth and AES priv
|
||||
{
|
||||
name: "v3 coldStart authSHAPrivAES",
|
||||
version: gosnmp.Version3,
|
||||
secName: "authSHAPrivAES",
|
||||
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",
|
||||
},
|
||||
},
|
||||
},
|
||||
metrics: []telegraf.Metric{
|
||||
testutil.MustMetric(
|
||||
"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,
|
||||
},
|
||||
fakeTime,
|
||||
),
|
||||
testutil.MustMetric(
|
||||
"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,
|
||||
},
|
||||
fakeTime,
|
||||
),
|
||||
},
|
||||
},
|
||||
//ordinary v3 coldStart SHA trap auth and AES256 priv
|
||||
{
|
||||
name: "v3 coldStart authSHAPrivAES256",
|
||||
version: gosnmp.Version3,
|
||||
secName: "authSHAPrivAES256",
|
||||
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",
|
||||
},
|
||||
},
|
||||
},
|
||||
metrics: []telegraf.Metric{
|
||||
testutil.MustMetric(
|
||||
"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,
|
||||
},
|
||||
fakeTime,
|
||||
),
|
||||
testutil.MustMetric(
|
||||
"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,
|
||||
},
|
||||
fakeTime,
|
||||
)},
|
||||
},
|
||||
//ordinary v3 coldStart SHA trap auth and AES256C priv
|
||||
{
|
||||
name: "v3 coldStart authSHAPrivAES256C",
|
||||
version: gosnmp.Version3,
|
||||
secName: "authSHAPrivAES256C",
|
||||
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",
|
||||
},
|
||||
},
|
||||
},
|
||||
metrics: []telegraf.Metric{
|
||||
testutil.MustMetric(
|
||||
"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,
|
||||
},
|
||||
fakeTime,
|
||||
),
|
||||
testutil.MustMetric(
|
||||
"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,
|
||||
},
|
||||
fakeTime,
|
||||
),
|
||||
},
|
||||
},
|
||||
}
|
||||
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 port1 = 12399
|
||||
const port2 = 12400
|
||||
|
||||
// Hook into the trap handler so the test knows when the
|
||||
// trap has been received
|
||||
received1 := make(chan int)
|
||||
wrap1 := func(f gosnmp.TrapHandlerFunc) gosnmp.TrapHandlerFunc {
|
||||
return func(p *gosnmp.SnmpPacket, a *net.UDPAddr) {
|
||||
f(p, a)
|
||||
received1 <- 0
|
||||
}
|
||||
}
|
||||
|
||||
// Set up the service input plugin
|
||||
s1 := &SnmpTrap{
|
||||
ServiceAddress: "udp://:" + strconv.Itoa(port1),
|
||||
makeHandlerWrapper: wrap1,
|
||||
timeFunc: func() time.Time {
|
||||
return fakeTime
|
||||
},
|
||||
//if cold start be answer otherwise err
|
||||
Log: testutil.Logger{},
|
||||
Version: tt.version.String(),
|
||||
SecName: config.NewSecret([]byte(tt.secName + "1")),
|
||||
SecLevel: tt.secLevel,
|
||||
AuthProtocol: tt.authProto,
|
||||
AuthPassword: config.NewSecret([]byte(tt.authPass + "1")),
|
||||
PrivProtocol: tt.privProto,
|
||||
PrivPassword: config.NewSecret([]byte(tt.privPass + "1")),
|
||||
Translator: "netsnmp",
|
||||
}
|
||||
|
||||
// Hook into the trap handler so the test knows when the
|
||||
// trap has been received
|
||||
received2 := make(chan int)
|
||||
wrap2 := func(f gosnmp.TrapHandlerFunc) gosnmp.TrapHandlerFunc {
|
||||
return func(p *gosnmp.SnmpPacket, a *net.UDPAddr) {
|
||||
f(p, a)
|
||||
received2 <- 0
|
||||
}
|
||||
}
|
||||
|
||||
// Set up a second service input plugin
|
||||
s2 := &SnmpTrap{
|
||||
ServiceAddress: "udp://:" + strconv.Itoa(port2),
|
||||
makeHandlerWrapper: wrap2,
|
||||
timeFunc: func() time.Time {
|
||||
return fakeTime
|
||||
},
|
||||
//if cold start be answer otherwise err
|
||||
Log: testutil.Logger{},
|
||||
Version: tt.version.String(),
|
||||
SecName: config.NewSecret([]byte(tt.secName + "2")),
|
||||
SecLevel: tt.secLevel,
|
||||
AuthProtocol: tt.authProto,
|
||||
AuthPassword: config.NewSecret([]byte(tt.authPass + "2")),
|
||||
PrivProtocol: tt.privProto,
|
||||
PrivPassword: config.NewSecret([]byte(tt.privPass + "2")),
|
||||
Translator: "netsnmp",
|
||||
}
|
||||
|
||||
require.NoError(t, s1.Init())
|
||||
require.NoError(t, s2.Init())
|
||||
|
||||
//inject test translator
|
||||
s1.transl = newTestTranslator(tt.entries)
|
||||
s2.transl = newTestTranslator(tt.entries)
|
||||
|
||||
var acc testutil.Accumulator
|
||||
require.NoError(t, s1.Start(&acc))
|
||||
require.NoError(t, s2.Start(&acc))
|
||||
|
||||
defer s1.Stop()
|
||||
defer s2.Stop()
|
||||
|
||||
var goSNMP1 gosnmp.GoSNMP
|
||||
var goSNMP2 gosnmp.GoSNMP
|
||||
if tt.version == gosnmp.Version3 {
|
||||
msgFlags := newMsgFlagsV3(tt.secLevel)
|
||||
sp1 := newUsmSecurityParametersForV3(tt.authProto, tt.privProto, tt.secName+"1", tt.privPass+"1", tt.authPass+"1")
|
||||
sp2 := newUsmSecurityParametersForV3(tt.authProto, tt.privProto, tt.secName+"2", tt.privPass+"2", tt.authPass+"2")
|
||||
goSNMP1 = newGoSNMPV3(port1, tt.contextName, tt.engineID, msgFlags, sp1)
|
||||
goSNMP2 = newGoSNMPV3(port2, tt.contextName, tt.engineID, msgFlags, sp2)
|
||||
} else {
|
||||
goSNMP1 = newGoSNMP(tt.version, port1)
|
||||
goSNMP2 = newGoSNMP(tt.version, port2)
|
||||
}
|
||||
|
||||
// Send the trap to both receivers
|
||||
sendTrap(t, goSNMP1, tt.trap)
|
||||
sendTrap(t, goSNMP2, tt.trap)
|
||||
|
||||
// Wait for trap to be received on receiver1
|
||||
select {
|
||||
case <-received1:
|
||||
case <-time.After(2 * time.Second):
|
||||
t.Fatal("timed out waiting for trap to be received")
|
||||
}
|
||||
// Wait for trap to be received on receiver2
|
||||
select {
|
||||
case <-received2:
|
||||
case <-time.After(2 * time.Second):
|
||||
t.Fatal("timed out waiting for trap to be received")
|
||||
}
|
||||
|
||||
// Verify plugin output
|
||||
testutil.RequireMetricsEqual(t,
|
||||
tt.metrics, acc.GetTelegrafMetrics(),
|
||||
testutil.SortMetrics())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue