Compare commits

..

6 Commits

Author SHA1 Message Date
sijie.sun
dd5b00faf4 bump version to v2.2.2
Some checks are pending
EasyTier Core / pre_job (push) Waiting to run
EasyTier Core / build (freebsd-13.2-x86_64, 13.2, ubuntu-22.04, x86_64-unknown-freebsd) (push) Blocked by required conditions
EasyTier Core / build (linux-aarch64, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-arm, ubuntu-22.04, arm-unknown-linux-musleabi) (push) Blocked by required conditions
EasyTier Core / build (linux-armhf, ubuntu-22.04, arm-unknown-linux-musleabihf) (push) Blocked by required conditions
EasyTier Core / build (linux-armv7, ubuntu-22.04, armv7-unknown-linux-musleabi) (push) Blocked by required conditions
EasyTier Core / build (linux-armv7hf, ubuntu-22.04, armv7-unknown-linux-musleabihf) (push) Blocked by required conditions
EasyTier Core / build (linux-mips, ubuntu-22.04, mips-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-mipsel, ubuntu-22.04, mipsel-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-x86_64, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (macos-aarch64, macos-latest, aarch64-apple-darwin) (push) Blocked by required conditions
EasyTier Core / build (macos-x86_64, macos-latest, x86_64-apple-darwin) (push) Blocked by required conditions
EasyTier Core / build (windows-arm64, windows-latest, aarch64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier Core / build (windows-x86_64, windows-latest, x86_64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier Core / core-result (push) Blocked by required conditions
EasyTier GUI / pre_job (push) Waiting to run
EasyTier GUI / build-gui (linux-aarch64, aarch64-unknown-linux-gnu, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier GUI / build-gui (linux-x86_64, x86_64-unknown-linux-gnu, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier GUI / build-gui (macos-aarch64, aarch64-apple-darwin, macos-latest, aarch64-apple-darwin) (push) Blocked by required conditions
EasyTier GUI / build-gui (macos-x86_64, x86_64-apple-darwin, macos-latest, x86_64-apple-darwin) (push) Blocked by required conditions
EasyTier GUI / build-gui (windows-arm64, aarch64-pc-windows-msvc, windows-latest, aarch64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier GUI / build-gui (windows-x86_64, x86_64-pc-windows-msvc, windows-latest, x86_64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier GUI / gui-result (push) Blocked by required conditions
EasyTier Mobile / pre_job (push) Waiting to run
EasyTier Mobile / build-mobile (android, ubuntu-22.04, android) (push) Blocked by required conditions
EasyTier Mobile / mobile-result (push) Blocked by required conditions
EasyTier Test / pre_job (push) Waiting to run
EasyTier Test / test (push) Blocked by required conditions
2025-02-10 08:47:18 +08:00
sijie.sun
0caec3e4da fix label translate
Some checks are pending
EasyTier Core / pre_job (push) Waiting to run
EasyTier Core / build (freebsd-13.2-x86_64, 13.2, ubuntu-22.04, x86_64-unknown-freebsd) (push) Blocked by required conditions
EasyTier Core / build (linux-aarch64, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-arm, ubuntu-22.04, arm-unknown-linux-musleabi) (push) Blocked by required conditions
EasyTier Core / build (linux-armhf, ubuntu-22.04, arm-unknown-linux-musleabihf) (push) Blocked by required conditions
EasyTier Core / build (linux-armv7, ubuntu-22.04, armv7-unknown-linux-musleabi) (push) Blocked by required conditions
EasyTier Core / build (linux-armv7hf, ubuntu-22.04, armv7-unknown-linux-musleabihf) (push) Blocked by required conditions
EasyTier Core / build (linux-mips, ubuntu-22.04, mips-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-mipsel, ubuntu-22.04, mipsel-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-x86_64, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (macos-aarch64, macos-latest, aarch64-apple-darwin) (push) Blocked by required conditions
EasyTier Core / build (macos-x86_64, macos-latest, x86_64-apple-darwin) (push) Blocked by required conditions
EasyTier Core / build (windows-arm64, windows-latest, aarch64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier Core / build (windows-x86_64, windows-latest, x86_64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier Core / core-result (push) Blocked by required conditions
EasyTier GUI / pre_job (push) Waiting to run
EasyTier GUI / build-gui (linux-aarch64, aarch64-unknown-linux-gnu, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier GUI / build-gui (linux-x86_64, x86_64-unknown-linux-gnu, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier GUI / build-gui (macos-aarch64, aarch64-apple-darwin, macos-latest, aarch64-apple-darwin) (push) Blocked by required conditions
EasyTier GUI / build-gui (macos-x86_64, x86_64-apple-darwin, macos-latest, x86_64-apple-darwin) (push) Blocked by required conditions
EasyTier GUI / build-gui (windows-arm64, aarch64-pc-windows-msvc, windows-latest, aarch64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier GUI / build-gui (windows-x86_64, x86_64-pc-windows-msvc, windows-latest, x86_64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier GUI / gui-result (push) Blocked by required conditions
EasyTier Mobile / pre_job (push) Waiting to run
EasyTier Mobile / build-mobile (android, ubuntu-22.04, android) (push) Blocked by required conditions
EasyTier Mobile / mobile-result (push) Blocked by required conditions
EasyTier Test / pre_job (push) Waiting to run
EasyTier Test / test (push) Blocked by required conditions
2025-02-09 22:01:26 +08:00
sijie.sun
e48e62cac0 fix tcp proxy not close properly 2025-02-09 22:01:09 +08:00
sijie.sun
06ebda2e2f update kcp-sys to fix unexpected disconnect
Some checks are pending
EasyTier Core / pre_job (push) Waiting to run
EasyTier Core / build (freebsd-13.2-x86_64, 13.2, ubuntu-22.04, x86_64-unknown-freebsd) (push) Blocked by required conditions
EasyTier Core / build (linux-aarch64, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-arm, ubuntu-22.04, arm-unknown-linux-musleabi) (push) Blocked by required conditions
EasyTier Core / build (linux-armhf, ubuntu-22.04, arm-unknown-linux-musleabihf) (push) Blocked by required conditions
EasyTier Core / build (linux-armv7, ubuntu-22.04, armv7-unknown-linux-musleabi) (push) Blocked by required conditions
EasyTier Core / build (linux-armv7hf, ubuntu-22.04, armv7-unknown-linux-musleabihf) (push) Blocked by required conditions
EasyTier Core / build (linux-mips, ubuntu-22.04, mips-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-mipsel, ubuntu-22.04, mipsel-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-x86_64, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (macos-aarch64, macos-latest, aarch64-apple-darwin) (push) Blocked by required conditions
EasyTier Core / build (macos-x86_64, macos-latest, x86_64-apple-darwin) (push) Blocked by required conditions
EasyTier Core / build (windows-arm64, windows-latest, aarch64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier Core / build (windows-x86_64, windows-latest, x86_64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier Core / core-result (push) Blocked by required conditions
EasyTier GUI / pre_job (push) Waiting to run
EasyTier GUI / build-gui (linux-aarch64, aarch64-unknown-linux-gnu, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier GUI / build-gui (linux-x86_64, x86_64-unknown-linux-gnu, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier GUI / build-gui (macos-aarch64, aarch64-apple-darwin, macos-latest, aarch64-apple-darwin) (push) Blocked by required conditions
EasyTier GUI / build-gui (macos-x86_64, x86_64-apple-darwin, macos-latest, x86_64-apple-darwin) (push) Blocked by required conditions
EasyTier GUI / build-gui (windows-arm64, aarch64-pc-windows-msvc, windows-latest, aarch64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier GUI / build-gui (windows-x86_64, x86_64-pc-windows-msvc, windows-latest, x86_64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier GUI / gui-result (push) Blocked by required conditions
EasyTier Mobile / pre_job (push) Waiting to run
EasyTier Mobile / build-mobile (android, ubuntu-22.04, android) (push) Blocked by required conditions
EasyTier Mobile / mobile-result (push) Blocked by required conditions
EasyTier Test / pre_job (push) Waiting to run
EasyTier Test / test (push) Blocked by required conditions
2025-02-09 00:30:27 +08:00
sijie.sun
53c449b9fb fix net2net kcp proxy 2025-02-08 23:11:10 +08:00
sijie.sun
51e0fac72c improve user experience
Some checks are pending
EasyTier Core / pre_job (push) Waiting to run
EasyTier Core / build (freebsd-13.2-x86_64, 13.2, ubuntu-22.04, x86_64-unknown-freebsd) (push) Blocked by required conditions
EasyTier Core / build (linux-aarch64, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-arm, ubuntu-22.04, arm-unknown-linux-musleabi) (push) Blocked by required conditions
EasyTier Core / build (linux-armhf, ubuntu-22.04, arm-unknown-linux-musleabihf) (push) Blocked by required conditions
EasyTier Core / build (linux-armv7, ubuntu-22.04, armv7-unknown-linux-musleabi) (push) Blocked by required conditions
EasyTier Core / build (linux-armv7hf, ubuntu-22.04, armv7-unknown-linux-musleabihf) (push) Blocked by required conditions
EasyTier Core / build (linux-mips, ubuntu-22.04, mips-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-mipsel, ubuntu-22.04, mipsel-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-x86_64, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (macos-aarch64, macos-latest, aarch64-apple-darwin) (push) Blocked by required conditions
EasyTier Core / build (macos-x86_64, macos-latest, x86_64-apple-darwin) (push) Blocked by required conditions
EasyTier Core / build (windows-arm64, windows-latest, aarch64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier Core / build (windows-x86_64, windows-latest, x86_64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier Core / core-result (push) Blocked by required conditions
EasyTier GUI / pre_job (push) Waiting to run
EasyTier GUI / build-gui (linux-aarch64, aarch64-unknown-linux-gnu, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier GUI / build-gui (linux-x86_64, x86_64-unknown-linux-gnu, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier GUI / build-gui (macos-aarch64, aarch64-apple-darwin, macos-latest, aarch64-apple-darwin) (push) Blocked by required conditions
EasyTier GUI / build-gui (macos-x86_64, x86_64-apple-darwin, macos-latest, x86_64-apple-darwin) (push) Blocked by required conditions
EasyTier GUI / build-gui (windows-arm64, aarch64-pc-windows-msvc, windows-latest, aarch64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier GUI / build-gui (windows-x86_64, x86_64-pc-windows-msvc, windows-latest, x86_64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier GUI / gui-result (push) Blocked by required conditions
EasyTier Mobile / pre_job (push) Waiting to run
EasyTier Mobile / build-mobile (android, ubuntu-22.04, android) (push) Blocked by required conditions
EasyTier Mobile / mobile-result (push) Blocked by required conditions
EasyTier Test / pre_job (push) Waiting to run
EasyTier Test / test (push) Blocked by required conditions
1. add config generator to easytier-web
2. add command to show tcp/kcp proxy entries
2025-02-07 23:59:36 +08:00
46 changed files with 551 additions and 74 deletions

View File

@@ -11,7 +11,7 @@ on:
image_tag: image_tag:
description: 'Tag for this image build' description: 'Tag for this image build'
type: string type: string
default: 'v2.2.1' default: 'v2.2.2'
required: true required: true
mark_latest: mark_latest:
description: 'Mark this image as latest' description: 'Mark this image as latest'
@@ -39,6 +39,12 @@ jobs:
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: login github container registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Download artifact - name: Download artifact
id: download-artifact id: download-artifact
uses: dawidd6/action-download-artifact@v6 uses: dawidd6/action-download-artifact@v6
@@ -58,4 +64,6 @@ jobs:
platforms: linux/amd64,linux/arm64 platforms: linux/amd64,linux/arm64
push: true push: true
file: .github/workflows/Dockerfile file: .github/workflows/Dockerfile
tags: easytier/easytier:${{ inputs.image_tag }}${{ inputs.mark_latest && ',easytier/easytier:latest' || '' }}, tags: |
easytier/easytier:${{ inputs.image_tag }}${{ inputs.mark_latest && ',easytier/easytier:latest' || '' }},
ghcr.io/${{ github.actor }}/easytier:${{ inputs.image_tag }}${{ inputs.mark_latest && ',easytier/easytier:latest' || '' }},

View File

@@ -21,7 +21,7 @@ on:
version: version:
description: 'Version for this release' description: 'Version for this release'
type: string type: string
default: 'v2.2.1' default: 'v2.2.2'
required: true required: true
make_latest: make_latest:
description: 'Mark this release as latest' description: 'Mark this release as latest'

8
Cargo.lock generated
View File

@@ -1874,7 +1874,7 @@ checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125"
[[package]] [[package]]
name = "easytier" name = "easytier"
version = "2.2.1" version = "2.2.2"
dependencies = [ dependencies = [
"aes-gcm", "aes-gcm",
"anyhow", "anyhow",
@@ -1977,7 +1977,7 @@ dependencies = [
[[package]] [[package]]
name = "easytier-gui" name = "easytier-gui"
version = "2.2.1" version = "2.2.2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"chrono", "chrono",
@@ -2022,7 +2022,7 @@ dependencies = [
[[package]] [[package]]
name = "easytier-web" name = "easytier-web"
version = "2.2.1" version = "2.2.2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@@ -3563,7 +3563,7 @@ dependencies = [
[[package]] [[package]]
name = "kcp-sys" name = "kcp-sys"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/EasyTier/kcp-sys#9ce5c08807378ad0486291928994c4f80878c196" source = "git+https://github.com/EasyTier/kcp-sys#0f0a0558391ba391c089806c23f369651f6c9eeb"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"auto_impl", "auto_impl",

View File

@@ -1,7 +1,7 @@
{ {
"name": "easytier-gui", "name": "easytier-gui",
"type": "module", "type": "module",
"version": "2.2.1", "version": "2.2.2",
"private": true, "private": true,
"packageManager": "pnpm@9.12.1+sha512.e5a7e52a4183a02d5931057f7a0dbff9d5e9ce3161e33fa68ae392125b79282a8a8a470a51dfc8a0ed86221442eb2fb57019b0990ed24fab519bf0e1bc5ccfc4", "packageManager": "pnpm@9.12.1+sha512.e5a7e52a4183a02d5931057f7a0dbff9d5e9ce3161e33fa68ae392125b79282a8a8a470a51dfc8a0ed86221442eb2fb57019b0990ed24fab519bf0e1bc5ccfc4",
"scripts": { "scripts": {

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "easytier-gui" name = "easytier-gui"
version = "2.2.1" version = "2.2.2"
description = "EasyTier GUI" description = "EasyTier GUI"
authors = ["you"] authors = ["you"]
edition = "2021" edition = "2021"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 68 KiB

View File

@@ -17,7 +17,7 @@
"createUpdaterArtifacts": false "createUpdaterArtifacts": false
}, },
"productName": "easytier-gui", "productName": "easytier-gui",
"version": "2.2.1", "version": "2.2.2",
"identifier": "com.kkrainbow.easytier", "identifier": "com.kkrainbow.easytier",
"plugins": {}, "plugins": {},
"app": { "app": {

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "easytier-web" name = "easytier-web"
version = "2.2.1" version = "2.2.2"
edition = "2021" edition = "2021"
description = "Config server for easytier. easytier-core gets config from this and web frontend use it as restful api server." description = "Config server for easytier. easytier-core gets config from this and web frontend use it as restful api server."

View File

@@ -75,13 +75,13 @@ latency_first: 开启延迟优先模式
latency_first_help: 忽略中转跳数,选择总延迟最低的路径 latency_first_help: 忽略中转跳数,选择总延迟最低的路径
use_smoltcp: 使用用户态协议栈 use_smoltcp: 使用用户态协议栈
use_smoltcp_help: 使用用户态 TCP/IP 协议栈,避免操作系统防火墙问题导致无法子网代理。 use_smoltcp_help: 使用用户态 TCP/IP 协议栈,避免操作系统防火墙问题导致无法子网代理 / KCP代理
enable_kcp_proxy: 启用 KCP 代理 enable_kcp_proxy: 启用 KCP 代理
enable_kcp_proxy_help: 将 TCP 流量转为 KCP 流量,降低传输延迟,提升传输速度。 enable_kcp_proxy_help: 将 TCP 流量转为 KCP 流量,降低传输延迟,提升传输速度。
disable_kcp_input: 禁用 KCP 输入 disable_kcp_input: 禁用 KCP 输入
disable_kcp_input_help: 禁用 KCP 入站流量,其他开启 KCP 代理的节点无法连接到本节点。 disable_kcp_input_help: 禁用 KCP 入站流量,其他开启 KCP 代理的节点仍然使用 TCP 连接到本节点。
disable_p2p: 禁用 P2P disable_p2p: 禁用 P2P
disable_p2p_help: 禁用 P2P 模式,所有流量通过手动指定的服务器中转。 disable_p2p_help: 禁用 P2P 模式,所有流量通过手动指定的服务器中转。

View File

@@ -68,6 +68,29 @@ upload_bytes: Upload
download_bytes: Download download_bytes: Download
loss_rate: Loss Rate loss_rate: Loss Rate
flags_switch: Feature Switch
latency_first: Enable Latency-First Mode
latency_first_help: Ignore hop count and select the path with the lowest total latency
use_smoltcp: Use User-Space Protocol Stack
use_smoltcp_help: Use a user-space TCP/IP stack to avoid issues with operating system firewalls blocking subnet or KCP proxy functionality.
enable_kcp_proxy: Enable KCP Proxy
enable_kcp_proxy_help: Convert TCP traffic to KCP traffic to reduce latency and boost transmission speed.
disable_kcp_input: Disable KCP Input
disable_kcp_input_help: Disable inbound KCP traffic, while nodes with KCP proxy enabled continue to connect using TCP.
disable_p2p: Disable P2P
disable_p2p_help: Disable P2P mode; route all traffic through a manually specified relay server.
bind_device: Bind to Physical Device Only
bind_device_help: Use only the physical network interface to prevent EasyTier from connecting via virtual networks.
no_tun: No TUN Mode
no_tun_help: Do not use a TUN interface, suitable for environments without administrator privileges. This node is only accessible; accessing other nodes requires SOCKS5.
status: status:
version: Version version: Version
local: Local local: Local

View File

@@ -1,6 +1,7 @@
import axios, { AxiosError, AxiosInstance, AxiosResponse, InternalAxiosRequestConfig } from 'axios'; import axios, { AxiosError, AxiosInstance, AxiosResponse, InternalAxiosRequestConfig } from 'axios';
import { Md5 } from 'ts-md5' import { Md5 } from 'ts-md5'
import { UUID } from './utils'; import { UUID } from './utils';
import { NetworkConfig } from '../types/network';
export interface ValidateConfigResponse { export interface ValidateConfigResponse {
toml_config: string; toml_config: string;
@@ -37,6 +38,15 @@ export interface ListNetworkInstanceIdResponse {
disabled_inst_ids: Array<UUID>, disabled_inst_ids: Array<UUID>,
} }
export interface GenerateConfigRequest {
config: NetworkConfig;
}
export interface GenerateConfigResponse {
toml_config?: string;
error?: string;
}
export class ApiClient { export class ApiClient {
private client: AxiosInstance; private client: AxiosInstance;
private authFailedCb: Function | undefined; private authFailedCb: Function | undefined;
@@ -193,6 +203,18 @@ export class ApiClient {
public captcha_url() { public captcha_url() {
return this.client.defaults.baseURL + '/auth/captcha'; return this.client.defaults.baseURL + '/auth/captcha';
} }
public async generate_config(config: GenerateConfigRequest): Promise<GenerateConfigResponse> {
try {
const response = await this.client.post<any, GenerateConfigResponse>('/generate-config', config);
return response;
} catch (error) {
if (error instanceof AxiosError) {
return { error: error.response?.data };
}
return { error: 'Unknown error: ' + error };
}
}
} }
export default ApiClient; export default ApiClient;

View File

@@ -0,0 +1,39 @@
<script setup lang="ts">
import { NetworkTypes } from 'easytier-frontend-lib';
import { ref } from 'vue';
import { Api } from 'easytier-frontend-lib'
const defaultApiHost = 'https://config-server.easytier.cn'
const api = new Api.ApiClient(defaultApiHost);
const newNetworkConfig = ref<NetworkTypes.NetworkConfig>(NetworkTypes.DEFAULT_NETWORK_CONFIG());
const toml_config = ref<string>("Press 'Run Network' to generate TOML configuration");
const generateConfig = (config: NetworkTypes.NetworkConfig) => {
api.generate_config({
config: config
}).then((res) => {
if (res.error) {
toml_config.value = res.error;
} else if (res.toml_config) {
toml_config.value = res.toml_config;
} else {
toml_config.value = "Api server returned an unexpected response";
}
});
};
</script>
<template>
<div class="flex items-center justify-center m-5">
<div class="flex w-full">
<div class="w-1/2 p-4">
<Config :cur-network="newNetworkConfig" @run-network="generateConfig" />
</div>
<div class="w-1/2 p-4 bg-gray-100">
<pre class="whitespace-pre-wrap">{{ toml_config }}</pre>
</div>
</div>
</div>
</template>

View File

@@ -15,6 +15,7 @@ import DeviceManagement from './components/DeviceManagement.vue'
import Dashboard from './components/Dashboard.vue' import Dashboard from './components/Dashboard.vue'
import DialogService from 'primevue/dialogservice'; import DialogService from 'primevue/dialogservice';
import ToastService from 'primevue/toastservice'; import ToastService from 'primevue/toastservice';
import ConfigGenerator from './components/ConfigGenerator.vue'
const routes = [ const routes = [
{ {
@@ -66,6 +67,10 @@ const routes = [
} }
} }
}, },
{
path: '/config_generator',
component: ConfigGenerator,
}
] ]
const router = createRouter({ const router = createRouter({

View File

@@ -6,11 +6,14 @@ mod users;
use std::{net::SocketAddr, sync::Arc}; use std::{net::SocketAddr, sync::Arc};
use axum::http::StatusCode; use axum::http::StatusCode;
use axum::routing::post;
use axum::{extract::State, routing::get, Json, Router}; use axum::{extract::State, routing::get, Json, Router};
use axum_login::tower_sessions::{ExpiredDeletion, SessionManagerLayer}; use axum_login::tower_sessions::{ExpiredDeletion, SessionManagerLayer};
use axum_login::{login_required, AuthManagerLayerBuilder, AuthzBackend}; use axum_login::{login_required, AuthManagerLayerBuilder, AuthzBackend};
use axum_messages::MessagesManagerLayer; use axum_messages::MessagesManagerLayer;
use easytier::common::config::ConfigLoader;
use easytier::common::scoped_task::ScopedTask; use easytier::common::scoped_task::ScopedTask;
use easytier::launcher::NetworkConfig;
use easytier::proto::rpc_types; use easytier::proto::rpc_types;
use network::NetworkApi; use network::NetworkApi;
use sea_orm::DbErr; use sea_orm::DbErr;
@@ -48,6 +51,17 @@ struct GetSummaryJsonResp {
device_count: u32, device_count: u32,
} }
#[derive(Debug, serde::Deserialize, serde::Serialize)]
struct GenerateConfigRequest {
config: NetworkConfig,
}
#[derive(Debug, serde::Deserialize, serde::Serialize)]
struct GenerateConfigResponse {
error: Option<String>,
toml_config: Option<String>,
}
#[derive(Debug, serde::Deserialize, serde::Serialize)] #[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct Error { pub struct Error {
message: String, message: String,
@@ -131,6 +145,24 @@ impl RestfulServer {
.into()) .into())
} }
async fn handle_generate_config(
Json(req): Json<GenerateConfigRequest>,
) -> Result<Json<GenerateConfigResponse>, HttpHandleError> {
let config = req.config.gen_config();
match config {
Ok(c) => Ok(GenerateConfigResponse {
error: None,
toml_config: Some(c.dump()),
}
.into()),
Err(e) => Ok(GenerateConfigResponse {
error: Some(format!("{:?}", e)),
toml_config: None,
}
.into()),
}
}
pub async fn start(&mut self) -> Result<(), anyhow::Error> { pub async fn start(&mut self) -> Result<(), anyhow::Error> {
let listener = TcpListener::bind(self.bind_addr).await?; let listener = TcpListener::bind(self.bind_addr).await?;
@@ -178,6 +210,10 @@ impl RestfulServer {
.route_layer(login_required!(Backend)) .route_layer(login_required!(Backend))
.merge(auth::router()) .merge(auth::router())
.with_state(self.client_mgr.clone()) .with_state(self.client_mgr.clone())
.route(
"/api/v1/generate-config",
post(Self::handle_generate_config),
)
.layer(MessagesManagerLayer) .layer(MessagesManagerLayer)
.layer(auth_layer) .layer(auth_layer)
.layer(tower_http::cors::CorsLayer::very_permissive()) .layer(tower_http::cors::CorsLayer::very_permissive())

View File

@@ -3,7 +3,7 @@ name = "easytier"
description = "A full meshed p2p VPN, connecting all your devices in one network with one command." description = "A full meshed p2p VPN, connecting all your devices in one network with one command."
homepage = "https://github.com/EasyTier/EasyTier" homepage = "https://github.com/EasyTier/EasyTier"
repository = "https://github.com/EasyTier/EasyTier" repository = "https://github.com/EasyTier/EasyTier"
version = "2.2.1" version = "2.2.2"
edition = "2021" edition = "2021"
authors = ["kkrainbow"] authors = ["kkrainbow"]
keywords = ["vpn", "p2p", "network", "easytier"] keywords = ["vpn", "p2p", "network", "easytier"]

View File

@@ -100,8 +100,8 @@ core_clap:
en: "do not create TUN device, can use subnet proxy to access node" en: "do not create TUN device, can use subnet proxy to access node"
zh-CN: "不创建TUN设备可以使用子网代理访问节点" zh-CN: "不创建TUN设备可以使用子网代理访问节点"
use_smoltcp: use_smoltcp:
en: "enable smoltcp stack for subnet proxy" en: "enable smoltcp stack for subnet proxy and kcp proxy"
zh-CN: "为子网代理启用smoltcp堆栈" zh-CN: "为子网代理和 KCP 代理启用smoltcp堆栈"
manual_routes: manual_routes:
en: "assign routes cidr manually, will disable subnet proxy and wireguard routes propagated from peers. e.g.: 192.168.0.0/16" en: "assign routes cidr manually, will disable subnet proxy and wireguard routes propagated from peers. e.g.: 192.168.0.0/16"
zh-CN: "手动分配路由CIDR将禁用子网代理和从对等节点传播的wireguard路由。例如192.168.0.0/16" zh-CN: "手动分配路由CIDR将禁用子网代理和从对等节点传播的wireguard路由。例如192.168.0.0/16"

View File

@@ -580,13 +580,15 @@ pub mod tests {
) )
.await; .await;
println!("start punching {:?}", p_a.list_routes().await);
wait_for_condition( wait_for_condition(
|| async { || async {
wait_route_appear_with_cost(p_a.clone(), p_c.my_peer_id(), Some(1)) wait_route_appear_with_cost(p_a.clone(), p_c.my_peer_id(), Some(1))
.await .await
.is_ok() .is_ok()
}, },
Duration::from_secs(5), Duration::from_secs(10),
) )
.await; .await;
println!("{:?}", p_a.list_routes().await); println!("{:?}", p_a.list_routes().await);

View File

@@ -20,7 +20,8 @@ use easytier::{
DumpRouteRequest, GetVpnPortalInfoRequest, ListConnectorRequest, DumpRouteRequest, GetVpnPortalInfoRequest, ListConnectorRequest,
ListForeignNetworkRequest, ListGlobalForeignNetworkRequest, ListPeerRequest, ListForeignNetworkRequest, ListGlobalForeignNetworkRequest, ListPeerRequest,
ListPeerResponse, ListRouteRequest, ListRouteResponse, NodeInfo, PeerManageRpc, ListPeerResponse, ListRouteRequest, ListRouteResponse, NodeInfo, PeerManageRpc,
PeerManageRpcClientFactory, ShowNodeInfoRequest, VpnPortalRpc, PeerManageRpcClientFactory, ShowNodeInfoRequest, TcpProxyEntryState,
TcpProxyEntryTransportType, TcpProxyRpc, TcpProxyRpcClientFactory, VpnPortalRpc,
VpnPortalRpcClientFactory, VpnPortalRpcClientFactory,
}, },
common::NatType, common::NatType,
@@ -50,14 +51,24 @@ struct Cli {
#[derive(Subcommand, Debug)] #[derive(Subcommand, Debug)]
enum SubCommand { enum SubCommand {
#[command(about = "show peers info")]
Peer(PeerArgs), Peer(PeerArgs),
#[command(about = "manage connectors")]
Connector(ConnectorArgs), Connector(ConnectorArgs),
#[command(about = "do stun test")]
Stun, Stun,
#[command(about = "show route info")]
Route(RouteArgs), Route(RouteArgs),
#[command(about = "show global peers info")]
PeerCenter, PeerCenter,
#[command(about = "show vpn portal (wireguard) info")]
VpnPortal, VpnPortal,
#[command(about = "inspect self easytier-core status")]
Node(NodeArgs), Node(NodeArgs),
#[command(about = "manage easytier-core as a system service")]
Service(ServiceArgs), Service(ServiceArgs),
#[command(about = "show tcp/kcp proxy status")]
Proxy,
} }
#[derive(Args, Debug)] #[derive(Args, Debug)]
@@ -114,7 +125,9 @@ enum ConnectorSubCommand {
#[derive(Subcommand, Debug)] #[derive(Subcommand, Debug)]
enum NodeSubCommand { enum NodeSubCommand {
#[command(about = "show node info")]
Info, Info,
#[command(about = "show node config")]
Config, Config,
} }
@@ -135,10 +148,15 @@ struct ServiceArgs {
#[derive(Subcommand, Debug)] #[derive(Subcommand, Debug)]
enum ServiceSubCommand { enum ServiceSubCommand {
#[command(about = "register easytier-core as a system service")]
Install(InstallArgs), Install(InstallArgs),
#[command(about = "unregister easytier-core system service")]
Uninstall, Uninstall,
#[command(about = "check easytier-core system service status")]
Status, Status,
#[command(about = "start easytier-core system service")]
Start, Start,
#[command(about = "stop easytier-core system service")]
Stop, Stop,
} }
@@ -153,13 +171,17 @@ struct InstallArgs {
#[arg(long, default_value = "false")] #[arg(long, default_value = "false")]
disable_autostart: bool, disable_autostart: bool,
#[arg(long)] #[arg(long, help = "path to easytier-core binary")]
core_path: Option<PathBuf>, core_path: Option<PathBuf>,
#[arg(long)] #[arg(long)]
service_work_dir: Option<PathBuf>, service_work_dir: Option<PathBuf>,
#[arg(trailing_var_arg = true, allow_hyphen_values = true)] #[arg(
trailing_var_arg = true,
allow_hyphen_values = true,
help = "args to pass to easytier-core"
)]
core_args: Option<Vec<OsString>>, core_args: Option<Vec<OsString>>,
} }
@@ -221,6 +243,19 @@ impl CommandHandler {
.with_context(|| "failed to get vpn portal client")?) .with_context(|| "failed to get vpn portal client")?)
} }
async fn get_tcp_proxy_client(
&self,
transport_type: &str,
) -> Result<Box<dyn TcpProxyRpc<Controller = BaseController>>, Error> {
Ok(self
.client
.lock()
.unwrap()
.scoped_client::<TcpProxyRpcClientFactory<BaseController>>(transport_type.to_string())
.await
.with_context(|| "failed to get vpn portal client")?)
}
async fn list_peers(&self) -> Result<ListPeerResponse, Error> { async fn list_peers(&self) -> Result<ListPeerResponse, Error> {
let client = self.get_peer_manager_client().await?; let client = self.get_peer_manager_client().await?;
let request = ListPeerRequest::default(); let request = ListPeerRequest::default();
@@ -647,12 +682,22 @@ impl Service {
environment: None, environment: None,
}; };
if self.status()? != ServiceStatus::NotInstalled { if self.status()? != ServiceStatus::NotInstalled {
return Err(anyhow::anyhow!("Service is already installed")); return Err(anyhow::anyhow!(
"Service is already installed! Service Name: {}",
self.lable
));
} }
self.service_manager self.service_manager
.install(ctx) .install(ctx.clone())
.map_err(|e| anyhow::anyhow!("failed to install service: {}", e)) .map_err(|e| anyhow::anyhow!("failed to install service: {:?}", e))?;
println!(
"Service installed successfully! Service Name: {}",
self.lable
);
Ok(())
} }
pub fn uninstall(&self) -> Result<(), Error> { pub fn uninstall(&self) -> Result<(), Error> {
@@ -769,7 +814,8 @@ impl Service {
writeln!(unit_content, "Type=simple")?; writeln!(unit_content, "Type=simple")?;
writeln!(unit_content, "WorkingDirectory={work_dir}")?; writeln!(unit_content, "WorkingDirectory={work_dir}")?;
writeln!(unit_content, "ExecStart={target_app} {args}")?; writeln!(unit_content, "ExecStart={target_app} {args}")?;
writeln!(unit_content, "Restart=Always")?; writeln!(unit_content, "Restart=always")?;
writeln!(unit_content, "RestartSec=1")?;
writeln!(unit_content, "LimitNOFILE=infinity")?; writeln!(unit_content, "LimitNOFILE=infinity")?;
writeln!(unit_content)?; writeln!(unit_content)?;
writeln!(unit_content, "[Install]")?; writeln!(unit_content, "[Install]")?;
@@ -1088,6 +1134,57 @@ async fn main() -> Result<(), Error> {
} }
} }
} }
SubCommand::Proxy => {
let mut entries = vec![];
let client = handler.get_tcp_proxy_client("tcp").await?;
let ret = client
.list_tcp_proxy_entry(BaseController::default(), Default::default())
.await;
entries.extend(ret.unwrap_or_default().entries);
let client = handler.get_tcp_proxy_client("kcp_src").await?;
let ret = client
.list_tcp_proxy_entry(BaseController::default(), Default::default())
.await;
entries.extend(ret.unwrap_or_default().entries);
let client = handler.get_tcp_proxy_client("kcp_dst").await?;
let ret = client
.list_tcp_proxy_entry(BaseController::default(), Default::default())
.await;
entries.extend(ret.unwrap_or_default().entries);
#[derive(tabled::Tabled)]
struct TableItem {
src: String,
dst: String,
start_time: String,
state: String,
transport_type: String,
}
let table_rows = entries
.iter()
.map(|e| TableItem {
src: SocketAddr::from(e.src.unwrap_or_default()).to_string(),
dst: SocketAddr::from(e.dst.unwrap_or_default()).to_string(),
start_time: chrono::DateTime::<chrono::Utc>::from_timestamp_millis(
(e.start_time * 1000) as i64,
)
.unwrap()
.with_timezone(&chrono::Local)
.format("%Y-%m-%d %H:%M:%S")
.to_string(),
state: format!("{:?}", TcpProxyEntryState::try_from(e.state).unwrap()),
transport_type: format!(
"{:?}",
TcpProxyEntryTransportType::try_from(e.transport_type).unwrap()
),
})
.collect::<Vec<_>>();
println!("{}", tabled::Table::new(table_rows).with(Style::modern()));
}
} }
Ok(()) Ok(())

View File

@@ -6,6 +6,7 @@ use std::{
time::Duration, time::Duration,
}; };
use anyhow::Context;
use pnet::packet::{ use pnet::packet::{
icmp::{self, echo_reply::MutableEchoReplyPacket, IcmpCode, IcmpTypes, MutableIcmpPacket}, icmp::{self, echo_reply::MutableEchoReplyPacket, IcmpCode, IcmpTypes, MutableIcmpPacket},
ip::IpNextHeaderProtocols, ip::IpNextHeaderProtocols,
@@ -212,7 +213,7 @@ impl IcmpProxy {
Err(e) => { Err(e) => {
tracing::warn!("create icmp socket failed: {:?}", e); tracing::warn!("create icmp socket failed: {:?}", e);
if !self.global_ctx.no_tun() { if !self.global_ctx.no_tun() {
return Err(e); return Err(anyhow::anyhow!("create icmp socket failed: {:?}", e).into());
} }
} }
} }
@@ -281,10 +282,15 @@ impl IcmpProxy {
dst_ip: Ipv4Addr, dst_ip: Ipv4Addr,
icmp_packet: &icmp::echo_request::EchoRequestPacket, icmp_packet: &icmp::echo_request::EchoRequestPacket,
) -> Result<(), Error> { ) -> Result<(), Error> {
self.socket.lock().unwrap().as_ref().unwrap().send_to( self.socket
icmp_packet.packet(), .lock()
&SocketAddrV4::new(dst_ip.into(), 0).into(), .unwrap()
)?; .as_ref()
.with_context(|| "icmp socket not created")?
.send_to(
icmp_packet.packet(),
&SocketAddrV4::new(dst_ip.into(), 0).into(),
)?;
Ok(()) Ok(())
} }

View File

@@ -1,13 +1,14 @@
use std::{ use std::{
net::{IpAddr, Ipv4Addr, SocketAddr}, net::{IpAddr, Ipv4Addr, SocketAddr},
sync::Arc, sync::{Arc, Weak},
time::Duration, time::Duration,
}; };
use anyhow::Context; use anyhow::Context;
use bytes::Bytes; use bytes::Bytes;
use dashmap::DashMap;
use kcp_sys::{ use kcp_sys::{
endpoint::{KcpEndpoint, KcpPacketReceiver}, endpoint::{ConnId, KcpEndpoint, KcpPacketReceiver},
ffi_safe::KcpConfig, ffi_safe::KcpConfig,
packet_def::KcpPacket, packet_def::KcpPacket,
stream::KcpStream, stream::KcpStream,
@@ -31,7 +32,14 @@ use crate::{
global_ctx::{ArcGlobalCtx, GlobalCtx}, global_ctx::{ArcGlobalCtx, GlobalCtx},
}, },
peers::{peer_manager::PeerManager, NicPacketFilter, PeerPacketFilter}, peers::{peer_manager::PeerManager, NicPacketFilter, PeerPacketFilter},
proto::peer_rpc::KcpConnData, proto::{
cli::{
ListTcpProxyEntryRequest, ListTcpProxyEntryResponse, TcpProxyEntry, TcpProxyEntryState,
TcpProxyEntryTransportType, TcpProxyRpc,
},
peer_rpc::KcpConnData,
rpc_types::{self, controller::BaseController},
},
tunnel::packet_def::{PacketType, PeerManagerHeader, ZCPacket}, tunnel::packet_def::{PacketType, PeerManagerHeader, ZCPacket},
}; };
@@ -106,8 +114,9 @@ pub struct NatDstKcpConnector {
impl NatDstConnector for NatDstKcpConnector { impl NatDstConnector for NatDstKcpConnector {
type DstStream = KcpStream; type DstStream = KcpStream;
async fn connect(&self, nat_dst: SocketAddr) -> Result<Self::DstStream> { async fn connect(&self, src: SocketAddr, nat_dst: SocketAddr) -> Result<Self::DstStream> {
let conn_data = KcpConnData { let conn_data = KcpConnData {
src: Some(src.into()),
dst: Some(nat_dst.into()), dst: Some(nat_dst.into()),
}; };
@@ -153,9 +162,12 @@ impl NatDstConnector for NatDstKcpConnector {
hdr: &PeerManagerHeader, hdr: &PeerManagerHeader,
_ipv4: &Ipv4Packet, _ipv4: &Ipv4Packet,
) -> bool { ) -> bool {
// TODO: how to support net to net kcp proxy?
return hdr.from_peer_id == hdr.to_peer_id; return hdr.from_peer_id == hdr.to_peer_id;
} }
fn transport_type(&self) -> TcpProxyEntryTransportType {
TcpProxyEntryTransportType::Kcp
}
} }
#[derive(Clone)] #[derive(Clone)]
@@ -191,17 +203,10 @@ impl NicPacketFilter for TcpProxyForKcpSrc {
return true; return true;
} }
let Some(my_ipv4) = self.0.get_global_ctx().get_ipv4() else {
return false;
};
let data = zc_packet.payload(); let data = zc_packet.payload();
let ip_packet = Ipv4Packet::new(data).unwrap(); let ip_packet = Ipv4Packet::new(data).unwrap();
if ip_packet.get_version() != 4 if ip_packet.get_version() != 4
// TODO: how to support net to net kcp proxy?
|| ip_packet.get_source() != my_ipv4.address()
|| ip_packet.get_next_level_protocol() != IpNextHeaderProtocols::Tcp || ip_packet.get_next_level_protocol() != IpNextHeaderProtocols::Tcp
|| !self.check_dst_allow_kcp_input(&ip_packet.get_destination()).await
{ {
return false; return false;
} }
@@ -210,15 +215,33 @@ impl NicPacketFilter for TcpProxyForKcpSrc {
let tcp_packet = TcpPacket::new(ip_packet.payload()).unwrap(); let tcp_packet = TcpPacket::new(ip_packet.payload()).unwrap();
let is_syn = tcp_packet.get_flags() & TcpFlags::SYN != 0 let is_syn = tcp_packet.get_flags() & TcpFlags::SYN != 0
&& tcp_packet.get_flags() & TcpFlags::ACK == 0; && tcp_packet.get_flags() & TcpFlags::ACK == 0;
if !is_syn if is_syn {
&& !self.0.is_tcp_proxy_connection(SocketAddr::new( // only check dst feature flag when SYN packet
IpAddr::V4(my_ipv4.address()), if !self
.check_dst_allow_kcp_input(&ip_packet.get_destination())
.await
{
return false;
}
} else {
// if not syn packet, only allow established connection
if !self.0.is_tcp_proxy_connection(SocketAddr::new(
IpAddr::V4(ip_packet.get_source()),
tcp_packet.get_source(), tcp_packet.get_source(),
)) )) {
{ return false;
return false; }
} }
if let Some(my_ipv4) = self.0.get_global_ctx().get_ipv4() {
// this is a net-to-net packet, only allow it when smoltcp is enabled
// because the syn-ack packet will not be through and handled by the tun device when
// the source ip is in the local network
if ip_packet.get_source() != my_ipv4.address() && !self.0.is_smoltcp_enabled() {
return false;
}
};
zc_packet.mut_peer_manager_header().unwrap().to_peer_id = self.0.get_my_peer_id().into(); zc_packet.mut_peer_manager_header().unwrap().to_peer_id = self.0.get_my_peer_id().into();
true true
@@ -272,11 +295,16 @@ impl KcpProxySrc {
.await; .await;
self.tcp_proxy.0.start(false).await.unwrap(); self.tcp_proxy.0.start(false).await.unwrap();
} }
pub fn get_tcp_proxy(&self) -> Arc<TcpProxy<NatDstKcpConnector>> {
self.tcp_proxy.0.clone()
}
} }
pub struct KcpProxyDst { pub struct KcpProxyDst {
kcp_endpoint: Arc<KcpEndpoint>, kcp_endpoint: Arc<KcpEndpoint>,
peer_manager: Arc<PeerManager>, peer_manager: Arc<PeerManager>,
proxy_entries: Arc<DashMap<ConnId, TcpProxyEntry>>,
tasks: JoinSet<()>, tasks: JoinSet<()>,
} }
@@ -296,6 +324,7 @@ impl KcpProxyDst {
Self { Self {
kcp_endpoint: Arc::new(kcp_endpoint), kcp_endpoint: Arc::new(kcp_endpoint),
peer_manager, peer_manager,
proxy_entries: Arc::new(DashMap::new()),
tasks, tasks,
} }
} }
@@ -304,6 +333,7 @@ impl KcpProxyDst {
async fn handle_one_in_stream( async fn handle_one_in_stream(
mut kcp_stream: KcpStream, mut kcp_stream: KcpStream,
global_ctx: ArcGlobalCtx, global_ctx: ArcGlobalCtx,
proxy_entries: Arc<DashMap<ConnId, TcpProxyEntry>>,
) -> Result<()> { ) -> Result<()> {
let mut conn_data = kcp_stream.conn_data().clone(); let mut conn_data = kcp_stream.conn_data().clone();
let parsed_conn_data = KcpConnData::decode(&mut conn_data) let parsed_conn_data = KcpConnData::decode(&mut conn_data)
@@ -316,6 +346,21 @@ impl KcpProxyDst {
))? ))?
.into(); .into();
let conn_id = kcp_stream.conn_id();
proxy_entries.insert(
conn_id,
TcpProxyEntry {
src: parsed_conn_data.src,
dst: parsed_conn_data.dst,
start_time: chrono::Local::now().timestamp() as u64,
state: TcpProxyEntryState::ConnectingDst.into(),
transport_type: TcpProxyEntryTransportType::Kcp.into(),
},
);
crate::defer! {
proxy_entries.remove(&conn_id);
}
if Some(dst_socket.ip()) == global_ctx.get_ipv4().map(|ip| IpAddr::V4(ip.address())) { if Some(dst_socket.ip()) == global_ctx.get_ipv4().map(|ip| IpAddr::V4(ip.address())) {
dst_socket = format!("127.0.0.1:{}", dst_socket.port()).parse().unwrap(); dst_socket = format!("127.0.0.1:{}", dst_socket.port()).parse().unwrap();
} }
@@ -324,7 +369,13 @@ impl KcpProxyDst {
let _g = global_ctx.net_ns.guard(); let _g = global_ctx.net_ns.guard();
let connector = NatDstTcpConnector {}; let connector = NatDstTcpConnector {};
let mut ret = connector.connect(dst_socket).await?; let mut ret = connector
.connect("0.0.0.0:0".parse().unwrap(), dst_socket)
.await?;
if let Some(mut e) = proxy_entries.get_mut(&kcp_stream.conn_id()) {
e.state = TcpProxyEntryState::Connected.into();
}
copy_bidirectional(&mut ret, &mut kcp_stream).await?; copy_bidirectional(&mut ret, &mut kcp_stream).await?;
Ok(()) Ok(())
@@ -333,6 +384,7 @@ impl KcpProxyDst {
async fn run_accept_task(&mut self) { async fn run_accept_task(&mut self) {
let kcp_endpoint = self.kcp_endpoint.clone(); let kcp_endpoint = self.kcp_endpoint.clone();
let global_ctx = self.peer_manager.get_global_ctx().clone(); let global_ctx = self.peer_manager.get_global_ctx().clone();
let proxy_entries = self.proxy_entries.clone();
self.tasks.spawn(async move { self.tasks.spawn(async move {
while let Ok(conn) = kcp_endpoint.accept().await { while let Ok(conn) = kcp_endpoint.accept().await {
let stream = KcpStream::new(&kcp_endpoint, conn) let stream = KcpStream::new(&kcp_endpoint, conn)
@@ -340,8 +392,9 @@ impl KcpProxyDst {
.unwrap(); .unwrap();
let global_ctx = global_ctx.clone(); let global_ctx = global_ctx.clone();
let proxy_entries = proxy_entries.clone();
tokio::spawn(async move { tokio::spawn(async move {
let _ = Self::handle_one_in_stream(stream, global_ctx).await; let _ = Self::handle_one_in_stream(stream, global_ctx, proxy_entries).await;
}); });
} }
}); });
@@ -357,3 +410,30 @@ impl KcpProxyDst {
.await; .await;
} }
} }
#[derive(Clone)]
pub struct KcpProxyDstRpcService(Weak<DashMap<ConnId, TcpProxyEntry>>);
impl KcpProxyDstRpcService {
pub fn new(kcp_proxy_dst: &KcpProxyDst) -> Self {
Self(Arc::downgrade(&kcp_proxy_dst.proxy_entries))
}
}
#[async_trait::async_trait]
impl TcpProxyRpc for KcpProxyDstRpcService {
type Controller = BaseController;
async fn list_tcp_proxy_entry(
&self,
_: BaseController,
_request: ListTcpProxyEntryRequest, // Accept request of type HelloRequest
) -> std::result::Result<ListTcpProxyEntryResponse, rpc_types::error::Error> {
let mut reply = ListTcpProxyEntryResponse::default();
if let Some(tcp_proxy) = self.0.upgrade() {
for item in tcp_proxy.iter() {
reply.entries.push(item.value().clone());
}
}
Ok(reply)
}
}

View File

@@ -10,9 +10,9 @@ use pnet::packet::MutablePacket;
use pnet::packet::Packet; use pnet::packet::Packet;
use std::net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4}; use std::net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4};
use std::sync::atomic::{AtomicBool, AtomicU16}; use std::sync::atomic::{AtomicBool, AtomicU16};
use std::sync::Arc; use std::sync::{Arc, Weak};
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use tokio::io::{copy_bidirectional, AsyncRead, AsyncWrite}; use tokio::io::{copy_bidirectional, AsyncRead, AsyncWrite, AsyncWriteExt};
use tokio::net::{TcpListener, TcpSocket, TcpStream}; use tokio::net::{TcpListener, TcpSocket, TcpStream};
use tokio::sync::{mpsc, Mutex}; use tokio::sync::{mpsc, Mutex};
use tokio::task::JoinSet; use tokio::task::JoinSet;
@@ -24,6 +24,12 @@ use crate::common::join_joinset_background;
use crate::peers::peer_manager::PeerManager; use crate::peers::peer_manager::PeerManager;
use crate::peers::{NicPacketFilter, PeerPacketFilter}; use crate::peers::{NicPacketFilter, PeerPacketFilter};
use crate::proto::cli::{
ListTcpProxyEntryRequest, ListTcpProxyEntryResponse, TcpProxyEntry, TcpProxyEntryState,
TcpProxyEntryTransportType, TcpProxyRpc,
};
use crate::proto::rpc_types;
use crate::proto::rpc_types::controller::BaseController;
use crate::tunnel::packet_def::{PacketType, PeerManagerHeader, ZCPacket}; use crate::tunnel::packet_def::{PacketType, PeerManagerHeader, ZCPacket};
use super::CidrSet; use super::CidrSet;
@@ -35,7 +41,7 @@ use super::tokio_smoltcp::{self, channel_device, Net, NetConfig};
pub(crate) trait NatDstConnector: Send + Sync + Clone + 'static { pub(crate) trait NatDstConnector: Send + Sync + Clone + 'static {
type DstStream: AsyncRead + AsyncWrite + Unpin + Send; type DstStream: AsyncRead + AsyncWrite + Unpin + Send;
async fn connect(&self, dst: SocketAddr) -> Result<Self::DstStream>; async fn connect(&self, src: SocketAddr, dst: SocketAddr) -> Result<Self::DstStream>;
fn check_packet_from_peer_fast(&self, cidr_set: &CidrSet, global_ctx: &GlobalCtx) -> bool; fn check_packet_from_peer_fast(&self, cidr_set: &CidrSet, global_ctx: &GlobalCtx) -> bool;
fn check_packet_from_peer( fn check_packet_from_peer(
&self, &self,
@@ -44,6 +50,7 @@ pub(crate) trait NatDstConnector: Send + Sync + Clone + 'static {
hdr: &PeerManagerHeader, hdr: &PeerManagerHeader,
ipv4: &Ipv4Packet, ipv4: &Ipv4Packet,
) -> bool; ) -> bool;
fn transport_type(&self) -> TcpProxyEntryTransportType;
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -53,7 +60,7 @@ pub struct NatDstTcpConnector;
impl NatDstConnector for NatDstTcpConnector { impl NatDstConnector for NatDstTcpConnector {
type DstStream = TcpStream; type DstStream = TcpStream;
async fn connect(&self, nat_dst: SocketAddr) -> Result<Self::DstStream> { async fn connect(&self, _src: SocketAddr, nat_dst: SocketAddr) -> Result<Self::DstStream> {
let socket = TcpSocket::new_v4().unwrap(); let socket = TcpSocket::new_v4().unwrap();
if let Err(e) = socket.set_nodelay(true) { if let Err(e) = socket.set_nodelay(true) {
tracing::warn!("set_nodelay failed, ignore it: {:?}", e); tracing::warn!("set_nodelay failed, ignore it: {:?}", e);
@@ -90,19 +97,13 @@ impl NatDstConnector for NatDstTcpConnector {
true true
} }
fn transport_type(&self) -> TcpProxyEntryTransportType {
TcpProxyEntryTransportType::Tcp
}
} }
#[derive(Debug, Clone, Copy, PartialEq)] type NatDstEntryState = TcpProxyEntryState;
enum NatDstEntryState {
// receive syn packet but not start connecting to dst
SynReceived,
// connecting to dst
ConnectingDst,
// connected to dst
Connected,
// connection closed
Closed,
}
#[derive(Debug)] #[derive(Debug)]
pub struct NatDstEntry { pub struct NatDstEntry {
@@ -110,6 +111,7 @@ pub struct NatDstEntry {
src: SocketAddr, src: SocketAddr,
dst: SocketAddr, dst: SocketAddr,
start_time: Instant, start_time: Instant,
start_time_local: chrono::DateTime<chrono::Local>,
tasks: Mutex<JoinSet<()>>, tasks: Mutex<JoinSet<()>>,
state: AtomicCell<NatDstEntryState>, state: AtomicCell<NatDstEntryState>,
} }
@@ -121,10 +123,21 @@ impl NatDstEntry {
src, src,
dst, dst,
start_time: Instant::now(), start_time: Instant::now(),
start_time_local: chrono::Local::now(),
tasks: Mutex::new(JoinSet::new()), tasks: Mutex::new(JoinSet::new()),
state: AtomicCell::new(NatDstEntryState::SynReceived), state: AtomicCell::new(NatDstEntryState::SynReceived),
} }
} }
fn into_pb(&self, transport_type: TcpProxyEntryTransportType) -> TcpProxyEntry {
TcpProxyEntry {
src: Some(self.src.clone().into()),
dst: Some(self.dst.clone().into()),
start_time: self.start_time_local.timestamp() as u64,
state: self.state.load().into(),
transport_type: transport_type.into(),
}
}
} }
enum ProxyTcpStream { enum ProxyTcpStream {
@@ -145,6 +158,20 @@ impl ProxyTcpStream {
} }
} }
pub async fn shutdown(&mut self) -> Result<()> {
match self {
Self::KernelTcpStream(stream) => {
stream.shutdown().await?;
Ok(())
}
#[cfg(feature = "smoltcp")]
Self::SmolTcpStream(stream) => {
stream.shutdown().await?;
Ok(())
}
}
}
pub async fn copy_bidirectional<D: AsyncRead + AsyncWrite + Unpin>( pub async fn copy_bidirectional<D: AsyncRead + AsyncWrite + Unpin>(
&mut self, &mut self,
dst: &mut D, dst: &mut D,
@@ -644,7 +671,7 @@ impl<C: NatDstConnector> TcpProxy<C> {
}; };
let _guard = global_ctx.net_ns.guard(); let _guard = global_ctx.net_ns.guard();
let Ok(dst_tcp_stream) = connector.connect(nat_dst).await else { let Ok(dst_tcp_stream) = connector.connect(nat_entry.src, nat_dst).await else {
tracing::error!("connect to dst failed: {:?}", nat_entry); tracing::error!("connect to dst failed: {:?}", nat_entry);
nat_entry.state.store(NatDstEntryState::Closed); nat_entry.state.store(NatDstEntryState::Closed);
Self::remove_entry_from_all_conn_map(conn_map, addr_conn_map, nat_entry); Self::remove_entry_from_all_conn_map(conn_map, addr_conn_map, nat_entry);
@@ -679,7 +706,15 @@ impl<C: NatDstConnector> TcpProxy<C> {
let ret = src_tcp_stream.copy_bidirectional(&mut dst_tcp_stream).await; let ret = src_tcp_stream.copy_bidirectional(&mut dst_tcp_stream).await;
tracing::info!(nat_entry = ?nat_entry_clone, ret = ?ret, "nat tcp connection closed"); tracing::info!(nat_entry = ?nat_entry_clone, ret = ?ret, "nat tcp connection closed");
nat_entry_clone.state.store(NatDstEntryState::Closed); nat_entry_clone.state.store(NatDstEntryState::Closed);
let ret = src_tcp_stream.shutdown().await;
tracing::info!(nat_entry = ?nat_entry_clone, ret = ?ret, "src tcp stream shutdown");
let ret = dst_tcp_stream.shutdown().await;
tracing::info!(nat_entry = ?nat_entry_clone, ret = ?ret, "dst tcp stream shutdown");
drop(src_tcp_stream); drop(src_tcp_stream);
drop(dst_tcp_stream);
// sleep later so the fin packet can be processed // sleep later so the fin packet can be processed
tokio::time::sleep(Duration::from_secs(10)).await; tokio::time::sleep(Duration::from_secs(10)).await;
@@ -802,4 +837,45 @@ impl<C: NatDstConnector> TcpProxy<C> {
pub fn is_tcp_proxy_connection(&self, src: SocketAddr) -> bool { pub fn is_tcp_proxy_connection(&self, src: SocketAddr) -> bool {
self.syn_map.contains_key(&src) || self.addr_conn_map.contains_key(&src) self.syn_map.contains_key(&src) || self.addr_conn_map.contains_key(&src)
} }
pub fn list_proxy_entries(&self) -> Vec<TcpProxyEntry> {
let mut entries: Vec<TcpProxyEntry> = Vec::new();
let transport_type = self.connector.transport_type();
for entry in self.syn_map.iter() {
entries.push(entry.value().as_ref().into_pb(transport_type));
}
for entry in self.conn_map.iter() {
entries.push(entry.value().as_ref().into_pb(transport_type));
}
entries
}
}
#[derive(Clone)]
pub struct TcpProxyRpcService<C: NatDstConnector> {
tcp_proxy: Weak<TcpProxy<C>>,
}
#[async_trait::async_trait]
impl<C: NatDstConnector> TcpProxyRpc for TcpProxyRpcService<C> {
type Controller = BaseController;
async fn list_tcp_proxy_entry(
&self,
_: BaseController,
_request: ListTcpProxyEntryRequest, // Accept request of type HelloRequest
) -> std::result::Result<ListTcpProxyEntryResponse, rpc_types::error::Error> {
let mut reply = ListTcpProxyEntryResponse::default();
if let Some(tcp_proxy) = self.tcp_proxy.upgrade() {
reply.entries = tcp_proxy.list_proxy_entries();
}
Ok(reply)
}
}
impl<C: NatDstConnector> TcpProxyRpcService<C> {
pub fn new(tcp_proxy: Arc<TcpProxy<C>>) -> Self {
Self {
tcp_proxy: Arc::downgrade(&tcp_proxy),
}
}
} }

View File

@@ -2,6 +2,7 @@ use parking_lot::Mutex;
use smoltcp::{ use smoltcp::{
iface::{SocketHandle as InnerSocketHandle, SocketSet}, iface::{SocketHandle as InnerSocketHandle, SocketSet},
socket::tcp, socket::tcp,
time::Duration,
}; };
use std::{ use std::{
ops::{Deref, DerefMut}, ops::{Deref, DerefMut},
@@ -53,6 +54,8 @@ impl SocketAlloctor {
let tx_buffer = tcp::SocketBuffer::new(vec![0; self.buffer_size.tcp_tx_size]); let tx_buffer = tcp::SocketBuffer::new(vec![0; self.buffer_size.tcp_tx_size]);
let mut tcp = tcp::Socket::new(rx_buffer, tx_buffer); let mut tcp = tcp::Socket::new(rx_buffer, tx_buffer);
tcp.set_nagle_enabled(false); tcp.set_nagle_enabled(false);
tcp.set_keep_alive(Some(Duration::from_secs(10)));
tcp.set_timeout(Some(Duration::from_secs(60)));
tcp tcp
} }

View File

@@ -17,8 +17,8 @@ use crate::connector::direct::DirectConnectorManager;
use crate::connector::manual::{ConnectorManagerRpcService, ManualConnectorManager}; use crate::connector::manual::{ConnectorManagerRpcService, ManualConnectorManager};
use crate::connector::udp_hole_punch::UdpHolePunchConnector; use crate::connector::udp_hole_punch::UdpHolePunchConnector;
use crate::gateway::icmp_proxy::IcmpProxy; use crate::gateway::icmp_proxy::IcmpProxy;
use crate::gateway::kcp_proxy::{KcpProxyDst, KcpProxySrc}; use crate::gateway::kcp_proxy::{KcpProxyDst, KcpProxyDstRpcService, KcpProxySrc};
use crate::gateway::tcp_proxy::{NatDstTcpConnector, TcpProxy}; use crate::gateway::tcp_proxy::{NatDstTcpConnector, TcpProxy, TcpProxyRpcService};
use crate::gateway::udp_proxy::UdpProxy; use crate::gateway::udp_proxy::UdpProxy;
use crate::peer_center::instance::PeerCenterInstance; use crate::peer_center::instance::PeerCenterInstance;
use crate::peers::peer_conn::PeerConnId; use crate::peers::peer_conn::PeerConnId;
@@ -380,8 +380,6 @@ impl Instance {
self.check_dhcp_ip_conflict(); self.check_dhcp_ip_conflict();
} }
self.run_rpc_server().await?;
if self.global_ctx.get_flags().enable_kcp_proxy { if self.global_ctx.get_flags().enable_kcp_proxy {
let src_proxy = KcpProxySrc::new(self.get_peer_manager()).await; let src_proxy = KcpProxySrc::new(self.get_peer_manager()).await;
src_proxy.start().await; src_proxy.start().await;
@@ -419,6 +417,8 @@ impl Instance {
#[cfg(feature = "socks5")] #[cfg(feature = "socks5")]
self.socks5_server.run().await?; self.socks5_server.run().await?;
self.run_rpc_server().await?;
Ok(()) Ok(())
} }
@@ -541,6 +541,26 @@ impl Instance {
s.registry() s.registry()
.register(VpnPortalRpcServer::new(vpn_portal_rpc), ""); .register(VpnPortalRpcServer::new(vpn_portal_rpc), "");
if let Some(ip_proxy) = self.ip_proxy.as_ref() {
s.registry().register(
TcpProxyRpcServer::new(TcpProxyRpcService::new(ip_proxy.tcp_proxy.clone())),
"tcp",
);
}
if let Some(kcp_proxy) = self.kcp_proxy_src.as_ref() {
s.registry().register(
TcpProxyRpcServer::new(TcpProxyRpcService::new(kcp_proxy.get_tcp_proxy())),
"kcp_src",
);
}
if let Some(kcp_proxy) = self.kcp_proxy_dst.as_ref() {
s.registry().register(
TcpProxyRpcServer::new(KcpProxyDstRpcService::new(kcp_proxy)),
"kcp_dst",
);
}
let _g = self.global_ctx.net_ns.guard(); let _g = self.global_ctx.net_ns.guard();
Ok(s.serve().await.with_context(|| "rpc server start failed")?) Ok(s.serve().await.with_context(|| "rpc server start failed")?)
} }

View File

@@ -256,7 +256,7 @@ impl EasyTierLauncher {
fetch_node_info, fetch_node_info,
)); ));
if let Err(e) = ret { if let Err(e) = ret {
error_msg.write().unwrap().replace(e.to_string()); error_msg.write().unwrap().replace(format!("{:?}", e));
} }
instance_alive.store(false, std::sync::atomic::Ordering::Relaxed); instance_alive.store(false, std::sync::atomic::Ordering::Relaxed);
notifier.notify_one(); notifier.notify_one();

View File

@@ -177,3 +177,39 @@ service VpnPortalRpc {
rpc GetVpnPortalInfo(GetVpnPortalInfoRequest) rpc GetVpnPortalInfo(GetVpnPortalInfoRequest)
returns (GetVpnPortalInfoResponse); returns (GetVpnPortalInfoResponse);
} }
enum TcpProxyEntryTransportType {
TCP = 0;
KCP = 1;
}
enum TcpProxyEntryState {
Unknown = 0;
// receive syn packet but not start connecting to dst
SynReceived = 1;
// connecting to dst
ConnectingDst = 2;
// connected to dst
Connected = 3;
// connection closed
Closed = 4;
}
message TcpProxyEntry {
common.SocketAddr src = 1;
common.SocketAddr dst = 2;
uint64 start_time = 3;
TcpProxyEntryState state = 4;
TcpProxyEntryTransportType transport_type = 5;
}
message ListTcpProxyEntryRequest {}
message ListTcpProxyEntryResponse {
repeated TcpProxyEntry entries = 1;
}
service TcpProxyRpc {
rpc ListTcpProxyEntry(ListTcpProxyEntryRequest)
returns (ListTcpProxyEntryResponse);
}

View File

@@ -101,7 +101,11 @@ impl From<cidr::Ipv4Inet> for Ipv4Inet {
impl From<Ipv4Inet> for cidr::Ipv4Inet { impl From<Ipv4Inet> for cidr::Ipv4Inet {
fn from(value: Ipv4Inet) -> Self { fn from(value: Ipv4Inet) -> Self {
cidr::Ipv4Inet::new(value.address.unwrap().into(), value.network_length as u8).unwrap() cidr::Ipv4Inet::new(
value.address.unwrap_or_default().into(),
value.network_length as u8,
)
.unwrap()
} }
} }
@@ -168,6 +172,9 @@ impl From<std::net::SocketAddr> for SocketAddr {
impl From<SocketAddr> for std::net::SocketAddr { impl From<SocketAddr> for std::net::SocketAddr {
fn from(value: SocketAddr) -> Self { fn from(value: SocketAddr) -> Self {
if value.ip.is_none() {
return "0.0.0.0:0".parse().unwrap();
}
match value.ip.unwrap() { match value.ip.unwrap() {
socket_addr::Ip::Ipv4(ip) => std::net::SocketAddr::V4(std::net::SocketAddrV4::new( socket_addr::Ip::Ipv4(ip) => std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
std::net::Ipv4Addr::from(ip), std::net::Ipv4Addr::from(ip),

View File

@@ -207,5 +207,6 @@ message HandshakeRequest {
} }
message KcpConnData { message KcpConnData {
common.SocketAddr src = 1;
common.SocketAddr dst = 4; common.SocketAddr dst = 4;
} }

View File

@@ -520,12 +520,13 @@ pub async fn proxy_three_node_disconnect_test(#[values("tcp", "wg")] proto: &str
})); }));
wait_for_condition( wait_for_condition(
|| async { || async {
insts[0] insts[2]
.get_peer_manager() .get_peer_manager()
.list_routes() .get_peer_map()
.list_peers_with_conn()
.await .await
.iter() .iter()
.find(|r| r.peer_id == inst4.peer_id()) .find(|r| **r == inst4.peer_id())
.is_none() .is_none()
}, },
// 0 down, assume last packet is recv in -0.01 // 0 down, assume last packet is recv in -0.01
@@ -534,6 +535,21 @@ pub async fn proxy_three_node_disconnect_test(#[values("tcp", "wg")] proto: &str
Duration::from_secs(11), Duration::from_secs(11),
) )
.await; .await;
wait_for_condition(
|| async {
insts[0]
.get_peer_manager()
.list_routes()
.await
.iter()
.find(|r| r.peer_id == inst4.peer_id())
.is_none()
},
Duration::from_secs(7),
)
.await;
set_link_status("net_d", true); set_link_status("net_d", true);
} }
}); });