Encrypted Iran ↔ Kharej port forwarding that keeps working when the network is filtered, even when only ping gets through.
English · فارسی
WatTunnel connects an Iran server (where your users connect) to a Kharej server (where your service runs) and forwards any TCP or UDP port between them. One static binary, no dependencies, and a menu that sets everything up from one pasted token.
3.0.0 is the anti-censorship release. When the route is filtered, the tunnel does not slow down: it changes shape.
It rides whatever your route lets through (icmp, udp, tcp, tls, ws, wss, h2, h2c, grpc, Cloudflare
with ECH, reverse mode, or several of them bonded into one tunnel), and since 2.6.0 it has learned to get past
permit-lists, flow cutters and traffic classifiers that kill every ordinary tunnel.
On a route that cuts every direct Iran → Kharej flow within a second (UDP after 3 datagrams, TCP after 3 KB, on every port, with every payload), a plain tunnel connects for a moment and dies. WatTunnel 3.0.0 was field-tested on exactly such a pair, and these are the results through that filter:
| Setup on the filtered route | Download | Upload |
|---|---|---|
Direct udp-dns: every datagram looks like DNS on port 53 (400 MB run) |
36 MB/s | 29 MB/s |
Direct udp-dns, 4 sessions |
32 MB/s | 24 MB/s |
Direct tcp + fake-SNI rst, pool-rotate |
28 MB/s | 32 MB/s |
Direct tcp + fake-SNI rst, 4 sessions |
21 MB/s | 29 MB/s |
Reverse tcp (Kharej dials Iran), pool-rotate |
28 MB/s | 32 MB/s |
Reverse wss / tls / h2 |
22-25 MB/s | 27-30 MB/s |
Reverse udp (Iran never sends first) |
21 MB/s | 19 MB/s |
One vCPU per server, 82 ms round trip, one run each, so read these as orders of magnitude (the pair also passes ICMP, at 7-10 MB/s). Every one of them is a tunnel that plain tools cannot bring up at all on this route. Old and new releases work together. Conditions, failures and the full list of 92 configurations: docs/TECHNICAL.md.
Everything below is shipped, tested and reachable from the menu (wattun → tunnel page → z for the anti-censor
presets).
Looks like nothing a classifier knows (3.0.0)
- No constant byte in any packet. The session id and packet number are masked (
hdr-mask), so there is no fixed prefix and no counter to fingerprint. Idle packets are padded to random sizes and the keepalive interval is jittered (shape). - Reverse
udp. The Iran server only listens and never starts a flow towards a foreign address. Kharej sends authenticated punches. Previously only the TCP-family carriers had a reverse mode. udp-dns. Every UDP datagram starts like a DNS query (and every answer like a DNS answer) on port 53, with a fresh id and name each time. It is the one shape the measured filter let through at full speed.- TLS-shaped raw
tcp(tcp-mimic). The first bytes of atcptunnel are a Chrome-like 517-byte ClientHello carrying the authentication, with masked frame lengths, not a plain pool id. - Pick the source address of the tunnel from the manager (
src-ip), for servers with several IPs.
Gets past permit-lists and flow cutters (2.6.0 - 2.10.12)
- Fake-SNI desync (
fake-sni). When a filter lets a flow live only if its TLS name is on a list, the tunnel sends a decoy hello that the filter reads and the server ignores. Modes:seq,ttl(calibrated automatically),sum,seqsum,rst,synrst,split,hlen0,fakeip,all, andauto, which probes the filter and picks one. Works ontcp,tls,ws,wss,h2,grpc, over IPv4 and IPv6, also from a reverse server. Field result: awsstunnel through Cloudflare connected under a Cloudflare-only whitelist withrst, while every other mode did not. - Churn (
pool-rotate). Connections are recycled at random times or byte budgets, so none shows a stable lifetime. - ECH, even when DNS is hijacked (
ech: ladder): the ECH config is fetched through the tunnel itself. tls-small-chkeeps the ClientHello at about 250 bytes for DPIs that drop large hellos.- ICMP volume governor learns a path's reroute ceiling, so the tunnel stays under it.
wattun blackout NAMEturns the whole kit on with one command,wattun matrix NAMEtries every combination from the Iran server and applies the best one.
Safer and tougher
- Forward secrecy on every session (X25519 key exchange, 2.10.0): recorded traffic stays unreadable even if a key file leaks later.
- Signed installer: every download is checked against an RSA-signed checksum list, so a mirror cannot swap the binary.
- Reliability: no stall after a short one-way blackout, no hang after a Kharej restart, honest
alivein/stats, a header mask that no longer stays off when the client starts before its server, fixed data races and buffer reuse. - Manager: anti-censor toggle with presets (Cloudflare first), reverse switch, resilience profiles, DNS-shaped option in the Kharej wizard, auto-heal, update with rollback.
Measured on real Iran ⇄ Europe servers (2 vCPU each), October 2026, on an unfiltered path. Ping is the round trip through the tunnel while it is fully loaded; the route's own ping is about 40 ms.
| Connection type | Download | Upload | Ping under load |
|---|---|---|---|
| TCP / TLS / reverse (2.5.1, production tunnel with users on it) | 195 MB/s (~1.6 Gbit) | 276 MB/s (~2.2 Gbit) | 40-51 ms |
| ICMP (only ping passes on the route) | up to ~33 MB/s, the route's maximum | 26 MB/s | 40-45 ms |
Cloudflare CDN (wss + ECH, 4 sessions) |
44-48 MB/s | up to 82 MB/s | 109-116 ms (CDN base 86) |
Plain TCP with no tunnel at all reaches 243 MB/s down on the same route with 16 connections. WatTunnel, encrypted, reaches 80 % of that. Full tables, every version and how each was measured: docs/TECHNICAL.md.
Same servers, 2026-10-25, on a route where Iran → Kharej directly is cut about a second into every flow, and Iran →
Cloudflare passes only flows whose TLS name is www.cloudflare.com.
| Setup | Result |
|---|---|
Reverse tcp (Kharej dials Iran), with or without fake-sni |
36-40 MB/s down, 30-36 MB/s up, 38-46 ms idle |
wss through a Cloudflare edge, real name filtered, fake-mode: rst |
connected in 2 s: 9-15 MB/s down, 2-5 MB/s up, ~125 ms; 100 MB test 13.8 MB/s down, 4.9 MB/s up |
Details and the failures: docs/TECHNICAL.md.
Same servers, same filtered route (only ICMP passes), real VLESS traffic through each tunnel:
| Tool | Throughput | Latency p50 | Iran CPU per MB | RAM |
|---|---|---|---|---|
| WatTunnel | up to ~33 MB/s | 40-45 ms | 0.026 CPU-s | ~40 MB |
| GOST v3 ICMP | 1.6-5.4 MB/s | ~51 ms | 0.15-0.20 CPU-s | 50-70 MB |
| BackPack (tuned) | ~6.1 MB/s | 102 ms | - | - |
| Hans | 1.1-3.8 MB/s | - | - | - |
| WireGuard over pingtunnel | 0.25-2.7 MB/s | - | - | - |
| pingtunnel | ~0.5 MB/s under load | - | CPU saturates | - |
| ptunnel-ng | ~20 KB/s | - | - | - |
That is about 5x the next best ICMP tunnel, 6-20x GOST at 6-7x less CPU, and one process carries 250-300 real users.
| WatTunnel | GOST | pingtunnel | Hans | ptunnel-ng | |
|---|---|---|---|---|---|
| ICMP carrier | ✅ | ✅ | ✅ | ✅ | ✅ |
| UDP / TCP / TLS / WS / gRPC carriers | ✅ | ✅ | ❌ | ❌ | ❌ |
| Cloudflare/CDN with ECH | ✅ | - | ❌ | ❌ | ❌ |
| Fake-SNI desync, DNS-shaped UDP, masked headers | ✅ | ❌ | ❌ | ❌ | ❌ |
| One token tests every connection type for you | ✅ | ❌ | ❌ | ❌ | ❌ |
| Loss does not slow it down (delay-based pacing) | ✅ | ❌ | ❌ | ❌ | ❌ |
| All ports in one process | ✅ | ✅ | ❌ | ✅ (tun) | ❌ |
| Add or remove ports without a restart | ✅ | ❌ | ❌ | - | ❌ |
| Reverse mode / reverse ICMP | ✅ / ✅ | ✅ / ❌ | ❌ | ❌ | ❌ |
| Per-port traffic stats and quotas | ✅ | ❌ | ❌ | ❌ | ❌ |
| Guided menu, auto-heal, update and rollback | ✅ | ❌ | ❌ | ❌ | ❌ |
On a clean, unfiltered link every tunnel reaches line rate and GOST uses a little less CPU; WatTunnel still halves small-request latency there (43 ms vs 83 ms) because interactive traffic goes first. Benchmark details.
An idle tunnel sends about 2 small packets a second, about 133 bytes/s with headers on udp or icmp. That is
~11.5 MB a day, or ~345 MB a month. It uses 0.1-0.2 % of one CPU core and 10-17 MB of RAM with no users. A busy ICMP
tunnel with 250-300 users runs in about 40 MB.
Under load, the tunnel adds +0.25 % to the bytes your users move on tcp (16 KB packets), and about +5 % on udp or
icmp with mtu: 1400, headers included.
You need two Linux servers (amd64 or arm64) with systemd and root. Set up Kharej first: it makes the token that Iran needs.
1. Install on both servers
bash <(curl -fsSL https://raw.githubusercontent.com/JohnWatsoon/WatTunnel/main/install.sh)GitHub is blocked on the server?
WAT_PROXY=https://ghproxy.net/ bash <(curl -fsSL https://ghproxy.net/https://raw.githubusercontent.com/JohnWatsoon/WatTunnel/main/install.sh)The proxy is a third-party mirror: it sees and could alter the bytes. install.sh therefore checks every download
against an RSA-signed checksum list, with the public key baked into the script, and refuses a mismatch (openssl
must be installed; it is everywhere).
Or download wattun-linux-amd64, install.sh and wattun-manager from the
releases page, put the binary in dist/ next to install.sh,
and run bash install.sh.
2. Kharej. Run wattun, press n (new tunnel), choose Kharej, and pick Auto as the connection type. You
get a token:
wat1:aWNtcHwyMDMuMC4xMTMuMTB8a1E3...
3. Iran. Run wattun, press n, choose Iran, and paste the token. The menu tests every connection type
(about 2-3 minutes) and offers the fastest that works: press Enter to take it. Then enter your ports, for example
443, 8443:443, 53/udp (8443:443 means users connect to Iran port 8443 and reach Kharej port 443).
✔ Writing key and config
✔ Starting wattun@iran
✔ CONNECTED (RTT 41.3 ms)
users ──► Iran :8443 ══ tcp ══► Kharej ──► 127.0.0.1:443
Done. Users connect to the Iran server's IP, and Kharej forwards only to its own 127.0.0.1.
Day to day. wattun opens the dashboard: press a tunnel's number for its page (speed test, diagnose, logs,
ports, change connection type with m), u updates, s holds rollback and auto-heal. Auto-heal is on for Iran
tunnels and restarts one whose link has been down for 3 minutes.
wattun status # one line per tunnel
wattun test NAME # connection doctor + speed test
wattun ports NAME # edit port mappings (applied live)
wattun update # update the binary and the menuAuto uses data. The test on Iran moves up to about 400 MB in total (about 50-150 MB on a filtered route where few types connect). On a traffic-billed server, pick a type yourself. After Iran has picked, run wattun-manager auto-narrow NAME on Kharej (or u on the tunnel page) to close the carrier ports it does not use.
If you already know what your route allows, you can pick a type yourself in step 2:
| Your route | Use |
|---|---|
| Only ping passes | icmp |
| UDP passes | udp (lowest latency) |
| TCP passes | tcp, or tls to look like HTTPS |
| Only a CDN gets out, or SNI is filtered | wss through Cloudflare (CDN setup) |
| Only Kharej can reach Iran | any of the above with reverse mode |
Traffic is encrypted with ChaCha20-Poly1305 under a key derived from the pre-shared key and a random per-session
id; from 2.10.0 the two ends also run an ephemeral X25519 exchange on every session, so traffic recorded before a
later key-file leak stays unreadable. That protects the Iran ↔ Kharej leg only: between your users and the Iran
server the traffic is whatever the forwarded service itself provides, so run an end-to-end encrypted protocol
(VLESS+Reality, TLS, WireGuard) inside the tunnel. Kharej dials only its own 127.0.0.1, and only ports on the
allowlist.
From 3.0.0 no byte of a packet header is constant (the session id and packet number are masked), idle packets are
padded to random sizes with a jittered keepalive, and udp has a reverse mode: the Iran
server only listens and never starts a flow towards a foreign address. Raw tcp opens with a TLS ClientHello-shaped
hello and masked frame lengths instead of a plain pool id (tcp-mimic). On a route that cuts every UDP flow after a few
packets but passes DNS, udp-dns: true starts each udp datagram like a DNS message on port 53
(DNS shape, traffic shape).
The installer verifies the download's signature against a public key baked into it, so a WAT_PROXY mirror (or
any tampered copy) cannot slip in a changed binary. The connection token contains the key: treat it like a
password and share it only over a private channel. Keep metrics on 127.0.0.1. What stays visible to the
network: the server's address, and the volume and timing of packets. Details:
security model.
Everything else is in docs/TECHNICAL.md: reverse mode · auto mode · Cloudflare / CDN · bonding · managing tunnels · config file · monitoring · troubleshooting · security · Windows and building from source
- Telegram channel (news and releases): @WatxDev, developer: @watxox
- Bugs and ideas: issues. A change that makes the ICMP path slower won't be merged.
- License: MIT. Uses
golang.org/x/crypto,x/netandx/sys(BSD-3-Clause). GOST, Backhaul and BackPack were studied for ideas and as benchmarks; no code was copied.