""" Running at secondary network interface ++++++++++++++++++++++++++++++++++++++ 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 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.asyncore.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