SnapshotAgent cannot simulate LLDP-MIB

Hello Frank,
recently I’ve been experimenting with org.snmp4j.agent.test.SnapshotAgent.
I read an snmp walk output, create VariableBindings from them and put them into a binary file, so that SnapshotAgent reads the file and simulates the contents.

I have found that it cannot simulate oids that are not started with .1.3.6.1. I needed to simulate variables from the LLDP-MIB (.1.0.8802). I think the second sub-id, which is zero is the root of the issue. These seemed to be the most relevant log messages:
{noformat}
2024-01-19 17:38:10.169 INFO test.SnapshotAgent(118) [main]: Identified the following sub-tree candidates: {1=1, 1.0.8802.1.1.1.1.1=1.0.8802.1.1.1.1.1, 1.0.8802.1.1.1.1.1.2.1.2=1.0.8802.1.1.1.1.1.2.1.2, 1.3.6.1.2.1.1=1.3.6.1.2.1.1}
2024-01-19 17:38:10.169 INFO test.SnapshotAgent(118) [main]: Identified the following sub-trees {}
{noformat}
Please kindly check the SnapshotAgent.registerManagedObjects() method and the sub-tree candidate algorithms there!

Hi kovacsz,

Thank you reporting this issue! It is indeed a bug in SnapshotAgent.registerManagedObjects() which occurs if there are objects registered starting with “1.1.and1.0.”. I am going to post a fix here soon…

Best regards,
Frank

1 Like

The following code change for SnapshotAgent.java fixes the issue:

static SortedMap<OID, OID> getSubtreeRoots(List<VariableBinding> l) {
    SortedMap<OID, OID> roots = new TreeMap<>();
    for (VariableBinding vb : l) {
        if (roots.isEmpty()) {
            OID rootCandidate = vb.getOid().trim();
            if (rootCandidate.size() >= 2) {
                roots.put(rootCandidate, rootCandidate);
            }
        }
        else {
            boolean rootFound = false;
            for (OID rootCandidate : roots.keySet()) {
                if (vb.getOid().startsWith(rootCandidate)) {
                    rootFound = true;
                    break;
                }
                else {
                    OID newRootCandidate = vb.getOid().trim();
                    if (rootCandidate.startsWith(newRootCandidate)) {
                        roots.remove(rootCandidate);
                        roots.put(newRootCandidate, newRootCandidate);
                        rootFound= true;
                    }
                }
            }
            if (!rootFound) {
                OID rootCandidate = vb.getOid().trim();
                if (rootCandidate.size() >= 2) {
                    roots.put(rootCandidate, rootCandidate);
                }
            }
        }
    }
    logger.info("Identified the following sub-tree candidates: " + roots);
    SortedMap<OID, OID> rootsCopy = new TreeMap<>();
    for (OID k : roots.keySet()) {
        if (k.size() > 1) {
            OID sk = new OID(k.getValue(), 0, k.size() - 1);
            while ((sk.size() > 0) && (roots.get(sk) == null)) {
                sk.trim(1);
            }
            if (sk.size() == 0) {
                rootsCopy.put(k, k);
            }
        }
    }
    return rootsCopy;
}

Hello Frank,

I have tested your fix, but I still had issues. Please check this short class. This class is overriding SnapshotAgent.main, and creates a snapshot of two variable bindings.
According to the logs the .1.0.8802 VB was loaded, but I could not query the .1.0.8802 OID from the SnapshotAgent. The system name can be queried with Net::Snmp snmpwalk command.
Kind regards,
Zoltan

public class CustomSnapshotAgent extends SnapshotAgent{
    private static final Logger logger = LogManager.getLogger();
    public CustomSnapshotAgent(File bootCounterFile, File configFile, File snapshot) throws IOException {
        super(bootCounterFile, configFile, snapshot);
    }

