为什么 iptables 标记但不将路由作为我的例外

为什么 iptables 标记但不将路由作为我的例外

我想用域名来控制浏览器的路由数据包,让特殊域名内的流量包通过VPN,而其他不带特殊域名的流量包通过正常路由。我使用 dnsmasq 和 iptables 实现了这个目标。

顺便说一句,我使用我的笔记本电脑(deepinLinux 4.15.0-29deepin-generic)与dnsmasq和wireguard,我的笔记本电脑是VPN客户端,也是dns服务器。

步骤如下:

1配置 dnsmasq

配置文件:

root@notebook-PC:~# cat /etc/dnsmasq.conf
# auto-generated config file from /etc/config/dhcp
conf-file=/etc/dnsmasq.conf
domain-needed
log-queries
log-facility=/var/log/dnsmasq.log
no-resolv
localise-queries
read-ethers
bogus-priv
expand-hosts
local-service
cache-size=150
domain=lan
server=/lan/
server=223.6.6.6
# dhcp-leasefile=/tmp/dhcp.leases
# addn-hosts=/tmp/hosts
conf-dir=/etc/dnsmasq.d
stop-dns-rebind
rebind-localhost-ok
dhcp-broadcast=tag:needs-broadcast


dhcp-range=lan,192.168.10.100,192.168.10.249,255.255.255.0,12h
# no-dhcp-interface=eth0.2

并且只有 /etc/dnsmasq.d 中的一个文件

root@notebook-PC:~# cd /etc/dnsmasq.d/
root@notebook-PC:/etc/dnsmasq.d# more newgfw.conf 
# dnsmasq rules generated by ss_spec_dst_fw
# Last Updated on 2019-04-29 13:36:58
# 
server=/030buy.com/8.8.8.8#53
ipset=/030buy.com/ss_spec_dst_fw
server=/0rz.tw/8.8.8.8#53
ipset=/0rz.tw/ss_spec_dst_fw
server=/1000giri.net/8.8.8.8#53
ipset=/1000giri.net/ss_spec_dst_fw
...........

选项“server”和“ipset”让这些dns查询8.8.8.8,然后将响应主机ip添加到ipset ss_spec_dst_fw

当然,我没有忘记添加dnsmasq作为dns服务器

root@notebook-PC:~# nslookup
> google.com
Server:     127.0.0.1
Address:    127.0.0.1#53

Non-authoritative answer:
Name:   google.com
Address: 172.217.24.206
Name:   google.com
Address: 2404:6800:4005:806::200e
> 

2修改wg-quick并启动wireguard

我发现wg-quick.sh让所有流量通过VPN接口,然后我注释了3行代码,我想自己添加规则。

vi /usr/bin/wg-quick

................
add_default() {
    local table proto key value
    if ! get_fwmark table; then
        table=51820
        while [[ -n $(ip -4 route show table $table) || -n $(ip -6 route show table $table) ]]; do
            ((table++))
        done
        cmd wg set "$INTERFACE" fwmark $table
    fi
    proto=-4
    [[ $1 == *:* ]] && proto=-6
#   cmd ip $proto route add "$1" dev "$INTERFACE" table $table
#   cmd ip $proto rule add not fwmark $table table $table
#   cmd ip $proto rule add table main suppress_prefixlength 0
    while read -r key _ value; do
        [[ $value -eq 1 ]] && sysctl -q "$key=2"
    done < <(sysctl -a -r '^net\.ipv4.conf\.[^ .=]+\.rp_filter$')
    return 0
}
................

然后当wireguard启动时

wg-quick up wgnet0

