# HevSocks5Tunnel: routing all TCP and UDP traffic through a SOCKS5 proxy with a tun device

> HevSocks5Tunnel is a C implementation of tun2socks that intercepts raw IP packets at a virtual network interface and forwards them to a SOCKS5 server. It builds from source on six platforms and ships a Docker image for Linux container environments.

**heiher/hev-socks5-tunnel** — A lightweight, fast and reliable tun2socks

- Repository: https://github.com/heiher/hev-socks5-tunnel
- Stars: 2,058 · Forks: 399
- Language: C
- License: MIT
- Published: 2026-10-11 · Updated: 2026-10-11 · Language: en
- Canonical page: https://hysenlabs.com/projects/heiher-hev-socks5-tunnel

## tun device puts a C process between the kernel routing table and the SOCKS5 upstream

Transparent proxying without per-application configuration is the problem this project addresses. Instead of asking each program to speak SOCKS5, the tunnel claims a virtual tun interface. All IP packets the kernel would send to the outside world are delivered to the hev-socks5-tunnel process instead, which reads them from the tun device, reconstructs TCP connections and UDP sessions using an embedded lwip TCP/IP stack, and opens fresh connections to the configured SOCKS5 server.

The Makefile names three third-party dependencies pulled as git submodules: yaml, lwip and hev-task-system. yaml handles config parsing, lwip is the embedded TCP/IP stack, and hev-task-system provides a coroutine-based task scheduler. All three live under third-part/ and must be present at build time.

An alternative approach is iptables-based transparent proxying, where a tool intercepts TCP connections at the socket level using firewall redirect rules rather than a tun device. That requires no routing table changes, but iptables is absent on macOS, iOS and Windows. HevSocks5Tunnel targets all six of those platforms: Linux, Android, FreeBSD, macOS, iOS and Windows.

## Building on Unix needs --recursive; Android uses NDK, Apple uses build-apple.sh

The Unix build path is three commands:

```bash
git clone --recursive https://github.com/heiher/hev-socks5-tunnel
cd hev-socks5-tunnel
make
```

Without --recursive, the three submodule directories in third-part/ are empty and the build fails on the first missing header. The Makefile places the binary at bin/hev-socks5-tunnel and expects config at conf/main.yml.

Android requires the NDK and places the sources under a jni/ directory:

```bash
mkdir hev-socks5-tunnel
cd hev-socks5-tunnel
git clone --recursive https://github.com/heiher/hev-socks5-tunnel jni
ndk-build
```

Apple platforms use a build script that wraps Xcode and produces a framework:

```bash
git clone --recursive https://github.com/heiher/hev-socks5-tunnel
cd hev-socks5-tunnel
./build-apple.sh
```

The script comment says it will generate HevSocks5Tunnel.xcframework. Windows builds under MSYS2 and needs one environment variable set before cloning to permit the symlinks the build system creates:

```bash
export MSYS=winsymlinks:native
git clone --recursive https://github.com/heiher/hev-socks5-tunnel
cd hev-socks5-tunnel
make
```

OpenWrt 24.10 and later is the only non-Docker platform with a pre-built package:

```sh
opkg install hev-socks5-tunnel
```

After installation the service config lives at /etc/config/hev-socks5-tunnel, and /etc/init.d/hev-socks5-tunnel restart applies changes. For every other platform outside Docker, a C toolchain is required.

## The YAML config connects the tun interface to the SOCKS5 upstream in a single file

The config has four top-level keys: tunnel, socks5, mapdns and misc. The first two are required for any deployment; the last two are optional and commented out in the default config.

The tunnel block names the interface and sets its addresses:

```yaml
tunnel:
  name: tun0
  mtu: 8500
  multi-queue: false
  ipv4: 198.18.0.1
  ipv6: 'fc00::1'
  icmp: 'off'
```

Setting icmp to reply instead of off makes the tun interface respond to ping, which is useful for verifying that the tunnel is active. The ipv6 field can be left empty if IPv6 is not needed.

The socks5 block points at the upstream proxy:

```yaml
socks5:
  port: 1080
  address: 127.0.0.1
  udp: 'udp'
```

