Skip to content

Commit

Permalink
Merge branch 'master' into refactor/tuic
Browse files Browse the repository at this point in the history
  • Loading branch information
iHsin authored Apr 9, 2024
2 parents a1529e8 + a53f14a commit cbec37e
Show file tree
Hide file tree
Showing 16 changed files with 197 additions and 26 deletions.
7 changes: 7 additions & 0 deletions clash/tests/data/config/wg_config/.donoteditthisfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
ORIG_SERVERURL="36.161.169.198"
ORIG_SERVERPORT="51820"
ORIG_PEERDNS="10.13.13.1"
ORIG_PEERS="1"
ORIG_INTERFACE="10.13.13"
ORIG_ALLOWEDIPS="0.0.0.0/0"
ORIG_PERSISTENTKEEPALIVE_PEERS=""
5 changes: 5 additions & 0 deletions clash/tests/data/config/wg_config/coredns/Corefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
. {
loop
health
forward . /etc/resolv.conf
}
11 changes: 11 additions & 0 deletions clash/tests/data/config/wg_config/peer1/peer1.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[Interface]
Address = 10.13.13.2
PrivateKey = KIlDUePHyYwzjgn18przw/ZwPioJhh2aEyhxb/dtCXI=
ListenPort = 51820
DNS = 10.13.13.1

[Peer]
PublicKey = INBZyvB715sA5zatkiX8Jn3Dh5tZZboZ09x4pkr66ig=
PresharedKey = +JmZErvtDT4ZfQequxWhZSydBV+ItqUcPMHUWY1j2yc=
Endpoint = 127.0.0.1:10002
AllowedIPs = 0.0.0.0/0
Binary file added clash/tests/data/config/wg_config/peer1/peer1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions clash/tests/data/config/wg_config/peer1/presharedkey-peer1
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
+JmZErvtDT4ZfQequxWhZSydBV+ItqUcPMHUWY1j2yc=
1 change: 1 addition & 0 deletions clash/tests/data/config/wg_config/peer1/privatekey-peer1
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
KIlDUePHyYwzjgn18przw/ZwPioJhh2aEyhxb/dtCXI=
1 change: 1 addition & 0 deletions clash/tests/data/config/wg_config/peer1/publickey-peer1
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
H7NHC22d44AhrJf7BSzbNJrW1wiTDCRYNfP0rQicM3g=
1 change: 1 addition & 0 deletions clash/tests/data/config/wg_config/server/privatekey-server
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CA7cMGAh7BF/kD000ZRN+ZXDe1SGd1Z3kqNjQxnCAmQ=
1 change: 1 addition & 0 deletions clash/tests/data/config/wg_config/server/publickey-server
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
INBZyvB715sA5zatkiX8Jn3Dh5tZZboZ09x4pkr66ig=
11 changes: 11 additions & 0 deletions clash/tests/data/config/wg_config/templates/peer.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[Interface]
Address = ${CLIENT_IP}
PrivateKey = $(cat /config/${PEER_ID}/privatekey-${PEER_ID})
ListenPort = 10002
DNS = ${PEERDNS}

[Peer]
PublicKey = $(cat /config/server/publickey-server)
PresharedKey = $(cat /config/${PEER_ID}/presharedkey-${PEER_ID})
Endpoint = ${SERVERURL}:${SERVERPORT}
AllowedIPs = ${ALLOWEDIPS}
6 changes: 6 additions & 0 deletions clash/tests/data/config/wg_config/templates/server.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[Interface]
Address = ${INTERFACE}.1
ListenPort = 10002
PrivateKey = $(cat /config/server/privatekey-server)
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth+ -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth+ -j MASQUERADE
13 changes: 13 additions & 0 deletions clash/tests/data/config/wg_config/wg_confs/wg0.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[Interface]
Address = 10.13.13.1
ListenPort = 10002
PrivateKey = CA7cMGAh7BF/kD000ZRN+ZXDe1SGd1Z3kqNjQxnCAmQ=
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth+ -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth+ -j MASQUERADE

