2026-08-16 02:50:24 -03:00
v3
2026-08-16 02:50:24 -03:00
2026-08-16 02:33:07 -03:00
v3
2026-08-16 02:50:24 -03:00
2026-08-16 02:33:07 -03:00
2026-08-16 02:33:07 -03:00
v3
2026-08-16 02:50:24 -03:00
v3
2026-08-16 02:50:24 -03:00

DragonTCP VPN v3

DragonTCP v3 is a real Android layer-3 VPN over DragonTCP's adaptive XOR-framed TCP transport. It captures IPv4 and IPv6 through Android VpnService, passes the TUN file descriptor to the Go core, and transfers raw IP packets to a Linux DragonTCP server listening on TCP/53.

This release fixes two important problems from the previous packet-VPN build:

  1. Android can emit IPv6 link-local/control packets such as fe80::... on the VPN TUN. Those packets no longer terminate the DragonTCP session. The client drops packets whose source is not the assigned DragonTCP VPN address, and the server independently treats source-mismatch/control packets as non-fatal drops.
  2. The DragonTCP transport chunk ceiling is restored to 1 MiB (1,048,576 bytes). Raw IP packets remain limited to 65,535 bytes, but multiple TUN packets are batched into transfer objects up to 1 MiB so chunk sizes above the VPN MTU are actually useful.

Architecture

Android apps
    |
    | IPv4 + IPv6 default routes
    v
Android VpnService TUN (MTU 1280)
    |
    | raw IPv4/IPv6 packets
    v
DragonTCP Android Go core
    |
    | packet batching (up to 1 MiB transfer objects)
    | adaptive fragmentation 32 B .. 1 MiB
    | XOR 0xAD framing
    v
TCP/53
    |
    v
DragonTCP Linux server
    |
    v
Linux TUN dragontcp0
    |
    | forwarding / NAT
    v
Internet

Because the tunnel carries raw IP packets, it can carry TCP, UDP, DNS, ICMP, IPv4 and IPv6. It does not depend on applications supporting an HTTP proxy.

Included files

bin/dragontcp-vpn-server-linux-amd64
bin/dragontcp-vpn-server-linux-arm64
bin/dragontcp-vpn-client-linux-amd64   # test/debug client
android/build/DragonTCP-VPN.apk
android/lib/arm64-v8a/libdragontcp_vpn.so
core/                                  # complete Go source
android/src/                           # complete Android Java source
build_core.sh
build_all.sh
android/build_apk.sh

Server

The server needs root or equivalent CAP_NET_ADMIN permissions because it creates a Linux TUN and configures forwarding/NAT.

Install networking tools on Debian/Ubuntu if needed:

sudo apt-get update
sudo apt-get install -y iproute2 iptables

Start:

sudo ./dragontcp-vpn-server-linux-amd64 \
  --token 'YOUR_SECRET' \
  --debug

Defaults:

listen TCP port       53
server chunk max      1048576 bytes (1 MiB)
transfer batch max    1048576 bytes (1 MiB)
batch delay           1ms
TUN MTU               1280
server IPv4           10.123.0.1/16
server IPv6           fd7a:4472:6167:6f6e::1/64
poll wait             100ms
queued packet limit   2048/client
queued byte limit     8 MiB/client
auto NAT              enabled

Useful explicit command:

sudo ./dragontcp-vpn-server-linux-amd64 \
  --token 'YOUR_SECRET' \
  --chunk-max 1048576 \
  --vpn-buffer-bytes 8388608 \
  --batch-delay 1ms \
  --debug \
  --debug-stats-interval 5s

Per-packet/batch diagnostics are very verbose:

--debug-packets

Android app

Install android/build/DragonTCP-VPN.apk.

The UI asks for:

Server
TCP port
Token
Maximum transport fragment
Minimum transport fragment
Timeout

Defaults:

Port       53
Max        1048576
Min        32
Timeout    2s
Pollers    1 (fixed)
VPN MTU    1280

The adaptive record starts at Max and shrinks after transport failures. The 1 MiB value is a DragonTCP transport ceiling, not the IP MTU.

Why 1 MiB can now help even though the VPN MTU is 1280

The old packet-VPN sent one TUN packet per DragonTCP transfer object, so a record size larger than the IP packet had no benefit. v3 batches adjacent TUN packets for a short window:

1280-byte packet --+
1280-byte packet ---+
1280-byte packet ----+--> one DragonTCP transfer object --> adaptive fragments
...                  |
1280-byte packet ----+

A busy flow can therefore produce transfer objects much larger than 65,535 bytes. If the network accepts large DragonTCP records, fewer transactions are needed. If it does not, the same transfer object is automatically fragmented into smaller records and retried.

A log such as this from the previous build:

VPN stopped: source fe80::... does not match session address

is no longer fatal.

The Android core now logs an occasional line such as:

VPN DROP local packet (source fe80::... is not assigned VPN address) dropped=1

and continues running. The server also performs a non-fatal drop as a second line of defense.

Build from source

Requirements:

  • Go 1.22+
  • JDK 17+
  • Android SDK platform/build-tools
  • zip

No Android NDK is required for this build.

Set the SDK directory:

export ANDROID_SDK_ROOT="$HOME/Android/Sdk"

Build native components and APK:

./build_all.sh

Outputs:

bin/dragontcp-vpn-server-linux-amd64
bin/dragontcp-vpn-server-linux-arm64
android/lib/arm64-v8a/libdragontcp_vpn.so
android/build/DragonTCP-VPN.apk

Build only the Go/native components:

./build_core.sh

Build only the APK after the native core exists:

cd android
./build_apk.sh

Testing performed

The Go packages compile with go test ./....

A local mock-TUN test verified:

  • an IPv6 fe80:: source packet is dropped without terminating the client;
  • a valid packet immediately afterward still passes;
  • 120 IPv4 packets were combined into a 120,241-byte transfer object, proving that transfer objects larger than 65,535 bytes work;
  • the echoed packets were returned byte-for-byte and in order;
  • the same path also works with a fixed 32-byte DragonTCP fragment size.

A physical Android phone is still required to validate device/vendor-specific VpnService behavior and the real mobile-network TCP/53 path.

S
Description
No description provided
Readme
163 MiB
Languages
Go 64.7%
Kotlin 14%
Java 13.8%
PowerShell 4.2%
Python 1.6%
Other 1.7%