Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial SNMPv3 Support #30

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ The configuration file defines common parameters in a JSON format.
```
{
"snmp" : [
{ "community":"SNMPv3Example", "ver":3 , "v3Username":"testuser", "v3AuthKey":"abc12345678", "v3PrivKey":"12345678abc", "v3AuthProtocol":"SHA", "v3PrivProtocol":"AES128"},
{ "community":"private", "ver":2 },
{ "community":"public", "ver":2 }
],
Expand Down Expand Up @@ -220,6 +221,17 @@ The configuration file defines common parameters in a JSON format.
| `discover` | Defines a Cisco-style ACL. See the `Network Discovery` section. |
| `diagram` | Defines values used by the diagram module. Detailed below in the *Diagram block* table. |

### SNMPv3 Options
| Option | Description |
| --- | --- |
| `community` | Name of SNMPv3 credentials. It doesn't affect the SNMPv3 fucntionality but is used as a reference in case an error occurs. |
| `ver` | For SNMPv3, always set to 3. |
| `v3Username` | SNMPv3 Username |
| `v3AuthKey` | (optional) Authentication Key - *Required if v3AuthProtocol is defined.* |
| `v3PrivKey` | (optional) Privacy Key - *Required if v3PrivProtocol is defined.* |
| `v3AuthProtocol` | (optional) Authentication Protocol - MD5, SHA |
| `v3PrivProtocol` | (optional) Privacy Protocol - DES, 3DES, AES128, AES192, AES256 |

### Diagram block
| Variable | Type | Default Value | Description |
| --- | --- | --- | --- |
Expand Down
49 changes: 25 additions & 24 deletions natlas.conf
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
{
"snmp" : [
{ "community":"private", "ver":2 },
{ "community":"public", "ver":2 }
],
"domains" : [
".company.net",
".company.com"
],
"discover" : [
"permit ip 10.0.0.0/8",
"permit ip 192.168.1.0/24",
"permit ip 0.0.0.0/32"
],
"diagram" : {
"node_text_size" : 10,
"link_text_size" : 9,
"title_text_size" : 15,
"get_stack_members" : 0,
"get_vss_members" : 0,
"expand_stackwise" : 0,
"expand_vss" : 1,
"expand_lag" : 1,
"group_vpc" : 0
}
"snmp" : [
{ "community":"private", "ver":2 },
{ "community":"public", "ver":2 },
{ "community":"SNMPv3Example", "ver":3 , "v3Username":"myUsername", "v3AuthKey":"abc12345678", "v3PrivKey":"12345678abc", "v3AuthProtocol":"SHA", "v3PrivProtocol":"AES128"}
],
"domains" : [
".company.net",
".company.com"
],
"discover" : [
"permit ip 10.0.0.0/8",
"permit ip 192.168.1.0/24",
"permit ip 0.0.0.0/32"
],
"diagram" : {
"node_text_size" : 10,
"link_text_size" : 9,
"title_text_size" : 15,
"get_stack_members" : 0,
"get_vss_members" : 0,
"expand_stackwise" : 0,
"expand_vss" : 0,
"expand_lag" : 1,
"group_vpc" : 0
}
}
31 changes: 28 additions & 3 deletions natlas/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,8 @@ def generate_new(self):
return '{\n' \
' "snmp" : [\n' \
' { "community":"private", "ver":2 },\n' \
' { "community":"public", "ver":2 }\n' \
' { "community":"public", "ver":2 },\n' \
' { "community":"SNMPv3Example", "ver":3 , "v3Username":"myUsername", "v3AuthKey":"abc12345678", "v3PrivKey":"12345678abc", "v3AuthProtocol":"SHA", "v3PrivProtocol":"AES128"}\n' \
' ],\n' \
' "domains" : [\n' \
' ".company.net",\n' \
Expand Down Expand Up @@ -216,9 +217,33 @@ def __validate_config_snmp(self, data):
print('version is not an int')
return 0
else:
if (c != 2):
print('version for \'%s\' is not supported' % cred['community'])
#Make sure we're using a supported SNMP version
if (c != 2) and (c != 3):
print('version \'%s\' for \'%s\' is not supported' % (cred['ver'], cred['community']))
return 0
if (c == 3):
c = cred['v3Username']
if (type(c) != str):
print('v3Username \'%s\' for \'%s\' must be string.' % (cred['v3Username'], cred['community']))
return 0
if 'v3AuthProtocol' in cred:
c = cred['v3AuthProtocol']
if (c != 'MD5' and c != 'SHA'):
print('v3AuthProtocol for \'%s\' must be MD5 or SHA.' % (cred['community']))
return 0
c = cred['v3AuthKey']
if (type(c) != str):
print('v3AuthKey \'%s\' for \'%s\' must be string.' % (cred['v3AuthKey'], cred['community']))
return 0
if 'v3PrivProtocol' in cred:
c = cred['v3PrivProtocol']
if (c != 'DES' and c != '3DES' and c!= 'AES128' and c!='AES192' and c!='AES256'):
print('v3PrivProtocol for \'%s\' must be DES, 3DES, AES128, AES192 or AES256.' % (cred['community']))
return 0
c = cred['v3PrivKey']
if (type(c) != str):
print('v3PrivKey \'%s\' for \'%s\' must be string.' % (cred['v3PrivKey'], cred['community']))
return 0
except KeyError as e:
print('one or more entries does not include %s' % e)
return 0
Expand Down
149 changes: 115 additions & 34 deletions natlas/snmp.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,48 +137,119 @@ def __init__(self, ip='0.0.0.0'):
self.ver = 0
self.v2_community = None
self._ip = ip
self.v3Username = None
self.v3AuthKey = None
self.v3PrivKey = None
self.v3AuthProtocol = None
self.v3PrivProtocol = None

#
# Try to find valid SNMP credentials in the provided list.
# Returns 1 if success, 0 if failed.
#
def get_cred(self, snmp_creds):
for cred in snmp_creds:
# we don't currently support anything other than SNMPv2
if (cred['ver'] != 2):
continue

community = cred['community']

cmdGen = cmdgen.CommandGenerator()
errIndication, errStatus, errIndex, varBinds = cmdGen.getCmd(
cmdgen.CommunityData(community),
cmdgen.UdpTransportTarget((self._ip, SNMP_PORT)),
'1.3.6.1.2.1.1.5.0',
lookupNames = False, lookupValues = False
)
if errIndication:
continue
else:
self.ver = 2
self.success = 1
self.v2_community = community

return 1

return 0


#SNMPv2
if (cred['ver'] == 2):
community = cred['community']

errIndication, errStatus, errIndex, varBinds = cmdGen.getCmd(
cmdgen.CommunityData(community),
cmdgen.UdpTransportTarget((self._ip, SNMP_PORT)),
'1.3.6.1.2.1.1.5.0',
lookupNames = False, lookupValues = False
)

if errIndication:
continue
else:
self.ver = 2
self.success = 1
self.v2_community = community
self.v3Username = None
self.v3AuthKey = None
self.v3PrivKey = None
self.v3AuthProtocol = None
self.v3PrivProtocol = None

return 1

#SNMPv3
if (cred['ver'] == 3):
community = cred['community']

v3Username = cred['v3Username']

v3AuthProtocol = cmdgen.usmNoAuthProtocol
if 'v3AuthProtocol' in cred:
if cred['v3AuthProtocol'] == 'MD5':
v3AuthProtocol = cmdgen.usmHMACMD5AuthProtocol
if cred['v3AuthProtocol'] == 'SHA':
v3AuthProtocol = cmdgen.usmHMACSHAAuthProtocol

v3PrivProtocol = cmdgen.usmNoPrivProtocol
if 'v3PrivProtocol' in cred:
if cred['v3PrivProtocol'] == 'DES':
v3PrivProtocol = cmdgen.usmDESPrivProtocol
if cred['v3PrivProtocol'] == '3DES':
v3PrivProtocol = cmdgen.usm3DESEDEPrivProtocol
if cred['v3PrivProtocol'] == 'AES128':
v3PrivProtocol = cmdgen.usmAesCfb128Protocol
if cred['v3PrivProtocol'] == 'AES192':
v3PrivProtocol = cmdgen.usmAesCfb192Protocol
if cred['v3PrivProtocol'] == 'AES256':
v3PrivProtocol = cmdgen.usmAesCfb256Protocol

v3AuthKey = None
v3PrivKey = None

if (v3AuthProtocol != cmdgen.usmNoAuthProtocol):
v3AuthKey = cred['v3AuthKey']
if (v3PrivProtocol != cmdgen.usmNoPrivProtocol):
v3PrivKey = cred['v3PrivKey']

errIndication, errStatus, errIndex, varBinds = cmdGen.getCmd(
cmdgen.UsmUserData(v3Username, v3AuthKey, v3PrivKey, v3AuthProtocol, v3PrivProtocol),
cmdgen.UdpTransportTarget((self._ip, SNMP_PORT)),
'1.3.6.1.2.1.1.5.0',
lookupNames = False, lookupValues = False
)

if errIndication:
continue
else:
self.ver = 3
self.success = 1
self.v2_community = community
self.v3Username = v3Username
self.v3AuthProtocol = v3AuthProtocol
self.v3PrivProtocol = v3PrivProtocol
self.v3AuthKey = v3AuthKey
self.v3PrivKey = v3PrivKey

return 1

return 0
#
# Get single SNMP value at OID.
#
def get_val(self, oid):
cmdGen = cmdgen.CommandGenerator()
errIndication, errStatus, errIndex, varBinds = cmdGen.getCmd(
cmdgen.CommunityData(self.v2_community),
if self.ver == 2:
errIndication, errStatus, errIndex, varBinds = cmdGen.getCmd(
cmdgen.CommunityData(self.v2_community),
cmdgen.UdpTransportTarget((self._ip, SNMP_PORT), retries=2),
oid, lookupNames = False, lookupValues = False
)
if self.ver == 3:
errIndication, errStatus, errIndex, varBinds = cmdGen.getCmd(
cmdgen.UsmUserData(self.v3Username, self.v3AuthKey, self.v3PrivKey, self.v3AuthProtocol, self.v3PrivProtocol),
cmdgen.UdpTransportTarget((self._ip, SNMP_PORT), retries=2),
oid, lookupNames = False, lookupValues = False
)

)
if errIndication:
print('[E] get_snmp_val(%s): %s' % (self.v2_community, errIndication))
else:
Expand All @@ -197,14 +268,24 @@ def get_val(self, oid):
#
def get_bulk(self, oid):
cmdGen = cmdgen.CommandGenerator()
errIndication, errStatus, errIndex, varBindTable = cmdGen.bulkCmd(
cmdgen.CommunityData(self.v2_community),
cmdgen.UdpTransportTarget((self._ip, SNMP_PORT), timeout=30, retries=2),
0, 50,
oid,
lookupNames = False, lookupValues = False
)
if self.ver == 2:
errIndication, errStatus, errIndex, varBindTable = cmdGen.bulkCmd(
cmdgen.CommunityData(self.v2_community),
cmdgen.UdpTransportTarget((self._ip, SNMP_PORT), timeout=30, retries=2),
0, 50,
oid,
lookupNames = False, lookupValues = False
)

if self.ver == 3:
errIndication, errStatus, errIndex, varBindTable = cmdGen.bulkCmd(
cmdgen.UsmUserData(self.v3Username, self.v3AuthKey, self.v3PrivKey, self.v3AuthProtocol, self.v3PrivProtocol),
cmdgen.UdpTransportTarget((self._ip, SNMP_PORT), timeout=30, retries=2),
0, 50,
oid,
lookupNames = False, lookupValues = False
)

if errIndication:
print('[E] get_snmp_bulk(%s): %s' % (self.v2_community, errIndication))
else:
Expand Down