badnet 0.2.0

Emulation of terrible networks for unit tests and more.
Documentation
  • Coverage
  • 62.07%
    18 out of 29 items documented3 out of 17 items with examples
  • Size
  • Source code size: 63.2 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 3.7 MB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 19s Average build duration of successful builds.
  • all releases: 14s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • Homepage
  • graysonhead/badnet
    1 0 0
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • graysonhead

badnet

crates.io docs.rs

Simulate bad network conditions in Rust integration tests using Linux tc-netem.

Each BadNet instance provisions a loopback address pair wired through a configurable netem qdisc. Traffic sent between the two addresses passes through the impairment rules. Everything is cleaned up when the handle is dropped.

Usage

use std::time::Duration;
use badnet::BadNet;

let net = BadNet::builder()
    .seed(42)           // reproducible pattern across runs
    .loss(0.10)         // 10% packet loss
    .corrupt(0.02)      // 2% bit-flip (appears as loss at the socket layer)
    .duplicate(0.05)    // 5% duplication
    .delay(Duration::from_millis(50))
    .reorder(0.20)      // 20% of packets skip the delay and arrive early
    .build()?;

// Bind sockets to the two ends and communicate normally.
let tx = UdpSocket::bind(format!("{}:0",    net.left_addr()))?;
let rx = UdpSocket::bind(format!("{}:9000", net.right_addr()))?;

reorder requires delay. gap (periodic deterministic reorder) requires both:

let net = BadNet::builder()
    .delay(Duration::from_millis(20))
    .reorder(1.0)
    .gap(5)   // every 5th packet is sent immediately
    .build()?;

Requirements

Linux only. Requires CAP_NET_ADMIN. Grant it with setcap or run as root:

sudo setcap cap_net_admin+eip target/debug/deps/<test_binary>

Troubleshooting

Stale state after Ctrl-C or SIGKILL

BadNet cleans up TC rules and loopback addresses in its Drop implementation. If the process is killed before Drop runs, the next run will fail with errors like Exclusivity flag on or Address already assigned. To clean up manually:

sudo tc qdisc del dev lo root 2>/dev/null; \
sudo ip -4 addr show dev lo | awk '/inet 10\./{print $2}' | \
  xargs -r -I{} sudo ip addr del {} dev lo

Ctrl-C in long-running binaries

Rust does not run destructors on SIGINT by default. Install a signal handler that calls std::process::exit (which does run destructors) to ensure cleanup on interrupt. The ctrlc crate makes this easy:

ctrlc::set_handler(|| std::process::exit(0)).unwrap();