feat(inputs.snmp): Convert octet string with invalid data to hex (#15439)
This commit is contained in:
parent
c2a67ecd03
commit
c1bbce3e96
|
|
@ -2,12 +2,14 @@ package snmp
|
|||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/gosnmp/gosnmp"
|
||||
)
|
||||
|
|
@ -94,6 +96,10 @@ func (f *Field) Init(tr Translator) error {
|
|||
// fieldConvert converts from any type according to the conv specification
|
||||
func (f *Field) Convert(ent gosnmp.SnmpPDU) (interface{}, error) {
|
||||
if f.Conversion == "" {
|
||||
// OctetStrings may contain hex data that needs its own conversion
|
||||
if ent.Type == gosnmp.OctetString && !utf8.ValidString(string(ent.Value.([]byte)[:])) {
|
||||
return hex.EncodeToString(ent.Value.([]byte)), nil
|
||||
}
|
||||
if bs, ok := ent.Value.([]byte); ok {
|
||||
return string(bs), nil
|
||||
}
|
||||
|
|
@ -188,7 +194,29 @@ func (f *Field) Convert(ent gosnmp.SnmpPDU) (interface{}, error) {
|
|||
case []byte:
|
||||
v = net.HardwareAddr(vt).String()
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid type (%T) for hwaddr conversion", v)
|
||||
return nil, fmt.Errorf("invalid type (%T) for hwaddr conversion", vt)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
if f.Conversion == "hex" {
|
||||
switch vt := ent.Value.(type) {
|
||||
case string:
|
||||
switch ent.Type {
|
||||
case gosnmp.IPAddress:
|
||||
ip := net.ParseIP(vt)
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
v = hex.EncodeToString(ip4)
|
||||
} else {
|
||||
v = hex.EncodeToString(ip)
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported Asn1BER (%#v) for hex conversion", ent.Type)
|
||||
}
|
||||
case []byte:
|
||||
v = hex.EncodeToString(vt)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported type (%T) for hex conversion", vt)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
|
@ -242,7 +270,7 @@ func (f *Field) Convert(ent gosnmp.SnmpPDU) (interface{}, error) {
|
|||
case []byte:
|
||||
ipbs = vt
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid type (%T) for ipaddr conversion", v)
|
||||
return nil, fmt.Errorf("invalid type (%T) for ipaddr conversion", vt)
|
||||
}
|
||||
|
||||
switch len(ipbs) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,243 @@
|
|||
package snmp
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gosnmp/gosnmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestConvertDefault(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
ent gosnmp.SnmpPDU
|
||||
expected interface{}
|
||||
errmsg string
|
||||
}{
|
||||
{
|
||||
name: "integer",
|
||||
ent: gosnmp.SnmpPDU{
|
||||
Type: gosnmp.Integer,
|
||||
Value: int(2),
|
||||
},
|
||||
expected: 2,
|
||||
},
|
||||
{
|
||||
name: "octet string with valid bytes",
|
||||
ent: gosnmp.SnmpPDU{
|
||||
Type: gosnmp.OctetString,
|
||||
Value: []byte{0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64},
|
||||
},
|
||||
expected: "Hello world",
|
||||
},
|
||||
{
|
||||
name: "octet string with invalid bytes",
|
||||
ent: gosnmp.SnmpPDU{
|
||||
Type: gosnmp.OctetString,
|
||||
Value: []byte{0x84, 0xc8, 0x7, 0xff, 0xfd, 0x38, 0x54, 0xc1},
|
||||
},
|
||||
expected: "84c807fffd3854c1",
|
||||
},
|
||||
}
|
||||
|
||||
f := Field{}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
actual, err := f.Convert(tt.ent)
|
||||
|
||||
if tt.errmsg != "" {
|
||||
require.ErrorContains(t, err, tt.errmsg)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
require.Equal(t, tt.expected, actual)
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("invalid", func(t *testing.T) {
|
||||
f.Conversion = "invalid"
|
||||
actual, err := f.Convert(gosnmp.SnmpPDU{})
|
||||
|
||||
require.Nil(t, actual)
|
||||
require.ErrorContains(t, err, "invalid conversion type")
|
||||
})
|
||||
}
|
||||
|
||||
func TestConvertHex(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
ent gosnmp.SnmpPDU
|
||||
expected interface{}
|
||||
errmsg string
|
||||
}{
|
||||
{
|
||||
name: "octet string with valid bytes",
|
||||
ent: gosnmp.SnmpPDU{
|
||||
Type: gosnmp.OctetString,
|
||||
Value: []byte{0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64},
|
||||
},
|
||||
expected: "48656c6c6f20776f726c64",
|
||||
},
|
||||
{
|
||||
name: "octet string with invalid bytes",
|
||||
ent: gosnmp.SnmpPDU{
|
||||
Type: gosnmp.OctetString,
|
||||
Value: []byte{0x84, 0xc8, 0x7, 0xff, 0xfd, 0x38, 0x54, 0xc1},
|
||||
},
|
||||
expected: "84c807fffd3854c1",
|
||||
},
|
||||
{
|
||||
name: "IPv4",
|
||||
ent: gosnmp.SnmpPDU{
|
||||
Type: gosnmp.IPAddress,
|
||||
Value: "192.0.2.1",
|
||||
},
|
||||
expected: "c0000201",
|
||||
},
|
||||
{
|
||||
name: "IPv6",
|
||||
ent: gosnmp.SnmpPDU{
|
||||
Type: gosnmp.IPAddress,
|
||||
Value: "2001:db8::1",
|
||||
},
|
||||
expected: "20010db8000000000000000000000001",
|
||||
},
|
||||
{
|
||||
name: "oid",
|
||||
ent: gosnmp.SnmpPDU{
|
||||
Type: gosnmp.ObjectIdentifier,
|
||||
Value: ".1.2.3",
|
||||
},
|
||||
errmsg: "unsupported Asn1BER (0x6) for hex conversion",
|
||||
},
|
||||
{
|
||||
name: "integer",
|
||||
ent: gosnmp.SnmpPDU{
|
||||
Type: gosnmp.Integer,
|
||||
Value: int(2),
|
||||
},
|
||||
errmsg: "unsupported type (int) for hex conversion",
|
||||
},
|
||||
}
|
||||
|
||||
f := Field{Conversion: "hex"}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
actual, err := f.Convert(tt.ent)
|
||||
|
||||
if tt.errmsg != "" {
|
||||
require.ErrorContains(t, err, tt.errmsg)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
require.Equal(t, tt.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertHextoint(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
conversion string
|
||||
ent gosnmp.SnmpPDU
|
||||
expected interface{}
|
||||
errmsg string
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
conversion: "hextoint:BigEndian:uint64",
|
||||
ent: gosnmp.SnmpPDU{},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "big endian uint64",
|
||||
conversion: "hextoint:BigEndian:uint64",
|
||||
ent: gosnmp.SnmpPDU{
|
||||
Type: gosnmp.OctetString,
|
||||
Value: []byte{0x84, 0xc8, 0x7, 0xff, 0xfd, 0x38, 0x54, 0xc1},
|
||||
},
|
||||
expected: uint64(0x84c807fffd3854c1),
|
||||
},
|
||||
{
|
||||
name: "big endian uint32",
|
||||
conversion: "hextoint:BigEndian:uint32",
|
||||
ent: gosnmp.SnmpPDU{
|
||||
Type: gosnmp.OctetString,
|
||||
Value: []byte{0x84, 0xc8, 0x7, 0xff},
|
||||
},
|
||||
expected: uint32(0x84c807ff),
|
||||
},
|
||||
{
|
||||
name: "big endian uint16",
|
||||
conversion: "hextoint:BigEndian:uint16",
|
||||
ent: gosnmp.SnmpPDU{
|
||||
Type: gosnmp.OctetString,
|
||||
Value: []byte{0x84, 0xc8},
|
||||
},
|
||||
expected: uint16(0x84c8),
|
||||
},
|
||||
{
|
||||
name: "big endian invalid",
|
||||
conversion: "hextoint:BigEndian:invalid",
|
||||
ent: gosnmp.SnmpPDU{Type: gosnmp.OctetString, Value: []uint8{}},
|
||||
errmsg: "invalid bit value",
|
||||
},
|
||||
{
|
||||
name: "little endian uint64",
|
||||
conversion: "hextoint:LittleEndian:uint64",
|
||||
ent: gosnmp.SnmpPDU{
|
||||
Type: gosnmp.OctetString,
|
||||
Value: []byte{0x84, 0xc8, 0x7, 0xff, 0xfd, 0x38, 0x54, 0xc1},
|
||||
},
|
||||
expected: uint64(0xc15438fdff07c884),
|
||||
},
|
||||
{
|
||||
name: "little endian uint32",
|
||||
conversion: "hextoint:LittleEndian:uint32",
|
||||
ent: gosnmp.SnmpPDU{
|
||||
Type: gosnmp.OctetString,
|
||||
Value: []byte{0x84, 0xc8, 0x7, 0xff},
|
||||
},
|
||||
expected: uint32(0xff07c884),
|
||||
},
|
||||
{
|
||||
name: "little endian uint16",
|
||||
conversion: "hextoint:LittleEndian:uint16",
|
||||
ent: gosnmp.SnmpPDU{
|
||||
Type: gosnmp.OctetString,
|
||||
Value: []byte{0x84, 0xc8},
|
||||
},
|
||||
expected: uint16(0xc884),
|
||||
},
|
||||
{
|
||||
name: "little endian invalid",
|
||||
conversion: "hextoint:LittleEndian:invalid",
|
||||
ent: gosnmp.SnmpPDU{Type: gosnmp.OctetString, Value: []byte{}},
|
||||
errmsg: "invalid bit value",
|
||||
},
|
||||
{
|
||||
name: "invalid",
|
||||
conversion: "hextoint:invalid:uint64",
|
||||
ent: gosnmp.SnmpPDU{Type: gosnmp.OctetString, Value: []byte{}},
|
||||
errmsg: "invalid Endian value",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
f := Field{Conversion: tt.conversion}
|
||||
|
||||
actual, err := f.Convert(tt.ent)
|
||||
|
||||
if tt.errmsg != "" {
|
||||
require.ErrorContains(t, err, tt.errmsg)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
require.Equal(t, tt.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -294,7 +294,6 @@ func TestFieldConvertGosmi(t *testing.T) {
|
|||
conv string
|
||||
expected interface{}
|
||||
}{
|
||||
{[]byte("foo"), "", "foo"},
|
||||
{"0.123", "float", float64(0.123)},
|
||||
{[]byte("0.123"), "float", float64(0.123)},
|
||||
{float32(0.123), "float", float64(float32(0.123))},
|
||||
|
|
@ -333,12 +332,6 @@ func TestFieldConvertGosmi(t *testing.T) {
|
|||
{[]byte("abcd"), "ipaddr", "97.98.99.100"},
|
||||
{"abcd", "ipaddr", "97.98.99.100"},
|
||||
{[]byte("abcdefghijklmnop"), "ipaddr", "6162:6364:6566:6768:696a:6b6c:6d6e:6f70"},
|
||||
{[]byte{0x00, 0x09, 0x3E, 0xE3, 0xF6, 0xD5, 0x3B, 0x60}, "hextoint:BigEndian:uint64", uint64(2602423610063712)},
|
||||
{[]byte{0x00, 0x09, 0x3E, 0xE3}, "hextoint:BigEndian:uint32", uint32(605923)},
|
||||
{[]byte{0x00, 0x09}, "hextoint:BigEndian:uint16", uint16(9)},
|
||||
{[]byte{0x00, 0x09, 0x3E, 0xE3, 0xF6, 0xD5, 0x3B, 0x60}, "hextoint:LittleEndian:uint64", uint64(6934371307618175232)},
|
||||
{[]byte{0x00, 0x09, 0x3E, 0xE3}, "hextoint:LittleEndian:uint32", uint32(3812493568)},
|
||||
{[]byte{0x00, 0x09}, "hextoint:LittleEndian:uint16", uint16(2304)},
|
||||
{3, "enum", "testing"},
|
||||
{3, "enum(1)", "testing(3)"},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ package snmp
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gosnmp/gosnmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/influxdata/telegraf/testutil"
|
||||
|
|
@ -222,72 +221,6 @@ func TestTableBuild_noWalk(t *testing.T) {
|
|||
require.Contains(t, tb.Rows, rtr)
|
||||
}
|
||||
|
||||
func TestFieldConvert(t *testing.T) {
|
||||
testTable := []struct {
|
||||
input interface{}
|
||||
conv string
|
||||
expected interface{}
|
||||
}{
|
||||
{[]byte("foo"), "", "foo"},
|
||||
{"0.123", "float", float64(0.123)},
|
||||
{[]byte("0.123"), "float", float64(0.123)},
|
||||
{float32(0.123), "float", float64(float32(0.123))},
|
||||
{float64(0.123), "float", float64(0.123)},
|
||||
{float64(0.123123123123), "float", float64(0.123123123123)},
|
||||
{123, "float", float64(123)},
|
||||
{123, "float(0)", float64(123)},
|
||||
{123, "float(4)", float64(0.0123)},
|
||||
{int8(123), "float(3)", float64(0.123)},
|
||||
{int16(123), "float(3)", float64(0.123)},
|
||||
{int32(123), "float(3)", float64(0.123)},
|
||||
{int64(123), "float(3)", float64(0.123)},
|
||||
{uint(123), "float(3)", float64(0.123)},
|
||||
{uint8(123), "float(3)", float64(0.123)},
|
||||
{uint16(123), "float(3)", float64(0.123)},
|
||||
{uint32(123), "float(3)", float64(0.123)},
|
||||
{uint64(123), "float(3)", float64(0.123)},
|
||||
{"123", "int", int64(123)},
|
||||
{[]byte("123"), "int", int64(123)},
|
||||
{"123123123123", "int", int64(123123123123)},
|
||||
{[]byte("123123123123"), "int", int64(123123123123)},
|
||||
{float32(12.3), "int", int64(12)},
|
||||
{float64(12.3), "int", int64(12)},
|
||||
{int(123), "int", int64(123)},
|
||||
{int8(123), "int", int64(123)},
|
||||
{int16(123), "int", int64(123)},
|
||||
{int32(123), "int", int64(123)},
|
||||
{int64(123), "int", int64(123)},
|
||||
{uint(123), "int", int64(123)},
|
||||
{uint8(123), "int", int64(123)},
|
||||
{uint16(123), "int", int64(123)},
|
||||
{uint32(123), "int", int64(123)},
|
||||
{uint64(123), "int", int64(123)},
|
||||
{[]byte("abcdef"), "hwaddr", "61:62:63:64:65:66"},
|
||||
{"abcdef", "hwaddr", "61:62:63:64:65:66"},
|
||||
{[]byte("abcd"), "ipaddr", "97.98.99.100"},
|
||||
{"abcd", "ipaddr", "97.98.99.100"},
|
||||
{[]byte("abcdefghijklmnop"), "ipaddr", "6162:6364:6566:6768:696a:6b6c:6d6e:6f70"},
|
||||
{[]byte{0x00, 0x09, 0x3E, 0xE3, 0xF6, 0xD5, 0x3B, 0x60}, "hextoint:BigEndian:uint64", uint64(2602423610063712)},
|
||||
{[]byte{0x00, 0x09, 0x3E, 0xE3}, "hextoint:BigEndian:uint32", uint32(605923)},
|
||||
{[]byte{0x00, 0x09}, "hextoint:BigEndian:uint16", uint16(9)},
|
||||
{[]byte{0x00, 0x09, 0x3E, 0xE3, 0xF6, 0xD5, 0x3B, 0x60}, "hextoint:LittleEndian:uint64", uint64(6934371307618175232)},
|
||||
{[]byte{0x00, 0x09, 0x3E, 0xE3}, "hextoint:LittleEndian:uint32", uint32(3812493568)},
|
||||
{[]byte{0x00, 0x09}, "hextoint:LittleEndian:uint16", uint16(2304)},
|
||||
}
|
||||
|
||||
for _, tc := range testTable {
|
||||
f := Field{
|
||||
Name: "test",
|
||||
Conversion: tc.conv,
|
||||
}
|
||||
require.NoError(t, f.Init(NewNetsnmpTranslator(testutil.Logger{})))
|
||||
|
||||
act, err := f.Convert(gosnmp.SnmpPDU{Value: tc.input})
|
||||
require.NoError(t, err, "input=%T(%v) conv=%s expected=%T(%v)", tc.input, tc.input, tc.conv, tc.expected, tc.expected)
|
||||
require.EqualValues(t, tc.expected, act, "input=%T(%v) conv=%s expected=%T(%v)", tc.input, tc.input, tc.conv, tc.expected, tc.expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSnmpTranslateCache_miss(t *testing.T) {
|
||||
snmpTranslateCaches = nil
|
||||
oid := "IF-MIB::ifPhysAddress.1"
|
||||
|
|
|
|||
|
|
@ -171,11 +171,12 @@ option operate similar to the `snmpget` utility.
|
|||
## int: Convert the value into an integer.
|
||||
## hwaddr: Convert the value to a MAC address.
|
||||
## ipaddr: Convert the value to an IP address.
|
||||
## hextoint:X:Y Convert a hex string value to integer. Where X is the Endian
|
||||
## and Y the bit size. For example: hextoint:LittleEndian:uint64
|
||||
## or hextoint:BigEndian:uint32. Valid options for the Endian are:
|
||||
## BigEndian and LittleEndian. For the bit size: uint16, uint32
|
||||
## and uint64.
|
||||
## hex: Convert bytes to a hex string.
|
||||
## hextoint:X:Y Convert bytes to integer, where X is the endian and Y the
|
||||
## bit size. For example: hextoint:LittleEndian:uint64 or
|
||||
## hextoint:BigEndian:uint32. Valid options for the endian
|
||||
## are: BigEndian and LittleEndian. For the bit size:
|
||||
## uint16, uint32 and uint64.
|
||||
## enum(1): Convert the value according to its syntax in the MIB (full).
|
||||
## (Only supported with gosmi translator)
|
||||
## enum: Convert the value according to its syntax in the MIB.
|
||||
|
|
|
|||
Loading…
Reference in New Issue