Clash TUN模式下的UDP服务异常诊断与解决
Clash开启TUN模式后,本地UDP端口无法与外部访问正常建立连接,如何诊断问题?如何解决问题?
问题背景 Background
在我的日常生活中,我已经习惯通过ClashX的TUN模式(增强模式)接管MacOS系统的所有流量,便于观察实时流量并配置规则。Clash作为Surge的平替,已经成为了我在MacOS系统中的出口流量管理中心和出口防火墙。
MacOS的入口流量管理还是推荐使用PF防火墙
最近随着幻兽帕鲁的爆火,我在本地建立专用服务器后,发现局域网可以正常登录,来自公网的客户端却显示无法连接(我拥有公网IP并已建立好公网映射)。
问题诊断 Troubleshooting
UDP&TCP 进出流量检查
首先,我对TCP和UDP的进出流量进行诊断
TCP出口流量,很容易测试,可以发现流量分流运行正常
TCP入口流量,可以发现也可以正常通过公网IP访问,也可以正常双向通信(Hello从客户端发到服务端,Back从服务端发回客户端)
UDP出口流量,这里我们尝试访问谷歌的NTP服务,可以发现正常建立连接
UDP入口流量,我们先建立测试服务
尝试发送一个UDP包,可以发现成功接收到数据包?似乎UDP监听目前也可以正常使用?事情真的如此吗?
尝试发送响应包,我们发现客户端无法接收到。让客户端再次发送请求包,我们发现服务端也无法接收到。
此时在局域网中或不开启TUN进行测试,所有连接均正常,方法也一样,就跳过了
Wireshark分析
此时我们已经确认了问题,让我们用Wireshark深入诊断一下,在使用Wireshark监听的同时再次复现上文中出现问题的操作
你可能发现了此时我的公网IP和端口改变了,IP从218.79.x.x变为117.131.x.x,端口变为50011,不要在意这个
我们可以发现第一个成功发送的UDP数据包,目标端口是50011,这个数据包我们在服务端正常收到了
第二个数据包是服务端返回发送的数据包,在客户端中我们并没能收到。图中可以看到Source Port为50131,这与服务开放的端口50011并不一致,此处我猜测是Clash在TUN NIC中对Source Port进行了重新映射,避免多个代理客户端发送了同样的Source Port产生冲突。那么因为Source Port和客户端发出的Destination Port不同,由于目前广泛采用的端口受限型NAT(在PlayStation中被称为NAT3),所以无法成功连接。此时如果客户端是地址受限型NAT(NAT2)或完全圆锥形NAT(NAT1),我推测是可以正常通信的。
问题解决 Solve it
此时我们已经确定了问题的原因——UDP服务出口流量经过TUN网卡导致Source Port不正确,无法与客户端构建连接。那么我们应该如何解决呢?
方案1 - 手动管理路由表
既然问题出在流量经过TUN网卡,那么我们可以为特定客户端IP指定路由表规则,让其直接使用物理网卡,忽略TUN网卡。
在MacOS中,我通过如下命令指定去往117.131.x.x
的流量直接使用en20
物理网卡,成功解决问题。
1 | sudo route add 117.131.x.x -interface en20 |
更换ClashX客户端为ClashX.Meta,其允许对TUN功能进行更详细的配置,我们也可以编写配置文件以使其自动排除特定的网段
1 | tun: |
缺陷
- 此方法只能用于经常通信的几个客户端
方案2 - 使用端口转发工具
一些端口转发工具可以指定使用的监听地址,并且从对应的网卡发送响应数据包。如 gost
使用gost绑定物理网卡对应的地址,进行流量转发
1 | gost -L=udp://192.168.x.x:50111/127.0.0.1:50112 |
客户端可以正常发送数据包,也可以正常收到响应数据包
缺陷
- 流量增加一层应用层转发,增大了服务器性能压力,也增加了网络延迟
- 服务端应用无法正确判断客户端真实地址
方案3 - 网卡桥接
仅为猜想,并未测试
可以使用Vmware添加桥接网卡,直接接入物理网卡,即可忽略Clash TUN网卡
缺陷
- 路由器的端口转发很可能不认桥接网卡,导致无法连接(我就遇到了这个情况,华为k662c路由器的端口转发无法绑定桥接设备的端口)
Clash TUN模式下的UDP服务异常诊断与解决