1"""
2SNMPv2c-to-SNMPv3 conversion
3++++++++++++++++++++++++++++
4
5Act as a local SNMPv1/v2c Agent, relay messages to distant SNMPv3 Agent:
6* over IPv4/UDP
7* with local SNMPv2c community 'public'
8* local Agent listening at 127.0.0.1:161
9* remote SNMPv3 user usr-md5-none, MD5 auth and no privacy protocols
10* remote Agent listening at 104.236.166.95:161
11
12This script can be queried with the following Net-SNMP command:
13
14| $ snmpget -v2c -c public 127.0.0.1:161 1.3.6.1.2.1.1.1.0
15
16due to proxy, it is equivalent to
17
18| $ snmpget -v3 -l authNoPriv -u usr-md5-none -A authkey1 -ObentU 104.236.166.95:161  1.3.6.1.2.1.1.1.0
19
20Warning: for production operation you would need to modify this script
21so that it will re-map possible duplicate request-ID values, coming in
22initial request PDUs from different Managers, into unique values to
23avoid sending duplicate request-IDs to Agents.
24
25"""#
26from pysnmp.carrier.asyncore.dgram import udp
27from pysnmp.entity import engine, config
28from pysnmp.entity.rfc3413 import cmdrsp, cmdgen, context
29from pysnmp.proto.api import v2c
30from pysnmp import error
31
32# Create SNMP engine with autogenernated engineID and pre-bound
33# to socket transport dispatcher
34snmpEngine = engine.SnmpEngine()
35
36#
37# Transport setup
38#
39
40# Agent section
41
42# UDP over IPv4
43config.addTransport(
44    snmpEngine,
45    udp.domainName + (1,),
46    udp.UdpTransport().openServerMode(('127.0.0.1', 161))
47)
48
49# Manager section
50
51# UDP over IPv4
52config.addTransport(
53    snmpEngine,
54    udp.domainName + (2,),
55    udp.UdpTransport().openClientMode()
56)
57
58#
59# SNMPv1/2c setup (Agent role)
60#
61
62# SecurityName <-> CommunityName mapping
63config.addV1System(snmpEngine, 'my-area', 'public')
64
65#
66# SNMPv3/USM setup (Manager role)
67#
68
69# user: usr-md5-none, auth: MD5, priv NONE
70config.addV3User(
71    snmpEngine, 'usr-md5-none', config.usmHMACMD5AuthProtocol, 'authkey1'
72)
73
74#
75# Transport target used by Manager
76#
77
78config.addTargetParams(
79    snmpEngine, 'distant-agent-auth', 'usr-md5-none', 'authNoPriv'
80)
81config.addTargetAddr(
82    snmpEngine, 'distant-agent',
83    udp.domainName + (2,), ('104.236.166.95', 161),
84    'distant-agent-auth', retryCount=0
85)
86
87# Default SNMP context
88config.addContext(snmpEngine, '')
89
90
91class CommandResponder(cmdrsp.CommandResponderBase):
92    cmdGenMap = {
93        v2c.GetRequestPDU.tagSet: cmdgen.GetCommandGenerator(),
94        v2c.SetRequestPDU.tagSet: cmdgen.SetCommandGenerator(),
95        v2c.GetNextRequestPDU.tagSet: cmdgen.NextCommandGeneratorSingleRun(),
96        v2c.GetBulkRequestPDU.tagSet: cmdgen.BulkCommandGeneratorSingleRun()
97    }
98    pduTypes = cmdGenMap.keys()  # This app will handle these PDUs
99
100    # SNMP request relay
101    def handleMgmtOperation(self, snmpEngine, stateReference, contextName,
102                            PDU, acInfo):
103        cbCtx = stateReference, PDU
104        contextEngineId = None  # address authoritative SNMP Engine
105        try:
106            self.cmdGenMap[PDU.tagSet].sendPdu(
107                snmpEngine, 'distant-agent',
108                contextEngineId, contextName,
109                PDU,
110                self.handleResponsePdu, cbCtx
111            )
112        except error.PySnmpError:
113            self.handleResponsePdu(
114                snmpEngine, stateReference, 'error', None, cbCtx
115            )
116
117    # SNMP response relay
118    # noinspection PyUnusedLocal
119    def handleResponsePdu(self, snmpEngine, sendRequestHandle,
120                          errorIndication, PDU, cbCtx):
121        stateReference, reqPDU = cbCtx
122
123        if errorIndication:
124            PDU = v2c.apiPDU.getResponse(reqPDU)
125            PDU.setErrorStatus(PDU, 5)
126
127        self.sendPdu(
128            snmpEngine, stateReference, PDU
129        )
130
131        self.releaseStateInformation(stateReference)
132
133
134CommandResponder(snmpEngine, context.SnmpContext(snmpEngine))
135
136snmpEngine.transportDispatcher.jobStarted(1)  # this job would never finish
137
138# Run I/O dispatcher which would receive queries and send responses
139try:
140    snmpEngine.transportDispatcher.runDispatcher()
141except:
142    snmpEngine.transportDispatcher.closeDispatcher()
143    raise
144