Merge pull request #904 from Steve-Mcl/snmpv3_Add_smc

Add SNMPv3 to node-red-node-snmp
This commit is contained in:
Stephen McLaughlin 2022-04-28 20:36:29 +01:00 committed by GitHub
commit c77f13a90f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 833 additions and 235 deletions

View File

@ -1,8 +1,13 @@
node-red-node-snmp node-red-node-snmp
================== ==================
A pair of <a href="http://nodered.org" target="_new">Node-RED</a> nodes that A set of <a href="http://nodered.org" target="_new">Node-RED</a> nodes that
fetch either individual oids, or a table oid from a SNMP enabled host. fetch values from SNMP enabled hosts. Supports v1, v2c and v3.
* SNMP get - Simple SNMP oid or oid list fetcher
* SNMP set - Simple snmp Set node.
* SNMP subtree - Simple sub tree fetcher
* SNMP table - Simple SNMP oid table fetcher
* SNMP walker - Simple SNMP oid walker fetcher
Install Install
------- -------
@ -18,9 +23,15 @@ Usage
SNMP oids fetcher. Can fetch a single or comma separated list of oids. Triggered by any input. SNMP oids fetcher. Can fetch a single or comma separated list of oids. Triggered by any input.
`msg.host` may contain the host. `msg.host` may contain the host including the port.
`msg.community` may contain the community. `msg.community` may contain the community. (v1 and v2c only)
`msg.username` may contain the username. (v3 only)
`msg.authkey` may contain the digest security key. (v3 only)
`msg.privkey` may contain the encryption security key. (v3 only)
`msg.oid` may contain a comma separated list of oids to search for. (no spaces) `msg.oid` may contain a comma separated list of oids to search for. (no spaces)
@ -28,6 +39,12 @@ The host configured in the edit config will override `msg.host`. Leave blank if
The community configured in the edit config will override `msg.community`. Leave blank if you want to use `msg.community` to provide input. The community configured in the edit config will override `msg.community`. Leave blank if you want to use `msg.community` to provide input.
The username configured in the edit config will override `msg.username`. Leave blank if you want to use `msg.username` to provide input.
The digest security key configured in the edit config will override `msg.authkey`. Leave blank if you want to use `msg.authkey` to provide input.
The encryption security key configured in the edit config will override `msg.privkey`. Leave blank if you want to use `msg.privkey` to provide input.
The oids configured in the edit config will override `msg.oid`. Leave blank if you The oids configured in the edit config will override `msg.oid`. Leave blank if you
want to use `msg.oid` to provide input. want to use `msg.oid` to provide input.
@ -38,9 +55,15 @@ Values depends on the oids being requested.
SNMP sets the value of one or more OIDs. SNMP sets the value of one or more OIDs.
`msg.host` may contain the host. `msg.host` may contain the host including the port.
`msg.community` may contain the community. `msg.community` may contain the community. (v1 and v2c only)
`msg.username` may contain the username. (v3 only)
`msg.authkey` may contain the digest security key. (v3 only)
`msg.privkey` may contain the encryption security key. (v3 only)
`msg.varbinds` may contain an array of varbind JSON objects e.g.: `msg.varbinds` may contain an array of varbind JSON objects e.g.:
``` ```
@ -81,6 +104,12 @@ The host configured in the edit config will override `msg.host`. Leave blank if
The community configured in the edit config will override `msg.community`. Leave blank if you want to use `msg.community` to provide input. The community configured in the edit config will override `msg.community`. Leave blank if you want to use `msg.community` to provide input.
The username configured in the edit config will override `msg.username`. Leave blank if you want to use `msg.username` to provide input.
The digest security key configured in the edit config will override `msg.authkey`. Leave blank if you want to use `msg.authkey` to provide input.
The encryption security key configured in the edit config will override `msg.privkey`. Leave blank if you want to use `msg.privkey` to provide input.
The varbinds configured in the edit config will override `msg.varbinds`. Leave blank if you want to use `msg.varbinds` to provide input. The varbinds configured in the edit config will override `msg.varbinds`. Leave blank if you want to use `msg.varbinds` to provide input.
@ -89,16 +118,28 @@ The varbinds configured in the edit config will override `msg.varbinds`. Leave b
Simple SNMP table oid fetcher. Triggered by any input. Simple SNMP table oid fetcher. Triggered by any input.
`msg.host` may contain the host. `msg.host` may contain the host including the port.
`msg.community` may contain the community. `msg.community` may contain the community. (v1 and v2c only)
`msg.oid` may contain the oid of a single table to search for. `msg.username` may contain the username. (v3 only)
`msg.authkey` may contain the digest security key. (v3 only)
`msg.privkey` may contain the encryption security key. (v3 only)
`msg.oid` may contain a comma separated list of oids to search for. (no spaces)
The host configured in the edit config will override `msg.host`. Leave blank if you want to use `msg.host` to provide input. The host configured in the edit config will override `msg.host`. Leave blank if you want to use `msg.host` to provide input.
The community configured in the edit config will override `msg.community`. Leave blank if you want to use `msg.community` to provide input. The community configured in the edit config will override `msg.community`. Leave blank if you want to use `msg.community` to provide input.
The username configured in the edit config will override `msg.username`. Leave blank if you want to use `msg.username` to provide input.
The digest security key configured in the edit config will override `msg.authkey`. Leave blank if you want to use `msg.authkey` to provide input.
The encryption security key configured in the edit config will override `msg.privkey`. Leave blank if you want to use `msg.privkey` to provide input.
The oid configured in the edit config will override `msg.oid`. Leave blank if you The oid configured in the edit config will override `msg.oid`. Leave blank if you
want to use `msg.oid` to provide input. want to use `msg.oid` to provide input.
@ -109,9 +150,15 @@ Values depends on the oids being requested.
Simple SNMP oid subtree fetcher. Triggered by any input. Reads from OID specified and any below it. Simple SNMP oid subtree fetcher. Triggered by any input. Reads from OID specified and any below it.
`msg.host` may contain the host. `msg.host` may contain the host including the port.
`msg.community` may contain the community. `msg.community` may contain the community. (v1 and v2c only)
`msg.username` may contain the username. (v3 only)
`msg.authkey` may contain the digest security key. (v3 only)
`msg.privkey` may contain the encryption security key. (v3 only)
`msg.oid` may contain the oid of a single table to search for. `msg.oid` may contain the oid of a single table to search for.
@ -119,6 +166,12 @@ The host configured in the edit config will override `msg.host`. Leave blank if
The community configured in the edit config will override `msg.community`. Leave blank if you want to use `msg.community` to provide input. The community configured in the edit config will override `msg.community`. Leave blank if you want to use `msg.community` to provide input.
The username configured in the edit config will override `msg.username`. Leave blank if you want to use `msg.username` to provide input.
The digest security key configured in the edit config will override `msg.authkey`. Leave blank if you want to use `msg.authkey` to provide input.
The encryption security key configured in the edit config will override `msg.privkey`. Leave blank if you want to use `msg.privkey` to provide input.
The oid configured in the edit config will override `msg.oid`. Leave blank if you The oid configured in the edit config will override `msg.oid`. Leave blank if you
want to use `msg.oid` to provide input. want to use `msg.oid` to provide input.
@ -129,9 +182,15 @@ Values depends on the oids being requested.
Simple SNMP oid walker fetcher. Triggered by any input. Reads from OID specified to the end of the table. Simple SNMP oid walker fetcher. Triggered by any input. Reads from OID specified to the end of the table.
`msg.host` may contain the host. `msg.host` may contain the host including the port.
`msg.community` may contain the community. `msg.community` may contain the community. (v1 and v2c only)
`msg.username` may contain the username. (v3 only)
`msg.authkey` may contain the digest security key. (v3 only)
`msg.privkey` may contain the encryption security key. (v3 only)
`msg.oid` may contain the oid of a single table to search for. `msg.oid` may contain the oid of a single table to search for.
@ -139,6 +198,12 @@ The host configured in the edit config will override `msg.host`. Leave blank if
The community configured in the edit config will override `msg.community`. Leave blank if you want to use `msg.community` to provide input. The community configured in the edit config will override `msg.community`. Leave blank if you want to use `msg.community` to provide input.
The username configured in the edit config will override `msg.username`. Leave blank if you want to use `msg.username` to provide input.
The digest security key configured in the edit config will override `msg.authkey`. Leave blank if you want to use `msg.authkey` to provide input.
The encryption security key configured in the edit config will override `msg.privkey`. Leave blank if you want to use `msg.privkey` to provide input.
The oid configured in the edit config will override `msg.oid`. Leave blank if you The oid configured in the edit config will override `msg.oid`. Leave blank if you
want to use `msg.oid` to provide input. want to use `msg.oid` to provide input.

