Android 完整部署¶
WutherCore 在 Android 上提供四条实际可用的接管路径:
- root TUN,由 native 进程直接打开
/dev/net/tun。 - root TPROXY,由 native 进程建立 TCP 和 UDP 透明监听并安装策略路由。
- root REDIRECT,由 native 进程建立 TCP 透明监听并原子安装 nftables NAT 规则。
- 非 root VpnService,由宿主应用建立 TUN,再把文件描述符交给 native。
method: auto 在 Android 上选择 TUN。TUN 打开时先尝试 root /dev/net/tun,失败后 才消费宿主注入的 VpnService 文件描述符。因此同一份 virtual_nic 配置可以同时 服务 root daemon 和普通 Android 应用,但两条路径的启动方式、路由所有权和应用 过滤边界不同。
仓库提供三份 root 配置:
| 数据面 | 配置文件 |
|---|---|
| root TUN | android-root-tun.yaml |
| root TPROXY | android-root-tproxy.yaml |
| root REDIRECT | android-root-redirect.yaml |
非 root 宿主配置见 android.yaml。
模式选择¶
| 模式 | capture.method | TCP | UDP | 设备或规则 | 最低权限 |
|---|---|---|---|---|---|
| root TUN | virtual_nic 或 auto | 支持 | 支持 | /dev/net/tun,接口地址,策略路由 | UID 0 或足够的有效 capability |
| root TPROXY | tproxy | 支持 | 支持 | 透明 socket,iptables,策略路由 | 有效 CAP_NET_ADMIN |
| root REDIRECT | redirect | 支持 | 不支持 | 透明 TCP socket,nftables NAT | 有效 CAP_NET_ADMIN |
| VpnService | virtual_nic 或 auto | 支持 | 支持 | 宿主提供的预配置 TUN fd | Android VPN 授权 |
优先按下面的条件选择:
| 需求 | 推荐 |
|---|---|
| 普通应用分发,不要求 root | VpnService |
| root 设备,需要完整 TCP 和 UDP,要求按应用过滤 | root TUN |
| root 设备,不希望创建 TUN,内核具备 TPROXY | root TPROXY |
| 旧内核或只需要 TCP 接管 | root REDIRECT |
| 同时兼容 root 和非 root 宿主 | method: auto,由启动方式决定 TUN 来源 |
不要把 tun.auto_redirect 当作 Android root 开关。Android 支持显式 method: redirect,但不支持 Linux 专用的 tun.auto_redirect 混合数据面。
源码中的实际调用链¶
Android 平台入口位于 platform/android.rs。 生产 Android 构建会把 TUN、TPROXY 和 REDIRECT 都交给 Linux 系数据面实现:
CapturePlan
-> platform::android::build_engine
-> platform::linux::build_engine
-> LinuxTun | LinuxTproxy | LinuxRedirect
三条路径分别继续执行:
root TUN
-> android_tun_io::open
-> LinuxTunIo::open("/dev/net/tun")
-> 配置接口和策略路由
-> 启动 TUN dispatcher
root TPROXY
-> require_net_admin
-> 绑定 7894 TCP 和 UDP 透明监听
-> 安装 iptables 和 ip6tables 规则
-> 安装 fwmark 策略路由
root REDIRECT
-> require_net_admin
-> 先绑定临时 TCP 监听
-> 检查 nftables 批处理
-> 原子发布 NAT 规则
VpnService
-> android_tun_io::open
-> root TUN 打开失败
-> 消费 JNI 注入的 fd 和实际 MTU
-> 跳过 native 接口与路由配置
关键实现文件:
| 行为 | 源码 |
|---|---|
| Android 数据面选择 | platform/android.rs |
| root TUN 优先和 VpnService fallback | platform/android_tun_io.rs |
| TUN、TPROXY 和 REDIRECT 生命周期 | platform/linux.rs |
| TPROXY 固定 mark、端口和规则 | tproxy_rules.rs |
| REDIRECT 原子 nftables 规则 | platform/linux_auto_redirect.rs |
JNI 和 VpnService.protect | platform/android_jni.rs |
| 权限检测 | core-inbound/privilege.rs |
四层 capability 探测与生产数据面的关系¶
源码中还保留了 AndroidCapability::select_tier,它可以描述四层旧式 root 能力:
| tier | 探测条件 | 能力描述 |
|---|---|---|
nftables-full | nft、双栈 TPROXY、IPv6 NAT、owner match | 完整双栈透明代理 |
iptables-v4v6-tproxy | iptables、ip6tables、双栈 TPROXY | 双栈 TCP 和 UDP |
iptables-v4v6-redirect | iptables、ip6tables、IPv6 NAT | 双栈 TCP,UDP 受限 |
iptables-v4-only | 只有 IPv4 iptables | IPv4 TCP |
这段代码当前只用于能力描述、测试和非 Android 调试 stub。生产 Android 的 build_engine 不会调用这个 tier 自动降级器,而是直接按配置选择 Linux 系 TUN、TPROXY 或 REDIRECT。
因此当前生产行为是:
method: tproxy缺少 TPROXY 能力时启动失败,不会自动退到 REDIRECT。method: redirect走当前 nftables 原子后端,不会自动退到旧 iptables v4。method: virtual_nic只在 root TUN 和 VpnService fd 之间 fallback。
部署脚本应在启动前探测设备能力,再选择对应配置文件。不能只看到 select_tier 的四个枚举,就假定生产 engine 已经自动执行四级降级。
root 权限的真实含义¶
启动时执行的 su -c id 只是探测 root 管理器是否允许 root 子命令。它不会改变当前 native 进程的 UID,也不会给当前进程补上 CAP_NET_ADMIN。
这意味着下面的启动方式不成立:
root 数据面要求下面两种方式之一:
- 直接由 root shell、Magisk service 或 KernelSU service 启动整个
wuther-core进程。 - 让服务进程真正持有所需的有效 capability。只有可以执行
su -c但当前进程 仍是普通应用 UID,不足以创建 root TUN 或透明 socket。
通过 adb 首次验证:
adb push wuther-core /data/local/tmp/wuther-core
adb push config.yaml /data/local/tmp/wuther-core.yaml
adb shell su -c 'chmod 0755 /data/local/tmp/wuther-core'
adb shell su -c '/data/local/tmp/wuther-core components'
adb shell su -c '/data/local/tmp/wuther-core check /data/local/tmp/wuther-core.yaml'
adb shell su -c '/data/local/tmp/wuther-core explain /data/local/tmp/wuther-core.yaml'
adb shell su -c '/data/local/tmp/wuther-core run --config /data/local/tmp/wuther-core.yaml'
确认实际身份:
adb shell su -c 'id'
adb shell su -c 'grep CapEff /proc/self/status'
adb shell su -c 'test -c /dev/net/tun'
使用 Magisk 或 KernelSU 模块时,可由 service.sh 启动:
#!/system/bin/sh
MODDIR=${0%/*}
RUNDIR="$MODDIR/run"
LOGDIR="$MODDIR/log"
mkdir -p "$RUNDIR" "$LOGDIR"
chmod 0700 "$RUNDIR" "$LOGDIR"
"$MODDIR/bin/wuther-core" check "$MODDIR/config.yaml" || exit 1
"$MODDIR/bin/wuther-core" run \
--config "$MODDIR/config.yaml" \
>>"$LOGDIR/wuther-core.log" 2>&1 &
echo $! >"$RUNDIR/wuther-core.pid"
配置、日志和状态目录不要放在普通应用可以修改的位置。root daemon 读取的订阅、 规则集和证书也应限制文件权限。
root TUN¶
完整配置¶
capture:
on: true
method: virtual_nic
traffic: system
resolver: hijack
stack: mixed
mtu: 1400
offload: true
exclude:
cidr:
- 127.0.0.0/8
- ::1/128
tun:
interface_name: rpktun0
address:
- 172.19.0.1/30
- fdfe:dcba:9876::1/126
inet6: true
auto_route: true
auto_redirect: false
strict_route: false
iproute2_table_index: 2024
iproute2_rule_index: 9100
route_exclude_address:
- 127.0.0.0/8
- ::1/128
- 10.0.0.0/8
- 172.16.0.0/12
- 192.168.0.0/16
- 223.5.5.5/32
- 1.1.1.1/32
endpoint_independent_nat: true
udp_timeout: 5m
include_android_user: [0]
exclude_package:
- com.android.captiveportallogin
完整文件见 android-root-tun.yaml。
启动顺序¶
root TUN 启动不是简单打开一个文件描述符。当前实现按下面的顺序建立所有权:
- 打开
/dev/net/tun。 - 使用
TUNSETIFF创建或绑定interface_name。 - 记录崩溃恢复账本。
- 设置接口 MTU、地址和启用状态。
- 根据
auto_route安装策略路由。 - 安装出站 fwmark 绕行,避免代理出站再次进入 TUN。
- 安装 UID、GID 和 Android 包名的内核级绕行规则。
- 启动 TUN dispatcher,接管设备读写。
如果 root TUN 成功打开,即使宿主已经注入 VpnService fd,也优先使用 root TUN。 此时 native 自己管理接口和路由,不需要 VpnService.protect。
字段行为¶
| 字段 | root TUN 行为 |
|---|---|
interface_name | 请求的 TUN 名称,最终名称以内核返回值为准 |
address | 第一条 IPv4 和第一条 IPv6 地址进入执行计划 |
inet6 | 控制 IPv6 地址、路由和数据面 |
auto_route | 安装接管路由和出站绕行规则 |
strict_route | 安装更严格的防泄漏路由,首次部署建议关闭 |
iproute2_table_index | root TUN 私有路由表,默认回填为 2022 |
iproute2_rule_index | root TUN 规则优先级,默认回填为 9000 |
route_address | 只把指定目的 CIDR 导入 TUN |
route_exclude_address | 明确绕过 TUN 的静态 CIDR |
route_address_set | 使用规则集生成的动态接管集合 |
route_exclude_address_set | 使用规则集生成的动态绕行集合 |
endpoint_independent_nat | 控制 UDP NAT 映射复用 |
udp_timeout | 控制 UDP 会话老化 |
exclude_mptcp | 尝试让 MPTCP 流量绕行 |
auto_redirect 必须保持 false。Android root TUN 本身已经是完整接管路径, Linux auto_redirect 的 TCP NAT 和 UDP TUN 混合逻辑在 Android 上会被明确拒绝。
UID、GID、Android user 和包名¶
root TUN 可以使用:
capture:
tun:
include_uid: []
include_uid_range:
- "10000:99999"
exclude_uid:
- 1000
exclude_uid_range: []
include_gid: []
include_gid_range: []
exclude_gid: []
exclude_gid_range: []
include_android_user:
- 0
- 10
include_package: []
exclude_package:
- com.android.captiveportallogin
语义如下:
| 字段 | 语义 |
|---|---|
include_uid | 只接管列出的 UID |
include_uid_range | 只接管闭区间中的 UID |
exclude_uid | 让列出的 UID 绕过 |
exclude_uid_range | 让闭区间中的 UID 绕过 |
| GID 对应字段 | 与 UID 规则相同,但匹配 socket GID |
include_android_user | Android user N 映射为 N * 100000 到 N * 100000 + 99999 |
include_package | 包名白名单 |
exclude_package | 包名黑名单 |
内核级包名绕行会读取 /data/system/packages.list 把包名解析为 app UID。多用户设备 再结合 include_android_user 添加 user 偏移。包名无法解析时,对应内核规则会跳过 并记录日志,因此生产环境应检查启动日志中的解析结果。
显式 include_uid 或 include_uid_range 存在时,它们优先定义 UID 白名单。 include_android_user 不会再替代这组显式 UID 条件。
内核级 identity bypass 只在 auto_route: true 时安装,因为它依赖同一套 fwmark 策略路由。关闭 auto_route 后,宿主必须自己建立等价的绕行规则。
root TPROXY¶
完整配置¶
capture:
on: true
method: tproxy
traffic: system
resolver: hijack
stack: mixed
exclude:
cidr:
- 127.0.0.0/8
- ::1/128
- 10.0.0.0/8
- 172.16.0.0/12
- 192.168.0.0/16
tun:
inet6: true
auto_redirect: false
route_exclude_address:
- 223.5.5.5/32
- 1.1.1.1/32
auto_redirect_output_mark: "0x2024"
完整文件见 android-root-tproxy.yaml。
TPROXY 不创建 TUN,因此不要配置 capture.mtu。tun 子块仍承载透明接管共用的 IPv6、绕行 CIDR 和出站 mark 字段。
当前固定资源¶
| 资源 | 当前值 |
|---|---|
| TCP 透明监听 | 0.0.0.0:7894,IPv6 开启时同时监听 [::]:7894 |
| UDP 透明监听 | 与 TCP 使用同一端口 |
| TPROXY mark | 0x2d0 |
| 策略路由表 | 0x2d0 |
| 本地路由设备 | lo |
| IPv4 防火墙 | iptables -t mangle |
| IPv6 防火墙 | ip6tables -t mangle |
启动时先验证有效 CAP_NET_ADMIN,再绑定 TCP 和 UDP listener,随后安装策略路由和 防火墙链。规则安装失败时会删除已安装的链和策略路由。停止时先终止 listener,再 撤销规则。
TPROXY 规则会绕过:
capture.exclude.cidr。tun.route_exclude_address。- IPv4 和 IPv6 的内置本地、私网、组播、文档地址与 Tailnet 网段。
- 带出站 mark 的 WutherCore socket。
当前显式 TPROXY 规则生成器同时安装 PREROUTING 和 OUTPUT 链,不使用 traffic 选择单一 hook。不要依赖 traffic: system 把规则缩小到只有本机进程。需要限制 入口接口或转发来源时,应在设备自己的上游防火墙链中完成。
当前 TPROXY 规则生成器不把这些字段翻译成内核规则:
| 不应依赖的字段 | 原因 |
|---|---|
include_package、exclude_package | TPROXY 规则没有包名到 UID 的翻译阶段 |
| UID 和 GID 过滤 | 当前 TPROXY 链只生成 CIDR 绕行 |
| interface 和 MAC 过滤 | 当前 TPROXY 链没有生成对应匹配 |
route_address | 当前 TPROXY 防火墙入口不使用接管白名单 |
| 动态 route set | 当前 TPROXY 入口没有同步动态集合 |
需要按应用接管时优先使用 root TUN。需要限制 TPROXY 范围时,用静态 exclude.cidr 和 route_exclude_address 明确绕行,并通过设备防火墙控制哪些链 进入 WutherCore。
检查内核能力:
adb shell su -c 'iptables -t mangle -j TPROXY -h'
adb shell su -c 'ip6tables -t mangle -j TPROXY -h'
adb shell su -c 'grep -i TPROXY /proc/net/ip_tables_matches'
运行后检查:
adb shell su -c 'ip rule show'
adb shell su -c 'ip -6 rule show'
adb shell su -c 'iptables -t mangle -S WUTHERCORE_PREROUTING'
adb shell su -c 'iptables -t mangle -S WUTHERCORE_OUTPUT'
adb shell su -c 'ss -lntup | grep 7894'
root REDIRECT¶
完整配置¶
capture:
on: true
method: redirect
traffic: system
resolver: off
stack: system
exclude:
cidr:
- 127.0.0.0/8
- ::1/128
- 10.0.0.0/8
- 172.16.0.0/12
- 192.168.0.0/16
tun:
inet6: true
auto_redirect: false
route_exclude_address:
- 223.5.5.5/32
- 1.1.1.1/32
include_android_user:
- 0
exclude_uid:
- 1000
完整文件见 android-root-redirect.yaml。
REDIRECT 只接管 TCP。UDP 和普通 UDP DNS 不会进入 REDIRECT listener,因此该模式 不适合要求完整 UDP 代理的设备。需要 UDP 时使用 root TUN 或 root TPROXY。
traffic 与过滤器¶
traffic | nftables hook | 可用过滤 |
|---|---|---|
system | output | UID、UID 范围、GID、GID 范围、Android user |
apps | output | 与 system 相同 |
lan | prerouting | interface、MAC |
本机 OUTPUT 没有可靠的入口接口和源 MAC,所以 system 或 apps 配置 interface 或 MAC 过滤会失败。转发流量的 PREROUTING 没有可靠的 socket owner,所以 traffic: lan 配置 UID、GID 或 Android user 过滤会失败。
当前 REDIRECT 原子规则后端明确拒绝:
exclude.process。include_package和exclude_package。route_address_set和route_exclude_address_set。
包名需要先解析成 UID,再写入 include_uid 或 exclude_uid。不能把包名字段写进 配置后假定它已经生效。
REDIRECT 会先绑定实际 TCP listener,获得端口后生成 nftables 批处理,先执行 nft --check,再原子提交。规则发布失败时 listener 会被关闭,不留下半套 NAT 状态。停止时保留清理账本,只有 nftables 撤销成功后才报告完成。
检查规则:
VpnService¶
VpnService 是非 root 路径,也可作为 root TUN 打开失败时的 fallback。宿主必须按 完整顺序执行:
- 调用
VpnBridge.vpnServiceConfigJson(configPath)。 - 把返回的 addresses、routes、dns_servers 写入
VpnService.Builder。 - 根据返回值设置 allowed 或 disallowed applications。
- 把返回的 MTU 传给
Builder.setMtu。 - 调用
establish()。 - 对
ParcelFileDescriptor调用detachFd()。 - 调用
VpnBridge.setVpnService(service)注册 socket protect。 - 调用
VpnBridge.setVpnFdWithMtu(fd, mtu)注入 fd 和真实 MTU。 - 启动 native 核心。
native 会逐值比较注入的 MTU 与 capture.mtu。旧的 setVpnFd(fd) 只为 ABI 兼容 保留,新宿主必须使用 setVpnFdWithMtu。
VpnService fd 已由 Android framework 配置接口、地址和路由。native 检测到它是 预配置设备后,会跳过 Linux 接口、路由和防火墙管理。所有代理出站 socket 必须先 执行 VpnService.protect(fd),否则会再次进入自己的 VPN。
应用过滤在 VpnService 路径分为两层:
| 层 | 行为 |
|---|---|
| Android Builder | 使用 allowed 或 disallowed applications 决定哪些应用进入 TUN |
| native 数据面 | 使用 UID、GID、包名和 Android user 信息执行流级策略 |
Builder 不允许同时使用 allowed 和 disallowed 列表。配置同时出现 include_package 和 exclude_package 时,导出逻辑优先使用 allowed 模式并报告 被忽略的排除列表。
method: auto¶
Android 上的 method: auto 固定解析为 TUN,不会自动改成 TPROXY 或 REDIRECT。 随后由 TUN opener 决定设备来源:
尝试 root /dev/net/tun
-> 成功,使用 root TUN
-> 失败,检查是否有注入的 VpnService fd
-> 有 fd,使用 VpnService
-> 无 fd,启动失败
如果要使用 TPROXY 或 REDIRECT,必须显式写:
或:
构建¶
三种 root 模式和 VpnService 都需要 with_tun:
只构建 TUN、API、VLESS、REALITY、gRPC 和 uTLS:
pwsh -File scripts/build-all.ps1 `
-Tags "with_api,with_tun,with_vless,with_reality,with_grpc,with_utls" `
-Targets "aarch64-linux-android"
构建后检查组件:
如果输出没有 with_tun,任何 Android capture 配置都会在启动前被组件门禁拒绝。
启动前检查¶
通用¶
root TUN¶
root TPROXY¶
root REDIRECT¶
运行状态和回滚¶
root TUN、TPROXY 和 REDIRECT 都使用资源所有权账本。启动过程中任何一步失败,已经 取得所有权的接口、listener、路由和防火墙规则会按逆序清理。清理失败会保留账本, 后续 stop 可以继续重试,不会把残留规则当作已成功撤销。
运行后至少核对:
ip link show rpktun0
ip rule show
ip route show table all
ip -6 route show table all
iptables-save
nft list ruleset
只检查进程存在不能证明接管已经成功。应同时确认接口或透明 listener 存在,策略 路由已安装,出站节点可达,DNS 没有循环,停止后规则能够恢复。
常见错误¶
su 可用但 root TUN 仍失败¶
原因是当前 native 进程仍是普通应用 UID。把整个 daemon 放进 root service 启动, 不要依赖进程内部的一次 su -c id 探测。
method: auto 没有进入 TPROXY¶
这是设计行为。Android 的 auto 只选择 TUN。TPROXY 和 REDIRECT 必须显式配置。
root TUN 意外使用了 VpnService¶
检查 /dev/net/tun 权限、有效 capability 和 TUNSETIFF 错误。root open 失败后 才会消费已注入的 VpnService fd。
TPROXY 启动时缺少 CAP_NET_ADMIN¶
给当前 daemon 真正授予有效 capability,或直接由 root service 启动。只允许 su 子命令不足以创建透明 socket和策略路由。
REDIRECT 下 UDP 不工作¶
REDIRECT 的能力声明就是 TCP only。改用 root TUN 或 root TPROXY。
包名过滤没有生效¶
root TUN 需要能够读取并解析 Android 包到 UID 的映射。REDIRECT 要求先把包名转换 成 UID。TPROXY 当前不生成包名或 UID 过滤规则。
开启 tun.auto_redirect 被拒绝¶
它是 Linux root managed TUN 的专用混合数据面,不是 Android root 模式开关。 Android 请使用 root TUN,显式 TPROXY,显式 REDIRECT 或 VpnService。