diff --git a/gapit-snmp.html b/gapit-snmp.html
index 27dd0e9..91c5213 100644
--- a/gapit-snmp.html
+++ b/gapit-snmp.html
@@ -3,19 +3,60 @@
-
-
-
-
Timeout
S
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -99,10 +140,19 @@
RED.nodes.registerType('gapit-snmp', {
category: 'network-input',
color: "YellowGreen",
+ credentials: {
+ username: {type:"text"},
+ auth_key: {type:"password"},
+ priv_key: {type:"password"}
+ },
defaults: {
host: { value: "127.0.0.1" },
community: { value: "public" },
version: { value: "1", required: true },
+ context: { value: "" },
+ security_level: { value: "authPriv" },
+ auth_protocol: { value: "sha" },
+ priv_protocol: { value: "des" },
tagname_device_name: { value: "device_name", required: true },
tagvalue_device_name: { value: "default_name", required: true },
minion_ids: { value: "" },
@@ -125,6 +175,7 @@
return this.name ? "node_label_italic" : "";
},
oneditprepare: function () {
+ // field customization
$("#node-input-custom_tags").typedInput({
type:"json",
types:["json"]
@@ -144,6 +195,32 @@
}
]
});
+
+ ///// dynamic UI
+ //
+ // hide fields based on SNMP version
+ $("#node-input-version").on("change", function() {
+ let version = $("#node-input-version").val();
+ let optionsClass = `.snmpoptions_v${version}`;
+ $(".snmpoptions").hide();
+ $(optionsClass).show();
+ });
+
+ // hide fields based on SNMP security level
+ $("#node-input-security_level").on("change", function() {
+ let securityLevel = $("#node-input-security_level").val();
+ let levelClass = `.snmpoptions_v3_sec_${securityLevel}`;
+ $(".snmpoptions_v3_sec").hide();
+ $(levelClass).show();
+ });
+
+ // Trigger change events for initial form setup.
+ // Doesn't seem to work when called immediately,
+ // so use a timer (even 1 msec seems to work).
+ setTimeout(function() {
+ $("#node-input-version").triggerHandler("change");
+ $("#node-input-security_level").triggerHandler("change");
+ }, 1);
},
});
diff --git a/gapit-snmp.js b/gapit-snmp.js
index b8e1590..8f7e8ba 100644
--- a/gapit-snmp.js
+++ b/gapit-snmp.js
@@ -2,23 +2,10 @@
module.exports = function (RED) {
"use strict";
var snmp = require("net-snmp");
+ var crypto = require("crypto");
var sessions = {};
- function getSession(host, community, version, timeout) {
- var sessionKey = host + ":" + community + ":" + version;
- var port = 161;
- if (host.indexOf(":") !== -1) {
- port = host.split(":")[1];
- host = host.split(":")[0];
- }
- if (!(sessionKey in sessions)) {
- sessions[sessionKey] = snmp.createSession(host, community, { port:port, version:version, timeout:(timeout || 5000) });
- }
- return sessions[sessionKey];
- }
-
-
function varbindsParseCounter64Buffers(varbinds, convertToNumbers=true) {
// Counter64 is not directly supported by net-snmp,
// but returned as a buffer (array). This function
@@ -259,7 +246,7 @@ module.exports = function (RED) {
this.blockTuningSteps = 10;
this.config = config;
this.multi_device_separator = ";";
- this.version = (config.version === "2c") ? snmp.Version2c : snmp.Version1;
+ this.version = snmp.Version[config.version];
this.tagname_device_name = config.tagname_device_name.trim();
this.tagvalue_device_name = config.tagvalue_device_name.trim();
// split device_name from config into an array
@@ -345,6 +332,83 @@ module.exports = function (RED) {
console.info("initializing nonexistent_oids in context (set to empty Array)")
nodeContext.set("nonexistent_oids", Array());
+ this.getSession = function () {
+ // check if session already exists, return if it does
+ if (node.sessionKey !== undefined && node.sessionKey in sessions) {
+ console.debug(`Found existing session for ${node.sessionKey}`);
+ return sessions[node.sessionKey];
+ }
+
+ let host = node.config.host;
+ let sessionKey;
+ if (node.version === snmp.Version3) {
+ // include (a hash of) all v3 options to create a unique session key
+ let v3OptionsHash = crypto.createHash('sha256').update(
+ node.config.security_level +
+ node.config.auth_protocol +
+ node.credentials.auth_key +
+ node.config.priv_protocol +
+ node.credentials.priv_key).digest('hex').slice(-8)
+ sessionKey = host + ":" + node.credentials.username + ":" + node.version + ":" + v3OptionsHash;
+ }
+ else { // not SNMPv3
+ sessionKey = host + ":" + node.config.community + ":" + node.version;
+ }
+ var port = 161;
+ if (host.indexOf(":") !== -1) {
+ port = host.split(":")[1];
+ host = host.split(":")[0];
+ }
+
+ console.info(`Creating session for ${sessionKey}`);
+ let options = { port:port, version:node.version, timeout:(node.timeout || 5000) }
+ if (node.version === snmp.Version3) {
+ if (node.config.context.trim() != "") {
+ options.context = node.config.context.trim();
+ }
+ let user = node.createV3UserObject();
+ sessions[sessionKey] = snmp.createV3Session(host, user, options);
+ }
+ else {
+ sessions[sessionKey] = snmp.createSession(host, node.config.community, options);
+ }
+
+ sessions[sessionKey].on("error", function (error) {
+ console.log ("Session error: " + error.toString());
+ node.closeSession();
+ })
+
+ node.sessionKey = sessionKey;
+ return sessions[sessionKey];
+ }
+
+ this.createV3UserObject = function() {
+ // set up user object for SNMPv3
+ // empty object if other version
+ let user = {}
+ if (node.version == snmp.Version3) {
+ user.name = node.credentials.username;
+ user.level = snmp.SecurityLevel[node.config.security_level];
+ if (user.level == snmp.SecurityLevel.authNoPriv || user.level == snmp.SecurityLevel.authPriv) {
+ user.authProtocol = snmp.AuthProtocols[node.config.auth_protocol];
+ user.authKey = node.credentials.auth_key;
+ }
+ if (user.level == snmp.SecurityLevel.authPriv) {
+ user.privProtocol = snmp.PrivProtocols[node.config.priv_protocol];
+ user.privKey = node.credentials.priv_key;
+ }
+ }
+ return user;
+ }
+
+ this.closeSession = function () {
+ if (node.sessionKey !== undefined && node.sessionKey in sessions) {
+ console.debug(`Closing session ${node.sessionKey}`);
+ sessions[node.sessionKey].close();
+ delete sessions[node.sessionKey];
+ }
+ }
+
this.processVarbinds = function (msg, varbinds) {
// parse Counter64 values in varbinds
varbindsParseCounter64Buffers(varbinds, node.config.convert_counter64_bigint_to_number);
@@ -589,7 +653,7 @@ module.exports = function (RED) {
}
this.tuneSnmpBlockSize = function (host, community, oids, msg, blockSize) {
- getSession(host, community, node.version, node.timeout).get(oids.slice(0, blockSize), function (error, varbinds) {
+ node.getSession().get(oids.slice(0, blockSize), function (error, varbinds) {
if (error) {
// error object has .name, .message and, optionally, .status
// error.status is only set for RequestFailed, so check
@@ -644,7 +708,7 @@ module.exports = function (RED) {
for (let blockStart = 0; blockStart < oids.length; blockStart=blockStart+blockSize) {
let oidBlock = oids.slice(blockStart, blockStart+blockSize);
- getSession(host, community, node.version, node.timeout).get(oidBlock, function (error, blockVarbinds) {
+ node.getSession().get(oidBlock, function (error, blockVarbinds) {
msg.totalBlockResponseCount += oidBlock.length;
if (error) {
// error object has .name, .message and, optionally, .status
@@ -723,6 +787,16 @@ module.exports = function (RED) {
node.warn("No oid(s) to search for");
}
});
+
+ this.on("close", function() {
+ node.closeSession();
+ });
}
- RED.nodes.registerType("gapit-snmp", GapitSnmpNode);
+ RED.nodes.registerType("gapit-snmp", GapitSnmpNode, {
+ credentials: {
+ username: {type:"text"},
+ auth_key: {type:"password"},
+ priv_key: {type:"password"}
+ }
+ });
};