hev-socks5-tunnel

Introduction: A high-performance tun2socks for Linux/Android/FreeBSD/macOS/iOS/WSL2 (IPv4/IPv6/TCP/UDP)
More: Author   ReportBugs   
Tags:
Socks5 proxy-

status

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

License

MIT

[^1]: See protocol specification. The hev-socks5-server supports UDP relay over TCP. [^2]: See Application.mk

Apps
About Me
GitHub: Trinea
Facebook: Dev Tools
AI Daily Digest