#![allow(dead_code)]
use std::{net::IpAddr, process::Command, thread, time::Duration};
use crate::interfaces;
#[cfg(bsd_like)]
mod bsd;
#[cfg(linux_like)]
mod linux;
struct TestInterface {
name: String,
local: IpAddr,
remote: IpAddr,
setup_cmds: Vec<Command>,
teardown_cmds: Vec<Command>,
}
impl TestInterface {
fn new(local: IpAddr, remote: IpAddr) -> Self {
Self {
name: String::new(),
local,
remote,
setup_cmds: Vec::new(),
teardown_cmds: Vec::new(),
}
}
fn setup(&mut self) -> std::io::Result<()> {
for cmd in &mut self.setup_cmds {
let status = cmd.status()?;
if !status.success() {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!("command failed: {cmd:?}"),
));
}
match cmd.output() {
Ok(output) => {
if !output.status.success() {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!("command failed: {cmd:?}"),
));
}
}
Err(e) => {
return Err(std::io::Error::new(
e.kind(),
format!("args={:?} err={e}", cmd.get_args()),
));
}
}
}
Ok(())
}
fn teardown(&mut self) -> std::io::Result<()> {
for cmd in &mut self.teardown_cmds {
let status = cmd.status()?;
if !status.success() {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!("command failed: {cmd:?}"),
));
}
match cmd.output() {
Ok(output) => {
if !output.status.success() {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!("command failed: {cmd:?}"),
));
}
}
Err(e) => {
return Err(std::io::Error::new(
e.kind(),
format!("args={:?} err={e}", cmd.get_args()),
));
}
}
}
Ok(())
}
}
#[test]
#[cfg(all(not(apple), unix,))]
fn point_to_point_interface() {
#[cfg(bsd_like)]
let uid = unsafe { libc::getuid() };
#[cfg(linux_like)]
let uid = rustix::process::getuid().as_raw();
if uid != 0 {
return;
}
let local: IpAddr = "169.254.0.1".parse().unwrap();
let remote: IpAddr = "169.254.0.254".parse().unwrap();
for i in 0..3 {
let mut ti = TestInterface::new(local, remote);
if let Err(e) = ti.set_point_to_point(5963 + i) {
panic!("test requires external command: {}", e);
}
match ti.setup() {
Ok(_) => {
std::thread::sleep(Duration::from_millis(3));
}
Err(e) => {
let err_msg = e.to_string();
if err_msg.contains("No such device") && err_msg.contains("gre0") {
println!("skipping test; no gre0 device. likely running in container?");
return; }
panic!("{}", e);
}
}
match interfaces() {
Ok(interfaces) => {
for ifi in interfaces {
if ti.name != ifi.name {
continue;
}
let ifat = ifi.addrs().unwrap();
for ifa in &ifat {
if ifa.addr() == remote {
ti.teardown().unwrap();
panic!("got {ifa:?}");
}
}
}
}
Err(e) => {
ti.teardown().unwrap();
panic!("{}", e);
}
}
ti.teardown().unwrap();
std::thread::sleep(Duration::from_millis(3));
}
}
#[cfg(unix)]
#[test]
fn test_interface_arrival_and_departure() {
if std::env::var("RUST_TEST_SHORT").is_ok() {
return;
}
#[cfg(bsd_like)]
let uid = unsafe { libc::getuid() };
#[cfg(linux_like)]
let uid = rustix::process::getuid().as_raw();
if uid != 0 {
return;
}
let local: IpAddr = "169.254.0.1".parse().unwrap();
let remote: IpAddr = "169.254.0.254".parse().unwrap();
let ip = remote;
for vid in [1002, 1003, 1004, 1005].iter() {
let ift1 = interfaces().unwrap();
let mut ti = TestInterface::new(local, remote);
if let Err(e) = ti.set_broadcast(*vid) {
println!("test requires external command: {e}");
return;
}
ti.setup().unwrap();
thread::sleep(Duration::from_millis(3));
let ift2 = match interfaces() {
Ok(interfaces) => interfaces,
Err(e) => {
ti.teardown().unwrap();
panic!("{}", e);
}
};
if ift2.len() <= ift1.len() {
for ifi in &ift1 {
println!("before: {ifi:?}");
}
for ifi in &ift2 {
println!("after: {ifi:?}");
}
ti.teardown().unwrap();
panic!("got {}; want gt {}", ift2.len(), ift1.len());
}
for ifi in ift2.iter() {
if ti.name != ifi.name {
continue;
}
let addrs = ifi.addrs().unwrap();
for addr in addrs {
if let IpAddr::V4(addr_ip) = addr.addr() {
if ip == IpAddr::V4(addr_ip) {
ti.teardown().unwrap();
panic!("got {addr:?}");
}
}
}
}
ti.teardown().unwrap();
thread::sleep(Duration::from_millis(3));
let ift3 = interfaces().unwrap();
if ift3.len() >= ift2.len() {
for ifi in &ift2 {
println!("before: {ifi:?}");
}
for ifi in &ift3 {
println!("after: {ifi:?}");
}
panic!("got {}; want lt {}", ift3.len(), ift2.len());
}
}
}