diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c41a9f51c..8d9f69d03 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,51 +7,51 @@ on: name: CI jobs: -# test: -# runs-on: ${{ matrix.os }} -# strategy: -# matrix: -# build: -# - linux-stable -# - linux-musl-stable -# - linux-beta -# - linux-nightly -# - macos-stable -# - windows-stable -# include: -# - build: linux-stable -# os: ubuntu-20.04 -# target: x86_64-unknown-linux-gnu -# rust: stable -# - build: linux-musl-stable -# os: ubuntu-20.04 -# target: x86_64-unknown-linux-musl -# rust: stable -# - build: linux-beta -# os: ubuntu-22.04 -# target: x86_64-unknown-linux-gnu -# rust: beta -# - build: linux-nightly -# os: ubuntu-22.04 -# target: x86_64-unknown-linux-gnu -# rust: nightly -# - build: macos-stable -# os: macos-latest -# target: x86_64-apple-darwin -# rust: stable -# - build: windows-stable -# os: windows-latest -# target: x86_64-pc-windows-msvc -# rust: stable -# steps: -# - uses: actions/checkout@v2 -# - uses: dtolnay/rust-toolchain@stable -# with: -# toolchain: ${{ matrix.rust }} -# target: ${{ matrix.target }} -# - run: cargo test --workspace --all-features --target ${{ matrix.target }} + test: + runs-on: ${{ matrix.os }} + strategy: + matrix: + build: + - linux-stable + - linux-musl-stable + - linux-beta + - linux-nightly + - macos-stable + - windows-stable + include: + - build: linux-stable + os: ubuntu-20.04 + target: x86_64-unknown-linux-gnu + rust: stable + - build: linux-musl-stable + os: ubuntu-20.04 + target: x86_64-unknown-linux-musl + rust: stable + - build: linux-beta + os: ubuntu-22.04 + target: x86_64-unknown-linux-gnu + rust: beta + - build: linux-nightly + os: ubuntu-22.04 + target: x86_64-unknown-linux-gnu + rust: nightly + - build: macos-stable + os: macos-latest + target: x86_64-apple-darwin + rust: stable + - build: windows-stable + os: windows-latest + target: x86_64-pc-windows-msvc + rust: stable + steps: + - uses: actions/checkout@v2 + - uses: dtolnay/rust-toolchain@stable + with: + toolchain: ${{ matrix.rust }} + target: ${{ matrix.target }} + - run: cargo test --target ${{ matrix.target }} - e2e-test: + sim-test: runs-on: ${{ matrix.os }} strategy: matrix: @@ -74,7 +74,27 @@ jobs: with: toolchain: ${{ matrix.rust }} target: ${{ matrix.target }} - - run: sudo -E env "PATH=$PATH" cargo test --target ${{ matrix.target }} --package trippy --test it -- --exact --nocapture + - run: sudo -E env "PATH=$PATH" cargo test --target ${{ matrix.target }} --features sim-tests --test sim -- --exact --nocapture + + sim-test-windows: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - uses: Swatinem/rust-cache@v2 + - uses: dtolnay/rust-toolchain@stable + with: + toolchain: stable + target: x86_64-pc-windows-msvc + - name: Copy wintun.dll to current dir + shell: bash + run: | + cp "tests/resources/wintun.dll" "." + - name: Allow ICMPv4 and ICMPv6 in Windows defender firewall + shell: pwsh + run: | + New-NetFirewallRule -DisplayName "ICMPv4 Trippy Allow" -Name ICMPv4_TRIPPY_ALLOW -Protocol ICMPv4 -Action Allow + New-NetFirewallRule -DisplayName "ICMPv6 Trippy Allow" -Name ICMPv6_TRIPPY_ALLOW -Protocol ICMPv6 -Action Allow + - run: cargo test --target x86_64-pc-windows-msvc --features sim-tests --test sim -- --exact --nocapture fmt: runs-on: ubuntu-22.04 diff --git a/Cargo.toml b/Cargo.toml index 4381d1851..a90583f0a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -83,6 +83,10 @@ rand = "0.8.5" test-case = "3.3.1" tun = "0.6.1" +[features] +# Enable simulation integration tests +sim-tests = [] + # cargo-generate-rpm dependencies [package.metadata.generate-rpm] assets = [ diff --git a/tests/it/main.rs b/tests/it/main.rs deleted file mode 100644 index 29a321331..000000000 --- a/tests/it/main.rs +++ /dev/null @@ -1,64 +0,0 @@ -use crate::simulation::Simulation; -use crate::tun_device::{tun, tun_lock}; -use std::sync::mpsc::channel; -use std::sync::Arc; -use std::thread; -use std::time::Duration; - -mod network; -mod simulation; -mod tracer; -mod tun_device; - -/// this is for testing the net/packet modules with a real network stack for all OS -/// this is NOT for testing the tracing algorithm, for that ww can mock the Network -/// -/// we want to test all combinations of: -/// -/// OS: Linux, macOS, Windows, netBSD -/// Protocol: ICMP, UDP, TCP -/// Family: IPv4, IPv6 -/// Strategy: Classic, Paris, Dublin -/// -/// What else? -/// -/// - TCP re-issue probes -/// - Soak test -/// - Unprivileged mode -/// - Extensions -/// - Garbage replies (negative tests, i.e. wrong original datagram, back checksums, wrong identifier etc) -/// - source address selection (by interface, by IP) -/// - Capture real traffic to replay -/// -/// Out of scope: -/// - out of order responses -/// - backend tests (multi-round) -/// - Target found vs not-found -/// - Probe Timeouts - -#[test] -fn test1() -> anyhow::Result<()> { - run_test(simulation::make_sim()) -} - -#[test] -fn test2() -> anyhow::Result<()> { - run_test(simulation::make_sim()) -} - -fn run_test(simulation: Simulation) -> anyhow::Result<()> { - let _lock = tun_lock().lock(); - let tun = tun(); - let (tx, rx) = channel(); - let sim = Arc::new(simulation); - let handle = { - let sim = sim.clone(); - thread::spawn(move || network::Network::new().run(tun, sim, rx).unwrap()) - }; - tracer::Tracer::new(sim).trace()?; - // after the tracing round is done, allow the network time to process all in-flight packets - thread::sleep(Duration::from_millis(1000)); - tx.send(())?; - handle.join().expect("join"); - Ok(()) -} diff --git a/tests/resources/wintun.dll b/tests/resources/wintun.dll new file mode 100644 index 000000000..aee04e77b Binary files /dev/null and b/tests/resources/wintun.dll differ diff --git a/tests/sim/main.rs b/tests/sim/main.rs new file mode 100644 index 000000000..4094d0f24 --- /dev/null +++ b/tests/sim/main.rs @@ -0,0 +1,6 @@ +#![cfg(feature = "sim-tests")] +mod network; +mod simulation; +mod tests; +mod tracer; +mod tun_device; diff --git a/tests/it/network.rs b/tests/sim/network.rs similarity index 99% rename from tests/it/network.rs rename to tests/sim/network.rs index bff28fbdd..213c06f91 100644 --- a/tests/it/network.rs +++ b/tests/sim/network.rs @@ -62,12 +62,14 @@ impl Network { // sleep to allow tracer to send more packets before we respond // TODO variable delay per hop (per ip per hop for ECMP) and out-of-order reply thread::sleep(Duration::from_millis(100)); - println!("sending ttl {} reply from {}", ipv4.get_ttl(), reply_addr); let echo_request = EchoRequestPacket::new_view(ipv4.payload())?; if echo_request.get_identifier() != simulation.identifier { continue; } + + println!("sending ttl {} reply from {}", ipv4.get_ttl(), reply_addr); + // TODO validate ER checksum // TODO what about sequence, should we respond per sequence rather than per hop/host? diff --git a/tests/it/simulation.rs b/tests/sim/simulation.rs similarity index 100% rename from tests/it/simulation.rs rename to tests/sim/simulation.rs diff --git a/tests/sim/tests.rs b/tests/sim/tests.rs new file mode 100644 index 000000000..d5a58454e --- /dev/null +++ b/tests/sim/tests.rs @@ -0,0 +1,33 @@ +use crate::simulation::Simulation; +use crate::tun_device::{tun, tun_lock}; +use crate::{network, simulation, tracer}; +use std::sync::mpsc::channel; +use std::sync::Arc; +use std::thread; +use std::time::Duration; + +#[test] +fn test_simulation() -> anyhow::Result<()> { + run_test(simulation::make_sim()) +} + +fn run_test(simulation: Simulation) -> anyhow::Result<()> { + let _lock = tun_lock().lock(); + let tun = tun(); + let (_tx, rx) = channel(); + let sim = Arc::new(simulation); + let _handle = { + let sim = sim.clone(); + thread::spawn(move || network::Network::new().run(tun, sim, rx).unwrap()) + }; + // allow time for the routing table to reflect the tun device. + thread::sleep(Duration::from_millis(10000)); + + tracer::Tracer::new(sim).trace()?; + + // after the tracing round is done, allow the network time to process all in-flight packets + // thread::sleep(Duration::from_millis(1000)); + // tx.send(())?; + // handle.join().expect("join"); + Ok(()) +} diff --git a/tests/it/tracer.rs b/tests/sim/tracer.rs similarity index 100% rename from tests/it/tracer.rs rename to tests/sim/tracer.rs diff --git a/tests/it/tun_device.rs b/tests/sim/tun_device.rs similarity index 79% rename from tests/it/tun_device.rs rename to tests/sim/tun_device.rs index 09a03bf18..f0a25bdd7 100644 --- a/tests/it/tun_device.rs +++ b/tests/sim/tun_device.rs @@ -29,7 +29,7 @@ impl TunDevice { .netmask((255, 255, 255, 0)) .up(); let dev = tun::create(&config)?; - dev.set_nonblock()?; + // dev.set_nonblock()?; Self::create_route()?; Ok(Self { dev }) } @@ -48,6 +48,7 @@ impl TunDevice { } } +#[cfg(not(target_os = "windows"))] impl Read for TunDevice { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { let bytes_read = self.dev.read(buf)?; @@ -60,6 +61,7 @@ impl Read for TunDevice { } } +#[cfg(not(target_os = "windows"))] impl Write for TunDevice { fn write(&mut self, buf: &[u8]) -> std::io::Result { if self.dev.has_packet_information() { @@ -77,3 +79,22 @@ impl Write for TunDevice { self.dev.flush() } } + +#[cfg(target_os = "windows")] +impl Read for TunDevice { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + Ok(self.dev.read(buf)?) + } +} + +#[cfg(target_os = "windows")] +impl Write for TunDevice { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.dev.write_all(buf)?; + Ok(buf.len()) + } + + fn flush(&mut self) -> std::io::Result<()> { + self.dev.flush() + } +}