A powerful, extremely lightweight, encrypted port forwarder with NAT traversal support, built on a reliable UDP transport.
Status: Stable
kcptun-libev is a TCP port forwarder built on KCP, a reliable UDP‑based transport protocol.
NAT traversal is the primary use case: kcptun-libev can connect a TCP service behind NAT to clients anywhere on the internet, without port forwarding or a VPN. A small, publicly reachable rendezvous server bootstraps the connection; all subsequent traffic flows directly between peers.
client -> NAT1 -> rendezvous server
server -> NAT2 -> rendezvous server
(after hole-punching)
client -> NAT1 -> NAT2 -> server
Example: play LAN multiplayer games with friends over the internet. The game host runs kcptun-libev server behind their home NAT; each friend runs kcptun-libev client. After hole-punching via the rendezvous server, everyone connects to the host's game port as if on a local network — no router configuration needed on either side.
It also works as a plain KCP transport accelerator for services on networks with packet loss or congestion:
client -> kcptun-libev client ->
lossy network (carried by KCP)
-> kcptun-libev server -> server
Because KCP retransmits packets aggressively, we recommend enabling proper QoS at the NIC level when running on public networks.
Read more about KCP
- NAT traversal: Servers behind certain types of NAT can connect directly to clients via a well‑known rendezvous server, with no port forwarding required.
- Secure: Proper integration with modern authenticated encryption.
- Responsive: No multiplexer; one TCP connection maps to one KCP connection with 0‑RTT opening.
- Precise: KCP flushes on demand; no artificial latency introduced.
- Simple: Does one thing well — acts as a Layer 4 forwarder.
- Modern: Full IPv6 support.
- Dynamic DNS aware: Dynamic IP addresses can be resolved automatically.
- Configurable: When used with other encryption (e.g., udp2raw, WireGuard), built‑in encryption can be disabled or omitted at build time.
- Portable: Compliant with ISO C; supports both GNU/Linux and POSIX APIs.
- Long-Term Supported: Follow the latest releases of the dependent projects. Even if we don't make any changes, the binary release will be rebuilt at least once a year.
kcptun-libev is extremely lightweight. The main executable is 100~200 KiB on most platforms*, with low CPU usage and memory footprint.
* Some required libraries are dynamically linked; see runtime dependencies below. Statically linked executables can be larger due to these libraries.
For your convenience, some statically-linked executables are also provided in the Releases section.
kcptun-libev can encrypt packets with a password or pre-shared key. Security and privacy can only be guaranteed if encryption is enabled. We use the authenticated encryption methods provided by libsodium.
In config file:
"method": "// name here"If encryption is disabled or not compiled in, there is no packet overhead. However, no authentication tag is added to protect the server from crafted packets. In this case, security relies on third‑party components. We recommend disabling encryption only when unsolicited packets cannot reach the service, or when the traffic is already protected (e.g., WireGuard).
In practice, we suggest using the --genpsk command‑line argument to generate a strong random pre‑shared key instead of a simple password.
| Encryption Method | Since | Form | Packet Overhead | Notes |
|---|---|---|---|---|
| xchacha20poly1305_ietf | v1.0 | AEAD | 40 bytes | recommended |
| xsalsa20poly1305 | v2.2 | AE | 40 bytes | |
| chacha20poly1305_ietf | v2.0 | AEAD | 28 bytes | |
| aes256gcm | v2.0 | AEAD | 28 bytes | requires specific hardware* |
* Specifically: x86 CPU with SSSE3, AES‑NI, and PCLMUL.
kcptun-libev ships with additional encryption methods to ensure that users have alternatives for specific reasons. Although the strength of each method is discussed, in most cases the recommended one just works.
Obfuscation is optional and helps evade inspection. This feature is available on Linux only.
In config file:
"obfs": "// name here"Currently one obfuscator is implemented: dpi/tcp-wnd. It behaves like a HTTP service and cannot be probed without the pre‑shared key.
With obfuscation enabled, kcptun-libev sends IP packets over raw sockets. Therefore, Linux capability CAP_NET_RAW is required. For example, the following commands may work on some Linux distributions:
# run as root and drop privileges after necessary setup
sudo ./kcptun-libev -u nobody:nogroup -c server.json
# or grant the capability and run as a normal user
sudo setcap cap_net_raw+ep kcptun-libev
./kcptun-libev -c server.jsonAll systems that support ISO C11 and POSIX.1‑2008.
| System | Tier | Notes |
|---|---|---|
| Ubuntu | developed | |
| OpenWrt | tested | |
| Other Linux / Android | supported | |
| macOS | supported | without obfuscator |
| Windows (MSYS2) | supported | without obfuscator |
For security reasons, kcptun-libev does NOT provide compatibility with any other KCP implementation.
We use semantic versioning.
Given a version number MAJOR.MINOR.PATCH:
-
As long as
MAJORremains unchanged, the versions should speak a compatible protocol. -
As long as
MAJOR.MINORremains unchanged, later versions should be compatible with working configuration files from previous versions.
| Name | Version | Required | Feature |
|---|---|---|---|
| json-c | >= 0.15 | yes | config file |
| libev | >= 4.31 | yes | |
| libsodium | >= 1.0.18 | no | encryption |
# Debian / Ubuntu
sudo apt install libjson-c-dev libev-dev libsodium-dev
# Alpine Linux
apk add json-c-dev libev-dev libsodium-devgit clone https://github.com/hexian000/kcptun-libev.git
mkdir -p kcptun-libev-build && cd kcptun-libev-build
cmake -DCMAKE_BUILD_TYPE="Release" \
../kcptun-libev
cmake --build . --parallelSee m.sh for cross‑compiling support.
Statically-linked setup: Download a -static build from the Releases section — no additional runtime dependencies are needed.
Dynamically-linked setup: The following dependencies should be installed.
# Debian / Ubuntu
sudo apt install libjson-c5 libev4 libsodium23
# Alpine Linux
apk add json-c libev libsodium
# OpenWRT
opkg install libjson-c5 libev libsodiumCommon fields in server.json/client.json:
- Client:
listendefines the local TCP address; traffic is sent tokcp_connect. - Server: receives on
kcp_bindand forwards connections toconnect. - Setting
passwordorpskis strongly recommended on public networks. loglevel: 0–8 map to Silence, Fatal, Error, Warning, Notice, Info, Debug, Verbose, VeryVerbose. The default is 4 (Notice). Higher levels can affect performance.
First, generate a random key for encryption:
./kcptun-libev --genpsk xchacha20poly1305_ietfRendezvous mode lets a server behind NAT accept connections from clients, without any port forwarding. The rendezvous server only bootstraps the connection; all subsequent traffic flows directly between client and server.
Rendezvous mode requires UDP at the transport layer; it is incompatible with non‑UDP obfuscators.
The method is non-standard and may not work with all NAT implementations.
rendezvous_server.json: Deploy the rendezvous server at a publicly reachable address accessible by both client and server.
{
"kcp_bind": "0.0.0.0:12345",
"method": "xchacha20poly1305_ietf",
"psk": "// your key here"
}server.json: The server may be behind one or more levels of NAT.
{
"connect": "127.0.0.1:25565",
"rendezvous_server": "203.0.113.1:12345",
"service_id": "myservice",
"method": "xchacha20poly1305_ietf",
"psk": "// your key here"
}client.json: The client may be behind one or more levels of NAT, which may or may not be the same ones as the server.
{
"listen": "127.0.0.1:25565",
"rendezvous_server": "203.0.113.1:12345",
"service_id": "myservice",
"method": "xchacha20poly1305_ietf",
"psk": "// your key here"
}Scaling: rendezvous_server : server : client = 1 : m : m×n
All peers must use the same address family (all IPv4 or all IPv6).
For direct connectivity where both peers have reachable addresses, use the standard forwarding mode.
Create a server.json file:
{
"kcp_bind": "0.0.0.0:12345",
"connect": "127.0.0.1:1080",
"method": "xchacha20poly1305_ietf",
"psk": "// your key here"
}Start the server:
./kcptun-libev -c server.jsonCreate a client.json file:
{
"listen": "127.0.0.1:1080",
"kcp_connect": "203.0.113.1:12345",
"method": "xchacha20poly1305_ietf",
"psk": "// your key here"
}Start the client:
./kcptun-libev -c client.json127.0.0.1:1080 on the client is now forwarded to the server via kcptun-libev.
See server.json and client.json in the repository for more tunables.
kcptun-libev works out of the box. In most cases, the default options are recommended.
Some tunables are the same as KCP; read their docs for a full explanation. Hints:
kcp.sndwnd,kcp.rcvwnd:- Tune according to RTT.
- To estimate theoretical bandwidth, start an idle client with
loglevel >= 5and wait ~1 minute. - On memory‑constrained systems, reduce these values to save memory.
kcp.nodelay: Enabled by default. Note: not equivalent toTCP_NODELAY.kcp.interval:- Because KCP runs differently here, the recommended value is higher than in previous implementations and saves CPU.
- Not intended for traffic shaping. On Linux, see sqm-scripts and CAKE.
kcp.resend: Disabled by default.kcp.nc: Enabled by default.kcp.mtu: Specifies the final IP packet size, including all overhead.
kcptun-libev–specific options:
kcp.flush: 0 = periodic only; 1 = flush after sending; 2 = also flush ACKs (for benchmarking).tcp.sndbuf,tcp.rcvbuf,udp.sndbuf,udp.rcvbuf: Socket buffer sizes; see your OS manual.- Defaults usually work.
- Larger UDP buffers (e.g., 1048576) can help; however, overly large receive buffers may be counterproductive here.
- Avoid too‑small buffers to prevent performance degradation.
user: switch to this user to drop privileges, e.g.,"user": "nobody:"means the user named "nobody" and that user's login group
There is a built‑in HTTP server for monitoring service status.
Add this line to your config file:
"http_listen": "127.0.1.1:8081"Then run the commands below from shell:
watch curl -sX POST http://127.0.1.1:8081/statsThe URI "/healthy" always responds with HTTP 200; use it for health checks.
Thanks to: