# async-snmp
[](https://github.com/lukeod/async-snmp/actions/workflows/ci.yml)
[](https://crates.io/crates/async-snmp)
[](https://docs.rs/async-snmp)
[](https://blog.rust-lang.org/)
[](#license)
Modern, async-first SNMP client library for Rust.
## Note
This library is not currently stable. While pre v1.0, breaking changes are likely to occur frequently, no attempt will be made to maintain backward compatibility pre-1.0.
## Features
- **Full protocol support**: SNMPv1, v2c, and v3 (USM)
- **Async-first**: Built on Tokio for high-performance async I/O
- **All operations**: GET, GETNEXT, GETBULK, SET, WALK, BULKWALK
- **SNMPv3 security**: MD5/SHA-1/SHA-2 authentication, DES/3DES/AES-128/192/256 privacy
- **Multiple transports**: UDP, TCP, and shared UDP for scalable polling
- **Zero-copy decoding**: Minimal allocations using `bytes` crate
- **Type-safe**: Compile-time OID validation with `oid!` macro
### Protocol Support Matrix
| GET / GETNEXT | Y | Y | Y |
| GETBULK | - | Y | Y |
| SET | Y | Y | Y |
| WALK / BULKWALK | Y | Y | Y |
| Receive Traps | Y | Y | Y |
| Receive Informs | - | Y | Y |
### SNMPv3 Security
**Authentication:** MD5, SHA-1, SHA-224, SHA-256, SHA-384, SHA-512
**Privacy:** DES, 3DES, AES-128, AES-192, AES-256
## Installation
```bash
cargo add async-snmp
```
Or add to your `Cargo.toml`:
```toml
[dependencies]
async-snmp = "0.5"
```
## Quick Start
### SNMPv2c
```rust
use async_snmp::{Auth, Client, oid};
use std::time::Duration;
#[tokio::main]
async fn main() -> Result<(), async_snmp::Error> {
let client = Client::builder("192.168.1.1:161", Auth::v2c("public"))
.timeout(Duration::from_secs(5))
.connect()
.await?;
let result = client.get(&oid!(1, 3, 6, 1, 2, 1, 1, 1, 0)).await?;
println!("sysDescr: {:?}", result.value);
Ok(())
}
```
### SNMPv3 with Authentication and Privacy
```rust
use async_snmp::{Auth, Client, oid, v3::{AuthProtocol, PrivProtocol}};
#[tokio::main]
async fn main() -> Result<(), async_snmp::Error> {
let client = Client::builder("192.168.1.1:161",
Auth::usm("admin")
.auth(AuthProtocol::Sha256, "authpass123")
.privacy(PrivProtocol::Aes128, "privpass123"))
.connect()
.await?;
let result = client.get(&oid!(1, 3, 6, 1, 2, 1, 1, 1, 0)).await?;
println!("sysDescr: {:?}", result.value);
Ok(())
}
```
### Walking a Subtree
```rust
use async_snmp::{Auth, Client, oid};
use futures::StreamExt;
#[tokio::main]
async fn main() -> Result<(), async_snmp::Error> {
let client = Client::builder("192.168.1.1:161", Auth::v2c("public"))
.connect()
.await?;
// Walk the system subtree
let mut walk = client.walk(oid!(1, 3, 6, 1, 2, 1, 1))?;
while let Some(result) = walk.next().await {
let vb = result?;
println!("{}: {:?}", vb.oid, vb.value);
}
Ok(())
}
```
### Scalable Polling (Shared Transport)
For monitoring systems polling thousands of targets, share a single UDP socket across all clients. This provides significant resource efficiency without sacrificing throughput:
```rust
use async_snmp::{Auth, Client, UdpTransport, oid};
#[tokio::main]
async fn main() -> Result<(), async_snmp::Error> {
// Single dual-stack socket shared across all clients
let shared = UdpTransport::bind("[::]:0").await?;
let targets = vec!["192.168.1.1:161", "192.168.1.2:161", "192.168.1.3:161"];
let clients: Vec<_> = targets.iter()
.map(|t| {
Client::builder(*t, Auth::v2c("public"))
.build_with(&shared)
})
.collect::<Result<_, _>>()?;
// Poll all targets concurrently - sharing one UDP socket
let results = futures::future::join_all(
clients.iter().map(|c| c.get(&oid!(1, 3, 6, 1, 2, 1, 1, 3, 0)))
).await;
for (client, result) in clients.iter().zip(results) {
match result {
Ok(vb) => println!("{}: {:?}", client.peer_addr(), vb.value),
Err(e) => eprintln!("{}: {}", client.peer_addr(), e),
}
}
Ok(())
}
```
**Benefits of shared transport:**
- **1 file descriptor** for all targets (vs 1 per target with separate sockets)
- **Firewall session reuse** between polls to the same target
- **Lower memory** from shared socket buffers
- **No per-poll socket creation** overhead
**Scaling guidance:**
| Single shared socket | Recommended for most use cases |
| Multiple shared sockets | Extreme scale (~100,000s+ targets), shard by target |
| Per-client socket (`.connect()`) | When scrape isolation is required (has FD and syscall overhead) |
### Tracing
The library uses the `tracing` crate for structured logging. Filter by target:
```bash
# All library logs at debug level
RUST_LOG=async_snmp=debug cargo run
# Trace client operations only
RUST_LOG=async_snmp::client=trace cargo run
# Debug transport layer
RUST_LOG=async_snmp::transport=debug cargo run
```
Available targets:
- **Core**: `async_snmp::client`, `async_snmp::agent`, `async_snmp::notification`
- **Protocol**: `async_snmp::ber`, `async_snmp::pdu`, `async_snmp::oid`, `async_snmp::value`
- **SNMPv3**: `async_snmp::v3`, `async_snmp::usm`, `async_snmp::crypto`, `async_snmp::engine`
- **Transport**: `async_snmp::transport`, `async_snmp::transport::tcp`, `async_snmp::transport::udp`
- **Operations**: `async_snmp::walk`, `async_snmp::error`
## Documentation
Full API documentation is available on [docs.rs](https://docs.rs/async-snmp).
## Feature Flags
| `cli` | CLI utilities (`asnmp-get`, `asnmp-walk`, `asnmp-set`) |
## Minimum Supported Rust Version
This crate requires Rust 1.88 or later. The MSRV may be increased in minor version releases.
## License
Licensed under either of:
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or <http://www.apache.org/licenses/LICENSE-2.0>)
- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
at your option.
## Contributing
Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.