Skip to content

Commit 0645fc6

Browse files
authored
fixed SNMPv3 retries algorithm inconsistencies (#106)
1 parent 9f62301 commit 0645fc6

File tree

3 files changed

+35
-21
lines changed

3 files changed

+35
-21
lines changed

CHANGES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Revision 4.4.2, released 2017-11-XX
1111
- Example scripts rearranged in a way that IPv6 requirement is
1212
clearly encoded in the script's name
1313
- Fixed non-implied-OID encoding in SNMP table indices
14+
- Fixed inconsistent SNMPv3 discovery and retrying algorithm
1415

1516
Revision 4.4.1, released 2017-10-23
1617
-----------------------------------

pysnmp/entity/rfc3413/cmdgen.py

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ def getNextVarBinds(varBinds, origVarBinds=None):
4343
class CommandGenerator(object):
4444
_null = univ.Null('')
4545

46-
def __init__(self):
46+
def __init__(self, **options):
47+
self.__options = options
4748
self.__pendingReqs = {}
4849

4950
def processResponsePdu(self, snmpEngine, messageProcessingModel,
@@ -61,26 +62,28 @@ def processResponsePdu(self, snmpEngine, messageProcessingModel,
6162
origSecurityName, origSecurityLevel, origContextEngineId,
6263
origContextName, origPduVersion, origPdu,
6364
origTimeout, origRetryCount,
64-
origRetries) = self.__pendingReqs.pop(sendPduHandle)
65+
origRetries, origDiscoveryRetries) = self.__pendingReqs.pop(sendPduHandle)
6566

6667
snmpEngine.transportDispatcher.jobFinished(id(self))
6768

6869
# 3.1.3
6970
if statusInformation:
7071
debug.logger & debug.flagApp and debug.logger(
7172
'processResponsePdu: sendPduHandle %s, statusInformation %s' % (sendPduHandle, statusInformation))
73+
7274
errorIndication = statusInformation['errorIndication']
7375

74-
# SNMP engine discovery will take extra retries, allow that
75-
if (errorIndication in (errind.notInTimeWindow,
76-
errind.unknownEngineID) and
77-
origRetries == origRetryCount + 2 or
78-
errorIndication not in (errind.notInTimeWindow, errind.unknownEngineID) and
79-
origRetries == origRetryCount):
76+
if errorIndication in (errind.notInTimeWindow, errind.unknownEngineID):
77+
origDiscoveryRetries += 1
78+
origRetries = 0
79+
else:
80+
origDiscoveryRetries = 0
81+
origRetries += 1
82+
83+
if origRetries > origRetryCount or origDiscoveryRetries > self.__options.get('discoveryRetries', 4):
8084
debug.logger & debug.flagApp and debug.logger(
8185
'processResponsePdu: sendPduHandle %s, retry count %d exceeded' % (sendPduHandle, origRetries))
82-
cbFun(snmpEngine, origSendRequestHandle,
83-
statusInformation['errorIndication'], None, cbCtx)
86+
cbFun(snmpEngine, origSendRequestHandle, errorIndication, None, cbCtx)
8487
return
8588

8689
# User-side API assumes SMIv2
@@ -107,7 +110,7 @@ def processResponsePdu(self, snmpEngine, messageProcessingModel,
107110
origMessageProcessingModel, origSecurityModel,
108111
origSecurityName, origSecurityLevel, origContextEngineId,
109112
origContextName, origPduVersion, origPdu, origTimeout,
110-
origRetryCount, origRetries + 1
113+
origRetryCount, origRetries, origDiscoveryRetries
111114
)
112115
return
113116

@@ -192,7 +195,7 @@ def sendPdu(self, snmpEngine, targetName, contextEngineId,
192195
transportDomain, transportAddress, messageProcessingModel,
193196
securityModel, securityName, securityLevel, contextEngineId,
194197
contextName, pduVersion, origPDU, timeoutInTicks,
195-
retryCount, 0
198+
retryCount, 0, 0
196199
)
197200

198201
debug.logger & debug.flagApp and debug.logger(

pysnmp/entity/rfc3413/ntforg.py

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from pysnmp.proto.proxy import rfc2576
1111
from pysnmp.proto import rfc3411
1212
from pysnmp.proto.api import v2c
13-
from pysnmp.proto import error
13+
from pysnmp.proto import errind, error
1414
from pysnmp.smi import view, rfc1902
1515
from pysnmp import nextid
1616
from pysnmp import debug
@@ -21,10 +21,11 @@
2121
class NotificationOriginator(object):
2222
acmID = 3 # default MIB access control method to use
2323

24-
def __init__(self, snmpContext=None):
24+
def __init__(self, **options):
2525
self.__pendingReqs = {}
2626
self.__pendingNotifications = {}
27-
self.snmpContext = snmpContext # this is deprecated
27+
self.snmpContext = options.pop('snmpContext', None) # this is deprecated
28+
self.__options = options
2829

2930
def processResponsePdu(self, snmpEngine, messageProcessingModel,
3031
securityModel, securityName, securityLevel,
@@ -40,20 +41,29 @@ def processResponsePdu(self, snmpEngine, messageProcessingModel,
4041
origMessageProcessingModel, origSecurityModel,
4142
origSecurityName, origSecurityLevel, origContextEngineId,
4243
origContextName, origPdu, origTimeout,
43-
origRetryCount, origRetries) = self.__pendingReqs.pop(sendPduHandle)
44+
origRetryCount, origRetries, origDiscoveryRetries) = self.__pendingReqs.pop(sendPduHandle)
4445

4546
snmpEngine.transportDispatcher.jobFinished(id(self))
4647

4748
if statusInformation:
4849
debug.logger & debug.flagApp and debug.logger(
4950
'processResponsePdu: sendRequestHandle %s, sendPduHandle %s statusInformation %s' % (
5051
sendRequestHandle, sendPduHandle, statusInformation))
51-
if origRetries == origRetryCount:
52+
53+
errorIndication = statusInformation['errorIndication']
54+
55+
if errorIndication in (errind.notInTimeWindow, errind.unknownEngineID):
56+
origDiscoveryRetries += 1
57+
origRetries = 0
58+
else:
59+
origDiscoveryRetries = 0
60+
origRetries += 1
61+
62+
if origRetries > origRetryCount or origDiscoveryRetries > self.__options.get('discoveryRetries', 4):
5263
debug.logger & debug.flagApp and debug.logger(
5364
'processResponsePdu: sendRequestHandle %s, sendPduHandle %s retry count %d exceeded' % (
5465
sendRequestHandle, sendPduHandle, origRetries))
55-
cbFun(snmpEngine, sendRequestHandle,
56-
statusInformation['errorIndication'], None, cbCtx)
66+
cbFun(snmpEngine, sendRequestHandle, errorIndication, None, cbCtx)
5767
return
5868

5969
# Convert timeout in seconds into timeout in timer ticks
@@ -98,7 +108,7 @@ def processResponsePdu(self, snmpEngine, messageProcessingModel,
98108
origMessageProcessingModel, origSecurityModel,
99109
origSecurityName, origSecurityLevel,
100110
origContextEngineId, origContextName, origPdu,
101-
origTimeout, origRetryCount, origRetries + 1
111+
origTimeout, origRetryCount, origRetries, origDiscoveryRetries
102112
)
103113
return
104114

@@ -148,7 +158,7 @@ def sendPdu(self, snmpEngine, targetName, contextEngineId,
148158
self.__pendingReqs[sendPduHandle] = (
149159
transportDomain, transportAddress, messageProcessingModel,
150160
securityModel, securityName, securityLevel, contextEngineId,
151-
contextName, pdu, timeout, retryCount, True
161+
contextName, pdu, timeout, retryCount, 0, 0
152162
)
153163
snmpEngine.transportDispatcher.jobStarted(id(self))
154164
else:

0 commit comments

Comments
 (0)