Agent not returning correct OID value when using SNMP4j (org.snmp4j) v3 and user authentication?

1.1k views Asked by At

I have written an SNMP agent and registered a managed object (created/set a value of an MIB OID). When I retrieve this value using SNMPv2c, the value is returned correctly - the PDU from ResponseEvent.getResponse has type=GET and the variable bindings have expected data - correct OID etc. When I retrieve this value using SNMPv3 and user authentication, the value is not returned correctly - the PDU from ResponseEvent.getResponse has type=REPORT and the variable bindings have a different OID from that in the request - from what I've read so far this indicates a config/authentication error. Below is sample code (snippets) used for client & agent - please can you inform me how to create agent & client - where I'm going wrong?

// TestSNMPAgent:
public class TestSNMPAgent {

    private OID sysDescr = new OID("1.3.6.1.2.1.1.1.0");
    ...
    public static void main(String[] args) throws IOException {
        TestSNMPAgent agent = new TestSNMPAgent();
        agent.init("0.0.0.0/4071");

    private void init(String agentIp) throws IOException {

        agent = new SNMPAgent(agentIp);

        agent.start();

        agent.unregisterManagedObject(agent.getSnmpv2MIB());

        agent.registerManagedObject(new MOScalar(oid,
            MOAccessImpl.ACCESS_READ_WRITE,
            getVariable(value),sysDescr,
            "1")));
        ...
    }

}

// SNMPAgent:
public class SNMPAgent extends BaseAgent {
...
    @Override
    protected void addUsmUser(USM arg0) {
       UsmUser user = new UsmUser(new OctetString("SHADES"), 
       AuthSHA.ID, 
       new OctetString("SHADESAuthPassword"), 
       PrivDES.ID, 
       new OctetString("SHADESPrivPassword"));
    }

    @Override
    protected void addViews(VacmMIB vacm) {
        vacm.addGroup(SecurityModel.SECURITY_MODEL_USM, 
                  new OctetString("SHADES"), 
                  new OctetString("v3group"), 
                  StorageType.nonVolatile); 

        vacm.addAccess(new OctetString("v3group"), new OctetString(), 
                   SecurityModel.SECURITY_MODEL_USM, 
                   SecurityLevel.NOAUTH_NOPRIV, VacmMIB.vacmExactMatch, 
                   new OctetString("fullReadView"), 
                   new OctetString("fullWriteView"), 
                   new OctetString("fullNotifyView"), 
                   StorageType.nonVolatile); 
    }

    public void registerManagedObject(ManagedObject mo) {
        try {
            server.register(mo, null);
        } catch (DuplicateRegistrationException ex) {
        throw new RuntimeException(ex);
    }
}

// TestSNMPMgr
public class TestSNMPMgr {

    public static void main(String[] args) throws IOException {

        TestSNMPMgr client = new TestSNMPMgr();
        client.init();
    }

    public void init() {
        SNMPMgr client = new SNMPMgr();
        client.start();
        // Get back Value which is set
        String value = client.getAsString(new OID("1.3.6.1.2.1.1.1.0"));
    }
}

// SNMPMgr
public class SNMPMgr {

    Snmp snmp = null;
    Address address = null;

    public SNMPMgr()
    {
        address = "1.3.6.1.2.1.1.1.0";
    }

    /**
     * Start the Snmp session. If you forget the listen() method you will not
     * get any answers because the communication is asynchronous
     * and the listen() method listens for answers.
     * @throws IOException
    */
    public void start() throws IOException {
       address = GenericAddress.parse("udp:127.0.0.1/4701");
       TransportMapping transport = new DefaultUdpTransportMapping();
       snmp = new Snmp(transport);
       USM usm = new USM(SecurityProtocols.getInstance(),
                         new OctetString(MPv3.createLocalEngineID()), 0);
       SecurityModels.getInstance().addSecurityModel(usm);
       transport.listen();
    }

    public void end() {
        try {
            snmp.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * Method which takes a single OID and returns the response from the agent as a String.
     * @param oid
     * @return
     * @throws IOException
     */
    public String getAsString(OID oid) throws IOException {
        ResponseEvent event = get(new OID[] { oid });
        return event.getResponse().get(0).getVariable().toString();
    }


    public ResponseEvent get(OID oids[]) throws IOException {
       PDU pdu = new ScopedPDU();
       for (OID oid : oids) {
            pdu.add(new VariableBinding(oid));
       }

       pdu.setType(PDU.GET);

       // add user to the USM
       snmp.getUSM().addUser(new OctetString("SHADES"),
               new UsmUser(new OctetString("SHADES"),
               AuthSHA.ID, 
               new OctetString("SHADESAuthPassword"), 
               PrivDES.ID, 
               new OctetString("SHADESPrivPassword")));


       // send the PDU
       ResponseEvent event = snmp.send(pdu, getTarget(), null);

       if(event != null) {
           return event;
       }
       throw new RuntimeException("GET timed out");
    }

    /**
     * This method returns a Target, which contains information about
     * where the data should be fetched and how.
     * @return
     */
    private UserTarget getTarget() {
       UserTarget target = new UserTarget();
       target.setAddress(address);
       target.setRetries(1);
       target.setTimeout(5000);
       target.setVersion(SnmpConstants.version3);
       target.setSecurityLevel(SecurityLevel.NOAUTH_NOPRIV);
       target.setSecurityName(new OctetString("SHADES"));
       return target;
    }

}
1

There are 1 answers

0
Michael Kirkham On

The OID in the Report PDU should tell you what is happening. Under typical circumstances there will be one or two (or one of two) request/report exchanges to establish initial SNMPv3 communications between manager and agent (or, rather, non-authoritative and authoritative engines, respectively).

The first is typically a usmStatUnknownEngineIDs report that allows the manager to discover the agent's Engine ID (needed for key localization/etc.) and will happen if you don't specify the proper Engine ID in the initial request. The second/other happens if using auth/noPriv or auth/priv level security, and that is usmStatsNotInTimeWindows, which is sent if the request doesn't specify Engine Boots/Engine Time values within proper range of the agent's values. These values prevent message replay attacks by making requests no longer valid if they fall out of the time window, and the manager typically doesn't know what they are until it receives them from the agent by way of a Report PDU.

After the manager has the proper Engine ID, Boots, and Time, and has localized keys to the Engine ID if necessary, then the normal request/response exchange can proceed as expected. Some SNMP APIs will take care of this exchange for you so you just send your request and get the eventual result after the exchange. It would seem that SNMP4j doesn't and you may have to handle it yourself if it's one of these reports.

If it's not one of these reports, then you likely have a mismatch in configuration.