This is preliminary work towards a framework for developing SNMP v3 agents, using the rasn ASN-1 library for decoding the on the wire data. While a manager (effectively a client) has to support a range of legacy agents, an agent (e.g. server) can offer a subset of features and still be useful.
This agent only supports SNMP v3.
This project follows semver - but that means no guarantees at all pre v1!
This release implements (more or less) the transaction model for Set PDUs. This required a change to the OidKeeper trait, defined in src/keeper.rs. The sample handlers, and the Rust MIB compiler have been updated to follow this.
The agent server loop is a single threaded blocking design. I would argue that this is appropriate for almost all agents, as typically a single manager will interact with multiple agents. Managers may well want to support high levels of concurrency. The single threaded design avoids many issues with concurrency and locking. Of course, there is nothing to stop handlers for specific long running operations using a thread, and this might be a good option for the CreateAndWait model of table row creation. Good luck with that!
At present, there is a simplistic permissions model, and some real world applications will need more than that. Coming real soon. It may not be RFC view action model, as that seems complex, and the facility to dynamically change the permissions model remotely is not often implemented. Instead, some sort of compile time model seems more appropriate to the sort of boxes that run agents.
In this release the Engine ID is loaded from a configuration file. A sample configuration file is included at .snmp-agent.conf. Two example MIB module handlers are included under handlers for SNMPv3-MIB and SNMP-USER-BASED-SM-MIB. They were written based on stubs generated by the Rust tool. The USM handler does not yet include remote password changes and user creation.
There is no support for notifications so far.
There is no explicit support for Module Compliance.
There is a small python tool for generating a username and password file under tools/usekey.py. It picks the EngineID up from the configuration file.
Changing passwords on the wire is not yet implemented, and would be a really good project.
At present, there is only rough tooling to help implement an useful agent, but it is just about possible with enough patience. There are two stub generators, one in Python and one in Rust. The Python one is now deprecated and will be removed in a future release.
The source files for the Rust stub generator are under src/bin/stub-gen. It uses the nom parser combinator library for parsing. It has a reasonably complete parser implementation, which can parse almost all the MIBs on my machine except for legacy MIBS in Smi v1 and a few bootstrap definition files. The code generator in this version ignores everything to do with notifications and compliance. It also does the wrong thing with AUGMENTS.
First build the stub generator with:
cargo build --bin stub-gen
then generate the stubs for the mibs you want with:
target/debug/stub-gen -o src/stubs/ MIB1 MIB2 ...
where MIB1 and MIB2 and so on are the names of the MIB files to generate stubs from. The generator searches /var/lib/mibs/ietf, /var/lib/mibs/iana and /usr/share/snmp/mibs to find the files, and tries adding .txt extension as well. If your system has the files somewhere different, or you wish to include vendor mibs, edit src/bin/stub-gen/importer.rs and change the SEARCH_PATH constant. Arguably, this should be settable by the command line and / or an environment variable.
The generated stubs will be placed under src/stubs/.
If you want the agent to do something useful, you need to write your own back-end implementations.The generated stubs are placed in the src/stubs/ directory. The basic idea is to associate instances that support the OidKeeper trait with the OID value or values that they support in the OidMap. This is populated and then the agent loop_forever() runs.
Two toy implementations of the OidKeeper trait are provided by way of example, both purely memory based. One is for scalars, and the other is a limited table mode. Set can change cell values in existing rows. New rows can be created by the CreateAndWait mechanism if there is a RowStatus column in the table, and destroyed by Destroy. If you change the value of index cells, the results may be puzzling. If the MIB is correctly structured, the permissions checks should stop you making that mistake. The generated stub implementations just wrap the toy struct types, and need to be replaced by real actions.
Edit the configuration file .snmp-agent.conf, and insert your Enterprise number and other details.
Create a password file called users.txt, using python3 tools/usekey.py. For example,
python3 tools/usekey.py admin myv3user password password1 > users.txt
Optionally, edit groups.txt.
Set a suitable log level with, for example, export RUST_LOG=info.
Run the agent with cargo run.
Install netsnmp for testing purposes, and run in another terminal:
'''shell snmpwalk -v 3 -l authPriv -a SHA -A password -x AES -X password1 -u myv3user 127.0.0.1:2161 1.3.6.1 1.3.6.1.6.3 '''