hev-socks5-tunnel
A simple, lightweight tunnel over Socks5 proxy (tun2socks).
Features
- IPv4/IPv6. (dual stack)
- Redirect TCP connections.
- Redirect UDP packets. (Fullcone NAT, UDP-in-UDP and UDP-in-TCP [^1])
- Optional local ICMP Echo (ping) replies.
- Linux/Android/FreeBSD/macOS/iOS/Windows.
Benchmarks
See here for more details.
Speed

CPU usage

Memory usage

How to Build
Unix
git clone --recursive https://github.com/heiher/hev-socks5-tunnel
cd hev-socks5-tunnel
make
Android
mkdir hev-socks5-tunnel
cd hev-socks5-tunnel
git clone --recursive https://github.com/heiher/hev-socks5-tunnel jni
ndk-build
iOS and macOS
git clone --recursive https://github.com/heiher/hev-socks5-tunnel
cd hev-socks5-tunnel
# will generate HevSocks5Tunnel.xcframework
./build-apple.sh
Windows (MSVC + Clang)
The same Makefile builds with clang targeting MSVC. Open a Visual
Studio developer prompt (clang from the VS LLVM component or PATH), make
sure GNU make and a POSIX sh.exe (for example the one of Git for Windows,
C:\Program Files\Git\usr\bin) are on PATH, then:
git clone --recursive https://github.com/heiher/hev-socks5-tunnel
cd hev-socks5-tunnel
make
make ARCH=x86
make ARCH=arm64
make shared
ARCH is x64 (default), x86 or arm64 and must match the developer
prompt architecture. The executable is bin/hev-socks5-tunnel.exe; the shared
library is bin/hev-socks5-tunnel.dll with the import library
bin/hev-socks5-tunnel.dll.lib, it embeds the third-parts. Run make clean
when switching between the executable and the shared library.
The Wintun driver is loaded at runtime, put the
wintun.dll of the same architecture next to the executable and run it as
Administrator. On Windows there is no daemon mode (pid-file only writes
the process id), no limit-nofile and no mark. Stop the tunnel with Ctrl+C.
Library
git clone --recursive https://github.com/heiher/hev-socks5-tunnel
cd hev-socks5-tunnel
# Static library
make static
# Shared library
make shared
How to Use
Config
tunnel:
# Interface name
name: tun0
# Interface GUID (Windows)
# guid: 12345678-9abc-def0-1234-56789abcdef0
# Interface MTU
mtu: 8500
# Multi-queue
multi-queue: false
# IPv4 address
ipv4: 198.18.0.1
# IPv6 address
ipv6: 'fc00::1'
# ICMP Echo mode (off|reply)
icmp: 'off'
# Offloads (Linux), each one is used only if the kernel supports it
# offload:
# TCP segmentation offload
# tso: true
# UDP segmentation offload
# uso: true
# Send TCP segments larger than the MTU without TSO, only for local
# delivery, disable it if the traffic is forwarded to other interfaces
# jumbo: true
# Post up script
# post-up-script: up.sh
# Pre down script
# pre-down-script: down.sh
socks5:
# Socks5 server port
port: 1080
# Socks5 server address (ipv4/ipv6)
address: 127.0.0.1
# Socks5 UDP relay mode (tcp|udp)
udp: 'udp'
# Override the UDP address provided by the Socks5 server (ipv4/ipv6)
# udp-address: ''
# Socks5 handshake using pipeline mode
# pipeline: false
# Socks5 server username
# username: 'username'
# Socks5 server password
# password: 'password'
# Socket mark
# mark: 0
# TCP fastopen
# tcp-fastopen: false
#mapdns:
# Mapped DNS address
# address: 198.18.0.2
# Mapped DNS port
# port: 53
# Mapped IP network base
# network: 100.64.0.0
# Mapped IP network mask
# netmask: 255.192.0.0
# Mapped DNS cache size
# cache-size: 10000
#misc:
# task stack size (bytes)
# task-stack-size: 86016
# wintun ring buffer capacity (bytes, windows only, power of two,
# range: 131072 - 67108864)
# wintun-ring-capacity: 524288
# tcp buffer size (bytes)
# tcp-buffer-size: 65536
# udp socket recv buffer (SO_RCVBUF) size (bytes)
# udp-recv-buffer-size: 524288
# number of udp buffers in splice, 1500 bytes per buffer.
# udp-copy-buffer-nums: 10
# maximum session count (0: unlimited)
# max-session-count: 0
# connect timeout (ms)
# connect-timeout: 10000
# TCP read-write timeout (ms)
# tcp-read-write-timeout: 300000
# UDP read-write timeout (ms)
# udp-read-write-timeout: 60000
# null, stdout, stderr or file-path
# log-file: null
# debug, info, warn or error
# log-level: warn
# If present, run as a daemon with this pid file
# pid-file: /run/hev-socks5-tunnel.pid
# If present, set rlimit nofile; else use default value
# limit-nofile: 65535
Run
Linux
# Set socks5.mark = 438
bin/hev-socks5-tunnel conf/main.yml
# Disable reverse path filter
sudo sysctl -w net.ipv4.conf.all.rp_filter=0
sudo sysctl -w net.ipv4.conf.tun0.rp_filter=0
# Bypass upstream socks5 server
sudo ip rule add fwmark 438 lookup main pref 10
sudo ip -6 rule add fwmark 438 lookup main pref 10
# Route others
sudo ip route add default dev tun0 table 20
sudo ip rule add lookup 20 pref 20
sudo ip -6 route add default dev tun0 table 20
sudo ip -6 rule add lookup 20 pref 20
FreeBSD/macOS
# Bypass upstream socks5 server
# 10.0.0.1: socks5 server
# 10.0.2.2: default gateway
sudo route add -net 10.0.0.1/32 10.0.2.2
# Route others
sudo route change -inet default -interface tun0
sudo route change -inet6 default -interface tun0
Windows
# Bypass upstream socks5 server
# 10.0.0.1: socks5 server
# 10.0.2.2: default gateway
route add 10.0.0.1/32 10.0.2.2
# Route others
route change 0.0.0.0/0 0.0.0.0 if tun-index
route change ::/0 :: if tun-index
OpenWrt 24.10+
Repo: https://github.com/openwrt/packages/tree/master/net/hev-socks5-tunnel
# Install package
opkg install hev-socks5-tunnel
# Edit /etc/config/hev-socks5-tunnel
# Restart service
/etc/init.d/hev-socks5-tunnel restart
Low memory usage
On low-memory systems like iOS, reducing the size of the TCP buffer and task stack, as well as limiting the maximum session count, can help prevent out-of-memory issues.
misc:
# task stack size (bytes)
task-stack-size: 24576 # 20480 + tcp-buffer-size
# tcp buffer size (bytes)
tcp-buffer-size: 4096
# maximum session count
max-session-count: 1200
Docker Compose
version: "3.9"
services:
client:
image: alpine:latest # just for network testing
tty: true # you can test network in terminal
depends_on:
tun:
condition: service_healthy
network_mode: "service:tun"
tun:
image: ghcr.io/heiher/hev-socks5-tunnel:latest # `latest` for the latest published version; `nightly` for the latest source build; `vX.Y.Z` for the specific version
cap_add:
- NET_ADMIN # needed
devices:
- /dev/net/tun:/dev/net/tun # needed
environment:
TUN: tun0 # optional, tun interface name, default `tun0`
MTU: 8500 # optional, MTU is MTU, default `8500`
IPV4: 198.18.0.1 # optional, tun interface ip, default `198.18.0.1`
IPV6: fc00::1 # optional, tun interface ip
ICMP: off # optional, ICMP Echo mode, default `off`, other option `reply`
TABLE: 20 # optional, ip route table id, default `20`
MARK: 438 # optional, ip route rule mark, dec or hex format, default `438`
SOCKS5_ADDR: a.b.c.d # socks5 proxy server address
SOCKS5_PORT: 1080 # socks5 proxy server port
SOCKS5_USERNAME: user # optional, socks5 proxy username, only set when need to auth
SOCKS5_PASSWORD: pass # optional, socks5 proxy password, only set when need to auth
SOCKS5_UDP_MODE: udp # optional, UDP relay mode, default `udp`, other option `tcp`
SOCKS5_UDP_ADDR: a.b.c.d # optional, override the UDP address provided by the Socks5 server
CONFIG_ROUTES: 1 # optional, set 0 to ignore TABLE, IPV4_INCLUDED_ROUTES and IPV4_EXCLUDED_ROUTES, with MARK defaults to 0
IPV4_INCLUDED_ROUTES: 0.0.0.0/0 # optional, demo means proxy all traffic. for multiple network segments, join with `,` or `\n`
IPV4_EXCLUDED_ROUTES: a.b.c.d # optional, demo means exclude traffic from the proxy itself. for multiple network segments, join with `,` or `\n`
DNS_CACHE: 1 # optional, enable the lightweight caching resolver (dnsmasq) for use with mapdns disabled, default `0`
DNS_CACHE_SERVERS: 8.8.8.8,1.1.1.1 # optional, upstream resolvers of the cache, they are queried via the tun device, default `8.8.8.8,1.1.1.1`
DNS_CACHE_SIZE: 10000 # optional, number of cached DNS records, default `10000`
LOG_LEVEL: warn # optional, default `warn`, other option `debug`/`info`/`error`
dns:
- 127.0.0.1 # point clients at the caching resolver when DNS_CACHE=1, or a remote resolver like 8.8.8.8 otherwise
DNS caching
The tunnel has no built-in DNS cache when mapdns is disabled, but the Docker
image ships an optional lightweight caching resolver (dnsmasq). Enable it with
DNS_CACHE=1, no entrypoint override needed. It listens on 127.0.0.1 and the
tun interface addresses, so clients sharing the network namespace
(network_mode: "service:tun") use it with dns: 127.0.0.1 (or the tun IPv4,
e.g. dns: 198.18.0.1). Upstream servers (DNS_CACHE_SERVERS) are queried
through the tun device, i.e. resolved remotely via the Socks5 server, and
successful answers are cached locally (DNS_CACHE_SIZE records).
You can also set the route rules with multiple network segments like:
environment:
IPV4_INCLUDED_ROUTES: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
IPV4_EXCLUDED_ROUTES: |-
a.b.c.d/24
a.b.c.f/24
API
C
/**
* hev_socks5_tunnel_main:
* @config_path: config file path
* @tun_fd: tunnel file descriptor
*
* Start and run the socks5 tunnel, this function will blocks until the
* hev_socks5_tunnel_quit is called or an error occurs.
*
* Alias of hev_socks5_tunnel_main_from_file
*
* Returns: returns zero on successful, otherwise returns -1. *
* Since: 2.4.6
*/
int hev_socks5_tunnel_main (const char *config_path, int tun_fd);
/**
* hev_socks5_tunnel_main_from_file:
* @config_path: config file path
* @tun_fd: tunnel file descriptor
*
* Start and run the socks5 tunnel, this function will blocks until the
* hev_socks5_tunnel_quit is called or an error occurs.
*
* Returns: returns zero on successful, otherwise returns -1. *
* Since: 2.6.7
*/
int hev_socks5_tunnel_main_from_file (const char *config_path, int tun_fd);
/**
* hev_socks5_tunnel_main_from_str:
* @config_str: string config
* @config_len: the byte length of string config
* @tun_fd: tunnel file descriptor
*
* Start and run the socks5 tunnel, this function will blocks until the
* hev_socks5_tunnel_quit is called or an error occurs.
*
* Returns: returns zero on successful, otherwise returns -1. *
* Since: 2.6.7
*/
int hev_socks5_tunnel_main_from_str (const unsigned char *config_str,
unsigned int config_len, int tun_fd);
/**
* hev_socks5_tunnel_quit:
*
* Stop the socks5 tunnel.
*
* Since: 2.4.6
*/
void hev_socks5_tunnel_quit (void);
/**
* hev_socks5_tunnel_stats:
* @tx_packets (out): transmitted packets
* @tx_bytes (out): transmitted bytes
* @rx_packets (out): received packets
* @rx_bytes (out): received bytes
*
* Retrieve tunnel interface traffic statistics.
*
* Since: 2.6.5
*/
void hev_socks5_tunnel_stats (size_t *tx_packets, size_t *tx_bytes,
size_t *rx_packets, size_t *rx_bytes);
Java
public class TProxyService {
private static native boolean TProxyStartService(String config_path, int fd);
private static native boolean TProxyStopService();
private static native boolean TProxyIsRunning();
private static native long[] TProxyGetStats();
static {
System.loadLibrary("hev-socks5-tunnel");
}
}
Kotlin
object TProxyService {
private external fun TProxyStartService(config_path: String, fd: Int): Boolean
private external fun TProxyStopService(): Boolean
private external fun TProxyIsRunning(): Boolean
private external fun TProxyGetStats(): LongArray
init {
System.loadLibrary("hev-socks5-tunnel")
}
}
Allow overriding the package and class names in Application.mk[^2].
APP_CFLAGS := -DPKGNAME=hev/sockstun -DCLSNAME=TProxyService
Prebuilt Android AAR
The Android CI job also produces an AAR (hev-socks5-tunnel.aar, containing all four ABIs)
built with the default JNI contract — no PKGNAME/CLSNAME overrides — so its natives
register to hev.htproxy.TProxyService. The AAR is self-contained: the binding class below
is bundled in it (classes.jar, plus a proguard.txt that keeps it), so it is called
directly and no shim class has to be added to the app:
package hev.htproxy;
public final class TProxyService {
public static native boolean TProxyStartService(String config_path, int fd);
public static native boolean TProxyStopService();
public static native boolean TProxyIsRunning();
public static native long[] TProxyGetStats();
static {
System.loadLibrary("hev-socks5-tunnel");
}
}
Then call these methods from your own code; no per-package rebuild is required. The AAR is
also attached to tagged releases. Consumers that need a custom package or class name can
still override PKGNAME/CLSNAME in Application.mk as shown above and build themselves.
Use Cases
Android VPN
iOS
Contributors
- acidroper - https://github.com/acidroper
- arror - https://github.com/arror
- bazuchan - https://github.com/bazuchan
- bitmold - https://github.com/bitmold
- codewithtamim - https://github.com/codewithtamim
- daemooon - https://github.com/daemooon
- dovecoteescapee - https://github.com/dovecoteescapee
- ebrahimtahernejad - https://github.com/ebrahimtahernejad
- egavr - https://github.com/egavr
- fwx - https://github.com/fwx
- heiby - https://github.com/heiby
- hev - https://hev.cc
- ihipop - https://ihipop.com
- katana - https://github.com/officialkatana
- mmjvox - https://github.com/mmjvox
- nange - https://github.com/nange
- plangto - https://github.com/plangto
- popsiclelmlm - https://github.com/popsiclelmlm
- pronebird - https://github.com/pronebird
- saeeddev94 - https://github.com/saeeddev94
- simplerick-simplefun - https://github.com/simplerick-simplefun
- sskaje - https://github.com/sskaje
- wankkoree - https://github.com/wankkoree
- xiguagua - https://github.com/xiguagua
- xz-dev - https://github.com/xz-dev
- yiguodev - https://github.com/yiguodev
- yujinpan - https://github.com/yujinpan
- zheshinicheng - https://github.com/zheshinicheng
License
MIT
[^1]: See protocol specification. The hev-socks5-server supports UDP relay over TCP. [^2]: See Application.mk