    public static void main(String[] args) {
        try{
            LogFactory.setLogFactory(new Log4jLogFactory());
            File binarySnapshot = File.createTempFile( "snmpwalk", ".snapshot");
            try(
                    FileOutputStream fos = new FileOutputStream(binarySnapshot);
                    ObjectOutputStream oos = new ObjectOutputStream(fos);
                ){

                List<VariableBinding> varbinds = List.of(
                        new VariableBinding(new OID(".1.3.6.1.2.1.1.5.0"), new OctetString("hostname")),
                        new VariableBinding(new OID(".1.0.8802.1.1.1.1.2.1.0"), new Gauge32(1)));
                oos.writeObject(varbinds);
            }
            CustomSnapshotAgent testAgent1 =
                    new CustomSnapshotAgent(new File("SNMP4JSnapshotAgentBC.cfg"),
                            new File("SNMP4JSnapshotAgentConfig.cfg"),
                            binarySnapshot);
            testAgent1.address = "0.0.0.0/161";
            testAgent1.init();
            testAgent1.loadConfig(ImportMode.RESTORE_CHANGES);
            testAgent1.addShutdownHook();
            testAgent1.getServer().addContext(new OctetString("public"));
            testAgent1.finishInit();
            testAgent1.run();
            testAgent1.sendColdStartNotification();
            Thread.sleep(3600 * 1000);
            testAgent1.stop();
        }catch(Exception ex){
            logger.error(ex.getMessage(), ex);
        }
    }
}

First, please make sure that your OIDs, do not start with “.”. That is a non-standard syntax used by NET-SNMP. It is not supported in SNMP4J.

What is the SNMP4J-Agent DEBUG log when you do a GETNEXT on “1.0.8802”?

I have removed the leading dots from the OIDs, so

List<VariableBinding> varbinds = List.of(
    new VariableBinding(new OID("1.3.6.1.2.1.1.5.0"), new OctetString("hostname")),
    new VariableBinding(new OID("1.0.8802.1.1.1.1.2.1.0"), new Gauge32(1)));
oos.writeObject(varbinds);

Testing the behavior:
snmpgetnext -v 2c -c public 127.0.0.1 1.0.8802
SNMPv2-MIB::sysName.0 = STRING: hostname

