這幾天閒來無事, 學習了一些關於iptables的知識, 同時也瞭解了一下OpenClash在OpenWRT上是怎麼對經過網關的流量做透明代理的.

首先是Filter規則, filter是一個專門的流量過濾器, 它是不做任何流量處理的, 只負責攔截.

 1Chain INPUT (policy ACCEPT)
 2target     prot opt source               destination
 3openclash_wan_input  all  --  anywhere             anywhere             ! match-set localnetwork src
 4ACCEPT     all  --  anywhere             anywhere             policy match dir in pol ipsec proto esp
 5SOCAT      all  --  anywhere             anywhere
 6ACCEPT     all  --  anywhere             anywhere             /* !fw3 */
 7input_rule  all  --  anywhere             anywhere             /* !fw3: Custom input rule chain */
 8ACCEPT     all  --  anywhere             anywhere             ctstate RELATED,ESTABLISHED /* !fw3 */
 9syn_flood  tcp  --  anywhere             anywhere             tcp flags:FIN,SYN,RST,ACK/SYN /* !fw3 */
10zone_lan_input  all  --  anywhere             anywhere             /* !fw3 */
11zone_wan_input  all  --  anywhere             anywhere             /* !fw3 */
12zone_VPN_input  all  --  anywhere             anywhere             /* !fw3 */
13zone_vpn_input  all  --  anywhere             anywhere             /* !fw3 */
14zone_docker_input  all  --  anywhere             anywhere             /* !fw3 */

這裡我們只看第一條目標是openclash_wan_input 的規則簡單的解釋一下, 所有自外部網絡的所有流量(all)轉發到 OpenClash 進行處理,但不包括來自本地網絡的流量(localnetwork),

1Chain openclash_wan_input (1 references)
2target     prot opt source               destination
3REJECT     udp  --  anywhere             anywhere             multiport dports 7892,7895,9090,7890,7891,7893,7874 reject-with icmp-port-unreachable
4REJECT     tcp  --  anywhere             anywhere             multiport dports 7892,7895,9090,7890,7891,7893,7874 reject-with icmp-port-unreachable

然後openclash_wan_input 這個也比較簡單, 拒絕所有TCP/UDP目標是7892,7895,9090,7890,7891,7893,7874的流量. 這些端口是clash的一些dns, 透明代理等端口.

上下文連起來就可以知道, 這個filter就是openclash不希望非內部的主機連接clash的端口.


接下來看NAT的規則, NAT可以對數據包的目的地和源地址做修改, 起著轉發的作用. 這裡是PREROUTING鏈, 一般是數據包在進入防火牆之前會被放在這裡處理.

 1Chain PREROUTING (policy ACCEPT)
 2target     prot opt source               destination
 3prerouting_rule  all  --  anywhere             anywhere             /* !fw3: Custom prerouting rule chain */
 4zone_lan_prerouting  all  --  anywhere             anywhere             /* !fw3 */
 5zone_wan_prerouting  all  --  anywhere             anywhere             /* !fw3 */
 6zone_VPN_prerouting  all  --  anywhere             anywhere             /* !fw3 */
 7zone_vpn_prerouting  all  --  anywhere             anywhere             /* !fw3 */
 8zone_docker_prerouting  all  --  anywhere             anywhere             /* !fw3 */
 9REDIRECT   udp  --  anywhere             OpenWrt.lan          udp dpt:domain redir ports 5352
10REDIRECT   udp  --  anywhere             192.168.1.5          udp dpt:domain redir ports 5352
11openclash  tcp  --  anywhere             anywhere

openclash自己的是這裡表裡的最後一條規則. 如果上面的規則都沒有命中, 就會走到Openclash這裡.

1Chain openclash (1 references)
2target     prot opt source               destination
3RETURN     tcp  --  anywhere             anywhere             tcp spt:1688
4RETURN     tcp  --  anywhere             anywhere             tcp spt:1723
5RETURN     tcp  --  anywhere             anywhere             tcp spt:openvpn
6RETURN     all  --  anywhere             anywhere             match-set localnetwork dst
7REDIRECT   tcp  --  anywhere             anywhere             redir ports 7892

來看看openclash.其實這裡的規則我不用解釋應該也能看得懂. 就不解釋了

然後看看nat中的output鏈, 該鏈的處理時間是在數據包通過防火牆的時候會被處理.

