1"""
2Running at secondary network interface
3++++++++++++++++++++++++++++++++++++++
4
5Listen on all local IPv4 interfaces respond to SNMP GET/SET/GETNEXT/GETBULK
6queries with the following options:
7
8* SNMPv3
9* with USM user 'usr-md5-des', auth: MD5, priv DES
10* allow access to SNMPv2-MIB objects (1.3.6.1.2.1)
11* over IPv4/UDP, listening at 0.0.0.0:161
12* preserve local IP address when responding (Python 3.3+ required)
13
14The following Net-SNMP command will walk this Agent:
15
16| $ snmpwalk -v3 -u usr-md5-des -l authPriv -A authkey1 -X privkey1 localhost .1.3.6
17
18In the situation when UDP responder receives a datagram targeted to
19a secondary (AKA virtial) IP interface or a non-local IP interface
20(e.g. routed through policy routing or iptables TPROXY facility),
21OS stack will by default put primary local IP interface address into
22the IP source field of the response IP packet. Such datagram may not
23reach the sender as either the sender itself or a stateful firewall
24somewhere in between would not be able to match response to original
25request.
26
27The following script solves this problem by preserving original request
28destination IP address and put it back into response IP packet's source
29address field.
30
31To respond from a non-local (e.g. spoofed) IP address, uncomment the
32.enableTransparent() method call and run this script as root.
33
34"""#
35from pysnmp.entity import engine, config
36from pysnmp.entity.rfc3413 import cmdrsp, context
37from pysnmp.carrier.asyncore.dgram import udp
38
39# Create SNMP engine
40snmpEngine = engine.SnmpEngine()
41
42# Transport setup
43
44# Initialize asyncore-based UDP/IPv4 transport
45udpSocketTransport = udp.UdpSocketTransport().openServerMode(('0.0.0.0', 161))
46
47# Use sendmsg()/recvmsg() for socket communication (used for preserving
48# original destination IP address when responding)
49udpSocketTransport.enablePktInfo()
50
51# Enable IP source spoofing (requires root privileges)
52# udpSocketTransport.enableTransparent()
53
54# Register this transport at SNMP Engine
55config.addTransport(
56    snmpEngine,
57    udp.domainName,
58    udpSocketTransport
59)
60
61# SNMPv3/USM setup
62
63# user: usr-md5-des, auth: MD5, priv DES
64config.addV3User(
65    snmpEngine, 'usr-md5-des',
66    config.usmHMACMD5AuthProtocol, 'authkey1',
67    config.usmDESPrivProtocol, 'privkey1'
68)
69
70# Allow full MIB access for each user at VACM
71config.addVacmUser(snmpEngine, 3, 'usr-md5-des', 'authPriv', (1, 3, 6, 1, 2, 1), (1, 3, 6, 1, 2, 1))
72
73# Get default SNMP context this SNMP engine serves
74snmpContext = context.SnmpContext(snmpEngine)
75
76# Register SNMP Applications at the SNMP engine for particular SNMP context
77cmdrsp.GetCommandResponder(snmpEngine, snmpContext)
78cmdrsp.SetCommandResponder(snmpEngine, snmpContext)
79cmdrsp.NextCommandResponder(snmpEngine, snmpContext)
80cmdrsp.BulkCommandResponder(snmpEngine, snmpContext)
81
82# Register an imaginary never-ending job to keep I/O dispatcher running forever
83snmpEngine.transportDispatcher.jobStarted(1)
84
85# Run I/O dispatcher which would receive queries and send responses
86try:
87    snmpEngine.transportDispatcher.runDispatcher()
88except:
89    snmpEngine.observer.unregisterObserver()
90    snmpEngine.transportDispatcher.closeDispatcher()
91    raise
92