the SnmpV3MessageProcessingModel.getPeerEngineInfo() method is implemented

to communicate discovered peer SNMP engine information to SNMP apps what
can be used for fine usmUserTable configuration.
pull/45/head
elie 2013-05-25 09:43:28 +00:00
parent bdaef1032a
commit a0894c9008
3 changed files with 111 additions and 28 deletions

View File

@ -42,6 +42,9 @@ Revision 4.2.5rc2
renamed into securityEngineId as it's semantically correct
- Oneliner transport target classes now support the getTransportInfo()
method that returns network addresses used on protocol level.
- The SnmpV3MessageProcessingModel.getPeerEngineInfo() method is implemented
to communicate discovered peer SNMP engine information to SNMP apps what
can be used for fine usmUserTable configuration.
- AsynNotificationOriginator.cfgCmdGen() does not take into account
securityModel & securityLevel when reducing LCD access via addTrapUser().
This improves LCD consistency on sparse add/del operatons but also

View File

@ -0,0 +1,71 @@
#
# Command Generator
#
# Send SNMP GET request using the following scenario and options:
#
# * try to communicate with a SNMPv3 Engine using:
# ** a non-existing user
# ** over IPv4/UDP
# ** to an Agent at demo.snmplabs.com:161
# * if remote SNMP Engine ID is discovered, send SNMP GET request:
# ** with SNMPv3, user 'usr-md5-none', MD5 authentication, no privacy
# at discovered securityEngineId
# ** to the same SNMP Engine ID
# ** for an OID in text form
#
from pysnmp.entity import engine
from pysnmp.entity.rfc3413.oneliner import cmdgen
snmpEngine = engine.SnmpEngine()
cmdGen = cmdgen.CommandGenerator(snmpEngine)
transportTarget = cmdgen.UdpTransportTarget(('demo.snmplabs.com', 161))
#
# Discover remote SNMP EngineID
#
authData = cmdgen.UsmUserData('non-existing-user')
errorIndication, errorStatus, errorIndex, varBinds = cmdGen.getCmd(
authData, transportTarget
)
# Check for errors and print out results
if errorIndication == 'unknownUserName':
snmpV3MessageProcessor = snmpEngine.messageProcessingSubsystems[3]
securityEngineId, contextEngineId, contextName = snmpV3MessageProcessor.getPeerEngineInfo(*transportTarget.getTransportInfo())
if securityEngineId:
print('securityEngineId = %s' % securityEngineId.prettyPrint())
else:
print('Peer EngineID not available')
raise Exception()
else:
print('Can\'t discover peer EngineID', errorIndication)
raise Exception()
#
# Query remote SNMP Engine using usmUserTable entry configured for it
#
authData = cmdgen.UsmUserData('usr-md5-none', 'authkey1',
securityEngineId=securityEngineId)
errorIndication, errorStatus, errorIndex, varBinds = cmdGen.getCmd(
authData, transportTarget, '1.3.6.1.2.1.1.1.0'
)
# Check for errors and print out results
if errorIndication:
print(errorIndication)
else:
if errorStatus:
print('%s at %s' % (
errorStatus.prettyPrint(),
errorIndex and varBinds[int(errorIndex)-1][0] or '?'
)
)
else:
for name, val in varBinds:
print('%s = %s' % (name.prettyPrint(), val.prettyPrint()))

View File

