Skip to main content

SNMP Trapd LAM v2 and Indirect Traps

An indirect trap is an SNMP trap payload sent to Moogsoft Onprem encapsulated in another transport, for example, as a REST, Kafka, Syslog, etc. payload. This may occur where SNMP traps are received by an underlying system such as ServiceNow, Splunk, Elastic or similar system, or transported via a common event bus.

The TrapdLamV2 allows indirect traps to be processed by any LAMbot using the existing include files. Traps can be processed directly (via the trapd_lam) and indirectly at the same time, and without any modifications needed to the include file.

Indirect processing is achieved by converting the indirect payload (for example OID key:value pairs) into a format suitable for the include files.

Prerequisites

  • TrapdLamV2 bundle installed.

  • A suitable payload:

    • Varbind key:value pairs

    • Trap meta-data

      Version, source, trap identification (enterprise, specific, generic, snmpTrapOID or similar)

  • The MIBs to support the indirect traps

    The trap processing will still need the appropriate MIBs to process an indirect trap.

Suitable payloads

To process an indirect trap, the LAMbot needs to convert the incoming payload into "proto-trap" - a set of OID key:value pairs, and suitable trap meta data (version, source, etc.) to allow the trap processing to normalize, route, and process the data via the correct include file as if it were being received directly via the trapd_lam.

The simplest payload is an OID key:value pair.

For example:

{
  ".1.3.6.1.4.1.116.5.11.4.2.1" : 1 
  ".1.3.6.1.4.1.116.5.11.4.2.2" : "A value for eventTrapNickname",
  ".1.3.6.1.4.1.116.5.11.4.2.3" : "A value for eventTrapREFCODE" ,
  ".1.3.6.1.4.1.116.5.11.4.2.4" : "1.3.6.1.4.1.116.5.11.4.1.1.6.1.2",
  ".1.3.6.1.4.1.116.5.11.4.2.5" : "A value for eventTrapDate" ,
  ".1.3.6.1.4.1.116.5.11.4.2.6" : "A value for eventTrapTime" ,
  ".1.3.6.1.4.1.116.5.11.4.2.7" : "A value for eventTrapDescription" 
}

But partial or full translation also accepted:

{
  "SNMPv2-SMI::enterprises.116.5.11.4.2.1" : 1 
  "SNMPv2-SMI::enterprises.116.5.11.4.2.2" : "A value for eventTrapNickname",
  "SNMPv2-SMI::enterprises.116.5.11.4.2.3" : "A value for eventTrapREFCODE" ,
  "SNMPv2-SMI::enterprises.116.5.11.4.2.4" : "1.3.6.1.4.1.116.5.11.4.1.1.6.1.2",
  "SNMPv2-SMI::enterprises.116.5.11.4.2.5" : "A value for eventTrapDate" ,
  "SNMPv2-SMI::enterprises.116.5.11.4.2.6" : "A value for eventTrapTime" ,
  "SNMPv2-SMI::enterprises.116.5.11.4.2.7" : "A value for eventTrapDescription" 
}

Note

If it is possible to control the source format, aim to get as close to the raw “OID : value” pairs as possible. Full translation can lead to issues over duplicate object names. For example, a fully translated name of “ifIndex” with no MIB module qualifier could come from multiple mibs.

Required payload components

Any indirect trap must be able to supply the following required data to be processed:

SNMP v1
  • A source - generally the agent address is the most authoritative

  • A generic trap number 0-6

  • A specific trap number

  • An enterprise OID (or name that can be translated)

  • An optional set of varbinds

SNMP v2c or SNMP v3 (inform or notification)
  • A source - either explicitly defined or as the snmpTrapAddress varbind (as from a forwarded trap)

  • An snmpTrapOID - the unique OID (or name that can be translated)

  • An optional set of varbinds.

Example SNMP V1 REST Payload

