shixudong163.comLinux上经常使用iptables的OUTPUT/REDIRECT规则搭配redsocks实现本机tcp应用的透明代理功能早期版本的WSL2mirrored也支持这一用法然而新版WSL2mirrored已无法使用。本文结合mirrored模式的特点进行分析并给出解决方案。对于OUTPUT链的REDIRECT功能上等价于DNAT --to 127.0.0.1完成REDIRECT后由于目标IP已改为127.0.0.1需要重新路由。然而对于新版WSL2mirrored而言该127.0.0.1实际上对应Win11主机的127.0.0.1而非WSL2自身因此需要到Win11绕一圈才能返回WSL2。当数据包从Win11再次返回WSL2就conntrack状态来说事实上已经是新数据包并且无法匹配先前conntrack记录因此将生成新的conntrack记录。使用conntrack -L也能看到两条conntrack记录第一条记录了初始目标IP和端口但状态为SYN_SENTUNREPLIED第二条虽然状态已变为ASSURED但记录的目标IP和端口却为127.0.0.1:12345。由于第二条记录已不再包含初始目标IP和端口因此redsocks通过SO_ORIGINAL_DST只能获取到127.0.0.1:12345,无法获取初始目标IP和端口导致对端socks5连接127.0.0.1:12345失败透明代理功能失效。2.3.11之前版本的WSL2mirrored模式自带的策略路由相对简单需要透明代理的数据包经REDIRECT处理后虽然目标IP也已改为127.0.0.1但不需要绕行Win11而是直接发往本机127.0.0.1:12345redsocks能顺利获取到初始目标IP和端口透明代理功能正常。针对新版WSL2mirrored无法使用REDIRECT实现透明代理的缺陷有两种解决方案。由于新版WSL2已完全支持TPROXY并且TPROXY不用修改目标IP需要透明代理的数据包可通过TPROXY策略路由直接发往本机TPROXY对应的后端无需绕行Win11。而且更为巧妙的是针对WSL2本机发出的需透明代理的数据包在mangle表OUTPUT链打mark后立即调用ip_route_me_harder重新路由此后该mark就失去意义直到数据包后续被mangle表PREROUTING链自发自收并重新打mark。即使TPROXY策略路由的mark设置为1全程也都不会和filter表OUTPUT链处WSL2mirrored对自身发出的数据包打的mark也是1相冲突详见《WSL2和podman》。使用TPROXY取代REDIRECT实现透明代理不足之处在于redsocks不支持TCP协议使用TPROXY因此需要搭配其他软件使用。此外TPROXY和br_netfilter天然存在冲突而新版WSL2又默认加载了br_netfilter导致WSL2上的嵌套虚拟机因使用网桥而无法利用WSL2的TPROXY实现透明代理功能。另一种解决方案是使用DNAT取代REDIRECT由于REDIRECT等价于DNAT --to 127.0.0.1当redsocks绑定0.0.0.0:12345后DNAT --to 127.0.0.2也等同于DNAT --to 127.0.0.1。也就是说DNAT --to 127.0.0.2功能上也等价于REDIRECT并且完美避开了WSL2mirrored的127.0.0.1实际对应Win11的127.0.0.1这个坑。这对于内核版本不高于6.6的WSL2足以恢复WSL2本机tcp应用的透明代理功能。然而对于使用6.18内核的最新版WSL2由于redsocks的返回包在发给WSL2之前也需要绕行Win11存在和《WSL2和podman》一文中高版本内核同样的问题。因此还需要设置hostAddressLoopbackfalse这样一来redsocks的返回包就不会绕行Win11也就完美避开了《WSL2和podman》中新内核引发的问题。本方案的不足之处就是网络上其他机器如也想使用WSL2上的redsocks实现透明代理功能WSL2还需要启用相应网卡或all的route_localnet。考虑到WSL2mirrored已默认启用和Win11主机共用网卡的route_localnetWSL2上的Docker或podman也能针对自己生成的网桥自动启用route_localnet因此只需要针对WSL2上嵌套虚拟机对应的网桥或者all启用route_localnet即可。