/******************************************************************************* * This file is part of OpenNMS(R). * * Copyright (C) 2009-2011 The OpenNMS Group, Inc. * OpenNMS(R) is Copyright (C) 1999-2011 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 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenNMS(R). If not, see: * http://www.gnu.org/licenses/ * * For more information contact: * OpenNMS(R) Licensing <license@opennms.org> * http://www.opennms.org/ * http://www.opennms.com/ *******************************************************************************/ package org.opennms.netmgt.poller.monitors; import java.io.IOException; import java.lang.reflect.UndeclaredThrowableException; import java.net.InetAddress; import java.util.HashMap; import java.util.Map; import org.apache.log4j.Level; import org.opennms.core.utils.InetAddressUtils; import org.opennms.core.utils.ParameterMap; import org.opennms.netmgt.config.SnmpPeerFactory; import org.opennms.netmgt.model.PollStatus; import org.opennms.netmgt.poller.Distributable; import org.opennms.netmgt.poller.DistributionContext; import org.opennms.netmgt.poller.MonitoredService; import org.opennms.netmgt.poller.NetworkInterface; import org.opennms.netmgt.snmp.SnmpAgentConfig; import org.opennms.netmgt.snmp.SnmpObjId; import org.opennms.netmgt.snmp.SnmpUtils; import org.opennms.netmgt.snmp.SnmpValue; /** * <p> * Check for BgpPeering states via RFC1269-MIB. * </p> * <p> * This does SNMP and therefore relies on the SNMP configuration so it is not distributable. * </p> * * @author <A HREF="mailto:r.trommer@open-factory.org">Ronny Trommer</A> * @version $Id: $ */ @Distributable(DistributionContext.DAEMON) final public class BgpSessionMonitor extends SnmpMonitorStrategy { /** * Name of monitored service. */ private static final String m_serviceName = "BGP_Session"; /** * Default OID for the table that represents the BGP-peer states. */ private static final String BGP_PEER_STATE_OID = ".1.3.6.1.2.1.15.3.1.2"; /** * Default OID for the table that represents the BGP-peer admin states. */ private static final String BGP_PEER_ADMIN_STATE_OID = ".1.3.6.1.2.1.15.3.1.3"; /** * Default OID for the table that represents the BGP-peer remote AS number. */ private static final String BGP_PEER_REMOTEAS_OID = ".1.3.6.1.2.1.15.3.1.9"; /** * Default OID for the table that represents the BGP-peer last error code. */ private static final String BGP_PEER_LAST_ERROR_OID = ".1.3.6.1.2.1.15.3.1.14"; /** * Default OID for the table that represents the BGP-peer established time. */ private static final String BGP_PEER_FSM_EST_TIME_OID = ".1.3.6.1.2.1.15.3.1.16"; /** * Implement the BGP Peer states */ private enum BGP_PEER_STATE { IDLE(1), CONNECT(2), ACTIVE(3), OPEN_SENT(4), OPEN_CONFIRM(5), ESTABLISHED( 6); private final int state; // state code BGP_PEER_STATE(int s) { this.state = s; } private int value() { return this.state; } }; /** * Implement the BGP Peer admin states */ private static enum BGP_PEER_ADMIN_STATE { STOP(1), START(2); private final int state; // state code BGP_PEER_ADMIN_STATE(int s) { this.state = s; } private int value() { return this.state; } }; /** * <P> * Returns the name of the service that the plug-in monitors * ("BGP-SessionMonitor"). * </P> * * @return The service that the plug-in monitors. */ public String serviceName() { return m_serviceName; } /** * {@inheritDoc} * * <P> * Initialize the service monitor. * </P> * @exception RuntimeException * Thrown if an unrecoverable error occurs that prevents the * plug-in from functioning. */ public void initialize(Map<String, Object> parameters) { // Initialize the SnmpPeerFactory // try { SnmpPeerFactory.init(); } catch (IOException ex) { log().fatal("initialize: Failed to load SNMP configuration", ex); throw new UndeclaredThrowableException(ex); } return; } /** * <P> * Called by the poller framework when an interface is being added to the * scheduler. Here we perform any necessary initialization to prepare the * NetworkInterface object for polling. * </P> * * @exception RuntimeException * Thrown if an unrecoverable error occurs that prevents the * interface from being monitored. * @param svc a {@link org.opennms.netmgt.poller.MonitoredService} object. */ public void initialize(MonitoredService svc) { super.initialize(svc); return; } /** * {@inheritDoc} * * <P> * The poll() method is responsible for polling the specified address for * SNMP service availability. * </P> * @exception RuntimeException * Thrown for any uncrecoverable errors. */ public PollStatus poll(MonitoredService svc, Map<String, Object> parameters) { NetworkInterface<InetAddress> iface = svc.getNetInterface(); String returnValue = ""; PollStatus status = PollStatus.unavailable(); InetAddress ipaddr = (InetAddress) iface.getAddress(); // Initialize the messages if the session is down String adminStateMsg = "N/A"; String peerStateMsg = "N/A"; String remoteAsMsg = "N/A"; String lastErrorMsg = "N/A"; String estTimeMsg = "N/A"; // Retrieve this interface's SNMP peer object // SnmpAgentConfig agentConfig = SnmpPeerFactory.getInstance().getAgentConfig(ipaddr); if (agentConfig == null) throw new RuntimeException("SnmpAgentConfig object not available for interface " + ipaddr); final String hostAddress = InetAddressUtils.str(ipaddr); log().debug("poll: setting SNMP peer attribute for interface " + hostAddress); // Get configuration parameters // // This should never need to be overridden, but it can be in order to be used with similar tables. String bgpPeerIp = ParameterMap.getKeyedString(parameters, "bgpPeerIp", null); if (bgpPeerIp == null) { log().warn("poll: No BGP-Peer IP Defined! "); return status; } // set timeout and retries on SNMP peer object // agentConfig.setTimeout(ParameterMap.getKeyedInteger(parameters, "timeout", agentConfig.getTimeout())); agentConfig.setRetries(ParameterMap.getKeyedInteger(parameters, "retry", ParameterMap.getKeyedInteger(parameters, "retries", agentConfig.getRetries()))); agentConfig.setPort(ParameterMap.getKeyedInteger(parameters, "port", agentConfig.getPort())); // Establish SNMP session with interface // try { if (log().isDebugEnabled()) { log().debug("poll: SnmpAgentConfig address: " +agentConfig); } // Get the BGP peer state SnmpObjId bgpPeerStateSnmpObject = SnmpObjId.get(BGP_PEER_STATE_OID + "." + bgpPeerIp); SnmpValue bgpPeerState = SnmpUtils.get(agentConfig, bgpPeerStateSnmpObject); // If no peer state is received or SNMP is not possible, service is down if (bgpPeerState == null) { log().warn("No BGP peer state received!"); return status; } else { if (log().isDebugEnabled()) { log().debug("poll: bgpPeerState: " + bgpPeerState); } peerStateMsg = resolvePeerState(bgpPeerState.toInt()); } /* * Do no unnecessary SNMP requests, if peer state is up, return with * service available and go away. */ if (bgpPeerState.toInt() == BGP_PEER_STATE.ESTABLISHED.value()) { if (log().isDebugEnabled()) { log().debug("poll: bgpPeerState: " + BGP_PEER_STATE.ESTABLISHED.name()); } return PollStatus.available(); } // Peer state is not established gather some information SnmpObjId bgpPeerAdminStateSnmpObject = SnmpObjId.get(BGP_PEER_ADMIN_STATE_OID + "." + bgpPeerIp); SnmpValue bgpPeerAdminState = SnmpUtils.get(agentConfig, bgpPeerAdminStateSnmpObject); // Check correct MIB-Support if (bgpPeerAdminState == null) { log().warn("Cannot receive bgpAdminState"); } else { if (log().isDebugEnabled()) { log().debug("poll: bgpPeerAdminState: " + bgpPeerAdminState); } adminStateMsg = resolveAdminState(bgpPeerAdminState.toInt()); } SnmpObjId bgpPeerRemoteAsSnmpObject = SnmpObjId.get(BGP_PEER_REMOTEAS_OID + "." + bgpPeerIp); SnmpValue bgpPeerRemoteAs = SnmpUtils.get(agentConfig, bgpPeerRemoteAsSnmpObject); // Check correct MIB-Support if (bgpPeerRemoteAs == null) { log().warn("Cannot receive bgpPeerRemoteAs"); } else { if (log().isDebugEnabled()) { log().debug("poll: bgpPeerRemoteAs: " + bgpPeerRemoteAs); } remoteAsMsg = bgpPeerRemoteAs.toString(); } SnmpObjId bgpPeerLastErrorSnmpObject = SnmpObjId.get(BGP_PEER_LAST_ERROR_OID + "." + bgpPeerIp); SnmpValue bgpPeerLastError = SnmpUtils.get(agentConfig, bgpPeerLastErrorSnmpObject); // Check correct MIB-Support if (bgpPeerLastError == null) { log().warn("Cannot receive bgpPeerLastError"); } else { if (log().isDebugEnabled()) { log().debug("poll: bgpPeerLastError: " + bgpPeerLastError); } lastErrorMsg = resolveBgpErrorCode(bgpPeerLastError.toHexString()); } SnmpObjId bgpPeerFsmEstTimeSnmpObject = SnmpObjId.get(BGP_PEER_FSM_EST_TIME_OID + "." + bgpPeerIp); SnmpValue bgpPeerFsmEstTime = SnmpUtils.get(agentConfig, bgpPeerFsmEstTimeSnmpObject); // Check correct MIB-Support if (bgpPeerFsmEstTime == null) { log().warn("Cannot receive bgpPeerFsmEstTime"); } else { if (log().isDebugEnabled()) { log().debug("poll: bgpPeerFsmEsmTime: " + bgpPeerFsmEstTime); } estTimeMsg = bgpPeerFsmEstTime.toString(); } returnValue = "BGP Session state to AS-" + remoteAsMsg + " via " + bgpPeerIp + " is " + peerStateMsg + "! Last peer " +"error message is " + lastErrorMsg + ". BGP admin state is " + adminStateMsg + ". BGP Session established time: " + estTimeMsg; // Set service down and return gathered information status = PollStatus.unavailable(returnValue); } catch (NullPointerException e) { status = logDown(Level.WARN, "Unexpected error during SNMP poll of interface " + hostAddress, e); } catch (NumberFormatException e) { status = logDown(Level.WARN, "Number operator used on a non-number " + e.getMessage()); } catch (IllegalArgumentException e) { status = logDown(Level.WARN, "Invalid SNMP Criteria: " + e.getMessage()); } catch (Throwable t) { status = logDown(Level.WARN, "Unexpected exception during SNMP poll of interface " + hostAddress, t); } // If matchAll is set to true, then the status is set to available above with a single match. // Otherwise, the service will be unavailable. return status; } /** * Method to convert BGP Error codes in plain text messages. * * @param bgpCode * BGP Hex code * @return Plain text error message */ private String resolveBgpErrorCode (String bgpCode) { String clearCode = "unknown error"; HashMap<String, String> codeMap = new HashMap<String, String> (); codeMap.put("0100", "Message Header Error"); codeMap.put("0101", "Message Header Error - Connection Not Synchronized"); codeMap.put("0102", "Message Header Error - Bad Message Length"); codeMap.put("0103", "Message Header Error - Bad Message Type"); codeMap.put("0200", "OPEN Message Error"); codeMap.put("0201", "OPEN Message Error - Unsupported Version Number"); codeMap.put("0202", "OPEN Message Error - Bad Peer AS"); codeMap.put("0203", "OPEN Message Error - Bad BGP Identifier"); codeMap.put("0204", "OPEN Message Error - Unsupported Optional Parameter"); codeMap.put("0205", "OPEN Message Error (deprecated)"); codeMap.put("0206", "OPEN Message Error - Unacceptable Hold Time"); codeMap.put("0300", "UPDATE Message Error"); codeMap.put("0301", "UPDATE Message Error - Malformed Attribute List"); codeMap.put("0302", "UPDATE Message Error - Unrecognized Well-known Attribute"); codeMap.put("0303", "UPDATE Message Error - Missing Well-known Attribute"); codeMap.put("0304", "UPDATE Message Error - Attribute Flags Error"); codeMap.put("0305", "UPDATE Message Error - Attribute Length Error"); codeMap.put("0306", "UPDATE Message Error - Invalid ORIGIN Attribute"); codeMap.put("0307", "UPDATE Message Error (deprecated)"); codeMap.put("0308", "UPDATE Message Error - Invalid NEXT_HOP Attribute"); codeMap.put("0309", "UPDATE Message Error - Optional Attribute Error"); codeMap.put("030A", "UPDATE Message Error - Invalid Network Field"); codeMap.put("030B", "UPDATE Message Error - Malformed AS_PATH"); codeMap.put("0400", "Hold Timer Expired"); codeMap.put("0500", "Finite State Machine Error"); codeMap.put("0600", "Cease"); codeMap.put("0601", "Cease - Maximum Number of Prefixes Reached"); codeMap.put("0602", "Cease - Administrative Shutdown"); codeMap.put("0603", "Cease - Peer De-configured"); codeMap.put("0604", "Cease - Administrative Reset"); codeMap.put("0605", "Cease - Connection Rejected"); codeMap.put("0606", "Cease - Other Configuration Change"); codeMap.put("0607", "Cease - Connection Collision Resolution"); codeMap.put("0608", "Cease - Out of Resources"); if (codeMap.containsKey(bgpCode)) { clearCode = codeMap.get(bgpCode); } return clearCode; } /** * Method to resolve a given peer state to human readable string. TODO: * Check if there is a better way to resolve the states backward * * @param sc * BGP-Peer admin state code * @return Human readable BGP peer admin state */ private String resolveAdminState(int sc) { String name = "UNKNOWN"; if (BGP_PEER_ADMIN_STATE.STOP.value() == sc) name = BGP_PEER_ADMIN_STATE.STOP.name(); if (BGP_PEER_ADMIN_STATE.START.value() == sc) name = BGP_PEER_ADMIN_STATE.START.name(); return name; } /** * Method to resolve a given peer state to human readable string. TODO: * Check if there is a better way to resolve the states backward * * @param sc * BGP-Peer state code * @return Human readable BGP peer state */ private String resolvePeerState(int sc) { String name = "UNKNOWN"; if (BGP_PEER_STATE.IDLE.value() == sc) name = BGP_PEER_STATE.IDLE.name(); if (BGP_PEER_STATE.CONNECT.value() == sc) name = BGP_PEER_STATE.CONNECT.name(); if (BGP_PEER_STATE.ACTIVE.value() == sc) name = BGP_PEER_STATE.ACTIVE.name(); if (BGP_PEER_STATE.OPEN_SENT.value() == sc) name = BGP_PEER_STATE.OPEN_SENT.name(); if (BGP_PEER_STATE.OPEN_CONFIRM.value() == sc) name = BGP_PEER_STATE.OPEN_CONFIRM.name(); if (BGP_PEER_STATE.ESTABLISHED.value() == sc) name = BGP_PEER_STATE.ESTABLISHED.name(); return name; } }