2024-01-29 21:39:58.690 DEBUG transport.DefaultUdpTransportMapping(59) [DefaultUDPTransportMapping_0.0.0.0/161]: Received message from /127.0.0.1/59926 with length 36: 30:22:02:01:01:04:06:70:75:62:6c:69:63:a1:15:02:02:7e:bf:02:01:00:02:01:00:30:09:30:07:06:03:28:c4:62:05:00
2024-01-29 21:39:58.690 DEBUG snmp4j.Snmp(59) [DefaultUDPTransportMapping_0.0.0.0/161]: Fire process PDU event: CommandResponderEvent[securityModel=2, securityLevel=1, maxSizeResponsePDU=65512, pduHandle=PduHandle[32447], stateReference=StateReference[msgID=0,pduHandle=PduHandle[32447],securityEngineID=null,securityModel=null,securityName=public,securityLevel=1,contextEngineID=null,contextName=null,retryMsgIDs=null], pdu=GETNEXT[requestID=32447, errorStatus=Success(0), errorIndex=0, VBS[1.0.8802 = Null]], messageProcessingModel=1, securityName=public, processed=false, peerAddress=127.0.0.1/59926, transportMapping=org.snmp4j.transport.DefaultUdpTransportMapping@78f5c518, tmStateReference=TransportStateReference[transport=org.snmp4j.transport.DefaultUdpTransportMapping@78f5c518, address=0.0.0.0/161, securityName=null, requestedSecurityLevel=undefined, transportSecurityLevel=undefined, sameSecurity=false, sessionID=java.net.DatagramSocket@d3d3969, target=null]]
2024-01-29 21:39:58.690 DEBUG snmp.SnmpCommunityMIB(59) [DefaultUDPTransportMapping_0.0.0.0/161]: Looking up coexistence info for 'public'
2024-01-29 21:39:58.690 DEBUG snmp.SnmpCommunityMIB(59) [DefaultUDPTransportMapping_0.0.0.0/161]: Found coexistence info for 'public'=CoexistenceInfo[securityName=cpublic,contextEngineID=80:00:13:70:01:64:40:65:11:04:63:06:50,contextName=,transportTag=]
2024-01-29 21:39:58.690 DEBUG snmp.SnmpCommunityMIB(59) [DefaultUDPTransportMapping_0.0.0.0/161]: Address 127.0.0.1/59926 passes filter, because source address filtering is disabled
2024-01-29 21:39:58.690 DEBUG request.SnmpRequest(59) [RequestPool.0]: Limiting GETBULK maxRepetitions received as 0 to 0
2024-01-29 21:39:58.690 DEBUG snmp.VacmMIB(59) [RequestPool.0]: Found group name 'v1v2group' for secName 'cpublic' and secModel 2
2024-01-29 21:39:58.690 DEBUG snmp.VacmMIB(59) [RequestPool.0]: Got views [DefaultMOMutableRow2PC[index=9.118.49.118.50.103.114.111.117.112.0.0.1,values=[1, fullReadView, fullWriteView, fullNotifyView, 3, 1]] for group name 'v1v2group'
2024-01-29 21:39:58.690 DEBUG snmp.VacmMIB(59) [RequestPool.0]: Matching against access entry DefaultMOMutableRow2PC[index=9.118.49.118.50.103.114.111.117.112.0.0.1,values=[1, fullReadView, fullWriteView, fullNotifyView, 3, 1] with exactContextMatch=true, prefixMatch=false, matchSecModel=true and matchSecLevel=true
2024-01-29 21:39:58.690 DEBUG snmp.VacmMIB(59) [RequestPool.0]: Matching view found for group name 'v1v2group' is 'fullReadView'
2024-01-29 21:39:58.690 DEBUG request.SnmpRequest(59) [RequestPool.0]: Created subrequest 0 with scope org.snmp4j.agent.DefaultMOContextScope[context=,lowerBound=1.0.8802,lowerIncluded=false,upperBound=null,upperIncluded=false] from 1.0.8802 = Null
2024-01-29 21:39:58.690 DEBUG request.SnmpRequest(59) [RequestPool.0]: SnmpSubRequests initialized: [org.snmp4j.agent.request.SnmpRequest$SnmpSubRequest[scope=org.snmp4j.agent.DefaultMOContextScope[context=,lowerBound=1.0.8802,lowerIncluded=false,upperBound=null,upperIncluded=false],vb=1.0.8802 = Null,status=RequestStatus{processed=false, phaseComplete=false, errorStatus=0},query=null,index=0,targetMO=null,lookupEvent=null,maxRepetitionsWithLimit=0]]
2024-01-29 21:39:58.690 DEBUG snmp.VacmMIB(59) [RequestPool.0]: Access allowed for view 'fullReadView' by subtree 1.3 for OID 1.3.6.1.2.1.1.5.0 and mask 
2024-01-29 21:39:58.690 DEBUG agent.CommandProcessor(59) [RequestPool.0]: Processing NEXT query org.snmp4j.agent.CommandProcessor$VACMQuery[]=1.0.8802< x <null[viewName=fullReadView] with StaticMOGroup[root=1.3.6.1.2.1.1.5,vbs={1.3.6.1.2.1.1.5.0=hostname}] sub-request with index 0
2024-01-29 21:39:58.690 DEBUG transport.DefaultUdpTransportMapping(59) [RequestPool.0]: Sending message to 127.0.0.1/59926 from 0.0.0.0/161 with length 49: 30:2f:02:01:01:04:06:70:75:62:6c:69:63:a2:22:02:02:7e:bf:02:01:00:02:01:00:30:16:30:14:06:08:2b:06:01:02:01:01:05:00:04:08:68:6f:73:74:6e:61:6d:65
2024-01-29 21:39:58.690 DEBUG transport.DefaultUdpTransportMapping(59) [RequestPool.0]: Sending packet to 127.0.0.1/59926

With the community you are using, you can only access sub-tree 1.3.x but not 1.0.x. You need to change your VACM configuration of the snapshot agent.

The view configuration did the trick. Thanks for your help!
Kind regards,
Zoltan

snmpwalk -v 2c -c public -On 127.0.0.1 1
.1.0.8802.1.1.1.1.2.1.0 = Gauge32: 1
.1.3.6.1.2.1.1.5.0 = STRING: hostname
.1.3.6.1.4.1.4976.10.1.1.1.1.1.1.0 = STRING: "org.snmp4j.log.Log4jLogFactory@78a773fd"
.1.3.6.1.4.1.4976.10.1.1.1.1.1.2.0 = STRING: "org.snmp4j.log.Log4jLogFactory"
...