Engine ID and receiving traps from Cisco routers with SNMP4J


I guess I am the lucky person to post first in the new forum!

I have written a sophisticated custom client with SNMP4J playing an important role.

It receives traps, if they come from a source, such a test trap, produced by SNMP4J, on another system.

Enter Cisco Router, configured for authPriv, and authNoPriv…

tcpdump echoes the packets are received. SNMP4J code never acknowledges them!

Hunch is that although I have a UsmUser on the trap receiver class/method code for authPriv, and then reduced it to authNoPriv, both MD5, it’s trying to correlate an Engine ID somehow. Maybe it doesn’t exist in the Trap itself, maybe the router isn’t configured for an Engine ID.

The telecommunications company isn’t going to just let me start poking around in the router equipment, but when I used snmpwalk with authPriv, the snmpview worked and I received all OID’s. No engine ID in the output generated from the snmpwalk.

Is there a way in SNMP4J without Log4J or other loggers, to output to my own log file, that the trap was received, but ignored due to bad credentials or bad engineID? Catch an exception maybe?

I have a hit a total work stoppage after investing 3 months of programming full time man hours in SNMP4J, and team is about to throw it all away, because of this lack of really understanding how it all works.

Thank you for help!


A warm welcome to the AGENTPP forum - you are indeed the first poster, congratulations!

Regarding your questions: Are you sending TRAPS (notifications) or INFORM requests?

  1. For TRAPS:
    To successfully receive TRAPS with SNMPv3 User based Security Model (USM) and authNoPriv or authPriv security level enabled, you need to add the USM user with the engine ID of the notification generator (the agent) to you local USM.
    This is different to adding users for sending requests to that agent, because in that cause you can leave the engine ID unset and SNMP4J will do the authoritative engine ID lookup for you by default, when the first request is being sent to the agent.
    For notifications the engine ID lookup works on-the-fly.
  2. For INFORMS:
    When sending INFORMs from the agent to the SNMP4J manager (notification receiver), then the SNMP4J notification receiver is authoritative. Thus, the agent (command responder) needs to use the engine ID of the manager (notification receiver) to send the SNMPv3 INFORM request.
    In this case, is it absolutely necessary that you add the USM user the agent is using as localised user with the engineID of the agent to the SNMP4J manager!
    Otherwise the manager will not be able to handle the INFORM request.

In any case, you have the following options to get more information about that on the SNMP4J manager side:

  • Call SNMP4JSettings.setForwardRuntimeExceptions(true) to get exceptions forwarded to your code.
  • On the TransportMapping you can add a listener with addTransportListener method. In that listener you will be able to log any communication that happens on that transport.
  • There are other more advanced (low level) hooks that can be used. I could provide examples, if I got more details, what we are looking for, i.e. TRAP vs INFORM.

Best regards,

This was very helpful, thank you Frank!

I would like to accept both INFORMS and TRAP(s). I think we began with traps and it sounds like I’ll need the engine ID of the Cisco router in advance to be able to process any traps.

I see that requirement with net-snmp package as well.

The dummy or mock trap I use for unit testing, does set the engine ID, per below:

ScopedPDU trapPduToBeSent = new ScopedPDU();
trapPduToBeSent.setContextEngineID( this.localEngineId );
trapPduToBeSent.setRequestID(new Integer32(1234)); // <-- fix to vary
trapPduToBeSent.add(new VariableBinding(SnmpConstants.sysUpTime));
trapPduToBeSent.add(new VariableBinding(SnmpConstants.snmpTrapOID, SnmpConstants.linkDown));
trapPduToBeSent.add(new VariableBinding(new OID(oid), new OctetString(pduMessage)));

I also prepare to send the mock/fake trap with the following:

this.localEngineId = new OctetString(MPv3.createLocalEngineID());

this.userBasedSecurityModel = new USM(SecurityProtocols.getInstance(), this.localEngineId, 0);

UsmUser userConnectingAs = new UsmUser(securityNameOctet, authProtocol, authPassOctet, privProtocol, privPassOctet);

snmpClientFactory.getUSM().addUser(securityNameOctet, userConnectingAs);

SecurityModels.getInstance().addSecurityModel(new TSM(this.localEngineId, false));

snmpClientFactory.setLocalEngine(localEngineId.toByteArray(), 0, 0);

Obviously I cannot do anything on the SNMP client listening for traps, if I do not know the engine ID of the device where TRAP originated. I therefore only declare the user that is acceptable credentials to acknowledge:

new OctetString(authorizedUser),
new UsmUser(new OctetString(authorizedUser),AuthMD5.ID,
new OctetString(authPassPhrase), PrivAES128.ID,
new OctetString(privacyPassPhrase)));

I can certainly try to use addTransportListener and see what that does to help.

Thanks again!

The code is ok in general, but you are adding a TSM instance but not USM. I think this is either a typo or real bug.

Everything else is OK, as I wrote before, SNMP4J will automatically localise the user you add in the client (notification receiver). Thus, there is no need to discover either the receiver engine ID nor the sender engine ID.
Instead, on both sides (sender and receiver) the boolean flag USM.setEngineDiscoveryEnabled(true) should to be set. That automatic discovery is enabled by default. Therefore the root cause is something else. A very common error is a non-unique engine ID.

Within a domain, engine IDs must be unique! Otherwise SNMPv3 will not work for those devices with matching engine ID!

I fixed the TSM bug, that was a good catch. Somehow that made it in there from one of many examples I have been using to learn SNMP4J. That bug was in my mock trap sender class.

I will try to send a trap from another, non SNMP4J origin.

Now I am battling another issue, ScopedPDU.GET’s with Version 3 to same Cisco routers. Same credentials and encryption, I can use SNMP Walk just fine, but with SNMP 4J, never have the processor events run responselistener.

I think it all has to do with the network and hardware I am using. Earlier you mentioned low level hooks that exists. I was curious what are these?

Thank you!

OK, now we are probably getting to the CISCO specific issue. Cisco decided to use their own AES encryption mechanism for AES192 and AES256. If you are trying to use one of them, you need to use PrivAES192With3DESKeyExtension and PrivAES256With3DESKeyExtension from the org.snmp4j.security.nonstandard package instead of the standard ones activated by default in SNMP4J.

The standard ones, however, were only defined in a IETF draft which was then cancelled because the necessary key length for AES192/256 could not be provided by SHA-1 at that time. The situation is now a bit different with SHA-256 and SHA-384.

Cisco used a key extension mechanism from 3DES. That is why those AES variants are not compatible.

If you are searching for callbacks for warning/error conditions that are SNMP protocol related (i.e. standardised) in SNMP4J, then write your own CounterSupport instance and add it to the classes you want to get called when such an event occurs.
In USM use the method setCounterSupport for example.

You won’t get much context information, but the OID that describes the error condition. If you then need more context, the stack-trace might help (in conjunction with the debugger).

Yeah I’ll have to enable the remote debugger, the subnet I’m testing with is in a datacenter, so I have to deploy code to test/debug. Good to know there are non compliant aspects, I think were using AES 128, but to be honest, when I use snmpwalk tool, I only specify AES. I have to test trap receipt with some other tool/platform, to ensure its not a lingering issues outside of the code that’s been developed.

There’s no real way to attach it here, so let’s see what the debugger clues me in to.

Thanks again for the help.