Skip to content
This repository has been archived by the owner on Sep 19, 2023. It is now read-only.

Commit

Permalink
Merge pull request #84 from GuessWhoSamFoo/vio-2316
Browse files Browse the repository at this point in the history
Determine MsgFlag based on auth and priv config
  • Loading branch information
GuessWhoSamFoo authored May 18, 2022
2 parents be31ed2 + 813acda commit 40f5f8b
Show file tree
Hide file tree
Showing 16 changed files with 182 additions and 84 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,5 @@ synse-snmp-plugin
dist/

#Local config for debugging real devices
config.yaml
config.yml

29 changes: 0 additions & 29 deletions config.yml

This file was deleted.

17 changes: 9 additions & 8 deletions emulator/ups/pxgms_ups/test_snmp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@
# would have its own emulator as well.
#
# This emulator runs on port 1024.
snmp-emulator-pxgms-ups:
container_name: snmp-emulator-pxgms-ups
build: .
dockerfile: Dockerfile
# This command will override what is in the dockerfile.
command: ./start_snmp_emulator.sh ./data 1024 snmp-emulator-pxgms-ups.log V3 authPriv SHA AES
ports:
- 1024:1024/udp
version: "3"
services:
snmp-emulator-pxgms-ups:
container_name: snmp-emulator-pxgms-ups
build: .
# This command will override what is in the dockerfile.
command: ./start_snmp_emulator.sh ./data 1024 snmp-emulator-pxgms-ups.log V3 authPriv SHA AES
ports:
- 1024:1024/udp
17 changes: 9 additions & 8 deletions emulator/ups/tripplite_ups/test_snmp.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
# This is the container running the Tripplite UPS SNMP emulator to test against.
# This emulator runs on port 1025.
snmp-emulator-tripplite-ups:
container_name: snmp-emulator-tripplite-ups
build: .
dockerfile: Dockerfile
# This command will override what is in the dockerfile.
command: ./start_snmp_emulator.sh ./data 1025 snmp-emulator-triplite-ups.log V3 authPriv MD5 DES
ports:
- 1025:1025/udp
version: "3"
services:
snmp-emulator-tripplite-ups:
container_name: snmp-emulator-tripplite-ups
build: .
# This command will override what is in the dockerfile.
command: ./start_snmp_emulator.sh ./data 1025 snmp-emulator-triplite-ups.log V3 authPriv MD5 DES
ports:
- 1025:1025/udp
1 change: 0 additions & 1 deletion pkg/devices/current_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ func TestCurrentNilReadingValue(t *testing.T) {
}
// Call SnmpCurrentRead.
readings, err := SnmpCurrentRead(&device)
// Verify we get a nil reading and no error.
assert.NoError(t, err)
assert.Len(t, readings, 1)
assert.Nil(t, readings[0].Value)
Expand Down
10 changes: 10 additions & 0 deletions pkg/devices/devices.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package devices

