pub struct AgentBuilder { /* private fields */ }Expand description
Builder for Agent.
Use this builder to configure and construct an SNMP agent. The builder
pattern allows you to chain configuration methods before calling
build() to create the agent.
§Access Control
By default, the agent operates in permissive mode: any authenticated request (valid community string for v1/v2c, valid USM credentials for v3) has full read and write access to all registered handlers.
For production deployments, use the vacm() method
to configure View-based Access Control (RFC 3415), which allows fine-grained
control over which security names can access which OID subtrees.
§Minimal Example
use async_snmp::agent::Agent;
use async_snmp::handler::{MibHandler, RequestContext, GetResult, GetNextResult, BoxFuture};
use async_snmp::{Oid, Value, VarBind, oid};
use std::sync::Arc;
struct MyHandler;
impl MibHandler for MyHandler {
fn get<'a>(&'a self, _: &'a RequestContext, _: &'a Oid) -> BoxFuture<'a, GetResult> {
Box::pin(async { GetResult::NoSuchObject })
}
fn get_next<'a>(&'a self, _: &'a RequestContext, _: &'a Oid) -> BoxFuture<'a, GetNextResult> {
Box::pin(async { GetNextResult::EndOfMibView })
}
}
let agent = Agent::builder()
.bind("0.0.0.0:1161") // Use non-privileged port
.community(b"public")
.handler(oid!(1, 3, 6, 1, 4, 1, 99999), Arc::new(MyHandler))
.build()
.await?;Implementations§
Source§impl AgentBuilder
impl AgentBuilder
Sourcepub fn new() -> Self
pub fn new() -> Self
Create a new builder with default settings.
Defaults:
- Bind address:
0.0.0.0:161(UDP) - Max message size: 1472 bytes (Ethernet MTU - IP/UDP headers)
- Max concurrent requests: 1000
- Receive buffer size: 4MB (requested from kernel)
- No communities or USM users (all requests rejected)
- No handlers registered
Sourcepub fn bind(self, addr: impl Into<String>) -> Self
pub fn bind(self, addr: impl Into<String>) -> Self
Set the UDP bind address.
Default is 0.0.0.0:161 (standard SNMP agent port). Note that binding
to UDP port 161 typically requires root/administrator privileges.
§IPv4 Examples
use async_snmp::agent::Agent;
// Bind to all IPv4 interfaces on standard port (requires privileges)
let agent = Agent::builder().bind("0.0.0.0:161").community(b"public").build().await?;
// Bind to localhost only on non-privileged port
let agent = Agent::builder().bind("127.0.0.1:1161").community(b"public").build().await?;
// Bind to specific interface
let agent = Agent::builder().bind("192.168.1.100:161").community(b"public").build().await?;§IPv6 / Dual-Stack Examples
use async_snmp::agent::Agent;
// Bind to all interfaces (IPv6, with dual-stack on Linux)
let agent = Agent::builder().bind("[::]:161").community(b"public").build().await?;
// Bind to IPv6 localhost only
let agent = Agent::builder().bind("[::1]:1161").community(b"public").build().await?;Sourcepub fn community(self, community: &[u8]) -> Self
pub fn community(self, community: &[u8]) -> Self
Add an accepted community string for v1/v2c requests.
Multiple communities can be added. If none are added, all v1/v2c requests are rejected.
§Example
use async_snmp::agent::Agent;
let agent = Agent::builder()
.bind("0.0.0.0:1161")
.community(b"public") // Read-only access
.community(b"private") // Read-write access (with VACM)
.build()
.await?;Sourcepub fn communities<I, C>(self, communities: I) -> Self
pub fn communities<I, C>(self, communities: I) -> Self
Add multiple community strings.
§Example
use async_snmp::agent::Agent;
let communities = ["public", "private", "monitor"];
let agent = Agent::builder()
.bind("0.0.0.0:1161")
.communities(communities)
.build()
.await?;Sourcepub fn usm_user<F>(self, username: impl Into<Bytes>, configure: F) -> Self
pub fn usm_user<F>(self, username: impl Into<Bytes>, configure: F) -> Self
Add a USM user for SNMPv3 authentication.
Configure authentication and privacy settings using the closure. Multiple users can be added with different security levels.
§Security Levels
- noAuthNoPriv: No authentication or encryption
- authNoPriv: Authentication only (HMAC verification)
- authPriv: Authentication and encryption
§Example
use async_snmp::agent::Agent;
use async_snmp::{AuthProtocol, PrivProtocol};
let agent = Agent::builder()
.bind("0.0.0.0:1161")
// Read-only user with authentication only
.usm_user("monitor", |u| {
u.auth(AuthProtocol::Sha256, b"monitorpass123")
})
// Admin user with full encryption
.usm_user("admin", |u| {
u.auth(AuthProtocol::Sha256, b"adminauth123")
.privacy(PrivProtocol::Aes128, b"adminpriv123")
})
.build()
.await?;Sourcepub fn engine_id(self, engine_id: impl Into<Vec<u8>>) -> Self
pub fn engine_id(self, engine_id: impl Into<Vec<u8>>) -> Self
Set the engine ID for SNMPv3.
If not set, a default engine ID will be generated based on the RFC 3411 format using enterprise number and timestamp.
§Example
use async_snmp::agent::Agent;
let agent = Agent::builder()
.bind("0.0.0.0:1161")
.engine_id(b"\x80\x00\x00\x00\x01MyEngine".to_vec())
.community(b"public")
.build()
.await?;Sourcepub fn engine_boots(self, boots: u32) -> Self
pub fn engine_boots(self, boots: u32) -> Self
Set the initial engine boots value.
Per RFC 3414 Section 2.3, snmpEngineBoots must be monotonically increasing across restarts. The application is responsible for persisting and restoring this value. If not set, defaults to 1.
§Example
use async_snmp::agent::Agent;
// Load persisted value (e.g. from file or database)
let persisted_boots: u32 = 42;
let agent = Agent::builder()
.bind("0.0.0.0:1161")
.engine_boots(persisted_boots)
.community(b"public")
.build()
.await?;Sourcepub fn max_message_size(self, size: usize) -> Self
pub fn max_message_size(self, size: usize) -> Self
Set the maximum message size for responses.
Default is 1472 octets (fits Ethernet MTU minus IP/UDP headers). GETBULK responses will be truncated to fit within this limit.
For SNMPv3 requests, the agent uses the minimum of this value and the msgMaxSize from the request.
Sourcepub fn max_concurrent_requests(self, limit: Option<usize>) -> Self
pub fn max_concurrent_requests(self, limit: Option<usize>) -> Self
Set the maximum number of concurrent requests the agent will process.
Default is 1000. Requests beyond this limit will queue until a slot
becomes available. Set to None for unbounded concurrency.
This controls memory usage under high load while still allowing parallel request processing.
Sourcepub fn recv_buffer_size(self, size: Option<usize>) -> Self
pub fn recv_buffer_size(self, size: Option<usize>) -> Self
Set the UDP socket receive buffer size.
Default is 4MB. The kernel may cap this at net.core.rmem_max.
A larger buffer prevents packet loss during request bursts.
Set to None to use the kernel default.
Sourcepub fn handler(self, prefix: Oid, handler: Arc<dyn MibHandler>) -> Self
pub fn handler(self, prefix: Oid, handler: Arc<dyn MibHandler>) -> Self
Register a MIB handler for an OID subtree.
Handlers are matched by longest prefix. When a request comes in, the handler with the longest matching prefix is used.
§Example
use async_snmp::agent::Agent;
use async_snmp::handler::{MibHandler, RequestContext, GetResult, GetNextResult, BoxFuture};
use async_snmp::{Oid, Value, VarBind, oid};
use std::sync::Arc;
struct SystemHandler;
impl MibHandler for SystemHandler {
fn get<'a>(&'a self, _: &'a RequestContext, oid: &'a Oid) -> BoxFuture<'a, GetResult> {
Box::pin(async move {
if oid == &oid!(1, 3, 6, 1, 2, 1, 1, 1, 0) {
GetResult::Value(Value::OctetString("My Agent".into()))
} else {
GetResult::NoSuchObject
}
})
}
fn get_next<'a>(&'a self, _: &'a RequestContext, _: &'a Oid) -> BoxFuture<'a, GetNextResult> {
Box::pin(async { GetNextResult::EndOfMibView })
}
}
let agent = Agent::builder()
.bind("0.0.0.0:1161")
.community(b"public")
// Register handler for system MIB subtree
.handler(oid!(1, 3, 6, 1, 2, 1, 1), Arc::new(SystemHandler))
.build()
.await?;Sourcepub fn vacm<F>(self, configure: F) -> Self
pub fn vacm<F>(self, configure: F) -> Self
Configure VACM (View-based Access Control Model) using a builder function.
When VACM is configured, all requests are checked against the configured
access control rules. Requests that don’t have proper access are rejected
with noAccess error (v2c/v3) or noSuchName (v1).
Without VACM configuration, the agent operates in permissive mode: any authenticated request has full read/write access to all handlers.
§Example
use async_snmp::agent::{Agent, SecurityModel, VacmBuilder};
use async_snmp::message::SecurityLevel;
use async_snmp::oid;
let agent = Agent::builder()
.bind("0.0.0.0:161")
.community(b"public")
.community(b"private")
.vacm(|v| v
.group("public", SecurityModel::V2c, "readonly_group")
.group("private", SecurityModel::V2c, "readwrite_group")
.access("readonly_group", |a| a
.read_view("full_view"))
.access("readwrite_group", |a| a
.read_view("full_view")
.write_view("write_view"))
.view("full_view", |v| v
.include(oid!(1, 3, 6, 1)))
.view("write_view", |v| v
.include(oid!(1, 3, 6, 1, 2, 1, 1))))
.build()
.await?;Sourcepub fn cancel(self, token: CancellationToken) -> Self
pub fn cancel(self, token: CancellationToken) -> Self
Set a cancellation token for graceful shutdown.
If not set, the agent creates its own token accessible via Agent::cancel().
Sourcepub fn trap_sink(self, dest: impl Into<String>, auth: impl Into<Auth>) -> Self
pub fn trap_sink(self, dest: impl Into<String>, auth: impl Into<Auth>) -> Self
Add a trap/inform destination.
The agent will send notifications to all configured trap sinks when
Agent::send_trap() or Agent::send_inform() is called.
§Example
use async_snmp::agent::Agent;
use async_snmp::{Auth, AuthProtocol, PrivProtocol};
let agent = Agent::builder()
.bind("0.0.0.0:1161")
.community(b"public")
.trap_sink("192.168.1.100:162", Auth::v2c("public"))
.trap_sink("10.0.0.1:162", Auth::usm("trapuser")
.auth(AuthProtocol::Sha256, "authpass")
.privacy(PrivProtocol::Aes128, "privpass"))
.build()
.await?;Sourcepub fn inform_timeout(self, timeout: Duration) -> Self
pub fn inform_timeout(self, timeout: Duration) -> Self
Set the timeout for inform requests sent to trap sinks.
Default is 5 seconds. Only affects send_inform, not send_trap.
Sourcepub fn inform_retry(self, retry: Retry) -> Self
pub fn inform_retry(self, retry: Retry) -> Self
Set the retry policy for inform requests sent to trap sinks.
Default is Retry::default() (3 retries with 1-second delay).
Only affects send_inform, not send_trap.
Sourcepub fn without_builtin_handler(self, mib: BuiltinMib) -> Self
pub fn without_builtin_handler(self, mib: BuiltinMib) -> Self
Disable a specific built-in MIB handler group.
By default, the agent registers handlers for snmpEngine, USM stats, and MPD stats. Call this to prevent registration of a specific group, e.g., if you want to provide your own handler for those OIDs.
Sourcepub fn without_builtin_handlers(self) -> Self
pub fn without_builtin_handlers(self) -> Self
Disable all built-in MIB handlers.
The agent will not register any internal handlers for snmpEngine,
USM stats, or MPD stats. You can still query the counter values
via accessor methods like Agent::usm_unknown_engine_ids().
Trait Implementations§
Auto Trait Implementations§
impl Freeze for AgentBuilder
impl !RefUnwindSafe for AgentBuilder
impl Send for AgentBuilder
impl Sync for AgentBuilder
impl Unpin for AgentBuilder
impl UnsafeUnpin for AgentBuilder
impl !UnwindSafe for AgentBuilder
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more