{
    "agent_address" : "10.0.0.1",
    "trap_address" : "127.0.0.1",
    "version" : "1",
    "enterprise" : ".1.3.6.1.4.1.116.3.11.4.1.1",
    "generic_code" : 6,
    "specific_code" : 1,
    "trap_oid" : ".0.0",
    "varbinds"  [
      { "oid" : ".1.3.6.1.4.1.116.5.11.4.2.1", "type" : "integer" , "value" : 1 },
      { "oid" : ".1.3.6.1.4.1.116.5.11.4.2.2", "type" : "string" , "value" : "A value for eventTrapNickname" },
      { "oid" : ".1.3.6.1.4.1.116.5.11.4.2.3", "type" : "string" , "value" : "A value for eventTrapREFCODE" },
      { "oid" : ".1.3.6.1.4.1.116.5.11.4.2.4", "type" : "oid" , "value" : "1.3.6.1.4.1.116.5.11.4.1.1.6.1.2""},
      { "oid" : ".1.3.6.1.4.1.116.5.11.4.2.5", "type" : "string" , "value" : "A value for eventTrapDate" },
      { "oid" : ".1.3.6.1.4.1.116.5.11.4.2.6", "type" : "string" , "value" : "A value for eventTrapTime" },
      { "oid" : ".1.3.6.1.4.1.116.5.11.4.2.7", "type" : "string" , "value" : "A value for eventTrapDescription" }
    ]
}

Example SNMP v2c REST Payload

{
    "trap_address" : "10.0.0.1",
    "version" : "2c",
    "trap_oid" : ".1.3.6.1.4.1.17163.2.1.1.6",
    "varbinds" : [
      { "oid" : ".1.3.6.1.6.3.1.1.4.1.0", "type" : "oid" , "value" :  ".1.3.6.1.4.1.17163.2.1.1.6" },
      { "oid" : ".1.3.6.1.4.1.17163.2.1.2.21", "type" : "string" , "value" :  "A value for auditUsername" },
      { "oid" : ".1.3.6.1.4.1.17163.2.1.2.33", "type" : "integer" , "value" : 1 },
      { "oid" : ".1.3.6.1.4.1.17163.2.1.2.29", "type" : "string" , "value" : "A value for auditRESTNamespace" },
      { "oid" : ".1.3.6.1.4.1.17163.2.1.2.34", "type" : "string" , "value" : "A value for auditIdentifier" },
      { "oid" : ".1.3.6.1.4.1.17163.2.1.2.17", "type" : "string" , "value" : "A value for auditTimestamp" },
      { "oid" : ".1.3.6.1.4.1.17163.2.1.2.27", "type" : "integer" , "value" : 1 },
      { "oid" : ".1.3.6.1.4.1.17163.2.1.2.18", "type" : "string" , "value" : "A value for auditAction" }, 
      { "oid" : ".1.3.6.1.4.1.17163.2.1.2.28", "type" : "string" , "value" : "A value for auditRESTLink" }, 
      { "oid" : ".1.3.6.1.4.1.17163.2.1.2.22", "type" : "string" , "value" : "A value for auditIPAddr" },
      { "oid" : ".1.3.6.1.4.1.17163.2.1.2.19", "type" : "string" , "value" : "A value for auditEventType" },
      { "oid" : ".1.3.6.1.4.1.17163.2.1.2.30", "type" : "string" , "value" : "A value for auditRESTVersion" },
      { "oid" : ".1.3.6.1.4.1.17163.2.1.2.31", "type" : "string" , "value" : "A value for auditRESTResource" },
      { "oid" : ".1.3.6.1.4.1.17163.2.1.2.26", "type" : "string" , "value" : "A value for auditModule" }, 
      { "oid" : ".1.3.6.1.4.1.17163.2.1.2.32", "type" : "string" , "value" : "A value for auditDetails" },
      { "oid" : ".1.3.6.1.4.1.17163.2.1.2.20", "type" : "integer" , "value" : 1 },
      { "oid" : ".1.3.6.1.4.1.17163.2.1.2.23", "type" : "string" , "value" : "A value for auditUserAgent" },
      { "oid" : ".1.3.6.1.4.1.17163.2.1.2.24", "type" : "integer" , "value" : 1 },
      { "oid" : ".1.3.6.1.4.1.17163.2.1.2.25", "type" : "string" , "value" : "A value for auditSID" }
    ]
}

Creating a LAMbot to process Indirect Traps

Important

The MoogTrapdLamV2.js has example code for processing both v1 and v2c indirect traps.

In this example we are going to use the basic REST LAM and a custom LAMbot to process a basic OID key:value payload. The v1 and v2c payloads will contain the OID:key value pairs and also the v1 or v2c specific metadata. In this case, the varbinds are presented as a list and include the data type. The payload you have will be different, and you will need to adjust the code as needed to adapt to your unique payload structure.

Step 1

Ensure that the appropriate MIBs are included in a precompiledMib file generated from the mibparser.

mibparser -i $MOOGSOFT_HOME/etc/mibs -o $MOOGSOFT_HOME/etc/precompiledMibs.EXAMPLE.json

Step 2

Create a new lam config file using the rest_lam.conf as a template.

cp $MOOGSOFT_HOME/config/rest_lam.conf $MOOGSOFT_HO?ME/config/rest_trap.conf

Step 3

Edit the rest_trap.conf file, add the required port number, credentials and other information, and modify the “mapping” and “filter” sections to remove all mapping apart from severity and agent_time:

 mapping :
        {
            catchAll: "overflow",
            rules:
            [
                { name: "signature", rule:      "" },
                { name: "source_id", rule:      "" },
                { name: "external_id", rule:    "" },
                { name: "manager", rule:        "" },
                { name: "source", rule:         "" },
                { name: "class", rule:          "" },
                { name: "agent", rule:          "" },
                { name: "agent_location", rule: "" },
                { name: "type", rule:           "" },
                { name: "severity", rule:       "1", conversion: "stringToInt" },
                { name: "description", rule:    "" },
                { name: "agent_time", rule:     "$moog_now" }
            ]
        },
        filter:
        {
            presend: "RestTrap.js"
        }

Important

Notice the filter section contains a custom LAMbot (RestTrap.js) and not the standard MoogTrapdLamV2.js.

Ensure that accept_all_json is set to true if the Moogsoft event format is not being followed.

Step 4

In the agent section, disable LAMbot optimization:

        agent:
        {
                #---------------------------------------------
                # The agent name that will appear as
                # $LamInstanceName in the mapping rules below
                #---------------------------------------------
                name    : "REST_TRAP",
                lambot_optimization : -1
                #,capture_log: "$MOOGSOFT_HOME/log/data-capture/rest_lam.log"
        },

Step 5

Create a blank $MOOGSOFT_HOME/bots/lambots/RestTrap.js and add the following lines:

Load and initialize the required LAMbot, trap utility, and associated modules for processing the indirect trap
// ----------------------------------------------------------------------------------------
// Initialise
// ----------------------------------------------------------------------------------------
// Load standard modules

var logger = LamBot.loadModule("Logger");
var constants = LamBot.loadModule("Constants");
var mibDb = LamBot.loadModule("MibDb");
var utilities = LamBot.loadModule("Utilities");

// ----------------------------------------------------------------------------------------
// Load the utility modules
// ----------------------------------------------------------------------------------------

LamBot.loadModule("BotUtility.js");
LamBot.loadModule("SyslogUtil.js");
LamBot.loadModule("SyslogEvents.js");
LamBot.loadModule("RegExpUtil.js");
LamBot.loadModule("TrapUtility.js");

// ----------------------------------------------------------------------------------------
// Initialise utiity modules and pass to the TrapUtility module.
// ----------------------------------------------------------------------------------------

var botUtil = new BotUtility();
var syslogUtil = new SyslogUtil(botUtil);
var regExpUtil = new RegExpUtil(botUtil);
var regExpLib = regExpUtil.buildRegExpLib();

var botModules = {
    "constants" : constants,
    "botUtil" : botUtil,
    "mibDb" : mibDb,
    "syslogUtil" : syslogUtil,
    "regExpLib" : regExpLib,
    "utilities" : utilities
};
Initialize the trapUtility and mibDB
// ----------------------------------------------------------------------------------------
// Instaniate the trapUtility.
// Load the precompiled mibs
// ----------------------------------------------------------------------------------------

var trapUtil = new TrapUtility(botModules);

mibDb.loadMibDb("/usr/share/moogsoft/etc/precompiledMibs.EXAMPLE.json");

Line 9 above is loading the precompiled MIB file created in step 1, above.

Define the onLoad() function
function onLoad() {

  // ------------------------------------------------------------------------------------
  // Intitialise the trapUtility passing the include file
  // if no include file is specified, the default trapModules/master.includes will be
  // used.
  //
  // trapUtil.init("trapModules/master.custom.includes");
  //
  // Log the loaded modules and the routing keys:
  // (only shown at debug level)
  //
  // trapUtil.logLoadedModules();
  // trapUtil.logTrapRoutes();
  // ------------------------------------------------------------------------------------
  // trapUtil.init("trapModules/master.none.includes");

  trapUtil.init("trapModules/master.includes");

  // ------------------------------------------------------------------------------------
  // Enable trap debug (trap payload will be added to final event).
  // Note: this can lead to large event sizes.
  // ------------------------------------------------------------------------------------
  
  trapUtil.setTrapDebug(true);
  trapUtil.logLoadedModules();
  trapUtil.logTrapRoutes();
  
  // ------------------------------------------------------------------------------------

  return true;
}

This code is executed when the LAM first loads. This initializes the trap utility (trapUtil), passing the master.includes files that will be used to define the list of include files to include for processing.

The three trapUtil functions (setTrapDebug, logLoadedModules, logTrapRoutes) are useful for development and debug, but do not need to be included for production processing.

Declare the presend function
LamBot.filterFunction("presend");
Define the presend() function
function presend(event) {

  // ----------------------------------------------------
  // Build the trap, and add varbinds.
  // ----------------------------------------------------

  var overflow = botUtil.getOverflow(event);

  if ( !overflow ) {
    logger.warning("Failed to retrieve overflow, dropping event");
    return false;
  }

All of the indirect trap payload will be held in “overflow” as there was no mapping defined in the rest_trap.conf configuration.

Check for required attributes in the payload

This code will be different for each indirect source - adjust your code as needed. Our example payload has a “version” key which we will use to switch between v1 and v2c processing paths.

  var version = overflow.version.toString();
  var trap = null;
  if ( version === "1" ) {
      // V1 processing 
      ...
  }
  else if ( /^(2(c)?|3)$/.test(version) ) {
      // V2c | 3 processing 
      ...
  }
  else {
    logger.warning("Unrecognised SNMP version: " + overflow.version + ", dropping event.");
    return false;
  }
v1 processing

In our v1 example payload, the trap structure is:

{
    "agent_address" : "<ip>",
    "trap_address" : "<ip>",
    "version" : "1",
    "enterprise" : "<oid>",
    "generic_code" : n,
    "specific_code" : n,
    "trap_oid" : "<oid>",
    "varbinds"  [
      ...
    ]
}

We’ll add code to our v1 block to check for the required items - using the botUtil.checkConfig() utility passing the overflow and a list of required items.

 if ( version === "1" ) {
    
    // Check our v1 payload has the minimum required items to proceed.
    var requiredV1items = [ "agent_address", "enterprise", "generic_code", "specific_code" ];
    if ( !botUtil.checkConfig(overflow,requiredV1items) ) {
        logger.warning("Failed to find SNMP v1 trap required items in overflow, dropping event.");
        return false;
    }

Note

Subsequent code for V1 will be within this code block - see the Full LAMbot for a full structure.

Add code to create a V1 "trap" and populate the values from overflow
    // Create a v1 "trap" and populate the required fields
    // for a v1 trap
    // - version
    // - enterprise
    // - source
    // - generic
    // - specific
    // - varbinds

    trap = new trapUtil.Trap();
    trap.setSnmpVersion(1);
    trap.setEnterprise(overflow.enterprise)
    trap.setSpecificCode(overflow.specific_code);
    trap.setGenericCode(overflow.generic_code);
    trap.setSource(overflow.agent_address);

Note

See the Trap functions for a full list of available functions

  • version - setting to v1

  • enterprise - set to the “enterprise” value.

  • specific - set to the “specific_code” value

  • generic - set to the “generic_code” value

  • source - In this case we are setting the source to the agent_address - which for a v1 trap would be considered the authoritative source (overriding trap_source).

v2c | v3 processing

In our v2 payload the structure is:

{
    "trap_address" : "<ip>",
    "version" : "2c",
    "trap_oid" : "<oid>",
    "varbinds" : [
        ...
    ]
}

We’ll add code to the v2c/3 block to check for the required items, create a v2c trap, and populate the required fields:

...
 else if ( /^(2(c)?|3)$/.test(version) ) {

    // Create a v2 "trap" and populate the required fields
    // for a v2c / 3 trap.
    // - version
    // - source - either explicitly or via addition of the snmpTrapAddress OID
    //   as a varbind (1.3.6.1.6.3.18.1.3.0)

    var requiredV2items = [ "trap_address", "trap_oid" ];
    if ( !botUtil.checkConfig(overflow,requiredV2items) ) {
        logger.warning("Failed to find SNMP v2c trap required items in overflow, dropping event.");
        return false;
    }

    trap = new trapUtil.Trap();
    trap.setSnmpVersion(2);
    trap.setSnmpTrapOID(overflow.trap_oid);
    trap.setSource(overflow.trap_address);

  }

We now have a “trap” object with the correct characteristics for a v1 or v2c trap - we now want to add varbinds to this. Both the v1 and v2c incoming payloads have a list of varbind objects - and we will iterate through these adding these to the trap object.

At the end of the initial processing block add a loop to iterate over the varbinds and add these to the trap:

  // Populate varbinds - we are expecting a list of objects with a 'oid' and 'value' key.
  if ( botUtil.isPopulatedList(overflow.varbinds) ) {
      
      overflow.varbinds.forEach(function(varbind) {
          if ( typeof varbind.oid !== 'undefined' && typeof varbind.value !== 'undefined' ) {
              trap.addVarbind(varbind.oid,varbind.value);
          }
      });
  }

Notes on adding varbinds:

  • Varbinds values do not need to be coerced to the correct data type (the eventual trap processing will do this in the include file if needed)

  • Full OIDs do not need to be translated - the addVarbind() function will do the translation.

Validate, process and dispatch the event

At this stage we have a fully formed “trap” containing both meta data and varbind data. This then needs to be processed via the appropriate include file.

  // ----------------------------------------------------
  // Validate the trap contains enough data to proceed.
  // ----------------------------------------------------

  var validated = trap.validate();
  if ( !validated ) {
    logger.warning("Trap failed validation - generating ingeston alert");
    botUtil.printObj(trap,"failed to validate");
    botUtil.generateIngestEvent(event,"Trap failed to validate");
    return true;
  }

  // ----------------------------------------------------
  // Process the trap, prepare and despatch
  // ----------------------------------------------------

  var trapEvent = trap.processTrap();

  var eventOk = trapUtil.prepareEvent(event,trapEvent);
  if ( !eventOk ) {
    logger.warning("Trap failed validation - generating ingeston alert");
    botUtil.printObj(trapEvent,"failed to validate");
    botUtil.generateIngestEvent(event,"Trap failed to validate");
    return true;
  }

  // ------------------------------------------------------------------------------------
  // Check for a stream in the trapEvent and despatch.
  // ------------------------------------------------------------------------------------

  if ( trapEvent.streamName ) {
    return( { passed: true, stream: trapEvent.streamName } );
  }
  else {
    return(true);
  }

The TrapUtility provides functions to validate the “proto-trap” that has been created, process this via the appropriate include file and convert the resulting data into the originally received event:

  • validate() - ensure that the “trap” has enough data to continue processing.

    This validation will perform the following actions:

    • Validate the required meta-data for a v1 or v2c/3 trap.

    • If the source of the trap is an IP address - attempt to DNS resolve this (as the trapd_lam would do). This resolution uses the built in LAM DNS resolution utility and caches the results, so does not introduce any significant overhead.

  • processTrap() - pass the “trap” to the processing layer, find the correct include file and run the appropriate function within it.

  • prepareEvent() - take the output of the include processing, and copy these details into the original event.

If any of these steps fail, an ingestion error event will be produced - this details the source data and any reason for failure. Ingestion events are created as unique alerts in Moogsoft Onprem, allowing an operator or administrator to easily see where the indirect trap processing has failed allowing remedial action to be taken.

Full LAMbot

Payloads used:

V1:

{
    "agent_address" : "10.0.0.1",
    "trap_address" : "127.0.0.1",
    "version" : "1",
    "enterprise" : ".1.3.6.1.4.1.116.3.11.4.1.1",
    "generic_code" : 6,
    "specific_code" : 1,
    "trap_oid" : ".0.0",
    "varbinds" : [
      { "oid" : ".1.3.6.1.4.1.116.5.11.4.2.1", "type" : "integer" , "value" : 1 },
      { "oid" : ".1.3.6.1.4.1.116.5.11.4.2.2", "type" : "string" , "value" : "A value for eventTrapNickname" },
      { "oid" : ".1.3.6.1.4.1.116.5.11.4.2.3", "type" : "string" , "value" : "A value for eventTrapREFCODE" },
      { "oid" : ".1.3.6.1.4.1.116.5.11.4.2.4", "type" : "oid" , "value" : ".0.0"},
      { "oid" : ".1.3.6.1.4.1.116.5.11.4.2.5", "type" : "string" , "value" : "A value for eventTrapDate" },
      { "oid" : ".1.3.6.1.4.1.116.5.11.4.2.6", "type" : "string" , "value" : "A value for eventTrapTime" },
      { "oid" : ".1.3.6.1.4.1.116.5.11.4.2.7", "type" : "string" , "value" : "A value for eventTrapDescription" }
    ]
}

V2:

{
    "agent_address" : "10.0.0.1",
    "trap_address" : "10.0.0.1",
    "version" : "2c",
    "enterprise" : "0.0",
    "generic_code" : 0,
    "specific_code" : 0,
    "trap_oid" : ".1.3.6.1.4.1.17163.2.1.1.6",
    "varbinds" : [
      { "oid" : ".1.3.6.1.6.3.1.1.4.1.0", "type" : "oid" , "value" :  ".1.3.6.1.4.1.17163.2.1.1.6" },
      { "oid" : ".1.3.6.1.4.1.17163.2.1.2.21", "type" : "string" , "value" :  "A value for auditUsername" },
      { "oid" : ".1.3.6.1.4.1.17163.2.1.2.33", "type" : "integer" , "value" : 1 },
      { "oid" : ".1.3.6.1.4.1.17163.2.1.2.29", "type" : "string" , "value" : "A value for auditRESTNamespace" },
      { "oid" : ".1.3.6.1.4.1.17163.2.1.2.34", "type" : "string" , "value" : "A value for auditIdentifier" },
      { "oid" : ".1.3.6.1.4.1.17163.2.1.2.17", "type" : "string" , "value" : "A value for auditTimestamp" },
      { "oid" : ".1.3.6.1.4.1.17163.2.1.2.27", "type" : "integer" , "value" : 1 },
      { "oid" : ".1.3.6.1.4.1.17163.2.1.2.18", "type" : "string" , "value" : "A value for auditAction" }, 
      { "oid" : ".1.3.6.1.4.1.17163.2.1.2.28", "type" : "string" , "value" : "A value for auditRESTLink" }, 
      { "oid" : ".1.3.6.1.4.1.17163.2.1.2.22", "type" : "string" , "value" : "A value for auditIPAddr" },
      { "oid" : ".1.3.6.1.4.1.17163.2.1.2.19", "type" : "string" , "value" : "A value for auditEventType" },
      { "oid" : ".1.3.6.1.4.1.17163.2.1.2.30", "type" : "string" , "value" : "A value for auditRESTVersion" },
      { "oid" : ".1.3.6.1.4.1.17163.2.1.2.31", "type" : "string" , "value" : "A value for auditRESTResource" },
      { "oid" : ".1.3.6.1.4.1.17163.2.1.2.26", "type" : "string" , "value" : "A value for auditModule" }, 
      { "oid" : ".1.3.6.1.4.1.17163.2.1.2.32", "type" : "string" , "value" : "A value for auditDetails" },
      { "oid" : ".1.3.6.1.4.1.17163.2.1.2.20", "type" : "integer" , "value" : 1 },
      { "oid" : ".1.3.6.1.4.1.17163.2.1.2.23", "type" : "string" , "value" : "A value for auditUserAgent" },
      { "oid" : ".1.3.6.1.4.1.17163.2.1.2.24", "type" : "integer" , "value" : 1 },
      { "oid" : ".1.3.6.1.4.1.17163.2.1.2.25", "type" : "string" , "value" : "A value for auditSID" }
    ]
}

LAMbot:

/*******************************************************************************
 *                       Copyright (c) Moogsoft Inc 2021                       *
 *                                                                             *
 *-----------------------------------------------------------------------------*
 *                                                                             *
 * All information contained herein is and remains the property of Moogsoft    *
 * Inc. The intellectual and technical concepts contained herein may be        *
 * covered by U.S. and Foreign Patents or patents in process, are protected by *
 * trade secret law, and cannot be disseminated beyond authorized users.       *
 *                                                                             *
 * This source code is confidential and protected by copyright law. Any person *
 * who believes that they are viewing it without permission is asked to notify *
 * Moogsoft Inc.'s legal department at legal@moogsoft.com. Dissemination of    *
 * this information in any form or by any means is strictly forbidden unless   *
 * prior written permission is obtained from Moogsoft Inc. This source code    *
 * may be modified for purposes of configuration or integration into Moogsoft  *
 * Inc.'s authorized customer's computer systems in accordance with the terms  *
 * of the customer's license.                                                  *
 *                                                                             *
 * If you are uncertain about the means in which you may modify this source    *
 * code, contact your Moogsoft Inc. customer representative or Moogsoft Inc.'s *
 * legal department at legal@moogsoft.com.                                     *
 *                                                                             *
 *******************************************************************************/

// ----------------------------------------------------------------------------------------
// Initialise
// ----------------------------------------------------------------------------------------
// Load standard modules

var logger = LamBot.loadModule("Logger");
var constants = LamBot.loadModule("Constants");
var mibDb = LamBot.loadModule("MibDb");
var utilities = LamBot.loadModule("Utilities");

// ----------------------------------------------------------------------------------------
// Load the utility modules 
// ----------------------------------------------------------------------------------------

LamBot.loadModule("BotUtility.js");
LamBot.loadModule("SyslogUtil.js");
LamBot.loadModule("SyslogEvents.js");
LamBot.loadModule("RegExpUtil.js");
LamBot.loadModule("TrapUtility.js");

// ----------------------------------------------------------------------------------------
// Initialise utiity modules and pass to the TrapUtility module.
// ----------------------------------------------------------------------------------------

var botUtil = new BotUtility();
var syslogUtil = new SyslogUtil(botUtil);
var regExpUtil = new RegExpUtil(botUtil);
var regExpLib = regExpUtil.buildRegExpLib();

var botModules = {
    "constants" : constants,
    "botUtil" : botUtil,
    "mibDb" : mibDb,
    "syslogUtil" : syslogUtil,
    "regExpLib" : regExpLib,
    "utilities" : utilities
};

// ----------------------------------------------------------------------------------------
// Instaniate the trapUtility. 
// Load the precompiled mibs 
// ----------------------------------------------------------------------------------------

var trapUtil = new TrapUtility(botModules);

mibDb.loadMibDb("/usr/share/moogsoft/etc/precompiledMibs.EXAMPLE.json");

// ----------------------------------------------------------------------------------------

function onLoad() {

  // ------------------------------------------------------------------------------------
  // Intitialise the trapUtility passing the include file 
  // if no include file is specified, the default trapModules/master.includes will be 
  // used.
  //
  // trapUtil.init("trapModules/master.custom.includes");
  // 
  // Log the loaded modules and the routing keys:
  // (only shown at debug level)
  // 
  // trapUtil.logLoadedModules();
  // trapUtil.logTrapRoutes();
  // ------------------------------------------------------------------------------------
  // trapUtil.init("trapModules/master.none.includes");

  trapUtil.init("trapModules/master.includes");

  // ------------------------------------------------------------------------------------
  // Enable trap debug (trap payload will be added to final event).
  // Note: this can lead to large event sizes. 
  // ------------------------------------------------------------------------------------
  //
  // trapUtil.setTrapDebug(true);

  trapUtil.logLoadedModules();

  // ------------------------------------------------------------------------------------

  return true;
}


//
// Tell the LamBot that we filter using the presend function
//

LamBot.filterFunction("presend");


function presend(event) { 

  // ----------------------------------------------------
  // Build the trap, and add varbinds. 
  // ----------------------------------------------------

  var overflow = botUtil.getOverflow(event);

  if ( !overflow ) {
    logger.warning("Failed to retrieve overflow, dropping event.");
    return false;
  }

  // Check to see if we have a version. 

  if ( !overflow.version ) {
    logger.warning("Failed to find SNMP trap version in overflow, dropping event.");
    return false;
  }

  var version = overflow.version.toString();
  var trap = null;

  if ( version === "1" ) {
  
    // Check our v1 payload has the minimum required items to proceed. 

    var requiredV1items = [ "agent_address", "enterprise", "generic_code", "specific_code" ];
    if ( !botUtil.checkConfig(overflow,requiredV1items) ) {
        logger.warning("Failed to find SNMP v1 trap required items in overflow, dropping event.");
        return false;
    }

    // Create a v1 "trap" and populate the required fields 
    // for a v1 trap
    // - version 
    // - enterprise
    // - source 
    // - generic
    // - specific
    // - varbinds
    
    trap = new trapUtil.Trap();
    trap.setSnmpVersion(1);
    trap.setEnterprise(overflow.enterprise);
   trap.setSpecificCode(overflow.specific_code);
    trap.setGenericCode(overflow.generic_code);
    trap.setSource(overflow.agent_address);

  }
  else if ( /^(2(c)?|3)$/.test(version) ) {

    // Create a v2 "trap" and populate the required fields
    // for a v2c / 3 trap.
    // - version
    // - source - either explicitly or via addition of the snmpTrapAddress OID 
    //   as a varbind (1.3.6.1.6.3.18.1.3.0)

    var requiredV2items = [ "trap_address", "trap_oid" ];
    if ( !botUtil.checkConfig(overflow,requiredV2items) ) {
        logger.warning("Failed to find SNMP v2c trap required items in overflow, dropping event.");
        return false;
    }

    trap = new trapUtil.Trap();
    trap.setSnmpVersion(2);
    trap.setSnmpTrapOID(overflow.trap_oid);
    trap.setSource(overflow.trap_address);

  }
  else { 
    logger.warning("Unrecognised SNMP version: " + overflow.version + ", dropping event.");
    return false;
  }

  // ----------------------------------------------------
  // Populate varbinds - we are expecting a list of objects with a 'oid' and 'value' key. 
  // ----------------------------------------------------
  
  if ( botUtil.isPopulatedList(overflow.varbinds) ) {
    
      overflow.varbinds.forEach(function(varbind) {
          if ( typeof varbind.oid !== 'undefined' && typeof varbind.value !== 'undefined' ) {
              trap.addVarbind(varbind.oid,varbind.value);
          }
      });
  }

  // ----------------------------------------------------
  // Validate the trap contains enough data to proceed.
  // ----------------------------------------------------

  var validated = trap.validate();
  if ( !validated ) {
    logger.warning("Trap failed validation - generating ingeston alert");
    botUtil.printObj(trap,"failed to validate");
    botUtil.generateIngestEvent(event,"Trap failed to validate");
    return true;
  }

  // ----------------------------------------------------
  // Process the trap, prepare and despatch
  // ----------------------------------------------------

  var trapEvent = trap.processTrap();

  var eventOk = trapUtil.prepareEvent(event,trapEvent);
  if ( !eventOk ) {
    logger.warning("Trap failed validation - generating ingeston alert");
    botUtil.printObj(trapEvent,"failed to validate");
    botUtil.generateIngestEvent(event,"Trap failed to validate");
    return true;
  }

  // ------------------------------------------------------------------------------------
  // Check for a stream in the trapEvent and despatch.
  // ------------------------------------------------------------------------------------

  if ( trapEvent.streamName ) {
    return( { passed: true, stream: trapEvent.streamName } );
  }
  else {
    return(true);
  }

}