@ -67,10 +67,19 @@ class SnmpV3MessageProcessingModel(AbstractMessageProcessingModel):
def __init__(self):
AbstractMessageProcessingModel.__init__(self)
self.__scopedPDU = ScopedPDU()
self.__engineIDs = {}
self.__engineIDsExpQueue = {}
self.__engineIdCache = {}
self.__engineIdCacheExpQueue = {}
self.__expirationTimer = 0
def getPeerEngineInfo(self, transportDomain, transportAddress):
k = transportDomain, transportAddress
if k in self.__engineIdCache:
return self.__engineIdCache[k]['securityEngineId'], \
self.__engineIdCache[k]['contextEngineId'], \
self.__engineIdCache[k]['contextName']
else:
return None, None, None
# 7.1.1a
def prepareOutgoingMessage(
self,
@ -97,8 +106,8 @@ class SnmpV3MessageProcessingModel(AbstractMessageProcessingModel):
debug.logger & debug.flagMP and debug.logger('prepareOutgoingMessage: new msgID %s' % msgID)
k = (transportDomain, transportAddress)
if k in self.__engineIDs:
peerSnmpEngineData = self.__engineIDs[k]
if k in self.__engineIdCache:
peerSnmpEngineData = self.__engineIdCache[k]
else:
peerSnmpEngineData = None
@ -110,11 +119,11 @@ class SnmpV3MessageProcessingModel(AbstractMessageProcessingModel):
contextEngineId = snmpEngineID
else:
contextEngineId = peerSnmpEngineData['contextEngineId']
# Defaulting contextEngineID to securityEngineID should
# Defaulting contextEngineID to securityEngineId should
# probably be done on Agent side (see 7.1.3.d.2,) so this
# is a sort of workaround.
if not contextEngineId:
contextEngineId = peerSnmpEngineData['securityEngineID']
contextEngineId = peerSnmpEngineData['securityEngineId']
# 7.1.5
if not contextName:
contextName = self._emptyStr
@ -185,11 +194,11 @@ class SnmpV3MessageProcessingModel(AbstractMessageProcessingModel):
# 7.1.9.a
if pdu.tagSet in rfc3411.unconfirmedClassPDUs:
securityEngineID = snmpEngineID
securityEngineId = snmpEngineID
else:
if peerSnmpEngineData is None:
# Force engineID discovery (rfc3414, 4)
securityEngineID = securityName = self._emptyStr
securityEngineId = securityName = self._emptyStr
securityLevel = 1
# Clear possible auth&priv flags
headerData.setComponentByPosition(
@ -212,9 +221,9 @@ class SnmpV3MessageProcessingModel(AbstractMessageProcessingModel):
)
debug.logger & debug.flagMP and debug.logger('prepareOutgoingMessage: force engineID discovery')
else:
securityEngineID = peerSnmpEngineData['securityEngineID']
securityEngineId = peerSnmpEngineData['securityEngineId']
debug.logger & debug.flagMP and debug.logger('prepareOutgoingMessage: securityModel %r, securityEngineID %r, securityName %r, securityLevel %r' % (securityModel, securityEngineID, securityName, securityLevel))
debug.logger & debug.flagMP and debug.logger('prepareOutgoingMessage: securityModel %r, securityEngineId %r, securityName %r, securityLevel %r' % (securityModel, securityEngineId, securityName, securityLevel))
# 7.1.9.b
( securityParameters,
@ -224,7 +233,7 @@ class SnmpV3MessageProcessingModel(AbstractMessageProcessingModel):
msg,
snmpEngineMaxMessageSize.syntax,
securityModel,
securityEngineID,
securityEngineId,
securityName,
securityLevel,
scopedPDU
@ -417,7 +426,7 @@ class SnmpV3MessageProcessingModel(AbstractMessageProcessingModel):
errorIndication = errind.unsupportedSecurityModel
)
debug.logger & debug.flagMP and debug.logger('prepareResponseMessage: securityModel %r, securityEngineID %r, securityName %r, securityLevel %r' % (securityModel, snmpEngineID, securityName, securityLevel))
debug.logger & debug.flagMP and debug.logger('prepareResponseMessage: securityModel %r, securityEngineId %r, securityName %r, securityLevel %r' % (securityModel, snmpEngineID, securityName, securityLevel))
# 7.1.8a
try:
@ -511,7 +520,7 @@ class SnmpV3MessageProcessingModel(AbstractMessageProcessingModel):
# 7.2.6
smHandler = snmpEngine.securityModels[securityModel]
try:
( securityEngineID,
( securityEngineId,
securityName,
scopedPDU,
maxSizeResponseScopedPDU,
@ -598,26 +607,26 @@ class SnmpV3MessageProcessingModel(AbstractMessageProcessingModel):
# (seems to be irrelevant on Py3 but just in case)
del origTraceback
else:
# Sniff for engineIDs
# Sniff for engineIdCache
k = (transportDomain, transportAddress)
if k not in self.__engineIDs:
if k not in self.__engineIdCache:
contextEngineId, contextName, pdus = scopedPDU
pdu = pdus.getComponent()
# Here we assume that authentic/default EngineIDs
# come only in the course of engine-to-engine communication.
if pdu.tagSet in rfc3411.internalClassPDUs:
self.__engineIDs[k] = {
'securityEngineID': securityEngineID,
self.__engineIdCache[k] = {
'securityEngineId': securityEngineId,
'contextEngineId': contextEngineId,
'contextName': contextName
}
}
expireAt = int(self.__expirationTimer + 300 / snmpEngine.transportDispatcher.getTimerResolution())
if expireAt not in self.__engineIDsExpQueue:
self.__engineIDsExpQueue[expireAt] = []
self.__engineIDsExpQueue[expireAt].append(k)
if expireAt not in self.__engineIdCacheExpQueue:
self.__engineIdCacheExpQueue[expireAt] = []
self.__engineIdCacheExpQueue[expireAt].append(k)
debug.logger & debug.flagMP and debug.logger('prepareDataElements: cache securityEngineID %r for %r %r' % (securityEngineID, transportDomain, transportAddress))
debug.logger & debug.flagMP and debug.logger('prepareDataElements: cache securityEngineId %r for %r %r' % (securityEngineId, transportDomain, transportAddress))
snmpEngineID, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('__SNMP-FRAMEWORK-MIB', 'snmpEngineID')
snmpEngineID = snmpEngineID.syntax
@ -714,7 +723,7 @@ class SnmpV3MessageProcessingModel(AbstractMessageProcessingModel):
# 7.2.13
if pduType in rfc3411.confirmedClassPDUs:
# 7.2.13a
if securityEngineID != snmpEngineID:
if securityEngineId != snmpEngineID:
smHandler.releaseStateInformation(securityStateReference)
raise error.StatusInformation(
errorIndication = errind.engineIDMismatch
@ -784,11 +793,11 @@ class SnmpV3MessageProcessingModel(AbstractMessageProcessingModel):
)
def __expireEnginesInfo(self):
if self.__expirationTimer in self.__engineIDsExpQueue:
for engineKey in self.__engineIDsExpQueue[self.__expirationTimer]:
del self.__engineIDs[engineKey]
if self.__expirationTimer in self.__engineIdCacheExpQueue:
for engineKey in self.__engineIdCacheExpQueue[self.__expirationTimer]:
del self.__engineIdCache[engineKey]
debug.logger & debug.flagMP and debug.logger('__expireEnginesInfo: expiring %r' % (engineKey,))
del self.__engineIDsExpQueue[self.__expirationTimer]
del self.__engineIdCacheExpQueue[self.__expirationTimer]
self.__expirationTimer = self.__expirationTimer + 1
def receiveTimerTick(self, snmpEngine, timeNow):