netconf-rust 0.5.0

An async NETCONF client library implementing RFC 6241 (NETCONF), RFC 6242 (chunked framing), and RFC 4742 (EOM framing) with pipelining and zero-copy parsing
Documentation

netconf-rust

Warning: This library is EXTREMELY experimental and under active development. The API is subject to change without notice. Use in production at your own risk.

An async Rust NETCONF client implementing RFC 6241, RFC 6242, and RFC 4742 with pipelining and zero-copy XML parsing.

Features

  • Async -- Built on tokio with split read/write architecture
  • Pipelining -- Send multiple RPCs without waiting for replies, correlated by message-id
  • Streaming -- Stream large responses chunk-by-chunk via AsyncRead, avoiding unbounded memory usage
  • Zero-copy parsing -- DataPayload references the codec buffer directly, avoiding copies for large responses
  • RFC 6242 chunked framing -- Automatically negotiated during the hello exchange
  • RFC 4742 EOM framing -- Fallback for NETCONF 1.0-only devices
  • Keyboard-interactive auth -- Fallback for devices (Cisco, Nokia) that require it

Usage

use netconf_rust::{Session, Datastore};

#[tokio::main]
async fn main() -> netconf_rust::Result<()> {
    let mut session = Session::connect("192.168.1.1", 830, "admin", "admin").await?;

    let config = session.get_config(Datastore::Running, None).await?;
    println!("{config}");

    session.close_session().await?;
    Ok(())
}

Subtree filtering

let interfaces = session.get_config(
    Datastore::Running,
    Some(r#"<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"/>"#),
).await?;

Edit config with candidate commit

session.lock(Datastore::Candidate).await?;
session.edit_config(Datastore::Candidate, r#"
    <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
        <interface>
            <name>eth0</name>
            <description>Uplink</description>
        </interface>
    </interfaces>
"#).await?;
session.commit().await?;
session.unlock(Datastore::Candidate).await?;

Pipelining

let fut1 = session.rpc_send("<get-config><source><running/></source></get-config>").await?;
let fut2 = session.rpc_send("<get-config><source><running/></source></get-config>").await?;
let fut3 = session.rpc_send("<get-config><source><running/></source></get-config>").await?;

let reply1 = fut1.response().await?;
let reply2 = fut2.response().await?;
let reply3 = fut3.response().await?;

Zero-copy DataPayload

let payload = session.get_config_payload(Datastore::Running, None).await?;
println!("Config size: {} bytes", payload.len());

// Zero-copy &str view
let s: &str = payload.as_str();

// Stream XML events directly from the payload's bytes
let mut reader = payload.reader();

Streaming

use tokio::io::AsyncReadExt;

let mut stream = session.get_config_stream(Datastore::Running, None).await?;
let mut buf = [0u8; 8192];
loop {
    let n = stream.read(&mut buf).await?;
    if n == 0 { break; }
    // Process chunks as they arrive — constant memory usage
}

Raw RPC

let reply = session.rpc_raw(
    "<get-schema><identifier>ietf-interfaces</identifier></get-schema>"
).await?;

Built with

  • russh -- Async SSH implementation used for the transport layer
  • quick-xml -- Fast, zero-allocation XML parser powering our event-based parsing and span extraction
  • bytes -- Reference-counted byte buffers enabling zero-copy message handling

RFCs

  • RFC 6241 -- NETCONF Configuration Protocol
  • RFC 6242 -- Using NETCONF over SSH (chunked framing)
  • RFC 4742 -- Using NETCONF over SSH (EOM framing)

License

Licensed under either of Apache License, Version 2.0 or MIT License at your option.