View File

@ -1,9 +1,9 @@
{ {
"name" : "node-red-node-snmp", "name" : "node-red-node-snmp",
"version" : "0.0.25", "version" : "1.0.0",
"description" : "A Node-RED node that looks for SNMP oids.", "description" : "A Node-RED node that gets and sets SNMP oid values. Supports v1, v2c and v3",
"dependencies" : { "dependencies" : {
"net-snmp" : "1.2.4" "net-snmp" : "^3.6.3"
}, },
"repository" : { "repository" : {
"type":"git", "type":"git",
@ -11,7 +11,7 @@
"directory" : "tree/master/io/snmp" "directory" : "tree/master/io/snmp"
}, },
"license": "Apache-2.0", "license": "Apache-2.0",
"keywords": [ "node-red", "snmp", "oid" ], "keywords": [ "node-red", "snmp", "oid", "snmpv3" ],
"node-red" : { "node-red" : {
"nodes" : { "nodes" : {
"snmp": "snmp.js" "snmp": "snmp.js"
@ -24,6 +24,8 @@
}, },
"contributors": [ "contributors": [
{ "name": "Mika Karaila" }, { "name": "Mika Karaila" },
{ "name": "Bryan Malyn" } { "name": "Bryan Malyn" },
{ "name": "Steve-Mcl" },
{ "name": "Andres" }
] ]
} }

View File

@ -1,21 +1,102 @@
<style id="node-red-node-snmp-common-style">
.form-row.form-row-snmpv1v2.hidden {
display: none !important;
}
.form-row.form-row-snmpv3.hidden {
display: none !important;
}
.form-row.form-row-snmpv3-auth.hidden {
display: none;
}
</style>
<script type="text/javascript" id="node-red-node-snmp-common-script">
const node_snmp_common = {
oneditprepare: function (node) {
$("#node-input-version").on("change", function(evt) {
const isV3 = $("#node-input-version").val() === "v3";
$(".form-row-snmpv1v2").toggleClass("hidden", isV3);
$(".form-row-snmpv3").toggleClass("hidden", !isV3);
$("#node-input-auth").trigger("change");
});
$("#node-input-auth").on("change", function(evt) {
const isV3 = $("#node-input-version").val() === "v3";
const auth = $("#node-input-auth").val();
if(isV3) {
switch (auth) {
case "authNoPriv":
$(".form-row-snmpv3-auth").toggleClass("hidden", false);
$(".form-row-snmpv3-priv").toggleClass("hidden", true);
break;
case "authPriv":
$(".form-row-snmpv3-auth").toggleClass("hidden", false);
$(".form-row-snmpv3-priv").toggleClass("hidden", false);
break;
default: //"noAuthNoPriv":
$(".form-row-snmpv3-auth").toggleClass("hidden", true);
$(".form-row-snmpv3-priv").toggleClass("hidden", true);
break;
}
}
});
$("#node-input-version").trigger("change");
}
}
</script>
<script type="text/html" data-template-name="snmp"> <script type="text/html" data-template-name="snmp">
<div class="form-row"> <div class="form-row">
<label for="node-input-host"><i class="fa fa-globe"></i> Host</label> <label for="node-input-host"><i class="fa fa-globe"></i> Host</label>
<input type="text" id="node-input-host" placeholder="ip address(:optional port)"> <input type="text" id="node-input-host" placeholder="ip address(:optional port)">
</div> </div>
<div class="form-row">
<label for="node-input-community"><i class="fa fa-user"></i> Community</label>
<input type="text" id="node-input-community" placeholder="public">
</div>
<div class="form-row"> <div class="form-row">
<label for="node-input-version"><i class="fa fa-bookmark"></i> Version</label> <label for="node-input-version"><i class="fa fa-bookmark"></i> Version</label>
<select type="text" id="node-input-version" style="width:150px;"> <select type="text" id="node-input-version" style="width:150px;">
<option value="1">v1</option> <option value="v1">v1</option>
<option value="2c">v2c</option> <option value="v2c">v2c</option>
<option value="v3">v3</option>
</select> </select>
<span style="margin-left:50px;">Timeout</span> <span style="margin-left:50px;">Timeout</span>
<input type="text" id="node-input-timeout" placeholder="secs" style="width:50px; direction:rtl; vertical-align:baseline;">&nbsp;S <input type="text" id="node-input-timeout" placeholder="secs" style="width:50px; direction:rtl; vertical-align:baseline;">&nbsp;S
</div> </div>
<div class="form-row form-row-snmpv1v2">
<label for="node-input-community"><i class="fa fa-user"></i> Community</label>
<input type="text" id="node-input-community" placeholder="public">
</div>
<!-- Following Data is used for V3 Only -->
<div class="form-row form-row-snmpv3">
<label for="node-input-username"><i class="fa fa-user"></i> Username</label>
<input type="text" id="node-input-username" placeholder="username">
</div>
<div class="form-row form-row-snmpv3">
<label for="node-input-auth"><i class="fa fa-user-secret"></i> Auth.</label>
<select type="text" id="node-input-auth" style="width:150px;">
<option value="noAuthNoPriv">noAuthNoPriv</option>
<option value="authNoPriv">authNoPriv</option>
<option value="authPriv">authPriv</option>
</select>
</div>
<div class="form-row form-row-snmpv3 form-row-snmpv3-auth">
<label for="node-input-authprot"><i class="fa fa-shield"></i> Auth.Prot.</label>
<select type="text" id="node-input-authprot" style="width:150px;">
<option value="MD5">MD5</option>
<option value="SHA">SHA</option>
</select>
</div>
<div class="form-row form-row-snmpv3 form-row-snmpv3-auth">
<label for="node-input-authkey"><i class="fa fa-key"></i> Auth.Key</label>
<input type="password" id="node-input-authkey" placeholder="Authentication key">
</div>
<div class="form-row form-row-snmpv3 form-row-snmpv3-priv">
<label for="node-input-privprot"><i class="fa fa-shield"></i> Priv.Prot.</label>
<select type="text" id="node-input-privprot" style="width:150px;">
<option value="DES">DES</option>
<option value="AES">AES</option>
</select>
</div>
<div class="form-row form-row-snmpv3 form-row-snmpv3-priv">
<label for="node-input-privkey"><i class="fa fa-key"></i> Priv.Key</label>
<input type="password" id="node-input-privkey" placeholder="Encryption key">
</div>
<!-- End of unique data for V3 -->
<div class="form-row"> <div class="form-row">
<label for="node-input-oids"><i class="fa fa-tags"></i> OIDs</label> <label for="node-input-oids"><i class="fa fa-tags"></i> OIDs</label>
<textarea rows="4" cols="60" id="node-input-oids" placeholder="e.g. 1.3.6.1.2.1.1.5.0" style="width:70%;"></textarea> <textarea rows="4" cols="60" id="node-input-oids" placeholder="e.g. 1.3.6.1.2.1.1.5.0" style="width:70%;"></textarea>
@ -31,6 +112,9 @@
<p>Simple SNMP oid or oid list fetcher. Triggered by any input.</p> <p>Simple SNMP oid or oid list fetcher. Triggered by any input.</p>
<p><code>msg.host</code> may contain the host.</p> <p><code>msg.host</code> may contain the host.</p>
<p><code>msg.community</code> may contain the community.</p> <p><code>msg.community</code> may contain the community.</p>
<p><code>msg.username</code> may contain the username. (V3 only)</p>
<p><code>msg.authkey</code> may contain the digest security key. (V3 only)</p>
<p><code>msg.privkey</code> may contain the encryption security key. (V3 only)</p>
<p><code>msg.oid</code> may contain a comma separated list of oids to request. (no spaces)</p> <p><code>msg.oid</code> may contain a comma separated list of oids to request. (no spaces)</p>
<p>OIDs must be numeric. iso. is the same a 1. </p> <p>OIDs must be numeric. iso. is the same a 1. </p>
<p>The node will output <code>msg.payload</code> and <code>msg.oid</code>.</p> <p>The node will output <code>msg.payload</code> and <code>msg.oid</code>.</p>
@ -42,11 +126,19 @@
color: "YellowGreen", color: "YellowGreen",
defaults: { defaults: {
host: { value: "127.0.0.1" }, host: { value: "127.0.0.1" },
community: { value: "public" }, version: { value: "v1", required: true },
version: { value: "1", required: true },
oids: { value: "" },
timeout: { value: 5 }, timeout: { value: 5 },
name: { value: "" } community: { value: "public" },
auth: { value: "noAuthNoPriv", required: true },
authprot: { value: "MD5", required: true },
privprot: { value: "DES", required: true },
oids: { value: "" },
name: { value: "" },
},
credentials: {
username: { type: "text" },
authkey: { type: "password" },
privkey: { type: "password" }
}, },
inputs: 1, inputs: 1,
outputs: 1, outputs: 1,
@ -56,6 +148,9 @@
}, },
labelStyle: function () { labelStyle: function () {
return this.name ? "node_label_italic" : ""; return this.name ? "node_label_italic" : "";
},
oneditprepare: function () {
node_snmp_common.oneditprepare(this);
} }
}); });
</script> </script>
@ -65,19 +160,58 @@
<label for="node-input-host"><i class="fa fa-globe"></i> Host</label> <label for="node-input-host"><i class="fa fa-globe"></i> Host</label>
<input type="text" id="node-input-host" placeholder="ip address(:optional port)"> <input type="text" id="node-input-host" placeholder="ip address(:optional port)">
</div> </div>
<div class="form-row">
<label for="node-input-community"><i class="fa fa-user"></i> Community</label>
<input type="text" id="node-input-community" placeholder="public">
</div>
<div class="form-row"> <div class="form-row">
<label for="node-input-version"><i class="fa fa-bookmark"></i> Version</label> <label for="node-input-version"><i class="fa fa-bookmark"></i> Version</label>
<select type="text" id="node-input-version" style="width:150px;"> <select type="text" id="node-input-version" style="width:150px;">
<option value="1">v1</option> <option value="v1">v1</option>
<option value="2c">v2c</option> <option value="v2c">v2c</option>
<!-- Following Data is used for V3 Only -->
<option value="v3">v3</option>
<!-- End of unique data for V3 -->
</select> </select>
<span style="margin-left:50px;">Timeout</span> <span style="margin-left:50px;">Timeout</span>
<input type="text" id="node-input-timeout" placeholder="secs" style="width:50px; direction:rtl; vertical-align:baseline;">&nbsp;S <input type="text" id="node-input-timeout" placeholder="secs" style="width:50px; direction:rtl; vertical-align:baseline;">&nbsp;S
</div> </div>
<div class="form-row form-row-snmpv1v2">
<label for="node-input-community"><i class="fa fa-user"></i> Community</label>
<input type="text" id="node-input-community" placeholder="public">
</div>
<!-- Following Data is used for V3 Only -->
<div class="form-row form-row-snmpv3">
<label for="node-input-username"><i class="fa fa-user"></i> Username</label>
<input type="text" id="node-input-username" placeholder="username">
</div>
<div class="form-row form-row-snmpv3">
<label for="node-input-auth"><i class="fa fa-user-secret"></i> Auth.</label>
<select type="text" id="node-input-auth" style="width:150px;">
<option value="noAuthNoPriv">noAuthNoPriv</option>
<option value="authNoPriv">authNoPriv</option>
<option value="authPriv">authPriv</option>
</select>
</div>
<div class="form-row form-row-snmpv3 form-row-snmpv3-auth">
<label for="node-input-authprot"><i class="fa fa-shield"></i> Auth.Prot.</label>
<select type="text" id="node-input-authprot" style="width:150px;">
<option value="MD5">MD5</option>
<option value="SHA">SHA</option>
</select>
</div>
<div class="form-row form-row-snmpv3 form-row-snmpv3-auth">
<label for="node-input-authkey"><i class="fa fa-key"></i> Auth.Key</label>
<input type="password" id="node-input-authkey" placeholder="Authentication key">
</div>
<div class="form-row form-row-snmpv3 form-row-snmpv3-priv">
<label for="node-input-privprot"><i class="fa fa-shield"></i> Priv.Prot.</label>
<select type="text" id="node-input-privprot" style="width:150px;">
<option value="DES">DES</option>
<option value="AES">AES</option>
</select>
</div>
<div class="form-row form-row-snmpv3 form-row-snmpv3-priv">
<label for="node-input-privkey"><i class="fa fa-key"></i> Priv.Key</label>
<input type="password" id="node-input-privkey" placeholder="Encryption key">
</div>
<!-- End of unique data for V3 -->
<div class="form-row"> <div class="form-row">
<label for="node-input-varbinds"><i class="fa fa-tags"></i> Varbinds</label> <label for="node-input-varbinds"><i class="fa fa-tags"></i> Varbinds</label>
<textarea rows="10" cols="60" id="node-input-varbinds" placeholder="e.g. [ { &quot;oid&quot;: &quot;1.3.6.1.2.1.1.5.0&quot;, &quot;type&quot;: &quot;OctetString&quot;, &quot;value&quot;: &quot;host1&quot;}, { &quot;oid&quot;: &quot;1.3.6.1.2.1.1.6.0&quot;, &quot;type&quot;: &quot;OctetString&quot;, value: &quot;somewhere&quot; } ]" <textarea rows="10" cols="60" id="node-input-varbinds" placeholder="e.g. [ { &quot;oid&quot;: &quot;1.3.6.1.2.1.1.5.0&quot;, &quot;type&quot;: &quot;OctetString&quot;, &quot;value&quot;: &quot;host1&quot;}, { &quot;oid&quot;: &quot;1.3.6.1.2.1.1.6.0&quot;, &quot;type&quot;: &quot;OctetString&quot;, value: &quot;somewhere&quot; } ]"
@ -94,15 +228,18 @@
<p>Simple snmp Set node. Trigger by any input</p> <p>Simple snmp Set node. Trigger by any input</p>
<p><code>msg.host</code> may contain the host.</p> <p><code>msg.host</code> may contain the host.</p>
<p><code>msg.community</code> may contain the community.</p> <p><code>msg.community</code> may contain the community.</p>
<p><code>msg.username</code> may contain the username. (V3 only)</p>
<p><code>msg.authkey</code> may contain the digest security key. (V3 only)</p>
<p><code>msg.privkey</code> may contain the encryption security key. (V3 only)</p>
<p><code>msg.varbinds</code> may contain varbinds as an array of json objects containing multiple oids, types and values. <p><code>msg.varbinds</code> may contain varbinds as an array of json objects containing multiple oids, types and values.
<pre>[ <code style="font-size: smaller;"><pre style="white-space: pre;">[
{ {
"oid": "1.3.6.1.2.1.1.5.0", "oid": "1.3.6.1.2.1.1.5.0",
"type": "OctetString", "type": "OctetString",
"value": "host1" "value": "host1"
}, },
{ "oid": ... } { "oid": ... }
]</pre> ]</pre></code>
<p>Any numeric inputs must be numbers, not strings, e.g. 1 not "1".</p> <p>Any numeric inputs must be numbers, not strings, e.g. 1 not "1".</p>
<p>OIDs must be numeric. iso. is the same a 1.</p> <p>OIDs must be numeric. iso. is the same a 1.</p>
</p> </p>
@ -114,12 +251,27 @@
color: "YellowGreen", color: "YellowGreen",
defaults: { defaults: {
host: { value: "127.0.0.1" }, host: { value: "127.0.0.1" },
community: { value: "public" }, version: { value: "v1", required: true },
version: { value: "1", required: true },
varbinds: { value: "" },
timeout: { value: 5 }, timeout: { value: 5 },
community: { value: "public" },
auth: { value: "noAuthNoPriv", required: true },
authprot: { value: "MD5", required: true },
privprot: { value: "DES", required: true },
oids: { value: "" },
varbinds: { value: "", validate:function(v) {
try {
return !v || !!JSON.parse(v);
} catch(e) {
return false;
}
}},
name: { value: "" } name: { value: "" }
}, },
credentials: {
username: { type: "text" },
authkey: { type: "password" },
privkey: { type: "password" }
},
inputs: 1, inputs: 1,
outputs: 0, outputs: 0,
icon: "snmp.png", icon: "snmp.png",
@ -128,6 +280,9 @@
}, },
labelStyle: function () { labelStyle: function () {
return this.name ? "node_label_italic" : ""; return this.name ? "node_label_italic" : "";
},
oneditprepare: function () {
node_snmp_common.oneditprepare(this);
} }
}); });
</script> </script>
@ -137,19 +292,58 @@
<label for="node-input-host"><i class="fa fa-globe"></i> Host</label> <label for="node-input-host"><i class="fa fa-globe"></i> Host</label>
<input type="text" id="node-input-host" placeholder="ip address(:optional port)"> <input type="text" id="node-input-host" placeholder="ip address(:optional port)">
</div> </div>
<div class="form-row">
<label for="node-input-community"><i class="fa fa-user"></i> Community</label>
<input type="text" id="node-input-community" placeholder="public">
</div>
<div class="form-row"> <div class="form-row">
<label for="node-input-version"><i class="fa fa-bookmark"></i> Version</label> <label for="node-input-version"><i class="fa fa-bookmark"></i> Version</label>
<select type="text" id="node-input-version" style="width:150px;"> <select type="text" id="node-input-version" style="width:150px;">
<option value="1">v1</option> <option value="v1">v1</option>
<option value="2c">v2c</option> <option value="v2c">v2c</option>
<!-- Following Data is used for V3 Only -->
<option value="v3">v3</option>
<!-- End of unique data for V3 -->
</select> </select>
<span style="margin-left:50px;">Timeout</span> <span style="margin-left:50px;">Timeout</span>
<input type="text" id="node-input-timeout" placeholder="secs" style="width:50px; direction:rtl; vertical-align:baseline;">&nbsp;S <input type="text" id="node-input-timeout" placeholder="secs" style="width:50px; direction:rtl; vertical-align:baseline;">&nbsp;S
</div> </div>
<div class="form-row form-row-snmpv1v2">
<label for="node-input-community"><i class="fa fa-user"></i> Community</label>
<input type="text" id="node-input-community" placeholder="public">
</div>
<!-- Following Data is used for V3 Only -->
<div class="form-row form-row-snmpv3">
<label for="node-input-username"><i class="fa fa-user"></i> Username</label>
<input type="text" id="node-input-username" placeholder="username">
</div>
<div class="form-row form-row-snmpv3">
<label for="node-input-auth"><i class="fa fa-user-secret"></i> Auth.</label>
<select type="text" id="node-input-auth" style="width:150px;">
<option value="noAuthNoPriv">noAuthNoPriv</option>
<option value="authNoPriv">authNoPriv</option>
<option value="authPriv">authPriv</option>
</select>
</div>
<div class="form-row form-row-snmpv3 form-row-snmpv3-auth">
<label for="node-input-authprot"><i class="fa fa-shield"></i> Auth.Prot.</label>
<select type="text" id="node-input-authprot" style="width:150px;">
<option value="MD5">MD5</option>
<option value="SHA">SHA</option>
</select>
</div>
<div class="form-row form-row-snmpv3 form-row-snmpv3-auth">
<label for="node-input-authkey"><i class="fa fa-key"></i> Auth.Key</label>
<input type="password" id="node-input-authkey" placeholder="Authentication key">
</div>
<div class="form-row form-row-snmpv3 form-row-snmpv3-priv">
<label for="node-input-privprot"><i class="fa fa-shield"></i> Priv.Prot.</label>
<select type="text" id="node-input-privprot" style="width:150px;">
<option value="DES">DES</option>
<option value="AES">AES</option>
</select>
</div>
<div class="form-row form-row-snmpv3 form-row-snmpv3-priv">
<label for="node-input-privkey"><i class="fa fa-key"></i> Priv.Key</label>
<input type="password" id="node-input-privkey" placeholder="Encryption key">
</div>
<!-- End of unique data for V3 -->
<div class="form-row"> <div class="form-row">
<label for="node-input-oids"><i class="fa fa-tags"></i> OID</label> <label for="node-input-oids"><i class="fa fa-tags"></i> OID</label>
<input type="text" id="node-input-oids" placeholder="e.g. 1.3.6.1.2.1.1.5.0"> <input type="text" id="node-input-oids" placeholder="e.g. 1.3.6.1.2.1.1.5.0">
@ -165,6 +359,9 @@
<p>Simple SNMP oid table fetcher. Triggered by any input.</p> <p>Simple SNMP oid table fetcher. Triggered by any input.</p>
<p><code>msg.host</code> may contain the host.</p> <p><code>msg.host</code> may contain the host.</p>
<p><code>msg.community</code> may contain the community.</p> <p><code>msg.community</code> may contain the community.</p>
<p><code>msg.username</code> may contain the username. (V3 only)</p>
<p><code>msg.authkey</code> may contain the digest security key. (V3 only)</p>
<p><code>msg.privkey</code> may contain the encryption security key. (V3 only)</p>
<p><code>msg.oid</code> may contain the oid of a table to request.</p> <p><code>msg.oid</code> may contain the oid of a table to request.</p>
<p>OID must be numeric. iso. is the same a 1.</p> <p>OID must be numeric. iso. is the same a 1.</p>
<p>The node will output <code>msg.payload</code> and <code>msg.oid</code>.</p> <p>The node will output <code>msg.payload</code> and <code>msg.oid</code>.</p>
@ -176,12 +373,20 @@
color: "YellowGreen", color: "YellowGreen",
defaults: { defaults: {
host: { value: "127.0.0.1" }, host: { value: "127.0.0.1" },
community: { value: "public" }, version: { value: "v1", required: true },
version: { value: "1", required: true },
oids: { value: "" },
timeout: { value: 5 }, timeout: { value: 5 },
community: { value: "public" },
auth: { value: "noAuthNoPriv", required: true },
authprot: { value: "MD5", required: true },
privprot: { value: "DES", required: true },
oids: { value: "" },
name: { value: "" } name: { value: "" }
}, },
credentials: {
username: { type: "text" },
authkey: { type: "password" },
privkey: { type: "password" }
},
inputs: 1, inputs: 1,
outputs: 1, outputs: 1,
icon: "snmp.png", icon: "snmp.png",
@ -190,6 +395,9 @@
}, },
labelStyle: function () { labelStyle: function () {
return this.name ? "node_label_italic" : ""; return this.name ? "node_label_italic" : "";
},
oneditprepare: function () {
node_snmp_common.oneditprepare(this);
} }
}); });
</script> </script>
@ -199,19 +407,58 @@
<label for="node-input-host"><i class="fa fa-globe"></i> Host</label> <label for="node-input-host"><i class="fa fa-globe"></i> Host</label>
<input type="text" id="node-input-host" placeholder="ip address(:optional port)"> <input type="text" id="node-input-host" placeholder="ip address(:optional port)">
</div> </div>
<div class="form-row">
<label for="node-input-community"><i class="fa fa-user"></i> Community</label>
<input type="text" id="node-input-community" placeholder="public">
</div>
<div class="form-row"> <div class="form-row">
<label for="node-input-version"><i class="fa fa-bookmark"></i> Version</label> <label for="node-input-version"><i class="fa fa-bookmark"></i> Version</label>
<select type="text" id="node-input-version" style="width:150px;"> <select type="text" id="node-input-version" style="width:150px;">
<option value="1">v1</option> <option value="v1">v1</option>
<option value="2c">v2c</option> <option value="v2c">v2c</option>
<!-- Following Data is used for V3 Only -->
<option value="v3">v3</option>
<!-- End of unique data for V3 -->
</select> </select>
<span style="margin-left:50px;">Timeout</span> <span style="margin-left:50px;">Timeout</span>
<input type="text" id="node-input-timeout" placeholder="secs" style="width:50px; direction:rtl; vertical-align:baseline;">&nbsp;S <input type="text" id="node-input-timeout" placeholder="secs" style="width:50px; direction:rtl; vertical-align:baseline;">&nbsp;S
</div> </div>
<div class="form-row form-row-snmpv1v2">
<label for="node-input-community"><i class="fa fa-user"></i> Community</label>
<input type="text" id="node-input-community" placeholder="public">
</div>
<!-- Following Data is used for V3 Only -->
<div class="form-row form-row-snmpv3">
<label for="node-input-username"><i class="fa fa-user"></i> Username</label>
<input type="text" id="node-input-username" placeholder="username">
</div>
<div class="form-row form-row-snmpv3">
<label for="node-input-auth"><i class="fa fa-user-secret"></i> Auth.</label>
<select type="text" id="node-input-auth" style="width:150px;">
<option value="noAuthNoPriv">noAuthNoPriv</option>
<option value="authNoPriv">authNoPriv</option>
<option value="authPriv">authPriv</option>
</select>
</div>
<div class="form-row form-row-snmpv3 form-row-snmpv3-auth">
<label for="node-input-authprot"><i class="fa fa-shield"></i> Auth.Prot.</label>
<select type="text" id="node-input-authprot" style="width:150px;">
<option value="MD5">MD5</option>
<option value="SHA">SHA</option>
</select>
</div>
<div class="form-row form-row-snmpv3 form-row-snmpv3-auth">
<label for="node-input-authkey"><i class="fa fa-key"></i> Auth.Key</label>
<input type="password" id="node-input-authkey" placeholder="Authentication key">
</div>
<div class="form-row form-row-snmpv3 form-row-snmpv3-priv">
<label for="node-input-privprot"><i class="fa fa-shield"></i> Priv.Prot.</label>
<select type="text" id="node-input-privprot" style="width:150px;">
<option value="DES">DES</option>
<option value="AES">AES</option>
</select>
</div>
<div class="form-row form-row-snmpv3 form-row-snmpv3-priv">
<label for="node-input-privkey"><i class="fa fa-key"></i> Priv.Key</label>
<input type="password" id="node-input-privkey" placeholder="Encryption key">
</div>
<!-- End of unique data for V3 -->
<div class="form-row"> <div class="form-row">
<label for="node-input-oids"><i class="fa fa-tags"></i> OID</label> <label for="node-input-oids"><i class="fa fa-tags"></i> OID</label>
<input type="text" id="node-input-oids" placeholder="e.g. 1.3.6.1.2.1.1.5.0"> <input type="text" id="node-input-oids" placeholder="e.g. 1.3.6.1.2.1.1.5.0">
@ -227,6 +474,9 @@
<p>Simple SNMP oid subtree fetcher. Triggered by any input. Reads all OIDS at and below the current base OID.</p> <p>Simple SNMP oid subtree fetcher. Triggered by any input. Reads all OIDS at and below the current base OID.</p>
<p><code>msg.host</code> may contain the host.</p> <p><code>msg.host</code> may contain the host.</p>
<p><code>msg.community</code> may contain the community.</p> <p><code>msg.community</code> may contain the community.</p>
<p><code>msg.username</code> may contain the username. (V3 only)</p>
<p><code>msg.authkey</code> may contain the digest security key. (V3 only)</p>
<p><code>msg.privkey</code> may contain the encryption security key. (V3 only)</p>
<p><code>msg.oid</code> may contain the oid of a table to request.</p> <p><code>msg.oid</code> may contain the oid of a table to request.</p>
<p>OID must be numeric. iso. is the same a 1. </p> <p>OID must be numeric. iso. is the same a 1. </p>
<p>The node will output <code>msg.payload</code> and <code>msg.oid</code>.</p> <p>The node will output <code>msg.payload</code> and <code>msg.oid</code>.</p>
@ -238,12 +488,20 @@
color: "YellowGreen", color: "YellowGreen",
defaults: { defaults: {
host: { value: "127.0.0.1" }, host: { value: "127.0.0.1" },
community: { value: "public" }, version: { value: "v1", required: true },
version: { value: "1", required: true },
oids: { value: "" },
timeout: { value: 5 }, timeout: { value: 5 },
community: { value: "public" },
auth: { value: "noAuthNoPriv", required: true },
authprot: { value: "MD5", required: true },
privprot: { value: "DES", required: true },
oids: { value: "" },
name: { value: "" } name: { value: "" }
}, },
credentials: {
username: { type: "text" },
authkey: { type: "password" },
privkey: { type: "password" }
},
inputs: 1, inputs: 1,
outputs: 1, outputs: 1,
icon: "snmp.png", icon: "snmp.png",
@ -252,6 +510,9 @@
}, },
labelStyle: function () { labelStyle: function () {
return this.name ? "node_label_italic" : ""; return this.name ? "node_label_italic" : "";
},
oneditprepare: function () {
node_snmp_common.oneditprepare(this);
} }
}); });
</script> </script>
@ -262,19 +523,58 @@
<label for="node-input-host"><i class="fa fa-globe"></i> Host</label> <label for="node-input-host"><i class="fa fa-globe"></i> Host</label>
<input type="text" id="node-input-host" placeholder="ip address(:optional port)"> <input type="text" id="node-input-host" placeholder="ip address(:optional port)">
</div> </div>
<div class="form-row">
<label for="node-input-community"><i class="fa fa-user"></i> Community</label>
<input type="text" id="node-input-community" placeholder="public">
</div>
<div class="form-row"> <div class="form-row">
<label for="node-input-version"><i class="fa fa-bookmark"></i> Version</label> <label for="node-input-version"><i class="fa fa-bookmark"></i> Version</label>
<select type="text" id="node-input-version" style="width:150px;"> <select type="text" id="node-input-version" style="width:150px;">
<option value="1">v1</option> <option value="v1">v1</option>
<option value="2c">v2c</option> <option value="v2c">v2c</option>
<!-- Following Data is used for V3 Only -->
<option value="v3">v3</option>
<!-- End of unique data for V3 -->
</select> </select>
<span style="margin-left:50px;">Timeout</span> <span style="margin-left:50px;">Timeout</span>
<input type="text" id="node-input-timeout" placeholder="secs" style="width:50px; direction:rtl; vertical-align:baseline;">&nbsp;S <input type="text" id="node-input-timeout" placeholder="secs" style="width:50px; direction:rtl; vertical-align:baseline;">&nbsp;S
</div> </div>
<div class="form-row form-row-snmpv1v2">
<label for="node-input-community"><i class="fa fa-user"></i> Community</label>
<input type="text" id="node-input-community" placeholder="public">
</div>
<!-- Following Data is used for V3 Only -->
<div class="form-row form-row-snmpv3">
<label for="node-input-username"><i class="fa fa-user"></i> Username</label>
<input type="text" id="node-input-username" placeholder="username">
</div>
<div class="form-row form-row-snmpv3">
<label for="node-input-auth"><i class="fa fa-user-secret"></i> Auth.</label>
<select type="text" id="node-input-auth" style="width:150px;">
<option value="noAuthNoPriv">noAuthNoPriv</option>
<option value="authNoPriv">authNoPriv</option>
<option value="authPriv">authPriv</option>
</select>
</div>
<div class="form-row form-row-snmpv3 form-row-snmpv3-auth">
<label for="node-input-authprot"><i class="fa fa-shield"></i> Auth.Prot.</label>
<select type="text" id="node-input-authprot" style="width:150px;">
<option value="MD5">MD5</option>
<option value="SHA">SHA</option>
</select>
</div>
<div class="form-row form-row-snmpv3 form-row-snmpv3-auth">
<label for="node-input-authkey"><i class="fa fa-key"></i> Auth.Key</label>
<input type="password" id="node-input-authkey" placeholder="Authentication key">
</div>
<div class="form-row form-row-snmpv3 form-row-snmpv3-priv">
<label for="node-input-privprot"><i class="fa fa-shield"></i> Priv.Prot.</label>
<select type="text" id="node-input-privprot" style="width:150px;">
<option value="DES">DES</option>
<option value="AES">AES</option>
</select>
</div>
<div class="form-row form-row-snmpv3 form-row-snmpv3-priv">
<label for="node-input-privkey"><i class="fa fa-key"></i> Priv.Key</label>
<input type="password" id="node-input-privkey" placeholder="Encryption key">
</div>
<!-- End of unique data for V3 -->
<div class="form-row"> <div class="form-row">
<label for="node-input-oids"><i class="fa fa-tags"></i> OID</label> <label for="node-input-oids"><i class="fa fa-tags"></i> OID</label>
<input type="text" id="node-input-oids" placeholder="e.g. 1.3.6.1.2.1.1.5.0"> <input type="text" id="node-input-oids" placeholder="e.g. 1.3.6.1.2.1.1.5.0">
@ -291,6 +591,9 @@
Fetches all nodes from this OID to the end of the table.</p> Fetches all nodes from this OID to the end of the table.</p>
<p><code>msg.host</code> may contain the host.</p> <p><code>msg.host</code> may contain the host.</p>
<p><code>msg.community</code> may contain the community.</p> <p><code>msg.community</code> may contain the community.</p>
<p><code>msg.username</code> may contain the username. (V3 only)</p>
<p><code>msg.authkey</code> may contain the digest security key. (V3 only)</p>
<p><code>msg.privkey</code> may contain the encryption security key. (V3 only)</p>
<p><code>msg.oid</code> may contain the oid of a table to request.</p> <p><code>msg.oid</code> may contain the oid of a table to request.</p>
<p>OID must be numeric. iso. is the same a 1. </p> <p>OID must be numeric. iso. is the same a 1. </p>
<p>The node will output <code>msg.payload</code> and <code>msg.oid</code>.</p> <p>The node will output <code>msg.payload</code> and <code>msg.oid</code>.</p>
@ -304,12 +607,20 @@
color: "YellowGreen", color: "YellowGreen",
defaults: { defaults: {
host: { value: "127.0.0.1" }, host: { value: "127.0.0.1" },
community: { value: "public" }, version: { value: "v1", required: true },
version: { value: "1", required: true },
oids: { value: "" },
timeout: { value: 5 }, timeout: { value: 5 },
community: { value: "public" },
auth: { value: "noAuthNoPriv", required: true },
authprot: { value: "MD5", required: true },
privprot: { value: "DES", required: true },
oids: { value: "" },
name: { value: "" } name: { value: "" }
}, },
credentials: {
username: { type: "text" },
authkey: { type: "password" },
privkey: { type: "password" }
},
inputs: 1, inputs: 1,
outputs: 1, outputs: 1,
icon: "snmp.png", icon: "snmp.png",
@ -318,6 +629,9 @@
}, },
labelStyle: function () { labelStyle: function () {
return this.name ? "node_label_italic" : ""; return this.name ? "node_label_italic" : "";
},
oneditprepare: function () {
node_snmp_common.oneditprepare(this);
} }
}); });
</script> </script>

