net-snmp 3.23.0 → 3.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +132 -0
- package/eslint.config.js +3 -0
- package/example/agentx-subagent.js +44 -9
- package/index.js +345 -125
- package/package.json +2 -2
- package/test/README-test-coverage.md +76 -0
- package/test/snmpv3-auth-failures.test.js +331 -0
- package/test/subagent.test.js +469 -0
package/README.md
CHANGED
@@ -1947,6 +1947,8 @@ var myScalarProvider = {
|
|
1947
1947
|
// e.g. can update the MIB data before responding to the request here
|
1948
1948
|
mibRequest.done ();
|
1949
1949
|
}
|
1950
|
+
// Note: handler is optional for scalar providers - if omitted,
|
1951
|
+
// mibRequest.done() is called automatically
|
1950
1952
|
};
|
1951
1953
|
var mib = agent.getMib ();
|
1952
1954
|
mib.registerProvider (myScalarProvider);
|
@@ -2157,6 +2159,8 @@ objects`, below, for details.
|
|
2157
2159
|
* `handler` *(optional)* - an optional callback function, which is called before the request to the
|
2158
2160
|
MIB is made. This could update the MIB value(s) handled by this provider. If not given,
|
2159
2161
|
the values are simply returned from (or set in) the MIB without any other processing.
|
2162
|
+
If no `handler` is provided, the system automatically calls `mibRequest.done()` to complete
|
2163
|
+
the request, making both scalar and table providers fully functional without explicit handlers.
|
2160
2164
|
The callback function takes a `MibRequest` instance, which has a `done()` function. This
|
2161
2165
|
must be called when finished processing the request. To signal an error, give a single error object
|
2162
2166
|
in the form of `{errorStatus: <status>}`, where `<status>` is a value from ErrorStatus e.g.
|
@@ -2704,6 +2708,79 @@ to both register the provider on its internal `Mib` object, *and* send a Registe
|
|
2704
2708
|
agent for the provider's MIB region. The latter step is skipped if registering the provider directly
|
2705
2709
|
on the MIB object.
|
2706
2710
|
|
2711
|
+
## Simplified Provider Example
|
2712
|
+
|
2713
|
+
AgentX subagents provide automatic request handling for both scalar and table providers, making them simple to implement:
|
2714
|
+
|
2715
|
+
```js
|
2716
|
+
// Both scalars and tables work with automatic request handling (no explicit handlers needed)
|
2717
|
+
var stringProvider = {
|
2718
|
+
name: "scalarString",
|
2719
|
+
type: snmp.MibProviderType.Scalar,
|
2720
|
+
oid: "1.3.6.1.4.1.8072.9999.9999.1",
|
2721
|
+
scalarType: snmp.ObjectType.OctetString,
|
2722
|
+
maxAccess: snmp.MaxAccess["read-write"]
|
2723
|
+
// No handler required - automatic fallback will call mibRequest.done()
|
2724
|
+
};
|
2725
|
+
|
2726
|
+
var intProvider = {
|
2727
|
+
name: "scalarInt",
|
2728
|
+
type: snmp.MibProviderType.Scalar,
|
2729
|
+
oid: "1.3.6.1.4.1.8072.9999.9999.3",
|
2730
|
+
scalarType: snmp.ObjectType.Integer,
|
2731
|
+
maxAccess: snmp.MaxAccess["read-write"]
|
2732
|
+
// No handler required - automatic fallback will call mibRequest.done()
|
2733
|
+
};
|
2734
|
+
|
2735
|
+
// Table providers also work without explicit handlers
|
2736
|
+
var tableProvider = {
|
2737
|
+
name: "smallIfTable",
|
2738
|
+
type: snmp.MibProviderType.Table,
|
2739
|
+
oid: "1.3.6.1.4.1.8072.9999.9999.2",
|
2740
|
+
maxAccess: snmp.MaxAccess['not-accessible'],
|
2741
|
+
tableColumns: [
|
2742
|
+
{
|
2743
|
+
number: 1,
|
2744
|
+
name: "ifIndex",
|
2745
|
+
type: snmp.ObjectType.Integer,
|
2746
|
+
maxAccess: snmp.MaxAccess['read-only']
|
2747
|
+
},
|
2748
|
+
{
|
2749
|
+
number: 2,
|
2750
|
+
name: "ifDescr",
|
2751
|
+
type: snmp.ObjectType.OctetString,
|
2752
|
+
maxAccess: snmp.MaxAccess['read-write']
|
2753
|
+
}
|
2754
|
+
],
|
2755
|
+
tableIndex: [
|
2756
|
+
{
|
2757
|
+
columnName: "ifIndex"
|
2758
|
+
}
|
2759
|
+
]
|
2760
|
+
// No handler required - automatic fallback works for tables too!
|
2761
|
+
};
|
2762
|
+
|
2763
|
+
// Register providers and set values
|
2764
|
+
subagent.registerProvider(stringProvider, function(error, data) {
|
2765
|
+
if (!error) {
|
2766
|
+
subagent.getMib().setScalarValue("scalarString", "Hello World!");
|
2767
|
+
}
|
2768
|
+
});
|
2769
|
+
|
2770
|
+
subagent.registerProvider(intProvider, function(error, data) {
|
2771
|
+
if (!error) {
|
2772
|
+
subagent.getMib().setScalarValue("scalarInt", 42);
|
2773
|
+
}
|
2774
|
+
});
|
2775
|
+
|
2776
|
+
subagent.registerProvider(tableProvider, function(error, data) {
|
2777
|
+
if (!error) {
|
2778
|
+
subagent.getMib().addTableRow("smallIfTable", [1, "lo"]);
|
2779
|
+
subagent.getMib().addTableRow("smallIfTable", [2, "eth0"]);
|
2780
|
+
}
|
2781
|
+
});
|
2782
|
+
```
|
2783
|
+
|
2707
2784
|
## snmp.createSubagent (options)
|
2708
2785
|
|
2709
2786
|
The `createSubagent ()` function instantiates and returns an instance of the `Subagent`
|
@@ -2764,6 +2841,11 @@ subsequent `Response` PDU from the master to the `Register` PDU. This is not to
|
|
2764
2841
|
with the `handler` optional callback on the provider definition, which is invoked for any
|
2765
2842
|
"request processing" PDU received by the subagent for MIB objects in the registered MIB region.
|
2766
2843
|
|
2844
|
+
**Note for AgentX subagents**: Neither scalar nor table providers require explicit `handler` functions.
|
2845
|
+
If no `handler` is provided, the subagent automatically calls `mibRequest.done()` to complete the request,
|
2846
|
+
allowing both scalar and table values to be served directly from the MIB without additional processing.
|
2847
|
+
This greatly simplifies provider definitions - you only need to specify the structure and `maxAccess` properties.
|
2848
|
+
|
2767
2849
|
## subagent.unregisterProvider (name, callback)
|
2768
2850
|
|
2769
2851
|
Unregisters a previously registered MIB region by the supplied name of the provider. Sends
|
@@ -2814,6 +2896,48 @@ Sends a "ping" to the master agent using a `Ping` PDU, to confirm that the maste
|
|
2814
2896
|
responsive. The supplied `callback` is called on reception of the subsequent
|
2815
2897
|
`Response` PDU from the master to the `Ping` PDU.
|
2816
2898
|
|
2899
|
+
## subagent.setBulkSetHandler (callback)
|
2900
|
+
|
2901
|
+
Sets a bulk set handler for the subagent that will be called for both **TestSet** and **CommitSet**
|
2902
|
+
phases of SNMP SET operations. This provides atomic validation capabilities, allowing you to validate
|
2903
|
+
all varbinds as a complete set before committing any changes.
|
2904
|
+
|
2905
|
+
The handler callback function receives two arguments:
|
2906
|
+
* `testSet` - A boolean indicating the phase: `true` for TestSet phase, `false` for CommitSet phase
|
2907
|
+
* `varbinds` - An array of all varbinds in the SET request
|
2908
|
+
|
2909
|
+
During the TestSet phase (`testSet = true`), you should validate all the requested changes but not
|
2910
|
+
apply them. During the CommitSet phase (`testSet = false`), you should apply the previously validated changes.
|
2911
|
+
|
2912
|
+
Example usage:
|
2913
|
+
|
2914
|
+
```js
|
2915
|
+
subagent.setBulkSetHandler(function(testSet, varbinds) {
|
2916
|
+
if (testSet) {
|
2917
|
+
// TestSet phase - validate all varbinds atomically
|
2918
|
+
console.log("Validating SET operation with", varbinds.length, "varbinds");
|
2919
|
+
for (let varbind of varbinds) {
|
2920
|
+
// Perform validation logic here
|
2921
|
+
if (!isValidValue(varbind.oid, varbind.value)) {
|
2922
|
+
throw new Error("Invalid value for " + varbind.oid);
|
2923
|
+
}
|
2924
|
+
}
|
2925
|
+
console.log("All varbinds validated successfully");
|
2926
|
+
} else {
|
2927
|
+
// CommitSet phase - apply the changes
|
2928
|
+
console.log("Committing SET operation");
|
2929
|
+
for (let varbind of varbinds) {
|
2930
|
+
// Apply the validated changes here
|
2931
|
+
applyValue(varbind.oid, varbind.value);
|
2932
|
+
}
|
2933
|
+
console.log("All changes committed");
|
2934
|
+
}
|
2935
|
+
});
|
2936
|
+
```
|
2937
|
+
|
2938
|
+
This approach ensures atomic SET operations where either all varbinds in a request succeed or all fail,
|
2939
|
+
maintaining data consistency across your MIB implementation.
|
2940
|
+
|
2817
2941
|
## subagent.on ("close", callback)
|
2818
2942
|
|
2819
2943
|
The `close` event is emitted by the subagent when its underlying TCP socket is closed.
|
@@ -3565,6 +3689,14 @@ Example programs are included under the module's `example` directory.
|
|
3565
3689
|
|
3566
3690
|
* Add support for custom dgram module
|
3567
3691
|
|
3692
|
+
# Version 3.24.0 - 28/08/2025
|
3693
|
+
|
3694
|
+
* Improve USM error handling compliance with RFC 3414
|
3695
|
+
|
3696
|
+
# Version 3.25.0 - 28/08/2025
|
3697
|
+
|
3698
|
+
* Add separate AgentX subagent TestSet and CommitSet phases
|
3699
|
+
|
3568
3700
|
# License
|
3569
3701
|
|
3570
3702
|
Copyright (c) 2020 Mark Abrahams <mark@abrahams.co.nz>
|
package/eslint.config.js
CHANGED
@@ -23,10 +23,13 @@ module.exports = [
|
|
23
23
|
setTimeout: 'readonly',
|
24
24
|
clearInterval: 'readonly',
|
25
25
|
clearTimeout: 'readonly',
|
26
|
+
setImmediate: 'readonly',
|
27
|
+
clearImmediate: 'readonly',
|
26
28
|
|
27
29
|
// Mocha globals
|
28
30
|
describe: 'readonly',
|
29
31
|
it: 'readonly',
|
32
|
+
xit: 'readonly',
|
30
33
|
before: 'readonly',
|
31
34
|
after: 'readonly',
|
32
35
|
beforeEach: 'readonly',
|
@@ -15,33 +15,39 @@ var stringProvider = {
|
|
15
15
|
name: "scalarString",
|
16
16
|
type: snmp.MibProviderType.Scalar,
|
17
17
|
oid: "1.3.6.1.4.1.8072.9999.9999.1",
|
18
|
-
scalarType: snmp.ObjectType.OctetString
|
18
|
+
scalarType: snmp.ObjectType.OctetString,
|
19
|
+
maxAccess: snmp.MaxAccess["read-write"]
|
19
20
|
};
|
20
21
|
var intProvider = {
|
21
22
|
name: "scalarInt",
|
22
23
|
type: snmp.MibProviderType.Scalar,
|
23
24
|
oid: "1.3.6.1.4.1.8072.9999.9999.3",
|
24
|
-
scalarType: snmp.ObjectType.Integer
|
25
|
+
scalarType: snmp.ObjectType.Integer,
|
26
|
+
maxAccess: snmp.MaxAccess["read-write"]
|
25
27
|
};
|
26
28
|
var tableProvider = {
|
27
29
|
name: "smallIfTable",
|
28
30
|
type: snmp.MibProviderType.Table,
|
29
31
|
oid: "1.3.6.1.4.1.8072.9999.9999.2",
|
32
|
+
maxAccess: snmp.MaxAccess['not-accessible'],
|
30
33
|
tableColumns: [
|
31
34
|
{
|
32
35
|
number: 1,
|
33
36
|
name: "ifIndex",
|
34
|
-
type: snmp.ObjectType.Integer
|
37
|
+
type: snmp.ObjectType.Integer,
|
38
|
+
maxAccess: snmp.MaxAccess['read-only']
|
35
39
|
},
|
36
40
|
{
|
37
41
|
number: 2,
|
38
42
|
name: "ifDescr",
|
39
|
-
type: snmp.ObjectType.OctetString
|
43
|
+
type: snmp.ObjectType.OctetString,
|
44
|
+
maxAccess: snmp.MaxAccess['read-write']
|
40
45
|
},
|
41
46
|
{
|
42
47
|
number: 3,
|
43
48
|
name: "ifType",
|
44
|
-
type: snmp.ObjectType.Integer
|
49
|
+
type: snmp.ObjectType.Integer,
|
50
|
+
maxAccess: snmp.MaxAccess['read-only']
|
45
51
|
}
|
46
52
|
],
|
47
53
|
tableIndex: [
|
@@ -49,10 +55,7 @@ var tableProvider = {
|
|
49
55
|
columnName: "ifIndex"
|
50
56
|
}
|
51
57
|
],
|
52
|
-
|
53
|
-
// e.g. can update the table before responding to the request here
|
54
|
-
mibRequest.done ();
|
55
|
-
}
|
58
|
+
|
56
59
|
};
|
57
60
|
|
58
61
|
agent.open(function (error, data) {
|
@@ -66,9 +69,41 @@ agent.open(function (error, data) {
|
|
66
69
|
agent.registerProvider (tableProvider, null);
|
67
70
|
agent.getMib ().addTableRow ("smallIfTable", [1, "lo", 24]);
|
68
71
|
agent.getMib ().addTableRow ("smallIfTable", [2, "eth0", 6]);
|
72
|
+
|
73
|
+
console.log("AgentX subagent ready and providers registered successfully.");
|
74
|
+
|
69
75
|
agent.on("close", function() {
|
70
76
|
console.log ("Subagent socket closed");
|
71
77
|
});
|
78
|
+
|
79
|
+
// Handle graceful shutdown on Ctrl-C
|
80
|
+
process.on('SIGINT', function() {
|
81
|
+
console.log('\nReceived SIGINT. Cleaning up...');
|
82
|
+
|
83
|
+
// Unregister all providers
|
84
|
+
try {
|
85
|
+
agent.unregisterProvider("scalarString", function(err, data) {
|
86
|
+
if (err) console.error("Error unregistering scalarString:", err);
|
87
|
+
});
|
88
|
+
agent.unregisterProvider("scalarInt", function(err, data) {
|
89
|
+
if (err) console.error("Error unregistering scalarInt:", err);
|
90
|
+
});
|
91
|
+
agent.unregisterProvider("smallIfTable", function(err, data) {
|
92
|
+
if (err) console.error("Error unregistering smallIfTable:", err);
|
93
|
+
});
|
94
|
+
} catch (e) {
|
95
|
+
console.error("Error during unregistration:", e.message);
|
96
|
+
}
|
97
|
+
|
98
|
+
// Close the agent after a brief delay to allow unregistrations to complete
|
99
|
+
setTimeout(function() {
|
100
|
+
agent.close(function(err) {
|
101
|
+
if (err) console.error("Error closing agent:", err);
|
102
|
+
console.log("Agent closed gracefully");
|
103
|
+
process.exit(0);
|
104
|
+
});
|
105
|
+
}, 100);
|
106
|
+
});
|
72
107
|
}
|
73
108
|
});
|
74
109
|
|