wgnet0: flags=209<UP,POINTOPOINT,RUNNING,NOARP>  mtu 1420
        inet 192.168.32.2  netmask 255.255.255.0  destination 192.168.32.2
        unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  txqueuelen 1000  (UNSPEC)
        RX packets 15  bytes 1380 (1.3 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 615  bytes 21652 (21.1 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

wlp5s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.1.3  netmask 255.255.255.0  broadcast 192.168.1.255

wgnet0 是通过特殊域名流量使用的 vpn 接口,wlp5s0 是通过普通流量使用的普通接口。

ip rule show follow

root@notebook-PC:~# ip rule
0:  from all lookup local 
220:    from all lookup 220 
32766:  from all lookup main 
32767:  from all lookup default 

[3]配置规则和路由

在文件 /etc/iproute2/rt_tables 中添加 wg 表

root@notebook-PC:~# cat /etc/iproute2/rt_tables
#
# reserved values
#
255 local
254 main
253 default
201     wg
0   unspec
#
# local
#
#1  inr.ruhep

然后添加规则如下

ip -4 route add table wg default via 192.168.32.1 dev wgnet0 metric 100
ip rule add prio 100 from all fwmark 51820 lookup wg

root@notebook-PC:~# ip rule show all
0:  from all lookup local 
100:    from all fwmark 0xca6c lookup wg 
220:    from all lookup 220 
32766:  from all lookup main 
32767:  from all lookup default 
root@notebook-PC:~# ip route show table wg
default via 192.168.32.1 dev wgnet0 metric 100 

[4]启用标记

root@notebook-PC:~# cat /proc/sys/net/ipv4/tcp_fwmark_accept 
0
root@notebook-PC:~# sysctl -w net.ipv4.tcp_fwmark_accept=1
net.ipv4.tcp_fwmark_accept = 1
root@notebook-PC:~# cat /proc/sys/net/ipv4/tcp_fwmark_accept 
1

[5]配置iptables添加链和规则

由于前面的步骤,我已将需要路由 VPN 接口的 IP 添加到 ipset ss_spec_dst_fw 中,然后我需要标记这些数据包。

有了这个文章http://www.faqs.org/docs/iptables/traversingoftables.html),iptables分两步做出路由决策,本地进程(本例中是我笔记本上的浏览器进程)在进入OUTPUT chian之前做出路由决策,然后我需要本地进程重新路由。

所以按照我的做法,我在OUTPUT_direct链中添加规则,所有匹配ipset ss_spec_dst_fw的数据包都跳转到PREROUTING_direct链,并在PREROUTING_direct链中添加规则,所有匹配ipset ss_spec_dst_fw的数据包都进行标记,如下图所示:

图像

当 PREROUTING_direct chain() 作为 ACCEPT 数据包时,再次进行路由决策,操作如下:

root@notebook-PC:~# iptables -t mangle -A OUTPUT_direct -m set --match-set ss_spec_dst_fw dsts -j PREROUTING_direct

root@notebook-PC:~# iptables -t mangle -A PREROUTING_direct -j CONNMARK --restore-mark
root@notebook-PC:~# iptables -t mangle -A PREROUTING_direct -m set --match-set ss_spec_dst_fw dsts -j MARK --set-mark 51820
root@notebook-PC:~# iptables -t mangle -A PREROUTING_direct -j CONNMARK --save-mark
root@notebook-PC:~# iptables -t mangle -A PREROUTING_direct -j ACCEPT
root@notebook-PC:~# 
root@notebook-PC:~# iptables -L -t mangle
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination         
PREROUTING_direct  all  --  anywhere             anywhere            
PREROUTING_ZONES_SOURCE  all  --  anywhere             anywhere            
PREROUTING_ZONES  all  --  anywhere             anywhere            

Chain INPUT (policy ACCEPT)
target     prot opt source               destination         
INPUT_direct  all  --  anywhere             anywhere            

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         
FORWARD_direct  all  --  anywhere             anywhere            

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         
OUTPUT_direct  all  --  anywhere             anywhere            

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination         
POSTROUTING_direct  all  --  anywhere             anywhere            

Chain FORWARD_direct (1 references)
target     prot opt source               destination         

Chain INPUT_direct (1 references)
target     prot opt source               destination         

Chain OUTPUT_direct (1 references)
target     prot opt source               destination         
PREROUTING_direct  all  --  anywhere             anywhere             match-set ss_spec_dst_fw dst

Chain POSTROUTING_direct (1 references)
target     prot opt source               destination         

Chain PREROUTING_ZONES (1 references)
target     prot opt source               destination         
PRE_public  all  --  anywhere             anywhere            [goto] 
PRE_public  all  --  anywhere             anywhere            [goto] 

Chain PREROUTING_ZONES_SOURCE (1 references)
target     prot opt source               destination         

Chain PREROUTING_direct (2 references)
target     prot opt source               destination         
CONNMARK   all  --  anywhere             anywhere             CONNMARK restore
MARK       all  --  anywhere             anywhere             match-set ss_spec_dst_fw dst MARK set 0xca6c
CONNMARK   all  --  anywhere             anywhere             CONNMARK save
ACCEPT     all  --  anywhere             anywhere            

Chain PRE_public (2 references)
target     prot opt source               destination         
PRE_public_log  all  --  anywhere             anywhere            
PRE_public_deny  all  --  anywhere             anywhere            
PRE_public_allow  all  --  anywhere             anywhere            

Chain PRE_public_allow (1 references)
target     prot opt source               destination         

Chain PRE_public_deny (1 references)
target     prot opt source               destination         

Chain PRE_public_log (1 references)
target     prot opt source               destination         
root@notebook-PC:~# 

现在,所有工作都完成了,但结果不是我,除了,它不起作用!

ipset ss_spec_dst_fw 添加了来自 dns 查询的 ip 响应

root@notebook-PC:~# ipset list ss_spec_dst_fw
Name: ss_spec_dst_fw
Type: hash:ip
Revision: 4
Header: family inet hashsize 1024 maxelem 65536
Size in memory: 5368
References: 2
Number of entries: 113
Members:
216.58.221.229
172.217.161.170
172.217.161.173
203.208.43.95
216.58.199.5
216.58.221.234
216.58.199.110
172.217.161.141
172.217.161.133
216.58.200.74
172.217.161.138
203.208.39.215
...........

dnsmasq 很好,然后我测试这些 ip 路由:

root@notebook-PC:~# ip route get 216.58.221.229
216.58.221.229 via 192.168.1.1 dev wlp5s0 src 192.168.1.3 uid 0 
    cache 

这个结果不是我所期望的,192.168.1.1是普通网关不是VPN网关,然后我测试添加标记51820(上面代码的标记值),

root@notebook-PC:~# ip route flush cache

root@notebook-PC:~# ip route get 216.58.221.229 mark 51820
216.58.221.229 via 192.168.32.1 dev wgnet0 table wg src 192.168.32.2 mark 0xca6c uid 0 
    cache 
root@notebook-PC:~# ip route flush cache
root@notebook-PC:~# ip route get 216.58.221.229
216.58.221.229 via 192.168.1.1 dev wlp5s0 src 192.168.1.3 uid 0 
    cache 

这些结果验证 ip 规则和路由是否正常,但其他情况则不然,然后我检查 iptables 是否标记这些数据包,我跟踪路由 216.58.221.229 并记录

root@notebook-PC:~# traceroute 216.58.221.229
traceroute to 216.58.221.229 (216.58.221.229), 30 hops max, 60 byte packets
 1  * * *
 2  * * *
 3  * * *
 4  * * *
 5  * * *
 6  *

并连接跟踪日志

root@notebook-PC:~# conntrack -E -d 216.58.221.229
    [NEW] udp      17 30 src=192.168.1.3 dst=216.58.221.229 sport=44951 dport=33434 [UNREPLIED] src=216.58.221.229 dst=192.168.1.3 sport=33434 dport=44951 mark=51820
    [NEW] udp      17 30 src=192.168.1.3 dst=216.58.221.229 sport=34181 dport=33435 [UNREPLIED] src=216.58.221.229 dst=192.168.1.3 sport=33435 dport=34181 mark=51820
    [NEW] udp      17 30 src=192.168.1.3 dst=216.58.221.229 sport=58059 dport=33436 [UNREPLIED] src=216.58.221.229 dst=192.168.1.3 sport=33436 dport=58059 mark=51820
    [NEW] udp      17 30 src=192.168.1.3 dst=216.58.221.229 sport=40739 dport=33437 [UNREPLIED] src=216.58.221.229 dst=192.168.1.3 sport=33437 dport=40739 mark=51820
........................

标记51820完成!

现在我很困惑,我错过了什么?

答案1

本地发起的数据包不通过mangle/PREROUTING链传递。您应该将标记规则移至mangle/OUTPUT链中。您可以使用“ iptables-save -c”或“ iptables -L -n -v”列出规则集(第一个命令更好,因为它显示完整的规则集)并检查规则计数器。在您的情况下,计数器可能为零。

相关内容