summaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorelie <elie>2015-01-20 16:57:59 +0000
committerelie <elie>2015-01-20 16:57:59 +0000
commitfd457f8135120a10b6789bafe0d84f943eea893d (patch)
tree9ba83ae52f221cccbf5fce433b8f16ff48bdbae5 /examples
parent61d145ab7be929790beba74760db4e0ce9e70ef1 (diff)
downloadpysnmp-fd457f8135120a10b6789bafe0d84f943eea893d.tar.gz
- The asyncore-based transport subsystem extended to support POSIX
sendmsg()/recvmsg() based socket communication what could be used, among other things, in the context of a transparent SNMP proxy application. Technically, the following features were brought into pysnmp with this update: * Sending SNMP packets from a non-local IP address * Receiving IP packets for non-local IP addresses * Responding to SNMP requests from exactly the same IP address the query was sent to. This proves to be useful when listening on both primary and secondary IP interfaces.
Diffstat (limited to 'examples')
-rw-r--r--examples/v1arch/manager/get-v2c-spoof-source-address.py87
-rw-r--r--examples/v3arch/agent/cmdrsp/v3-observe-request-processing.py2
-rw-r--r--examples/v3arch/agent/cmdrsp/v3-preserve-original-destination-address.py89
-rw-r--r--examples/v3arch/manager/cmdgen/get-v2c-spoof-source-address.py99
4 files changed, 276 insertions, 1 deletions
diff --git a/examples/v1arch/manager/get-v2c-spoof-source-address.py b/examples/v1arch/manager/get-v2c-spoof-source-address.py
new file mode 100644
index 0000000..28f2190
--- /dev/null
+++ b/examples/v1arch/manager/get-v2c-spoof-source-address.py
@@ -0,0 +1,87 @@
+from pysnmp.carrier.asynsock.dispatch import AsynsockDispatcher
+from pysnmp.carrier.asynsock.dgram import udp
+from pysnmp.proto import api
+from pyasn1.codec.ber import encoder, decoder
+from time import time
+
+# Send request message to this address
+transportAddress = udp.UdpTransportAddress(('195.218.195.228', 161))
+
+# Send request message from this non-local (!) IP address
+transportAddress.setLocalAddress(('1.2.3.4', 0))
+
+# Protocol version to use
+#pMod = api.protoModules[api.protoVersion1]
+pMod = api.protoModules[api.protoVersion2c]
+
+# Build PDU
+reqPDU = pMod.GetRequestPDU()
+pMod.apiPDU.setDefaults(reqPDU)
+pMod.apiPDU.setVarBinds(
+ reqPDU, ( ('1.3.6.1.2.1.1.1.0', pMod.Null('')),
+ ('1.3.6.1.2.1.1.3.0', pMod.Null('')) )
+ )
+
+# Build message
+reqMsg = pMod.Message()
+pMod.apiMessage.setDefaults(reqMsg)
+pMod.apiMessage.setCommunity(reqMsg, 'public')
+pMod.apiMessage.setPDU(reqMsg, reqPDU)
+
+startedAt = time()
+
+class StopWaiting(Exception): pass
+
+def cbTimerFun(timeNow):
+ if timeNow - startedAt > 3:
+ raise StopWaiting()
+
+def cbRecvFun(transportDispatcher, transportDomain, transportAddress,
+ wholeMsg, reqPDU=reqPDU):
+ while wholeMsg:
+ rspMsg, wholeMsg = decoder.decode(wholeMsg, asn1Spec=pMod.Message())
+ rspPDU = pMod.apiMessage.getPDU(rspMsg)
+ # Match response to request
+ if pMod.apiPDU.getRequestID(reqPDU)==pMod.apiPDU.getRequestID(rspPDU):
+ # Check for SNMP errors reported
+ errorStatus = pMod.apiPDU.getErrorStatus(rspPDU)
+ if errorStatus:
+ print(errorStatus.prettyPrint())
+ else:
+ for oid, val in pMod.apiPDU.getVarBinds(rspPDU):
+ print('%s = %s' % (oid.prettyPrint(), val.prettyPrint()))
+ transportDispatcher.jobFinished(1)
+ return wholeMsg
+
+transportDispatcher = AsynsockDispatcher()
+
+transportDispatcher.registerRecvCbFun(cbRecvFun)
+transportDispatcher.registerTimerCbFun(cbTimerFun)
+
+# Initialize UDP/IPv4 transport
+udpSocketTransport = udp.UdpSocketTransport().openClientMode()
+
+# Use sendmsg()/recvmsg() for socket communication (required for
+# IP source spoofing functionality)
+udpSocketTransport.enablePktInfo()
+
+# Enable IP source spoofing (requires root privileges)
+udpSocketTransport.enableTransparent()
+
+transportDispatcher.registerTransport(udp.domainName, udpSocketTransport)
+
+# Pass message to dispatcher
+transportDispatcher.sendMessage(
+ encoder.encode(reqMsg), udp.domainName, transportAddress
+)
+
+# We might never receive any response as we sent request with fake source IP
+transportDispatcher.jobStarted(1)
+
+# Dispatcher will finish as all jobs counter reaches zero
+try:
+ transportDispatcher.runDispatcher()
+except StopWaiting:
+ transportDispatcher.closeDispatcher()
+else:
+ raise
diff --git a/examples/v3arch/agent/cmdrsp/v3-observe-request-processing.py b/examples/v3arch/agent/cmdrsp/v3-observe-request-processing.py
index 98ec64a..842f1d7 100644
--- a/examples/v3arch/agent/cmdrsp/v3-observe-request-processing.py
+++ b/examples/v3arch/agent/cmdrsp/v3-observe-request-processing.py
@@ -32,7 +32,7 @@ snmpEngine = engine.SnmpEngine()
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('* transportAddress: %s (local %s)' % ('@'.join([str(x) for x in variables['transportAddress']]), '@'.join([str(x) for x in variables['transportAddress'].getLocalAddress()])))
print('* securityModel: %s' % variables['securityModel'])
print('* securityName: %s' % variables['securityName'])
print('* securityLevel: %s' % variables['securityLevel'])
diff --git a/examples/v3arch/agent/cmdrsp/v3-preserve-original-destination-address.py b/examples/v3arch/agent/cmdrsp/v3-preserve-original-destination-address.py
new file mode 100644
index 0000000..a371cd6
--- /dev/null
+++ b/examples/v3arch/agent/cmdrsp/v3-preserve-original-destination-address.py
@@ -0,0 +1,89 @@
+#
+# Command Responder
+#
+# Listen on all local IPv4 interfaces respond to SNMP GET/SET/GETNEXT/GETBULK
+# queries with the following options:
+#
+# * SNMPv3
+# * with USM user 'usr-md5-des', auth: MD5, priv DES
+# * allow access to SNMPv2-MIB objects (1.3.6.1.2.1)
+# * over IPv4/UDP, listening at 0.0.0.0:161
+# * preserve local IP address when responding (Python 3.3+ required)
+#
+# The following Net-SNMP's command will walk this Agent:
+#
+# $ snmpwalk -v3 -u usr-md5-des -l authPriv -A authkey1 -X privkey1 localhost .1.3.6
+#
+# In the situation when UDP responder receives a datagram targeted to
+# a secondary (AKA virtial) IP interface or a non-local IP interface
+# (e.g. routed through policy routing or iptables TPROXY facility),
+# OS stack will by default put primary local IP interface address into
+# the IP source field of the response IP packet. Such datagram may not
+# reach the sender as either the sender itself or a stateful firewall
+# somewhere in between would not be able to match response to original
+# request.
+#
+# The following script solves this problem by preserving original request
+# destination IP address and put it back into response IP packet's source
+# address field.
+#
+# To respond from a non-local (e.g. spoofed) IP address, uncomment the
+# .enableTransparent() method call and run this script as root.
+#
+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()
+
+# Transport setup
+
+# Initialize asyncore-based UDP/IPv4 transport
+udpSocketTransport = udp.UdpSocketTransport().openServerMode(('0.0.0.0', 161))
+
+# Use sendmsg()/recvmsg() for socket communication (used for preserving
+# original destination IP address when responding)
+udpSocketTransport.enablePktInfo()
+
+# Enable IP source spoofing (requires root privileges)
+# udpSocketTransport.enableTransparent()
+
+# Register this transport at SNMP Engine
+config.addTransport(
+ snmpEngine,
+ udp.domainName,
+ udpSocketTransport
+)
+
+# 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
diff --git a/examples/v3arch/manager/cmdgen/get-v2c-spoof-source-address.py b/examples/v3arch/manager/cmdgen/get-v2c-spoof-source-address.py
new file mode 100644
index 0000000..036cf3f
--- /dev/null
+++ b/examples/v3arch/manager/cmdgen/get-v2c-spoof-source-address.py
@@ -0,0 +1,99 @@
+#
+# GET Command Generator
+#
+# Send a SNMP GET request
+# with SNMPv2c, community 'public'
+# over IPv4/UDP
+# to an Agent at 195.218.195.228:161
+# from a non-local, spoofed IP 1.2.3.4 (root and Python 3.3+ required)
+# for an OID in tuple form
+#
+# This script performs similar to the following Net-SNMP command:
+#
+# $ snmpget -v2c -c public -ObentU 195.218.195.228 1.3.6.1.2.1.1.1.0
+#
+# But unlike the above command, this script issues SNMP request from
+# a non-default, non-local IP address.
+#
+# It is indeed possible to originate SNMP traffic from any valid local
+# IP addresses. It could be a secondary IP interface, for instance.
+# Superuser privileges are only required to send spoofed packets.
+# Alternatively, sending from local interface could also be achieved by
+# binding to it (via openClientMode() parameter).
+#
+#
+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()
+
+#
+# SNMPv1 setup
+#
+
+# SecurityName <-> CommunityName mapping
+config.addV1System(snmpEngine, 'my-area', 'public')
+
+# Specify security settings per SecurityName (SNMPv1 - 0, SNMPv2c - 1)
+config.addTargetParams(snmpEngine, 'my-creds', 'my-area', 'noAuthNoPriv', 0)
+
+#
+# Setup transport endpoint and bind it with security settings yielding
+# a target name
+#
+
+# Initialize asyncore-based UDP/IPv4 transport
+udpSocketTransport = udp.UdpSocketTransport().openClientMode()
+
+# Use sendmsg()/recvmsg() for socket communication (required for
+# IP source spoofing functionality)
+udpSocketTransport.enablePktInfo()
+
+# Enable IP source spoofing (requires root privileges)
+udpSocketTransport.enableTransparent()
+
+# Register this transport at SNMP Engine
+config.addTransport(
+ snmpEngine,
+ udp.domainName,
+ udpSocketTransport
+)
+
+# Configure destination IPv4 address as well as source IPv4 address
+config.addTargetAddr(
+ snmpEngine, 'my-router',
+ udp.domainName, ('195.218.195.228', 161),
+ 'my-creds',
+ sourceAddress=('1.2.3.4', 0)
+)
+
+# Error/response receiver
+def cbFun(snmpEngine, sendRequestHandle, errorIndication,
+ errorStatus, errorIndex, varBinds, cbCtx):
+ if errorIndication:
+ print(errorIndication)
+ # SNMPv1 response may contain noSuchName error *and* SNMPv2c exception,
+ # so we ignore noSuchName error here
+ elif errorStatus and errorStatus != 2:
+ 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().sendVarBinds(
+ snmpEngine,
+ 'my-router',
+ None, '', # contextEngineId, contextName
+ [ ((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()