Skip to content

xkcptun: add new package - #30458

Open
liudf0716 wants to merge 1 commit into
openwrt:masterfrom
liudf0716:xkcptun-new
Open

xkcptun: add new package#30458
liudf0716 wants to merge 1 commit into
openwrt:masterfrom
liudf0716:xkcptun-new

Conversation

@liudf0716

Copy link
Copy Markdown
Contributor

Add xkcptun: a lightweight, high-performance KCP-based secure tunnel written in C (libevent2 + ikcp), released under GPL-3.0-or-later.

Maintainer: Dengfeng Liu liudf0716@gmail.com
Compile tested: aarch64_cortex-a53 (chawrt 25.12 / OpenWrt snapshot)
Run tested: aarch64_cortex-a53 (clawwifi M3000 v1 router, client+server pair with DNS and transparent TCP tunnels over ~1 month)

Description:

  • Ships both daemons (xkcp_client / xkcp_server) plus the xkcp_spy monitoring tool
  • procd init script renders per-instance TOML configs from /etc/config/xkcptun (uci-validated), supporting multiple client tunnels, SOCKS5 and transparent REDIRECT proxy modes, and tcp/udp forwarding
  • Integrates with eBPF-based xdns-bpf (xdns_tcp_sessions map) for domain-based transparent steering of LAN clients through the encrypted KCP tunnel
  • Source: https://github.com/liudf0716/xkcptun (tag 1.09.561)

Note: this is the author/maintainer submitting their own upstream package.

