From 873851e6d07659f60a9a980c5740f9bbd073a989 Mon Sep 17 00:00:00 2001 From: "sijie.sun" Date: Fri, 3 May 2024 07:55:00 +0800 Subject: [PATCH] mips --- .cargo/config | 16 ++ .github/workflows/rust.yml | 48 +++-- Cargo.lock | 193 ++++++------------- easytier/Cargo.toml | 35 ++-- easytier/src/common/mod.rs | 1 - easytier/src/common/rkyv_util.rs | 72 -------- easytier/src/connector/mod.rs | 15 +- easytier/src/easytier-core.rs | 2 + easytier/src/instance/instance.rs | 3 + easytier/src/instance/listeners.rs | 18 +- easytier/src/peers/encrypt/aes_gcm.rs | 146 +++++++++++++++ easytier/src/peers/encrypt/mod.rs | 4 + easytier/src/peers/mod.rs | 1 - easytier/src/peers/packet.rs | 254 -------------------------- easytier/src/peers/peer_conn.rs | 2 +- easytier/src/peers/peer_manager.rs | 38 +++- easytier/src/peers/peer_ospf_route.rs | 5 +- easytier/src/tests/three_node.rs | 18 +- easytier/src/tunnel/mod.rs | 6 +- easytier/src/vpn_portal/mod.rs | 26 +++ 20 files changed, 373 insertions(+), 530 deletions(-) delete mode 100644 easytier/src/common/rkyv_util.rs create mode 100644 easytier/src/peers/encrypt/aes_gcm.rs delete mode 100644 easytier/src/peers/packet.rs diff --git a/.cargo/config b/.cargo/config index 170d627..6224081 100644 --- a/.cargo/config +++ b/.cargo/config @@ -5,3 +5,19 @@ linker = "aarch64-linux-musl-gcc" rustflags = ["-C", "target-feature=+crt-static"] [target.'cfg(all(windows, target_env = "msvc"))'] rustflags = ["-C", "target-feature=+crt-static"] + +[target.mipsel-unknown-linux-musl] +linker = "mipsel-linux-muslsf-gcc" +rustflags = ["-C", "target-feature=+crt-static", + "-L", "./musl_gcc/mipsel-linux-muslsf-cross/mipsel-linux-muslsf/lib", + "-L", "./musl_gcc/mipsel-linux-muslsf-cross/lib/gcc/mipsel-linux-muslsf/11.2.1", + "-l", "atomic" + ] + +[target.mips-unknown-linux-musl] +linker = "mips-linux-muslsf-gcc" +rustflags = ["-C", "target-feature=+crt-static", + "-L", "./musl_gcc/mips-linux-muslsf-cross/mips-linux-muslsf/lib", + "-L", "./musl_gcc/mips-linux-muslsf-cross/lib/gcc/mips-linux-muslsf/11.2.1", + "-l", "atomic" + ] \ No newline at end of file diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 9497505..c7dd6c1 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -26,6 +26,12 @@ jobs: - TARGET: x86_64-unknown-linux-musl OS: ubuntu-latest GUI_TARGET: x86_64-unknown-linux-gnu + - TARGET: mips-unknown-linux-musl + OS: ubuntu-latest + GUI_TARGET: + - TARGET: mipsel-unknown-linux-musl + OS: ubuntu-latest + GUI_TARGET: - TARGET: x86_64-apple-darwin OS: macos-latest GUI_TARGET: x86_64-apple-darwin @@ -81,27 +87,27 @@ jobs: # curl -s musl.cc | grep mipsel case $TARGET in mipsel-unknown-linux-musl) - MUSL_URI=mipsel-linux-musl-cross + MUSL_URI=mipsel-linux-muslsf ;; aarch64-unknown-linux-musl) - MUSL_URI=aarch64-linux-musl-cross + MUSL_URI=aarch64-linux-musl ;; armv7-unknown-linux-musleabihf) - MUSL_URI=armv7l-linux-musleabihf-cross + MUSL_URI=armv7l-linux-musleabihf ;; arm-unknown-linux-musleabihf) - MUSL_URI=arm-linux-musleabihf-cross + MUSL_URI=arm-linux-musleabihf ;; mips-unknown-linux-musl) - MUSL_URI=mips-linux-musl-cross + MUSL_URI=mips-linux-muslsf ;; esac if [ -n "$MUSL_URI" ]; then mkdir -p ./musl_gcc - wget -c https://musl.cc/$MUSL_URI.tgz -P ./musl_gcc/ - tar zxf ./musl_gcc/$MUSL_URI.tgz -C ./musl_gcc/ - sudo ln -s $(pwd)/musl_gcc/$MUSL_URI/bin/*gcc /usr/bin/ + wget -c https://musl.cc/${MUSL_URI}-cross.tgz -P ./musl_gcc/ + tar zxf ./musl_gcc/${MUSL_URI}-cross.tgz -C ./musl_gcc/ + sudo ln -s $(pwd)/musl_gcc/${MUSL_URI}-cross/bin/*gcc /usr/bin/ fi fi @@ -109,10 +115,25 @@ jobs: rustup set auto-self-update disable rustup install 1.75 rustup default 1.75 - rustup target add $TARGET - rustup target add $GUI_TARGET + + # mips/mipsel cannot add target from rustup, need compile by ourselves + if [[ $OS =~ ^ubuntu.*$ && $TARGET =~ ^mips.*$ ]]; then + cd "$PWD/musl_gcc/${MUSL_URI}-cross/lib/gcc/${MUSL_URI}/11.2.1" || exit 255 + cp libgcc_eh.a libunwind.a + rustup toolchain install nightly-x86_64-unknown-linux-gnu + rustup component add rust-src --toolchain nightly-x86_64-unknown-linux-gnu + cd - + else + rustup target add $TARGET + rustup target add $GUI_TARGET + fi - name: Run build - run: cargo build --release --verbose --target $TARGET + run: | + if [[ $OS =~ ^ubuntu.*$ && $TARGET =~ ^mips.*$ ]]; then + cargo +nightly build -r --verbose --target $TARGET -Z build-std --no-default-features --features mips + else + cargo build --release --verbose --target $TARGET + fi - name: Install for aarch64 gui cross compile run: | # see https://tauri.app/v1/guides/building/linux/ @@ -148,6 +169,9 @@ jobs: fi - name: Run build GUI run: | + if [ ! -n "$GUI_TARGET" ]; then + exit 0 + fi cd easytier-gui yarn install if [[ $OS =~ ^ubuntu.*$ && ! $GUI_TARGET =~ ^x86_64.*$ ]]; then @@ -178,7 +202,7 @@ jobs: mv ./target/$GUI_TARGET/release/bundle/nsis/*.exe ./artifacts/objects/ elif [[ $OS =~ ^macos.*$ ]]; then mv ./target/$GUI_TARGET/release/bundle/dmg/*.dmg ./artifacts/objects/ - elif [[ $OS =~ ^ubuntu.*$ ]]; then + elif [[ $OS =~ ^ubuntu.*$ && ! $TARGET =~ ^mips.*$ ]]; then mv ./target/$GUI_TARGET/release/bundle/deb/*.deb ./artifacts/objects/ if [[ $GUI_TARGET =~ ^x86_64.*$ ]]; then # currently only x86 appimage is supported diff --git a/Cargo.lock b/Cargo.lock index a522ec2..c21b9da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -39,14 +39,17 @@ dependencies = [ ] [[package]] -name = "ahash" -version = "0.7.7" +name = "aes-gcm" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" dependencies = [ - "getrandom 0.2.12", - "once_cell", - "version_check", + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", ] [[package]] @@ -228,6 +231,15 @@ dependencies = [ "critical-section", ] +[[package]] +name = "atomic-shim" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67cd4b51d303cf3501c301e8125df442128d3c6d7c69f71b27833d253de47e77" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "atomicbox" version = "0.4.0" @@ -348,18 +360,6 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - [[package]] name = "blake2" version = "0.10.6" @@ -445,28 +445,6 @@ version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" -[[package]] -name = "bytecheck" -version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6372023ac861f6e6dc89c8344a8f398fb42aaba2b5dbc649ca0c0e9dbcb627" -dependencies = [ - "bytecheck_derive", - "ptr_meta", - "simdutf8", -] - -[[package]] -name = "bytecheck_derive" -version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7ec4c6f261935ad534c0c22dbef2201b45918860eb1c574b972bd213a76af61" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "bytecodec" version = "0.4.15" @@ -983,6 +961,15 @@ dependencies = [ "syn 2.0.48", ] +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + [[package]] name = "cty" version = "0.2.2" @@ -1281,10 +1268,12 @@ checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" name = "easytier" version = "1.0.0" dependencies = [ + "aes-gcm", "anyhow", "async-recursion", "async-stream", "async-trait", + "atomic-shim", "atomicbox", "auto_impl", "base64 0.21.7", @@ -1304,6 +1293,7 @@ dependencies = [ "futures", "gethostname", "humansize", + "indexmap 1.9.3", "log", "mimalloc-rust", "network-interface", @@ -1321,7 +1311,6 @@ dependencies = [ "rcgen", "reqwest", "ring 0.16.20", - "rkyv", "rstest", "rustls", "serde", @@ -1532,12 +1521,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - [[package]] name = "futf" version = "0.1.5" @@ -1793,6 +1776,16 @@ dependencies = [ "wasi 0.11.0+wasi-snapshot-preview1", ] +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", +] + [[package]] name = "gimli" version = "0.28.1" @@ -1992,9 +1985,6 @@ name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash", -] [[package]] name = "hashbrown" @@ -3548,6 +3538,18 @@ dependencies = [ "universal-hash", ] +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + [[package]] name = "postcard" version = "1.0.8" @@ -3703,26 +3705,6 @@ dependencies = [ "prost", ] -[[package]] -name = "ptr_meta" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" -dependencies = [ - "ptr_meta_derive", -] - -[[package]] -name = "ptr_meta_derive" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "public-ip" version = "0.2.2" @@ -3810,12 +3792,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - [[package]] name = "radix_trie" version = "0.2.1" @@ -3995,15 +3971,6 @@ version = "1.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e898588f33fdd5b9420719948f9f2a32c922a246964576f71ba7f24f80610fbc" -[[package]] -name = "rend" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2571463863a6bd50c32f94402933f03457a3fbaf697a707c5be741e459f08fd" -dependencies = [ - "bytecheck", -] - [[package]] name = "reqwest" version = "0.11.27" @@ -4074,35 +4041,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "rkyv" -version = "0.7.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "527a97cdfef66f65998b5f3b637c26f5a5ec09cc52a3f9932313ac645f4190f5" -dependencies = [ - "bitvec", - "bytecheck", - "bytes", - "hashbrown 0.12.3", - "ptr_meta", - "rend", - "rkyv_derive", - "seahash", - "tinyvec", - "uuid", -] - -[[package]] -name = "rkyv_derive" -version = "0.7.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5c462a1328c8e67e4d6dbad1eb0355dd43e8ab432c6e227a43657f16ade5033" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "rstest" version = "0.18.2" @@ -4261,12 +4199,6 @@ dependencies = [ "untrusted 0.9.0", ] -[[package]] -name = "seahash" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" - [[package]] name = "security-framework" version = "2.9.2" @@ -4516,12 +4448,6 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" -[[package]] -name = "simdutf8" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" - [[package]] name = "siphasher" version = "0.3.11" @@ -4858,12 +4784,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - [[package]] name = "tar" version = "0.4.40" @@ -6456,15 +6376,6 @@ dependencies = [ "windows-implement", ] -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] - [[package]] name = "x11" version = "2.21.0" diff --git a/easytier/Cargo.toml b/easytier/Cargo.toml index 383fc21..73874ea 100644 --- a/easytier/Cargo.toml +++ b/easytier/Cargo.toml @@ -65,9 +65,11 @@ pin-project-lite = "0.2.13" atomicbox = "0.4.0" tachyonix = "0.2.1" -quinn = { version = "0.10.2" } -rustls = { version = "0.21.0", features = ["dangerous_configuration"] } -rcgen = "0.11.1" +quinn = { version = "0.10.2", optional = true } +rustls = { version = "0.21.0", features = [ + "dangerous_configuration", +], optional = true } +rcgen = { version = "0.11.1", optional = true } # for tap device tun = { version = "0.6.1", features = ["async"] } @@ -86,13 +88,6 @@ crossbeam-queue = "0.3" once_cell = "1.18.0" # for packet -rkyv = { "version" = "0.7.42", features = [ - "validation", - "archive_le", - "strict", - "copy_unsafe", - "arbitrary_enum_discriminant", -] } postcard = { "version" = "1.0.8", features = ["alloc"] } # for rpc @@ -130,9 +125,10 @@ network-interface = "1.1.1" pathfinding = "4.9.1" # for encryption -boringtun = { version = "0.6.0" } -ring = { version = "0.16" } +boringtun = { version = "0.6.0", optional = true } +ring = { version = "0.16", optional = true } bitflags = "2.5" +aes-gcm = { version = "0.10.3", optional = true } # for cli tabled = "0.15.*" @@ -142,7 +138,11 @@ base64 = "0.21.7" derivative = "2.2.0" -mimalloc-rust = "0.2.1" +mimalloc-rust = { version = "0.2.1", optional = true } + +indexmap = { version = "~1.9.3", optional = false, features = ["std"] } + +atomic-shim = "0.2.0" [target.'cfg(windows)'.dependencies] windows-sys = { version = "0.52", features = [ @@ -164,3 +164,12 @@ zip = "0.6.6" serial_test = "3.0.0" rstest = "0.18.2" defguard_wireguard_rs = "0.4.2" + + +[features] +default = ["wireguard", "quic", "mimalloc"] +mips = ["aes-gcm"] +wireguard = ["dep:boringtun", "dep:ring"] +quic = ["dep:quinn", "dep:rustls", "dep:rcgen"] +mimalloc = ["dep:mimalloc-rust"] +aes-gcm = ["dep:aes-gcm"] diff --git a/easytier/src/common/mod.rs b/easytier/src/common/mod.rs index d609ca5..a7c7a46 100644 --- a/easytier/src/common/mod.rs +++ b/easytier/src/common/mod.rs @@ -13,7 +13,6 @@ pub mod global_ctx; pub mod ifcfg; pub mod netns; pub mod network; -pub mod rkyv_util; pub mod stun; pub mod stun_codec_ext; diff --git a/easytier/src/common/rkyv_util.rs b/easytier/src/common/rkyv_util.rs deleted file mode 100644 index 0dfa730..0000000 --- a/easytier/src/common/rkyv_util.rs +++ /dev/null @@ -1,72 +0,0 @@ -use rkyv::{ - string::ArchivedString, - validation::{validators::DefaultValidator, CheckTypeError}, - vec::ArchivedVec, - Archive, CheckBytes, Serialize, -}; -use tokio_util::bytes::{Bytes, BytesMut}; - -pub fn decode_from_bytes_checked<'a, T: Archive>( - bytes: &'a [u8], -) -> Result<&'a T::Archived, CheckTypeError>> -where - T::Archived: CheckBytes>, -{ - rkyv::check_archived_root::(bytes) -} - -pub fn decode_from_bytes<'a, T: Archive>( - bytes: &'a [u8], -) -> Result<&'a T::Archived, CheckTypeError>> -where - T::Archived: CheckBytes>, -{ - // rkyv::check_archived_root::(bytes) - unsafe { Ok(rkyv::archived_root::(bytes)) } -} - -// allow deseraial T to Bytes -pub fn encode_to_bytes(val: &T) -> Bytes -where - T: Serialize>, -{ - let ret = rkyv::to_bytes::<_, N>(val).unwrap(); - // let mut r = BytesMut::new(); - // r.extend_from_slice(&ret); - // r.freeze() - ret.into_boxed_slice().into() -} - -pub fn extract_bytes_from_archived_vec(raw_data: &Bytes, archived_data: &ArchivedVec) -> Bytes { - let ptr_range = archived_data.as_ptr_range(); - let offset = ptr_range.start as usize - raw_data.as_ptr() as usize; - let len = ptr_range.end as usize - ptr_range.start as usize; - return raw_data.slice(offset..offset + len); -} - -pub fn extract_bytes_from_archived_string( - raw_data: &Bytes, - archived_data: &ArchivedString, -) -> Bytes { - let offset = archived_data.as_ptr() as usize - raw_data.as_ptr() as usize; - let len = archived_data.len(); - if offset + len > raw_data.len() { - return Bytes::new(); - } - - return raw_data.slice(offset..offset + archived_data.len()); -} - -pub fn extract_bytes_mut_from_archived_vec( - raw_data: &mut BytesMut, - archived_data: &ArchivedVec, -) -> BytesMut { - let ptr_range = archived_data.as_ptr_range(); - let offset = ptr_range.start as usize - raw_data.as_ptr() as usize; - let len = ptr_range.end as usize - ptr_range.start as usize; - raw_data.split_off(offset).split_to(len) -} - -pub fn vec_to_string(vec: Vec) -> String { - unsafe { String::from_utf8_unchecked(vec) } -} diff --git a/easytier/src/connector/mod.rs b/easytier/src/connector/mod.rs index af987ab..db43c1c 100644 --- a/easytier/src/connector/mod.rs +++ b/easytier/src/connector/mod.rs @@ -3,16 +3,15 @@ use std::{ sync::Arc, }; +#[cfg(feature = "quic")] +use crate::tunnel::quic::QUICTunnelConnector; +#[cfg(feature = "wireguard")] +use crate::tunnel::wireguard::{WgConfig, WgTunnelConnector}; use crate::{ common::{error::Error, global_ctx::ArcGlobalCtx, network::IPCollector}, tunnel::{ - check_scheme_and_get_socket_addr, - quic::QUICTunnelConnector, - ring::RingTunnelConnector, - tcp::TcpTunnelConnector, - udp::UdpTunnelConnector, - wireguard::{WgConfig, WgTunnelConnector}, - TunnelConnector, + check_scheme_and_get_socket_addr, ring::RingTunnelConnector, tcp::TcpTunnelConnector, + udp::UdpTunnelConnector, TunnelConnector, }, }; @@ -77,6 +76,7 @@ pub async fn create_connector_by_url( let connector = RingTunnelConnector::new(url); return Ok(Box::new(connector)); } + #[cfg(feature = "quic")] "quic" => { let dst_addr = check_scheme_and_get_socket_addr::(&url, "quic")?; let mut connector = QUICTunnelConnector::new(url); @@ -88,6 +88,7 @@ pub async fn create_connector_by_url( .await; return Ok(Box::new(connector)); } + #[cfg(feature = "wireguard")] "wg" => { let dst_addr = check_scheme_and_get_socket_addr::(&url, "wg")?; let nid = global_ctx.get_network_identity(); diff --git a/easytier/src/easytier-core.rs b/easytier/src/easytier-core.rs index 30b4406..080f3ba 100644 --- a/easytier/src/easytier-core.rs +++ b/easytier/src/easytier-core.rs @@ -32,8 +32,10 @@ use crate::common::{ global_ctx::GlobalCtxEvent, }; +#[cfg(feature = "mimalloc")] use mimalloc_rust::*; +#[cfg(feature = "mimalloc")] #[global_allocator] static GLOBAL_MIMALLOC: GlobalMiMalloc = GlobalMiMalloc; diff --git a/easytier/src/instance/instance.rs b/easytier/src/instance/instance.rs index 64742a6..4b38ebc 100644 --- a/easytier/src/instance/instance.rs +++ b/easytier/src/instance/instance.rs @@ -130,7 +130,10 @@ impl Instance { let peer_center = Arc::new(PeerCenterInstance::new(peer_manager.clone())); + #[cfg(feature = "wireguard")] let vpn_portal_inst = vpn_portal::wireguard::WireGuard::default(); + #[cfg(not(feature = "wireguard"))] + let vpn_portal_inst = vpn_portal::NullVpnPortal; Instance { inst_name: global_ctx.inst_name.clone(), diff --git a/easytier/src/instance/listeners.rs b/easytier/src/instance/listeners.rs index 40730af..e74697c 100644 --- a/easytier/src/instance/listeners.rs +++ b/easytier/src/instance/listeners.rs @@ -4,6 +4,10 @@ use anyhow::Context; use async_trait::async_trait; use tokio::{sync::Mutex, task::JoinSet}; +#[cfg(feature = "quic")] +use crate::tunnel::quic::QUICTunnelListener; +#[cfg(feature = "wireguard")] +use crate::tunnel::wireguard::{WgConfig, WgTunnelListener}; use crate::{ common::{ error::Error, @@ -12,30 +16,28 @@ use crate::{ }, peers::peer_manager::PeerManager, tunnel::{ - quic::QUICTunnelListener, - ring::RingTunnelListener, - tcp::TcpTunnelListener, - udp::UdpTunnelListener, - wireguard::{WgConfig, WgTunnelListener}, - Tunnel, TunnelListener, + ring::RingTunnelListener, tcp::TcpTunnelListener, udp::UdpTunnelListener, Tunnel, + TunnelListener, }, }; pub fn get_listener_by_url( l: &url::Url, - ctx: ArcGlobalCtx, + _ctx: ArcGlobalCtx, ) -> Result, Error> { Ok(match l.scheme() { "tcp" => Box::new(TcpTunnelListener::new(l.clone())), "udp" => Box::new(UdpTunnelListener::new(l.clone())), + #[cfg(feature = "wireguard")] "wg" => { - let nid = ctx.get_network_identity(); + let nid = _ctx.get_network_identity(); let wg_config = WgConfig::new_from_network_identity( &nid.network_name, &nid.network_secret.unwrap_or_default(), ); Box::new(WgTunnelListener::new(l.clone(), wg_config)) } + #[cfg(feature = "quic")] "quic" => Box::new(QUICTunnelListener::new(l.clone())), _ => { unreachable!("unsupported listener uri"); diff --git a/easytier/src/peers/encrypt/aes_gcm.rs b/easytier/src/peers/encrypt/aes_gcm.rs new file mode 100644 index 0000000..886b470 --- /dev/null +++ b/easytier/src/peers/encrypt/aes_gcm.rs @@ -0,0 +1,146 @@ +use aes_gcm::aead::consts::{U12, U16}; +use aes_gcm::aead::generic_array::GenericArray; +use aes_gcm::{AeadCore, AeadInPlace, Aes128Gcm, Aes256Gcm, Key, KeyInit, Nonce, Tag}; +use rand::rngs::OsRng; +use zerocopy::{AsBytes, FromBytes}; + +use crate::tunnel::packet_def::{AesGcmTail, ZCPacket, AES_GCM_ENCRYPTION_RESERVED}; + +use super::{Encryptor, Error}; + +#[derive(Clone)] +pub struct AesGcmCipher { + pub(crate) cipher: AesGcmEnum, +} + +#[derive(Clone)] +pub enum AesGcmEnum { + AES128GCM(Aes128Gcm), + AES256GCM(Aes256Gcm), +} + +impl AesGcmCipher { + pub fn new_128(key: [u8; 16]) -> Self { + let key: &Key = &key.into(); + Self { + cipher: AesGcmEnum::AES128GCM(Aes128Gcm::new(key)), + } + } + pub fn new_256(key: [u8; 32]) -> Self { + let key: &Key = &key.into(); + Self { + cipher: AesGcmEnum::AES256GCM(Aes256Gcm::new(key)), + } + } +} + +impl Encryptor for AesGcmCipher { + fn decrypt(&self, zc_packet: &mut ZCPacket) -> Result<(), Error> { + let pm_header = zc_packet.peer_manager_header().unwrap(); + if !pm_header.is_encrypted() { + return Err(Error::NotEcrypted); + } + + let payload_len = zc_packet.payload().len(); + if payload_len < AES_GCM_ENCRYPTION_RESERVED { + return Err(Error::PacketTooShort(zc_packet.payload().len())); + } + + let text_len = payload_len - AES_GCM_ENCRYPTION_RESERVED; + + let aes_tail = AesGcmTail::ref_from_suffix(zc_packet.payload()) + .unwrap() + .clone(); + let nonce: &GenericArray = Nonce::from_slice(&aes_tail.nonce); + + let tag: GenericArray = Tag::clone_from_slice(aes_tail.tag.as_slice()); + let rs = match &self.cipher { + AesGcmEnum::AES128GCM(aes_gcm) => aes_gcm.decrypt_in_place_detached( + nonce, + &[], + &mut zc_packet.mut_payload()[..text_len], + &tag, + ), + AesGcmEnum::AES256GCM(aes_gcm) => aes_gcm.decrypt_in_place_detached( + nonce, + &[], + &mut zc_packet.mut_payload()[..text_len], + &tag, + ), + }; + + if let Err(e) = rs { + println!("error: {:?}", e.to_string()); + return Err(Error::DecryptionFailed); + } + + let pm_header = zc_packet.mut_peer_manager_header().unwrap(); + pm_header.set_encrypted(false); + let old_len = zc_packet.buf_len(); + zc_packet + .mut_inner() + .truncate(old_len - AES_GCM_ENCRYPTION_RESERVED); + return Ok(()); + } + + fn encrypt(&self, zc_packet: &mut ZCPacket) -> Result<(), Error> { + let pm_header = zc_packet.peer_manager_header().unwrap(); + if pm_header.is_encrypted() { + tracing::warn!(?zc_packet, "packet is already encrypted"); + return Ok(()); + } + + let mut tail = AesGcmTail::default(); + let rs = match &self.cipher { + AesGcmEnum::AES128GCM(aes_gcm) => { + let nonce = Aes128Gcm::generate_nonce(&mut OsRng); + tail.nonce.copy_from_slice(nonce.as_slice()); + aes_gcm.encrypt_in_place_detached(&nonce, &[], zc_packet.mut_payload()) + } + AesGcmEnum::AES256GCM(aes_gcm) => { + let nonce = Aes256Gcm::generate_nonce(&mut OsRng); + tail.nonce.copy_from_slice(nonce.as_slice()); + aes_gcm.encrypt_in_place_detached(&nonce, &[], zc_packet.mut_payload()) + } + }; + + return match rs { + Ok(tag) => { + tail.tag.copy_from_slice(tag.as_slice()); + + let pm_header = zc_packet.mut_peer_manager_header().unwrap(); + pm_header.set_encrypted(true); + zc_packet.mut_inner().extend_from_slice(tail.as_bytes()); + Ok(()) + } + Err(_) => Err(Error::EncryptionFailed), + }; + } +} + +#[cfg(test)] +mod tests { + use crate::{ + peers::encrypt::{aes_gcm::AesGcmCipher, Encryptor}, + tunnel::packet_def::{ZCPacket, AES_GCM_ENCRYPTION_RESERVED}, + }; + + #[test] + fn test_aes_gcm_cipher() { + let key = [0u8; 16]; + let cipher = AesGcmCipher::new_128(key); + let text = b"1234567"; + let mut packet = ZCPacket::new_with_payload(text); + packet.fill_peer_manager_hdr(0, 0, 0); + cipher.encrypt(&mut packet).unwrap(); + assert_eq!( + packet.payload().len(), + text.len() + AES_GCM_ENCRYPTION_RESERVED + ); + assert_eq!(packet.peer_manager_header().unwrap().is_encrypted(), true); + + cipher.decrypt(&mut packet).unwrap(); + assert_eq!(packet.payload(), text); + assert_eq!(packet.peer_manager_header().unwrap().is_encrypted(), false); + } +} diff --git a/easytier/src/peers/encrypt/mod.rs b/easytier/src/peers/encrypt/mod.rs index 1ff923f..c63dee7 100644 --- a/easytier/src/peers/encrypt/mod.rs +++ b/easytier/src/peers/encrypt/mod.rs @@ -1,7 +1,11 @@ use crate::tunnel::packet_def::ZCPacket; +#[cfg(feature = "wireguard")] pub mod ring_aes_gcm; +#[cfg(feature = "aes-gcm")] +pub mod aes_gcm; + #[derive(thiserror::Error, Debug)] pub enum Error { #[error("packet is not encrypted")] diff --git a/easytier/src/peers/mod.rs b/easytier/src/peers/mod.rs index afc6054..3c622fc 100644 --- a/easytier/src/peers/mod.rs +++ b/easytier/src/peers/mod.rs @@ -1,4 +1,3 @@ -pub mod packet; pub mod peer; // pub mod peer_conn; pub mod peer_conn; diff --git a/easytier/src/peers/packet.rs b/easytier/src/peers/packet.rs deleted file mode 100644 index 943b07c..0000000 --- a/easytier/src/peers/packet.rs +++ /dev/null @@ -1,254 +0,0 @@ -use std::fmt::Debug; - -use rkyv::{Archive, Deserialize, Serialize}; -use tokio_util::bytes::Bytes; - -use crate::common::{ - global_ctx::NetworkIdentity, - rkyv_util::{decode_from_bytes, encode_to_bytes, vec_to_string}, - PeerId, -}; - -const MAGIC: u32 = 0xd1e1a5e1; -const VERSION: u32 = 1; - -#[derive(Archive, Deserialize, Serialize, PartialEq, Clone)] -#[archive(compare(PartialEq), check_bytes)] -// Derives can be passed through to the generated type: -#[archive_attr(derive(Debug))] -pub struct UUID(uuid::Bytes); - -// impl Debug for UUID -impl std::fmt::Debug for UUID { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let uuid = uuid::Uuid::from_bytes(self.0); - write!(f, "{}", uuid) - } -} - -impl From for UUID { - fn from(uuid: uuid::Uuid) -> Self { - UUID(*uuid.as_bytes()) - } -} - -impl From for uuid::Uuid { - fn from(uuid: UUID) -> Self { - uuid::Uuid::from_bytes(uuid.0) - } -} - -impl ArchivedUUID { - pub fn to_uuid(&self) -> uuid::Uuid { - uuid::Uuid::from_bytes(self.0) - } -} - -impl From<&ArchivedUUID> for UUID { - fn from(uuid: &ArchivedUUID) -> Self { - UUID(uuid.0) - } -} - -#[derive(serde::Serialize, serde::Deserialize, Debug)] -pub struct HandShake { - pub magic: u32, - pub my_peer_id: PeerId, - pub version: u32, - pub features: Vec, - pub network_identity: NetworkIdentity, -} - -#[derive(serde::Serialize, serde::Deserialize, Debug)] -pub struct RoutePacket { - pub route_id: u8, - pub body: Vec, -} - -#[derive(Debug, serde::Serialize, serde::Deserialize)] -pub enum CtrlPacketPayload { - HandShake(HandShake), - RoutePacket(RoutePacket), - Ping(u32), - Pong(u32), - TaRpc(u32, u32, bool, Vec), // u32: service_id, u32: transact_id, bool: is_req, Vec: rpc body -} - -impl CtrlPacketPayload { - pub fn from_packet(p: &ArchivedPacket) -> CtrlPacketPayload { - assert_ne!(p.packet_type, PacketType::Data); - postcard::from_bytes(p.payload.as_bytes()).unwrap() - } - - pub fn from_packet2(p: &Packet) -> CtrlPacketPayload { - postcard::from_bytes(p.payload.as_bytes()).unwrap() - } -} - -#[repr(u8)] -#[derive(Archive, Deserialize, Serialize, Debug)] -#[archive(compare(PartialEq), check_bytes)] -// Derives can be passed through to the generated type: -#[archive_attr(derive(Debug))] -pub enum PacketType { - Data = 1, - HandShake = 2, - RoutePacket = 3, - Ping = 4, - Pong = 5, - TaRpc = 6, -} - -#[derive(Archive, Deserialize, Serialize)] -#[archive(compare(PartialEq), check_bytes)] -// Derives can be passed through to the generated type: -pub struct Packet { - pub from_peer: PeerId, - pub to_peer: PeerId, - pub packet_type: PacketType, - pub payload: String, -} - -impl std::fmt::Debug for Packet { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "Packet {{ from_peer: {}, to_peer: {}, packet_type: {:?}, payload: {:?} }}", - self.from_peer, - self.to_peer, - self.packet_type, - &self.payload.as_bytes() - ) - } -} - -impl std::fmt::Debug for ArchivedPacket { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "Packet {{ from_peer: {}, to_peer: {}, packet_type: {:?}, payload: {:?} }}", - self.from_peer, - self.to_peer, - self.packet_type, - &self.payload.as_bytes() - ) - } -} - -impl Packet { - pub fn decode(v: &[u8]) -> &ArchivedPacket { - decode_from_bytes::(v).unwrap() - } - - pub fn new( - from_peer: PeerId, - to_peer: PeerId, - packet_type: PacketType, - payload: Vec, - ) -> Self { - Packet { - from_peer, - to_peer, - packet_type, - payload: vec_to_string(payload), - } - } -} - -impl From for Bytes { - fn from(val: Packet) -> Self { - encode_to_bytes::<_, 4096>(&val) - } -} - -impl Packet { - pub fn new_handshake(from_peer: PeerId, network: &NetworkIdentity) -> Self { - let handshake = CtrlPacketPayload::HandShake(HandShake { - magic: MAGIC, - my_peer_id: from_peer, - version: VERSION, - features: Vec::new(), - network_identity: network.clone().into(), - }); - Packet::new( - from_peer.into(), - 0, - PacketType::HandShake, - postcard::to_allocvec(&handshake).unwrap(), - ) - } - - pub fn new_data_packet(from_peer: PeerId, to_peer: PeerId, data: &[u8]) -> Self { - Packet::new(from_peer, to_peer, PacketType::Data, data.to_vec()) - } - - pub fn new_route_packet(from_peer: PeerId, to_peer: PeerId, route_id: u8, data: &[u8]) -> Self { - let route = CtrlPacketPayload::RoutePacket(RoutePacket { - route_id, - body: data.to_vec(), - }); - Packet::new( - from_peer, - to_peer, - PacketType::RoutePacket, - postcard::to_allocvec(&route).unwrap(), - ) - } - - pub fn new_ping_packet(from_peer: PeerId, to_peer: PeerId, seq: u32) -> Self { - let ping = CtrlPacketPayload::Ping(seq); - Packet::new( - from_peer, - to_peer, - PacketType::Ping, - postcard::to_allocvec(&ping).unwrap(), - ) - } - - pub fn new_pong_packet(from_peer: PeerId, to_peer: PeerId, seq: u32) -> Self { - let pong = CtrlPacketPayload::Pong(seq); - Packet::new( - from_peer, - to_peer, - PacketType::Pong, - postcard::to_allocvec(&pong).unwrap(), - ) - } - - pub fn new_tarpc_packet( - from_peer: PeerId, - to_peer: PeerId, - service_id: u32, - transact_id: u32, - is_req: bool, - body: Vec, - ) -> Self { - let ta_rpc = CtrlPacketPayload::TaRpc(service_id, transact_id, is_req, body); - Packet::new( - from_peer, - to_peer, - PacketType::TaRpc, - postcard::to_allocvec(&ta_rpc).unwrap(), - ) - } -} - -#[cfg(test)] -mod tests { - use crate::common::new_peer_id; - - use super::*; - - #[tokio::test] - async fn serialize() { - let a = "abcde"; - let out = Packet::new_data_packet(new_peer_id(), new_peer_id(), a.as_bytes()); - // let out = T::new(a.as_bytes()); - let out_bytes: Bytes = out.into(); - println!("out str: {:?}", a.as_bytes()); - println!("out bytes: {:?}", out_bytes); - - let archived = Packet::decode(&out_bytes[..]); - println!("in packet: {:?}", archived); - } -} diff --git a/easytier/src/peers/peer_conn.rs b/easytier/src/peers/peer_conn.rs index 960397a..4a6e586 100644 --- a/easytier/src/peers/peer_conn.rs +++ b/easytier/src/peers/peer_conn.rs @@ -29,8 +29,8 @@ use crate::{ global_ctx::ArcGlobalCtx, PeerId, }, - peers::packet::PacketType, rpc::{HandshakeRequest, PeerConnInfo, PeerConnStats, TunnelInfo}, + tunnel::packet_def::PacketType, tunnel::{ filter::{StatsRecorderTunnelFilter, TunnelFilter, TunnelWithFilter}, mpsc::{MpscTunnel, MpscTunnelSender}, diff --git a/easytier/src/peers/peer_manager.rs b/easytier/src/peers/peer_manager.rs index cde4271..0174828 100644 --- a/easytier/src/peers/peer_manager.rs +++ b/easytier/src/peers/peer_manager.rs @@ -22,17 +22,18 @@ use tokio_util::bytes::Bytes; use crate::{ common::{error::Error, global_ctx::ArcGlobalCtx, PeerId}, peers::{ - packet, peer_conn::PeerConn, peer_rpc::PeerRpcManagerTransport, - route_trait::RouteInterface, PeerPacketFilter, + peer_conn::PeerConn, peer_rpc::PeerRpcManagerTransport, route_trait::RouteInterface, + PeerPacketFilter, }, tunnel::{ + self, packet_def::{PacketType, ZCPacket}, SinkItem, Tunnel, TunnelConnector, }, }; use super::{ - encrypt::{ring_aes_gcm::AesGcmCipher, Encryptor, NullCipher}, + encrypt::{Encryptor, NullCipher}, foreign_network_client::ForeignNetworkClient, foreign_network_manager::ForeignNetworkManager, peer_conn::PeerConnId, @@ -176,12 +177,25 @@ impl PeerManager { my_peer_id, )); - let encryptor: Arc> = - Arc::new(if global_ctx.get_flags().enable_encryption { - Box::new(AesGcmCipher::new_128(global_ctx.get_128_key())) - } else { - Box::new(NullCipher) - }); + let mut encryptor: Arc> = Arc::new(Box::new(NullCipher)); + if global_ctx.get_flags().enable_encryption { + #[cfg(feature = "wireguard")] + { + use super::encrypt::ring_aes_gcm::AesGcmCipher; + encryptor = Arc::new(Box::new(AesGcmCipher::new_128(global_ctx.get_128_key()))); + } + + #[cfg(all(feature = "aes-gcm", not(feature = "wireguard")))] + { + use super::encrypt::aes_gcm::AesGcmCipher; + encryptor = Arc::new(Box::new(AesGcmCipher::new_128(global_ctx.get_128_key()))); + } + + #[cfg(all(not(feature = "wireguard"), not(feature = "aes-gcm")))] + { + compile_error!("wireguard or aes-gcm feature must be enabled for encryption"); + } + } // TODO: remove these because we have impl pipeline processor. let (peer_rpc_tspt_sender, peer_rpc_tspt_recv) = mpsc::unbounded_channel(); @@ -536,7 +550,11 @@ impl PeerManager { return Ok(()); } - msg.fill_peer_manager_hdr(self.my_peer_id, 0, packet::PacketType::Data as u8); + msg.fill_peer_manager_hdr( + self.my_peer_id, + 0, + tunnel::packet_def::PacketType::Data as u8, + ); self.run_nic_packet_process_pipeline(&mut msg).await; self.encryptor .encrypt(&mut msg) diff --git a/easytier/src/peers/peer_ospf_route.rs b/easytier/src/peers/peer_ospf_route.rs index a23e835..b7b9624 100644 --- a/easytier/src/peers/peer_ospf_route.rs +++ b/easytier/src/peers/peer_ospf_route.rs @@ -3,7 +3,7 @@ use std::{ fmt::Debug, net::Ipv4Addr, sync::{ - atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering}, + atomic::{AtomicBool, AtomicU32, Ordering}, Arc, Weak, }, time::{Duration, SystemTime}, @@ -473,7 +473,8 @@ impl RouteTable { } type SessionId = u64; -type AtomicSessionId = AtomicU64; + +type AtomicSessionId = atomic_shim::AtomicU64; // if we need to sync route info with one peer, we create a SyncRouteSession with that peer. #[derive(Debug)] diff --git a/easytier/src/tests/three_node.rs b/easytier/src/tests/three_node.rs index a5f9bd0..b9f37c3 100644 --- a/easytier/src/tests/three_node.rs +++ b/easytier/src/tests/three_node.rs @@ -9,17 +9,18 @@ use super::*; use crate::{ common::{ - config::{ConfigLoader, NetworkIdentity, TomlConfigLoader, VpnPortalConfig}, + config::{ConfigLoader, NetworkIdentity, TomlConfigLoader}, netns::{NetNS, ROOT_NETNS_NAME}, }, instance::instance::Instance, peers::tests::wait_for_condition, - tunnel::{ - ring::RingTunnelConnector, - tcp::TcpTunnelConnector, - udp::UdpTunnelConnector, - wireguard::{WgConfig, WgTunnelConnector}, - }, + tunnel::{ring::RingTunnelConnector, tcp::TcpTunnelConnector, udp::UdpTunnelConnector}, +}; + +#[cfg(feature = "wireguard")] +use crate::{ + common::config::VpnPortalConfig, + tunnel::wireguard::{WgConfig, WgTunnelConnector}, vpn_portal::wireguard::get_wg_config_for_portal, }; @@ -81,6 +82,7 @@ pub async fn init_three_node(proto: &str) -> Vec { "udp://10.1.1.1:11010".parse().unwrap(), )); } else if proto == "wg" { + #[cfg(feature = "wireguard")] inst2 .get_conn_manager() .add_connector(WgTunnelConnector::new( @@ -226,6 +228,7 @@ pub async fn icmp_proxy_three_node_test(#[values("tcp", "udp", "wg")] proto: &st .await; } +#[cfg(feature = "wireguard")] #[rstest::rstest] #[tokio::test] #[serial_test::serial] @@ -478,6 +481,7 @@ fn run_wireguard_client( Ok(()) } +#[cfg(feature = "wireguard")] #[tokio::test] #[serial_test::serial] pub async fn wireguard_vpn_portal() { diff --git a/easytier/src/tunnel/mod.rs b/easytier/src/tunnel/mod.rs index 454b654..07b1d3e 100644 --- a/easytier/src/tunnel/mod.rs +++ b/easytier/src/tunnel/mod.rs @@ -17,13 +17,17 @@ pub mod common; pub mod filter; pub mod mpsc; pub mod packet_def; -pub mod quic; pub mod ring; pub mod stats; pub mod tcp; pub mod udp; + +#[cfg(feature = "wireguard")] pub mod wireguard; +#[cfg(feature = "quic")] +pub mod quic; + #[derive(thiserror::Error, Debug)] pub enum TunnelError { #[error("io error")] diff --git a/easytier/src/vpn_portal/mod.rs b/easytier/src/vpn_portal/mod.rs index 3a2c171..7a6caa8 100644 --- a/easytier/src/vpn_portal/mod.rs +++ b/easytier/src/vpn_portal/mod.rs @@ -9,6 +9,7 @@ use std::sync::Arc; use crate::{common::global_ctx::ArcGlobalCtx, peers::peer_manager::PeerManager}; +#[cfg(feature = "wireguard")] pub mod wireguard; #[async_trait::async_trait] @@ -22,3 +23,28 @@ pub trait VpnPortal: Send + Sync { fn name(&self) -> String; async fn list_clients(&self) -> Vec; } + +pub struct NullVpnPortal; + +#[async_trait::async_trait] +impl VpnPortal for NullVpnPortal { + async fn start( + &mut self, + _global_ctx: ArcGlobalCtx, + _peer_mgr: Arc, + ) -> anyhow::Result<()> { + Ok(()) + } + + async fn dump_client_config(&self, _peer_mgr: Arc) -> String { + "".to_string() + } + + fn name(&self) -> String { + "null".to_string() + } + + async fn list_clients(&self) -> Vec { + vec![] + } +}