12 releases
new 0.4.2 | Jan 5, 2025 |
---|---|
0.4.1 | Jan 1, 2025 |
0.3.8 | Dec 29, 2024 |
#87 in Authentication
903 downloads per month
100KB
2.5K
SLoC
RUST-SNMP
Dependency-free basic SNMP v1/v2/v3 client in Rust.
This is a fork of the original snmp crate which has been abandoned long time ago.
SNMP2 is a part of RoboPLC project.
New features added to the fork:
- SNMP v1 support (including v1 traps)
- SNMP v3 authentication (MD5, SHA1, SHA224, SHA256, SHA384, SHA512)
- SNMP v3 privacy (DES, AES128, AES192, AES256)
- MIBs support (requires
mibs
feature andlibnetsnmp
library installed) - Async session (requires
tokio
feature) - Crate code has been refactored and cleaned up
- OIDs have been migrated to asn1
- Improved PDU API, added trap handling examples
Supports:
- GET
- GETNEXT
- GETBULK
- SET
- Basic SNMP v1/v2 types
- Synchronous/Asynchronous requests
- UDP transport
- MIBs (with
mibs
feature, requireslibnetsnmp
) - SNMP v3 (requires
v3
feature)
Examples
GET NEXT
use std::time::Duration;
use snmp2::{SyncSession, Value, Oid};
let sys_descr_oid = Oid::from(&[1,3,6,1,2,1,1,1,]).unwrap();
let agent_addr = "198.51.100.123:161";
let community = b"f00b4r";
let timeout = Duration::from_secs(2);
let mut sess = SyncSession::new_v2c(agent_addr, community, Some(timeout), 0).unwrap();
let mut response = sess.getnext(&sys_descr_oid).unwrap();
if let Some((_oid, Value::OctetString(sys_descr))) = response.varbinds.next() {
println!("myrouter sysDescr: {}", String::from_utf8_lossy(sys_descr));
}
GET BULK
use std::time::Duration;
use snmp2::{SyncSession, Oid};
let system_oid = Oid::from(&[1,3,6,1,2,1,1,]).unwrap();
let agent_addr = "[2001:db8:f00:b413::abc]:161";
let community = b"f00b4r";
let timeout = Duration::from_secs(2);
let non_repeaters = 0;
let max_repetitions = 7; // number of items in "system" OID
let mut sess = SyncSession::new_v2c(agent_addr, community, Some(timeout), 0).unwrap();
let response = sess.getbulk(&[&system_oid], non_repeaters, max_repetitions).unwrap();
for (name, val) in response.varbinds {
println!("{} => {:?}", name, val);
}
SET
use std::time::Duration;
use snmp2::{SyncSession, Value, Oid};
let syscontact_oid = Oid::from(&[1,3,6,1,2,1,1,4,0]).unwrap();
let contact = Value::OctetString(b"Thomas A. Anderson");
let agent_addr = "[2001:db8:f00:b413::abc]:161";
let community = b"f00b4r";
let timeout = Duration::from_secs(2);
let mut sess = SyncSession::new_v2c(agent_addr, community, Some(timeout), 0).unwrap();
let response = sess.set(&[(&syscontact_oid, contact)]).unwrap();
assert_eq!(response.error_status, snmp2::snmp::ERRSTATUS_NOERROR);
for (name, val) in response.varbinds {
println!("{} => {:?}", name, val);
}
TRAPS
use std::net::UdpSocket;
use snmp2::Pdu;
let socket = UdpSocket::bind("0.0.0.0:1162").expect("Could not bind socket");
loop {
let mut buf = [0; 1500];
let size = socket.recv(&mut buf).expect("Could not receive data");
let data = &buf[..size];
let pdu = Pdu::from_bytes(data).expect("Could not parse PDU");
println!("Version: {}", pdu.version().unwrap());
println!("Community: {}", std::str::from_utf8(pdu.community).unwrap());
for (name, value) in pdu.varbinds {
println!("{}={:?}", name, value);
}
}
Async session
use std::time::Duration;
use snmp2::{AsyncSession, Value, Oid};
async fn get_next() {
// timeouts should be handled by the caller with `tokio::time::timeout`
let sys_descr_oid = Oid::from(&[1,3,6,1,2,1,1,1,]).unwrap();
let agent_addr = "198.51.100.123:161";
let community = b"f00b4r";
let mut sess = AsyncSession::new_v2c(agent_addr, community, 0).await.unwrap();
let mut response = sess.getnext(&sys_descr_oid).await.unwrap();
if let Some((_oid, Value::OctetString(sys_descr))) = response.varbinds.next() {
println!("myrouter sysDescr: {}", String::from_utf8_lossy(sys_descr));
}
}
Working with MIBs
Prepare the system
apt-get install libsnmp-dev snmp-mibs-downloader
use snmp2::{mibs::{self, MibConversion as _}, Oid};
mibs::init(&mibs::Config::new().mibs(&["./ibmConvergedPowerSystems.mib"]))
.unwrap();
let snmp_oid = Oid::from(&[1, 3, 6, 1, 4, 1, 2, 6, 201, 3]).unwrap();
let name = snmp_oid.mib_name().unwrap();
assert_eq!(name, "IBM-CPS-MIB::cpsSystemSendTrap");
let snmp_oid2 = Oid::from_mib_name(&name).unwrap();
assert_eq!(snmp_oid, snmp_oid2);
SNMPv3
-
Requires
v3
crate feature. -
All cryptographic algorithms are provided by openssl.
-
For authentication, supports: MD5 (RFC3414), SHA1 (RFC3414) and non-standard SHA224, SHA256, SHA384, SHA512.
-
For privacy, supports: DES (RFC3414), AES128-CFB (RFC3826) and non-standard AES192-CFB, AES256-CFB. Additional/different AES modes are not supported and may require patching the crate.
Note: DES legacy encryption may be disabled in openssl by default or even not supported at all. Refer to the library documentation how to enable it.
Example
Authentication: SHA1, encryption: AES128-CFB
use snmp2::{SyncSession, v3, Oid};
use std::time::Duration;
// the security parameters also keep authoritative engine ID and boot/time
// counters. these can be either set or resolved/updated automatically.
let security = v3::Security::new(b"public", b"secure")
.with_auth_protocol(v3::AuthProtocol::Sha1)
.with_auth(v3::Auth::AuthPriv {
cipher: v3::Cipher::Aes128,
privacy_password: b"secure-encrypt".to_vec(),
});
let mut sess =
SyncSession::new_v3("192.168.1.1:161", Some(Duration::from_secs(2)), 0, security).unwrap();
// In case if engine_id is not provided in security parameters, it is necessary
// to call init() method to send a blank unauthenticated request to the target
// to get the engine_id.
sess.init().unwrap();
loop {
let res = match sess.get(&Oid::from(&[1, 3, 6, 1, 2, 1, 1, 3, 0]).unwrap()) {
Ok(r) => r,
// In case if the engine boot / time counters are not set in the security parameters or
// they have been changed on the target, e.g. after a reboot, the session returns
// an error with the AuthUpdated code. In this case, security parameters are automatically
// updated and the request should be repeated.
Err(snmp2::Error::AuthUpdated) => continue,
Err(e) => panic!("{}", e),
};
println!("{} {:?}", res.version().unwrap(), res.varbinds);
std::thread::sleep(Duration::from_secs(1));
}
Building
In case of problems (e.g. with cross-rs),
add openssl
with vendored
feature:
cargo add openssl --features vendored
FIPS-140 support
The crate uses openssl cryptography only and becomes FIPS-140 compliant as soon
as FIPS mode is activated in openssl
. Refer to the
openssl crate crate and
openssl library documentation for more details.
MSRV
1.68.0
Copyright
Copyright 2016-2018 Hroi Sigurdsson
Copyright 2024 Serhij Symonenko, Bohemia Automation Limited
Licensed under the Apache License, Version 2.0 or the MIT license, at your option. This file may not be copied, modified, or distributed except according to those terms.
Dependencies
~2–11MB
~112K SLoC