@openwrt openwrt Bot added the Add package label Sep 6, 2026
Comment on lines +152 to +153
start_service() {
mkdir -p "$confdir"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rendered TOML contains the pre-shared key (line 25, line 71) but is created under the default umask, so /var/etc/xkcptun/*.toml ends up 0644 and world-readable. That silently downgrades the secret, since /etc/config/xkcptun is installed 0600 by INSTALL_CONF. Restrict the directory:

Suggested change
start_service() {
mkdir -p "$confdir"
start_service() {
mkdir -p "$confdir"
chmod 0700 "$confdir"

Generated by Claude Code

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in dc8534a: start_service() now does chmod 0700 "$confdir" and sets umask 077 before the TOML files are rendered, so the generated configs (and the pre-shared key inside) are only readable by root.

Comment on lines +136 to +143
if [ "$cfgtype" = "server" ]; then
local srv_enabled
config_get_bool srv_enabled server enabled 0
if [ "$srv_enabled" -ne 1 ] && [ "$count" -eq 0 ]; then
rm -f "$conftoml"
return
fi
fi

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

enabled is only honoured when count is 0, so once any config server section exists the server daemon starts regardless of option enabled '0' — there is no way to turn it off short of deleting the sections.

Suggested change
if [ "$cfgtype" = "server" ]; then
local srv_enabled
config_get_bool srv_enabled server enabled 0
if [ "$srv_enabled" -ne 1 ] && [ "$count" -eq 0 ]; then
rm -f "$conftoml"
return
fi
fi
if [ "$cfgtype" = "server" ]; then
local srv_enabled
config_get_bool srv_enabled server enabled 0
if [ "$srv_enabled" -ne 1 ]; then
rm -f "$conftoml"
return
fi
fi

Generated by Claude Code

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in dc8534a: the count == 0 condition was dropped. The enabled option is now honoured unconditionally — with option enabled 0 (the default) the server daemon never starts, regardless of how many config server tunnel sections exist.

Comment on lines +228 to +234
validate_server_options() {
validate_common_options server "$1" "$2" \
'local_interface:string:eth0' \
'local_port:port:9089' \
'remote_addr:host:127.0.0.1' \
'remote_port:port:443'
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These defaults make every config server tunnel emit remote_addr = "127.0.0.1" / remote_port = 443 into its [[tunnel]] block via lines 70-71, which looks copied from the client side (the shipped server listens on 9089, not 443). Conversely target_addr/target_port are declared only for clients, so uci_validate_section drops them from server sections even though append_toml_tunnel writes them. Are both intentional?


Generated by Claude Code

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — those defaults were copy-paste from the client side. Fixed in dc8534a: the bogus remote_addr/remote_port defaults were removed from validate_server_options, and target_addr/target_port are now validated for server sections as well, since they are emitted into the generated TOML.

Comment thread net/xkcptun/Makefile Outdated

define Package/xkcptun/description
xkcptun is a lightweight and high-performance C language implementation of kcptun.
This package contains both client and server binaries.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: the package also installs xkcp_spy (line 50), which both the PR body and the commit message list; only the description omits it.

Suggested change
This package contains both client and server binaries.
This package contains the client and server binaries plus the xkcp_spy monitoring tool.

Generated by Claude Code

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in dc8534a: the description now mentions the xkcp_spy monitoring tool.

option target_addr '127.0.0.1'

config global 'server'
option enabled '0'

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this package uses two opposite-polarity flags — enabled here on the global sections, disabled on the tunnel sections (line 25). Picking one (enabled, as most of the feed does) would avoid users having to remember which section takes which.


Generated by Claude Code

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Standardized in dc8534a: all sections now use a single positive-polarity enabled option (default 1 for tunnel sections). The disabled flag is gone from both the validation schema and the sample config.

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commit checks

  • dc8534a "xkcptun: address review feedback on init script and description" — a fixup on the not-yet-merged commit it corrects; squash it into 48e44db so the package lands as a single xkcptun: add new package commit.

Generated by Claude Code

if [ "$cfgtype" = "server" ]; then
local srv_enabled
config_get_bool srv_enabled server enabled 0
if [ "$srv_enabled" -ne 1 ]; then

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing reads enabled on config global 'client' — the client daemon is gated only by the tunnel count at xkcptun.init:131, so option enabled '0' there is silently ignored while the same option turns the server off here. Either add the matching config_get_bool gate for the client, or keep enabled off the global schema so it cannot be set where it has no effect.


Generated by Claude Code

@liudf0716 liudf0716 Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed: added config_get_bool cli_enabled client enabled 1 in start_daemon() so option enabled 0 on config global 'client' properly gates the client daemon regardless of tunnel count.

Comment thread net/xkcptun/files/xkcptun.init Outdated
local mode_profiles='"fast3", "fast2", "fast", "normal", "manual"'

"${func:-validate}" "$cfgtype" "$cfg" "$@" \
'enabled:bool:1' \

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: with 'enabled:bool:0' dropped from validate_global_options, the global sections now inherit this default of 1, while the runtime gate at xkcptun.init:138 still uses config_get_bool srv_enabled server enabled 0. The schema procd and LuCI validate against says the server is on by default; the init script says off. Re-adding the 'enabled:bool:0' override in validate_global_options keeps the two in step.


Generated by Claude Code

@liudf0716 liudf0716 Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed: restored 'enabled:bool:0' in validate_global_options() so the schema validation default matches the runtime default.

@liudf0716

liudf0716 commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the review!

All feedback has been addressed:

  • Added config_get_bool cli_enabled client enabled 1 in start_daemon() so the client daemon honours enabled on config global 'client'.
  • Restored 'enabled:bool:0' in validate_global_options() to align the schema with the init script's runtime default.
  • Squashed all commits into a single commit (xkcptun: add new package).

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generated by Claude Code


Generated by Claude Code

Comment thread net/xkcptun/files/xkcptun.init Outdated

validate_global_options() {
validate_common_options global "$1" "$2" \
'enabled:bool:0' \

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

enabled ends up declared twice in the same tuple list: this one is forwarded via "$@" and 'enabled:bool:1' is appended after it inside validate_common_options, so the later default wins and this override has no effect on validate_data output or the procd schema. And with the runtime gates now defaulting the client to 1 (line 133) and the server to 0 (line 142), a single shared tuple cannot match both anyway — take the enabled default as an argument to validate_common_options rather than hardcoding it there.


Generated by Claude Code

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. Updated validate_common_options to accept the enabled default as an argument instead of hardcoding it, and updated validate_global_options to set enabled default to 1 for client and 0 for server, matching the runtime init script defaults.

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generated by Claude Code


Generated by Claude Code

Comment thread net/xkcptun/files/xkcptun.init Outdated
Comment on lines +228 to +229
local enabled_def=0
[ "$1" = "client" ] && enabled_def=1

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

During procd validate registration the validate_*_section functions are invoked with no arguments (firewall.init:34-36), so $1 is empty here and the schema registered for type global always carries enabled:bool:0 — the client's runtime default of 1 at line 133 is still not represented. procd registers one schema per section type, so a name-conditional default cannot be expressed there; settle on one enabled default for both global sections and use it in the runtime gates too.


Generated by Claude Code

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing this out!

I have settled on enabled:bool:0 (disabled by default) for both client and server global sections:

  1. Updated validate_global_options to uniformly register enabled:bool:0 without section-name conditional branching, matching procd schema registration in service_triggers().
  2. Updated the client runtime gate in start_daemon() to default to 0 (config_get_bool cli_enabled client enabled 0).
  3. Added option enabled '0' under config global 'client' in the sample configuration file.
  4. Squashed all changes into the single package commit and force-pushed.

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 1 new commit; no new issues found.


Generated by Claude Code

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generated by Claude Code


Generated by Claude Code

Comment thread net/xkcptun/test.sh Outdated

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: byte-identical to test-version.sh (both files are blob 3e9ab08), so CI just runs the same two version greps a second time and no package-specific behaviour is covered. Either drop this file and keep only the test-version.sh override, or make it exercise something real (e.g. xkcp_client -c on a generated TOML).


Generated by Claude Code

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Dropped test.sh and retained test-version.sh for the version assertion.

xkcptun is a lightweight, high-performance KCP-based secure tunnel
written in C (libevent2 + ikcp). This package ships both the client
(xkcp_client) and server (xkcp_server) daemons plus the xkcp_spy
monitoring tool, with a procd init script that renders per-instance
TOML configs from /etc/config/xkcptun.

Tunnels support tcp/udp forwarding, SOCKS5 and transparent REDIRECT
proxy modes, and can integrate with eBPF-based xdns-bpf for
domain-based transparent steering.

Compile tested: aarch64_cortex-a53 (chawrt 25.12 / OpenWrt snapshot)
Run tested: aarch64_cortex-a53 (clawwifi M3000 v1 router, client+server
pair with DNS and transparent TCP tunnels)

Signed-off-by: Dengfeng Liu <liudf0716@gmail.com>

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 1 new commit; no new issues found.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants