Simplify integration test

This commit is contained in:
Prabhpreet Dua
2024-04-15 22:10:40 +05:30
parent a3e91a95df
commit b56af8b696
7 changed files with 372 additions and 225 deletions

View File

@@ -1,5 +1,8 @@
use std::{fs, net::UdpSocket, path::PathBuf, process::Stdio, time::Duration};
use clap::Parser;
use rosenpass::{app_server::AppServerTestFlags, cli::CliArgs};
const BIN: &str = "rosenpass";
// check that we can generate keys
@@ -124,22 +127,12 @@ fn check_exchange_under_normal() {
}
// check that we can trigger a DoS condition and we can exchange keys under DoS
// This test creates a responder (server) with the feature flag "integration_test_dos_exchange". The feature flag posts a semaphore
// (linux) to indicate that the server is under load condition. It also modifies the responders behaviour to be permanently under DoS condition
// once triggered, and makes the DoS mechanism more sensitive to be easily triggered.
// The test also creates a thread to send UDP packets to the server to trigger the DoS condition. The test waits for the server to
// be under load condition and then stops the DoS attack. The test then starts the client (initiator) to exchange keys. The test checks that the keys are exchanged successfully under load condition.
// This test creates a responder (server) with the feature flag "integration_test_always_under_load" to always be under load condition for the test.
#[test]
fn check_exchange_under_dos() {
//Generate binary with responder with feature integration_test
let server_test_bin = test_binary::TestBinary::relative_to_parent(
"rp-it-dos",
&PathBuf::from_iter(["Cargo.toml"]),
)
.with_feature("integration_test_dos_exchange")
.build()
.unwrap();
procspawn::init();
//Generate binary with responder with feature integration_test
let tmpdir = PathBuf::from(env!("CARGO_TARGET_TMPDIR")).join("exchange-dos");
fs::create_dir_all(&tmpdir).unwrap();
@@ -162,90 +155,46 @@ fn check_exchange_under_dos() {
assert!(pub_key_path.is_file());
}
//Create a semaphore. The server will unblock this semaphore after it is under load condition.
//There are parameters setup under app_server to remain in load condition once triggered for this test feature.
let sem_name = b"/rp_integration_test_under_dos\0";
let sem = unsafe { libc::sem_open(sem_name.as_ptr() as *const i8, libc::O_CREAT, 0o644, 1) };
unsafe {
libc::sem_wait(sem);
}
// start first process, the server
let port = loop {
if let Some(port) = find_udp_socket() {
break port;
}
};
let listen_addr = format!("localhost:{port}");
let mut server = std::process::Command::new(server_test_bin)
.args(["--log-level", "debug"])
.args(["exchange", "secret-key"])
let mut server_cmd = std::process::Command::new(BIN);
server_cmd.args(["exchange", "secret-key"])
.arg(&secret_key_paths[0])
.arg("public-key")
.arg(&public_key_paths[0])
.args(["listen", &listen_addr, "verbose", "peer", "public-key"])
.arg(&public_key_paths[1])
.arg("outfile")
.arg(&shared_key_paths[0])
//.stdout(Stdio::null())
//.stderr(Stdio::null())
.spawn()
.expect("Test setup failed- Failed to start {server_bin}");
.arg(&shared_key_paths[0]);
//Create a UDP socket for DOS sender
let socket = UdpSocket::bind("127.0.0.1:0").expect("couldn't bind to address");
let server_addr = listen_addr.clone();
//Create thread safe atomic bool to stop the DoS attack
let stop_dos = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
let stop_dos_handle = stop_dos.clone();
let server_cmd: Vec<String> = server_cmd
.get_args()
.into_iter()
.fold(vec![BIN.to_string()], |mut acc, x| {
if let Some(s) = x.to_str() {
acc.push(s.to_string());
}
acc
});
//Spawn a thread to send DoS packets
let dos_attack = std::thread::spawn(move || {
while stop_dos.load(std::sync::atomic::Ordering::Relaxed) == false {
let buf = [0; 10];
socket
.send_to(&buf, &server_addr)
.expect("couldn't send data");
std::thread::sleep(Duration::from_micros(10));
}
let mut server = procspawn::spawn(server_cmd, |server_cmd: Vec<String>| {
let cli = CliArgs::try_parse_from(server_cmd.iter()).unwrap();
cli.command.run(AppServerTestFlags {
enable_dos_permanently: true,
}).unwrap();
});
//Wait till we are under load condition for upto 5 seconds
let mut ts = libc::timespec {
tv_sec: 0,
tv_nsec: 0,
};
let now = std::time::SystemTime::now();
let timeout_absolute = now + Duration::from_secs(5);
if let Ok(duration) = timeout_absolute.duration_since(std::time::SystemTime::UNIX_EPOCH) {
ts.tv_sec = duration.as_secs() as libc::time_t;
ts.tv_nsec = duration.subsec_nanos() as _;
} else {
panic!("Test setup failed- Failed to calculate timeout for semaphore");
}
let mut failed_wait = false;
if (unsafe { libc::sem_timedwait(sem, &ts) } == -1) {
failed_wait = true;
}
// Close and unlink the semaphore
if unsafe { libc::sem_close(sem) } == -1 {
panic!("Test setup failed- Failed to close semaphore");
}
if unsafe { libc::sem_unlink(sem_name.as_ptr() as *const i8) } == -1 {
panic!("Test setup failed- Failed to unlink semaphore");
}
if failed_wait {
panic!("Failed to wait for semaphore- load condition not reached");
}
//Stop DoS attack
stop_dos_handle.store(true, std::sync::atomic::Ordering::Relaxed);
// start second process, the client
let mut client = test_bin::get_test_bin(BIN)
.args(["--log-level", "debug"])
.args(["exchange", "secret-key"])
.arg(&secret_key_paths[1])
.arg("public-key")
@@ -255,8 +204,6 @@ fn check_exchange_under_dos() {
.args(["endpoint", &listen_addr])
.arg("outfile")
.arg(&shared_key_paths[1])
//.stdout(Stdio::null())
//.stderr(Stdio::null())
.spawn()
.expect("Failed to start {BIN}");
@@ -266,7 +213,6 @@ fn check_exchange_under_dos() {
// time's up, kill the childs
server.kill().unwrap();
client.kill().unwrap();
dos_attack.join().unwrap();
// read the shared keys they created
let shared_keys: Vec<_> = shared_key_paths