#[cfg(target_os = "linux")] mod three_node; mod ipv6_test; #[cfg(target_os = "linux")] mod credential_tests; use std::io::IsTerminal as _; use crate::common::PeerId; use crate::peers::peer_manager::PeerManager; pub fn get_guest_veth_name(net_ns: &str) -> &str { Box::leak(format!("veth_{}_g", net_ns).into_boxed_str()) } pub fn get_host_veth_name(net_ns: &str) -> &str { Box::leak(format!("veth_{}_h", net_ns).into_boxed_str()) } pub fn del_netns(name: &str) { // del veth host let _ = std::process::Command::new("ip") .args(["link", "del", get_host_veth_name(name)]) .output(); let _ = std::process::Command::new("ip") .args(["netns", "del", name]) .output(); } pub fn create_netns(name: &str, ipv4: &str, ipv6: &str) { // create netns let _ = std::process::Command::new("ip") .args(["netns", "add", name]) .output() .unwrap(); // set lo up let _ = std::process::Command::new("ip") .args(["netns", "exec", name, "ip", "link", "set", "lo", "up"]) .output() .unwrap(); let _ = std::process::Command::new("ip") .args([ "link", "add", get_host_veth_name(name), "type", "veth", "peer", "name", get_guest_veth_name(name), ]) .output() .unwrap(); let _ = std::process::Command::new("ip") .args(["link", "set", get_guest_veth_name(name), "netns", name]) .output() .unwrap(); let _ = std::process::Command::new("ip") .args([ "netns", "exec", name, "ip", "link", "set", get_guest_veth_name(name), "up", ]) .output() .unwrap(); let _ = std::process::Command::new("ip") .args(["link", "set", get_host_veth_name(name), "up"]) .output() .unwrap(); for ip in [ipv4, ipv6] { let _ = std::process::Command::new("ip") .args([ "netns", "exec", name, "ip", "addr", "add", ip, "dev", get_guest_veth_name(name), ]) .output() .unwrap(); } } pub fn prepare_bridge(name: &str) { // del bridge with brctl let _ = std::process::Command::new("brctl") .args(["delbr", name]) .output(); // create new br let _ = std::process::Command::new("brctl") .args(["addbr", name]) .output(); } pub fn add_ns_to_bridge(br_name: &str, ns_name: &str) { // use brctl to add ns to bridge let _ = std::process::Command::new("brctl") .args(["addif", br_name, get_host_veth_name(ns_name)]) .output() .unwrap(); // set bridge up let _ = std::process::Command::new("ip") .args(["link", "set", br_name, "up"]) .output() .unwrap(); } pub fn enable_log() { let filter = tracing_subscriber::EnvFilter::builder() .with_default_directive(tracing::level_filters::LevelFilter::TRACE.into()) .from_env() .unwrap() .add_directive("tarpc=error".parse().unwrap()); let use_ansi = std::io::stderr().is_terminal(); tracing_subscriber::fmt::fmt() .pretty() .with_ansi(use_ansi) .with_env_filter(filter) .with_writer(std::io::stderr) .init(); } fn check_route(ipv4: &str, dst_peer_id: PeerId, routes: Vec) { let mut found = false; for r in routes.iter() { if r.ipv4_addr == Some(ipv4.parse().unwrap()) { found = true; assert_eq!(r.peer_id, dst_peer_id, "{:?}", routes); } } assert!( found, "routes: {:?}, dst_peer_id: {}, ipv4: {}", routes, dst_peer_id, ipv4 ); } fn check_route_ex( routes: Vec, peer_id: PeerId, checker: impl Fn(&crate::proto::api::instance::Route) -> bool, ) { let mut found = false; for r in routes.iter() { if r.peer_id == peer_id { found = true; assert!(checker(r), "{:?}", routes); } } assert!(found, "routes: {:?}, dst_peer_id: {}", routes, peer_id); } async fn wait_proxy_route_appear( mgr: &std::sync::Arc, ipv4: &str, dst_peer_id: PeerId, proxy_cidr: &str, ) { let now = std::time::Instant::now(); loop { for r in mgr.list_routes().await.iter() { if r.proxy_cidrs.contains(&proxy_cidr.to_owned()) { assert_eq!(r.peer_id, dst_peer_id); assert_eq!(r.ipv4_addr, Some(ipv4.parse().unwrap())); return; } } if now.elapsed().as_secs() > 5 { panic!("wait proxy route appear timeout"); } tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; } } fn set_link_status(net_ns: &str, up: bool) { let ret = std::process::Command::new("ip") .args([ "netns", "exec", net_ns, "ip", "link", "set", get_guest_veth_name(net_ns), if up { "up" } else { "down" }, ]) .output() .unwrap(); tracing::info!("set link status: {:?}, net_ns: {}, up: {}", ret, net_ns, up); } pub async fn drop_insts(insts: Vec) { let mut set = tokio::task::JoinSet::new(); for mut inst in insts { set.spawn(async move { inst.clear_resources().await; let pm = std::sync::Arc::downgrade(&inst.get_peer_manager()); drop(inst); let now = std::time::Instant::now(); while now.elapsed().as_secs() < 5 && pm.strong_count() > 0 { tokio::time::sleep(std::time::Duration::from_millis(50)).await; } assert_eq!(pm.strong_count(), 0, "PeerManager should be dropped"); }); } while set.join_next().await.is_some() {} } pub async fn ping_test(from_netns: &str, target_ip: &str, payload_size: Option) -> bool { use crate::common::netns::{NetNS, ROOT_NETNS_NAME}; let _g = NetNS::new(Some(ROOT_NETNS_NAME.to_owned())).guard(); let code = tokio::process::Command::new("ip") .args([ "netns", "exec", from_netns, "ping", "-c", "1", "-s", payload_size.unwrap_or(56).to_string().as_str(), "-W", "1", target_ip.to_string().as_str(), ]) .stdout(std::process::Stdio::null()) .stderr(std::process::Stdio::null()) .status() .await .unwrap(); code.code().unwrap() == 0 }