# 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。&#x20;
  * 在 mangle 表 OUTPUT 链中使用 `uid-owner` 排除 Clash 发出的流量
  * 在 mangle 表 PREROUTING 中，将其他设备发来的流量打上 fwmark

## 实施方案

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

{% hint style="info" %}
用户 uid，fwmark ，路由表 id 是三个不相关的值。这里仅仅是为了举例方便都设置成 129.
{% endhint %}

### 创建NIC

配置 iptables 和 ip rule 前最好提前配置好 Tun NIC 并 bring up。创建 NIC 的方法可以参考[前面的部分](https://comzyh.gitbook.io/clash/#create-nic-ahead)。

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

### 配置 iptables&#x20;

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

```bash
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

```bash
# 限制高目标端口的流量
# 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。

```bash
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 上。

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

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

```bash
ip route get 1.1.1.1 mark 129
```

&#x20;返回应和下面类似，注意加粗的部分。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`功能。

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

{% hint style="warning" %}
将 `all.rp_filter`设置为 `0` 是必须的。
{% endhint %}

{% hint style="info" %}
将 all.rp\_filter 设置为 `0` 并不会将所有其他网卡的 `rp_filter`一并关闭。此时其他网卡的 `rp_filter`由它们各自的`rp_filter`控制。
{% endhint %}
