Skip to content

Commit

Permalink
Add DateAndTime display hint
Browse files Browse the repository at this point in the history
Also improve peformance:
 - unsafe string conversion to remove allocation
 - hex formatting is done manually instead by fmt.Sprintf
  • Loading branch information
grongor committed Oct 15, 2021
1 parent 87f3dc4 commit 654afb4
Show file tree
Hide file tree
Showing 7 changed files with 252 additions and 118 deletions.
3 changes: 1 addition & 2 deletions cmd/snmp-proxy/snmp-proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ func main() {
config.Logger.Fatalw("mib parser error: ", zap.Error(err))
}

mibDataProvider := mib.NewDataProvider(displayHints)
requester := snmpproxy.NewGosnmpRequester(mibDataProvider)
requester := snmpproxy.NewGosnmpRequester(snmpproxy.NewValueFormatter(mib.NewDataProvider(displayHints)))

apiListener := snmpproxy.NewApiListener(
validator,
Expand Down
1 change: 1 addition & 0 deletions snmpproxy/mib/mib.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const (
DisplayHintUnknown = DisplayHint(iota)
DisplayHintString
DisplayHintHexadecimal
DisplayHintDateAndTime
)

type DisplayHints map[string]DisplayHint
Expand Down
2 changes: 2 additions & 0 deletions snmpproxy/mib/netsnmp.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ func (p *NetsnmpMibParser) findStringTypesDisplayHints(displayHints DisplayHints
displayHints[oid] = DisplayHintString
case "PhysAddress":
displayHints[oid] = DisplayHintHexadecimal
case "DateAndTime":
displayHints[oid] = DisplayHintDateAndTime
}

return
Expand Down
45 changes: 5 additions & 40 deletions snmpproxy/requester.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,8 @@ import (
"fmt"
"strconv"
"strings"
"unicode"
"unicode/utf8"

"github.com/gosnmp/gosnmp"
"github.com/grongor/go-snmp-proxy/snmpproxy/mib"
)

type requestResult struct {
Expand All @@ -23,7 +20,7 @@ type Requester interface {
}

type GosnmpRequester struct {
mibDataProvider *mib.DataProvider
valueFormatter *ValueFormatter
}

func (r *GosnmpRequester) ExecuteRequest(apiRequest *ApiRequest) ([][]interface{}, error) {
Expand Down Expand Up @@ -141,7 +138,7 @@ func (r *GosnmpRequester) processGetPacket(packet *gosnmp.SnmpPacket, request Re
return result, fmt.Errorf("end of mib: %s", dataUnit.Name)
}

result = append(result, dataUnit.Name, r.getPduValue(dataUnit))
result = append(result, dataUnit.Name, r.valueFormatter.Format(dataUnit))
}

return result, nil
Expand Down Expand Up @@ -178,7 +175,7 @@ func (r *GosnmpRequester) executeWalk(apiRequest *ApiRequest, requestNo int, res
oid := request.Oids[0]

err = walker(oid, func(dataUnit gosnmp.SnmpPDU) error {
result.result = append(result.result, dataUnit.Name, r.getPduValue(dataUnit))
result.result = append(result.result, dataUnit.Name, r.valueFormatter.Format(dataUnit))

return nil
})
Expand Down Expand Up @@ -250,38 +247,6 @@ func (*GosnmpRequester) createSnmpHandler(apiRequest *ApiRequest) (gosnmp.Handle
return snmp, nil
}

func (r *GosnmpRequester) getPduValue(dataUnit gosnmp.SnmpPDU) interface{} {
switch dataUnit.Type { //nolint:exhaustive // We only care about gosnmp.OctetString, the rest is just passed along
case gosnmp.OctetString:
displayHint := r.mibDataProvider.GetDisplayHint(dataUnit.Name)
if displayHint == mib.DisplayHintString ||
// best effort to display octet strings correctly without the MIBs
displayHint == mib.DisplayHintUnknown && r.isStringPrintable(dataUnit.Value.([]byte)) {
return string(dataUnit.Value.([]byte))
}

return fmt.Sprintf("% X", dataUnit.Value)
default:
return dataUnit.Value
}
}

func (*GosnmpRequester) isStringPrintable(value []byte) bool {
if !utf8.Valid(value) {
return false
}

for _, b := range value {
if unicode.IsPrint(rune(b)) || unicode.IsSpace(rune(b)) {
continue
}

return false
}

return true
}

func NewGosnmpRequester(mibDataProvider *mib.DataProvider) *GosnmpRequester {
return &GosnmpRequester{mibDataProvider: mibDataProvider}
func NewGosnmpRequester(valueFormatter *ValueFormatter) *GosnmpRequester {
return &GosnmpRequester{valueFormatter: valueFormatter}
}
107 changes: 31 additions & 76 deletions snmpproxy/requester_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"sync"
"testing"
"time"
"unicode/utf8"

"github.com/gosnmp/gosnmp"
"github.com/grongor/go-snmp-proxy/snmpproxy"
Expand Down Expand Up @@ -82,7 +81,7 @@ func TestGet(t *testing.T) {

apiRequest := apiRequest(get([]string{".1.3.6.1.2.1.25.2.3.1.2.1", ".1.3.6.1.2.1.25.2.3.1.2.4"}))

requester := snmpproxy.NewGosnmpRequester(mib.NewDataProvider(nil))
requester := snmpproxy.NewGosnmpRequester(snmpproxy.NewValueFormatter(mib.NewDataProvider(nil)))
result, err := requester.ExecuteRequest(apiRequest)
assert.NoError(err)

Expand All @@ -97,59 +96,12 @@ func TestGet(t *testing.T) {
)
}

func TestGetStringDisplayHintGuessedString(t *testing.T) {
assert := require.New(t)

apiRequest := apiRequest(get([]string{".1.3.6.1.2.1.2.2.1.2.48"}))

requester := snmpproxy.NewGosnmpRequester(mib.NewDataProvider(nil))
result, err := requester.ExecuteRequest(apiRequest)
assert.NoError(err)

assert.Equal(
[][]interface{}{{".1.3.6.1.2.1.2.2.1.2.48", "Ethernet48"}},
result,
)
}

func TestGetStringDisplayHintGuessedHexadecimalBecauseNotUtf8Valid(t *testing.T) {
assert := require.New(t)

apiRequest := apiRequest(get([]string{".1.3.6.1.2.1.4.22.1.2.2000955.185.152.67.97"}))

requester := snmpproxy.NewGosnmpRequester(mib.NewDataProvider(nil))
result, err := requester.ExecuteRequest(apiRequest)
assert.NoError(err)

assert.Equal(
[][]interface{}{{".1.3.6.1.2.1.4.22.1.2.2000955.185.152.67.97", "91 E2 BA E3 5A 61"}},
result,
)
}

func TestGetStringDisplayHintGuessedHexadecimalBecauseNotPrintable(t *testing.T) {
assert := require.New(t)

apiRequest := apiRequest(get([]string{".1.3.6.1.2.1.4.22.1.2.2000955.185.152.67.100"}))

requester := snmpproxy.NewGosnmpRequester(mib.NewDataProvider(nil))
result, err := requester.ExecuteRequest(apiRequest)
assert.NoError(err)

assert.True(utf8.Valid([]byte{}))

assert.Equal(
[][]interface{}{{".1.3.6.1.2.1.4.22.1.2.2000955.185.152.67.100", "53 54 00 4C 5A 5D"}},
result,
)
}

func TestGetNext(t *testing.T) {
assert := require.New(t)

apiRequest := apiRequest(getNext([]string{".1.3.6.1.2.1.25.2.3.1.2", ".1.3.6.1.2.1.25.2.3.1.2.3"}))

requester := snmpproxy.NewGosnmpRequester(mib.NewDataProvider(nil))
requester := snmpproxy.NewGosnmpRequester(snmpproxy.NewValueFormatter(mib.NewDataProvider(nil)))
result, err := requester.ExecuteRequest(apiRequest)
assert.NoError(err)

Expand All @@ -169,7 +121,7 @@ func TestWalk(t *testing.T) {

apiRequest := apiRequest(walk(".1.3.6.1.2.1.31.1.1.1.15"))

requester := snmpproxy.NewGosnmpRequester(mib.NewDataProvider(nil))
requester := snmpproxy.NewGosnmpRequester(snmpproxy.NewValueFormatter(mib.NewDataProvider(nil)))
result, err := requester.ExecuteRequest(apiRequest)
assert.NoError(err)

Expand All @@ -191,7 +143,7 @@ func TestWalkWithSnmpVersion1(t *testing.T) {
apiRequest := apiRequest(walk(".1.3.6.1.2.1.31.1.1.1.15"))
apiRequest.Version = snmpproxy.SnmpVersion(gosnmp.Version1)

requester := snmpproxy.NewGosnmpRequester(mib.NewDataProvider(nil))
requester := snmpproxy.NewGosnmpRequester(snmpproxy.NewValueFormatter(mib.NewDataProvider(nil)))
result, err := requester.ExecuteRequest(apiRequest)
assert.NoError(err)

Expand All @@ -212,11 +164,14 @@ func TestWalkWholeTree(t *testing.T) {

apiRequest := apiRequest(walk(".1.3"))

mibDataProvider := mib.NewDataProvider(mib.DisplayHints{
".1.3.6.1.2.1.2.2.1.2": mib.DisplayHintString,
".1.3.6.1.2.1.4.22.1.2": mib.DisplayHintHexadecimal,
})

mibDataProvider := snmpproxy.NewValueFormatter(
mib.NewDataProvider(
mib.DisplayHints{
".1.3.6.1.2.1.2.2.1.2": mib.DisplayHintString,
".1.3.6.1.2.1.4.22.1.2": mib.DisplayHintHexadecimal,
},
),
)
requester := snmpproxy.NewGosnmpRequester(mibDataProvider)
result, err := requester.ExecuteRequest(apiRequest)
assert.NoError(err)
Expand Down Expand Up @@ -277,7 +232,7 @@ func TestWalkLastMibElement(t *testing.T) {

apiRequest := apiRequest(walk(".1.7"))

requester := snmpproxy.NewGosnmpRequester(mib.NewDataProvider(nil))
requester := snmpproxy.NewGosnmpRequester(snmpproxy.NewValueFormatter(mib.NewDataProvider(nil)))

result, err := requester.ExecuteRequest(apiRequest)
assert.NoError(err)
Expand All @@ -291,7 +246,7 @@ func TestWalkLastMibElementAndSnmpVersion1(t *testing.T) {
apiRequest := apiRequest(walk(".1.7"))
apiRequest.Version = snmpproxy.SnmpVersion(gosnmp.Version1)

requester := snmpproxy.NewGosnmpRequester(mib.NewDataProvider(nil))
requester := snmpproxy.NewGosnmpRequester(snmpproxy.NewValueFormatter(mib.NewDataProvider(nil)))

result, err := requester.ExecuteRequest(apiRequest)
assert.NoError(err)
Expand All @@ -308,7 +263,7 @@ func TestMultipleRequests(t *testing.T) {
getNext([]string{".1.3.6.1.2.1.2.2.1.14.9", ".1.3.6.1.2.1.2.2.1.14.10"}),
)

requester := snmpproxy.NewGosnmpRequester(mib.NewDataProvider(nil))
requester := snmpproxy.NewGosnmpRequester(snmpproxy.NewValueFormatter(mib.NewDataProvider(nil)))

result, err := requester.ExecuteRequest(apiRequest)
assert.NoError(err)
Expand All @@ -335,7 +290,7 @@ func TestMultipleRequestsWithSingleError(t *testing.T) {
getNext([]string{".1.3.6.1.2.1.2.2.1.14.9", ".1.7.9"}),
)

requester := snmpproxy.NewGosnmpRequester(mib.NewDataProvider(nil))
requester := snmpproxy.NewGosnmpRequester(snmpproxy.NewValueFormatter(mib.NewDataProvider(nil)))

result, err := requester.ExecuteRequest(apiRequest)
assert.Nil(result)
Expand All @@ -351,7 +306,7 @@ func TestMultipleRequestsAllError(t *testing.T) {
getNext([]string{".1.3.6.1.2.1.2.2.1.14.9", ".1.7.9"}),
)

requester := snmpproxy.NewGosnmpRequester(mib.NewDataProvider(nil))
requester := snmpproxy.NewGosnmpRequester(snmpproxy.NewValueFormatter(mib.NewDataProvider(nil)))

result, err := requester.ExecuteRequest(apiRequest)
assert.Nil(result)
Expand All @@ -367,7 +322,7 @@ func TestWalkWithTimeout(t *testing.T) {
apiRequest.Version = snmpproxy.SnmpVersion(gosnmp.Version1)
apiRequest.Timeout = time.Millisecond

requester := snmpproxy.NewGosnmpRequester(mib.NewDataProvider(nil))
requester := snmpproxy.NewGosnmpRequester(snmpproxy.NewValueFormatter(mib.NewDataProvider(nil)))
result, err := requester.ExecuteRequest(apiRequest)
assert.Nil(result)
assert.EqualError(err, "timeout: .1.15")
Expand All @@ -378,7 +333,7 @@ func TestWalkWithNoSuchInstanceError(t *testing.T) {

apiRequest := apiRequest(walk(".1.3.5"))

requester := snmpproxy.NewGosnmpRequester(mib.NewDataProvider(nil))
requester := snmpproxy.NewGosnmpRequester(snmpproxy.NewValueFormatter(mib.NewDataProvider(nil)))
result, err := requester.ExecuteRequest(apiRequest)
assert.Nil(result)
assert.EqualError(err, "no such instance: .1.3.5")
Expand All @@ -390,7 +345,7 @@ func TestWalkWithSnmpVersion1AndNoSuchInstanceError(t *testing.T) {
apiRequest := apiRequest(walk(".1.3.5"))
apiRequest.Version = snmpproxy.SnmpVersion(gosnmp.Version1)

requester := snmpproxy.NewGosnmpRequester(mib.NewDataProvider(nil))
requester := snmpproxy.NewGosnmpRequester(snmpproxy.NewValueFormatter(mib.NewDataProvider(nil)))
result, err := requester.ExecuteRequest(apiRequest)
assert.Nil(result)
assert.EqualError(err, "no such instance: .1.3.5")
Expand All @@ -401,7 +356,7 @@ func TestWalkWithEndOfMibError(t *testing.T) {

apiRequest := apiRequest(walk(".1.15"))

requester := snmpproxy.NewGosnmpRequester(mib.NewDataProvider(nil))
requester := snmpproxy.NewGosnmpRequester(snmpproxy.NewValueFormatter(mib.NewDataProvider(nil)))
result, err := requester.ExecuteRequest(apiRequest)
assert.Nil(result)
assert.EqualError(err, "end of mib: .1.15")
Expand All @@ -413,7 +368,7 @@ func TestWalkWithSnmpVersion1AndEndOfMibError(t *testing.T) {
apiRequest := apiRequest(walk(".1.15"))
apiRequest.Version = snmpproxy.SnmpVersion(gosnmp.Version1)

requester := snmpproxy.NewGosnmpRequester(mib.NewDataProvider(nil))
requester := snmpproxy.NewGosnmpRequester(snmpproxy.NewValueFormatter(mib.NewDataProvider(nil)))
result, err := requester.ExecuteRequest(apiRequest)
assert.Nil(result)
assert.EqualError(err, "end of mib: .1.15")
Expand All @@ -427,7 +382,7 @@ func TestGetWithTimeout(t *testing.T) {
apiRequest.Version = snmpproxy.SnmpVersion(gosnmp.Version1)
apiRequest.Timeout = time.Millisecond

requester := snmpproxy.NewGosnmpRequester(mib.NewDataProvider(nil))
requester := snmpproxy.NewGosnmpRequester(snmpproxy.NewValueFormatter(mib.NewDataProvider(nil)))
result, err := requester.ExecuteRequest(apiRequest)
assert.Nil(result)
assert.EqualError(err, "timeout: .1.15")
Expand All @@ -438,7 +393,7 @@ func TestGetWithNoSuchInstanceError(t *testing.T) {

apiRequest := apiRequest(get([]string{".1.3.5"}))

requester := snmpproxy.NewGosnmpRequester(mib.NewDataProvider(nil))
requester := snmpproxy.NewGosnmpRequester(snmpproxy.NewValueFormatter(mib.NewDataProvider(nil)))
result, err := requester.ExecuteRequest(apiRequest)
assert.Nil(result)
assert.EqualError(err, "no such instance: .1.3.5")
Expand All @@ -450,7 +405,7 @@ func TestGetWithSnmpVersion1AndNoSuchInstanceError(t *testing.T) {
apiRequest := apiRequest(get([]string{".1.3.5"}))
apiRequest.Version = snmpproxy.SnmpVersion(gosnmp.Version1)

requester := snmpproxy.NewGosnmpRequester(mib.NewDataProvider(nil))
requester := snmpproxy.NewGosnmpRequester(snmpproxy.NewValueFormatter(mib.NewDataProvider(nil)))
result, err := requester.ExecuteRequest(apiRequest)
assert.Nil(result)
assert.EqualError(err, "no such instance: .1.3.5")
Expand All @@ -462,7 +417,7 @@ func TestGetWithMultipleOidsAndSnmpVersion1AndNoSuchInstanceError(t *testing.T)
apiRequest := apiRequest(get([]string{".1.3.5", "1.3.2"}))
apiRequest.Version = snmpproxy.SnmpVersion(gosnmp.Version1)

requester := snmpproxy.NewGosnmpRequester(mib.NewDataProvider(nil))
requester := snmpproxy.NewGosnmpRequester(snmpproxy.NewValueFormatter(mib.NewDataProvider(nil)))
result, err := requester.ExecuteRequest(apiRequest)
assert.Nil(result)
assert.EqualError(err, "no such instance: one of .1.3.5 1.3.2")
Expand All @@ -473,7 +428,7 @@ func TestGetNextWithEndOfMib(t *testing.T) {

apiRequest := apiRequest(getNext([]string{".1.15"}))

requester := snmpproxy.NewGosnmpRequester(mib.NewDataProvider(nil))
requester := snmpproxy.NewGosnmpRequester(snmpproxy.NewValueFormatter(mib.NewDataProvider(nil)))
result, err := requester.ExecuteRequest(apiRequest)
assert.Nil(result)
assert.EqualError(err, "end of mib: .1.15")
Expand All @@ -485,7 +440,7 @@ func TestGetNextWithSnmpVersion1AndEndOfMib(t *testing.T) {
apiRequest := apiRequest(getNext([]string{".1.15"}))
apiRequest.Version = snmpproxy.SnmpVersion(gosnmp.Version1)

requester := snmpproxy.NewGosnmpRequester(mib.NewDataProvider(nil))
requester := snmpproxy.NewGosnmpRequester(snmpproxy.NewValueFormatter(mib.NewDataProvider(nil)))
result, err := requester.ExecuteRequest(apiRequest)
assert.Nil(result)
assert.EqualError(err, "end of mib: .1.15")
Expand All @@ -497,7 +452,7 @@ func TestGetWithSnmpError(t *testing.T) {
apiRequest := apiRequest(get([]string{".1.1"}))
apiRequest.Host = "localhost"

requester := snmpproxy.NewGosnmpRequester(mib.NewDataProvider(nil))
requester := snmpproxy.NewGosnmpRequester(snmpproxy.NewValueFormatter(mib.NewDataProvider(nil)))
result, err := requester.ExecuteRequest(apiRequest)
assert.Nil(result)
assert.Error(err)
Expand All @@ -510,7 +465,7 @@ func TestWalkWithSnmpError(t *testing.T) {
apiRequest := apiRequest(walk(""))
apiRequest.Host = "localhost"

requester := snmpproxy.NewGosnmpRequester(mib.NewDataProvider(nil))
requester := snmpproxy.NewGosnmpRequester(snmpproxy.NewValueFormatter(mib.NewDataProvider(nil)))
result, err := requester.ExecuteRequest(apiRequest)
assert.Nil(result)
assert.Error(err)
Expand Down Expand Up @@ -547,7 +502,7 @@ func TestInvalidHost(t *testing.T) {
apiRequest := apiRequest(get([]string{}), walk(""))
apiRequest.Host = test.host

requester := snmpproxy.NewGosnmpRequester(mib.NewDataProvider(nil))
requester := snmpproxy.NewGosnmpRequester(snmpproxy.NewValueFormatter(mib.NewDataProvider(nil)))
result, err := requester.ExecuteRequest(apiRequest)
assert.Nil(result)
assert.Error(err)
Expand Down
Loading

0 comments on commit 654afb4

Please sign in to comment.