Authentication credentials are commented out in the default config and only need values when the server requires them. The mark key (default 438) is what the Linux routing setup references in its ip rule commands, and the udp key controls UDP relay mode (covered separately).

The misc section exposes timeout controls. Connect timeout defaults to 10000 ms. TCP connections time out after 300000 ms by default, and UDP sessions after 60000 ms. All three are commented out in the default config and take these defaults when absent. Post-up and pre-down script hooks are also available as commented-out options (post-up-script and pre-down-script), though the config gives no examples of their contents.

## Linux routing needs fwmark 438 to keep SOCKS5 connections from re-entering tun0

Starting the binary takes a path to the config:

```bash
bin/hev-socks5-tunnel conf/main.yml
```

On Linux, several routing commands must follow. Reverse path filtering must first be disabled on both the tun interface and globally, because packets arriving on tun0 carry external source addresses that the kernel otherwise treats as invalid:

```bash
sudo sysctl -w net.ipv4.conf.all.rp_filter=0
sudo sysctl -w net.ipv4.conf.tun0.rp_filter=0
```

Next, add routing rules that send packets marked 438 through the main routing table rather than the tunnel table. That mark is applied by socks5.mark to every connection the tunnel opens toward the SOCKS5 server:

```bash
sudo ip rule add fwmark 438 lookup main pref 10
sudo ip -6 rule add fwmark 438 lookup main pref 10
```

Finally, set up table 20 to route all other traffic through tun0:

```bash
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
```

Priority ordering matters here. The fwmark rule at pref 10 is evaluated before the default-route rule at pref 20. Without the fwmark bypass, every connection the tunnel opens to the SOCKS5 server would follow pref 20, enter tun0, and try to proxy itself indefinitely.

FreeBSD, macOS and Windows use route commands instead of ip rule; those appear in the README under separate platform headings.

## UDP relay defaults to UDP-in-UDP; Fullcone NAT requires the upstream server's cooperation

Three UDP-related features appear in the project's feature list: UDP-in-UDP relay, UDP-in-TCP relay and Fullcone NAT. The socks5.udp config key selects between the first two: 'udp' (the default) uses standard SOCKS5 UDP associate, while 'tcp' wraps UDP payloads inside a TCP connection to the SOCKS5 server.

TCP mode is useful when the network between client and proxy blocks raw UDP traffic. Switching to it requires no other config change.

Fullcone NAT means that after the tunnel sends a UDP packet from an inside address, return packets from any external source address are accepted on the mapped port. Applications that use UDP hole-punching depend on this behavior. Whether it holds end-to-end is determined by the upstream SOCKS5 server, not by this tool. A server that routes all UDP through a single outbound address cannot provide Fullcone behavior regardless of how the client is configured.

An additional config key, socks5.udp-address, overrides the UDP relay address the SOCKS5 server reports during the handshake. This is relevant when the server advertises an internal address that the client cannot reach. The README gives no example of when to use this, only the config key name and its type.

## Low-memory deployments require task-stack-size to follow a formula tied to tcp-buffer-size

The default config shows task-stack-size at 86016 bytes and tcp-buffer-size at 65536 bytes. A close reading of the low-memory YAML example in the README reveals why these two values are coupled. The comment on task-stack-size reads "20480 + tcp-buffer-size". For the defaults: 20480 + 65536 = 86016. For the low-memory example, tcp-buffer-size is 4096 and task-stack-size is 24576: 20480 + 4096 = 24576 again.

This means the two settings cannot be changed independently. Lower tcp-buffer-size to save memory and task-stack-size must drop by the same amount, or the coroutine will overflow its stack allocation. The README states this relationship only as a comment in a code example, not as a documented constraint in the config reference.

The low-memory YAML for iOS that the README provides:

```yaml
misc:
  task-stack-size: 24576
  tcp-buffer-size: 4096
  max-session-count: 1200
```

The max-session-count key defaults to 0, meaning unlimited concurrent sessions. Setting it to 1200 caps that number to limit memory consumption on a constrained device. An additional key, udp-copy-buffer-nums, controls how many 1500-byte UDP buffers are used in splice operations, though the default value is not shown in the config comments.