1Chain OUTPUT (policy ACCEPT)
2target     prot opt source               destination
3openclash_output  all  --  anywhere             anywhere

非常簡單的一條, 所有流量都會流到openclash.

1Chain openclash_output (1 references)
2target     prot opt source               destination
3RETURN     tcp  --  anywhere             anywhere             tcp spt:1688
4RETURN     tcp  --  anywhere             anywhere             tcp spt:1723
5RETURN     tcp  --  anywhere             anywhere             tcp spt:openvpn
6REDIRECT   tcp  --  anywhere             198.18.0.0/16        ! owner UID match nobody redir ports 7892
7RETURN     all  --  anywhere             anywhere             match-set localnetwork dst
8REDIRECT   tcp  --  anywhere             anywhere             ! owner UID match nobody redir ports 7892

然後我們看看這裡面的規則.

第一條是如果源端口是1688, 並且是TCP協議, 那麼直接返回, 停止後續匹配, RETURN 就起著這樣的作用

第二天也是大概同理.

第三條也差不多, 不過openvpn是一個ipset也就是集合, 裡面存放了一些用來匹配的主機信息.

第四天是如果目標是198.18.0.0/16 網段, ! owner UID match nobody 是這個數據包的用戶不能是nobody, 這個我也不太理解, 外部來的數據包一般的所屬用戶會是誰? 最後會被轉發到7892端口, 這個端口也就是clash的透明代理端口, 至於198.18.0.0/16 這個實際上是我開了fake-ip的原因. 所有的dns都會是在該網段中的地址.

第五條和之前的filter類似, 如果目標連接是本地連接就終止匹配.

最後一條和第四條類似, 這條應該是非fake-ip狀態下的規則.


以上就是Openclash對直連情況下的流量做的處理, 還有一種就是流量會經過TUN的時候.

TUN 作為網絡層設備, 它可以用來處理 TCP、UDP、ICMP 流量. 它已經在生產環境中進行了廣泛的測試和使用 - 您甚至可以用它來玩競技遊戲.

使用 Clash TUN 的最大優勢之一是內置支持對操作系統路由表、路由規則和 nftable 的自動管理.

我使用的方案是混合模式, TCP流量走直連, UDP流量走TUN.

這裡也看一下Openclash是如何處理UDP流量的.

1root@OpenWrt:~# iptables -t mangle -L
2Chain PREROUTING (policy ACCEPT)
3target     prot opt source               destination
4openclash  udp  --  anywhere             anywhere

這是mangle表, 這張表所做的工作比filter和nat更細一些, mangle能修改數據包的各種屬性. 如標記(mark)、TTL(Time to Live)值、TOS(Type of Service)字段等。

 1Chain openclash (1 references)
 2target     prot opt source               destination
 3RETURN     udp  --  anywhere             anywhere             udp spt:openvpn
 4RETURN     udp  --  anywhere             anywhere             udp spt:4500
 5RETURN     udp  --  anywhere             anywhere             udp spt:isakmp
 6RETURN     udp  --  anywhere             anywhere             udp spt:isakmp
 7RETURN     udp  --  anywhere             anywhere             udp spt:bootpc
 8RETURN     all  --  anywhere             anywhere
 9RETURN     all  --  anywhere             anywhere             match-set localnetwork dst
10openclash_upnp  udp  --  anywhere             anywhere
11MARK       all  --  anywhere             anywhere             MARK set 0x162

上面部分其實沒什麼好看的, 主要是最後一條, 講數據包標記為162. mark是策略路由經常使用的一個方法.

接下來我們就需要知道162到底是會被如何路由.

1root@OpenWrt:~# ip rule list
20:      from all lookup local
332764:  from all fwmark 0x162 lookup 354
432765:  from all fwmark 0x162 lookup 354
532766:  from all lookup main
632767:  from all lookup default

如果數據包的標記等於0x162,那麼它們將被路由到表354(lookup 354)。

1root@OpenWrt:~# ip route list table 354
2default dev utun scope link

結束了, 所有進來的流量都會流到utun網卡.

以上就是OpenClash對防火牆做的所有操作.

不過聽說近幾年nftable有崛起的趨勢, 要把iptable代替. 並且nft性能也更優秀配置更加簡單.

不過這種事還早著.