先看一下iptables,正经图

image-20220225000210969

路由设置

ip rule add fwmark 1 table 100 #带有mark1的包都发到table 100
ip route add local 0.0.0.0/0 dev lo table 100 #所有包都走lo(本地回环)

不同场景

内网机器访问国内服务

A的请求到达路由器的PREROUTING链,根据链上规则

-m set --match-set reserved_ip dst -j RETURN 

直接返回原来的PREROUTING连,走FORWARD以及后续的操作

内网机器访问国外服务

A的请求到达路由器的PREROUTING链,根据链上规则

-p tcp -j TPROXY --on-port 1081 --tproxy-mark 0x1/0x1

给TCP请求加上mark,然后重定向到1081,根据前面的路由设置,会直接到本地回环,请求会到达Transparent Proxy

  1. 判断需要走Proxy,那接下来的流程就是,包装原请求并发出,带上了0xff(即255)的mark

    按照OUTPUT链上的规则

     -m mark --mark 0xff -j RETURN 
    

    ``

    直接返回OUTPUT链,走POSTROUTING链出去

  2. 判断为直连,直接原封不动的请求真实服务器,也是带上0xff的mark,后面和情况1一样

路由器访问国内服务

路由器的请求会从OUTPUT开始,会匹配上OUTPUT链上规则

--match-set chnroute dst -j RETURN 

直接返回OUTPUT链,走POSTROUTING链出去

路由器访问国外服务

路由器的请求会匹配上OUTPUT链上最后一条规则

-p tcp -j MARK --set-mark 0x1

给这个请求打上mark,然后打上mark的请求会重新走PREROUTING链,会匹配上如下规则

-p tcp -j TPROXY --on-port 1081 --tproxy-mark 0x1/0x1

也就是说,流量会绕一圈,重新进入Transparent Proxy

一个坑来了

当通过tproxy进入Transparent Proxy的时候,这个包的的remoteAddr是请求发起方的地址(即路由器IP),localAddr则是真实需要到达的服务器地址(比如Google.com),响应该请求的时候,DST会变成请求发起方的地址(正是路由器的地址)

image-20220224235031112

如果我们不在OUTPUT对这个包做操作的话,这个包会再次被设置mark,都无法完成握手

image-20220224235423320

最终成品:Github