View File

@ -1,262 +1,479 @@
module.exports = function (RED) { module.exports = function (RED) {
"use strict"; "use strict";
var snmp = require("net-snmp"); const SNMP = require("net-snmp");
const sessions = {};
var sessions = {}; function generateUUID() {
let d = Date.now();
function getSession(host, community, version, timeout) { let d2 = ((typeof performance !== 'undefined') && performance.now && (performance.now() * 1000)) || (Date.now() * Math.random() * 100000);//Time in microseconds since load
var sessionKey = host + ":" + community + ":" + version; return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var port = 161; let r = Math.random() * 16;//random number between 0 and 16
if (host.indexOf(":") !== -1) { if (d > 0) {//Use timestamp until depleted
port = host.split(":")[1]; r = (d + r) % 16 | 0;
host = host.split(":")[0]; d = Math.floor(d / 16);
} } else {//Use microseconds since page-load if supported
if (!(sessionKey in sessions)) { r = (d2 + r) % 16 | 0;
sessions[sessionKey] = snmp.createSession(host, community, { port:port, version:version, timeout:(timeout || 5000) }); d2 = Math.floor(d2 / 16);
}
return sessions[sessionKey];
}
function SnmpNode(n) {
RED.nodes.createNode(this, n);
this.community = n.community;
this.host = n.host;
this.version = (n.version === "2c") ? snmp.Version2c : snmp.Version1;
this.oids = n.oids.replace(/\s/g, "");
this.timeout = Number(n.timeout || 5) * 1000;
var node = this;
this.on("input", function (msg) {
var host = node.host || msg.host;
var community = node.community || msg.community;
var oids = node.oids || msg.oid;
if (oids) {
getSession(host, community, node.version, node.timeout).get(oids.split(","), function (error, varbinds) {
if (error) {
node.error(error.toString(), msg);
}
else {
for (var i = 0; i < varbinds.length; i++) {
if (snmp.isVarbindError(varbinds[i])) {
node.error(snmp.varbindError(varbinds[i]), msg);
}
else {
if (varbinds[i].type == 4) { varbinds[i].value = varbinds[i].value.toString(); }
varbinds[i].tstr = snmp.ObjectType[varbinds[i].type];
//node.log(varbinds[i].oid + "|" + varbinds[i].tstr + "|" + varbinds[i].value);
}
}
msg.oid = oids;
msg.payload = varbinds;
node.send(msg);
} }
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
}); });
} }
function openSession(sessionid, host, user, options) {
// SNMPv3 call
if (options.version === SNMP.Version3) {
sessions[sessionid] = SNMP.createV3Session(host, user, options);
}
// SNMPv1 or SNMPv2c call
else { else {
sessions[sessionid] = SNMP.createSession(host, user.community, options);
}
return sessions[sessionid];
}
// Any session needs to be closed after completion
function closeSession(sessionid) {
try {
sessions[sessionid].removeAllListeners();
} catch (e) { }
try {
sessions[sessionid].close();
} catch (e) { }
delete sessions[sessionid];
}
function initSnmpNode(node, config) {
node.community = config.community;
node.host = config.host;
node.version = config.version;
node.auth = config.auth;
node.authprot = config.authprot;
node.privprot = config.privprot;
if (node.credentials) {
node.username = node.credentials.username;
node.authkey = node.credentials.authkey;
node.privkey = node.credentials.privkey;
}
node.timeout = Number(config.timeout || 5) * 1000;
}
function prepareSnmpOptions(node, msg) {
let host = node.host || msg.host;
const sessionid = generateUUID();
const user = {}
const options = {};
options.version = node.version;
if (node.version === "v1") {
options.version = SNMP.Version1;
user.community = node.community || msg.community;
} else if (node.version === "v2c") {
options.version = SNMP.Version2c;
user.community = node.community || msg.community;
} else if (node.version === "v3") {
user.name = node.username || msg.username || "";
user.level = SNMP.SecurityLevel.noAuthNoPriv;
user.authProtocol = SNMP.AuthProtocols.none;
user.authKey = "";
user.privProtocol = SNMP.PrivProtocols.none;
user.privKey = "";
options.version = SNMP.Version3;
if (node.auth === "authNoPriv" || node.auth === "authPriv") {
user.level = SNMP.SecurityLevel.authNoPriv;
user.authProtocol = (node.authprot === "SHA") ? SNMP.AuthProtocols.sha : SNMP.AuthProtocols.md5;
user.authKey = node.authkey || msg.authkey || "";
if (node.auth === "authPriv") {
user.level = SNMP.SecurityLevel.authPriv;
if (node.privprot === "DES" || node.privprot === "AES") {
user.privProtocol = (node.privprot === "AES") ? SNMP.PrivProtocols.aes : SNMP.PrivProtocols.des;
user.privKey = node.privkey || msg.privkey || "";
}
}
}
}
options.timeout = node.timeout;
options.debug = msg.debug || undefined;
options.port = options.port || 161;
options.retries = options.retries || 1;
if (msg.engineID) {
options.engineID = msg.engineID;//The engineID used for SNMPv3 communications, given as a hex string - defaults to a system-generated engineID containing elements of random
}
if (msg.backoff) {
options.backoff = msg.backoff;//The factor by which to increase the timeout for every retry, defaults to 1 for no increase
}
if (msg.backwardsGetNexts) {
options.backwardsGetNexts = msg.backwardsGetNexts;//boolean to allow GetNext operations to retrieve lexicographically preceding OIDs
}
if (msg.idBitsSize === 16 || msg.idBitsSize === 32) {
options.idBitsSize = msg.idBitsSize;//Either 16 or 32, defaults to 32. Used to reduce the size of the generated id for compatibility with some older devices.
}
const ipv = parseIP(host);
if (ipv.version === 4) {
host = ipv.ip;
options.port = ipv.port || options.port;
options.transport = 'udp4';
} else if (ipv.version === 6) {
host = ipv.ip;
options.port = ipv.port || options.port;
options.transport = 'udp6';
} else {
//probably a host name
if (host.indexOf(":") > 0) {
host = host.split(":")[0];
options.port = host.split(":")[1];
}
}
return {
host: host,
sessionid: sessionid,
user: user,
options: options,
}
}
function parseIP(ip) {
const IPV4_PAT = /^(\d+)\.(\d+)\.(\d+)\.(\d+)(?::(\d+)){0,1}$/g;
const IPV6_DOUBLE_COL_PAT = /^\[{0,1}([0-9a-f:]*)::([0-9a-f:]*)(?:\]:(\d+)){0,1}$/g;
const ipv4Matcher = IPV4_PAT.exec(ip);
let hex = "";
let port = undefined;
let ipOnly = [];
try {
if (ipv4Matcher && ipv4Matcher.length) {
for (let i = 1; i <= 4; i++) {
ipOnly.push(ipv4Matcher[i]);
hex += toHex4(ipv4Matcher[i]);
}
if (ipv4Matcher[5]) {
port = parseInt(ipv4Matcher[5]);
}
return { ip: ipOnly.join("."), hex, port, version: 4 };
}
// IPV6 Must be colons format (a:b:c:d:e:A.B.C.D not currently supported)
let ipv6Pattern = "^\\[{0,1}";
for (let i = 1; i <= 7; i++) {
ipv6Pattern += "([0-9a-f]+):";
}
ipv6Pattern += "([0-9a-f]+)(?:\\]:(\\d+)){0,1}$";
const IPV6_PAT = new RegExp(ipv6Pattern);
// IPV6, double colon
const ipv6DoubleColonMatcher = IPV6_DOUBLE_COL_PAT.exec(ip);
if (ipv6DoubleColonMatcher && ipv6DoubleColonMatcher.length) {
let p1 = ipv6DoubleColonMatcher[1];
if (!p1) {
p1 = "0";
}
let p2 = ipv6DoubleColonMatcher[2];
if (!p2) {
p2 = "0";
}
p1 = p1.padStart(4, "0");
p2 = p2.padStart(4, "0");
ip = p1 + getZeros(8 - numCount(p1) - numCount(p2)) + p2;
if (ipv6DoubleColonMatcher[3]) {
ip = "[" + ip + "]:" + ipv6DoubleColonMatcher[3];
}
}
// IPV6
const ipv6Matcher = IPV6_PAT.exec(ip);
if (ipv6Matcher && ipv6Matcher.length) {
for (let i = 1; i <= 8; i++) {
const p = toHex6(ipv6Matcher[i]).padStart(4, "0");
ipOnly.push(p);
hex += p;
}
if (ipv6Matcher[9]) {
port = parseInt(ipv6Matcher[9]);
}
return { ip: ipOnly.join(":"), hex, port, version: 6 };
}
throw new Error("Unknown address: " + ip);
} catch (error) {
return { ip, hex, port, version: null, error: error };
}
function numCount(/** @type {string} */s) {
return s.split(":").length;
}
function getZeros(/** @type {number} */ count) {
const sb = [":"];
while (count > 0) {
sb.push("0000:");
count--;
}
return sb.join("");
}
function toHex4(/** @type {string} */ s) {
const val = parseInt(s);
if (val < 0 || val > 255) {
throw new Error("Invalid value : " + s);
}
return val.toString(16).padStart(2, "0");
}
function toHex6(/** @type {string} */ s) {
const val = parseInt(s, 16);
if (val < 0 || val > 65536) {
throw new Error("Invalid hex value : " + s);
}
return s;
}
}
function SnmpNode(n) {
const node = this;
RED.nodes.createNode(node, n);
initSnmpNode(node, n);
node.oids = n.oids ? n.oids.replace(/\s/g, "") : "";
node.on("input", function (msg) {
const oids = node.oids || msg.oid;
const { host, sessionid, user, options } = prepareSnmpOptions(node, msg);
if (oids) {
let sess = openSession(sessionid, host, user, options);
sess.on("error", function (err) {
node.error(err, msg);
})
sess.get(oids.split(","), function (error, varbinds) {
if (error) {
node.error(error.toString(), msg);
} else {
for (let i = 0; i < varbinds.length; i++) {
let vb = varbinds[i];
if (SNMP.isVarbindError(vb)) {
node.error(SNMP.varbindError(vb), msg);
vb._error = SNMP.varbindError(vb); //add _error to msg so users can determine the varbind is not valid
}
else {
if (vb.type == 4) { vb.value = vb.value.toString(); }
}
vb.tstr = SNMP.ObjectType[vb.type];
}
msg.payload = varbinds;
msg.oid = oids;
node.send(msg);
}
closeSession(sessionid); // Needed to close the session else a bad or good read could affect future readings
});
} else {
node.warn("No oid(s) to search for"); node.warn("No oid(s) to search for");
} }
}); });
} }
RED.nodes.registerType("snmp", SnmpNode); RED.nodes.registerType("snmp", SnmpNode, {
credentials: {
function SnmpSNode(n) { username: { type: "text" },
RED.nodes.createNode(this, n); authkey: { type: "password" },
this.community = n.community; privkey: { type: "password" }
this.host = n.host;
this.version = (n.version === "2c") ? snmp.Version2c : snmp.Version1;
this.varbinds = n.varbinds;
this.timeout = Number(n.timeout || 5) * 1000;
if (this.varbinds && this.varbinds.trim().length === 0) { delete this.varbinds; }
var node = this;
this.on("input", function (msg) {
var host = node.host || msg.host;
var community = node.community || msg.community;
var varbinds = (node.varbinds) ? JSON.parse(node.varbinds) : msg.varbinds;
if (varbinds) {
for (var i = 0; i < varbinds.length; i++) {
varbinds[i].type = snmp.ObjectType[varbinds[i].type];
}
getSession(host, community, node.version, node.timeout).set(varbinds, function (error, varbinds) {
if (error) {
node.error(error.toString(), msg);
}
else {
for (var i = 0; i < varbinds.length; i++) {
// for version 2c we must check each OID for an error condition
if (snmp.isVarbindError(varbinds[i])) {
node.error(snmp.varbindError(varbinds[i]), msg);
}
}
} }
}); });
function SnmpSNode(n) {
const node = this;
RED.nodes.createNode(node, n);
initSnmpNode(node, n);
node.varbinds = n.varbinds;
if (node.varbinds && node.varbinds.trim().length === 0) { delete node.varbinds; }
node.on("input", function (msg) {
const { host, sessionid, user, options } = prepareSnmpOptions(node, msg);
const varbinds = (node.varbinds) ? JSON.parse(node.varbinds) : msg.varbinds;
if (varbinds) {
for (let i = 0; i < varbinds.length; i++) {
varbinds[i].type = SNMP.ObjectType[varbinds[i].type];
} }
else { let sess = openSession(sessionid, host, user, options);
sess.on("error", function (err) {
node.error(err, msg);
})
sess.set(varbinds, function (error, varbinds) {
if (error) {
node.error(error.toString(), msg);
} else {
for (let i = 0; i < varbinds.length; i++) {
// for version 2c we must check each OID for an error condition
if (SNMP.isVarbindError(varbinds[i])) {
node.error(SNMP.varbindError(varbinds[i]), msg);
}
}
}
closeSession(sessionid);
});
} else {
node.warn("No varbinds to set"); node.warn("No varbinds to set");
} }
}); });
} }
RED.nodes.registerType("snmp set", SnmpSNode); RED.nodes.registerType("snmp set", SnmpSNode, {
credentials: {
username: { type: "text" },
authkey: { type: "password" },
privkey: { type: "password" }
}
});
function SnmpTNode(n) { function SnmpTNode(n) {
RED.nodes.createNode(this, n); const node = this;
this.community = n.community; RED.nodes.createNode(node, n);
this.host = n.host; initSnmpNode(node, n);
this.version = (n.version === "2c") ? snmp.Version2c : snmp.Version1; node.oids = n.oids ? n.oids.replace(/\s/g, "") : ""
this.oids = n.oids.replace(/\s/g, ""); const maxRepetitions = 20;
this.timeout = Number(n.timeout || 5) * 1000;
var node = this;
var maxRepetitions = 20;
function sortInt(a, b) { function sortInt(a, b) {
if (a > b) { return 1; } if (a > b) { return 1; }
else if (b > a) { return -1; } else if (b > a) { return -1; } else { return 0; }
else { return 0; }
} }
this.on("input", function (msg) { node.on("input", function (msg) {
var host = node.host || msg.host; const oids = node.oids || msg.oid;
var community = node.community || msg.community; const { host, sessionid, user, options } = prepareSnmpOptions(node, msg);
var oids = node.oids || msg.oid;
if (oids) { if (oids) {
msg.oid = oids; msg.oid = oids;
getSession(host, community, node.version, node.timeout).table(oids, maxRepetitions, function (error, table) { let sess = openSession(sessionid, host, user, options);
sess.on("error", function (err) {
node.error(err, msg);
})
sess.table(oids, maxRepetitions, function (error, table) {
if (error) { if (error) {
node.error(error.toString(), msg); node.error(error.toString(), msg);
} } else {
else { const indexes = [];
var indexes = []; for (let index in table) {
for (var index in table) {
if (table.hasOwnProperty(index)) { if (table.hasOwnProperty(index)) {
indexes.push(parseInt(index)); indexes.push(parseInt(index));
} }
} }
indexes.sort(sortInt); indexes.sort(sortInt);
for (var i = 0; i < indexes.length; i++) { for (let i = 0; i < indexes.length; i++) {
var columns = []; const columns = [];
for (var column in table[indexes[i]]) { for (let column in table[indexes[i]]) {
if (table[indexes[i]].hasOwnProperty(column)) { if (table[indexes[i]].hasOwnProperty(column)) {
columns.push(parseInt(column)); columns.push(parseInt(column));
} }
} }
columns.sort(sortInt); columns.sort(sortInt);
// console.log("row index = " + indexes[i]);
// for (var j = 0; j < columns.length; j++) {
// console.log(" column " + columns[j] + " = " + table[indexes[i]][columns[j]]);
// }
} }
msg.payload = table; msg.payload = table;
node.send(msg); node.send(msg);
} }
closeSession(sessionid);
}); });
} } else {
else {
node.warn("No oid to search for"); node.warn("No oid to search for");
} }
}); });
} }
RED.nodes.registerType("snmp table", SnmpTNode); RED.nodes.registerType("snmp table", SnmpTNode, {
credentials: {
username: { type: "text" },
authkey: { type: "password" },
privkey: { type: "password" }
}
});
function SnmpSubtreeNode(n) { function SnmpSubtreeNode(n) {
RED.nodes.createNode(this, n); const node = this;
this.community = n.community; RED.nodes.createNode(node, n);
this.host = n.host; initSnmpNode(node, n);
this.version = (n.version === "2c") ? snmp.Version2c : snmp.Version1; node.oids = n.oids ? n.oids.replace(/\s/g, "") : ""
this.oids = n.oids.replace(/\s/g, ""); const maxRepetitions = 20;
this.timeout = Number(n.timeout || 5) * 1000;
var node = this;
var maxRepetitions = 20;
var response = [];
node.on("input", function (msg) {
const oids = node.oids || msg.oid;
const { host, sessionid, user, options } = prepareSnmpOptions(node, msg);
if (oids) {
msg.oid = oids;
let sess = openSession(sessionid, host, user, options);
sess.on("error", function (err) {
node.error(err, msg);
})
//move response array & feedCb to inside `node.on("input",` to avoid subsequent
// calls overwriting results from previous operations (each call gets own result/response)
const response = [];
function feedCb(varbinds) { function feedCb(varbinds) {
for (var i = 0; i < varbinds.length; i++) { for (let i = 0; i < varbinds.length; i++) {
if (snmp.isVarbindError(varbinds[i])) { if (SNMP.isVarbindError(varbinds[i])) {
node.error(snmp.varbindError(varbinds[i]), msg); node.error(SNMP.varbindError(varbinds[i]), msg);
} } else {
else {
//console.log(varbinds[i].oid + "|" + varbinds[i].value);
response.push({ oid: varbinds[i].oid, value: varbinds[i].value }); response.push({ oid: varbinds[i].oid, value: varbinds[i].value });
} }
} }
} }
sess.subtree(msg.oid, maxRepetitions, feedCb, function (error) {
this.on("input", function (msg) {
var host = node.host || msg.host;
var community = node.community || msg.community;
var oids = node.oids || msg.oid;
if (oids) {
msg.oid = oids;
getSession(host, community, node.version, node.timeout).subtree(msg.oid, maxRepetitions, feedCb, function (error) {
if (error) { if (error) {
node.error(error.toString(), msg); node.error(error.toString(), msg);
} } else {
else { msg.payload = response;
// Clone the array
msg.payload = response.slice(0);
node.send(msg); node.send(msg);
//Clears response
response.length = 0;
} }
closeSession(sessionid);
}); });
} } else {
else {
node.warn("No oid to search for"); node.warn("No oid to search for");
} }
}); });
} }
RED.nodes.registerType("snmp subtree", SnmpSubtreeNode); RED.nodes.registerType("snmp subtree", SnmpSubtreeNode, {
credentials: {
username: { type: "text" },
authkey: { type: "password" },
privkey: { type: "password" }
}
});
function SnmpWalkerNode(n) { function SnmpWalkerNode(n) {
RED.nodes.createNode(this, n); const node = this;
this.community = n.community; RED.nodes.createNode(node, n);
this.host = n.host; initSnmpNode(node, n);
this.version = (n.version === "2c") ? snmp.Version2c : snmp.Version1; node.oids = n.oids ? n.oids.replace(/\s/g, "") : ""
this.oids = n.oids.replace(/\s/g, ""); const maxRepetitions = 20;
this.timeout = Number(n.timeout || 5) * 1000;
var node = this;
var maxRepetitions = 20;
var response = [];
node.on("input", function (msg) {
const oids = node.oids || msg.oid;
const { host, sessionid, user, options } = prepareSnmpOptions(node, msg);
if (oids) {
msg.oid = oids;
let sess = openSession(sessionid, host, user, options);
sess.on("error", function (err) {
node.error(err, msg);
})
//move response array & feedCb to inside `node.on("input",` to avoid subsequent
// calls overwriting results from previous operations (each call gets own result/response)
const response = [];
function feedCb(varbinds) { function feedCb(varbinds) {
for (var i = 0; i < varbinds.length; i++) { for (let i = 0; i < varbinds.length; i++) {
if (snmp.isVarbindError(varbinds[i])) { if (SNMP.isVarbindError(varbinds[i])) {
node.error(snmp.varbindError(varbinds[i]), msg); node.error(SNMP.varbindError(varbinds[i]), msg);
} } else {
else {
//console.log(varbinds[i].oid + "|" + varbinds[i].value);
response.push({ oid: varbinds[i].oid, value: varbinds[i].value }); response.push({ oid: varbinds[i].oid, value: varbinds[i].value });
} }
} }
} }
sess.walk(msg.oid, maxRepetitions, feedCb, function (error) {
this.on("input", function (msg) {
node.msg = msg;
var oids = node.oids || msg.oid;
var host = node.host || msg.host;
var community = node.community || msg.community;
if (oids) {
msg.oid = oids;
getSession(host, community, node.version, node.timeout).walk(msg.oid, maxRepetitions, feedCb, function (error) {
if (error) { if (error) {
node.error(error.toString(), msg); node.error(error.toString(), msg);
} } else {
else { msg.payload = response;
// Clone the array
msg.payload = response.slice(0);
node.send(msg); node.send(msg);
//Clears response
response.length = 0;
} }
closeSession(sessionid);
}); });
} } else {
else {
node.warn("No oid to search for"); node.warn("No oid to search for");
} }
}); });
} }
RED.nodes.registerType("snmp walker", SnmpWalkerNode); RED.nodes.registerType("snmp walker", SnmpWalkerNode, {
credentials: {
username: { type: "text" },
authkey: { type: "password" },
privkey: { type: "password" }
}
});
}; };