// Copyright 2014-2015 Boundary, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.boundary.sdk.event.snmp; import java.io.File; import java.util.Date; import java.util.Vector; import org.apache.camel.Exchange; import org.apache.camel.Message; import org.apache.camel.Processor; import org.apache.camel.component.snmp.SnmpMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.snmp4j.PDU; import org.snmp4j.PDUv1; import org.snmp4j.mp.SnmpConstants; import org.snmp4j.smi.OID; import org.snmp4j.smi.VariableBinding; import com.boundary.sdk.event.RawEvent; import com.boundary.sdk.event.Severity; import com.boundary.sdk.event.Status; /** * Implements {@link Processor} interface and is responsible for converting * a {@link PDU} to a {@link RawEvent}. * * @author davidg * */ public class SnmpToEventProcessor implements Processor { private static final Logger LOG = LoggerFactory.getLogger(SnmpToEventProcessor.class); SmiSupport smi; private final static String HOSTNAME_PROPERTY_NAME = "hostname"; private final static String SENDER_REF = "Boundary Event SDK"; private final static String SENDER_TYPE = "Boundary Event SDK"; /** * * @param repositoryPath Path to compiled MIBs * @param license SNMP4J-SMI license from <a href="http://www.snmp4j.org">http://www.snmp4j.org</a> */ public SnmpToEventProcessor(String repositoryPath, String license) { // If the repositoryPath is null or zero length // then bypass configuring the structured management information. if (repositoryPath != null && repositoryPath.length() > 0) { smi = new SmiSupport(); smi.setLicense(license); // TBD: Assert if repositoryPath is empty File mibDirectory = new File(repositoryPath); smi.setRepository(mibDirectory.getAbsolutePath()); smi.initialize(); smi.loadModules(); } else { throw new IllegalStateException("MIB repository path is null or empty."); } } /** * Returns the current path to the MIB repository * * @return {@link String} */ public String getMibRepository() { return smi.getRepository(); } /* * Returns the current SNMP4J-SMI license */ public String getLicense() { return smi.getLicense(); } // TODO: Generalize mechanism for handling mapping of variable bindings private void mapVarbindToEvent(RawEvent event, VariableBinding var) { //TODO: Remove log statement when generalized mechanism for mapping variable binds is complete //LOG.warn("var.toString(): " + var.toString() + ", var.toValueString(): " + var.toValueString() + ",var.getOid(): " + var.getOid()); if (var.getOid().toString().equals("xTrapText")) { LOG.debug("Setting varible binding \"{}\" to \"{}\"",var.getOid().toString(),var.toValueString()); event.setMessage(var.toValueString()); } } /** * Converts an {@link SnmpMessage}/{@link PDUv1} into a {@link RawEvent} * * @param message - Contains a {@link SnmpMessage} * @param pdu - Contains a {@link PDUv1} with the actual trap contents * @param event - {@link RawEvent} to be populated. */ public void processV1Trap(SnmpMessage message, PDU pdu, RawEvent event) { PDUv1 v1pdu = (PDUv1) pdu; String hostname = getPeerAddress(message); // CREATED_AT - Set from timestamp on the PDU event.setCreatedAt(new Date()); // FINGERPRINT_FIELDS event.addFingerprintField(HOSTNAME_PROPERTY_NAME); event.addFingerprintField("enterprise"); // MESSAGE - TBD: Set based on the content of the trap? // ORGANIZATION_ID - TBD: Override based on content of trap? // PROPERTIES event.addProperty(HOSTNAME_PROPERTY_NAME, hostname); event.addProperty("enterprise", v1pdu.getEnterprise().toString()); event.addProperty("agent-addr", v1pdu.getAgentAddress().toString()); event.addProperty("generic-trap", Integer.toString(v1pdu.getGenericTrap())); event.addProperty("trap",Integer.toString(v1pdu.getSpecificTrap())); event.addProperty("time-stamp", Long.toString(v1pdu.getTimestamp())); // RECEIVED_AT - Default to value set by Boundary // SENDER - Defaults to the Boundary Event SDK event.getSender().setRef(SENDER_REF).setType(SENDER_TYPE); // SEVERITY - TBD: Set severity based on content of trap? event.setSeverity(Severity.WARN); // SOURCE event.getSource().setRef(hostname).setType("host"); //STATUS - TBD: Depends on trap fields? event.setStatus(Status.OPEN); // TAGS event.addTag(hostname); // TITLE event.setTitle(Integer.toString(v1pdu.getSpecificTrap()) + " trap received from " + hostname); } /** * Converts an {@link SnmpMessage}/{@link PDU} into a {@link RawEvent} * * @param message - Contains a {@link SnmpMessage} * @param pdu - Contains a {@link PDU} with the actual trap contents * @param event - {@link RawEvent} to be populated. */ public void processV2Trap(SnmpMessage message, PDU pdu, RawEvent event) { String specificTrap = ""; String hostname = getPeerAddress(message); // CREATED_AT event.setCreatedAt(new Date()); // FINGERPRINT_FIELDS event.addFingerprintField(HOSTNAME_PROPERTY_NAME); // ORGANIZATION_ID - TBD: Override based on content of the PDU? // PROPERTIES event.addProperty(HOSTNAME_PROPERTY_NAME, hostname); event.addProperty("error_status", pdu.getErrorStatusText()); // Get the variable bindings from the trap and create properties in the event Vector<? extends VariableBinding> varBinds = pdu.getVariableBindings(); for (VariableBinding var : varBinds) { OID oid = var.getOid(); if (oid.startsWith(SnmpConstants.snmpTraps) || oid.startsWith(SnmpConstants.snmpTrapOID)) { specificTrap = var.toValueString(); event.addProperty("trap",specificTrap); // MESSAGE event.setMessage(var.toValueString()); } else { event.addProperty(var.getOid().toString(),var.toValueString()); event.addFingerprintField(var.getOid().toString()); } mapVarbindToEvent(event,var); } // RECEIVED_AT - Default to value set by Boundary // SENDER event.getSender().setRef(SENDER_REF).setType(SENDER_TYPE); // SEVERITY - TBD: set the severity based on content of the trap event.setSeverity(Severity.WARN); // SOURCE event.getSource().setRef(hostname).setType("host"); //STATUS - TBD: Set status based on severity?? event.setStatus(Status.OPEN); // TAGS event.addTag(hostname); // TITLE event.setTitle(specificTrap + " trap received from " + hostname); } /** * Help function to get the peer address from an {@link SnmpMessage}. * * @param message * @return String */ private String getPeerAddress(SnmpMessage message) { String [] s = message.getHeader("peerAddress").toString().split("/"); String address = s[0]; return address; } /** * Handles the translation of a {@link SnmpMessage} to {@link RawEvent} * */ @Override public void process(Exchange exchange) throws Exception { // Extract the SnmpMessage and PDU instances from the Camel Exchange Message message = exchange.getIn(); SnmpMessage snmpMessage = message.getBody(SnmpMessage.class); PDU pdu = snmpMessage.getSnmpMessage(); // Create our event so that we can populate with SNMP data. RawEvent event = new RawEvent(); // Dispatch based on PDU type of the trap // TBD: Handling of v3 PDU traps if (pdu.getType() == PDU.V1TRAP) { processV1Trap(snmpMessage, pdu, event); } else { processV2Trap(snmpMessage, pdu, event); } // Dump out the contents of our event for debugging purposes LOG.debug("RawEvent: " + event); // Assign the RawEvent to the body message.setBody(event, RawEvent.class); } }