# # 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.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