Real-IP Tun Example

Real-IP 模式的复杂性主要来自于两个方面:

  • 如何使得 Clash Outbound 还能正确的连接互联网

  • 如何使得 DIRECT 出口的流量不要经过 Tun NIC 回到 Clash 造成回环

很显然,不能使用添加 Default 路由到 Tun NIC 的方式使用 Clash Tun 模式。否则 Clash 将无法连接远程代理服务器。

需要注意的是,当 Clash 作为网关工作,如果运行 Clash 的主机本身并不需要通过 Tun 代理,仅仅是转发其他设备的流量,那么上述两个复杂性将不会存在,相应的也会后更简单的配置方法。

本章将以运行 Clash 的 Linux 的主机本身也需要代理的条件下,给出一个使用方法。

解决思路

  • 将 Clash 运行在一个单独的用户下,用以给操作系统一个区分网络流量是否是 Clash 发出的特征。

  • 使用 iptables 在 mangle 表中,将流量打上 fwmark,再由 IP rule 决定将这些有 mark的 流量使用策略路由 (policy route)将流量转发给 Tun NIC。

    • 在 mangle 表 OUTPUT 链中使用 uid-owner 排除 Clash 发出的流量

    • 在 mangle 表 PREROUTING 中,将其他设备发来的流量打上 fwmark

实施方案

假设 clash 运行在 alice 用户下,该用户的 UID 为 129,我们 fwmark 也用 129,clash 策略路由的路由表 id 也为 129。

用户 uid,fwmark ,路由表 id 是三个不相关的值。这里仅仅是为了举例方便都设置成 129.

创建NIC

配置 iptables 和 ip rule 前最好提前配置好 Tun NIC 并 bring up。创建 NIC 的方法可以参考前面的部分

这里假设 Tun NIC 已经提前创建好,名称为 clash0,owner 也为 alice,并且已经被 bring up.

配置 iptables

首先在 mangle 表中创建 CLASH 链,主要用于排除一些局域网地址。如果你会使用 ipset ,可以参考下面被注释的 chnroute 项,排除一些国内地址。

iptables -t mangle -N CLASH # 创建 Clash Chain
# 排除一些局域网地址
iptables -t mangle -A CLASH -d 0.0.0.0/8 -j RETURN
iptables -t mangle -A CLASH -d 10.0.0.0/8 -j RETURN
iptables -t mangle -A CLASH -d 127.0.0.0/8 -j RETURN
iptables -t mangle -A CLASH -d 169.254.0.0/16 -j RETURN
iptables -t mangle -A CLASH -d 172.16.0.0/12 -j RETURN
iptables -t mangle -A CLASH -d 192.168.0.0/16 -j RETURN
iptables -t mangle -A CLASH -d 224.0.0.0/4 -j RETURN
iptables -t mangle -A CLASH -d 240.0.0.0/4 -j RETURN
# iptables -t mangle -A CLASH -m set --match-set chnroute dst -j RETURN
iptables -t mangle -A CLASH -j MARK --set-xmark 129

然后在 PREROUTING 链中,给所有转发(forward)流量打上 fwmark。PREROUTING 链的流量来自于其他设备。如果有必要,可以限制高目标端口以防止 BT 流量被转发到 Clash

# 限制高目标端口的流量
# iptables -t mangle -A PREROUTING -p udp -m udp --dport 4096:65535 -j RETURN
# iptables -t mangle -A PREROUTING -p tcp -m tcp --dport 8192:65535 -j RETURN
iptables -t mangle -A PREROUTING -j CLASH

最后在 OUTPUT 链中,将本机外出流量打上 fwmark。

iptables -t mangle -A OUTPUT -m owner --uid-owner 129 -j RETURN
# 如果本机有某些不想被代理的应用(如BT),可以将其运行在特定用户下,加以屏蔽
# iptables -t mangle -A OUTPUT -m owner --uid-owner xxx -j RETURN
iptables -t mangle -A OUTPUT -j CLASH

下面添加策略路由,使得被打上 fwmark 的流量交由特定路由表(129)处理,并转发到 Clash 监听的 Tun NIC 上。

ip route add default dev clash0 table 129
ip rule add fwmark 129 lookup 129

可以用 ip route验证下策略路由是否生效

ip route get 1.1.1.1 mark 129

返回应和下面类似,注意加粗的部分。0x81 是 129 的 16进制表示。

1.2.3.4 dev clash0 table 129 src 192.168.31.194 mark 0x81 uid 1000 cache

排除 rp_filter 的故障

这时,如果 clash 已经启动,可以验证下 clash 能否正常工作了。如果不能,可以使用 tcpdump -n -i clash0 抓包诊断下,如果看起来 clash0 端口两个方向的包都有,有可能是 Linux 的 rp_filter 导致了问题。

rp_filter 是 Linux 的安全功能。 rp_filter 会在计算路由决策的时候,计算包的反向路由,也就是将包中的 源地址和目的地址对调再查找路由表。由本机或者其他设备流向 clash0 的 IP Packet一般不会有问题,但是当 rp_filter 检查 clash0 流出的包时,由于这些包不会带有 fwmark,检查反向路由时不会走刚才定义的策略路由,导致 rp_filter 检查失败,包被丢弃。

解决方法时关闭 clash0 NIC 的 rp_filter功能。

sysctl -w net.ipv4.conf.clash0.rp_filter=0
sysctl -w net.ipv4.conf.all.rp_filter=0

all.rp_filter设置为 0 是必须的。

将 all.rp_filter 设置为 0 并不会将所有其他网卡的 rp_filter一并关闭。此时其他网卡的 rp_filter由它们各自的rp_filter控制。