mirror of
https://github.com/EasyTier/EasyTier.git
synced 2024-11-16 11:42:27 +08:00
add complete support for freebsd (#275)
add tun & websocket & wireguard support on freebsd
This commit is contained in:
parent
31b26222d3
commit
89b43684d8
53
.github/workflows/core.yml
vendored
53
.github/workflows/core.yml
vendored
|
@ -70,6 +70,11 @@ jobs:
|
||||||
OS: windows-latest
|
OS: windows-latest
|
||||||
ARTIFACT_NAME: windows-x86_64
|
ARTIFACT_NAME: windows-x86_64
|
||||||
|
|
||||||
|
- TARGET: x86_64-unknown-freebsd
|
||||||
|
OS: ubuntu-latest
|
||||||
|
ARTIFACT_NAME: freebsd-13.2-x86_64
|
||||||
|
BSD_VERSION: 13.2
|
||||||
|
|
||||||
runs-on: ${{ matrix.OS }}
|
runs-on: ${{ matrix.OS }}
|
||||||
env:
|
env:
|
||||||
NAME: easytier
|
NAME: easytier
|
||||||
|
@ -81,10 +86,6 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: 21
|
|
||||||
|
|
||||||
- name: Cargo cache
|
- name: Cargo cache
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
|
@ -93,9 +94,6 @@ jobs:
|
||||||
./target
|
./target
|
||||||
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
|
||||||
- name: Install rust target
|
|
||||||
run: bash ./.github/workflows/install_rust.sh
|
|
||||||
|
|
||||||
- name: Setup protoc
|
- name: Setup protoc
|
||||||
uses: arduino/setup-protoc@v2
|
uses: arduino/setup-protoc@v2
|
||||||
with:
|
with:
|
||||||
|
@ -103,13 +101,52 @@ jobs:
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Build Core & Cli
|
- name: Build Core & Cli
|
||||||
|
if: ${{ ! endsWith(matrix.TARGET, 'freebsd') }}
|
||||||
run: |
|
run: |
|
||||||
|
bash ./.github/workflows/install_rust.sh
|
||||||
if [[ $OS =~ ^ubuntu.*$ && $TARGET =~ ^mips.*$ ]]; then
|
if [[ $OS =~ ^ubuntu.*$ && $TARGET =~ ^mips.*$ ]]; then
|
||||||
cargo +nightly build -r --verbose --target $TARGET -Z build-std=std,panic_abort --no-default-features --features mips
|
cargo +nightly build -r --verbose --target $TARGET -Z build-std=std,panic_abort --no-default-features --features mips
|
||||||
else
|
else
|
||||||
cargo build --release --verbose --target $TARGET
|
cargo build --release --verbose --target $TARGET
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Copied and slightly modified from @lmq8267 (https://github.com/lmq8267)
|
||||||
|
- name: Build Core & Cli (X86_64 FreeBSD)
|
||||||
|
uses: cross-platform-actions/action@v0.23.0
|
||||||
|
if: ${{ endsWith(matrix.TARGET, 'freebsd') }}
|
||||||
|
env:
|
||||||
|
TARGET: ${{ matrix.TARGET }}
|
||||||
|
with:
|
||||||
|
operating_system: freebsd
|
||||||
|
environment_variables: TARGET
|
||||||
|
architecture: x86-64
|
||||||
|
version: ${{ matrix.BSD_VERSION }}
|
||||||
|
shell: bash
|
||||||
|
memory: 5G
|
||||||
|
cpu_count: 4
|
||||||
|
run: |
|
||||||
|
uname -a
|
||||||
|
echo $SHELL
|
||||||
|
pwd
|
||||||
|
ls -lah
|
||||||
|
whoami
|
||||||
|
env | sort
|
||||||
|
|
||||||
|
sudo pkg install -y git protobuf
|
||||||
|
curl --proto 'https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||||
|
source $HOME/.cargo/env
|
||||||
|
|
||||||
|
rustup set auto-self-update disable
|
||||||
|
|
||||||
|
rustup install 1.77
|
||||||
|
rustup default 1.77
|
||||||
|
|
||||||
|
export CC=clang
|
||||||
|
export CXX=clang++
|
||||||
|
export CARGO_TERM_COLOR=always
|
||||||
|
|
||||||
|
cargo build --release --verbose --target $TARGET
|
||||||
|
|
||||||
- name: Install UPX
|
- name: Install UPX
|
||||||
if: ${{ matrix.OS != 'macos-latest' }}
|
if: ${{ matrix.OS != 'macos-latest' }}
|
||||||
uses: crazy-max/ghaction-upx@v3
|
uses: crazy-max/ghaction-upx@v3
|
||||||
|
@ -132,7 +169,7 @@ jobs:
|
||||||
TAG=$GITHUB_SHA
|
TAG=$GITHUB_SHA
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ $OS =~ ^ubuntu.*$ ]]; then
|
if [[ $OS =~ ^ubuntu.*$ && ! $TARGET =~ ^.*freebsd$ ]]; then
|
||||||
upx --lzma --best ./target/$TARGET/release/easytier-core"$SUFFIX"
|
upx --lzma --best ./target/$TARGET/release/easytier-core"$SUFFIX"
|
||||||
upx --lzma --best ./target/$TARGET/release/easytier-cli"$SUFFIX"
|
upx --lzma --best ./target/$TARGET/release/easytier-cli"$SUFFIX"
|
||||||
fi
|
fi
|
||||||
|
|
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -588,9 +588,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "boringtun-easytier"
|
name = "boringtun-easytier"
|
||||||
version = "0.6.0"
|
version = "0.6.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8a62bfb866a2a03e8aea22e83a0c1e385304563ee77c89ebd2043c67d0a73065"
|
checksum = "2f09b4d1ada8affba260cb185bbdf6d5acff42f924dea1a17f938cf3e8fbe475"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aead",
|
"aead",
|
||||||
"atomic-shim",
|
"atomic-shim",
|
||||||
|
|
|
@ -142,7 +142,10 @@ network-interface = "2.0"
|
||||||
# for ospf route
|
# for ospf route
|
||||||
petgraph = "0.6.5"
|
petgraph = "0.6.5"
|
||||||
|
|
||||||
boringtun = { package = "boringtun-easytier", version = "0.6.0", optional = true } # for encryption
|
# for wireguard
|
||||||
|
boringtun = { package = "boringtun-easytier", version = "0.6.1", optional = true }
|
||||||
|
|
||||||
|
# for encryption
|
||||||
ring = { version = "0.17", optional = true }
|
ring = { version = "0.17", optional = true }
|
||||||
bitflags = "2.5"
|
bitflags = "2.5"
|
||||||
aes-gcm = { version = "0.10.3", optional = true }
|
aes-gcm = { version = "0.10.3", optional = true }
|
||||||
|
@ -219,7 +222,6 @@ full = [
|
||||||
"socks5",
|
"socks5",
|
||||||
]
|
]
|
||||||
mips = ["aes-gcm", "mimalloc", "wireguard", "tun", "smoltcp", "socks5"]
|
mips = ["aes-gcm", "mimalloc", "wireguard", "tun", "smoltcp", "socks5"]
|
||||||
bsd = ["aes-gcm", "mimalloc", "smoltcp", "socks5"]
|
|
||||||
wireguard = ["dep:boringtun", "dep:ring"]
|
wireguard = ["dep:boringtun", "dep:ring"]
|
||||||
quic = ["dep:quinn", "dep:rustls", "dep:rcgen"]
|
quic = ["dep:quinn", "dep:rustls", "dep:rcgen"]
|
||||||
mimalloc = ["dep:mimalloc-rust"]
|
mimalloc = ["dep:mimalloc-rust"]
|
||||||
|
|
|
@ -399,7 +399,7 @@ pub struct DummyIfConfiger {}
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl IfConfiguerTrait for DummyIfConfiger {}
|
impl IfConfiguerTrait for DummyIfConfiger {}
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
|
||||||
pub type IfConfiger = MacIfConfiger;
|
pub type IfConfiger = MacIfConfiger;
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
|
@ -408,5 +408,10 @@ pub type IfConfiger = LinuxIfConfiger;
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
pub type IfConfiger = WindowsIfConfiger;
|
pub type IfConfiger = WindowsIfConfiger;
|
||||||
|
|
||||||
#[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "windows")))]
|
#[cfg(not(any(
|
||||||
|
target_os = "macos",
|
||||||
|
target_os = "linux",
|
||||||
|
target_os = "windows",
|
||||||
|
target_os = "freebsd"
|
||||||
|
)))]
|
||||||
pub type IfConfiger = DummyIfConfiger;
|
pub type IfConfiger = DummyIfConfiger;
|
||||||
|
|
|
@ -60,7 +60,9 @@ impl InterfaceFilter {
|
||||||
|
|
||||||
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
|
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
|
||||||
impl InterfaceFilter {
|
impl InterfaceFilter {
|
||||||
async fn is_interface_physical(interface_name: &str) -> bool {
|
#[cfg(target_os = "macos")]
|
||||||
|
async fn is_interface_physical(&self) -> bool {
|
||||||
|
let interface_name = &self.iface.name;
|
||||||
let output = tokio::process::Command::new("networksetup")
|
let output = tokio::process::Command::new("networksetup")
|
||||||
.args(&["-listallhardwareports"])
|
.args(&["-listallhardwareports"])
|
||||||
.output()
|
.output()
|
||||||
|
@ -87,11 +89,17 @@ impl InterfaceFilter {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "freebsd")]
|
||||||
|
async fn is_interface_physical(&self) -> bool {
|
||||||
|
// if mac addr is not zero, then it's physical interface
|
||||||
|
self.iface.mac.map(|mac| !mac.is_zero()).unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
async fn filter_iface(&self) -> bool {
|
async fn filter_iface(&self) -> bool {
|
||||||
!self.iface.is_point_to_point()
|
!self.iface.is_point_to_point()
|
||||||
&& !self.iface.is_loopback()
|
&& !self.iface.is_loopback()
|
||||||
&& self.iface.is_up()
|
&& self.iface.is_up()
|
||||||
&& Self::is_interface_physical(&self.iface.name).await
|
&& self.is_interface_physical().await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
pub mod instance;
|
pub mod instance;
|
||||||
pub mod listeners;
|
pub mod listeners;
|
||||||
|
|
||||||
#[cfg(feature = "tun")]
|
|
||||||
pub mod tun_codec;
|
|
||||||
#[cfg(feature = "tun")]
|
#[cfg(feature = "tun")]
|
||||||
pub mod virtual_nic;
|
pub mod virtual_nic;
|
||||||
|
|
|
@ -1,179 +0,0 @@
|
||||||
use std::io;
|
|
||||||
|
|
||||||
use byteorder::{NativeEndian, NetworkEndian, WriteBytesExt};
|
|
||||||
use tokio_util::bytes::{BufMut, Bytes, BytesMut};
|
|
||||||
use tokio_util::codec::{Decoder, Encoder};
|
|
||||||
|
|
||||||
/// A packet protocol IP version
|
|
||||||
#[derive(Debug, Clone, Copy, Default)]
|
|
||||||
enum PacketProtocol {
|
|
||||||
#[default]
|
|
||||||
IPv4,
|
|
||||||
IPv6,
|
|
||||||
Other(u8),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: the protocol in the packet information header is platform dependent.
|
|
||||||
impl PacketProtocol {
|
|
||||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
|
||||||
fn into_pi_field(self) -> Result<u16, io::Error> {
|
|
||||||
use nix::libc;
|
|
||||||
match self {
|
|
||||||
PacketProtocol::IPv4 => Ok(libc::ETH_P_IP as u16),
|
|
||||||
PacketProtocol::IPv6 => Ok(libc::ETH_P_IPV6 as u16),
|
|
||||||
PacketProtocol::Other(_) => Err(io::Error::new(
|
|
||||||
io::ErrorKind::Other,
|
|
||||||
"neither an IPv4 nor IPv6 packet",
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
|
||||||
fn into_pi_field(self) -> Result<u16, io::Error> {
|
|
||||||
use nix::libc;
|
|
||||||
match self {
|
|
||||||
PacketProtocol::IPv4 => Ok(libc::PF_INET as u16),
|
|
||||||
PacketProtocol::IPv6 => Ok(libc::PF_INET6 as u16),
|
|
||||||
PacketProtocol::Other(_) => Err(io::Error::new(
|
|
||||||
io::ErrorKind::Other,
|
|
||||||
"neither an IPv4 nor IPv6 packet",
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
fn into_pi_field(self) -> Result<u16, io::Error> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum TunPacketBuffer {
|
|
||||||
Bytes(Bytes),
|
|
||||||
BytesMut(BytesMut),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<TunPacketBuffer> for Bytes {
|
|
||||||
fn from(buf: TunPacketBuffer) -> Self {
|
|
||||||
match buf {
|
|
||||||
TunPacketBuffer::Bytes(bytes) => bytes,
|
|
||||||
TunPacketBuffer::BytesMut(bytes) => bytes.freeze(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsRef<[u8]> for TunPacketBuffer {
|
|
||||||
fn as_ref(&self) -> &[u8] {
|
|
||||||
match self {
|
|
||||||
TunPacketBuffer::Bytes(bytes) => bytes.as_ref(),
|
|
||||||
TunPacketBuffer::BytesMut(bytes) => bytes.as_ref(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A Tun Packet to be sent or received on the TUN interface.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct TunPacket(PacketProtocol, TunPacketBuffer);
|
|
||||||
|
|
||||||
/// Infer the protocol based on the first nibble in the packet buffer.
|
|
||||||
fn infer_proto(buf: &[u8]) -> PacketProtocol {
|
|
||||||
match buf[0] >> 4 {
|
|
||||||
4 => PacketProtocol::IPv4,
|
|
||||||
6 => PacketProtocol::IPv6,
|
|
||||||
p => PacketProtocol::Other(p),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TunPacket {
|
|
||||||
/// Create a new `TunPacket` based on a byte slice.
|
|
||||||
pub fn new(buffer: TunPacketBuffer) -> TunPacket {
|
|
||||||
let proto = infer_proto(buffer.as_ref());
|
|
||||||
TunPacket(proto, buffer)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return this packet's bytes.
|
|
||||||
pub fn get_bytes(&self) -> &[u8] {
|
|
||||||
match &self.1 {
|
|
||||||
TunPacketBuffer::Bytes(bytes) => bytes.as_ref(),
|
|
||||||
TunPacketBuffer::BytesMut(bytes) => bytes.as_ref(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn into_bytes(self) -> Bytes {
|
|
||||||
match self.1 {
|
|
||||||
TunPacketBuffer::Bytes(bytes) => bytes,
|
|
||||||
TunPacketBuffer::BytesMut(bytes) => bytes.freeze(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn into_bytes_mut(self) -> BytesMut {
|
|
||||||
match self.1 {
|
|
||||||
TunPacketBuffer::Bytes(_) => panic!("cannot into_bytes_mut from bytes"),
|
|
||||||
TunPacketBuffer::BytesMut(bytes) => bytes,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A TunPacket Encoder/Decoder.
|
|
||||||
pub struct TunPacketCodec(bool, i32);
|
|
||||||
|
|
||||||
impl TunPacketCodec {
|
|
||||||
/// Create a new `TunPacketCodec` specifying whether the underlying
|
|
||||||
/// tunnel Device has enabled the packet information header.
|
|
||||||
pub fn new(pi: bool, mtu: i32) -> TunPacketCodec {
|
|
||||||
TunPacketCodec(pi, mtu)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Decoder for TunPacketCodec {
|
|
||||||
type Item = TunPacket;
|
|
||||||
type Error = io::Error;
|
|
||||||
|
|
||||||
fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
|
|
||||||
if buf.is_empty() {
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut pkt = buf.split_to(buf.len());
|
|
||||||
|
|
||||||
// reserve enough space for the next packet
|
|
||||||
if self.0 {
|
|
||||||
buf.reserve(self.1 as usize + 4);
|
|
||||||
} else {
|
|
||||||
buf.reserve(self.1 as usize);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the packet information is enabled we have to ignore the first 4 bytes
|
|
||||||
if self.0 {
|
|
||||||
let _ = pkt.split_to(4);
|
|
||||||
}
|
|
||||||
|
|
||||||
let proto = infer_proto(pkt.as_ref());
|
|
||||||
Ok(Some(TunPacket(proto, TunPacketBuffer::BytesMut(pkt))))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Encoder<TunPacket> for TunPacketCodec {
|
|
||||||
type Error = io::Error;
|
|
||||||
|
|
||||||
fn encode(&mut self, item: TunPacket, dst: &mut BytesMut) -> Result<(), Self::Error> {
|
|
||||||
dst.reserve(item.get_bytes().len() + 4);
|
|
||||||
match item {
|
|
||||||
TunPacket(proto, bytes) if self.0 => {
|
|
||||||
// build the packet information header comprising of 2 u16
|
|
||||||
// fields: flags and protocol.
|
|
||||||
let mut buf = Vec::<u8>::with_capacity(4);
|
|
||||||
|
|
||||||
// flags is always 0
|
|
||||||
buf.write_u16::<NativeEndian>(0)?;
|
|
||||||
// write the protocol as network byte order
|
|
||||||
buf.write_u16::<NetworkEndian>(proto.into_pi_field()?)?;
|
|
||||||
|
|
||||||
dst.put_slice(&buf);
|
|
||||||
dst.put(Bytes::from(bytes));
|
|
||||||
}
|
|
||||||
TunPacket(_, bytes) => dst.put(Bytes::from(bytes)),
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -119,7 +119,7 @@ impl PacketProtocol {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))]
|
||||||
fn into_pi_field(self) -> Result<u16, io::Error> {
|
fn into_pi_field(self) -> Result<u16, io::Error> {
|
||||||
use nix::libc;
|
use nix::libc;
|
||||||
match self {
|
match self {
|
||||||
|
@ -338,7 +338,7 @@ impl VirtualNic {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(any(target_os = "macos"))]
|
||||||
config.platform_config(|config| {
|
config.platform_config(|config| {
|
||||||
// disable packet information so we can process the header by ourselves, see tun2 impl for more details
|
// disable packet information so we can process the header by ourselves, see tun2 impl for more details
|
||||||
config.packet_information(false);
|
config.packet_information(false);
|
||||||
|
@ -513,7 +513,8 @@ impl NicCtx {
|
||||||
nic.link_up().await?;
|
nic.link_up().await?;
|
||||||
nic.remove_ip(None).await?;
|
nic.remove_ip(None).await?;
|
||||||
nic.add_ip(ipv4_addr, 24).await?;
|
nic.add_ip(ipv4_addr, 24).await?;
|
||||||
if cfg!(target_os = "macos") {
|
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
|
||||||
|
{
|
||||||
nic.add_route(ipv4_addr, 24).await?;
|
nic.add_route(ipv4_addr, 24).await?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
Loading…
Reference in New Issue
Block a user