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 that of 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 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 be 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 the entire responsibility of the application code.
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 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.
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@contex, 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.
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.0 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 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. SeeMIB Scoping in Master Agents for details.
The following tables show the end-to-end error translation performed by a master agent application.
Table 21–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 or truncated 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 21–5.
Table 21–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 |
(**) See Table 21–5.
Table 21–3 Error Translation From SNMPv1 Subagents to SNMPv1 Managers
PDU Type |
Error From SNMPv1 Subagents |
Error Sent to SNMPv1 Managers |
---|---|---|
get |
genErr |
genErr |
get |
noSuchName |
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 |
noSuchName |
noSuchName |
get-next |
tooBig |
handled by stack => resend or tooBig |
get-next |
any other error |
genErr |
(**) See Table 21–5.
Table 21–4 Error Translation from SNMPv2/v3 Subagents to SNMPv2/v3 Managers
PDU Type |
Error From SNMPv2/v3 Subagents |
Error Sent to SNMPv2/v3 Managers |
---|---|---|
get |
noError |
noError |
get |
tooBig |
handled by stack => resend or tooBig |
get |
any other error |
same error (if valid) or genErr |
set |
any error |
undoFailed (**) |
get-next/get-bulk |
noError |
noError |
get-next/get-bulk |
tooBig |
handled by stack => resend or tooBig or truncated response (GET-BULK) |
get-next/get-bulk |
any other error |
same error (if valid) or genErr |
(**) By default SnmpProxy sends the remote set request during the set() phase of the set operation. When an error occurs during the set() phase, undoFailed is returned to the manager because the atomicity is no longer guaranteed. Note that in the special case where an SnmpProxy is configured to perform the remote set request during the check() phase of the set operation, the following translation is applied for errors returned by the remote set request, even if the atomicity of the set request is broken, as shown in the following table:
Table 21–5 Error Translation When SnmpProxy Performs Remote set
Before Translation |
After Translation |
---|---|
v1 errorStatus |
v2/v3 errorStatus |
noError |
noError |
noSuchName |
noSuchName |
genErr |
genErr |
badValue |
wrongValue |
readOnly |
wrongValue |
v2/v3 errorStatus |
v1 errorStatus |
noError |
noError |
genErr |
genErr |
wrongValue, wrongEncoding, wrongLength, wrongType, inconsistentValue |
badValue |
noAccess, notWritable, noCreation, inconsistentName, authorizationError |
noSuchName |
resourceUnavailable, commitFailed, undoFailed, any other error |
genErr |
v1 errorStatus |
v1 errorStatus |
any error |
same error (if valid); genErr otherwise |
v2/v3 errorStatus |
v2/v3 errorStatus |
any error |
same error (if valid); genErr otherwise |
v1 errorStatus |
v2/v3 errorStatus |
noError |
noError |
noSuchName |
noSuchName |
genErr |
genErr |
badValue |
wrongValue |
readOnly |
wrongValue |
v2/v3 errorStatus |
v1 errorStatus |
noError |
noError |
genErr |
genErr |
wrongValue, wrongEncoding, wrongLength, wrongType, inconsistentValue |
badValue |
noAccess, notWritable, noCreation, inconsistentName, authorizationError |
noSuchName |
resourceUnavailable, commitFailed, undoFailed, any other error |
genErr |
v1 errorStatus |
v1 errorStatus |
any error |
same error (if valid); genErr otherwise |
v2/v3 errorStatus |
v2/v3 errorStatus |
any error |
same error (if valid); genErr otherwise |