Commit 25f57c13 authored by nanahira's avatar nanahira

fix crlf

parent d1b6812f
Pipeline #42031 passed with stages
in 1 minute and 43 seconds
stages: stages:
- build - build
- deploy - deploy
variables: variables:
GIT_DEPTH: "1" GIT_DEPTH: "1"
before_script: before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
.build-image: .build-image:
stage: build stage: build
script: script:
- docker build --pull -t $TARGET_IMAGE . - docker build --pull -t $TARGET_IMAGE .
- docker push $TARGET_IMAGE - docker push $TARGET_IMAGE
build-x86: build-x86:
extends: .build-image extends: .build-image
tags: tags:
- docker - docker
variables: variables:
TARGET_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG-x86 TARGET_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG-x86
build-arm: build-arm:
extends: .build-image extends: .build-image
tags: tags:
- docker-arm - docker-arm
variables: variables:
TARGET_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG-arm TARGET_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG-arm
.deploy: .deploy:
stage: deploy stage: deploy
tags: tags:
- docker - docker
script: script:
- docker pull $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG-x86 - docker pull $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG-x86
- docker pull $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG-arm - docker pull $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG-arm
- docker manifest create $TARGET_IMAGE --amend $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG-x86 --amend - docker manifest create $TARGET_IMAGE --amend $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG-x86 --amend
$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG-arm $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG-arm
- docker manifest push $TARGET_IMAGE - docker manifest push $TARGET_IMAGE
deploy_latest: deploy_latest:
extends: .deploy extends: .deploy
variables: variables:
TARGET_IMAGE: $CI_REGISTRY_IMAGE:latest TARGET_IMAGE: $CI_REGISTRY_IMAGE:latest
only: only:
- master - master
deploy_branch: deploy_branch:
extends: .deploy extends: .deploy
variables: variables:
TARGET_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG TARGET_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
FROM rust:1.91-alpine3.22 as chef FROM rust:1.91-alpine3.22 as chef
RUN apk add --no-cache musl-dev RUN apk add --no-cache musl-dev
RUN cargo install cargo-chef RUN cargo install cargo-chef
WORKDIR /usr/src/app WORKDIR /usr/src/app
FROM chef as planner FROM chef as planner
COPY Cargo.toml Cargo.lock ./ COPY Cargo.toml Cargo.lock ./
COPY src src COPY src src
RUN cargo chef prepare --recipe-path recipe.json RUN cargo chef prepare --recipe-path recipe.json
FROM chef as builder FROM chef as builder
COPY --from=planner /usr/src/app/recipe.json recipe.json COPY --from=planner /usr/src/app/recipe.json recipe.json
RUN cargo chef cook --release --recipe-path recipe.json RUN cargo chef cook --release --recipe-path recipe.json
COPY Cargo.toml Cargo.lock ./ COPY Cargo.toml Cargo.lock ./
COPY src src COPY src src
RUN cargo build --release RUN cargo build --release
FROM alpine:3.22 FROM alpine:3.22
RUN apk --no-cache add libgcc libstdc++ bash iproute2 iptables iptables-legacy ipset netcat-openbsd jq iputils RUN apk --no-cache add libgcc libstdc++ bash iproute2 iptables iptables-legacy ipset netcat-openbsd jq iputils
COPY --from=builder /usr/src/app/target/release/tun1 /usr/local/bin/tun COPY --from=builder /usr/src/app/target/release/tun1 /usr/local/bin/tun
COPY ./entrypoint.sh /entrypoint.sh COPY ./entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"] ENTRYPOINT ["/entrypoint.sh"]
CMD ["tun"] CMD ["tun"]
mod config; mod config;
mod router; mod router;
use crate::config::{Config, Schema}; use crate::config::{Config, Schema};
use crate::router::{Meta, Router, META_SIZE}; use crate::router::{Meta, Router, META_SIZE};
use anyhow::anyhow; use anyhow::anyhow;
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use crossbeam::epoch::{pin, Owned}; use crossbeam::epoch::{pin, Owned};
use crossbeam_utils::thread; use crossbeam_utils::thread;
use itertools::Itertools; use itertools::Itertools;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::net::Shutdown; use std::net::Shutdown;
use std::sync::atomic::Ordering; use std::sync::atomic::Ordering;
use std::time::Duration; use std::time::Duration;
use std::{env, mem::MaybeUninit}; use std::{env, mem::MaybeUninit};
use std::fs; use std::fs;
fn main() -> Result<()> { fn main() -> Result<()> {
println!("Starting"); println!("Starting");
let args: Vec<String> = env::args().collect(); let args: Vec<String> = env::args().collect();
if args.len() < 2 { if args.len() < 2 {
return Err(anyhow!("need JSON string or -c <config.json>")); return Err(anyhow!("need JSON string or -c <config.json>"));
} }
let config: Config; let config: Config;
if args[1] == "-c" || args[1] == "--config" { if args[1] == "-c" || args[1] == "--config" {
// 从文件读 // 从文件读
if args.len() < 3 { if args.len() < 3 {
return Err(anyhow!("missing value for -c/--config")); return Err(anyhow!("missing value for -c/--config"));
} }
let data = fs::read_to_string(&args[2])?; let data = fs::read_to_string(&args[2])?;
config = serde_json::from_str(&data)?; config = serde_json::from_str(&data)?;
} else { } else {
// 当作 JSON 字符串解析 // 当作 JSON 字符串解析
config = serde_json::from_str(&args[1])?; config = serde_json::from_str(&args[1])?;
} }
println!("Read config"); println!("Read config");
let routers = &config let routers = &config
.routers .routers
.into_iter() .into_iter()
.sorted_by_key(|r| r.remote_id) .sorted_by_key(|r| r.remote_id)
.map(|c| Router::new(c).map(|router| (router.config.remote_id, router))) .map(|c| Router::new(c).map(|router| (router.config.remote_id, router)))
.collect::<Result<BTreeMap<u8, Router>, _>>()?; .collect::<Result<BTreeMap<u8, Router>, _>>()?;
for (_, group) in &routers for (_, group) in &routers
.values() .values()
.filter(|r| r.config.schema == Schema::UDP && r.config.src_port != 0) .filter(|r| r.config.schema == Schema::UDP && r.config.src_port != 0)
.chunk_by(|r| r.config.src_port) .chunk_by(|r| r.config.src_port)
{ {
Router::attach_filter_udp(group.collect())?; Router::attach_filter_udp(group.collect())?;
} }
println!("created tuns"); println!("created tuns");
const TCP_RECONNECT: u64 = 10; const TCP_RECONNECT: u64 = 10;
thread::scope(|s| { thread::scope(|s| {
// IP, UDP // IP, UDP
for router in routers.values().filter(|&r| r.config.schema != Schema::TCP) { for router in routers.values().filter(|&r| r.config.schema != Schema::TCP) {
s.spawn(|_| { s.spawn(|_| {
router.handle_outbound_ip_udp(); router.handle_outbound_ip_udp();
}); });
s.spawn(|_| { s.spawn(|_| {
router.handle_inbound_ip_udp(); router.handle_inbound_ip_udp();
}); });
} }
for router in routers.values().filter(|&r| r.config.schema == Schema::TCP && r.config.dst_port != 0) { for router in routers.values().filter(|&r| r.config.schema == Schema::TCP && r.config.dst_port != 0) {
s.spawn(|_| { s.spawn(|_| {
loop { loop {
if let Ok(connection) = router.connect_tcp() { if let Ok(connection) = router.connect_tcp() {
let _ = thread::scope(|s| { let _ = thread::scope(|s| {
s.spawn(|_| router.handle_outbound_tcp(&connection)); s.spawn(|_| router.handle_outbound_tcp(&connection));
s.spawn(|_| router.handle_inbound_tcp(&connection)); s.spawn(|_| router.handle_inbound_tcp(&connection));
}); });
} }
std::thread::sleep(Duration::from_secs(TCP_RECONNECT)); std::thread::sleep(Duration::from_secs(TCP_RECONNECT));
} }
}); });
} }
// tcp listeners // tcp listeners
for router in routers for router in routers
.values() .values()
.filter(|&r| r.config.schema == Schema::TCP && r.config.dst_port == 0) .filter(|&r| r.config.schema == Schema::TCP && r.config.dst_port == 0)
.unique_by(|r| r.config.src_port) .unique_by(|r| r.config.src_port)
{ {
println!("listen on port {}", router.config.src_port); println!("listen on port {}", router.config.src_port);
let socket = router.listen_tcp(); let socket = router.listen_tcp();
s.spawn(move |s| { s.spawn(move |s| {
// listen 或 accept 出错直接 panic // listen 或 accept 出错直接 panic
loop { loop {
let (connection, _) = socket.accept().unwrap(); let (connection, _) = socket.accept().unwrap();
s.spawn(move |_| { s.spawn(move |_| {
connection.set_tcp_nodelay(true).unwrap(); connection.set_tcp_nodelay(true).unwrap();
let mut meta_bytes = [MaybeUninit::uninit(); META_SIZE]; let mut meta_bytes = [MaybeUninit::uninit(); META_SIZE];
Router::recv_exact_tcp(&connection, &mut meta_bytes).unwrap(); Router::recv_exact_tcp(&connection, &mut meta_bytes).unwrap();
let meta: &Meta = Meta::from_bytes(&meta_bytes); let meta: &Meta = Meta::from_bytes(&meta_bytes);
if meta.reversed == 0 if meta.reversed == 0
&& let Some(router) = routers.get(&meta.src_id) && let Some(router) = routers.get(&meta.src_id)
&& meta.dst_id == router.config.local_id && meta.dst_id == router.config.local_id
{ {
// let connection = Arc::new(connection); // let connection = Arc::new(connection);
// tcp listener 只许一个连接,过来新连接就把前一个关掉。 // tcp listener 只许一个连接,过来新连接就把前一个关掉。
{ {
let guard = pin(); let guard = pin();
let new_shared = Owned::new(connection).into_shared(&guard); let new_shared = Owned::new(connection).into_shared(&guard);
let old_shared = router.tcp_listener_connection.swap(new_shared, Ordering::AcqRel, &guard); let old_shared = router.tcp_listener_connection.swap(new_shared, Ordering::AcqRel, &guard);
// //
// SAFETY: this is guaranteed to still point to valid connection because // SAFETY: this is guaranteed to still point to valid connection because
// the guard is swapped with AcqRel so we are for sure tracked by the pin // the guard is swapped with AcqRel so we are for sure tracked by the pin
// list // list
// //
if let Some(old) = unsafe { old_shared.as_ref() } { if let Some(old) = unsafe { old_shared.as_ref() } {
let _ = old.shutdown(Shutdown::Both); let _ = old.shutdown(Shutdown::Both);
// SAFETY: At this point old_shared is guaranteed // SAFETY: At this point old_shared is guaranteed
// to be non-null (above if let checks that) // to be non-null (above if let checks that)
// And since it is already swapped out of the // And since it is already swapped out of the
// `tcp_listener_connection` no other thread // `tcp_listener_connection` no other thread
// should have access to it. // should have access to it.
unsafe { unsafe {
guard.defer_destroy(old_shared); guard.defer_destroy(old_shared);
} }
} }
} }
let _ = thread::scope(|s| { let _ = thread::scope(|s| {
s.spawn(|_| { s.spawn(|_| {
let guard = pin(); let guard = pin();
let shared = router.tcp_listener_connection.load(Ordering::Acquire, &guard); let shared = router.tcp_listener_connection.load(Ordering::Acquire, &guard);
// SAFETY: tcp_listener_connection shoud always either point to null or some valid thing // SAFETY: tcp_listener_connection shoud always either point to null or some valid thing
if let Some(connection) = unsafe { shared.as_ref() } { if let Some(connection) = unsafe { shared.as_ref() } {
router.handle_outbound_tcp(connection); router.handle_outbound_tcp(connection);
} }
}); });
s.spawn(|_| { s.spawn(|_| {
let guard = pin(); let guard = pin();
let shared = router.tcp_listener_connection.load(Ordering::Acquire, &guard); let shared = router.tcp_listener_connection.load(Ordering::Acquire, &guard);
// SAFETY: tcp_listener_connection shoud always either point to null or some valid thing // SAFETY: tcp_listener_connection shoud always either point to null or some valid thing
if let Some(connection) = unsafe { shared.as_ref() } { if let Some(connection) = unsafe { shared.as_ref() } {
router.handle_inbound_tcp(&connection); router.handle_inbound_tcp(&connection);
} }
}); });
}); });
} }
}); });
} }
}); });
} }
}) })
.unwrap(); .unwrap();
Ok(()) Ok(())
} }
use anyhow::{bail, ensure, Result}; use anyhow::{bail, ensure, Result};
use socket2::{Domain, Protocol, SockAddr, SockFilter, Socket, Type}; use socket2::{Domain, Protocol, SockAddr, SockFilter, Socket, Type};
use std::net::Shutdown; use std::net::Shutdown;
use std::{ use std::{
ffi::c_void, ffi::c_void,
mem::MaybeUninit, mem::MaybeUninit,
net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6, ToSocketAddrs}, net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6, ToSocketAddrs},
ops::Range, ops::Range,
os::fd::{AsRawFd, FromRawFd}, os::fd::{AsRawFd, FromRawFd},
process::{Command, ExitStatus}, process::{Command, ExitStatus},
sync::atomic::Ordering, sync::atomic::Ordering,
}; };
use tun::Device; use tun::Device;
use crate::config::{ConfigRouter, Schema}; use crate::config::{ConfigRouter, Schema};
use crossbeam::epoch::{pin, Atomic}; use crossbeam::epoch::{pin, Atomic};
use libc::{ use libc::{
setsockopt, sock_filter, sock_fprog, socklen_t, BPF_ABS, BPF_B, BPF_IND, BPF_JEQ, BPF_JMP, BPF_K, BPF_LD, BPF_LDX, BPF_MSH, BPF_RET, BPF_W, setsockopt, sock_filter, sock_fprog, socklen_t, BPF_ABS, BPF_B, BPF_IND, BPF_JEQ, BPF_JMP, BPF_K, BPF_LD, BPF_LDX, BPF_MSH, BPF_RET, BPF_W,
MSG_FASTOPEN, SOL_SOCKET, SO_ATTACH_REUSEPORT_CBPF, MSG_FASTOPEN, SOL_SOCKET, SO_ATTACH_REUSEPORT_CBPF,
}; };
pub const SECRET_LENGTH: usize = 32; pub const SECRET_LENGTH: usize = 32;
pub const META_SIZE: usize = size_of::<Meta>(); pub const META_SIZE: usize = size_of::<Meta>();
#[repr(C)] #[repr(C)]
#[derive(Debug, Clone, Copy, Default)] #[derive(Debug, Clone, Copy, Default)]
pub struct Meta { pub struct Meta {
pub src_id: u8, pub src_id: u8,
pub dst_id: u8, pub dst_id: u8,
pub reversed: u16, pub reversed: u16,
} }
impl Meta { impl Meta {
pub fn as_bytes(&self) -> &[u8; META_SIZE] { pub fn as_bytes(&self) -> &[u8; META_SIZE] {
unsafe { &*(self as *const Meta as *const [u8; META_SIZE]) } unsafe { &*(self as *const Meta as *const [u8; META_SIZE]) }
} }
pub fn from_bytes(bytes: &[MaybeUninit<u8>; META_SIZE]) -> &Meta { pub fn from_bytes(bytes: &[MaybeUninit<u8>; META_SIZE]) -> &Meta {
unsafe { &*(bytes.as_ptr() as *const Meta) } unsafe { &*(bytes.as_ptr() as *const Meta) }
} }
} }
pub struct Router { pub struct Router {
pub config: ConfigRouter, pub config: ConfigRouter,
pub tun: Device, pub tun: Device,
pub socket: Socket, pub socket: Socket,
pub endpoint: Atomic<SockAddr>, pub endpoint: Atomic<SockAddr>,
pub tcp_listener_connection: Atomic<Socket>, pub tcp_listener_connection: Atomic<Socket>,
} }
#[inline] #[inline]
fn xor_with_secret_offset<const L: usize>(data: &mut [u8], secret: &[u8; L], offset: usize) { fn xor_with_secret_offset<const L: usize>(data: &mut [u8], secret: &[u8; L], offset: usize) {
let len = data.len(); let len = data.len();
if len == 0 { return; } if len == 0 { return; }
let mut i = 0; let mut i = 0;
let mut key_pos = offset % L; let mut key_pos = offset % L;
// 1) 先把 key_pos 补到 0(也就是把相位对齐到块边界),这样后面能走“完整块” // 1) 先把 key_pos 补到 0(也就是把相位对齐到块边界),这样后面能走“完整块”
if key_pos != 0 { if key_pos != 0 {
let head = (L - key_pos).min(len); let head = (L - key_pos).min(len);
for j in 0..head { for j in 0..head {
data[j] ^= secret[key_pos + j]; // 这里不会越界,因为 j < L - key_pos data[j] ^= secret[key_pos + j]; // 这里不会越界,因为 j < L - key_pos
} }
i += head; i += head;
key_pos = 0; key_pos = 0;
} }
// 2) 处理完整块(key_pos 已经对齐到 0) // 2) 处理完整块(key_pos 已经对齐到 0)
while i + L <= len { while i + L <= len {
for j in 0..L { for j in 0..L {
data[i + j] ^= secret[j]; data[i + j] ^= secret[j];
} }
i += L; i += L;
} }
// 3) 处理尾部 // 3) 处理尾部
for j in 0..(len - i) { for j in 0..(len - i) {
data[i + j] ^= secret[j]; data[i + j] ^= secret[j];
} }
} }
impl Router { impl Router {
pub(crate) fn decrypt(&self, data: &mut [u8], secret: &[u8; SECRET_LENGTH]) { pub(crate) fn decrypt(&self, data: &mut [u8], secret: &[u8; SECRET_LENGTH]) {
xor_with_secret_offset::<SECRET_LENGTH>(data, secret, 0); xor_with_secret_offset::<SECRET_LENGTH>(data, secret, 0);
} }
pub(crate) fn decrypt2( pub(crate) fn decrypt2(
&self, &self,
data: &mut [u8], data: &mut [u8],
secret: &[u8; SECRET_LENGTH], secret: &[u8; SECRET_LENGTH],
range: Range<usize>, range: Range<usize>,
) { ) {
xor_with_secret_offset::<SECRET_LENGTH>(&mut data[range.clone()], secret, range.start); xor_with_secret_offset::<SECRET_LENGTH>(&mut data[range.clone()], secret, range.start);
} }
pub(crate) fn encrypt(&self, data: &mut [u8]) { pub(crate) fn encrypt(&self, data: &mut [u8]) {
xor_with_secret_offset::<SECRET_LENGTH>(data, &self.config.remote_secret, 0); xor_with_secret_offset::<SECRET_LENGTH>(data, &self.config.remote_secret, 0);
} }
pub fn create_socket(config: &ConfigRouter) -> Result<Socket> { pub fn create_socket(config: &ConfigRouter) -> Result<Socket> {
println!("create_socket {}", config.remote_id); println!("create_socket {}", config.remote_id);
match config.schema { match config.schema {
Schema::IP => { Schema::IP => {
let result = Socket::new(config.family, Type::RAW, Some(Protocol::from(config.proto as i32)))?; let result = Socket::new(config.family, Type::RAW, Some(Protocol::from(config.proto as i32)))?;
if config.mark != 0 { if config.mark != 0 {
result.set_mark(config.mark)?; result.set_mark(config.mark)?;
} }
Self::attach_filter_ip(config, &result)?; Self::attach_filter_ip(config, &result)?;
Ok(result) Ok(result)
} }
Schema::UDP => { Schema::UDP => {
let result = Socket::new(config.family, Type::DGRAM, Some(Protocol::UDP))?; let result = Socket::new(config.family, Type::DGRAM, Some(Protocol::UDP))?;
if config.mark != 0 { if config.mark != 0 {
result.set_mark(config.mark)?; result.set_mark(config.mark)?;
} }
if config.src_port != 0 { if config.src_port != 0 {
result.set_reuse_port(true)?; result.set_reuse_port(true)?;
let addr = Self::bind_addr(config); let addr = Self::bind_addr(config);
result.bind(&addr)?; result.bind(&addr)?;
} }
Ok(result) Ok(result)
} }
Schema::TCP => Ok(unsafe { Socket::from_raw_fd(0) }), Schema::TCP => Ok(unsafe { Socket::from_raw_fd(0) }),
} }
} }
pub fn listen_tcp(&self) -> Socket { pub fn listen_tcp(&self) -> Socket {
// listener // listener
let result = Socket::new(Domain::IPV6, Type::STREAM, Some(Protocol::TCP)).unwrap(); let result = Socket::new(Domain::IPV6, Type::STREAM, Some(Protocol::TCP)).unwrap();
result.set_reuse_address(true).unwrap(); result.set_reuse_address(true).unwrap();
let addr = SockAddr::from(SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, self.config.src_port, 0, 0)); let addr = SockAddr::from(SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, self.config.src_port, 0, 0));
result.bind(&addr).unwrap(); result.bind(&addr).unwrap();
result.listen(100).unwrap(); result.listen(100).unwrap();
result result
} }
pub fn connect_tcp(&self) -> Result<Socket> { pub fn connect_tcp(&self) -> Result<Socket> {
// tcp client 的 socket 不要在初始化时创建,在循环里创建 // tcp client 的 socket 不要在初始化时创建,在循环里创建
// 创建 socket 和 获取 endpoint 失败会 panic,连接失败会 error // 创建 socket 和 获取 endpoint 失败会 panic,连接失败会 error
let result = Socket::new(self.config.family, Type::STREAM, Some(Protocol::TCP)).unwrap(); let result = Socket::new(self.config.family, Type::STREAM, Some(Protocol::TCP)).unwrap();
result.set_tcp_nodelay(true).unwrap(); result.set_tcp_nodelay(true).unwrap();
if self.config.mark != 0 { if self.config.mark != 0 {
result.set_mark(self.config.mark).unwrap(); result.set_mark(self.config.mark).unwrap();
} }
if self.config.src_port != 0 { if self.config.src_port != 0 {
result.set_reuse_address(true).unwrap(); result.set_reuse_address(true).unwrap();
let addr = Self::bind_addr(&self.config); let addr = Self::bind_addr(&self.config);
result.bind(&addr)?; result.bind(&addr)?;
} }
let meta = Meta { let meta = Meta {
src_id: self.config.local_id, src_id: self.config.local_id,
dst_id: self.config.remote_id, dst_id: self.config.remote_id,
reversed: 0, reversed: 0,
}; };
let guard = pin(); let guard = pin();
let endpoint_ref = self.endpoint.load(Ordering::Relaxed, &guard); let endpoint_ref = self.endpoint.load(Ordering::Relaxed, &guard);
let endpoint = unsafe { endpoint_ref.as_ref() }.unwrap(); let endpoint = unsafe { endpoint_ref.as_ref() }.unwrap();
result.send_to_with_flags(meta.as_bytes(), endpoint, MSG_FASTOPEN)?; result.send_to_with_flags(meta.as_bytes(), endpoint, MSG_FASTOPEN)?;
Ok(result) Ok(result)
} }
fn attach_filter_ip(config: &ConfigRouter, socket: &Socket) -> Result<()> { fn attach_filter_ip(config: &ConfigRouter, socket: &Socket) -> Result<()> {
// 由于多个对端可能会使用相同的 ipprpto 号,这里确保每个 socket 上只会收到自己对应的对端发来的消息 // 由于多个对端可能会使用相同的 ipprpto 号,这里确保每个 socket 上只会收到自己对应的对端发来的消息
let meta = Meta { let meta = Meta {
src_id: config.remote_id, src_id: config.remote_id,
dst_id: config.local_id, dst_id: config.local_id,
reversed: 0, reversed: 0,
}; };
let value = u32::from_be_bytes(*meta.as_bytes()); let value = u32::from_be_bytes(*meta.as_bytes());
// 如果你的协议是 UDP,这里必须是 8 (跳过 UDP 头: SrcPort(2)+DstPort(2)+Len(2)+Sum(2)) // 如果你的协议是 UDP,这里必须是 8 (跳过 UDP 头: SrcPort(2)+DstPort(2)+Len(2)+Sum(2))
// 如果是纯自定义 IP 协议,这里是 0 // 如果是纯自定义 IP 协议,这里是 0
let payload_offset = 0; let payload_offset = 0;
// IP filter 工作原理: // IP filter 工作原理:
// 每个对端起一个 raw socket // 每个对端起一个 raw socket
// 根据报文内容判断是给谁的。拒绝掉不是给自己的报文 // 根据报文内容判断是给谁的。拒绝掉不是给自己的报文
// IPv4 raw socket 带 IP 头,IPv6 不带 // IPv4 raw socket 带 IP 头,IPv6 不带
let filters: &[SockFilter] = match socket.domain()? { let filters: &[SockFilter] = match socket.domain()? {
Domain::IPV4 => &[ Domain::IPV4 => &[
// [IPv4] 计算 IPv4 头长度: X = 4 * (IP[0] & 0xf) // [IPv4] 计算 IPv4 头长度: X = 4 * (IP[0] & 0xf)
bpf_stmt(BPF_LDX | BPF_B | BPF_MSH, 0), bpf_stmt(BPF_LDX | BPF_B | BPF_MSH, 0),
// A = Packet[X + payload_offset] // A = Packet[X + payload_offset]
bpf_stmt(BPF_LD | BPF_W | BPF_IND, payload_offset), bpf_stmt(BPF_LD | BPF_W | BPF_IND, payload_offset),
// if (A == target_val) goto Accept; else goto Reject; // if (A == target_val) goto Accept; else goto Reject;
bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, value, 0, 1), bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, value, 0, 1),
// 【接受 (True 路径)】 // 【接受 (True 路径)】
bpf_stmt(BPF_RET | BPF_K, u32::MAX), bpf_stmt(BPF_RET | BPF_K, u32::MAX),
// 【拒绝 (False 路径)】 // 【拒绝 (False 路径)】
bpf_stmt(BPF_RET | BPF_K, 0), bpf_stmt(BPF_RET | BPF_K, 0),
], ],
Domain::IPV6 => &[ Domain::IPV6 => &[
// raw socket IPv6 没有 header,加载第 0 字节到累加器 A // raw socket IPv6 没有 header,加载第 0 字节到累加器 A
bpf_stmt(BPF_LD | BPF_W | BPF_ABS, 0), bpf_stmt(BPF_LD | BPF_W | BPF_ABS, 0),
// if (A == target_val) goto Accept; else goto Reject; // if (A == target_val) goto Accept; else goto Reject;
bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, value, 0, 1), bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, value, 0, 1),
// 【接受 (True 路径)】 // 【接受 (True 路径)】
bpf_stmt(BPF_RET | BPF_K, u32::MAX), bpf_stmt(BPF_RET | BPF_K, u32::MAX),
// 【拒绝 (False 路径)】 // 【拒绝 (False 路径)】
bpf_stmt(BPF_RET | BPF_K, 0), bpf_stmt(BPF_RET | BPF_K, 0),
], ],
_ => bail!("unsupported family"), _ => bail!("unsupported family"),
}; };
socket.attach_filter(filters)?; socket.attach_filter(filters)?;
Ok(()) Ok(())
} }
pub fn attach_filter_udp(group: Vec<&Router>) -> Result<()> { pub fn attach_filter_udp(group: Vec<&Router>) -> Result<()> {
let values: Vec<u32> = group let values: Vec<u32> = group
.iter() .iter()
.map(|&f| { .map(|&f| {
let meta = Meta { let meta = Meta {
src_id: f.config.remote_id, src_id: f.config.remote_id,
dst_id: f.config.local_id, dst_id: f.config.local_id,
reversed: 0, reversed: 0,
}; };
u32::from_be_bytes(*meta.as_bytes()) u32::from_be_bytes(*meta.as_bytes())
}) })
.collect(); .collect();
let mut filters: Vec<SockFilter> = Vec::with_capacity(1 + values.len() * 2 + 1); let mut filters: Vec<SockFilter> = Vec::with_capacity(1 + values.len() * 2 + 1);
// udp filter 工作原理: // udp filter 工作原理:
// 每个对端起一个 udp socket // 每个对端起一个 udp socket
// 根据报文内容判断是给谁的,调度给对应的端口复用组序号 // 根据报文内容判断是给谁的,调度给对应的端口复用组序号
// Load the first 4 bytes of the packet into the accumulator (A) // Load the first 4 bytes of the packet into the accumulator (A)
filters.push(bpf_stmt(BPF_LD | BPF_W | BPF_ABS, 0)); filters.push(bpf_stmt(BPF_LD | BPF_W | BPF_ABS, 0));
for (i, &val) in values.iter().enumerate() { for (i, &val) in values.iter().enumerate() {
// 如果匹配,继续下一句(返回),如果不匹配,跳过下一句。 // 如果匹配,继续下一句(返回),如果不匹配,跳过下一句。
filters.push(bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, val, 0, 1)); filters.push(bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, val, 0, 1));
// If match, return the index (i + 1, since 0 means drop) // If match, return the index (i + 1, since 0 means drop)
filters.push(bpf_stmt(BPF_RET | BPF_K, i as u32)); filters.push(bpf_stmt(BPF_RET | BPF_K, i as u32));
} }
// If no match found after all comparisons, drop the packet // If no match found after all comparisons, drop the packet
filters.push(bpf_stmt(BPF_RET | BPF_K, u32::MAX)); filters.push(bpf_stmt(BPF_RET | BPF_K, u32::MAX));
let prog = sock_fprog { let prog = sock_fprog {
len: filters.len() as u16, len: filters.len() as u16,
filter: filters.as_mut_ptr() as *mut sock_filter, filter: filters.as_mut_ptr() as *mut sock_filter,
}; };
let fd = group[0].socket.as_raw_fd(); let fd = group[0].socket.as_raw_fd();
let ret = unsafe { let ret = unsafe {
setsockopt( setsockopt(
fd, fd,
SOL_SOCKET, SOL_SOCKET,
SO_ATTACH_REUSEPORT_CBPF, SO_ATTACH_REUSEPORT_CBPF,
&prog as *const _ as *const c_void, &prog as *const _ as *const c_void,
size_of_val(&prog) as socklen_t, size_of_val(&prog) as socklen_t,
) )
}; };
ensure!(ret != -1, std::io::Error::last_os_error()); ensure!(ret != -1, std::io::Error::last_os_error());
Ok(()) Ok(())
} }
pub(crate) fn handle_outbound_ip_udp(&self) { pub(crate) fn handle_outbound_ip_udp(&self) {
let mut buffer = [0u8; 1500]; let mut buffer = [0u8; 1500];
// Pre-initialize with our Meta header (local -> remote) // Pre-initialize with our Meta header (local -> remote)
let meta = Meta { let meta = Meta {
src_id: self.config.local_id, src_id: self.config.local_id,
dst_id: self.config.remote_id, dst_id: self.config.remote_id,
reversed: 0, reversed: 0,
}; };
buffer[..META_SIZE].copy_from_slice(meta.as_bytes()); buffer[..META_SIZE].copy_from_slice(meta.as_bytes());
loop { loop {
let n = self.tun.recv(&mut buffer[META_SIZE..]).unwrap(); // recv 失败直接 panic let n = self.tun.recv(&mut buffer[META_SIZE..]).unwrap(); // recv 失败直接 panic
let guard = pin(); let guard = pin();
let endpoint_ref = self.endpoint.load(Ordering::Relaxed, &guard); let endpoint_ref = self.endpoint.load(Ordering::Relaxed, &guard);
if let Some(endpoint) = unsafe { endpoint_ref.as_ref() } { if let Some(endpoint) = unsafe { endpoint_ref.as_ref() } {
self.encrypt(&mut buffer[META_SIZE..META_SIZE + n]); self.encrypt(&mut buffer[META_SIZE..META_SIZE + n]);
let _ = self.socket.send_to(&buffer[..META_SIZE + n], endpoint); let _ = self.socket.send_to(&buffer[..META_SIZE + n], endpoint);
} }
} }
} }
pub(crate) fn handle_inbound_ip_udp(&self) { pub(crate) fn handle_inbound_ip_udp(&self) {
let mut recv_buf = [MaybeUninit::uninit(); 1500]; let mut recv_buf = [MaybeUninit::uninit(); 1500];
loop { loop {
// 收到一个非法报文只丢弃一个报文 // 收到一个非法报文只丢弃一个报文
let (len, addr) = { self.socket.recv_from(&mut recv_buf).unwrap() }; // recv 出错直接 panic let (len, addr) = { self.socket.recv_from(&mut recv_buf).unwrap() }; // recv 出错直接 panic
let packet = unsafe { std::slice::from_raw_parts_mut(recv_buf.as_mut_ptr().cast(), len) }; let packet = unsafe { std::slice::from_raw_parts_mut(recv_buf.as_mut_ptr().cast(), len) };
// if addr.is_ipv6() { println!("{:X?}", packet) } // if addr.is_ipv6() { println!("{:X?}", packet) }
// 只有 ipv4 raw 会给 IP报头 // 只有 ipv4 raw 会给 IP报头
let offset = if self.config.family == Domain::IPV4 && self.config.schema == Schema::IP { let offset = if self.config.family == Domain::IPV4 && self.config.schema == Schema::IP {
(packet[0] & 0x0f) as usize * 4 (packet[0] & 0x0f) as usize * 4
} else { } else {
0 0
} + META_SIZE; } + META_SIZE;
{ {
let guard = pin(); let guard = pin();
let current_shared = self.endpoint.load(Ordering::Relaxed, &guard); let current_shared = self.endpoint.load(Ordering::Relaxed, &guard);
let is_same = unsafe { current_shared.as_ref() }.map(|c| *c == addr).unwrap_or(false); let is_same = unsafe { current_shared.as_ref() }.map(|c| *c == addr).unwrap_or(false);
if !is_same { if !is_same {
let new_shared = crossbeam::epoch::Owned::new(addr).into_shared(&guard); let new_shared = crossbeam::epoch::Owned::new(addr).into_shared(&guard);
let old_shared = self.endpoint.swap(new_shared, Ordering::Release, &guard); let old_shared = self.endpoint.swap(new_shared, Ordering::Release, &guard);
unsafe { guard.defer_destroy(old_shared) } unsafe { guard.defer_destroy(old_shared) }
} }
} }
let payload = &mut packet[offset..]; let payload = &mut packet[offset..];
self.decrypt(payload, &self.config.local_secret); self.decrypt(payload, &self.config.local_secret);
let _ = self.tun.send(payload); let _ = self.tun.send(payload);
} }
} }
pub(crate) fn handle_outbound_tcp(&self, connection: &Socket) { pub(crate) fn handle_outbound_tcp(&self, connection: &Socket) {
let _ = (|| -> Result<()> { let _ = (|| -> Result<()> {
let mut buffer = [0u8; 1500]; let mut buffer = [0u8; 1500];
loop { loop {
let n = self.tun.recv(&mut buffer)?; let n = self.tun.recv(&mut buffer)?;
self.encrypt(&mut buffer[..n]); self.encrypt(&mut buffer[..n]);
Router::send_all_tcp(&connection, &buffer[..n])?; Router::send_all_tcp(&connection, &buffer[..n])?;
} }
})(); })();
let _ = connection.shutdown(Shutdown::Both); let _ = connection.shutdown(Shutdown::Both);
} }
pub(crate) fn handle_inbound_tcp(&self, connection: &Socket) { pub(crate) fn handle_inbound_tcp(&self, connection: &Socket) {
let _ = (|| -> Result<()> { let _ = (|| -> Result<()> {
let mut buf = [MaybeUninit::uninit(); 1500]; let mut buf = [MaybeUninit::uninit(); 1500];
let packet: &mut [u8] = unsafe { std::slice::from_raw_parts_mut(buf.as_mut_ptr().cast(), buf.len()) }; let packet: &mut [u8] = unsafe { std::slice::from_raw_parts_mut(buf.as_mut_ptr().cast(), buf.len()) };
loop { loop {
Router::recv_exact_tcp(&connection, &mut buf[0..6])?; Router::recv_exact_tcp(&connection, &mut buf[0..6])?;
self.decrypt2(packet, &self.config.local_secret, 0..6); self.decrypt2(packet, &self.config.local_secret, 0..6);
let version = packet[0] >> 4; let version = packet[0] >> 4;
let total_len = match version { let total_len = match version {
4 => u16::from_be_bytes([packet[2], packet[3]]) as usize, 4 => u16::from_be_bytes([packet[2], packet[3]]) as usize,
6 => u16::from_be_bytes([packet[4], packet[5]]) as usize + 40, 6 => u16::from_be_bytes([packet[4], packet[5]]) as usize + 40,
_ => bail!("Invalid IP version"), _ => bail!("Invalid IP version"),
}; };
ensure!(6 < total_len && total_len <= buf.len(), "Invalid total length"); ensure!(6 < total_len && total_len <= buf.len(), "Invalid total length");
Router::recv_exact_tcp(&connection, &mut buf[6..total_len])?; Router::recv_exact_tcp(&connection, &mut buf[6..total_len])?;
self.decrypt2(packet, &self.config.local_secret, 6..total_len); self.decrypt2(packet, &self.config.local_secret, 6..total_len);
self.tun.send(&packet[..total_len])?; self.tun.send(&packet[..total_len])?;
} }
})(); })();
let _ = connection.shutdown(Shutdown::Both); let _ = connection.shutdown(Shutdown::Both);
} }
pub(crate) fn recv_exact_tcp(sock: &Socket, mut buf: &mut [MaybeUninit<u8>]) -> Result<()> { pub(crate) fn recv_exact_tcp(sock: &Socket, mut buf: &mut [MaybeUninit<u8>]) -> Result<()> {
while !buf.is_empty() { while !buf.is_empty() {
let n = sock.recv(buf)?; let n = sock.recv(buf)?;
ensure!(n != 0, std::io::ErrorKind::UnexpectedEof); ensure!(n != 0, std::io::ErrorKind::UnexpectedEof);
buf = &mut buf[n..]; buf = &mut buf[n..];
} }
Ok(()) Ok(())
} }
pub(crate) fn send_all_tcp(sock: &Socket, mut buf: &[u8]) -> Result<()> { pub(crate) fn send_all_tcp(sock: &Socket, mut buf: &[u8]) -> Result<()> {
while !buf.is_empty() { while !buf.is_empty() {
let n = sock.send(buf)?; let n = sock.send(buf)?;
buf = &buf[n..]; buf = &buf[n..];
} }
Ok(()) Ok(())
} }
fn create_tun_device(config: &ConfigRouter) -> Result<Device> { fn create_tun_device(config: &ConfigRouter) -> Result<Device> {
println!("create_tun_device {}", config.remote_id); println!("create_tun_device {}", config.remote_id);
let mut tun_config = tun::Configuration::default(); let mut tun_config = tun::Configuration::default();
tun_config.tun_name(config.dev.as_str()).up(); tun_config.tun_name(config.dev.as_str()).up();
let dev = tun::create(&tun_config)?; let dev = tun::create(&tun_config)?;
Ok(dev) Ok(dev)
} }
fn run_up_script(config: &ConfigRouter) -> Result<ExitStatus> { fn run_up_script(config: &ConfigRouter) -> Result<ExitStatus> {
Ok(Command::new("sh").args(["-c", config.up.as_str()]).status()?) Ok(Command::new("sh").args(["-c", config.up.as_str()]).status()?)
} }
fn create_endpoint(config: &ConfigRouter) -> Atomic<SockAddr> { fn create_endpoint(config: &ConfigRouter) -> Atomic<SockAddr> {
println!("create_endpoint {}", config.remote_id); println!("create_endpoint {}", config.remote_id);
match (config.endpoint.clone(), config.dst_port) match (config.endpoint.clone(), config.dst_port)
.to_socket_addrs() .to_socket_addrs()
.unwrap_or_default() .unwrap_or_default()
.filter(|a| match config.family { .filter(|a| match config.family {
Domain::IPV4 => a.is_ipv4(), Domain::IPV4 => a.is_ipv4(),
Domain::IPV6 => a.is_ipv6(), Domain::IPV6 => a.is_ipv6(),
_ => false, _ => false,
}) })
.next() .next()
{ {
None => Atomic::null(), None => Atomic::null(),
Some(addr) => Atomic::new(addr.into()), Some(addr) => Atomic::new(addr.into()),
} }
} }
pub fn new(config: ConfigRouter) -> Result<Router> { pub fn new(config: ConfigRouter) -> Result<Router> {
println!("creating {}", config.remote_id); println!("creating {}", config.remote_id);
let router = Router { let router = Router {
tun: Self::create_tun_device(&config)?, tun: Self::create_tun_device(&config)?,
endpoint: Self::create_endpoint(&config), endpoint: Self::create_endpoint(&config),
socket: Self::create_socket(&config)?, socket: Self::create_socket(&config)?,
tcp_listener_connection: Atomic::null(), tcp_listener_connection: Atomic::null(),
config, config,
}; };
println!("run_up_script {}", &router.config.remote_id); println!("run_up_script {}", &router.config.remote_id);
Self::run_up_script(&router.config)?; Self::run_up_script(&router.config)?;
Ok(router) Ok(router)
} }
fn bind_addr(config: &ConfigRouter) -> SockAddr { fn bind_addr(config: &ConfigRouter) -> SockAddr {
match config.family { match config.family {
Domain::IPV4 => SockAddr::from(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, config.src_port)), Domain::IPV4 => SockAddr::from(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, config.src_port)),
Domain::IPV6 => SockAddr::from(SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, config.src_port, 0, 0)), Domain::IPV6 => SockAddr::from(SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, config.src_port, 0, 0)),
_ => panic!("unsupported family"), _ => panic!("unsupported family"),
} }
} }
} }
fn bpf_stmt(code: u32, k: u32) -> SockFilter { fn bpf_stmt(code: u32, k: u32) -> SockFilter {
SockFilter::new(code as u16, 0, 0, k) SockFilter::new(code as u16, 0, 0, k)
} }
fn bpf_jump(code: u32, k: u32, jt: u8, jf: u8) -> SockFilter { fn bpf_jump(code: u32, k: u32, jt: u8, jf: u8) -> SockFilter {
SockFilter::new(code as u16, jt, jf, k) SockFilter::new(code as u16, jt, jf, k)
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment