832 lines
35 KiB
Python
832 lines
35 KiB
Python
#
|
|
# This file is part of pysnmp software.
|
|
#
|
|
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
|
|
# License: http://pysnmp.sf.net/license.html
|
|
#
|
|
import sys
|
|
from pysnmp.proto.mpmod.base import AbstractMessageProcessingModel
|
|
from pysnmp.proto import rfc1905, rfc3411, api, errind, error
|
|
from pyasn1.type import univ, namedtype, constraint
|
|
from pyasn1.codec.ber import decoder, eoo
|
|
from pyasn1.error import PyAsn1Error
|
|
from pysnmp import debug
|
|
|
|
# API to rfc1905 protocol objects
|
|
pMod = api.protoModules[api.protoVersion2c]
|
|
|
|
|
|
# SNMPv3 message format
|
|
|
|
class ScopedPDU(univ.Sequence):
|
|
componentType = namedtype.NamedTypes(
|
|
namedtype.NamedType('contextEngineId', univ.OctetString()),
|
|
namedtype.NamedType('contextName', univ.OctetString()),
|
|
namedtype.NamedType('data', rfc1905.PDUs())
|
|
)
|
|
|
|
|
|
class ScopedPduData(univ.Choice):
|
|
componentType = namedtype.NamedTypes(
|
|
namedtype.NamedType('plaintext', ScopedPDU()),
|
|
namedtype.NamedType('encryptedPDU', univ.OctetString()),
|
|
)
|
|
|
|
|
|
class HeaderData(univ.Sequence):
|
|
componentType = namedtype.NamedTypes(
|
|
namedtype.NamedType('msgID',
|
|
univ.Integer().subtype(subtypeSpec=constraint.ValueRangeConstraint(0, 2147483647))),
|
|
namedtype.NamedType('msgMaxSize',
|
|
univ.Integer().subtype(subtypeSpec=constraint.ValueRangeConstraint(484, 2147483647))),
|
|
namedtype.NamedType('msgFlags', univ.OctetString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, 1))),
|
|
namedtype.NamedType('msgSecurityModel',
|
|
univ.Integer().subtype(subtypeSpec=constraint.ValueRangeConstraint(1, 2147483647)))
|
|
)
|
|
|
|
|
|
class SNMPv3Message(univ.Sequence):
|
|
componentType = namedtype.NamedTypes(
|
|
namedtype.NamedType('msgVersion',
|
|
univ.Integer().subtype(subtypeSpec=constraint.ValueRangeConstraint(0, 2147483647))),
|
|
namedtype.NamedType('msgGlobalData', HeaderData()),
|
|
namedtype.NamedType('msgSecurityParameters', univ.OctetString()),
|
|
namedtype.NamedType('msgData', ScopedPduData())
|
|
)
|
|
|
|
|
|
# XXX move somewhere?
|
|
_snmpErrors = {(1, 3, 6, 1, 6, 3, 15, 1, 1, 1, 0): 'unsupportedSecLevel',
|
|
(1, 3, 6, 1, 6, 3, 15, 1, 1, 2, 0): 'notInTimeWindow',
|
|
(1, 3, 6, 1, 6, 3, 15, 1, 1, 3, 0): 'unknownUserName',
|
|
(1, 3, 6, 1, 6, 3, 15, 1, 1, 4, 0): 'unknownEngineID',
|
|
(1, 3, 6, 1, 6, 3, 15, 1, 1, 5, 0): 'wrongDigest',
|
|
(1, 3, 6, 1, 6, 3, 15, 1, 1, 6, 0): 'decryptionError'}
|
|
|
|
|
|
class SnmpV3MessageProcessingModel(AbstractMessageProcessingModel):
|
|
messageProcessingModelID = univ.Integer(3) # SNMPv3
|
|
snmpMsgSpec = SNMPv3Message
|
|
_emptyStr = univ.OctetString('')
|
|
_msgFlags = {0: univ.OctetString('\x00'),
|
|
1: univ.OctetString('\x01'),
|
|
3: univ.OctetString('\x03'),
|
|
4: univ.OctetString('\x04'),
|
|
5: univ.OctetString('\x05'),
|
|
7: univ.OctetString('\x07')}
|
|
|
|
def __init__(self):
|
|
AbstractMessageProcessingModel.__init__(self)
|
|
self.__scopedPDU = ScopedPDU()
|
|
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, snmpEngine, transportDomain,
|
|
transportAddress, messageProcessingModel,
|
|
securityModel, securityName, securityLevel,
|
|
contextEngineId, contextName, pduVersion,
|
|
pdu, expectResponse, sendPduHandle):
|
|
snmpEngineID, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('__SNMP-FRAMEWORK-MIB',
|
|
'snmpEngineID')
|
|
snmpEngineID = snmpEngineID.syntax
|
|
|
|
# 7.1.1b
|
|
msgID = self._cache.newMsgID()
|
|
|
|
debug.logger & debug.flagMP and debug.logger('prepareOutgoingMessage: new msgID %s' % msgID)
|
|
|
|
k = (transportDomain, transportAddress)
|
|
if k in self.__engineIdCache:
|
|
peerSnmpEngineData = self.__engineIdCache[k]
|
|
else:
|
|
peerSnmpEngineData = None
|
|
|
|
debug.logger & debug.flagMP and debug.logger(
|
|
'prepareOutgoingMessage: peer SNMP engine data %s for transport %s, address %s' % (
|
|
peerSnmpEngineData, transportDomain, transportAddress))
|
|
|
|
# 7.1.4
|
|
if contextEngineId is None:
|
|
if peerSnmpEngineData is None:
|
|
contextEngineId = snmpEngineID
|
|
else:
|
|
contextEngineId = peerSnmpEngineData['contextEngineId']
|
|
# 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']
|
|
# 7.1.5
|
|
if not contextName:
|
|
contextName = self._emptyStr
|
|
|
|
debug.logger & debug.flagMP and debug.logger(
|
|
'prepareOutgoingMessage: using contextEngineId %r, contextName %r' % (contextEngineId, contextName))
|
|
|
|
# 7.1.6
|
|
scopedPDU = self.__scopedPDU
|
|
scopedPDU.setComponentByPosition(0, contextEngineId)
|
|
scopedPDU.setComponentByPosition(1, contextName)
|
|
scopedPDU.setComponentByPosition(2)
|
|
scopedPDU.getComponentByPosition(2).setComponentByType(
|
|
pdu.tagSet, pdu, verifyConstraints=False
|
|
)
|
|
|
|
# 7.1.7
|
|
msg = self._snmpMsgSpec
|
|
|
|
# 7.1.7a
|
|
msg.setComponentByPosition(
|
|
0, self.messageProcessingModelID, verifyConstraints=False
|
|
)
|
|
headerData = msg.setComponentByPosition(1).getComponentByPosition(1)
|
|
|
|
# 7.1.7b
|
|
headerData.setComponentByPosition(0, msgID, verifyConstraints=False)
|
|
|
|
snmpEngineMaxMessageSize, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols(
|
|
'__SNMP-FRAMEWORK-MIB', 'snmpEngineMaxMessageSize')
|
|
|
|
# 7.1.7c
|
|
# XXX need to coerce MIB value as it has incompatible constraints set
|
|
headerData.setComponentByPosition(
|
|
1, snmpEngineMaxMessageSize.syntax, verifyConstraints=False
|
|
)
|
|
|
|
# 7.1.7d
|
|
msgFlags = 0
|
|
if securityLevel == 1:
|
|
pass
|
|
elif securityLevel == 2:
|
|
msgFlags |= 0x01
|
|
elif securityLevel == 3:
|
|
msgFlags |= 0x03
|
|
else:
|
|
raise error.ProtocolError(
|
|
'Unknown securityLevel %s' % securityLevel
|
|
)
|
|
|
|
if pdu.tagSet in rfc3411.confirmedClassPDUs:
|
|
msgFlags |= 0x04
|
|
|
|
headerData.setComponentByPosition(
|
|
2, self._msgFlags[msgFlags], verifyConstraints=False
|
|
)
|
|
|
|
# 7.1.7e
|
|
# XXX need to coerce MIB value as it has incompatible constraints set
|
|
headerData.setComponentByPosition(3, int(securityModel))
|
|
|
|
debug.logger & debug.flagMP and debug.logger('prepareOutgoingMessage: %s' % (msg.prettyPrint(),))
|
|
|
|
if securityModel in snmpEngine.securityModels:
|
|
smHandler = snmpEngine.securityModels[securityModel]
|
|
else:
|
|
raise error.StatusInformation(
|
|
errorIndication=errind.unsupportedSecurityModel
|
|
)
|
|
|
|
# 7.1.9.a
|
|
if pdu.tagSet in rfc3411.unconfirmedClassPDUs:
|
|
securityEngineId = snmpEngineID
|
|
else:
|
|
if peerSnmpEngineData is None:
|
|
# Force engineID discovery (rfc3414, 4)
|
|
securityEngineId = securityName = self._emptyStr
|
|
securityLevel = 1
|
|
# Clear possible auth&priv flags
|
|
headerData.setComponentByPosition(
|
|
2, self._msgFlags[msgFlags & 0xfc], verifyConstraints=False
|
|
)
|
|
# XXX
|
|
scopedPDU = self.__scopedPDU
|
|
scopedPDU.setComponentByPosition(
|
|
0, self._emptyStr, verifyConstraints=False
|
|
)
|
|
scopedPDU.setComponentByPosition(1, contextName)
|
|
scopedPDU.setComponentByPosition(2)
|
|
|
|
# Use dead-empty PDU for engine-discovery report
|
|
emptyPdu = pdu.clone()
|
|
pMod.apiPDU.setDefaults(emptyPdu)
|
|
|
|
scopedPDU.getComponentByPosition(2).setComponentByType(
|
|
emptyPdu.tagSet, emptyPdu, verifyConstraints=False
|
|
)
|
|
debug.logger & debug.flagMP and debug.logger('prepareOutgoingMessage: force engineID discovery')
|
|
else:
|
|
securityEngineId = peerSnmpEngineData['securityEngineId']
|
|
|
|
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,
|
|
wholeMsg) = smHandler.generateRequestMsg(
|
|
snmpEngine, self.messageProcessingModelID, msg,
|
|
snmpEngineMaxMessageSize.syntax, securityModel,
|
|
securityEngineId, securityName, securityLevel, scopedPDU
|
|
)
|
|
|
|
# Message size constraint verification
|
|
if len(wholeMsg) > snmpEngineMaxMessageSize.syntax:
|
|
raise error.StatusInformation(errorIndication=errind.tooBig)
|
|
|
|
# 7.1.9.c
|
|
if pdu.tagSet in rfc3411.confirmedClassPDUs:
|
|
# XXX rfc bug? why stateReference should be created?
|
|
self._cache.pushByMsgId(msgID, sendPduHandle=sendPduHandle,
|
|
msgID=msgID, snmpEngineID=snmpEngineID,
|
|
securityModel=securityModel,
|
|
securityName=securityName,
|
|
securityLevel=securityLevel,
|
|
contextEngineId=contextEngineId,
|
|
contextName=contextName,
|
|
transportDomain=transportDomain,
|
|
transportAddress=transportAddress)
|
|
|
|
snmpEngine.observer.storeExecutionContext(
|
|
snmpEngine, 'rfc3412.prepareOutgoingMessage',
|
|
dict(transportDomain=transportDomain,
|
|
transportAddress=transportAddress,
|
|
wholeMsg=wholeMsg,
|
|
securityModel=securityModel,
|
|
securityName=securityName,
|
|
securityLevel=securityLevel,
|
|
contextEngineId=contextEngineId,
|
|
contextName=contextName,
|
|
pdu=pdu)
|
|
)
|
|
snmpEngine.observer.clearExecutionContext(
|
|
snmpEngine, 'rfc3412.prepareOutgoingMessage'
|
|
)
|
|
|
|
return transportDomain, transportAddress, wholeMsg
|
|
|
|
def prepareResponseMessage(self, snmpEngine, messageProcessingModel,
|
|
securityModel, securityName, securityLevel,
|
|
contextEngineId, contextName, pduVersion,
|
|
pdu, maxSizeResponseScopedPDU, stateReference,
|
|
statusInformation):
|
|
snmpEngineID, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('__SNMP-FRAMEWORK-MIB',
|
|
'snmpEngineID')
|
|
snmpEngineID = snmpEngineID.syntax
|
|
|
|
# 7.1.2.b
|
|
cachedParams = self._cache.popByStateRef(stateReference)
|
|
msgID = cachedParams['msgID']
|
|
contextEngineId = cachedParams['contextEngineId']
|
|
contextName = cachedParams['contextName']
|
|
securityModel = cachedParams['securityModel']
|
|
securityName = cachedParams['securityName']
|
|
securityLevel = cachedParams['securityLevel']
|
|
securityStateReference = cachedParams['securityStateReference']
|
|
reportableFlag = cachedParams['reportableFlag']
|
|
maxMessageSize = cachedParams['msgMaxSize']
|
|
transportDomain = cachedParams['transportDomain']
|
|
transportAddress = cachedParams['transportAddress']
|
|
|
|
debug.logger & debug.flagMP and debug.logger('prepareResponseMessage: stateReference %s' % stateReference)
|
|
|
|
# 7.1.3
|
|
if statusInformation is not None and 'oid' in statusInformation:
|
|
# 7.1.3a
|
|
if pdu is None:
|
|
pduType = None
|
|
else:
|
|
requestID = pdu.getComponentByPosition(0)
|
|
pduType = pdu.tagSet
|
|
|
|
# 7.1.3b
|
|
if pdu is None and not reportableFlag or \
|
|
pduType is not None and \
|
|
pduType not in rfc3411.confirmedClassPDUs:
|
|
raise error.StatusInformation(
|
|
errorIndication=errind.loopTerminated
|
|
)
|
|
|
|
# 7.1.3c
|
|
reportPDU = rfc1905.ReportPDU()
|
|
pMod.apiPDU.setVarBinds(reportPDU, ((statusInformation['oid'], statusInformation['val']),))
|
|
pMod.apiPDU.setErrorStatus(reportPDU, 0)
|
|
pMod.apiPDU.setErrorIndex(reportPDU, 0)
|
|
if pdu is None:
|
|
pMod.apiPDU.setRequestID(reportPDU, 0)
|
|
else:
|
|
# noinspection PyUnboundLocalVariable
|
|
pMod.apiPDU.setRequestID(reportPDU, requestID)
|
|
|
|
# 7.1.3d.1
|
|
if 'securityLevel' in statusInformation:
|
|
securityLevel = statusInformation['securityLevel']
|
|
else:
|
|
securityLevel = 1
|
|
|
|
# 7.1.3d.2
|
|
if 'contextEngineId' in statusInformation:
|
|
contextEngineId = statusInformation['contextEngineId']
|
|
else:
|
|
contextEngineId = snmpEngineID
|
|
|
|
# 7.1.3d.3
|
|
if 'contextName' in statusInformation:
|
|
contextName = statusInformation['contextName']
|
|
else:
|
|
contextName = ""
|
|
|
|
# 7.1.3e
|
|
pdu = reportPDU
|
|
|
|
debug.logger & debug.flagMP and debug.logger(
|
|
'prepareResponseMessage: prepare report PDU for statusInformation %s' % statusInformation)
|
|
# 7.1.4
|
|
if not contextEngineId:
|
|
contextEngineId = snmpEngineID # XXX impl-dep manner
|
|
|
|
# 7.1.5
|
|
if not contextName:
|
|
contextName = self._emptyStr
|
|
|
|
debug.logger & debug.flagMP and debug.logger(
|
|
'prepareResponseMessage: using contextEngineId %r, contextName %r' % (contextEngineId, contextName))
|
|
|
|
# 7.1.6
|
|
scopedPDU = self.__scopedPDU
|
|
scopedPDU.setComponentByPosition(0, contextEngineId)
|
|
scopedPDU.setComponentByPosition(1, contextName)
|
|
scopedPDU.setComponentByPosition(2)
|
|
scopedPDU.getComponentByPosition(2).setComponentByType(
|
|
pdu.tagSet, pdu, verifyConstraints=False
|
|
)
|
|
|
|
# 7.1.7
|
|
msg = self._snmpMsgSpec
|
|
|
|
# 7.1.7a
|
|
msg.setComponentByPosition(0, self.messageProcessingModelID,
|
|
verifyConstraints=False)
|
|
|
|
headerData = msg.setComponentByPosition(1).getComponentByPosition(1)
|
|
|
|
# 7.1.7b
|
|
headerData.setComponentByPosition(0, msgID, verifyConstraints=False)
|
|
|
|
snmpEngineMaxMessageSize, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols(
|
|
'__SNMP-FRAMEWORK-MIB', 'snmpEngineMaxMessageSize')
|
|
|
|
# 7.1.7c
|
|
# XXX need to coerce MIB value as it has incompatible constraints set
|
|
headerData.setComponentByPosition(
|
|
1, snmpEngineMaxMessageSize.syntax, verifyConstraints=False
|
|
)
|
|
|
|
# 7.1.7d
|
|
msgFlags = 0
|
|
if securityLevel == 1:
|
|
pass
|
|
elif securityLevel == 2:
|
|
msgFlags |= 0x01
|
|
elif securityLevel == 3:
|
|
msgFlags |= 0x03
|
|
else:
|
|
raise error.ProtocolError('Unknown securityLevel %s' % securityLevel)
|
|
|
|
if pdu.tagSet in rfc3411.confirmedClassPDUs: # XXX not needed?
|
|
msgFlags |= 0x04
|
|
|
|
headerData.setComponentByPosition(
|
|
2, self._msgFlags[msgFlags], verifyConstraints=False
|
|
)
|
|
|
|
# 7.1.7e
|
|
headerData.setComponentByPosition(
|
|
3, securityModel, verifyConstraints=False
|
|
)
|
|
|
|
debug.logger & debug.flagMP and debug.logger('prepareResponseMessage: %s' % (msg.prettyPrint(),))
|
|
|
|
if securityModel in snmpEngine.securityModels:
|
|
smHandler = snmpEngine.securityModels[securityModel]
|
|
else:
|
|
raise error.StatusInformation(errorIndication=errind.unsupportedSecurityModel)
|
|
|
|
debug.logger & debug.flagMP and debug.logger(
|
|
'prepareResponseMessage: securityModel %r, securityEngineId %r, securityName %r, securityLevel %r' % (
|
|
securityModel, snmpEngineID, securityName, securityLevel))
|
|
|
|
# 7.1.8a
|
|
try:
|
|
(securityParameters,
|
|
wholeMsg) = smHandler.generateResponseMsg(
|
|
snmpEngine, self.messageProcessingModelID, msg,
|
|
snmpEngineMaxMessageSize.syntax, securityModel,
|
|
snmpEngineID, securityName, securityLevel, scopedPDU,
|
|
securityStateReference
|
|
)
|
|
except error.StatusInformation:
|
|
# 7.1.8.b
|
|
raise
|
|
|
|
debug.logger & debug.flagMP and debug.logger('prepareResponseMessage: SM finished')
|
|
|
|
# Message size constraint verification
|
|
if len(wholeMsg) > min(snmpEngineMaxMessageSize.syntax, maxMessageSize):
|
|
raise error.StatusInformation(errorIndication=errind.tooBig)
|
|
|
|
snmpEngine.observer.storeExecutionContext(
|
|
snmpEngine,
|
|
'rfc3412.prepareResponseMessage',
|
|
dict(transportDomain=transportDomain,
|
|
transportAddress=transportAddress,
|
|
securityModel=securityModel,
|
|
securityName=securityName,
|
|
securityLevel=securityLevel,
|
|
contextEngineId=contextEngineId,
|
|
contextName=contextName,
|
|
securityEngineId=snmpEngineID,
|
|
pdu=pdu)
|
|
)
|
|
snmpEngine.observer.clearExecutionContext(
|
|
snmpEngine, 'rfc3412.prepareResponseMessage'
|
|
)
|
|
|
|
return transportDomain, transportAddress, wholeMsg
|
|
|
|
# 7.2.1
|
|
|
|
def prepareDataElements(self, snmpEngine, transportDomain,
|
|
transportAddress, wholeMsg):
|
|
# 7.2.2
|
|
try:
|
|
msg, restOfwholeMsg = decoder.decode(wholeMsg, asn1Spec=self._snmpMsgSpec)
|
|
|
|
except PyAsn1Error:
|
|
debug.logger & debug.flagMP and debug.logger('prepareDataElements: %s' % (sys.exc_info()[1],))
|
|
snmpInASNParseErrs, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('__SNMPv2-MIB',
|
|
'snmpInASNParseErrs')
|
|
snmpInASNParseErrs.syntax += 1
|
|
raise error.StatusInformation(errorIndication=errind.parseError)
|
|
|
|
debug.logger & debug.flagMP and debug.logger('prepareDataElements: %s' % (msg.prettyPrint(),))
|
|
|
|
if eoo.endOfOctets.isSameTypeWith(msg):
|
|
raise error.StatusInformation(errorIndication=errind.parseError)
|
|
|
|
# 7.2.3
|
|
headerData = msg.getComponentByPosition(1)
|
|
msgVersion = messageProcessingModel = msg.getComponentByPosition(0)
|
|
msgID = headerData.getComponentByPosition(0)
|
|
msgFlags, = headerData.getComponentByPosition(2).asNumbers()
|
|
maxMessageSize = headerData.getComponentByPosition(1)
|
|
securityModel = headerData.getComponentByPosition(3)
|
|
securityParameters = msg.getComponentByPosition(2)
|
|
|
|
debug.logger & debug.flagMP and debug.logger(
|
|
'prepareDataElements: msg data msgVersion %s msgID %s securityModel %s' % (
|
|
msgVersion, msgID, securityModel))
|
|
|
|
# 7.2.4
|
|
if securityModel not in snmpEngine.securityModels:
|
|
snmpUnknownSecurityModels, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols(
|
|
'__SNMP-MPD-MIB', 'snmpUnknownSecurityModels')
|
|
snmpUnknownSecurityModels.syntax += 1
|
|
raise error.StatusInformation(errorIndication=errind.unsupportedSecurityModel)
|
|
|
|
# 7.2.5
|
|
if msgFlags & 0x03 == 0x00:
|
|
securityLevel = 1
|
|
elif (msgFlags & 0x03) == 0x01:
|
|
securityLevel = 2
|
|
elif (msgFlags & 0x03) == 0x03:
|
|
securityLevel = 3
|
|
else:
|
|
snmpInvalidMsgs = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('__SNMP-MPD-MIB',
|
|
'snmpInvalidMsgs')
|
|
snmpInvalidMsgs.syntax += 1
|
|
raise error.StatusInformation(errorIndication=errind.invalidMsg)
|
|
|
|
if msgFlags & 0x04:
|
|
reportableFlag = 1
|
|
else:
|
|
reportableFlag = 0
|
|
|
|
# 7.2.6
|
|
smHandler = snmpEngine.securityModels[securityModel]
|
|
try:
|
|
(securityEngineId, securityName,
|
|
scopedPDU, maxSizeResponseScopedPDU,
|
|
securityStateReference) = smHandler.processIncomingMsg(
|
|
snmpEngine, messageProcessingModel, maxMessageSize,
|
|
securityParameters, securityModel, securityLevel,
|
|
wholeMsg, msg
|
|
)
|
|
debug.logger & debug.flagMP and debug.logger('prepareDataElements: SM succeeded')
|
|
|
|
except error.StatusInformation:
|
|
statusInformation, origTraceback = sys.exc_info()[1:3]
|
|
debug.logger & debug.flagMP and debug.logger(
|
|
'prepareDataElements: SM failed, statusInformation %s' % statusInformation)
|
|
if 'errorIndication' in statusInformation:
|
|
# 7.2.6a
|
|
if 'oid' in statusInformation:
|
|
# 7.2.6a1
|
|
securityStateReference = statusInformation['securityStateReference']
|
|
contextEngineId = statusInformation['contextEngineId']
|
|
contextName = statusInformation['contextName']
|
|
if 'scopedPDU' in statusInformation:
|
|
scopedPDU = statusInformation['scopedPDU']
|
|
pdu = scopedPDU.getComponentByPosition(2).getComponent()
|
|
else:
|
|
pdu = None
|
|
maxSizeResponseScopedPDU = statusInformation['maxSizeResponseScopedPDU']
|
|
securityName = None # XXX secmod cache used
|
|
|
|
# 7.2.6a2
|
|
stateReference = self._cache.newStateReference()
|
|
self._cache.pushByStateRef(
|
|
stateReference, msgVersion=messageProcessingModel,
|
|
msgID=msgID, contextEngineId=contextEngineId,
|
|
contextName=contextName, securityModel=securityModel,
|
|
securityName=securityName, securityLevel=securityLevel,
|
|
securityStateReference=securityStateReference,
|
|
reportableFlag=reportableFlag,
|
|
msgMaxSize=maxMessageSize,
|
|
maxSizeResponseScopedPDU=maxSizeResponseScopedPDU,
|
|
transportDomain=transportDomain,
|
|
transportAddress=transportAddress
|
|
)
|
|
|
|
# 7.2.6a3
|
|
try:
|
|
snmpEngine.msgAndPduDsp.returnResponsePdu(
|
|
snmpEngine, 3, securityModel, securityName,
|
|
securityLevel, contextEngineId, contextName,
|
|
1, pdu, maxSizeResponseScopedPDU, stateReference,
|
|
statusInformation
|
|
)
|
|
except error.StatusInformation:
|
|
pass
|
|
|
|
debug.logger & debug.flagMP and debug.logger('prepareDataElements: error reported')
|
|
# 7.2.6b
|
|
if sys.version_info[0] <= 2:
|
|
raise statusInformation
|
|
else:
|
|
try:
|
|
raise statusInformation.with_traceback(origTraceback)
|
|
finally:
|
|
# Break cycle between locals and traceback object
|
|
# (seems to be irrelevant on Py3 but just in case)
|
|
del origTraceback
|
|
else:
|
|
# Sniff for engineIdCache
|
|
k = (transportDomain, transportAddress)
|
|
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.__engineIdCache[k] = {
|
|
'securityEngineId': securityEngineId,
|
|
'contextEngineId': contextEngineId,
|
|
'contextName': contextName
|
|
}
|
|
|
|
expireAt = int(self.__expirationTimer + 300 / snmpEngine.transportDispatcher.getTimerResolution())
|
|
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))
|
|
|
|
snmpEngineID, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('__SNMP-FRAMEWORK-MIB',
|
|
'snmpEngineID')
|
|
snmpEngineID = snmpEngineID.syntax
|
|
|
|
# 7.2.7 XXX PDU would be parsed here?
|
|
contextEngineId, contextName, pdu = scopedPDU
|
|
pdu = pdu.getComponent() # PDUs
|
|
|
|
# 7.2.8
|
|
pduVersion = api.protoVersion2c
|
|
|
|
# 7.2.9
|
|
pduType = pdu.tagSet
|
|
|
|
# 7.2.10
|
|
if pduType in rfc3411.responseClassPDUs or \
|
|
pduType in rfc3411.internalClassPDUs:
|
|
# 7.2.10a
|
|
try:
|
|
cachedReqParams = self._cache.popByMsgId(msgID)
|
|
except error.ProtocolError:
|
|
smHandler.releaseStateInformation(securityStateReference)
|
|
raise error.StatusInformation(
|
|
errorIndication=errind.dataMismatch
|
|
)
|
|
# 7.2.10b
|
|
sendPduHandle = cachedReqParams['sendPduHandle']
|
|
else:
|
|
sendPduHandle = None
|
|
|
|
debug.logger & debug.flagMP and debug.logger(
|
|
'prepareDataElements: using sendPduHandle %s for msgID %s' % (sendPduHandle, msgID))
|
|
|
|
# 7.2.11
|
|
if pduType in rfc3411.internalClassPDUs:
|
|
# 7.2.11a
|
|
varBinds = pMod.apiPDU.getVarBinds(pdu)
|
|
if varBinds:
|
|
statusInformation = error.StatusInformation(
|
|
errorIndication=_snmpErrors.get(varBinds[0][0],
|
|
'errorReportReceived'),
|
|
oid=varBinds[0][0], val=varBinds[0][1],
|
|
sendPduHandle=sendPduHandle
|
|
)
|
|
else:
|
|
statusInformation = error.StatusInformation(
|
|
sendPduHandle=sendPduHandle
|
|
)
|
|
|
|
# 7.2.11b (incomplete implementation)
|
|
|
|
snmpEngine.observer.storeExecutionContext(
|
|
snmpEngine, 'rfc3412.prepareDataElements:internal',
|
|
dict(transportDomain=transportDomain,
|
|
transportAddress=transportAddress,
|
|
securityModel=securityModel,
|
|
securityName=securityName,
|
|
securityLevel=securityLevel,
|
|
contextEngineId=contextEngineId,
|
|
contextName=contextName,
|
|
securityEngineId=securityEngineId,
|
|
pdu=pdu)
|
|
)
|
|
snmpEngine.observer.clearExecutionContext(
|
|
snmpEngine, 'rfc3412.prepareDataElements:internal'
|
|
)
|
|
|
|
# 7.2.11c
|
|
smHandler.releaseStateInformation(securityStateReference)
|
|
|
|
# 7.2.11d
|
|
# no-op
|
|
|
|
# 7.2.11e XXX may need to pass Reports up to app in some cases...
|
|
raise statusInformation
|
|
|
|
statusInformation = None # no errors ahead
|
|
|
|
# 7.2.12
|
|
if pduType in rfc3411.responseClassPDUs:
|
|
# 7.2.12a -> noop
|
|
|
|
# 7.2.12b
|
|
# noinspection PyUnboundLocalVariable
|
|
if securityModel != cachedReqParams['securityModel'] or \
|
|
securityName != cachedReqParams['securityName'] or \
|
|
securityLevel != cachedReqParams['securityLevel'] or \
|
|
contextEngineId != cachedReqParams['contextEngineId'] or \
|
|
contextName != cachedReqParams['contextName']:
|
|
smHandler.releaseStateInformation(securityStateReference)
|
|
raise error.StatusInformation(
|
|
errorIndication=errind.dataMismatch
|
|
)
|
|
|
|
snmpEngine.observer.storeExecutionContext(
|
|
snmpEngine, 'rfc3412.prepareDataElements:response',
|
|
dict(transportDomain=transportDomain,
|
|
transportAddress=transportAddress,
|
|
securityModel=securityModel,
|
|
securityName=securityName,
|
|
securityLevel=securityLevel,
|
|
contextEngineId=contextEngineId,
|
|
contextName=contextName,
|
|
securityEngineId=securityEngineId,
|
|
pdu=pdu)
|
|
)
|
|
snmpEngine.observer.clearExecutionContext(
|
|
snmpEngine, 'rfc3412.prepareDataElements:response'
|
|
)
|
|
|
|
# 7.2.12c
|
|
smHandler.releaseStateInformation(securityStateReference)
|
|
stateReference = None
|
|
|
|
# 7.2.12d
|
|
return (messageProcessingModel, securityModel, securityName,
|
|
securityLevel, contextEngineId, contextName,
|
|
pduVersion, pdu, pduType, sendPduHandle,
|
|
maxSizeResponseScopedPDU, statusInformation,
|
|
stateReference)
|
|
|
|
# 7.2.13
|
|
if pduType in rfc3411.confirmedClassPDUs:
|
|
# 7.2.13a
|
|
if securityEngineId != snmpEngineID:
|
|
smHandler.releaseStateInformation(securityStateReference)
|
|
raise error.StatusInformation(
|
|
errorIndication=errind.engineIDMismatch
|
|
)
|
|
|
|
# 7.2.13b
|
|
stateReference = self._cache.newStateReference()
|
|
self._cache.pushByStateRef(
|
|
stateReference, msgVersion=messageProcessingModel,
|
|
msgID=msgID, contextEngineId=contextEngineId,
|
|
contextName=contextName, securityModel=securityModel,
|
|
securityName=securityName, securityLevel=securityLevel,
|
|
securityStateReference=securityStateReference,
|
|
reportableFlag=reportableFlag, msgMaxSize=maxMessageSize,
|
|
maxSizeResponseScopedPDU=maxSizeResponseScopedPDU,
|
|
transportDomain=transportDomain,
|
|
transportAddress=transportAddress
|
|
)
|
|
|
|
debug.logger & debug.flagMP and debug.logger('prepareDataElements: new stateReference %s' % stateReference)
|
|
|
|
snmpEngine.observer.storeExecutionContext(
|
|
snmpEngine, 'rfc3412.prepareDataElements:confirmed',
|
|
dict(transportDomain=transportDomain,
|
|
transportAddress=transportAddress,
|
|
securityModel=securityModel,
|
|
securityName=securityName,
|
|
securityLevel=securityLevel,
|
|
contextEngineId=contextEngineId,
|
|
contextName=contextName,
|
|
securityEngineId=securityEngineId,
|
|
pdu=pdu)
|
|
)
|
|
snmpEngine.observer.clearExecutionContext(
|
|
snmpEngine, 'rfc3412.prepareDataElements:confirmed'
|
|
)
|
|
|
|
# 7.2.13c
|
|
return (messageProcessingModel, securityModel, securityName,
|
|
securityLevel, contextEngineId, contextName,
|
|
pduVersion, pdu, pduType, sendPduHandle,
|
|
maxSizeResponseScopedPDU, statusInformation,
|
|
stateReference)
|
|
|
|
# 7.2.14
|
|
if pduType in rfc3411.unconfirmedClassPDUs:
|
|
# Pass new stateReference to let app browse request details
|
|
stateReference = self._cache.newStateReference()
|
|
|
|
snmpEngine.observer.storeExecutionContext(
|
|
snmpEngine, 'rfc3412.prepareDataElements:unconfirmed',
|
|
dict(transportDomain=transportDomain,
|
|
transportAddress=transportAddress,
|
|
securityModel=securityModel,
|
|
securityName=securityName,
|
|
securityLevel=securityLevel,
|
|
contextEngineId=contextEngineId,
|
|
contextName=contextName,
|
|
securityEngineId=securityEngineId,
|
|
pdu=pdu)
|
|
)
|
|
snmpEngine.observer.clearExecutionContext(
|
|
snmpEngine, 'rfc3412.prepareDataElements:unconfirmed'
|
|
)
|
|
|
|
# This is not specified explicitly in RFC
|
|
smHandler.releaseStateInformation(securityStateReference)
|
|
|
|
return (messageProcessingModel, securityModel, securityName,
|
|
securityLevel, contextEngineId, contextName,
|
|
pduVersion, pdu, pduType, sendPduHandle,
|
|
maxSizeResponseScopedPDU, statusInformation,
|
|
stateReference)
|
|
|
|
smHandler.releaseStateInformation(securityStateReference)
|
|
raise error.StatusInformation(
|
|
errorIndication=errind.unsupportedPDUtype
|
|
)
|
|
|
|
def __expireEnginesInfo(self):
|
|
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.__engineIdCacheExpQueue[self.__expirationTimer]
|
|
self.__expirationTimer += 1
|
|
|
|
def receiveTimerTick(self, snmpEngine, timeNow):
|
|
self.__expireEnginesInfo()
|
|
AbstractMessageProcessingModel.receiveTimerTick(self, snmpEngine, timeNow)
|