Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/foundation-2018' into release-23…
Browse files Browse the repository at this point in the history
….0.4
  • Loading branch information
Jesse White committed Mar 20, 2019
2 parents 1196381 + 6df4302 commit e7e0e60
Show file tree
Hide file tree
Showing 5 changed files with 376 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ public String toString() {
.append("finished?", isFinished())
.toString();
}
@Override

@Override
public ResponseProcessor buildNextPdu(PduBuilder pduBuilder) throws SnmpException {
if (pduBuilder.getMaxVarsPerPdu() < 1) {
throw new SnmpException("maxVarsPerPdu < 1");
Expand All @@ -98,6 +99,17 @@ public void processResponse(SnmpObjId responseObjId, SnmpValue val) {
}
LOG.debug("Processing varBind: {} = {}", responseObjId, val);

// We requested OIDs following the m_last
// If the response OID is not a successor of m_last, then we have received an invalid response
// and should stop processing
// See NMS-10621 for details
if (!responseObjId.isSuccessorOf(m_last)) {
LOG.info("Received varBind: {} = {} after requesting an OID following: {}. "
+ "The received varBind is not a successor! Marking tracker as finished.",
responseObjId, val, m_last);
setFinished(true);
return;
}

m_last = responseObjId;
if (m_base.isPrefixOf(responseObjId) && !m_base.equals(responseObjId)) {
Expand Down
13 changes: 13 additions & 0 deletions core/snmp/api/src/main/java/org/opennms/netmgt/snmp/SnmpObjId.java
Original file line number Diff line number Diff line change
Expand Up @@ -291,5 +291,18 @@ public SnmpObjId decrement() {
}
}

/**
* If requesting a GETNEXT on the given base OID, would the
* current OID be expected in a response?
*
* Returns <code>true</code> if this OID is a successor (greater than)
* the given OID, or <code>false</code> otherwise.
*
* @param base base oid against which to compare
* @return true if this OID is a successor of the "base" oid, false otherwise
*/
public boolean isSuccessorOf(SnmpObjId base) {
return compareTo(base) > 0;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*******************************************************************************
* This file is part of OpenNMS(R).
*
* Copyright (C) 2019 The OpenNMS Group, Inc.
* OpenNMS(R) is Copyright (C) 1999-2019 The OpenNMS Group, Inc.
*
* OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc.
*
* OpenNMS(R) is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* OpenNMS(R) is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with OpenNMS(R). If not, see:
* http://www.gnu.org/licenses/
*
* For more information contact:
* OpenNMS(R) Licensing <[email protected]>
* http://www.opennms.org/
* http://www.opennms.com/
*******************************************************************************/

package org.opennms.netmgt.snmp;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;

import org.junit.Test;

public class SnmpObjIdTest {

/**
* Used to validate {@link SnmpObjId#isSuccessorOf(SnmpObjId)}.
*
* See NMS-10621 for details.
*/
@Test
public void canVerifySuccessor() {
SnmpObjId ifIndex = SnmpObjId.get("1.3.6.1.2.1.2.2.1.1");
SnmpObjId ifIndex2 = SnmpObjId.get("1.3.6.1.2.1.2.2.1.1.2");
SnmpObjId ifIndex3 = SnmpObjId.get("1.3.6.1.2.1.2.2.1.1.3");

assertThat(ifIndex.isSuccessorOf(ifIndex), equalTo(false));
assertThat(ifIndex2.isSuccessorOf(ifIndex), equalTo(true));
assertThat(ifIndex3.isSuccessorOf(ifIndex), equalTo(true));

assertThat(ifIndex2.isSuccessorOf(ifIndex2), equalTo(false));
assertThat(ifIndex2.isSuccessorOf(ifIndex3), equalTo(false));

assertThat(ifIndex3.isSuccessorOf(ifIndex3), equalTo(false));
assertThat(ifIndex3.isSuccessorOf(ifIndex2), equalTo(true));

SnmpObjId mib2 = SnmpObjId.get("1.3.6.1.2.1");
assertThat(ifIndex.isSuccessorOf(mib2), equalTo(true));
assertThat(mib2.isSuccessorOf(ifIndex), equalTo(false));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*******************************************************************************
* This file is part of OpenNMS(R).
*
* Copyright (C) 2009-2014 The OpenNMS Group, Inc.
* OpenNMS(R) is Copyright (C) 1999-2014 The OpenNMS Group, Inc.
*
* OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc.
*
* OpenNMS(R) is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* OpenNMS(R) is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with OpenNMS(R). If not, see:
* http://www.gnu.org/licenses/
*
* For more information contact:
* OpenNMS(R) Licensing <[email protected]>
* http://www.opennms.org/
* http://www.opennms.com/
*******************************************************************************/

package org.opennms.netmgt.snmp.snmp4j;

import org.opennms.netmgt.snmp.SnmpObjId;
import org.opennms.netmgt.snmp.TableTracker;

/**
* A minimal PhysInterfaceTableTracker used to testing
*/
class PhysInterfaceTableTracker extends TableTracker {

private static final SnmpObjId IF_TABLE_ENTRY = SnmpObjId.get(".1.3.6.1.2.1.2.2.1");
private static final SnmpObjId IF_INDEX = SnmpObjId.get(IF_TABLE_ENTRY, "1");
private static final SnmpObjId IF_DESCR = SnmpObjId.get(IF_TABLE_ENTRY, "2");
private static final SnmpObjId IF_TYPE = SnmpObjId.get(IF_TABLE_ENTRY, "3");
private static final SnmpObjId IF_MTU = SnmpObjId.get(IF_TABLE_ENTRY, "4");
private static final SnmpObjId IF_SPEED = SnmpObjId.get(IF_TABLE_ENTRY, "5");
private static final SnmpObjId IF_PHYS_ADDR = SnmpObjId.get(IF_TABLE_ENTRY, "6");
private static final SnmpObjId IF_ADMIN_STATUS = SnmpObjId.get(IF_TABLE_ENTRY, "7");
private static final SnmpObjId IF_OPER_STATUS = SnmpObjId.get(IF_TABLE_ENTRY, "8");
private static final SnmpObjId IF_LAST_CHANGE = SnmpObjId.get(IF_TABLE_ENTRY, "9");
private static final SnmpObjId IF_XTABLE_ENTRY = SnmpObjId.get( ".1.3.6.1.2.1.31.1.1.1");
private static final SnmpObjId IF_NAME = SnmpObjId.get(IF_XTABLE_ENTRY, "1");

private static SnmpObjId[] s_tableColumns = new SnmpObjId[] {
IF_INDEX,
IF_DESCR,
IF_TYPE,
IF_MTU,
IF_SPEED,
IF_PHYS_ADDR,
IF_ADMIN_STATUS,
IF_OPER_STATUS,
IF_LAST_CHANGE,
IF_NAME
};

PhysInterfaceTableTracker() {
super(s_tableColumns);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
/*******************************************************************************
* This file is part of OpenNMS(R).
*
* Copyright (C) 2019 The OpenNMS Group, Inc.
* OpenNMS(R) is Copyright (C) 1999-2019 The OpenNMS Group, Inc.
*
* OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc.
*
* OpenNMS(R) is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* OpenNMS(R) is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with OpenNMS(R). If not, see:
* http://www.gnu.org/licenses/
*
* For more information contact:
* OpenNMS(R) Licensing <[email protected]>
* http://www.opennms.org/
* http://www.opennms.com/
*******************************************************************************/

package org.opennms.netmgt.snmp.snmp4j;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;

import java.io.IOException;
import java.net.InetAddress;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.opennms.netmgt.snmp.SnmpAgentConfig;
import org.opennms.netmgt.snmp.SnmpUtils;
import org.opennms.netmgt.snmp.SnmpWalker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.snmp4j.CommandResponder;
import org.snmp4j.CommandResponderEvent;
import org.snmp4j.MessageException;
import org.snmp4j.PDU;
import org.snmp4j.Snmp;
import org.snmp4j.mp.StateReference;
import org.snmp4j.mp.StatusInformation;
import org.snmp4j.smi.Integer32;
import org.snmp4j.smi.Null;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.VariableBinding;
import org.snmp4j.transport.DefaultUdpTransportMapping;

/**
* Used to validate NMS-10621
*
* Verifies that we don't get stuck in a loop if a bad
* agent responds with VBs that come *before* or
* are *equal to* the requested VBs.
*/
public class TrackerSuccessorIT {
private static final Logger LOG = LoggerFactory.getLogger(TrackerSuccessorIT.class);

private DefaultUdpTransportMapping transportMapping;

@Before
public void setUp() throws IOException {
spawnAgent();
}

@After
public void tearDown() throws IOException {
if (transportMapping != null) {
transportMapping.close();
}
}

/**
* Verify that invalid response to not trigger infinite loop in SNMP tracking code.
*
* Request should look like:
* User Datagram Protocol, Src Port: 40440, Dst Port: 161
* Simple Network Management Protocol
* version: v2c (1)
* community: funkymonkey
* data: getBulkRequest (5)
* getBulkRequest
* request-id: 1705470191
* non-repeaters: 0
* max-repetitions: 2
* variable-bindings: 10 items
* 1.3.6.1.2.1.2.2.1.1.2: Value (Null)
* 1.3.6.1.2.1.2.2.1.2.2: Value (Null)
* 1.3.6.1.2.1.2.2.1.3.2: Value (Null)
* 1.3.6.1.2.1.2.2.1.4.2: Value (Null)
* 1.3.6.1.2.1.2.2.1.5.2: Value (Null)
* 1.3.6.1.2.1.2.2.1.6.2: Value (Null)
* 1.3.6.1.2.1.2.2.1.7.2: Value (Null)
* 1.3.6.1.2.1.2.2.1.8.2: Value (Null)
* 1.3.6.1.2.1.2.2.1.9.2: Value (Null)
* 1.3.6.1.2.1.31.1.1.1.1.2: Value (Null)
*
* And the response should look like:
* User Datagram Protocol, Src Port: 161, Dst Port: 40440
* Simple Network Management Protocol
* version: v2c (1)
* community: funkymonkey
* data: get-response (2)
* get-response
* request-id: 1705470191
* error-status: noError (0)
* error-index: 0
* variable-bindings: 20 items
* 1.3.6.1.2.1.2.2.1.1.2: 2
* 1.3.6.1.2.1.2.2.1.2.2: <MISSING>
* 1.3.6.1.2.1.2.2.1.3.2: 6
* 1.3.6.1.2.1.2.2.1.4.2: 1500
* 1.3.6.1.2.1.2.2.1.5.2: 100000000
* 1.3.6.1.2.1.2.2.1.6.2: xxxxxxxxxxxx
* 1.3.6.1.2.1.2.2.1.7.2: 1
* 1.3.6.1.2.1.2.2.1.8.2: 1
* 1.3.6.1.2.1.2.2.1.9.2: 0
* 1.3.6.1.2.1.31.1.1.1.1.2: yyyyyyyy
* 1.3.6.1.2.1.2.2.1.1.2: 2
* 1.3.6.1.2.1.2.2.1.2.2: <MISSING>
* 1.3.6.1.2.1.2.2.1.3.2: 6
* 1.3.6.1.2.1.2.2.1.4.2: 1500
* 1.3.6.1.2.1.2.2.1.5.2: 100000000
* 1.3.6.1.2.1.2.2.1.6.2: xxxxxxxxxxxx
* 1.3.6.1.2.1.2.2.1.7.2: 1
* 1.3.6.1.2.1.2.2.1.8.2: 1
* 1.3.6.1.2.1.2.2.1.9.2: 0
* 1.3.6.1.2.1.31.1.1.1.1.2: yyyyyyyy
*/
@Test(timeout=30000)
public void canHandleInvalidResponses() throws IOException, InterruptedException {
// Create our walker
SnmpAgentConfig agent = new SnmpAgentConfig();
agent.setAddress(InetAddress.getLocalHost());
agent.setPort(transportMapping.getListenAddress().getPort());
agent.setVersion(2);
PhysInterfaceTableTracker tracker = new PhysInterfaceTableTracker();
final SnmpWalker walker = SnmpUtils.createWalker(agent, "test", tracker);
walker.start();

// Wait and verify
walker.waitFor();
assertThat(walker.failed(), equalTo(false));

// We don't care about the actual results of the tracker, only that it did finish successfully
}

/**
* Leverage SNMP4J to create a UDP socket that allows us to receive
* and send PDUs.
*
* @throws IOException on error
*/
public void spawnAgent() throws IOException {
transportMapping = new DefaultUdpTransportMapping();
Snmp snmp = new Snmp(transportMapping);
snmp.addCommandResponder(new CommandResponder() {
@Override
public void processPdu(CommandResponderEvent e) {
LOG.debug("Got request PDU with: {}", e.getPDU());

// Create the fixed response, copy over the request id
PDU response = createFixedRepsonsePDU(e.getPDU().getRequestID());

StatusInformation statusInformation = new StatusInformation();
StateReference ref = e.getStateReference();
try {
LOG.debug("Replying with: {}", response);
e.setProcessed(true);
e.getMessageDispatcher().returnResponsePdu(e.getMessageProcessingModel(),
e.getSecurityModel(),
e.getSecurityName(),
e.getSecurityLevel(),
response,
e.getMaxSizeResponsePDU(),
ref,
statusInformation);
}
catch (MessageException ex) {
LOG.error("Error while sending response", ex);
}
}
});
transportMapping.listen();
}

private static PDU createFixedRepsonsePDU(Integer32 requestId) {
final PDU pdu = new PDU();
pdu.setType(PDU.RESPONSE);
pdu.setRequestID(requestId);
addVars(pdu);
addVars(pdu);
return pdu;
}

private static void addVars(PDU pdu) {
pdu.add(new VariableBinding(new OID("1.3.6.1.2.1.2.2.1.1.2"), new Integer32(2)));
pdu.add(new VariableBinding(new OID("1.3.6.1.2.1.2.2.1.2.2"), new Null()));
pdu.add(new VariableBinding(new OID("1.3.6.1.2.1.2.2.1.3.2"), new Integer32(6)));
pdu.add(new VariableBinding(new OID("1.3.6.1.2.1.2.2.1.4.2"), new Integer32(1500)));
pdu.add(new VariableBinding(new OID("1.3.6.1.2.1.2.2.1.5.2"), new Integer32(100000000)));
pdu.add(new VariableBinding(new OID("1.3.6.1.2.1.2.2.1.6.2"), new OctetString("xxxxxxxxxxxx")));
pdu.add(new VariableBinding(new OID("1.3.6.1.2.1.2.2.1.7.2"), new Integer32(1)));
pdu.add(new VariableBinding(new OID("1.3.6.1.2.1.2.2.1.8.2"), new Integer32(1)));
pdu.add(new VariableBinding(new OID("1.3.6.1.2.1.2.2.1.9.2"), new Integer32(0)));
pdu.add(new VariableBinding(new OID("1.3.6.1.2.1.31.1.1.1.1.2"), new OctetString("yyyyyyyy")));
}
}

0 comments on commit e7e0e60

Please sign in to comment.