rscamper 0.2.2

Rust interface to scamper network measurement tool
Documentation

rscamper

A Rust interface to scamper network measurement tool.

rscamper wraps the scamper C libraries (libscamperfile and libscamperctrl) through Rust FFI, providing idiomatic Rust types for all measurement data structures and a friendly control loop.

Features

  • File I/O — read and write scamper warts/JSON files via ScamperFile
  • Control loop — connect to a running scamper daemon and issue measurements via ScamperCtrl
  • All measurement typestrace, ping, tracelb, dealias, host, http, tbit, sniff, udpprobe, owamp, sting, neighbourdisc
  • Mux / VP support — enumerate and connect to vantage points via ScamperMux and ScamperVp

Prerequisites

rscamper links against libscamperfile and libscamperctrl, which are part of the scamper distribution. Build and install scamper from source following the official instructions.

By default, the libraries are searched for in /usr/local/lib and /usr/lib. If you installed scamper to a custom prefix, set the SCAMPER_LIB_DIR environment variable at build time:

SCAMPER_LIB_DIR=/opt/scamper/lib cargo build

Installation

Run the following Cargo command in your project directory:

cargo add rscamper

Usage

Reading a warts file

use rscamper::{ScamperFile, ScamperObject};

let file = ScamperFile::open("results.warts", 'r', None).unwrap();
for obj in file {
    if let ScamperObject::Ping(ping) = obj {
        println!("{}", ping.to_json().unwrap_or_default());
    }
}

One-shot measurement

Schedule a single measurement, wait for the result, and save it to disk. inst.done() is a required lifecycle signal — without it responses() and poll() block forever.

use rscamper::{ScamperCtrl, ScamperFile, ScamperObject};

let mut ctrl = ScamperCtrl::new(false, None).unwrap();
let inst = ctrl.add_inet(31337, None).unwrap();

ctrl.do_ping(
    &inst, "1.1.1.1",
    None, None, None, None, None, None, None, None,
    None, None, None, None, None, None, None, None,
    None, None, None, None, None,
).unwrap();
inst.done(); // no more commands on this instance

// Pull exactly one result — blocks until it is ready.
// Equivalent: ctrl.responses(None).next().unwrap()
let item = ctrl.poll().unwrap();

let mut f = ScamperFile::open("result.warts", 'w', Some("warts")).unwrap();
f.write(&item.obj).unwrap();
f.close();

Streaming multiple measurements

Schedule several measurements and process each result the moment it arrives, without waiting for the full batch to finish.

use std::time::Duration;
use rscamper::{ScamperCtrl, ScamperObject};

let mut ctrl = ScamperCtrl::new(false, None).unwrap();
let inst = ctrl.add_inet(31337, None).unwrap();

for dst in &["1.1.1.1", "8.8.8.8"] {
    ctrl.do_ping(
        &inst, dst,
        None, None, None, None, None, None, None, None,
        None, None, None, None, None, None, None, None,
        None, None, None, None, None,
    ).unwrap();
}
inst.done();

// poll() blocks until one result is ready, then returns it immediately.
// The loop ends when all tasks are complete.
while let Some(item) = ctrl.poll() {
    if let ScamperObject::Ping(ping) = &item.obj {
        if let Some(stats) = ping.stats() {
            println!("min RTT: {:?}", stats.min_rtt());
        }
    }
}

Iterator style

Use responses() when you want a for loop. The argument controls whether the iterator has a timeout:

Call Behaviour
responses(None) Waits indefinitely — ends only when all tasks are done
responses(Some(duration)) Stops early if the deadline elapses, even if tasks are still running

None is safe when you trust scamper to finish every measurement (e.g. each do_* call has its own wait_timeout). Some(duration) is a safety net for the whole batch — useful when you cannot predict how long the full set will take.

Choosing between poll() and responses()

Both deliver results one at a time as they arrive. The difference is timeout control and syntax:

poll() responses(Some(d))
Syntax while let Some(r) = ctrl.poll() for r in ctrl.responses(Some(d))
Per-result wait Indefinite — blocks until one result is ready Same
Batch timeout None — no way to stop the loop early Yes — stops the whole iteration if the deadline elapses
Use when You control timeouts per measurement via wait_timeout You need a hard cap on total wall-clock time for the batch

Use poll() when each do_* call already has a wait_timeout — scamper will time out individual measurements and poll() will eventually return None. Use responses(Some(duration)) when you want a single deadline that covers the entire batch regardless of how individual measurements behave.

use std::time::Duration;
use rscamper::{ScamperCtrl, ScamperObject};

let mut ctrl = ScamperCtrl::new(false, None).unwrap();
let inst = ctrl.add_inet(31337, None).unwrap();

ctrl.do_ping(
    &inst, "1.1.1.1",
    None, None, None, None, None, None, None, None,
    None, None, None, None, None, None, None, None,
    None, None, None, None, None,
).unwrap();
inst.done();

// No timeout — wait as long as it takes.
for item in ctrl.responses(None) {
    if let ScamperObject::Ping(ping) = &item.obj {
        if let Some(stats) = ping.stats() {
            println!("min RTT: {:?}", stats.min_rtt());
        }
    }
}

// With a 10-second safety timeout on the whole batch.
for item in ctrl.responses(Some(Duration::from_secs(10))) {
    if let ScamperObject::Ping(ping) = &item.obj {
        if let Some(stats) = ping.stats() {
            println!("min RTT: {:?}", stats.min_rtt());
        }
    }
}

Mux / vantage points

use rscamper::ScamperCtrl;

let mut ctrl = ScamperCtrl::new(false, None).unwrap();
let _mux_inst = ctrl.add_inet(31337, None).unwrap();

for vp in ctrl.vps() {
    let inst = ctrl.add_vp(&vp).unwrap();
    // issue measurements on inst ...
    inst.done();
}

License

GPL-3.0-only — see the GNU General Public License for details.