## Docker Compose runs the tunnel as a sidecar; the Dockerfile default SOCKS5 address targets 172.17.0.1

The Docker image at ghcr.io/heiher/hev-socks5-tunnel supports three tags: latest for the most recent published release, nightly for the most recent source build, and vX.Y.Z for a pinned version.

Running the container requires two things: the NET_ADMIN capability and a device binding for /dev/net/tun:

```yaml
tun:
  image: ghcr.io/heiher/hev-socks5-tunnel:latest
  cap_add:
    - NET_ADMIN
  devices:
    - /dev/net/tun:/dev/net/tun
```

Other services that should route through the tunnel set network_mode: "service:tun" rather than their own network configuration. The Docker Compose example in the README shows a client container that depends on the tun service being healthy before it starts, using the depends_on condition: service_healthy pattern.

A detail in the Dockerfile's ENV block: SOCKS5_ADDR defaults to 172.17.0.1, which is the Docker bridge gateway address. If the SOCKS5 proxy runs on the Docker host, SOCKS5_ADDR does not need to be overridden in the Compose file because that default already points at the host. The Docker Compose example in the README shows SOCKS5_ADDR: a.b.c.d as a placeholder requiring an explicit value, which gives the impression that overriding is always necessary. For a host-side proxy, only SOCKS5_PORT needs to be set.

The Makefile also supports building static and shared library targets (make static and make shared) for embedding the tunnel in other applications. Those targets set the ENABLE_LIBRARY flag and compile with -fPIC for the shared variant.

## Conclusion

HevSocks5Tunnel suits engineers who need transparent proxy support where per-application settings are not feasible, such as Android apps and iOS network extensions. Skip it when you need a ready-made binary: only OpenWrt and Docker ship packages, and other platforms require building from source. On Linux, confirm socks5.mark in the config matches the fwmark in your ip rule entries, since a mismatch silently routes SOCKS5 connections back through tun0 and creates a forwarding loop. Check that your upstream SOCKS5 server supports UDP associate before relying on Fullcone NAT.

## FAQ

### What is HevSocks5Tunnel?

HevSocks5Tunnel is a tun2socks implementation that routes TCP connections and UDP packets through a SOCKS5 proxy by capturing raw IP packets from a virtual tun interface. It supports dual-stack IPv4 and IPv6 and runs on Linux, Android, FreeBSD, macOS, iOS and Windows.

### How do I build HevSocks5Tunnel on Linux?

Clone with git clone --recursive https://github.com/heiher/hev-socks5-tunnel and run make. The --recursive flag is required because the build depends on three submodules in third-part/: yaml, lwip and hev-task-system.

### Why does the Linux setup require ip rule commands after starting the binary?

The tunnel marks its own connections to the SOCKS5 server with fwmark 438 so those packets follow the main routing table rather than entering tun0. Without an ip rule matching that mark at a lower preference, the SOCKS5 connections follow the default tunnel route and loop back through themselves.

### What UDP relay modes does HevSocks5Tunnel support?

The socks5.udp config key selects the mode: 'udp' uses standard UDP-in-UDP relay and 'tcp' wraps UDP payloads inside a TCP connection to the proxy. Fullcone NAT is supported at the local interface but requires server cooperation to hold end-to-end.

### Does HevSocks5Tunnel require authentication to connect to the SOCKS5 server?

Authentication is optional. Set socks5.username and socks5.password in the config file when the server requires credentials. Both keys are commented out in the default config, and omitting them means no credentials are sent during the handshake.

## Sources

- [heiher/hev-socks5-tunnel on GitHub](https://github.com/heiher/hev-socks5-tunnel)
- [Issues](https://github.com/heiher/hev-socks5-tunnel/issues)
- [License: MIT](https://github.com/heiher/hev-socks5-tunnel/blob/main/LICENSE)
- [README](https://github.com/heiher/hev-socks5-tunnel/blob/main/README.md)
- [Releases](https://github.com/heiher/hev-socks5-tunnel/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/heiher-hev-socks5-tunnel
