Commit 7dcbbb26 authored by nanamicat's avatar nanamicat

rust-next

parent f5ffc9fe
Pipeline #41903 failed with stages
in 38 seconds
/.idea/ /.idea/
/target /target
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
This diff is collapsed.
...@@ -4,12 +4,11 @@ version = "0.1.0" ...@@ -4,12 +4,11 @@ version = "0.1.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
tun = "0.7" tun = "0.8"
socket2 = { version = "0.5.8", features = ["all"] } socket2 = { version = "0.6.1", features = ["all"] }
pnet = "0.35.0" serde = { version = "1.0.228", features = ["derive"] }
serde = { version = "1.0.217", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
base64 = "0.22.1" base64 = "0.22.1"
crossbeam = "0.8.4" crossbeam = "0.8.4"
crossbeam-utils = "0.8.20" crossbeam-utils = "0.8.21"
grouping_by = "0.2.2" libc = "0.2.178"
FROM rust:1.84-alpine3.21 as builder FROM rust:1.84-alpine3.21 as builder
RUN apk add --no-cache musl-dev RUN apk add --no-cache musl-dev
WORKDIR /usr/src/app WORKDIR /usr/src/app
COPY Cargo.toml Cargo.lock ./ COPY Cargo.toml Cargo.lock ./
RUN mkdir src && \ RUN mkdir src && \
echo 'fn main() {}' > src/main.rs && \ echo 'fn main() {}' > src/main.rs && \
cargo build --release && \ cargo build --release && \
cargo clean --package $(awk '/name/ {gsub(/"/,""); print $3}' Cargo.toml | sed ':a;N;$!ba;s/\n//g' | tr -d '\r') && \ echo $(awk '/name/ {gsub(/"/,""); print $3}' Cargo.toml | sed ':a;N;$!ba;s/\n//g' | tr -d '\r') && \
rm -rf src cargo clean --package $(awk '/name/ {gsub(/"/,""); print $3}' Cargo.toml | sed ':a;N;$!ba;s/\n//g' | tr -d '\r') && \
rm -rf src
COPY src src
RUN cargo build --release COPY src src
RUN cargo build --release
FROM alpine:3.21
RUN apk --no-cache add libgcc libstdc++ bash iproute2 iptables iptables-legacy ipset netcat-openbsd jq FROM alpine:3.22
COPY --from=builder /usr/src/app/target/release/tun1 /usr/local/bin/tun RUN apk --no-cache add libgcc libstdc++ bash iproute2 iptables iptables-legacy ipset netcat-openbsd jq
COPY ./entrypoint.sh /entrypoint.sh COPY --from=builder /usr/src/app/target/release/tun1 /usr/local/bin/tun
COPY ./entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
CMD ["tun"] ENTRYPOINT ["/entrypoint.sh"]
CMD ["tun"]
#!/bin/bash #!/bin/bash
pid=0 pid=0
down=$(echo "$1" | jq -r '.routers | .[].down') down=$(echo "$1" | jq -r '.routers | .[].down')
echo "$down" echo "$down"
run_stop() { run_stop() {
signalCode=$1 signalCode=$1
if [ -n "$down" ]; then if [ -n "$down" ]; then
eval "$down" eval "$down"
fi fi
if [ $pid -ne 0 ]; then if [ $pid -ne 0 ]; then
kill "-$signalCode" "$pid" kill "-$signalCode" "$pid"
wait "$pid" wait "$pid"
fi fi
exit $((128 + signalCode)); exit $((128 + signalCode));
} }
trap 'kill ${!}; run_stop 2' SIGTERM trap 'kill ${!}; run_stop 2' SIGTERM
trap 'kill ${!}; run_stop 15' SIGINT trap 'kill ${!}; run_stop 15' SIGINT
tun "$@" & tun "$@" &
pid="$!" pid="$!"
# wait forever # wait forever
wait "$pid" wait "$pid"
pid=0 pid=0
run_stop 1 run_stop 1
mod router; mod router;
use crate::router::{Router, SECRET_LENGTH};
use crate::router::{Router, RouterReader, RouterWriter, SECRET_LENGTH}; use std::env;
use std::collections::HashMap; use std::error::Error;
use std::env; use std::io::{Read, Write};
use std::error::Error; use std::mem::MaybeUninit;
use std::intrinsics::transmute; use std::mem::{size_of, transmute};
use std::io::{Read, Write}; use std::sync::atomic::Ordering;
use std::mem::MaybeUninit; use crossbeam_utils::thread;
use std::sync::Arc;
#[repr(C)]
#[repr(C)] 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, }
}
use serde::Deserialize;
use serde::Deserialize;
#[derive(Deserialize)]
#[derive(Deserialize)] pub struct Config {
pub struct ConfigRouter { pub local_id: u8,
pub remote_id: u8, pub local_secret: String,
pub proto: i32, pub routers: Vec<ConfigRouter>,
pub family: u8, }
pub mark: u32, #[derive(Deserialize)]
pub endpoint: String, pub struct ConfigRouter {
pub remote_secret: String, pub remote_id: u8,
pub dev: String, pub proto: u8,
pub up: String, pub family: u8,
} pub mark: u32,
pub endpoint: String,
#[derive(Deserialize)] pub remote_secret: String,
pub struct Config { pub dev: String,
pub local_id: u8, pub up: String,
pub local_secret: String, }
pub routers: Vec<ConfigRouter>,
} fn main() -> Result<(), Box<dyn Error>> {
use crossbeam_utils::thread; println!("Starting");
use grouping_by::GroupingBy; let config: Config = serde_json::from_str(env::args().nth(1).ok_or("need param")?.as_str())?;
use pnet::packet::ipv4::Ipv4Packet; let local_secret: [u8; SECRET_LENGTH] = Router::create_secret(config.local_secret.as_str())?;
use socket2::Socket; let routers: Vec<Router> = config
.routers
fn main() -> Result<(), Box<dyn Error>> { .into_iter()
let config: Config = serde_json::from_str(env::args().nth(1).ok_or("need param")?.as_str())?; .map(|c| Router::new(c, config.local_id))
let local_secret: [u8; SECRET_LENGTH] = Router::create_secret(config.local_secret.as_str())?; .collect::<Result<Vec<_>, _>>()?;
let mut sockets: HashMap<u16, Arc<Socket>> = HashMap::new();
let routers: HashMap<u8, Router> = config println!("created tuns");
.routers const META_SIZE: usize = size_of::<Meta>();
.iter()
.map(|c| Router::new(c, &mut sockets).map(|router| (c.remote_id, router))) thread::scope(|s| {
.collect::<Result<_, _>>()?; for router in routers {
let (mut router_readers, router_writers): ( let (mut reader, mut writer) = router.split();
HashMap<u8, RouterReader>,
HashMap<u8, RouterWriter>, s.spawn(move |_| {
) = routers let mut buffer = [0u8; 1500 - 20]; // minus typical IP header space
.into_iter()
.map(|(id, router)| { // Pre-initialize with our Meta header (local -> remote)
let (reader, writer) = router.split(); let meta = Meta {
((id, reader), (id, writer)) src_id: config.local_id,
}) dst_id: reader.config.remote_id,
.unzip(); reversed: 0,
let router_writers3: Vec<(Arc<Socket>, HashMap<u8, RouterWriter>)> = router_writers };
.into_iter() // Turn the Meta struct into bytes
.grouping_by(|(_, v)| v.key()) let meta_bytes: &[u8; META_SIZE] =
.into_iter() unsafe { &*(&meta as *const Meta as *const [u8; META_SIZE]) };
.map(|(k, v)| { buffer[..META_SIZE].copy_from_slice(meta_bytes);
(
Arc::clone(sockets.get_mut(&k).unwrap()), loop {
v.into_iter().collect(), let n = reader.tun_reader.read(&mut buffer[META_SIZE..]).unwrap();
) let guard = crossbeam::epoch::pin();
}) let shared = reader.endpoint.load(Ordering::Acquire, &guard);
.collect(); if let Some(addr) = unsafe { shared.as_ref() } {
println!("created tuns"); reader.encrypt(&mut buffer[META_SIZE..META_SIZE + n]);
let _ = reader.socket.send_to(&buffer[..META_SIZE + n], addr);
thread::scope(|s| { }
for router in router_readers.values_mut() { }
s.spawn(|_| { });
let mut buffer = [0u8; 1500 - 20]; // minus typical IP header space
let meta_size = size_of::<Meta>(); s.spawn(move |_| {
let mut recv_buf = [MaybeUninit::uninit(); 1500];
// Pre-initialize with our Meta header (local -> remote) loop {
let meta = Meta { let _ = (|| {
src_id: config.local_id, let (len, addr) = writer.socket.recv_from(&mut recv_buf).unwrap();
dst_id: router.config.remote_id,
reversed: 0, let guard = crossbeam::epoch::pin();
}; let current_shared = writer.endpoint.load(Ordering::SeqCst, &guard);
// Turn the Meta struct into bytes let is_same = unsafe { current_shared.as_ref() }
let meta_bytes = unsafe { .map(|c| *c == addr)
std::slice::from_raw_parts(&meta as *const Meta as *const u8, meta_size) .unwrap_or(false);
}; if !is_same {
buffer[..meta_size].copy_from_slice(meta_bytes); let new_shared = crossbeam::epoch::Owned::new(addr).into_shared(&guard);
let old_shared =
loop { writer.endpoint.swap(new_shared, Ordering::SeqCst, &guard);
let n = router.tun_reader.read(&mut buffer[meta_size..]).unwrap(); unsafe {
if let Some(ref addr) = *router.endpoint.read().unwrap() { guard.defer_destroy(old_shared);
router.encrypt(&mut buffer[meta_size..meta_size + n]); }
#[cfg(target_os = "linux")] }
let _ = router.socket.set_mark(router.config.mark);
let _ = router.socket.send_to(&buffer[..meta_size + n], addr); let packet: &mut [u8] = unsafe { transmute(&mut recv_buf[..len]) };
} let version = packet[0] >> 4;
} let ip_header_len = match version {
}); 4 => (packet[0] & 0x0f) as usize * 4,
} 6 => 40,
_ => Err(format!("Unknown IP version: {}", version))?,
for (socket, mut router_writers) in router_writers3 { };
s.spawn(move |_| { let offset = ip_header_len + META_SIZE;
let mut recv_buf = [MaybeUninit::uninit(); 1500];
loop { let payload = &mut packet[offset..];
let _ = (|| { writer.decrypt(payload, &local_secret);
let (len, addr) = socket.recv_from(&mut recv_buf).unwrap(); writer.tun_writer.write_all(payload)?;
let data: &mut [u8] = unsafe { transmute(&mut recv_buf[..len]) };
Ok::<(), Box<dyn Error>>(())
let packet = Ipv4Packet::new(data).ok_or("malformed packet")?; })();
let header_len = packet.get_header_length() as usize * 4; }
let (_ip_header, rest) = data });
.split_at_mut_checked(header_len) }
.ok_or("malformed packet")?; })
let (meta_bytes, payload) = rest .unwrap();
.split_at_mut_checked(size_of::<Meta>()) Ok(())
.ok_or("malformed packet")?; }
let meta: &Meta = unsafe { transmute(meta_bytes.as_ptr()) };
if meta.dst_id == config.local_id && meta.reversed == 0 {
let router = router_writers
.get_mut(&meta.src_id)
.ok_or("missing router")?;
*router.endpoint.write().unwrap() = Some(addr);
router.decrypt(payload, &local_secret);
router.tun_writer.write_all(payload)?;
}
Ok::<(), Box<dyn Error>>(())
})();
}
});
}
})
.unwrap();
Ok(())
}
This diff is collapsed.
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