Skip to content

Commit

Permalink
Ensure TRAP PDU consistency in v1arch
Browse files Browse the repository at this point in the history
Also, consistency ensuring code unified with v3arch piece what
has the side effect of *requiring* snmpTrapOID to be always
present anywhere among user-supplied variable-bindings.
  • Loading branch information
etingof authored and lextm committed Aug 26, 2024
1 parent ed87ede commit 72bf329
Showing 1 changed file with 63 additions and 15 deletions.
78 changes: 63 additions & 15 deletions pysnmp/hlapi/v1arch/asyncio/ntforg.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from pysnmp.hlapi.v1arch.auth import *
from pysnmp.hlapi.varbinds import *
from pysnmp.hlapi.v1arch.asyncio.transport import *
from pysnmp.proto.api import v2c
from pysnmp.proto.proxy import rfc2576
from pysnmp.smi.rfc1902 import *
from pysnmp.proto import api
Expand Down Expand Up @@ -51,24 +52,27 @@ async def sendNotification(
or :py:class:`~pysnmp.smi.rfc1902.ObjectType` class instances
of :py:class:`~pysnmp.smi.rfc1902.NotificationType` objects.
SNMP Notification PDU places rigid requirement on the ordering of
the variable-bindings.
Mandatory variable-bindings:
Besides user variable-bindings, SNMP Notification PDU requires at
least two variable-bindings to be present:
0. SNMPv2-MIB::sysUpTime.0 = <agent uptime>
1. SNMPv2-SMI::snmpTrapOID.0 = {SNMPv2-MIB::coldStart, ...}
1. SNMPv2-SMI::snmpTrapOID.0 = <notification ID>
Optional variable-bindings (applicable to SNMP v1 TRAP):
When sending SNMPv1 TRAP, more variable-bindings could be present:
2. SNMP-COMMUNITY-MIB::snmpTrapAddress.0 = <agent-IP>
3. SNMP-COMMUNITY-MIB::snmpTrapCommunity.0 = <snmp-community-name>
4. SNMP-COMMUNITY-MIB::snmpTrapEnterprise.0 = <enterprise-OID>
Informational variable-bindings:
If user does not supply some or any of the above variable-bindings or
if they are at the wrong positions, the system will add/reorder the
missing ones automatically.
* SNMPv2-SMI::NOTIFICATION-TYPE
* SNMPv2-SMI::OBJECT-TYPE
On top of that, some notification types imply including some additional
variable-bindings providing additional details on the event being
reported. Therefore it is generally easier to use
:py:class:`~pysnmp.smi.rfc1902.NotificationType` object which will
help adding relevant variable-bindings.
Other Parameters
----------------
Expand Down Expand Up @@ -121,14 +125,54 @@ class instances (if `lookupMib` is `True`) representing MIB variables
>>>
"""

sysUpTime = v2c.apiTrapPDU.sysUpTime
snmpTrapOID = v2c.apiTrapPDU.snmpTrapOID

def _ensureVarBinds(varBinds):
# Add sysUpTime if not present already
if not varBinds or varBinds[0][0] != sysUpTime:
varBinds.insert(0, (v2c.ObjectIdentifier(sysUpTime), v2c.TimeTicks(0)))

# Search for and reposition sysUpTime if it's elsewhere
for idx, varBind in enumerate(varBinds[1:]):
if varBind[0] == sysUpTime:
varBinds[0] = varBind
del varBinds[idx]
break

if len(varBinds) < 2:
raise error.PySnmpError(
"SNMP notification PDU requires "
"SNMPv2-MIB::snmpTrapOID.0 to be present"
)

# Search for and reposition snmpTrapOID if it's elsewhere
for idx, varBind in enumerate(varBinds[2:]):
if varBind[0] == snmpTrapOID:
del varBinds[idx]
if varBinds[1][0] == snmpTrapOID:
varBinds[1] = varBind
else:
varBinds.insert(1, varBind)
break

# Fail on missing snmpTrapOID
if varBinds[1][0] != snmpTrapOID:
raise error.PySnmpError(
"SNMP notification PDU requires "
"SNMPv2-MIB::snmpTrapOID.0 to be present"
)

return varBinds

def _cbFun(snmpDispatcher, stateHandle, errorIndication, rspPdu, _cbCtx):
if future.cancelled():
return

errorStatus = pMod.apiTrapPDU.getErrorStatus(rspPdu)
errorIndex = pMod.apiTrapPDU.getErrorIndex(rspPdu)
errorStatus = v2c.apiTrapPDU.getErrorStatus(rspPdu)
errorIndex = v2c.apiTrapPDU.getErrorIndex(rspPdu)

varBinds = pMod.apiTrapPDU.getVarBinds(rspPdu)
varBinds = v2c.apiTrapPDU.getVarBinds(rspPdu)

try:
varBindsUnmade = VB_PROCESSOR.unmakeVarBinds(
Expand Down Expand Up @@ -173,10 +217,14 @@ def _cbFun(snmpDispatcher, stateHandle, errorIndication, rspPdu, _cbCtx):
if notifyType == "trap":
reqPdu = pMod.TrapPDU()
else:
reqPdu = pMod.InformRequestPDU()
reqPdu = v2c.InformRequestPDU()

v2c.apiTrapPDU.setDefaults(reqPdu)
v2c.apiTrapPDU.setVarBinds(reqPdu, varBinds)

varBinds = v2c.apiTrapPDU.getVarBinds(reqPdu)

pMod.apiTrapPDU.setDefaults(reqPdu)
pMod.apiTrapPDU.setVarBinds(reqPdu, varBinds)
v2c.apiTrapPDU.setVarBinds(reqPdu, _ensureVarBinds(varBinds))

if authData.mpModel == 0:
reqPdu = rfc2576.v2ToV1(reqPdu)
Expand Down

0 comments on commit 72bf329

Please sign in to comment.