20.6 Protocol Translation
As explained in the previous section, both the SnmpProxy and SnmpTrapForwarder are compliant
with RFC 2576. However,
RFC 2576 does not simply describe protocol conversion, but rather describes
how protocol conversion is performed in the scope of a proxy forwarding application.
In a master agent application, the question is more complex because we want
the master agent to behave as a single agent responding with the same protocol
as is used by the manager. We also want to hide the presence of subagents
from the manager.
When looking from an end-to-end perspective, the master agent does not
strictly behave as described in RFC 2576. This is because the protocol translation
applied by the SnmpProxy is post-processed and aggregated
in the master agent with results coming from other SnmpMibAgents, such as SnmpProxy objects and mibgen generated local MIBs, each possibly translating from different
protocol versions.
The main differences, when looking from an end to end perspective, between
protocol translation in the SNMP master agent and RFC 2576 are the following:
When the incoming request is an SNMPv2 or SNMPv3 request,
the error codes returned are SNMPv2 error codes, when applicable, even if
the subagent from which the error originally came is a SNMPv1 agent.
When the incoming request is an SNMPv2 or SNMPv3 request, get, get-next and get-bulk
always transform global SNMPv1 errors into a varbind value of noSuchInstance, noSuchObject, or endOfMibView,
and skip to the next varbind; this is the same behavior as for an SNMPv2/v3
agent.
If an exception is raised or an error is returned by a subagent
during the set() phase of a set request,
then the error returned is always undoFailed. In fact,
there is no way to apply a two-phased check()/set() action to a remote SNMP agent because the SNMP protocol does
not have any CHECK primitive. Consequently, the atomicity of a set request is only guaranteed when all the OIDs in the request are
processed by the same SNMP entity. See 20.6.3 Atomicity and Error Handling for details.
When creating an SnmpProxy object, the application
must decide whether the remote request is sent during the check() phase or the set() phase of the set operation. If the operation is performed during the check() phase, the error returned to the manager is that which is
emitted by the subagent. However, the atomicity is no longer guaranteed, because
the check() phase can now actually modify some of the sub
agent MIBs. According to SNMPv2, the error returned in that case should be undoFailed, but in fact it is whatever error was returned or raised
by the SnmpMibAgent. If the operation is performed
during the set() phase, the error returned is always undoFailed for SNMPv2 or SNMPv3, or genError
for SNMPv1. This will hide the actual error that was returned by the subagent.
Whether one method or the other is used is entirely the responsibility of
the application code.
20.6.1 SNMP Proxy Translation
The SnmpProxy object implements the
following translation:
SnmpProxy forwards the request in the
protocol specified by the subagent; a get request makes
the SnmpProxy object send a get
to the subagent, a set request makes the SnmpProxy object send a set to the subagent, and so
on.
If the incoming request is a get-bulk request,
and the subagent is an SNMPv1 subagent, SnmpProxy forwards
the request as a series of get-next requests.
If the incoming request is an SNMPv2 or SNMPv3 get or get-next request, and the subagent is an
SNMPv1 subagent, SnmpProxy subtracts the varbinds for
which an error was returned, and resends the request until either a value
or an error is obtained for each varbind. The requests are sent to emulate
SNMPv2 or SNMPv3 get and get-next behavior.
If the incoming request is a set, then
two types of behavior are possible, depending on how the SnmpProxy object is configured:
The set request can be performed during
the check phase, in which case nothing is done during
the set
The set request can be performed during
the set phase, in which case nothing is performed during
the check; this is the default behavior
Whichever solution is chosen has different impacts on the atomicity
of set requests. See 20.6.3 Atomicity and Error Handling for
details.
If the incoming request contains SNMPv1 or SNMPv2 community
strings, they must be translated using the translateParameters() method. The translateParameters() method
reuses the information contained in the SnmpMibRequest
PDU to construct new SnmpParams.
You can overload this method to change its behavior.
When an SNMPv1 or SNMPv2 request is received, the translateParameters() method is called. The received community
string is reused in the forwarded request. If an SNMPv3 request is received,
the community string is forwarded as public in a get request and as private in a set request.
20.6.2 SNMP USM Proxy Translation
The SnmpUsmProxy object
inherits from SnmpProxy. When a request is received, the translateParameters() method is called.
If the request is an SNMPv1 or SNMPv2 request, the security level is
set to noAuthNoPriv. If the received community string
is of the form community@context, the context name is set
to the received context value. If it is in any other form it is null.
If the request is an SNMPv3 request, the received context, security
level, and other values are reused.
20.6.3 Atomicity and Error Handling
As stated in the previous section, the atomicity of set requests is no longer guaranteed when remote MIBs are involved.
Although some strategies exist that try to offer a best-effort regarding the
atomicity of set requests, there is no generic mechanism
that is guaranteed to work in a master agent application. The best that can
be done in a generic toolkit is to identify those cases where atomicity might
have been broken, and to inform the manager of that situation. Java DMK 5.1
handles this by responding with undoFailed when an
error occurs during the set() phase of a set
request. In its default configuration, when an SNMPv2 set
request is received, Java DMK guarantees that undoFailed is sent when atomicity might have been broken. This no longer
holds if the application code configures the SnmpProxy
object to send the remote SNMP set request during the check() phase of the set operation.
Some toolkits attempt to implement atomicity by:
Getting the current values of all variables included in the set.
Performing the set.
Reverting to the old values by sending a second set with the values obtained in 1 above, or the values obtained
in 2 if no error is returned.
Although this might seem more satisfactory it is not guaranteed to work.
Depending on the semantics of the variables involved in the set,
several things might happen:
If the transition from value#1 to value#2 is valid, there is no guarantee that the transition from value#2 to value#1 will be accepted by the agent;
reverting to the old value might not be possible
Setting an object to a specific value might already have had
unrecoverable effects on the agent; for example removing a resource, destroying
a row, and so on
Some objects might be of type Test-And-Increment which means that getting their value in 1 above already modifies
them. Trying to perform a set on them will probably generate
an error as these objects are usually read-only. In the end, the whole process
will return an error while still having modified the objects as a side effect
Even if no generic mechanism is supported, the Java DMK can
still be customized to implement any specific behavior. The SnmpUserDataFactory makes it possible to allocate and release contextual data about
requests, which can be used to implement transactional behavior. By subclassing SnmpProxy, and mibgen-generated metadata
classes, any kind of specific transactional behavior can be implemented. However,
no generic solution exists, and if transactional behavior can be implemented,
it is specific to the semantics of the objects contained in the application
and subagent MIBs.
Another special case is when a subagent is entirely responsible for
a given context scope. In that case, the atomicity of set
requests can still be achieved by performing the remote SNMP set during the check() phase. See 20.4 MIB Scoping in Master Agents
for details.
The following tables show the end-to-end error translation performed
by a master agent application.
Table 20-1 Error Translation from SNMPv1 Subagents to SNMPv2/v3 Managers
PDU Type | Error From
SNMPv1 Subagents | Error Sent to SNMPv2/v3 Managers |
get | genErr | genErr |
get | noSuchName | noError => noSuchInstance
in varbind |
get | tooBig | handled by stack =>resend or tooBig |
get | any other
error | genErr |
set | any error | undoFailed(**) |
get-next/get-bulk | genErr(*) | genErr |
get-next/get-bulk | noSuchName | noError
=> skip to next SnmpMibAgent or endOfMibView
if none |
get-next/get-bulk | tooBig | handled by stack resend or tooBig cated response
(get-bulk) |
get-next/get-bulk | any other error(*) | genErr |
(*) The SnmpProxy objects can be configured to
hide such errors. In this case the master agent skips to the next SnmpMibAgent. This behavior can be very useful when dealing with
faulty legacy agents.
(**) See Table 20-5.
Table 20-2 Error Translation from SNMPv2/v3 Subagents to v1 Managers
PDU Type | Error From SNMPv2/v3 Subagents | Error Sent to SNMPv1 Managers |
get | genErr | genErr |
get | noError => noSuchInstance
in varbind | noSuchName |
get | noError => noSuchInstance
in varbind | noSuchName |
get | tooBig | handled by stack => resend or tooBig |
get | any other error | genErr |
set | any error | genErr (**) - undoFailed is translated into genErr |
get-next | genErr | genErr |
get-next | noError => endOfMibView in varbind | noSuchName |
get-next | tooBig | handled by stack => resend or tooBig |
get-next | any other error | genErr |
|