zero copy tunnel (#55)

make tunnel zero copy, for better performance. remove most of the locks in io path.
introduce quic tunnel
prepare for encryption
This commit is contained in:
Sijie.Sun
2024-04-24 23:12:46 +08:00
committed by GitHub
parent 39021d7b1b
commit 3467890270
44 changed files with 6504 additions and 688 deletions

View File

@@ -116,7 +116,7 @@ pub fn add_ns_to_bridge(br_name: &str, ns_name: &str) {
pub fn enable_log() {
let filter = tracing_subscriber::EnvFilter::builder()
.with_default_directive(tracing::level_filters::LevelFilter::INFO.into())
.with_default_directive(tracing::level_filters::LevelFilter::TRACE.into())
.from_env()
.unwrap()
.add_directive("tarpc=error".parse().unwrap());

View File

@@ -9,18 +9,18 @@ use super::*;
use crate::{
common::{
config::{ConfigLoader, NetworkIdentity, TomlConfigLoader},
config::{ConfigLoader, NetworkIdentity, TomlConfigLoader, VpnPortalConfig},
netns::{NetNS, ROOT_NETNS_NAME},
},
instance::instance::Instance,
peers::tests::wait_for_condition,
tunnels::{
common::tests::_tunnel_pingpong_netns,
ring_tunnel::RingTunnelConnector,
tcp_tunnel::{TcpTunnelConnector, TcpTunnelListener},
udp_tunnel::{UdpTunnelConnector, UdpTunnelListener},
tunnel::{
ring::RingTunnelConnector,
tcp::TcpTunnelConnector,
udp::UdpTunnelConnector,
wireguard::{WgConfig, WgTunnelConnector},
},
vpn_portal::wireguard::get_wg_config_for_portal,
};
pub fn prepare_linux_namespaces() {
@@ -113,6 +113,26 @@ pub async fn init_three_node(proto: &str) -> Vec<Instance> {
vec![inst1, inst2, inst3]
}
async fn ping_test(from_netns: &str, target_ip: &str) -> bool {
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",
"-W",
"1",
target_ip.to_string().as_str(),
])
.status()
.await
.unwrap();
code.code().unwrap() == 0
}
#[rstest::rstest]
#[tokio::test]
#[serial_test::serial]
@@ -130,12 +150,20 @@ pub async fn basic_three_node_test(#[values("tcp", "udp", "wg")] proto: &str) {
insts[2].peer_id(),
insts[0].get_peer_manager().list_routes().await,
);
wait_for_condition(
|| async { ping_test("net_c", "10.144.144.1").await },
Duration::from_secs(5),
)
.await;
}
#[rstest::rstest]
#[tokio::test]
#[serial_test::serial]
pub async fn tcp_proxy_three_node_test(#[values("tcp", "udp", "wg")] proto: &str) {
use crate::tunnel::{common::tests::_tunnel_pingpong_netns, tcp::TcpTunnelListener};
let insts = init_three_node(proto).await;
insts[2]
@@ -187,25 +215,19 @@ pub async fn icmp_proxy_three_node_test(#[values("tcp", "udp", "wg")] proto: &st
)
.await;
// wait updater
tokio::time::sleep(tokio::time::Duration::from_secs(6)).await;
// send ping with shell in net_a to net_d
let _g = NetNS::new(Some(ROOT_NETNS_NAME.to_owned())).guard();
let code = tokio::process::Command::new("ip")
.args(&[
"netns", "exec", "net_a", "ping", "-c", "1", "-W", "1", "10.1.2.4",
])
.status()
.await
.unwrap();
assert_eq!(code.code().unwrap(), 0);
wait_for_condition(
|| async { ping_test("net_a", "10.1.2.4").await },
Duration::from_secs(5),
)
.await;
}
#[rstest::rstest]
#[tokio::test]
#[serial_test::serial]
pub async fn proxy_three_node_disconnect_test(#[values("tcp", "wg")] proto: &str) {
use crate::tunnel::wireguard::{WgConfig, WgTunnelConnector};
let insts = init_three_node(proto).await;
let mut inst4 = Instance::new(get_inst_config("inst4", Some("net_d"), "10.144.144.4"));
if proto == "tcp" {
@@ -266,6 +288,8 @@ pub async fn proxy_three_node_disconnect_test(#[values("tcp", "wg")] proto: &str
#[tokio::test]
#[serial_test::serial]
pub async fn udp_proxy_three_node_test(#[values("tcp", "udp", "wg")] proto: &str) {
use crate::tunnel::{common::tests::_tunnel_pingpong_netns, udp::UdpTunnelListener};
let insts = init_three_node(proto).await;
insts[2]
@@ -389,21 +413,108 @@ pub async fn foreign_network_forward_nic_data() {
)
.await;
let _g = NetNS::new(Some(ROOT_NETNS_NAME.to_owned())).guard();
let code = tokio::process::Command::new("ip")
.args(&[
"netns",
"exec",
"net_b",
"ping",
"-c",
"1",
"-W",
"1",
"10.144.145.2",
])
.status()
.await
.unwrap();
assert_eq!(code.code().unwrap(), 0);
wait_for_condition(
|| async { ping_test("net_b", "10.144.145.2").await },
Duration::from_secs(5),
)
.await;
}
use std::{net::SocketAddr, str::FromStr};
use defguard_wireguard_rs::{
host::Peer, key::Key, net::IpAddrMask, InterfaceConfiguration, WGApi, WireguardInterfaceApi,
};
fn run_wireguard_client(
endpoint: SocketAddr,
peer_public_key: Key,
client_private_key: Key,
allowed_ips: Vec<String>,
client_ip: String,
) -> Result<(), Box<dyn std::error::Error>> {
// Create new API object for interface
let ifname: String = if cfg!(target_os = "linux") || cfg!(target_os = "freebsd") {
"wg0".into()
} else {
"utun3".into()
};
let wgapi = WGApi::new(ifname.clone(), false)?;
// create interface
wgapi.create_interface()?;
// Peer secret key
let mut peer = Peer::new(peer_public_key.clone());
log::info!("endpoint");
// Peer endpoint and interval
peer.endpoint = Some(endpoint);
peer.persistent_keepalive_interval = Some(25);
for ip in allowed_ips {
peer.allowed_ips.push(IpAddrMask::from_str(ip.as_str())?);
}
// interface configuration
let interface_config = InterfaceConfiguration {
name: ifname.clone(),
prvkey: client_private_key.to_string(),
address: client_ip,
port: 12345,
peers: vec![peer],
};
#[cfg(not(windows))]
wgapi.configure_interface(&interface_config)?;
#[cfg(windows)]
wgapi.configure_interface(&interface_config, &[])?;
wgapi.configure_peer_routing(&interface_config.peers)?;
Ok(())
}
#[tokio::test]
#[serial_test::serial]
pub async fn wireguard_vpn_portal() {
let mut insts = init_three_node("tcp").await;
let net_ns = NetNS::new(Some("net_d".into()));
let _g = net_ns.guard();
insts[2]
.get_global_ctx()
.config
.set_vpn_portal_config(VpnPortalConfig {
wireguard_listen: "0.0.0.0:22121".parse().unwrap(),
client_cidr: "10.14.14.0/24".parse().unwrap(),
});
insts[2].run_vpn_portal().await.unwrap();
let net_ns = NetNS::new(Some("net_d".into()));
let _g = net_ns.guard();
let wg_cfg = get_wg_config_for_portal(&insts[2].get_global_ctx().get_network_identity());
run_wireguard_client(
"10.1.2.3:22121".parse().unwrap(),
Key::try_from(wg_cfg.my_public_key()).unwrap(),
Key::try_from(wg_cfg.peer_secret_key()).unwrap(),
vec!["10.14.14.0/24".to_string(), "10.144.144.0/24".to_string()],
"10.14.14.2".to_string(),
)
.unwrap();
// ping other node in network
wait_for_condition(
|| async { ping_test("net_d", "10.144.144.1").await },
Duration::from_secs(5),
)
.await;
wait_for_condition(
|| async { ping_test("net_d", "10.144.144.2").await },
Duration::from_secs(5),
)
.await;
// ping portal node
wait_for_condition(
|| async { ping_test("net_d", "10.144.144.3").await },
Duration::from_secs(500),
)
.await;
}