我想用域名来控制浏览器的路由数据包,让特殊域名内的流量包通过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
”列出规则集(第一个命令更好,因为它显示完整的规则集)并检查规则计数器。在您的情况下,计数器可能为零。