zond-engine 0.6.1

A lightweight, fast, and highly concurrent networking backend for packet crafting, protocol fingerprinting, and host discovery.
Documentation
// Copyright (c) 2026 Erik Lening (hollowpointer) and Contributors
//
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at
// https://mozilla.org/MPL/2.0/.

use std::process::Command;
use std::thread;
use std::time::Duration;

/// RAII wrapper for a network namespace infrastructure used in integration tests.
///
/// It creates a new network namespace, a veth pair, and sets up IP addresses
/// to simulate a real network environment without touching the host's primary stack.
pub struct NetnsContext {
    pub ns_name: String,
    pub host_if: String,
    pub _target_if: String,
}

impl NetnsContext {
    /// Creates a new network namespace with the given suffix.
    /// Returns None if root privileges are missing or 'ip' command fails.
    pub fn new(suffix: &str) -> Option<Self> {
        let ns_name = format!("zond-ns-{}", suffix);
        let host_if = format!("v-host-{}", suffix);
        let target_if = format!("v-targ-{}", suffix);

        Self::cleanup(&ns_name, &host_if);

        if !run_cmd("ip", &["netns", "add", &ns_name]) {
            return None;
        }

        if !run_cmd(
            "ip",
            &[
                "link", "add", &host_if, "type", "veth", "peer", "name", &target_if,
            ],
        ) {
            Self::cleanup(&ns_name, &host_if);
            return None;
        }

        if !run_cmd("ip", &["link", "set", &target_if, "netns", &ns_name]) {
            Self::cleanup(&ns_name, &host_if);
            return None;
        }

        run_cmd("ip", &["addr", "add", "10.200.0.1/24", "dev", &host_if]);
        run_cmd("ip", &["link", "set", &host_if, "up"]);

        run_ns_cmd(
            &ns_name,
            "ip",
            &["addr", "add", "10.200.0.2/24", "dev", &target_if],
        );
        run_ns_cmd(&ns_name, "ip", &["link", "set", &target_if, "up"]);
        run_ns_cmd(&ns_name, "ip", &["link", "set", "lo", "up"]);

        thread::sleep(Duration::from_millis(500));

        Some(Self {
            ns_name,
            host_if,
            _target_if: target_if,
        })
    }

    fn cleanup(ns_name: &str, host_if: &str) {
        let _ = Command::new("ip").args(["netns", "del", ns_name]).output();
        let _ = Command::new("ip").args(["link", "del", host_if]).output();
    }
}

impl Drop for NetnsContext {
    fn drop(&mut self) {
        Self::cleanup(&self.ns_name, &self.host_if);
    }
}

pub fn run_cmd(cmd: &str, args: &[&str]) -> bool {
    let status = Command::new(cmd).args(args).status();
    match status {
        Ok(s) => s.success(),
        Err(_) => false,
    }
}

pub fn run_ns_cmd(ns: &str, cmd: &str, args: &[&str]) -> bool {
    let mut final_args = vec!["netns", "exec", ns, cmd];
    final_args.extend_from_slice(args);
    run_cmd("ip", &final_args)
}