// // This file is part of the OpenNMS(R) Application. // // OpenNMS(R) is Copyright (C) 2005 The OpenNMS Group, Inc. All rights reserved. // OpenNMS(R) is a derivative work, containing both original code, included code and modified // code that was published under the GNU General Public License. Copyrights for modified // and included code are below. // // OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc. // // Modifications: // // 2007 Jun 23: Code formatting, deduplication (especially the send methods), // and more specific log messages. - dj@opennms.org // 2007 Jun 22: Iterate over the proper array in the four-argument send method // and don't change the input values array. - dj@opennms.org // 2007 Jun 22: Make the static sendTest(...) method non-static. // - dj@opennms.org // 2007 Jun 22: Make the static send(...) method non-static and do some // various code cleanup. - dj@opennms.org // 2007 Jun 21: Always use SnmpHelpers.createSnmpSession() to create SNMP // sessions, including eliminating static Snmp object used // for sending traps. Improve error reporting. - dj@opennms.org // // Original code base Copyright (C) 1999-2001 Oculan Corp. All rights reserved. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // // For more information contact: // OpenNMS Licensing <license@opennms.org> // http://www.opennms.org/ // http://www.opennms.com/ // package org.infosec.ismp.snmp.snmp4j; import java.io.IOException; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import org.infosec.ismp.snmp.CollectionTracker; import org.infosec.ismp.snmp.SnmpAgentConfig; import org.infosec.ismp.snmp.SnmpObjId; import org.infosec.ismp.snmp.SnmpStrategy; import org.infosec.ismp.snmp.SnmpTrapBuilder; import org.infosec.ismp.snmp.SnmpV1TrapBuilder; import org.infosec.ismp.snmp.SnmpValue; import org.infosec.ismp.snmp.SnmpValueFactory; import org.infosec.ismp.snmp.SnmpWalker; import org.infosec.ismp.snmp.TrapNotificationListener; import org.infosec.ismp.snmp.TrapProcessorFactory; import org.infosec.ismp.util.ThreadCategory; import org.snmp4j.CommandResponderEvent; import org.snmp4j.MessageDispatcher; import org.snmp4j.PDU; import org.snmp4j.PDUv1; import org.snmp4j.SNMP4JSettings; import org.snmp4j.Snmp; import org.snmp4j.TransportMapping; import org.snmp4j.event.ResponseEvent; import org.snmp4j.mp.MPv3; import org.snmp4j.mp.MessageProcessingModel; import org.snmp4j.mp.PduHandle; import org.snmp4j.security.SecurityLevel; import org.snmp4j.security.SecurityModel; import org.snmp4j.security.SecurityModels; import org.snmp4j.security.SecurityProtocols; import org.snmp4j.security.USM; import org.snmp4j.smi.IpAddress; import org.snmp4j.smi.OID; import org.snmp4j.smi.OctetString; import org.snmp4j.smi.SMIConstants; import org.snmp4j.smi.UdpAddress; import org.snmp4j.smi.VariableBinding; import org.snmp4j.transport.DefaultUdpTransportMapping; public class Snmp4JStrategy implements SnmpStrategy { private static Map<TrapNotificationListener, RegistrationInfo> s_registrations = new HashMap<TrapNotificationListener, RegistrationInfo>(); private static boolean s_initialized = false; private Snmp4JValueFactory m_valueFactory; /** * Initialize for v3 communications */ private static void initialize() { if (s_initialized) { return; } // LogFactory.setLogFactory(new Log4jLogFactory()); MPv3.setEnterpriseID(5813); USM usm = new USM(SecurityProtocols.getInstance(), new OctetString( MPv3.createLocalEngineID()), 0); SecurityModels.getInstance().addSecurityModel(usm); // Enable extensibility in SNMP4J so that we can subclass some SMI // classes to work around // agent bugs if (System.getProperty("org.snmp4j.smisyntaxes", null) != null) { SNMP4JSettings.setExtensibilityEnabled(true); } if (Boolean .getBoolean("org.opennms.snmp.snmp4j.forwardRuntimeExceptions")) { SNMP4JSettings.setForwardRuntimeExceptions(true); } s_initialized = true; } public Snmp4JStrategy() { initialize(); } /** * SNMP4J createWalker implemenetation. * * @param snmpAgentConfig * @param name * @param tracker */ @Override public SnmpWalker createWalker(SnmpAgentConfig snmpAgentConfig, String name, CollectionTracker tracker) { return new Snmp4JWalker(new Snmp4JAgentConfig(snmpAgentConfig), name, tracker); } /** * Not yet implemented. Use a walker. */ @Override public SnmpValue[] getBulk(SnmpAgentConfig agentConfig, SnmpObjId[] oid) { throw new UnsupportedOperationException( "Snmp4JStrategy.getBulk not yet implemented."); } @Override public SnmpValue set(SnmpAgentConfig agentConfig, SnmpObjId oid, SnmpValue value) { if (log().isDebugEnabled()) { log().debug( "set: OID: " + oid + " value: " + value.toString() + " for Agent: " + agentConfig); } SnmpObjId[] oids = { oid }; SnmpValue[] values = { value }; SnmpValue[] retvalues = set(agentConfig, oids, values); return retvalues[0]; } @Override public SnmpValue[] set(SnmpAgentConfig agentConfig, SnmpObjId[] oids, SnmpValue[] values) { if (log().isDebugEnabled()) { log().debug( "set: OIDs: " + Arrays.toString(oids) + " values: " + Arrays.toString(values) + " for Agent: " + agentConfig); } return buildAndSendPdu(agentConfig, PDU.SET, oids, values); } /** * SNMP4J get helper that takes a single SnmpObjId * and calls get with an array.lenght =1 and returns * the first element of the returned array of SnmpValue. * * @param agentConfig * @param oid * */ @Override public SnmpValue get(SnmpAgentConfig agentConfig, SnmpObjId oid) { if (log().isDebugEnabled()) { log().debug("get: OID: " + oid + " for Agent:" + agentConfig); } SnmpObjId[] oids = { oid }; SnmpValue[] retvalues = get(agentConfig, oids); return retvalues[0]; } /** * SnmpGet implementation. * * @param agentConfig * @param oids * @return * Returns an array of Snmp4JValues. If the * get was unsuccessful, then the first elment * of the array will be null and lenth of 1. */ @Override public SnmpValue[] get(SnmpAgentConfig agentConfig, SnmpObjId[] oids) { if (log().isDebugEnabled()) { log().debug( "get: OID: " + Arrays.toString(oids) + " for Agent:" + agentConfig); } return buildAndSendPdu(agentConfig, PDU.GET, oids, null); } /** * SNMP4J getNext implementation * * @param agentConfig * @param oid * */ @Override public SnmpValue getNext(SnmpAgentConfig agentConfig, SnmpObjId oid) { if (log().isDebugEnabled()) { log().debug("getNext: OID: " + oid + " for Agent:" + agentConfig); } SnmpObjId[] oids = { oid }; SnmpValue[] retvalues = getNext(agentConfig, oids); return retvalues[0]; } /** * SNMP GetNext implementation. * * @param agentConfig * @param oids * @return * Returns an array of Snmp4JValues. If the * getNext was unsuccessful, then the first element * of the array will be null and length of 1. */ @Override public SnmpValue[] getNext(SnmpAgentConfig agentConfig, SnmpObjId[] oids) { if (log().isDebugEnabled()) { log().debug( "getNext: OID: " + Arrays.toString(oids) + " for Agent:" + agentConfig); } return buildAndSendPdu(agentConfig, PDU.GETNEXT, oids, null); } private SnmpValue[] buildAndSendPdu(SnmpAgentConfig agentConfig, int type, SnmpObjId[] oids, SnmpValue[] values) { Snmp4JAgentConfig snmp4jAgentConfig = new Snmp4JAgentConfig(agentConfig); PDU pdu = buildPdu(snmp4jAgentConfig, type, oids, values); if (pdu == null) { return null; } return send(snmp4jAgentConfig, pdu, true); } /** * Sends and SNMP4J request pdu. The attributes in SnmpAgentConfig should have been * adapted from default SnmpAgentConfig values to those compatible with the SNMP4J library. * * @param agentConfig * @param pduType TODO * @param oids * @param values can be null * @return */ protected SnmpValue[] send(Snmp4JAgentConfig agentConfig, PDU pdu, boolean expectResponse) { Snmp session; try { session = agentConfig.createSnmpSession(); } catch (IOException e) { log().error( "send: Could not create SNMP session for agent " + agentConfig + ": " + e, e); return new SnmpValue[] { null }; } try { if (expectResponse) { try { session.listen(); } catch (IOException e) { log().error( "send: error setting up listener for SNMP responses: " + e, e); return new SnmpValue[] { null }; } } try { ResponseEvent responseEvent = session.send(pdu, agentConfig.getTarget()); if (expectResponse) { return processResponse(agentConfig, responseEvent); } else { return null; } } catch (IOException e) { log().error("send: error during SNMP operation: " + e, e); return new SnmpValue[] { null }; } } finally { closeQuietly(session); } } protected PDU buildPdu(Snmp4JAgentConfig agentConfig, int pduType, SnmpObjId[] oids, SnmpValue[] values) { PDU pdu = agentConfig.createPdu(pduType); if (values == null) { for (SnmpObjId oid : oids) { pdu.add(new VariableBinding(new OID(oid.toString()))); } } else { // TODO should this throw an exception? This situation is fairly // bogus and probably signifies a coding error. if (oids.length != values.length) { Exception e = new Exception( "This is a bogus exception so we can get a stack backtrace"); log().error( "PDU to prepare has object values but not the same number as there are OIDs. There are " + oids.length + " OIDs and " + values.length + " object values.", e); return null; } for (int i = 0; i < oids.length; i++) { pdu.add(new VariableBinding(new OID(oids[i].toString()), new Snmp4JValue(values[i].getType(), values[i] .getBytes()).getVariable())); } } // TODO should this throw an exception? This situation is fairly bogus. if (pdu.getVariableBindings().size() != oids.length) { Exception e = new Exception( "This is a bogus exception so we can get a stack backtrace"); log().error( "Prepared PDU does not have as many variable bindings as there are OIDs. There are " + oids.length + " OIDs and " + pdu.getVariableBindings() + " variable bindings.", e); return null; } return pdu; } private SnmpValue[] processResponse(Snmp4JAgentConfig agentConfig, ResponseEvent responseEvent) throws IOException { SnmpValue[] retvalues = { null }; if (responseEvent.getResponse() == null) { log().warn("send: Timeout. Agent: " + agentConfig); } else if (responseEvent.getResponse().get(0).getSyntax() == SMIConstants.SYNTAX_NULL) { retvalues[0] = null; } else if (responseEvent.getError() != null) { log().warn( "send: Error during get operation. Error: " + responseEvent.getError().getLocalizedMessage()); } else if (responseEvent.getResponse().getType() == PDU.REPORT) { log().warn( "send: Error during get operation. Report returned with varbinds: " + responseEvent.getResponse().getVariableBindings()); } else if (responseEvent.getResponse().getVariableBindings().size() < 1) { log().warn("send: Received PDU with 0 varbinds."); } else { retvalues = convertResponseToValues(responseEvent); if (log().isDebugEnabled()) { log().debug( "send: Snmp operation successful. Value: " + Arrays.toString(retvalues)); } } return retvalues; } private SnmpValue[] convertResponseToValues(ResponseEvent responseEvent) { SnmpValue[] retvalues = new Snmp4JValue[responseEvent.getResponse() .getVariableBindings().size()]; for (int i = 0; i < retvalues.length; i++) { retvalues[i] = new Snmp4JValue(responseEvent.getResponse().get(i) .getVariable()); } return retvalues; } private ThreadCategory log() { return ThreadCategory.getInstance(getClass()); } @Override public SnmpValueFactory getValueFactory() { if (m_valueFactory == null) { m_valueFactory = new Snmp4JValueFactory(); } return m_valueFactory; } public static class RegistrationInfo { public TrapNotificationListener m_listener; int m_trapPort; Snmp m_trapSession; Snmp4JTrapNotifier m_trapHandler; private TransportMapping m_transportMapping; RegistrationInfo(TrapNotificationListener listener, int trapPort) { if (listener == null) { throw new NullPointerException("listener is null"); } m_listener = listener; m_trapPort = trapPort; } @Override public boolean equals(Object obj) { if (obj instanceof RegistrationInfo) { RegistrationInfo info = (RegistrationInfo) obj; return (m_listener == info.m_listener) && (m_trapPort == info.m_trapPort); } return false; } @Override public int hashCode() { return (m_listener.hashCode() ^ m_trapPort); } public void setSession(Snmp trapSession) { m_trapSession = trapSession; } public Snmp getSession() { return m_trapSession; } public void setHandler(Snmp4JTrapNotifier trapHandler) { m_trapHandler = trapHandler; } public Snmp4JTrapNotifier getHandler() { return m_trapHandler; } public int getPort() { return m_trapPort; } public void setTransportMapping(TransportMapping transport) { m_transportMapping = transport; } public TransportMapping getTransportMapping() { return m_transportMapping; } } @Override public void registerForTraps(TrapNotificationListener listener, TrapProcessorFactory processorFactory, int snmpTrapPort) throws IOException { RegistrationInfo info = new RegistrationInfo(listener, snmpTrapPort); Snmp4JTrapNotifier m_trapHandler = new Snmp4JTrapNotifier(listener, processorFactory); info.setHandler(m_trapHandler); TransportMapping transport = new DefaultUdpTransportMapping( new UdpAddress(snmpTrapPort)); info.setTransportMapping(transport); Snmp snmp = new Snmp(transport); snmp.addCommandResponder(m_trapHandler); info.setSession(snmp); s_registrations.put(listener, info); snmp.listen(); } @Override public void unregisterForTraps(TrapNotificationListener listener, int snmpTrapPort) throws IOException { RegistrationInfo info = s_registrations.remove(listener); closeQuietly(info.getSession()); } @Override public SnmpV1TrapBuilder getV1TrapBuilder() { return new Snmp4JV1TrapBuilder(this); } @Override public SnmpTrapBuilder getV2TrapBuilder() { return new Snmp4JV2TrapBuilder(this); } protected SnmpAgentConfig buildAgentConfig(String address, int port, String community, PDU pdu) throws UnknownHostException { SnmpAgentConfig config = new SnmpAgentConfig(); config.setAddress(InetAddress.getByName(address)); config.setPort(port); config.setVersion(pdu instanceof PDUv1 ? SnmpAgentConfig.VERSION1 : SnmpAgentConfig.VERSION2C); return config; } public void sendTest(String agentAddress, int port, String community, PDU pdu) { for (RegistrationInfo info : s_registrations.values()) { if (port == info.getPort()) { Snmp snmp = info.getSession(); MessageDispatcher dispatcher = snmp.getMessageDispatcher(); TransportMapping transport = info.getTransportMapping(); int securityModel = (pdu instanceof PDUv1 ? SecurityModel.SECURITY_MODEL_SNMPv1 : SecurityModel.SECURITY_MODEL_SNMPv2c); int messageModel = (pdu instanceof PDUv1 ? MessageProcessingModel.MPv1 : MessageProcessingModel.MPv2c); CommandResponderEvent e = new CommandResponderEvent(dispatcher, transport, new IpAddress(agentAddress), messageModel, securityModel, community.getBytes(), SecurityLevel.NOAUTH_NOPRIV, new PduHandle(), pdu, 1000, null); info.getHandler().processPdu(e); } } } private void closeQuietly(Snmp session) { if (session == null) { return; } try { session.close(); } catch (IOException e) { ThreadCategory.getInstance(Snmp4JStrategy.class).error( "error closing SNMP connection: " + e, e); } } }