智能分流
本文介绍在 Linux 系统上实现全局智能分流的常用方案,包括基于 iptables + ipset 的 CN IP 直连方案,以及 Clash 规则配置方法,适用于需要精细控制流量走向的服务器或桌面环境。
Linux系统全局智能分流方案
通过 iptables 实现智能分流
安装相关软件
- shadowsocks-libev
- ipset
$ pacman -S shadowsocks-libev ipset配置shadowsocks-libev(略过)
假设shadowsocks配置文件为/etc/shadowsocks.json
获取中国IP段
将以下命令写入脚本保存执行(假设保存在/home/yang/bin/路由表/目录下):
#!/bin/sh
wget -c http://ftp.apnic.net/stats/apnic/delegated-apnic-latest
cat delegated-apnic-latest | awk -F '|' '/CN/&&/ipv4/ {print $4 "/" 32-log($5)/log(2)}' | cat > /home/yang/bin/路由表/cn_rules.conf# 生产使用
# 下载ip表
wget -c http://ftp.apnic.net/stats/apnic/delegated-apnic-latest
cat delegated-apnic-latest | awk -F '|' '/CN/&&/ipv4/ {print $4 "/" 32-log($5)/log(2)}' | cat > cn_rules.conf
# 创建ipset
ipset create -n china hash:net maxelem 65536
for ip in $(cat cn_rules.conf); do ipset add china $ip; done
# 查看
ipset list china
# 打标记
iptables -t mangle -A PREROUTING -m set ! --match-set china dst -j MARK --set-mark 1
# tunnel表
ip route add 0.0.0.0/0 dev tun0 table tunnel
ip route show table main | grep -Ev ^default | while read ROUTE ; do ip route add table tunnel $ROUTE ; done
# 回国模式
iptables -t mangle -R PREROUTING 1 -m set --match-set china dst -j MARK --set-mark 1
# 出国模式
iptables -t mangle -R PREROUTING 1 -m set ! --match-set china dst -j MARK --set-mark 1
# 全局模式
iptables -t mangle -R PREROUTING 1 -d 0.0.0.0/0 -j MARK --set-xmark 0x1/0xffffffff
# 创建虚拟网桥
ip link add device_name type bridge
ip addr add ip/mask dev device_name
ip link set device_name up
# 删除网卡
ip link del dev global_ip
# vtysh配置
router rip
network device_name
network ipaddr/mask
version 2
# IP转发
iptables -t nat -A POSTROUTING -s 0.0.0.0/0 -m mark --mark 1 -j SNAT --to-source 1.2.3.4
iptables -t nat -A POSTROUTING -s 0.0.0.0/0 -m mark --mark 1 -j SNAT --to-source 1.2.3.4创建启动和关闭脚本
$ vim /home/yang/bin/shadowsocks/ss-up.sh#!/bin/bash
VPN_SERVER=$SERVER_IP # vpn服务器的 IP 地址
# 创建
ipset create -n sdwan hash:net maxelem 65536
for ip in $(cat cn_rules.conf); do
ipset add sdwan $ip
done
# 查看
ipset list sdwan
# 配置pppoe拨号自动协商
iptables -t mangle -I POSTROUTING -o pppoe-wan -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mssto-pmtu
# 创建自定义链
iptables -t nat -N openvpn
# 打标记
iptables -t mangle -A PREROUTING -p tcp -j MARK --set-mark 1
iptables -t nat -A PREROUTING -m set --match-set sdwan dst -j MARK --set-mark 123
iptables -t nat -A POSTROUTING -m mark --mark 1 -o eth0
# 追加规则,如果目标是服务器,直接return,中断当前规则的匹配匹配
iptables -t nat -A openvpn -d $VPN_SERVERR -j RETURN
# 允许连接到保留网络
iptables -t nat -A openvpn -d 0.0.0.0/8 -j RETURN
iptables -t nat -A openvpn -d 127.0.0.0/8 -j RETURN
iptables -t nat -A openvpn -d 169.254.0.0/16 -j RETURN
iptables -t nat -A openvpn -d 172.16.0.0/12 -j RETURN
iptables -t nat -A openvpn -d 192.168.0.0/16 -j RETURN
iptables -t nat -A openvpn -d 224.0.0.0/4 -j RETURN
iptables -t nat -A openvpn -d 240.0.0.0/4 -j RETURN
# 允许连接到中国ip
iptables -t nat -A openvpn -p tcp -m set --match-set sdwan dst -j RETURN
# 如果你想对 icmp 协议也实现智能分流,可以加上下面这一条
iptables -t nat -A openvpn -p icmp -m set --match-set sdwan dst -j RETURN
# Redirect to Shadowsocks
# 把1081改成你的shadowsocks本地端口
iptables -t nat -A openvpn -p tcp -j REDIRECT --to-port 1081
# 如果你想对 icmp 协议也实现智能分流,可以加上下面这一条
# iptables -t nat -A SHADOWSOCKS -p icmp -j REDIRECT --to-port 1081
# 将SHADOWSOCKS链中所有的规则追加到OUTPUT链中
iptables -t nat -A OUTPUT -p tcp -j SHADOWSOCKS
# 如果你想对 icmp 协议也实现智能分流,可以加上下面这一条
# iptables -t nat -A OUTPUT -p icmp -j SHADOWSOCKS#!/bin/bash
SOCKS_SERVER=$SERVER_IP # SOCKS 服务器的 IP 地址
# Setup the ipset
ipset -N chnroute hash:net maxelem 65536
for ip in $(cat '/home/yang/bin/路由表/cn_rules.conf'); do
ipset add chnroute $ip
done
# 在nat表中新增一个链,名叫:SHADOWSOCKS
iptables -t nat -N SHADOWSOCKS
# Allow connection to the server
iptables -t nat -A SHADOWSOCKS -d $SOCKS_SERVER -j RETURN
# Allow connection to reserved networks
iptables -t nat -A SHADOWSOCKS -d 0.0.0.0/8 -j RETURN
iptables -t nat -A SHADOWSOCKS -d 10.0.0.0/8 -j RETURN
iptables -t nat -A SHADOWSOCKS -d 127.0.0.0/8 -j RETURN
iptables -t nat -A SHADOWSOCKS -d 169.254.0.0/16 -j RETURN
iptables -t nat -A SHADOWSOCKS -d 172.16.0.0/12 -j RETURN
iptables -t nat -A SHADOWSOCKS -d 192.168.0.0/16 -j RETURN
iptables -t nat -A SHADOWSOCKS -d 224.0.0.0/4 -j RETURN
iptables -t nat -A SHADOWSOCKS -d 240.0.0.0/4 -j RETURN
# Allow connection to chinese IPs
iptables -t nat -A SHADOWSOCKS -p tcp -m set --match-set chnroute dst -j RETURN
# 如果你想对 icmp 协议也实现智能分流,可以加上下面这一条
# iptables -t nat -A SHADOWSOCKS -p icmp -m set --match-set chnroute dst -j RETURN
# Redirect to Shadowsocks
# 把1081改成你的shadowsocks本地端口
iptables -t nat -A SHADOWSOCKS -p tcp -j REDIRECT --to-port 1081
# 如果你想对 icmp 协议也实现智能分流,可以加上下面这一条
# iptables -t nat -A SHADOWSOCKS -p icmp -j REDIRECT --to-port 1081
# 将SHADOWSOCKS链中所有的规则追加到OUTPUT链中
iptables -t nat -A OUTPUT -p tcp -j SHADOWSOCKS
# 如果你想对 icmp 协议也实现智能分流,可以加上下面这一条
# iptables -t nat -A OUTPUT -p icmp -j SHADOWSOCKS
# 内网流量流经 shadowsocks 规则链
iptables -t nat -A PREROUTING -s 192.168/16 -j SHADOWSOCKS
# 内网流量源NAT
iptables -t nat -A POSTROUTING -s 192.168/16 -j MASQUERADE这是在启动 shadowsocks 之前执行的脚本,用来设置 iptables 规则,对全局应用代理并将 chnroute 导入 ipset 来实现自动分流。注意要把服务器 IP 和本地端口相关的代码全部替换成你自己的。
这里就有一个坑了,就是在把 chnroute.txt 加入 ipset 的时候。因为 chnroute.txt 是一个 IP 段列表,而中国持有的 IP 数量上还是比较大的,所以如果使用 hash:ip 来导入的话会使内存溢出。我在第二次重新配置的时候就撞进了这个大坑……
但是你也不能尝试把整个列表导入 iptables。虽然导入 iptables 不会导致内存溢出,但是 iptables 是线性查表,即使你全部导入进去,也会因为低下的性能而抓狂。
然后再创建 /home/yang/bin/shadowsocks/ss-down.sh, 这是用来清除上述规则的脚本,比较简单
#!/bin/bash
# iptables -t nat -D OUTPUT -p icmp -j SHADOWSOCKS
iptables -t nat -D OUTPUT -p tcp -j SHADOWSOCKS
iptables -t nat -F SHADOWSOCKS
iptables -t nat -X SHADOWSOCKS
ipset destroy chnroute接着执行
$ chmod +x ss-up.sh
$ chmod +x ss-down.sh配置ss-redir服务
首先,默认的 ss-local 并不能用来作为 iptables 流量转发的目标,因为它是 socks5 代理而非透明代理。我们至少要把 systemd 执行的程序改成 ss-redir。其次,上述两个脚本还不能自动执行,必须让 systemd 分别在启动 shadowsocks 之前和关闭之后将脚本执行,这样才能自动配置好 iptables 规则。
$ vim /usr/lib/systemd/system/[email protected][Unit]
Description=Shadowsocks-Libev Client Service
After=network.target
[Service]
User=root
CapabilityBoundingSet=~CAP_SYS_ADMIN
ExecStart=
ExecStartPre=/home/yang/bin/shadowsocks/ss-up.sh
ExecStart=/usr/bin/ss-redir -u -c /etc/%i.json
ExecStopPost=/home/yang/bin/shadowsocks/ss-down.sh
[Install]
WantedBy=multi-user.target然后启动服务
$ systemctl start shadowsocks-libev@shadowsocks开机自启
$ systemctl enable shadowsocks-libev@shadowsocks配置智能 DNS 服务
完成了以上工作之后是不是就可以实现全局科学上网了呢?答案是否定的,我们还有最后一项工作需要完成,那就是解决 DNS 污染问题。如果你不知道什么是 DNS 污染,我可以简单地给你普及一下:
DNS污染是一种让一般用户由于得到虚假目标主机IP而不能与其通信的方法,是一种DNS缓存投毒攻击(DNS cache poisoning)。其工作方式是:由于通常的DNS查询没有任何认证机制,而且DNS查询通常基于的UDP是无连接不可靠的协议,因此DNS的查询非常容易被篡改,通过对UDP端口 53 上的DNS查询进行入侵检测,一经发现与关键词相匹配的请求则立即伪装成目标域名的解析服务器(NS,Name Server)给查询者返回虚假结果。
DNS 污染症状:目前一些被禁止访问的网站很多就是通过 DNS 污染来实现的,例如 YouTube、Facebook 等网站。
应对dns污染的方法
- 对于
DNS污染,可以说,个人用户很难单单靠设置解决,通常可以使用VPN或者域名远程解析的方法解决,但这大多需要购买付费的VPN或SSH等 - 修改
Hosts的方法,手动设置域名正确的IP地址 dns加密解析:DNSCrypt- 忽略
DNS投毒污染小工具:Pcap_DNSProxy
我们选择用 Pcap_DNSProxy 来解决这个问题,以前用的是 Pdnsd + Dnsmasq 组合, 后来发现 TCP 请求效率太低加上家里网络与那些国外的 DNS 丢包实在是严重, 所以打算用 Pcap_DNSProxy 代替 Pdnsd。
关于 Pcap_DNSProxy 的详细介绍,可以参考:
https://github.com/chengr28/Pcap_DNSProxy
安装过程可以参考: https://github.com/chengr28/Pcap_DNSProxy/blob/master/Documents/ReadMe_Linux.zh-Hans.txt
更详细的使用说明可以参考: https://github.com/chengr28/Pcap_DNSProxy/blob/master/Documents/ReadMe.zh-Hans.txt
这里主要重点强调一些需要注意的配置项:
DNS- 境外域名解析参数区域(这是最关键的一项配置)
[DNS]
# 这里一定要填 IPv4 + TCP!!!表示只使用 TCP 协议向境外远程 DNS 服务器发出请求
Outgoing Protocol = IPv4 + TCP
# 建议当系统使用全局代理功能时启用,程序将除境内服务器外的所有请求直接交给系统而不作任何过滤等处理,系统会将请求自动发往远程服务器进行解析
Direct Request = IPv4
...
...Local DNS- 境内域名解析参数区域
[Local DNS]
# 发送请求到境内 DNS 服务器时所使用的协议
Local Protocol = IPv4 + UDP
...
...Addresses- 普通模式地址区域
[Addresses]
...
...
# IPv4 主要境外 DNS 服务器地址
IPv4 Main DNS Address = 8.8.4.4:53
# IPv4 备用境外 DNS 服务器地址
IPv4 Alternate DNS Address = 8.8.8.8:53|208.67.220.220:443|208.67.222.222:5353
# IPv4 主要境内 DNS 服务器地址,用于境内域名解析,推荐使用 onedns
IPv4 Local Main DNS Address = 112.124.47.27:53
# IPv4 备用境内 DNS 服务器地址,用于境内域名解析
IPv4 Local Alternate DNS Address = 114.215.126.16:53
...
...配置系统 DNS 服务器设置
- 可参见 https://developers.google.com/speed/public-dns/docs/using 中
Changing your DNS servers settings中Linux一节 - 图形界面以
GNOME 3为例: - 打开所有程序列表,并 -> 设置 – 硬件分类 – 网络
- 如果要对当前的网络配置进行编辑 -> 单击齿轮按钮
- 选中
IPv4 DNS栏目中,将自动拨向关闭- 在服务器中填入
127.0.0.1(或103.214.195.99:7300)并应用 - 选中
IPv6 DNS栏目中,将自动拨向关闭- 在服务器中填入 ::1 并应用
- 请务必确保只填入这两个地址,填入其它地址可能会导致系统选择其它 DNS 服务器绕过程序的代理
- 重启网络连接
- 直接修改系统文件修改 DNS 服务器设置:
- 自动获取地址(DHCP)时:
- 以
root权限进入/etc/dhcp或/etc/dhcp3目录(视乎 dhclient.conf 文件位置) - 直接修改
dhclient.conf文件,修改或添加prepend domain-name-servers一项即可 - 如果
prepend domain-name-servers一项被 # 注释则需要把注释去掉以使配置生效,不需要添加新的条目 dhclient.conf文件可能存在多个prepend domain-name-servers项,是各个网络接口的配置项目,直接修改总的配置项目即可- 使用
service network(/networking) restart或ifdown/ifup或ifconfig stop/start重启网络服务/网络端口 - 非自动获取地址(DHCP)时:
- 以
root权限进入/etc目录 - 直接修改
resolv.conf文件里的nameserver即可 - 如果重启后配置被覆盖,则需要修改或新建
/etc/resolvconf/resolv.conf.d文件,内容和resolv.conf一样 - 使用
service network(/networking) restart或ifdown/ifup或ifconfig stop/start重启网络服务/网络端口
打开流量转发
$ cat /etc/sysctl.d/30-ipforward.confnet.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding = 1
net.ipv4.tcp_congestion_control=westwood
net.ipv4.tcp_syn_retries = 5
net.ipv4.tcp_synack_retries = 5编辑完成后,执行以下命令使变动立即生效
$ sysctl -p2. 通过 nftables 实现智能分流
安装相关软件
- shadowsocks-libev
- nftables
$ pacman -S shadowsocks-libev nftables配置shadowsocks-libev(略过)
假设shadowsocks配置文件为/etc/shadowsocks.json
获取中国IP段
将以下命令写入脚本保存执行(假设保存在/home/yang/bin/路由表/目录下):
#!/bin/sh
wget -c http://ftp.apnic.net/stats/apnic/delegated-apnic-latest
cat delegated-apnic-latest | awk -F '|' '/CN/&&/ipv4/ {print $4 "/" 32-log($5)/log(2)}' | cat > /home/yang/bin/路由表/cn_rules.conf
cat cn_rules.conf|sed ':label;N;s/\n/, /;b label'|sed 's/$/& }/g'|sed 's/^/{ &/g' > /home/yang/bin/路由表/cn_rules1.conf创建启动和关闭脚本
$ vim /home/yang/bin/shadowsocks/nftables-up.sh#! /bin/bash
nft_pre="/usr/sbin/nft add rule nat prerouting"
nft_out="/usr/sbin/nft add rule nat output"
chnroute=$(cat '/home/yang/bin/路由表/cn_rules1.conf')
/usr/bin/nft -f /etc/nftables.conf
${nft_pre} tcp dport 8385 return
${nft_pre} ip daddr 139.162.87.98 return
${nft_pre} ip daddr { 0.0.0.0/8, 10.0.0.0/8, 127.0.0.0/8, 169.254.0.0/16, 172.16.0.0/12, 192.168.0.0/16, 224.0.0.0/4, 240.0.0.0/4, 172.16.39.0/24} return
${nft_pre} ip daddr $chnroute return
${nft_pre} tcp sport { 32768-61000} redirect to 1081
#${nft_pre} ip protocol icmp redirect to 1081
# 内网流量源NAT
nft add rule nat postrouting ip saddr 192.168.0.0/12 masquerade
${nft_out} tcp dport 8385 return
${nft_out} ip daddr 139.162.87.98 return
${nft_out} ip daddr { 0.0.0.0/8, 10.0.0.0/8, 127.0.0.0/8, 169.254.0.0/16, 172.16.0.0/12, 192.168.0.0/16, 224.0.0.0/4, 240.0.0.0/4, 172.16.39.0/24} return
${nft_out} ip daddr $chnroute return
# /proc/sys/net/ipv4/ip_local_port_range,本地发起的连接的端口范围
${nft_out} tcp sport { 32768-61000} redirect to 1081
${nft_out} ip protocol icmp redirect to 1081 这是在启动 shadowsocks 之前执行的脚本,用来设置 nftables 规则。
然后再创建 /home/yang/bin/shadowsocks/nftables-down.sh, 这是用来清除上述规则的脚本,比较简单
#!/bin/bash
sudo nft flush table nat
#sudo nft flush table filter接着执行
$ chmod +x nftables-up.sh
$ chmod +x nftables-down.sh配置ss-redir服务
首先,默认的 ss-local 并不能用来作为 nftables 流量转发的目标,因为它是 socks5 代理而非透明代理。我们至少要把 systemd 执行的程序改成 ss-redir。其次,上述两个脚本还不能自动执行,必须让 systemd 分别在启动 shadowsocks 之前和关闭之后将脚本执行,这样才能自动配置好 nftables 规则。
$ vim /usr/lib/systemd/system/[email protected][Unit]
Description=Shadowsocks-Libev Client Service
After=network.target
[Service]
User=root
CapabilityBoundingSet=~CAP_SYS_ADMIN
ExecStart=
ExecStartPre=/home/yang/bin/shadowsocks/nftables-up.sh
ExecStart=/usr/bin/ss-redir -u -c /etc/%i.json
ExecStopPost=/home/yang/bin/shadowsocks/nftables-down.sh
[Install]
WantedBy=multi-user.target然后启动服务
$ systemctl start nftables
$ systemctl start shadowsocks-libev@shadowsocks开机自启
$ systemctl enable nftables
$ systemctl enable shadowsocks-libev@shadowsocks配置智能 DNS 服务
同上
配置系统 DNS 服务器设置
同上
打开流量转发
同上
3. 通过策略路由实现智能分流
安装相关软件
- badvpn
- shadowsocks
$ pacman -S badvpn shadowsocks配置shadowsocks(略过)
假设shadowsocks配置文件为/etc/shadowsocks.json
获取中国IP段
将以下命令写入脚本保存执行(假设保存在/home/yang/bin/路由表/目录下):
#!/bin/sh
wget -c http://ftp.apnic.net/stats/apnic/delegated-apnic-latest
cat delegated-apnic-latest | awk -F '|' '/CN/&&/ipv4/ {print $4 "/" 32-log($5)/log(2)}' | cat > /home/yang/bin/路由表/cn_rules.conf配置智能 DNS 服务
同上
配置系统 DNS 服务器设置
同上
编写路由表启动和终止脚本
$ vim /usr/local/bin/socksfwd#!/bin/bash
SOCKS_SERVER=$SERVER_IP # SOCKS 服务器的 IP 地址
SOCKS_PORT=1081 # 本地SOCKS 服务器的端口
GATEWAY_IP=$(ip route|grep "default"|awk '{print $3}') # 家用网关(路由器)的 IP 地址,你也可以手动指定
TUN_NETWORK_DEV=tun0 # 选一个不冲突的 tun 设备号
TUN_NETWORK_PREFIX=10.0.0 # 选一个不冲突的内网 IP 段的前缀
start_fwd() {
ip tuntap del dev "$TUN_NETWORK_DEV" mode tun
# 添加虚拟网卡
ip tuntap add dev "$TUN_NETWORK_DEV" mode tun
# 给虚拟网卡绑定IP地址
ip addr add "$TUN_NETWORK_PREFIX.1/24" dev "$TUN_NETWORK_DEV"
# 启动虚拟网卡
ip link set "$TUN_NETWORK_DEV" up
ip route del default via "$GATEWAY_IP"
ip route add "$SOCKS_SERVER" via "$GATEWAY_IP"
# 特殊ip段走家用网关(路由器)的 IP 地址(如局域网联机)
# ip route add "172.16.39.0/24" via "$GATEWAY_IP"
# 国内网段走家用网关(路由器)的 IP 地址
for i in $(cat /home/yang/bin/路由表/cn_rules.conf)
do
ip route add "$i" via "$GATEWAY_IP"
done
# 将默认网关设为虚拟网卡的IP地址
ip route add 0.0.0.0/1 via "$TUN_NETWORK_PREFIX.1"
ip route add 128.0.0.0/1 via "$TUN_NETWORK_PREFIX.1"
# 将socks5转为vpn
badvpn-tun2socks --tundev "$TUN_NETWORK_DEV" --netif-ipaddr "$TUN_NETWORK_PREFIX.2" --netif-netmask 255.255.255.0 --socks-server-addr "127.0.0.1:$SOCKS_PORT"
TUN2SOCKS_PID="$!"
}
stop_fwd() {
ip route del 128.0.0.0/1 via "$TUN_NETWORK_PREFIX.1"
ip route del 0.0.0.0/1 via "$TUN_NETWORK_PREFIX.1"
for i in $(cat /home/yang/bin/路由表/cn_rules.conf)
do
ip route del "$i" via "$GATEWAY_IP"
done
ip route del "172.16.39.0/24" via "$GATEWAY_IP"
ip route del "$SOCKS_SERVER" via "$GATEWAY_IP"
ip route add default via "$GATEWAY_IP"
ip link set "$TUN_NETWORK_DEV" down
ip addr del "$TUN_NETWORK_PREFIX.1/24" dev "$TUN_NETWORK_DEV"
ip tuntap del dev "$TUN_NETWORK_DEV" mode tun
}
start_fwd
trap stop_fwd INT TERM
wait "$TUN2SOCKS_PID"$ vim /etc/systemd/system/socksfwd.service[Unit]
Description=Transparent SOCKS5 forwarding
After=network-online.target
[Service]
Type=simple
ExecStart=/usr/local/bin/socksfwd
LimitNOFILE=1048576
[Install]
WantedBy=multi-user.target启动服务
$ systemctl start socksfwd开机自启
$ systemctl enable socksfwd打开流量转发
$ cat /etc/sysctl.d/30-ipforward.confnet.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding = 1
net.ipv4.tcp_congestion_control=westwood
net.ipv4.tcp_syn_retries = 5
net.ipv4.tcp_synack_retries = 5编辑完成后,执行以下命令使变动立即生效
$ sysctl -p