[Peer]
# peer1
PublicKey = H7NHC22d44AhrJf7BSzbNJrW1wiTDCRYNfP0rQicM3g=
PresharedKey = +JmZErvtDT4ZfQequxWhZSydBV+ItqUcPMHUWY1j2yc=
AllowedIPs = 10.13.13.2/32

1 change: 1 addition & 0 deletions clash_lib/src/proxy/utils/test_utils/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pub const LOCAL_ADDR: &str = "127.0.0.1";
pub const EXAMPLE_REQ: &[u8] = b"GET / HTTP/1.1\r\nHost: example.com\r\nAccept: */*\r\n\r\n";
pub const EXAMLE_RESP_200: &[u8] = b"HTTP/1.1 200";

pub const IMAGE_WG: &str = "lscr.io/linuxserver/wireguard:latest";
pub const IMAGE_SS_RUST: &str = "ghcr.io/shadowsocks/ssserver-rust:latest";
pub const IMAGE_SHADOW_TLS: &str = "ghcr.io/ihciah/shadow-tls:latest";
pub const IMAGE_TROJAN_GO: &str = "p4gefau1t/trojan-go:latest";
Expand Down
27 changes: 24 additions & 3 deletions clash_lib/src/proxy/utils/test_utils/docker_runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,9 +188,9 @@ impl DockerTestRunnerBuilder {
pub fn port(mut self, port: u16) -> Self {
self._server_port = port;
self.exposed_ports = vec![format!("{}/tcp", port), format!("{}/udp", port)];
let mounts = self.host_config.mounts.take();
self.host_config = get_host_config(port);
self.host_config.mounts = mounts;
let new_host_config = get_host_config(port);
self.host_config.network_mode = new_host_config.network_mode;
self.host_config.port_bindings = new_host_config.port_bindings;

self
}
Expand Down Expand Up @@ -227,6 +227,27 @@ impl DockerTestRunnerBuilder {
self
}

pub fn sysctls(mut self, sysctls: &[(&str, &str)]) -> Self {
self.host_config.sysctls = Some(
sysctls
.into_iter()
.map(|(k, v)| (k.to_string(), v.to_string()))
.collect::<HashMap<_, _>>(),
);

self
}

pub fn cap_add(mut self, caps: &[&str]) -> Self {
self.host_config.cap_add = Some(caps.iter().map(|x| x.to_string()).collect());
self
}

pub fn net_mode(mut self, mode: &str) -> Self {
self.host_config.network_mode = Some(mode.to_string());
self
}

pub async fn build(self) -> anyhow::Result<DockerTestRunner> {
tracing::trace!("building docker test runner: {:?}", &self);
let exposed = self
Expand Down
70 changes: 47 additions & 23 deletions clash_lib/src/proxy/utils/test_utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,37 +176,61 @@ pub async fn latency_test(
Ok(end_time.duration_since(start_time))
}

pub async fn run_default_test_suites_and_cleanup(
#[derive(Clone, Copy)]
pub enum Suite {
PingPong,
Latency,
}

pub const DEFAULT_TEST_SUITES: &[Suite] = &[Suite::PingPong, Suite::Latency];

pub async fn run_test_suites_and_cleanup(
handler: Arc<dyn OutboundHandler>,
docker_test_runner: impl RunAndCleanup,
suites: &[Suite],
) -> anyhow::Result<()> {
let suites = suites.to_owned();
docker_test_runner
.run_and_cleanup(async move {
let rv = ping_pong_test(handler.clone(), 10001).await;
if rv.is_err() {
tracing::error!("ping_pong_test failed: {:?}", rv);
return rv;
} else {
tracing::info!("ping_pong_test success");
}

let rv = latency_test(
handler,
LatencyTestOption {
dst: SocksAddr::Domain("example.com".to_owned(), 80),
req: consts::EXAMPLE_REQ,
expected_resp: consts::EXAMLE_RESP_200,
read_exact: true,
},
)
.await;
if let Err(e) = rv {
return Err(e);
} else {
tracing::info!("latency test success: {}", rv.unwrap().as_millis());
for suite in suites {
match suite {
Suite::PingPong => {
let rv = ping_pong_test(handler.clone(), 10001).await;
if rv.is_err() {
tracing::error!("ping_pong_test failed: {:?}", rv);
return rv;
} else {
tracing::info!("ping_pong_test success");
}
}
Suite::Latency => {
let rv = latency_test(
handler.clone(),
LatencyTestOption {
dst: SocksAddr::Domain("example.com".to_owned(), 80),
req: consts::EXAMPLE_REQ,
expected_resp: consts::EXAMLE_RESP_200,
read_exact: true,
},
)
.await;
if rv.is_err() {
return Err(rv.unwrap_err());
} else {
tracing::info!("latency test success: {}", rv.unwrap().as_millis());
}
}
}
}

Ok(())
})
.await
}

pub async fn run_default_test_suites_and_cleanup(
handler: Arc<dyn OutboundHandler>,
docker_test_runner: impl RunAndCleanup,
) -> anyhow::Result<()> {
run_test_suites_and_cleanup(handler, docker_test_runner, DEFAULT_TEST_SUITES).await
}
67 changes: 67 additions & 0 deletions clash_lib/src/proxy/wg/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -290,3 +290,70 @@ impl OutboundHandler for Handler {
Ok(Box::new(chained))
}
}

#[cfg(all(test, not(ci)))]
mod tests {

use crate::proxy::utils::test_utils::docker_runner::DockerTestRunnerBuilder;
use crate::proxy::utils::test_utils::{config_helper::test_config_base_dir, Suite};

use super::super::utils::test_utils::{consts::*, docker_runner::DockerTestRunner};
use crate::proxy::utils::test_utils::run_test_suites_and_cleanup;

use super::*;

// see: https://github.com/linuxserver/docker-wireguard?tab=readme-ov-file#usage
// we shouldn't run the wireguard server with host mode, or
// the sysctl of `net.ipv4.conf.all.src_valid_mark` will fail
async fn get_runner() -> anyhow::Result<DockerTestRunner> {
let test_config_dir = test_config_base_dir();
let wg_config = test_config_dir.join("wg_config");
// the following configs is in accordance with the config in `wg_config` dir
DockerTestRunnerBuilder::new()
.image(IMAGE_WG)
.env(&[
"PUID=1000",
"PGID=1000",
"TZ=Etc/UTC",
"SERVERPORT=10002",
"PEERS=1",
"PEERDNS=auto",
"INTERNAL_SUBNET=10.13.13.0",
"ALLOWEDIPS=0.0.0.0/0",
])
.mounts(&[(wg_config.to_str().unwrap(), "/config")])
.sysctls(&[("net.ipv4.conf.all.src_valid_mark", "1")])
.cap_add(&["NET_ADMIN"])
.net_mode("bridge") // the default network mode for testing is `host`
.build()
.await
}

#[tokio::test]
#[serial_test::serial]
async fn test_wg() -> anyhow::Result<()> {
let opts = HandlerOpts {
name: "wg".to_owned(),
common_opts: CommonOption::default(),
server: "127.0.0.1".to_owned(),
port: 10002,
ip: Ipv4Addr::new(10, 13, 13, 2),
ipv6: None,
private_key: "KIlDUePHyYwzjgn18przw/ZwPioJhh2aEyhxb/dtCXI=".to_owned(),
public_key: "INBZyvB715sA5zatkiX8Jn3Dh5tZZboZ09x4pkr66ig=".to_owned(),
preshared_key: Some("+JmZErvtDT4ZfQequxWhZSydBV+ItqUcPMHUWY1j2yc=".to_owned()),
remote_dns_resolve: false,
dns: None,
mtu: Some(1000),
udp: true,
allowed_ips: Some(vec!["0.0.0.0/0".to_owned()]),
reserved_bits: None,
};
let handler = Handler::new(opts);

// cannot run the ping pong test, since the wireguard server is running on bridge network mode
// and the `net.ipv4.conf.all.src_valid_mark` is not supported in the host network mode
// the latency test should be enough
run_test_suites_and_cleanup(handler, get_runner().await?, &[Suite::Latency]).await
}
}

0 comments on commit cbec37e

Please sign in to comment.