import (
"fmt"
"github.com/gosnmp/gosnmp"

"github.com/vapor-ware/synse-sdk/sdk"
"github.com/vapor-ware/synse-snmp-plugin/pkg/snmp/core"
Expand Down Expand Up @@ -39,6 +40,15 @@ func getRawReading(device *sdk.Device) (result core.ReadResult, err error) {
return result, err
}

if err := snmpConfig.CheckPrivacyAndAuthFromData(data); err != nil {
return result, err
}

if snmpConfig.Endpoint == "ups" {
// Set MsgFlag based on device type
snmpConfig.MsgFlag = gosnmp.AuthNoPriv
}

// Create SnmpClient.
snmpClient, err := core.NewSnmpClient(snmpConfig)
if err != nil {
Expand Down
3 changes: 3 additions & 0 deletions pkg/devices/devices_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package devices

import (
"github.com/gosnmp/gosnmp"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -83,6 +84,8 @@ func TestDevices(t *testing.T) { // nolint: gocyclo
)
assert.NoError(t, err)

snmpConfig.MsgFlag = gosnmp.AuthPriv

// Create a client.
client, err := core.NewSnmpClient(snmpConfig)
assert.NoError(t, err)
Expand Down
76 changes: 39 additions & 37 deletions pkg/snmp/core/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,12 @@ func NewSecurityParameters(

// For now, require authorization and privacy.
// Empty user/passwords are okay.
if !(authenticationProtocol == MD5 || authenticationProtocol == SHA) {
if !(authenticationProtocol == MD5 || authenticationProtocol == SHA || authenticationProtocol == NoAuthentication) {
return nil, fmt.Errorf("unsupported authentication protocol [%v]",
authenticationProtocol)
}

if !(privacyProtocol == DES || privacyProtocol == AES) {
if !(privacyProtocol == DES || privacyProtocol == AES || privacyProtocol == NoPrivacy) {
return nil, fmt.Errorf("unsupported privacy protocol [%v]",
privacyProtocol)
}
Expand All @@ -74,14 +74,15 @@ func NewSecurityParameters(
// DeviceConfig is a thin wrapper around the configuration for gosnmp using SNMP V3.
// Tags are included here to expose on a Synse scan.
type DeviceConfig struct {
Version string // SNMP protocol version. Currently only SNMP V3 is supported.
Endpoint string // Endpoint of the SNMP server to connect to.
ContextName string // Context name for SNMP V3 messages.
Timeout time.Duration // Timeout for the SNMP query.
Retries int // The number of retries on the connection.
SecurityParameters *SecurityParameters // SNMP V3 security parameters.
Port uint16 // UDP port to connect to.
Tags []string // List of synse device tags.
Version string // SNMP protocol version. Currently only SNMP V3 is supported.
Endpoint string // Endpoint of the SNMP server to connect to.
ContextName string // Context name for SNMP V3 messages.
Timeout time.Duration // Timeout for the SNMP query.
Retries int // The number of retries on the connection.
SecurityParameters *SecurityParameters // SNMP V3 security parameters.
Port uint16 // UDP port to connect to.
Tags []string // List of synse device tags.
MsgFlag gosnmp.SnmpV3MsgFlags // Security level
}

// checkForEmptyString checks for an empty string variable and fails with an
Expand Down Expand Up @@ -113,8 +114,7 @@ func NewDeviceConfig(
}

// Check strings for emptyness. Version is already checked.
err := checkForEmptyString(endpoint, "endpoint")
if err != nil {
if err := checkForEmptyString(endpoint, "endpoint"); err != nil {
return nil, err
}

Expand Down Expand Up @@ -205,6 +205,8 @@ func GetDeviceConfig(instanceData map[string]interface{}) (*DeviceConfig, error)
authenticationProtocol = MD5
case "SHA":
authenticationProtocol = SHA
case "None":
authenticationProtocol = NoAuthentication
default:
return nil, fmt.Errorf("unsupported authentication protocol [%v]", authProtocolString)
}
Expand All @@ -216,6 +218,8 @@ func GetDeviceConfig(instanceData map[string]interface{}) (*DeviceConfig, error)
privacyProtocol = DES
case "AES":
privacyProtocol = AES
case "NONE":
privacyProtocol = NoPrivacy
default:
return nil, fmt.Errorf("unsupported privacy protocol [%v]", privProtocolString)
}
Expand Down Expand Up @@ -248,20 +252,20 @@ func GetDeviceConfig(instanceData map[string]interface{}) (*DeviceConfig, error)
}

// ToMap serializes DeviceConfig to map[string]interface{}.
func (deviceConfig *DeviceConfig) ToMap() (m map[string]interface{}, err error) {
func (d *DeviceConfig) ToMap() (m map[string]interface{}, err error) {

if deviceConfig.SecurityParameters == nil {
if d.SecurityParameters == nil {
return nil, fmt.Errorf("no security parameters")
}

m = make(map[string]interface{})
m["version"] = deviceConfig.Version
m["endpoint"] = deviceConfig.Endpoint
m["port"] = deviceConfig.Port
m["contextName"] = deviceConfig.ContextName
m["deviceTags"] = deviceConfig.Tags
m["version"] = d.Version
m["endpoint"] = d.Endpoint
m["port"] = d.Port
m["contextName"] = d.ContextName
m["deviceTags"] = d.Tags

securityParameters := deviceConfig.SecurityParameters
securityParameters := d.SecurityParameters
m["userName"] = securityParameters.UserName
if securityParameters.AuthenticationProtocol == MD5 {
m["authenticationProtocol"] = "MD5"
Expand Down Expand Up @@ -308,17 +312,13 @@ func (client *SnmpClient) Get(oid string) (result ReadResult, err error) {
return result, err
}

oids := []string{oid}
snmpPacket, err := goSnmp.Get(oids)
err2 := goSnmp.Conn.Close() // Do not leak connection.

// Return first error.
snmpPacket, err := goSnmp.Get([]string{oid})
if err != nil {
return result, err
}
if err2 != nil {
return result, err2
}
defer func() {
err = goSnmp.Conn.Close()
}()

data := snmpPacket.Variables[0]

Expand All @@ -335,7 +335,7 @@ func (client *SnmpClient) Get(oid string) (result ReadResult, err error) {
return ReadResult{
Oid: data.Name,
Data: data.Value,
}, nil
}, err
}

// Walk performs an SNMP bulk walk on the given OID.
Expand All @@ -347,15 +347,13 @@ func (client *SnmpClient) Walk(rootOid string) (results []ReadResult, err error)
}

resultSet, err := goSnmp.BulkWalkAll(rootOid)
err2 := goSnmp.Conn.Close() // Do not leak connection.

// Return first error.
if err != nil {
return nil, err
}
if err2 != nil {
return nil, err2
}

defer func() {
err = goSnmp.Conn.Close()
}()

// Package results.
for _, snmpPdu := range resultSet {
Expand All @@ -375,7 +373,7 @@ func (client *SnmpClient) Walk(rootOid string) (results []ReadResult, err error)
Data: snmpPdu.Value,
})
}
return results, nil
return results, err
}

// createGoSNMP is a helper to create gosnmp.GoSNMP from SnmpClient.
Expand All @@ -396,6 +394,8 @@ func (client *SnmpClient) createGoSNMP() (*gosnmp.GoSNMP, error) {
authProtocol = gosnmp.MD5
} else if securityParameters.AuthenticationProtocol == SHA {
authProtocol = gosnmp.SHA
} else if securityParameters.AuthenticationProtocol == NoAuthentication {
authProtocol = gosnmp.NoAuth
} else {
return nil, fmt.Errorf("unsupported authentication protocol [%v]", securityParameters.AuthenticationProtocol)
}
Expand All @@ -404,6 +404,8 @@ func (client *SnmpClient) createGoSNMP() (*gosnmp.GoSNMP, error) {
privProtocol = gosnmp.DES
} else if securityParameters.PrivacyProtocol == AES {
privProtocol = gosnmp.AES
} else if securityParameters.PrivacyProtocol == NoPrivacy {
privProtocol = gosnmp.NoPriv
} else {
return nil, fmt.Errorf("unsupported privacy protocol [%v]", securityParameters.PrivacyProtocol)
}
Expand All @@ -414,7 +416,7 @@ func (client *SnmpClient) createGoSNMP() (*gosnmp.GoSNMP, error) {
Version: gosnmp.Version3,
Timeout: client.DeviceConfig.Timeout,
SecurityModel: gosnmp.UserSecurityModel,
MsgFlags: gosnmp.AuthPriv,
MsgFlags: client.DeviceConfig.MsgFlag,
SecurityParameters: &gosnmp.UsmSecurityParameters{
UserName: client.DeviceConfig.SecurityParameters.UserName,
AuthenticationProtocol: authProtocol,
Expand Down
5 changes: 5 additions & 0 deletions pkg/snmp/core/client_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package core

import (
"github.com/gosnmp/gosnmp"
"testing"
"time"

Expand Down Expand Up @@ -31,6 +32,8 @@ func TestClientPxgmsUps(t *testing.T) {
)
assert.NoError(t, err)

config.MsgFlag = gosnmp.AuthPriv

// Create a client.
client, err := NewSnmpClient(config)
assert.NoError(t, err)
Expand Down Expand Up @@ -328,6 +331,8 @@ func TestClientTrippliteUps(t *testing.T) {
)
assert.NoError(t, err)

config.MsgFlag = gosnmp.AuthPriv

// Create a client.
client, err := NewSnmpClient(config)
assert.NoError(t, err)
Expand Down
29 changes: 29 additions & 0 deletions pkg/snmp/core/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package core

import (
"fmt"
"github.com/gosnmp/gosnmp"
)

// CheckPrivacyAndAuthFromData determines a MsgFlag based on parsed data
func (d *DeviceConfig) CheckPrivacyAndAuthFromData(data map[string]interface{}) error {
auth, ok := data["authenticationProtocol"].(string)
if !ok {
return fmt.Errorf("cannot find authenticationProtocol")
}
priv, ok := data["privacyProtocol"].(string)
if !ok {
return fmt.Errorf("cannot find privacyProtocol")
}

switch {
case auth == "None" && priv == "None":
d.MsgFlag = gosnmp.NoAuthNoPriv
case auth != "None" && priv == "None":
d.MsgFlag = gosnmp.AuthNoPriv
default:
d.MsgFlag = gosnmp.AuthPriv
}

return nil
}
Loading

0 comments on commit 40f5f8b

Please sign in to comment.