Skip to content

绕过大陆 IP 模式:nftables 规则仅覆盖 IPv4,IPv6 流量泄露至代理 #2024

Description

@foxhoundhcm

Issue: nftables 规则缺少 IPv6 国内分流

问题现象

所有 IPv6 流量都被转发到代理,包括国内 CDN 节点。以 B 站为例:

# IPv4 — 正常(返回国内 IP)
curl -4 https://api.live.bilibili.com/ip_service/v1/ip_service/get_ip_addr
# → 180.139.x.x  中国

# IPv6 — 错误(返回代理 IP)
curl -6 https://api.live.bilibili.com/ip_service/v1/ip_service/get_ip_addr
# → 173.242.x.x  美国 洛杉矶  ← 国内流量泄露到代理

B 站域名 api.live.bilibili.com → CNAME → a.w.bilicdn1.com,DNS 同时返回 IPv4 和 IPv6 地址:

IPv4: 117.21.179.18 ~ 20, 222.210.39.57 ~ 59
IPv6: 240e:974:ecff:25::28 ~ 30, 240e:cf:9000:306::2 ~ 4, ...

均为中国 CDN 节点。但用户设备支持 IPv6 时浏览器优先走 IPv6,导致本应直连的国内流量走了代理。

根因分析

/usr/bin/ssr-rules 在创建 nftables 链时只生成 IPv4 规则,完全缺少 IPv6 国内分流的代码。

当前 ss_spec_wan_ac 链的创建逻辑(ssr-rules nft 部分,约 line 393-434):

$NFT flush chain inet ss_spec ss_spec_wan_ac
$NFT add rule ... tcp dport 53 ip daddr 127.0.0.0/8 return
$NFT add rule ... tcp dport != 53 ip daddr $server return
$NFT add rule ... ip daddr @blacklist jump ss_spec_wan_fw    ← IPv4 only
$NFT add rule ... ip daddr @whitelist return                   ← IPv4 only
$NFT add rule ... ip saddr @fplan jump ss_spec_wan_fw         ← IPv4 only
$NFT add rule ... ip saddr @bplan return                       ← IPv4 only
$NFT add rule ... ip daddr @china return                       ← IPv4 only
$NFT add rule ... jump ss_spec_wan_fw                          ← 兜底

注意所有规则都是 ip 开头(IPv4),没有任何 ip6 规则。IPv6 流量进入该链后,逐条不命中。

三种运行模式的差异

模式 链末尾 IPv6 行为 影响
router(绕过大陆IP) jump ss_spec_wan_fw IPv6 所有规则不命中,落入兜底 jump → 代理 有问题
gfw(GFW列表) 无兜底 jump IPv6 所有规则不命中,链结束 → accept 直连 没问题 ✓
all(全局) jump ss_spec_wan_fw 预期全走代理 预期行为 ✓

因此该问题仅在 router 模式下出现。GFW 模式因为链末尾没有兜底 jump ss_spec_wan_fw,IPv6 流量"意外正确"地直连了。

注:GFW 模式下虽不影响 IPv6 直连,但 GFW 列表中的域名如果解析出 IPv6 地址,会因为 @gfwlist 只有 IPv4 条目而直连出去,这属于另一个潜在问题。

router 模式数据流对比:

协议 nftables 层 结果
IPv4 ip daddr @china return → 命中,直连 国内 IP ✓
IPv6 所有 ip 规则不命中 → jump ss_spec_wan_fw → 代理 代理 IP ✗

建议修复方案

新增 @china6 set(类型 ipv6_addr),在 ss_spec_wan_ac 链中添加 IPv6 国内直连规则:

1. 新增 nft set

nft add set inet ss_spec china6 { type ipv6_addr; flags interval; auto-merge; }

2. 加载中国 IPv6 CIDR 列表

数据来源:https://raw.githubusercontent.com/gaoyifan/china-operator-ip/ip-lists/china6.txt(约 1500 条,定期更新)

可参考现有的 chinaipset.sh 对 IPv4 @china 的加载方式。

3. 在链创建代码中添加 IPv6 规则

ip daddr @china return 之后、jump ss_spec_wan_fw 之前,加入:

$NFT add rule inet ss_spec ss_spec_wan_ac ip6 daddr @china6 return

该修改仅需在 router 模式的链生成代码中添加(gfw 模式可一并添加以显式声明 IPv6 直连,非必须)。

4. (可选)新增 CLI 参数

类似 -i 指定 IPv4 列表文件,可增加 -6 参数指定 IPv6 列表文件,如:

ssr-rules -6 /etc/ssrplus/china6_ssr.txt

当前临时方案

我们目前的 workaround 是通过 chinaipset6.shssr-rules 执行完后手动插入规则并挂到 /etc/init.d/shadowsocksrstart() 钩子中。该方案存在问题:

  • 固件升级会覆盖 /etc/init.d/shadowsocksr,钩子丢失
  • 作为外部补丁,维护成本高,不适合普通用户

验证方法

修复后可通过以下命令验证:

# nft 链中应包含 ip6 规则
nft list chain inet ss_spec ss_spec_wan_ac | grep china6

# IPv6 请求应返回国内 IP
curl -6 https://api.live.bilibili.com/ip_service/v1/ip_service/get_ip_addr
# 应返回 240e:* 开头、归属中国

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions