OpenWRT Chaos Calmer
Linux 3.18.27 mips
运行 ss-tunnel,监听 0.0.0.0:5301,转发至 8.8.8.8:53
先尝试添加这个 iptables 规则:
iptables -t nat -A OUTPUT \! -o lo -p udp --dport 53 -j REDIRECT --to-port 5301
结果居然不能用,nslookup 提示超时。
这个时候,tcpdump -i lo 保存 pcap 文件,用 wireshark 打开,发现 DNS 请求包的目的 IP 和端口已经被改为 127.0.0.1:5301,但是 DNS 响应包的源 IP 和端口却还是[本机 WAN 口 IP]:5301(应该被修改为[程序使用的 DNS 服务器 IP]:53 才对吧?),紧跟着的下一个包就是拒绝掉这个 UDP 响应包的 ICMP port unreachable。
然后删掉这个 iptables 规则,换用这一条:
iptables -t nat -A OUTPUT \! -o lo -p udp --dport 53 -j DNAT --to-destination [本机 WAN 口 IP]:5301
这下正常了。tcpdump 抓包,可以看到 DNS 请求包和响应包的目的 IP 端口 /源 IP 端口都被正确修改了。
但是 WAN 口 IP 不固定啊……况且我的另一台 Debian VPS 上也在用这种 REDIRECT,没发现有这种怪毛病啊。
iptables -t raw -L 似乎是空的。
需要 tproxy 模块
TPROXY 只能在 mangle 表的 PREROUTING 链上使用(其实这台路由也已经用上了),不能对本机生效吧。
其实路由下的机器都已经能走 ss 了,就是路由器自己不能走 ss,很奇怪。
抱歉没审清楚题目,那么下面这个能工作么?
iptables -t nat -A OUTPUT \! -o lo -p udp --dport 53 -j DNAT --to-destination 127.0.0.1:5301
还有我没看懂,按说 REDIRECT 应该重定向到 incoming interface,你的第一行命令里已经 ! -o lo,为什么请求包会被定向到 127.0.0.1:5301?
你可以试试 lan 口 IP
我在 Debian VPS 上抓包看了,确实是这样。
试一下倒是可以,不过这很奇怪啊,难道是我这路由器里有别的什么奇怪的规则在捣鬼?我不信我还能碰到 bug。
在 OUTPUT 里加入 REDIRECT 规则后,本机 DNS 请求包的目的 IP:端口被修改,变成 127.0.0.1:[REDIRECT 的目标端口];监听那个端口的是 dnsmasq,它发出的 DNS 响应包的源 IP:端口也被修改,变成[软件请求的 DNS 服务器]:53。
LAN 口 IP 也是一样的,不行……
Debian VPS 上用这个 REDIRECT 是没问题的,抓包也能看到来回数据包都被正确修改了。但是这台 OpenWRT 路由器就不正常,抓包可以看到只有请求包被修改了,响应包没有被修改,然后就被 ICMP port unreachable 拒绝。
openwrt 下,路由和内网的默认 DNS 都是路由器上的 dnsmasq 监听的 53 端口。路由器本身并不直接使用运营商分配的 dns 服务器,和内网一样都要通过本机的 dnsmasq 绕一下,你失败的原因就是这个。
正确的姿势是:
dnsmasq 里面把上游 dns 指定为 127.0.0.1:5301,路由器和内网的 dns 就会都通过你的 ss-tunnel 查询了。
不行的话,你先尝试一下把其他自己添加的规则删掉,在一个干净的表上单独尝试这个规则,排除干扰的可能。此外你要不试一下把 ! -o lo 删掉?我还是没明白这个规则怎么改的出 Localhost
对了,WAN 口上默认是有 MASQUERADE 的吧?这可能是 WAN 口成功的原因。
OUTPUT 链是对已经经过路由后的数据包的过程,你在 OUTPUT 链修改后的地址,一样送给上级路由了,你本地是不可能收到的;
想转发到本地,必须在 NAT 表的 PREROUTING 链里面改,改完后再经过路由,才会转发到本地。
我的 openwrt 没有用 ss, 用的 cow, 这个可以自动判断是否需要爬,墙。
我知道 openwrt 用了 dnsmasq,然后内网机器从 DHCP 获取的 DNS 服务器就是它。然后,内网机器的 DNS 请求被这个 dnsmasq 接受、转发,不就是相当于路由器本机上跑的软件发了 DNS 请求了么?
接下来不就是我碰到的问题了?
我也装了编译好的 ipk 包(ss-libev 和 luci 插件两个包),然后,好像是通过 ss-rule 生成规则,但对 UDP 来说,ss-rule 只是加了一个 PREROUTING 的 TPROXY,然而内网的机器 DNS 服务器都是路由器上的这个 dnsmasq 啊,所以这个 TPROXY 基本上是废的。
(ss-rule 还在 nat 表的 OUTPUT 链加了 REDIRECT,不过只对 tcp 生效)
没记错的话,为了绕过这个问题,我一般是手动在 nat 表的 PREROUTING 链再加一条 REDIRECT 或 DNAT,把内网(不是本机)的 DNS 请求转到 ss-tunnel 上(然后实际上是绕过了这个 dnsmasq)。
我 tcpdump -i lo 抓包了,OpenWRT 症状如上述,另一台 Debian VPS 则没有问题。
抓包没啥意义,不能显示出 iptables 的工作逻辑。你按着图里的逻辑排查吧。
dnsmasq
server=/请求这样转发给 ss tunnel 就行
其实也不能说路由器上的这个 TPROXY 是废的,因为其他 UDP 包(比如内网机器执行 nslookup qq.com 114.114.114.114 这种)还是会走这个 TPROXY 的。
但是,路由器上跑的软件自己发出的 UDP 包就不会走这个 TPROXY 了(压根不会过 mangle PREROUTING 链),尤其是路由器上的那个 dnsmasq。
这是让 dnsmasq 直接把 ss-tunnel 当上级服务器么?这样也许可以绕过问题,不过我还是好奇为什么会出现这种怪状……
换句话说,是我一个人碰到这个问题,还是很多人都能重复出来这个问题?
从另一个方面说,万一我有个需求是让路由器上其他软件发的 UDP 包也走代理呢? TPROXY+ss-redir 看样子是解决不了这个问题了(因为本机发出的包没经过 mangle PREROUTING 链),退而求其次,REDIRECT+ss-local 居然也不行,这就蛋疼了……
打错……退而求其次,REDIRECT+ss-tunnel
我其实没看懂你前一个回复的意思……不是本机发出的包走 OUTPUT、转发的包走 REROUTING,“井水不犯河水”么?
又打错…… REROUTING=>PREROUTING
并不是,你看我发的图;
PREROUTING 在路由选路前,不管是本地发出还是从外面转发的,进行路由选路前都匹配 PREROUTING (从名字也可以看出这个意思),所以 DNAT 过程都需要在这个链里完成,修改后的地址经过路由选路,才能往想要的地方去。
man iptables:
....
nat:
This table is consulted when a packet that creates a new connection is encountered. It consists of four built-ins:
PREROUTING (for altering packets as soon as they come in),
INPUT (for altering packets destined for local sockets),
OUTPUT (for altering **locally-generated** packets before routing),
and POSTROUTING (for altering packets as they are about to go out).
/>MASQUERADE 是 zone_wan_postrouting 里的,一个数据包需要走这个路径才能被 MASQUERADE:
POSTROUTING->delegate_postrouting->zone_wan_postrouting
但是,在 delegate_postrouting 那里有约束条件-o [WAN 口 iface] -j zone_wan_postrouting。
因为 DNS 响应包是从 lo 发出的,所以压根就不会走到 zone_wan_postrouting 和后面的 MASQUERADE 吧。