summaryrefslogtreecommitdiff
path: root/examples/v3arch/agent/cmdrsp/v3-preserve-original-destination-address.py
blob: a371cd694dd07a9cd0da30688af7df644cae67c5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
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