Execution Observer facility implemented to give app an inside view

of SNMP engine inner workings. This is thought to be a generic
framework for viewing (and modifying) various internal states
of pysnmp engine. Previously introduced non-RFC APIs (like
getting peer's transport endpoint) will be gradually migrated to
this new framework.
pull/45/head
elie 2013-11-09 22:01:33 +00:00
parent 7a821af6de
commit ce56f1b40f
15 changed files with 710 additions and 91 deletions

View File

@ -1,6 +1,12 @@
Revision 4.2.6rc0
-----------------
- Execution Observer facility implemented to give app an inside view
of SNMP engine inner workings. This is thought to be a generic
framework for viewing (and modifying) various internal states
of pysnmp engine. Previously introduced non-RFC APIs (like
getting peer's transport endpoint) will be gradually migrated to
this new framework.
- Memory leak fixed in CommandForwarder examples.
- Fix to CommandGenerator to make sendRequestHandle persistent across
multiple iterations of GETNEXT/GETBULK queries.

View File

@ -23,28 +23,38 @@ cmdGen = cmdgen.CommandGenerator(snmpEngine)
transportTarget = cmdgen.UdpTransportTarget(('demo.snmplabs.com', 161))
#
# Discover remote SNMP EngineID
# To discover remote SNMP EngineID we will tap on SNMP engine inner workings
# by setting up execution point observer setup on INTERNAL class PDU processing
#
observerContext = {}
# Register a callback to be invoked at specified execution point of
# SNMP Engine and passed local variables at execution point's local scope
snmpEngine.observer.registerObserver(
lambda e,p,v,c: c.update(securityEngineId=v['securityEngineId']),
'rfc3412.prepareDataElements:internal',
cbCtx=observerContext
)
# Send probe SNMP request with invalid credentials
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)
# See if our SNMP engine received REPORT PDU containing securityEngineId
if 'securityEngineId' not in observerContext:
print("Can't discover peer EngineID, errorIndication: %s" % errorIndication)
raise Exception()
securityEngineId = observerContext.pop('securityEngineId')
print('Remote securityEngineId = %s' % securityEngineId.prettyPrint())
#
# Query remote SNMP Engine using usmUserTable entry configured for it
#

View File

@ -0,0 +1,84 @@
#
# Command Responder
#
# Listen and respond to SNMP GET/SET/GETNEXT/GETBULK queries with
# the following options:
#
# * SNMPv3
# * with USM user 'usr-md5-des', auth: MD5, priv DES or
# * allow access to SNMPv2-MIB objects (1.3.6.1.2.1)
# * over IPv4/UDP, listening at 127.0.0.1:161
# * registers its own execution observer to snmpEngine
#
# Either of the following Net-SNMP's commands will walk this Agent:
#
# $ snmpwalk -v3 -u usr-md5-des -l authPriv -A authkey1 -X privkey1 localhost .1.3.6
#
from pysnmp.entity import engine, config
from pysnmp.entity.rfc3413 import cmdrsp, context
from pysnmp.carrier.asynsock.dgram import udp
# Create SNMP engine
snmpEngine = engine.SnmpEngine()
# Execution point observer setup
# Register a callback to be invoked at specified execution point of
# SNMP Engine and passed local variables at code point's local scope
def requestObserver(snmpEngine, execpoint, variables, cbCtx):
print('Execution point: %s' % execpoint)
print('* transportDomain: %s' % '.'.join([str(x) for x in variables['transportDomain']]))
print('* transportAddress: %s' % '@'.join([str(x) for x in variables['transportAddress']]))
print('* securityModel: %s' % variables['securityModel'])
print('* securityName: %s' % variables['securityName'])
print('* securityLevel: %s' % variables['securityLevel'])
print('* contextEngineId: %s' % variables['contextEngineId'].prettyPrint())
print('* contextName: %s' % variables['contextName'].prettyPrint())
print('* PDU: %s' % variables['pdu'].prettyPrint())
snmpEngine.observer.registerObserver(
requestObserver,
'rfc3412.receiveMessage:request',
'rfc3412.returnResponsePdu'
)
# Transport setup
# UDP over IPv4
config.addTransport(
snmpEngine,
udp.domainName,
udp.UdpTransport().openServerMode(('127.0.0.1', 161))
)
# SNMPv3/USM setup
# user: usr-md5-des, auth: MD5, priv DES
config.addV3User(
snmpEngine, 'usr-md5-des',
config.usmHMACMD5AuthProtocol, 'authkey1',
config.usmDESPrivProtocol, 'privkey1'
)
# Allow full MIB access for each user at VACM
config.addVacmUser(snmpEngine, 3, 'usr-md5-des', 'authPriv', (1,3,6,1,2,1), (1,3,6,1,2,1))
# Get default SNMP context this SNMP engine serves
snmpContext = context.SnmpContext(snmpEngine)
# Register SNMP Applications at the SNMP engine for particular SNMP context
cmdrsp.GetCommandResponder(snmpEngine, snmpContext)
cmdrsp.SetCommandResponder(snmpEngine, snmpContext)
cmdrsp.NextCommandResponder(snmpEngine, snmpContext)
cmdrsp.BulkCommandResponder(snmpEngine, snmpContext)
# Register an imaginary never-ending job to keep I/O dispatcher running forever
snmpEngine.transportDispatcher.jobStarted(1)
# Run I/O dispatcher which would receive queries and send responses
try:
snmpEngine.transportDispatcher.runDispatcher()
except:
snmpEngine.observer.unregisterObserver()
snmpEngine.transportDispatcher.closeDispatcher()
raise

View File

@ -0,0 +1,99 @@
#
# GET Command Generator
#
# Send a SNMP GET request
# with SNMPv3 with user 'usr-sha-aes', SHA auth and AES128 privacy protocols
# over IPv4/UDP
# to an Agent at 195.218.195.228:161
# for an OID in tuple form
# also registers its own execution observer to snmpEngine
#
# This script performs similar to the following Net-SNMP command:
#
# $ snmpget -v3 -l authPriv -u usr-sha-aes -a SHA -A authkey1 -x AES -X privkey1 -ObentU 195.218.195.228:161 1.3.6.1.2.1.1.1.0
#
from pysnmp.entity import engine, config
from pysnmp.carrier.asynsock.dgram import udp
from pysnmp.entity.rfc3413 import cmdgen
# Create SNMP engine instance
snmpEngine = engine.SnmpEngine()
# Execution point observer setup
# Register a callback to be invoked at specified execution point of
# SNMP Engine and passed local variables at code point's local scope
def requestObserver(snmpEngine, execpoint, variables, cbCtx):
print('Execution point: %s' % execpoint)
print('* transportDomain: %s' % '.'.join([str(x) for x in variables['transportDomain']]))
print('* transportAddress: %s' % '@'.join([str(x) for x in variables['transportAddress']]))
print('* securityModel: %s' % variables['securityModel'])
print('* securityName: %s' % variables['securityName'])
print('* securityLevel: %s' % variables['securityLevel'])
print('* contextEngineId: %s' % (variables['contextEngineId'] and variables['contextEngineId'].prettyPrint() or '<empty>',))
print('* contextName: %s' % variables['contextName'].prettyPrint())
print('* PDU: %s' % variables['pdu'].prettyPrint())
snmpEngine.observer.registerObserver(
requestObserver,
'rfc3412.sendPdu',
'rfc3412.receiveMessage:response'
)
#
# SNMPv3/USM setup
#
# user: usr-sha-aes, auth: SHA, priv AES
config.addV3User(
snmpEngine, 'usr-sha-aes',
config.usmHMACSHAAuthProtocol, 'authkey1',
config.usmAesCfb128Protocol, 'privkey1'
)
config.addTargetParams(snmpEngine, 'my-creds', 'usr-sha-aes', 'authPriv')
#
# Setup transport endpoint and bind it with security settings yielding
# a target name
#
# UDP/IPv4
config.addTransport(
snmpEngine,
udp.domainName,
udp.UdpSocketTransport().openClientMode()
)
config.addTargetAddr(
snmpEngine, 'my-router',
udp.domainName, ('195.218.195.228', 161),
'my-creds'
)
# Error/response receiver
def cbFun(sendRequestHandle,
errorIndication, errorStatus, errorIndex,
varBinds, cbCtx):
if errorIndication:
print(errorIndication)
elif errorStatus:
print('%s at %s' % (
errorStatus.prettyPrint(),
errorIndex and varBinds[int(errorIndex)-1][0] or '?'
)
)
else:
for oid, val in varBinds:
print('%s = %s' % (oid.prettyPrint(), val.prettyPrint()))
# Prepare and send a request message
cmdgen.GetCommandGenerator().sendReq(
snmpEngine,
'my-router',
( ((1,3,6,1,2,1,1,1,0), None), ),
cbFun
)
# Run I/O dispatcher which would send pending queries and process responses
snmpEngine.transportDispatcher.runDispatcher()
snmpEngine.observer.unregisterObserver()

View File

@ -0,0 +1,67 @@
#
# Notification Receiver
#
# Receive SNMP TRAP/INFORM messages with the following options:
#
# * SNMPv1/SNMPv2c
# * with SNMP community "public"
# * over IPv4/UDP, listening at 127.0.0.1:162
# * use observer facility to pull lower-level request details from SNMP engine
# * print received data on stdout
#
# Either of the following Net-SNMP's commands will send notifications to this
# receiver:
#
# $ snmptrap -v2c -c public 127.0.0.1:162 123 1.3.6.1.6.3.1.1.5.1 1.3.6.1.2.1.1.5.0 s test
#
from pysnmp.entity import engine, config
from pysnmp.carrier.asynsock.dgram import udp
from pysnmp.entity.rfc3413 import ntfrcv
# Create SNMP engine with autogenernated engineID and pre-bound
# to socket transport dispatcher
snmpEngine = engine.SnmpEngine()
# Transport setup
# UDP over IPv4, first listening interface/port
config.addTransport(
snmpEngine,
udp.domainName + (1,),
udp.UdpTransport().openServerMode(('127.0.0.1', 162))
)
# SNMPv1/2c setup
# SecurityName <-> CommunityName mapping
config.addV1System(snmpEngine, 'my-area', 'public')
# Callback function for receiving notifications
def cbFun(snmpEngine, stateReference, contextEngineId, contextName,
varBinds, cbCtx):
# Get an execution context...
execContext = snmpEngine.observer.getExecutionContext(
'rfc3412.receiveMessage:request'
)
# ... and use inner SNMP engine data to figure out peer address
print('Notification from %s, ContextEngineId "%s", ContextName "%s"' % (
'@'.join([str(x) for x in execContext['transportAddress']]),
contextEngineId.prettyPrint(),
contextName.prettyPrint()
)
)
for name, val in varBinds:
print('%s = %s' % (name.prettyPrint(), val.prettyPrint()))
# Register SNMP Application at the SNMP engine
ntfrcv.NotificationReceiver(snmpEngine, cbFun)
snmpEngine.transportDispatcher.jobStarted(1) # this job would never finish
# Run I/O dispatcher which would receive queries and send confirmations
try:
snmpEngine.transportDispatcher.runDispatcher()
except:
snmpEngine.transportDispatcher.closeDispatcher()
raise

View File

@ -45,14 +45,10 @@ config.addTransport(
config.addV1System(snmpEngine, 'my-area', 'public')
# Callback function for receiving notifications
def cbFun(snmpEngine,
stateReference,
contextEngineId, contextName,
varBinds,
cbCtx):
transportDomain, transportAddress = snmpEngine.msgAndPduDsp.getTransportInfo(stateReference)
print('Notification from %s, ContextEngineId "%s", ContextName "%s"' % (
transportAddress, contextEngineId.prettyPrint(),
def cbFun(snmpEngine, stateReference, contextEngineId, contextName,
varBinds, cbCtx):
print('Notification from ContextEngineId "%s", ContextName "%s"' % (
contextEngineId.prettyPrint(),
contextName.prettyPrint()
)
)

View File

@ -46,14 +46,10 @@ config.addTransport(
config.addV1System(snmpEngine, 'my-area', 'public')
# Callback function for receiving notifications
def cbFun(snmpEngine,
stateReference,
contextEngineId, contextName,
varBinds,
cbCtx):
transportDomain, transportAddress = snmpEngine.msgAndPduDsp.getTransportInfo(stateReference)
print('Notification from %s, ContextEngineId "%s", ContextName "%s"' % (
transportAddress, contextEngineId.prettyPrint(),
def cbFun(snmpEngine, stateReference, contextEngineId, contextName,
varBinds, cbCtx):
print('Notification from ContextEngineId "%s", ContextName "%s"' % (
contextEngineId.prettyPrint(),
contextName.prettyPrint()
)
)

View File

@ -84,13 +84,11 @@ config.addV3User(
)
# Callback function for receiving notifications
def cbFun(snmpEngine,
stateReference,
contextEngineId, contextName,
varBinds,
cbCtx):
print('Notification received, ContextEngineId "%s", ContextName "%s"' % (
contextEngineId.prettyPrint(), contextName.prettyPrint()
def cbFun(snmpEngine, stateReference, contextEngineId, contextName,
varBinds, cbCtx):
print('Notification from ContextEngineId "%s", ContextName "%s"' % (
contextEngineId.prettyPrint(),
contextName.prettyPrint()
)
)
for name, val in varBinds:

View File

@ -0,0 +1,92 @@
#
# Notification Receiver
#
# Receive SNMP TRAP/INFORM messages with the following options:
#
# * SNMPv1/SNMPv2c
# * with SNMP community "public"
# * over IPv4/UDP, listening at 127.0.0.1:162
# over IPv6/UDP, listening at [::1]:162
# * registers its own execution observer to snmpEngine
# * print received data on stdout
#
# Either of the following Net-SNMP's commands will send notifications to this
# receiver:
#
# $ snmptrap -v1 -c public 127.0.0.1 1.3.6.1.4.1.20408.4.1.1.2 127.0.0.1 1 1 123 1.3.6.1.2.1.1.1.0 s test
# $ snmptrap -v2c -c public udp6:[::1]:162 123 1.3.6.1.6.3.1.1.5.1 1.3.6.1.2.1.1.5.0 s test
# $ snmpinform -v2c -c public 127.0.0.1 123 1.3.6.1.6.3.1.1.5.1
#
from pysnmp.entity import engine, config
from pysnmp.carrier.asynsock.dgram import udp, udp6
from pysnmp.entity.rfc3413 import ntfrcv
# Create SNMP engine with autogenernated engineID and pre-bound
# to socket transport dispatcher
snmpEngine = engine.SnmpEngine()
# Execution point observer setup
# Register a callback to be invoked at specified execution point of
# SNMP Engine and passed local variables at code point's local scope
def requestObserver(snmpEngine, execpoint, variables, cbCtx):
print('Execution point: %s' % execpoint)
print('* transportDomain: %s' % '.'.join([str(x) for x in variables['transportDomain']]))
print('* transportAddress: %s' % '@'.join([str(x) for x in variables['transportAddress']]))
print('* securityModel: %s' % variables['securityModel'])
print('* securityName: %s' % variables['securityName'])
print('* securityLevel: %s' % variables['securityLevel'])
print('* contextEngineId: %s' % variables['contextEngineId'].prettyPrint())
print('* contextName: %s' % variables['contextName'].prettyPrint())
print('* PDU: %s' % variables['pdu'].prettyPrint())
snmpEngine.observer.registerObserver(
requestObserver,
'rfc3412.receiveMessage:request',
'rfc3412.returnResponsePdu'
)
# Transport setup
# UDP over IPv4
config.addTransport(
snmpEngine,
udp.domainName,
udp.UdpTransport().openServerMode(('127.0.0.1', 162))
)
# UDP over IPv6
config.addTransport(
snmpEngine,
udp6.domainName,
udp6.Udp6Transport().openServerMode(('::1', 162))
)
# SNMPv1/2c setup
# SecurityName <-> CommunityName mapping
config.addV1System(snmpEngine, 'my-area', 'public')
# Callback function for receiving notifications
def cbFun(snmpEngine, stateReference, contextEngineId, contextName,
varBinds, cbCtx):
print('Notification from ContextEngineId "%s", ContextName "%s"' % (
contextEngineId.prettyPrint(),
contextName.prettyPrint()
)
)
for name, val in varBinds:
print('%s = %s' % (name.prettyPrint(), val.prettyPrint()))
# Register SNMP Application at the SNMP engine
ntfrcv.NotificationReceiver(snmpEngine, cbFun)
snmpEngine.transportDispatcher.jobStarted(1) # this job would never finish
# Run I/O dispatcher which would receive queries and send confirmations
try:
snmpEngine.transportDispatcher.runDispatcher()
except:
snmpEngine.observer.unregisterObserver()
snmpEngine.transportDispatcher.closeDispatcher()
raise

View File

@ -47,15 +47,11 @@ config.addTransport(
config.addV1System(snmpEngine, 'my-area', 'public')
# Callback function for receiving notifications
def cbFun(snmpEngine,
stateReference,
contextEngineId, contextName,
varBinds,
cbCtx):
transportDomain, transportAddress = snmpEngine.msgAndPduDsp.getTransportInfo(stateReference)
print('Notification from %s, SNMP Engine %s, Context %s' % (
transportAddress, contextEngineId.prettyPrint(),
contextName.prettyPrint()
def cbFun(snmpEngine, stateReference, contextEngineId, contextName,
varBinds, cbCtx):
print('Notification from ContextEngineId "%s", Context "%s"' % (
contextEngineId.prettyPrint(),
contextName.prettyPrint()
)
)
for name, val in varBinds:

View File

@ -89,12 +89,9 @@ config.addV3User(
# -- end of SNMPv3/USM setup
# Callback function for receiving notifications
def cbFun(snmpEngine,
stateReference,
contextEngineId, contextName,
varBinds,
cbCtx):
print('Notification received, ContextEngineId "%s", ContextName "%s"' % (
def cbFun(snmpEngine, stateReference, contextEngineId, contextName,
varBinds, cbCtx):
print('Notification from ContextEngineId "%s", ContextName "%s"' % (
contextEngineId.prettyPrint(), contextName.prettyPrint()
)
)

View File

@ -7,12 +7,15 @@ from pysnmp.proto.secmod.rfc2576 import SnmpV1SecurityModel, \
SnmpV2cSecurityModel
from pysnmp.proto.secmod.rfc3414 import SnmpUSMSecurityModel
from pysnmp.proto.acmod import rfc3415, void
from pysnmp.entity import observer
from pysnmp import error
class SnmpEngine:
def __init__(self, snmpEngineID=None, maxMessageSize=65507,
msgAndPduDsp=None):
self.cache = {}
self.observer = observer.MetaObserver()
if msgAndPduDsp is None:
self.msgAndPduDsp = MsgAndPduDispatcher()
@ -63,14 +66,14 @@ class SnmpEngine:
self.msgAndPduDsp.receiveMessage(
self, transportDomain, transportAddress, wholeMsg
)
def __receiveTimerTickCbFun(self, timeNow):
self.msgAndPduDsp.receiveTimerTick(self, timeNow)
for mpHandler in self.messageProcessingSubsystems.values():
mpHandler.receiveTimerTick(self, timeNow)
for smHandler in self.securityModels.values():
smHandler.receiveTimerTick(self, timeNow)
def registerTransportDispatcher(self, transportDispatcher, recvId=None):
if self.transportDispatcher is not None and \
self.transportDispatcher is not transportDispatcher:

View File

@ -33,8 +33,8 @@ class SnmpV1MessageProcessingModel(AbstractMessageProcessingModel):
expectResponse,
sendPduHandle
):
snmpEngineID, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('__SNMP-FRAMEWORK-MIB', 'snmpEngineID')
snmpEngineID = snmpEngineID.syntax
snmpEngineId, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('__SNMP-FRAMEWORK-MIB', 'snmpEngineID')
snmpEngineId = snmpEngineId.syntax
# rfc3412: 7.1.1b
if pdu.tagSet in rfc3411.confirmedClassPDUs:
@ -45,7 +45,7 @@ class SnmpV1MessageProcessingModel(AbstractMessageProcessingModel):
# Since there's no SNMP engine identification in v1/2c,
# set destination contextEngineId to ours
if not contextEngineId:
contextEngineId = snmpEngineID
contextEngineId = snmpEngineId
# rfc3412: 7.1.5
if not contextName:
@ -86,7 +86,7 @@ class SnmpV1MessageProcessingModel(AbstractMessageProcessingModel):
globalData,
snmpEngineMaxMessageSize.syntax,
securityModel,
snmpEngineID,
snmpEngineId,
securityName,
securityLevel,
scopedPDU
@ -99,7 +99,7 @@ class SnmpV1MessageProcessingModel(AbstractMessageProcessingModel):
int(msgID),
sendPduHandle=sendPduHandle,
msgID=msgID,
snmpEngineID=snmpEngineID,
snmpEngineId=snmpEngineId,
securityModel=securityModel,
securityName=securityName,
securityLevel=securityLevel,
@ -109,6 +109,26 @@ class SnmpV1MessageProcessingModel(AbstractMessageProcessingModel):
transportAddress=transportAddress
)
communityName = msg.getComponentByPosition(1) # for observer
snmpEngine.observer.storeExecutionContext(
snmpEngine,
'rfc2576.prepareOutgoingMessage',
dict(transportDomain=transportDomain,
transportAddress=transportAddress,
wholeMsg=wholeMsg,
securityModel=securityModel,
securityName=securityName,
securityLevel=securityLevel,
contextEngineId=contextEngineId,
contextName=contextName,
communityName=communityName,
pdu=pdu)
)
snmpEngine.observer.clearExecutionContext(
snmpEngine, 'rfc2576.prepareOutgoingMessage'
)
return ( transportDomain, transportAddress, wholeMsg )
# rfc3412: 7.1
@ -127,8 +147,8 @@ class SnmpV1MessageProcessingModel(AbstractMessageProcessingModel):
stateReference,
statusInformation
):
snmpEngineID, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('__SNMP-FRAMEWORK-MIB', 'snmpEngineID')
snmpEngineID = snmpEngineID.syntax
snmpEngineId, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('__SNMP-FRAMEWORK-MIB', 'snmpEngineID')
snmpEngineId = snmpEngineId.syntax
# rfc3412: 7.1.2.b
if stateReference is None:
@ -162,7 +182,7 @@ class SnmpV1MessageProcessingModel(AbstractMessageProcessingModel):
# Since there's no SNMP engine identification in v1/2c,
# set destination contextEngineId to ours
if not contextEngineId:
contextEngineId = snmpEngineID
contextEngineId = snmpEngineId
# rfc3412: 7.1.5
if not contextName:
@ -193,8 +213,6 @@ class SnmpV1MessageProcessingModel(AbstractMessageProcessingModel):
errorIndication = errind.unsupportedSecurityModel
)
securityEngineId = snmpEngineID
# rfc3412: 7.1.8.a
( securityParameters,
wholeMsg ) = smHandler.generateResponseMsg(
@ -203,13 +221,31 @@ class SnmpV1MessageProcessingModel(AbstractMessageProcessingModel):
globalData,
maxMessageSize,
securityModel,
snmpEngineID,
snmpEngineId,
securityName,
securityLevel,
scopedPDU,
securityStateReference
)
snmpEngine.observer.storeExecutionContext(
snmpEngine,
'rfc2576.prepareResponseMessage',
dict(transportDomain=transportDomain,
transportAddress=transportAddress,
securityModel=securityModel,
securityName=securityName,
securityLevel=securityLevel,
contextEngineId=contextEngineId,
contextName=contextName,
securityEngineId=snmpEngineId,
communityName=msg.getComponentByPosition(1),
pdu=pdu)
)
snmpEngine.observer.clearExecutionContext(
snmpEngine, 'rfc2576.prepareResponseMessage'
)
return ( transportDomain, transportAddress, wholeMsg )
# rfc3412: 7.2.1
@ -241,8 +277,9 @@ class SnmpV1MessageProcessingModel(AbstractMessageProcessingModel):
# rfc2576: 5.2.1
snmpEngineMaxMessageSize, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('__SNMP-FRAMEWORK-MIB', 'snmpEngineMaxMessageSize')
communityName = msg.getComponentByPosition(1)
securityParameters = (
msg.getComponentByPosition(1),
communityName,
# transportDomain identifies local enpoint
(transportDomain, transportAddress)
)
@ -261,7 +298,7 @@ class SnmpV1MessageProcessingModel(AbstractMessageProcessingModel):
)
# rfc3412: 7.2.6
( securityEngineID,
( securityEngineId,
securityName,
scopedPDU,
maxSizeResponseScopedPDU,
@ -276,7 +313,7 @@ class SnmpV1MessageProcessingModel(AbstractMessageProcessingModel):
msg
)
debug.logger & debug.flagMP and debug.logger('prepareDataElements: SM returned securityEngineID %r securityName %r' % (securityEngineID, securityName))
debug.logger & debug.flagMP and debug.logger('prepareDataElements: SM returned securityEngineId %r securityName %r' % (securityEngineId, securityName))
# rfc3412: 7.2.6a --> noop
@ -329,6 +366,24 @@ class SnmpV1MessageProcessingModel(AbstractMessageProcessingModel):
stateReference = None
snmpEngine.observer.storeExecutionContext(
snmpEngine,
'rfc2576.prepareDataElements:response',
dict(transportDomain=transportDomain,
transportAddress=transportAddress,
securityModel=securityModel,
securityName=securityName,
securityLevel=securityLevel,
contextEngineId=contextEngineId,
contextName=contextName,
securityEngineId=securityEngineId,
communityName=communityName,
pdu=pdu)
)
snmpEngine.observer.clearExecutionContext(
snmpEngine, 'rfc2576.prepareDataElements:response'
)
# rfc3412: 7.2.12c
smHandler.releaseStateInformation(securityStateReference)
@ -353,12 +408,12 @@ class SnmpV1MessageProcessingModel(AbstractMessageProcessingModel):
msgID = pdu.getComponentByPosition(0)
# rfc3412: 7.2.13a
snmpEngineID, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('__SNMP-FRAMEWORK-MIB', 'snmpEngineID')
if securityEngineID != snmpEngineID.syntax:
snmpEngineId, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('__SNMP-FRAMEWORK-MIB', 'snmpEngineID')
if securityEngineId != snmpEngineId.syntax:
smHandler.releaseStateInformation(securityStateReference)
raise error.StatusInformation(
errorIndication = errind.engineIDMismatch
)
)
# rfc3412: 7.2.13b
stateReference = self._cache.newStateReference()
@ -378,6 +433,24 @@ class SnmpV1MessageProcessingModel(AbstractMessageProcessingModel):
transportAddress=transportAddress
)
snmpEngine.observer.storeExecutionContext(
snmpEngine,
'rfc2576.prepareDataElements:confirmed',
dict(transportDomain=transportDomain,
transportAddress=transportAddress,
securityModel=securityModel,
securityName=securityName,
securityLevel=securityLevel,
contextEngineId=contextEngineId,
contextName=contextName,
securityEngineId=securityEngineId,
communityName=communityName,
pdu=pdu)
)
snmpEngine.observer.clearExecutionContext(
snmpEngine, 'rfc2576.prepareDataElements:confirmed'
)
debug.logger & debug.flagMP and debug.logger('prepareDataElements: cached by new stateReference %s' % stateReference)
# rfc3412: 7.2.13c
@ -400,6 +473,24 @@ class SnmpV1MessageProcessingModel(AbstractMessageProcessingModel):
# Pass new stateReference to let app browse request details
stateReference = self._cache.newStateReference()
snmpEngine.observer.storeExecutionContext(
snmpEngine,
'rfc2576.prepareDataElements:unconfirmed',
dict(transportDomain=transportDomain,
transportAddress=transportAddress,
securityModel=securityModel,
securityName=securityName,
securityLevel=securityLevel,
contextEngineId=contextEngineId,
contextName=contextName,
securityEngineId=securityEngineId,
communityName=communityName,
pdu=pdu)
)
snmpEngine.observer.clearExecutionContext(
snmpEngine, 'rfc2576.prepareDataElements:unconfirmed'
)
# This is not specified explicitly in RFC
smHandler.releaseStateInformation(securityStateReference)

View File

@ -259,6 +259,23 @@ class SnmpV3MessageProcessingModel(AbstractMessageProcessingModel):
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 )
@ -296,7 +313,7 @@ class SnmpV3MessageProcessingModel(AbstractMessageProcessingModel):
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
@ -389,7 +406,7 @@ class SnmpV3MessageProcessingModel(AbstractMessageProcessingModel):
# XXX need to coerce MIB value as it has incompatible constraints set
headerData.setComponentByPosition(
1, snmpEngineMaxMessageSize.syntax, verifyConstraints=False
)
)
# 7.1.7d
msgFlags = 0
@ -452,6 +469,23 @@ class SnmpV3MessageProcessingModel(AbstractMessageProcessingModel):
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
@ -674,6 +708,23 @@ class SnmpV3MessageProcessingModel(AbstractMessageProcessingModel):
# 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)
@ -700,6 +751,23 @@ class SnmpV3MessageProcessingModel(AbstractMessageProcessingModel):
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
@ -749,6 +817,23 @@ class SnmpV3MessageProcessingModel(AbstractMessageProcessingModel):
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,
@ -768,7 +853,24 @@ class SnmpV3MessageProcessingModel(AbstractMessageProcessingModel):
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)

View File

@ -33,9 +33,10 @@ class MsgAndPduDispatcher:
# Source of sendPduHandle and cache of requesting apps
self.__sendPduHandle = nextid.Integer(0xffffff)
# To pass transport info to app
# To pass transport info to app (legacy)
self.__transportInfo = {}
# legacy
def getTransportInfo(self, stateReference):
if stateReference in self.__transportInfo:
return self.__transportInfo[stateReference]
@ -135,14 +136,17 @@ class MsgAndPduDispatcher:
debug.logger & debug.flagDsp and debug.logger('sendPdu: new sendPduHandle %s, timeout %s ticks, cbFun %s' % (sendPduHandle, timeout, cbFun))
origTransportDomain = transportDomain
origTransportAddress = transportAddress
# 4.1.1.4 & 4.1.1.5
try:
( destTransportDomain,
destTransportAddress,
( transportDomain,
transportAddress,
outgoingMessage ) = mpHandler.prepareOutgoingMessage(
snmpEngine,
transportDomain,
transportAddress,
origTransportDomain,
origTransportAddress,
messageProcessingModel,
securityModel,
securityName,
@ -168,21 +172,38 @@ class MsgAndPduDispatcher:
self.__cache.pop(sendPduHandle)
raise error.PySnmpError('Transport dispatcher not set')
snmpEngine.observer.storeExecutionContext(
snmpEngine,
'rfc3412.sendPdu',
dict(transportDomain=transportDomain,
transportAddress=transportAddress,
outgoingMessage=outgoingMessage,
messageProcessingModel=messageProcessingModel,
securityModel=securityModel,
securityName=securityName,
securityLevel=securityLevel,
contextEngineId=contextEngineId,
contextName=contextName,
pdu=PDU)
)
try:
snmpEngine.transportDispatcher.sendMessage(
outgoingMessage, destTransportDomain, destTransportAddress
outgoingMessage, transportDomain, transportAddress
)
except PySnmpError:
if expectResponse:
self.__cache.pop(sendPduHandle)
raise
snmpEngine.observer.clearExecutionContext(snmpEngine, 'rfc3412.sendPdu')
# Update cache with orignal req params (used for retrying)
if expectResponse:
self.__cache.update(
sendPduHandle,
transportDomain=transportDomain,
transportAddress=transportAddress,
transportDomain=origTransportDomain,
transportAddress=origTransportAddress,
securityModel=securityModel,
securityName=securityName,
securityLevel=securityLevel,
@ -210,7 +231,6 @@ class MsgAndPduDispatcher:
stateReference,
statusInformation
):
"""PDU dispatcher -- prepare and serialize a response"""
# Extract input values and initialize defaults
k = int(messageProcessingModel)
if k in snmpEngine.messageProcessingSubsystems:
@ -224,8 +244,8 @@ class MsgAndPduDispatcher:
# 4.1.2.2
try:
( destTransportDomain,
destTransportAddress,
( transportDomain,
transportAddress,
outgoingMessage ) = mpHandler.prepareResponseMessage(
snmpEngine,
messageProcessingModel,
@ -239,7 +259,7 @@ class MsgAndPduDispatcher:
maxSizeResponseScopedPDU,
stateReference,
statusInformation
)
)
debug.logger & debug.flagDsp and debug.logger('returnResponsePdu: MP suceeded')
except error.StatusInformation:
# 4.1.2.3
@ -253,12 +273,31 @@ class MsgAndPduDispatcher:
snmpSilentDrops.syntax = snmpSilentDrops.syntax + 1
raise error.StatusInformation(errorIndication=errind.tooBig)
snmpEngine.observer.storeExecutionContext(
snmpEngine,
'rfc3412.returnResponsePdu',
dict(transportDomain=transportDomain,
transportAddress=transportAddress,
outgoingMessage=outgoingMessage,
messageProcessingModel=messageProcessingModel,
securityModel=securityModel,
securityName=securityName,
securityLevel=securityLevel,
contextEngineId=contextEngineId,
contextName=contextName,
pdu=PDU)
)
# 4.1.2.4
snmpEngine.transportDispatcher.sendMessage(
outgoingMessage,
destTransportDomain,
destTransportAddress
)
transportDomain,
transportAddress
)
snmpEngine.observer.clearExecutionContext(
snmpEngine, 'rfc3412.returnResponsePdu'
)
# 4.2.1
def receiveMessage(
@ -396,11 +435,27 @@ class MsgAndPduDispatcher:
# 4.2.2.1.2.d
return restOfWholeMsg
else:
# Pass transport info to app
snmpEngine.observer.storeExecutionContext(
snmpEngine,
'rfc3412.receiveMessage:request',
dict(transportDomain=transportDomain,
transportAddress=transportAddress,
wholeMsg=wholeMsg,
messageProcessingModel=messageProcessingModel,
securityModel=securityModel,
securityName=securityName,
securityLevel=securityLevel,
contextEngineId=contextEngineId,
contextName=contextName,
pdu=PDU)
)
# pass transport info to app (legacy)
if stateReference is not None:
self.__transportInfo[stateReference] = (
transportDomain, transportAddress
)
)
# 4.2.2.1.3
processPdu(
snmpEngine,
@ -414,9 +469,16 @@ class MsgAndPduDispatcher:
PDU,
maxSizeResponseScopedPDU,
stateReference
)
)
snmpEngine.observer.clearExecutionContext(
snmpEngine, 'rfc3412.receiveMessage:request'
)
# legacy
if stateReference is not None:
del self.__transportInfo[stateReference]
debug.logger & debug.flagDsp and debug.logger('receiveMessage: processPdu succeeded')
return restOfWholeMsg
else:
@ -436,6 +498,21 @@ class MsgAndPduDispatcher:
# 4.2.2.2.3
# no-op ? XXX
snmpEngine.observer.storeExecutionContext(
snmpEngine,
'rfc3412.receiveMessage:response',
dict(transportDomain=transportDomain,
transportAddress=transportAddress,
wholeMsg=wholeMsg,
messageProcessingModel=messageProcessingModel,
securityModel=securityModel,
securityName=securityName,
securityLevel=securityLevel,
contextEngineId=contextEngineId,
contextName=contextName,
pdu=PDU)
)
# 4.2.2.2.4
processResponsePdu = cachedParams['cbFun']
processResponsePdu(
@ -451,7 +528,12 @@ class MsgAndPduDispatcher:
statusInformation,
cachedParams['sendPduHandle'],
cachedParams['cbCtx']
)
)
snmpEngine.observer.clearExecutionContext(
snmpEngine, 'rfc3412.receiveMessage:response'
)
debug.logger & debug.flagDsp and debug.logger('receiveMessage: processResponsePdu succeeded')
return restOfWholeMsg