OpenVPN 实现国内外流量分流+DNSmasq 实现国内外域名分流解析
一、引言
(一)搭建 OpenVPN
我们将搭建 OpenVPN 服务端,并在客户端本地添加路由表,实现国内外 IP 的访问分流。
(二)配合 DNSmasq 实现域名分流:
使用 DNSmasq 配置国内外域名的解析,将访问流量引导到最优的解析节点,从而提升整体访问效率。
二、环境
(一)系统
- Windows 10 家庭中文版
- Ubuntu 18.04
(二)软件
- OpenVPN GUI – v11.13.0.0
- OpenVPN – v2.4.4
- Dnsmasq – v2.79
三、OpenVPN 服务端的安装和配置
(一)安装 OpenVPN
首先,使用
apt
包管理器安装 OpenVPN。执行以下命令:
apt install openvpn
安装完成后,运行以下命令检查 OpenVPN 是否安装成功:
openvpn --version
如果安装成功,会看到OpenVPN 的版本信息输出:

(二)配置 OpenVPN
OpenVPN 配置文件通常位于
/etc/openvpn
目录下,编辑
/etc/openvpn/server.conf
文件,并根据需求进行调整。以下是一个基本的配置示例:
local 0.0.0.0 #服务端绑定地址
port 8080 #服务端绑定端口
proto udp
dev tun
ca ca.crt
dh dh.pem
server 192.168.90.0 255.255.255.0 #客户端分配地址
ifconfig-pool-persist ipp.txt
push "redirect-gateway"
push "dhcp-option DNS 192.168.90.1"
keepalive 10 120
cipher AES-256-GCM
status openvpn-status.log
log /var/log/openvpn.log
verb 3
explicit-exit-notify 1
script-security 3
auth-user-pass-verify /etc/openvpn/checkpsw.sh via-env #客户端用户名/密码登陆校验脚本
username-as-common-name
verify-client-cert none
在 OpenVPN 配置文件中,我们使用
push "dhcp-option DNS 192.168.90.1"
是为了避免国内部分域名遭遇 DNS 污染。如果你希望使用自己的 DNS 服务器(例如局域网中的 DNS 服务器),可以将其替换为:
push "dhcp-option DNS 192.168.90.1"
这样,客户端在连接到 OpenVPN 时将会使用
192.168.90.1
作为 DNS 服务器。
配置修改完成后,执行以下命令重启 OpenVPN 服务,使更改生效:
systemctl restart openvpn@server
(三)配置 NAT
为了让 OpenVPN 客户端能够通过 VPN 访问外网,我们需要配置 NAT(网络地址转换)。假设我们给客户端分配的地址段是 192.168.90.0/24,并且 OpenVPN 服务器的物理网卡是 ens3(可以连接外网),可以使用以下 iptables 命令进行配置:
# 设置 NAT 规则,使得客户端的流量能够通过服务器访问外网
iptables -t nat -I POSTROUTING -s 192.168.90.0/24 -o ens3 -j MASQUERADE
# 允许从客户端到 VPN 服务器的流量转发
iptables -I FORWARD -s 192.168.90.0/24 -j ACCEPT
# 允许从 VPN 服务器转发流量到客户端
iptables -I FORWARD -d 192.168.90.0/24 -j ACCEPT
(四)启动 OpenVPN 服务
1. 设置 OpenVPN 开机自启
systemctl enable openvpn@server
2. 启动 OpenVPN 服务
systemctl start openvpn@server
3. 检查 OpenVPN 是否启动成功
netstat -anp | grep 8080
如果 OpenVPN 正在监听该端口,命令输出中将显示相关信息,表明服务已成功启动。

四、DNSmasq 服务器安装
(一)安装 DNSmasq
- 安装 DNSmasq
使用
apt
包管理器安装 DNSmasq:
apt install dnsmasq
2. 检查 DNSmasq 安装是否成功
dnsmasq --version
如果安装成功,你将看到类似如下的版本信息:

(二)配置 DNSmasq
1. 编辑/etc/dnsmasq.conf文件
no-hosts
no-resolv
cache-size=1000
listen-address=192.168.90.1 #此处设置为OpenVPN虚拟网卡IP
port=53
all-servers
server=8.8.8.8
server=8.8.4.4
2. 编辑/etc/resolv.conf文件
nameserver 192.168.90.1
设置域名解析服务地址为 DNSmasq 监听地址”192.168.90.1″。
由于
systemd-resolved
服务会自动修改
/etc/resolv.conf
文件,我们希望手动管理 DNS 配置,可以通过以下步骤停止并禁用该服务:
- 执行
systemctl stop systemd-resolved
- 停止服务。
- 执行
systemctl disable systemd-resolved
- 禁用开机自启。
(三)下载国内域名列表
为了实现国内外域名的分流解析,我们需要下载并配置相应的域名列表文件。可以通过
wget
命令下载以下两个配置文件:
- 下载加速域名列表(
accelerated-domains.china.conf
- ):
wget /etc/dnsmasq.d/accelerated-domains.china.conf
2. 下载 Google 域名列表(
google.china.conf
):
wget /etc/dnsmasq.d/google.china.conf
当 DNS 查询的域名在这两个列表中时,DNSmasq 会将请求转发到指定的 DNS 服务器进行解析。国内域名将被转发给国内 DNS 服务器解析,而国外域名则交给 Google DNS(8.8.8.8)处理。
(四)修改 DNS 配置
由于部分公共 DNS 服务器(如
114.114.114.114
)可能会遭遇 CDN 加速,导致国内域名解析到国外 IP,这会影响国内域名的正常访问。因此,我们需要将这两个配置文件中的
114.114.114.114
替换为本地的 DNS 服务器地址,以确保国内域名正确解析到国内 IP。
- 使用以下命令将
114.114.114.114
- 替换为北京本地的 DNS 服务器(如:
202.106.0.20
- ):
vim /etc/dnsmasq.d/accelerated-domains.china.conf
# 在 Vim 中执行以下替换命令
:%s/114.114.114.114/202.106.0.20/g
# 保存并退出
:wq
2. 同样的操作需要在
google.china.conf
文件中执行:
vim /etc/dnsmasq.d/dnsmasq.d/google.china.conf
# 在 Vim 中执行以下替换命令
:%s/114.114.114.114/202.106.0.20/g
# 保存并退出
:wq
通过以上步骤,国内域名将会使用你指定的本地 DNS 服务器(如
202.106.0.20
)进行解析,确保访问国内资源时不会受到 CDN 的影响。
(五)启动 DNSmasq
- 设置开机启动
systemctl enable dnsmasq
2. 启动 DNSmasq 服务
service dnsmasq start
3. 检查 DNSmasq 是否启动成功
netstat -anp | grep 53
如果 DNSmasq 启动成功,能看到类似以下的输出,表明它正在监听 53 端口:

(六)测试 DNSmasq
执行
dig baidu.com
和
dig github.com

五、OpenVPN 客户端配置路由表
(一)获取国内 IP 地址
首先,需要获取国内 IP 地址的列表。可以从以下网站下载国内 IP 地址段的文件:
该文件包含了中国所有 IP 地址段。
(二)执行
build.py
生成路由批处理脚本
接下来,我们需要使用
build.py
脚本来解析国内 IP 地址并生成相应的路由配置文件(
add_route.bat
和
del_route.bat
)。执行以下命令:
python .\build.py .\all_cn_cidr.txt add_route.bat del_route.bat 192.168.2.254
其中:
all_cn_cidr.txt
- 是从上面下载的国内 IP 地址列表文件。
add_route.bat
- 是生成的用于添加路由的批处理脚本。
del_route.bat
- 是生成的用于删除路由的批处理脚本。
192.168.2.254
- 是你的默认网关地址。也就是说,这是客户端未连接 VPN 时,有线或无线网卡的默认网关地址。你需要根据实际情况填写本机的默认网关地址。
通过执行上述命令,
build.py
将根据国内 IP 地址段生成相应的路由脚本。
(三)build.py 脚本代码
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import sys
import re
import os
import math
import copy
def usage():
print('./build [route_file] [add_file] [del_file] [gateway]')
print('./build route.text add_route.bat del_route.bat 192.186.90.5')
#合并子网
def marge_subnet(ip_tables):
count = 2
start_i, end_i, prev_i = 0, -1, 0
new_ip_tables = []
serials = [ip_tables[0]]
for i in range(1, len(ip_tables)):
if ip_tables[i-1].is_serial(ip_tables[i]):
serials.append(ip_tables[i])
continue
flag = False
while len(serials) > 0:
count = 2
while len(serials) >= count and serials[0].get_last_net_no() % count == 0:
serials[0] = serials[0] << 1
count = count * 2
count = 1 if count == 2 else int(count / 2)
new_ip_tables.append(serials[0])
serials = serials[count:]
if len(serials) > count:
new_ip_tables = new_ip_tables + serials[count:]
serials = [ip_tables[i]]
new_ip_tables = new_ip_tables + serials
return new_ip_tables
#IP和MASK
class ip_mask:
def __init__(self, ip):
self.start = {}
self.ip = copy.deepcopy(ip)
self.mask = []
self.last_mask_index = 0
def __lt__(self, other):
return self.ip < other.ip
def __eq__(self, other):
return self.ip == other.ip
def __lshift__(self, value):
while value >= 8 and self.last_mask_index >= 0:
self.mask[self.last_mask_index] = (self.mask[self.last_mask_index] << 8 & 255)
value = value - 8
self.last_mask_index = self.last_mask_index - 1
if self.last_mask_index >= 0 and value > 0:
self.mask[self.last_mask_index] = (self.mask[self.last_mask_index] << value & 255)
return self
def set_mask(self, mask):
self.mask = copy.deepcopy(mask)
for i in range(len(self.mask) - 1, -1, -1):
if mask[i] != 0:
self.last_mask_index = i
break
def is_serial(self, other):
flag = False
for i in range(len(self.ip)):
if self.ip[i] + 1 == other.ip[i] or self.ip[i] - 1 == other.ip[i]:
flag = True
elif self.ip[i] != other.ip[i]:
return False
elif self.ip[i] != 0:
if flag:
return False
return flag
def get_last_net_no(self):
return self.ip[self.last_mask_index]
#解析IP地址
def parse_ip_table(content):
lines = content.split('\n')
ip_tables = []
for line in lines:
line = line.split('/')
if len(line) != 2:
continue
ip = list(map(int, line[0].split('.')))
im = ip_mask(ip)
im.set_mask([255,255,255,255])
im = im << (32 - int(line[1]))
ip_tables.append(im)
return ip_tables
#添加路由
def add_route(file, ip_tables, gateway):
f = open(file, 'w')
for ip_mask in ip_tables:
f.write('route add %d.%d.%d.%d mask %d.%d.%d.%d %s\n' %
(ip_mask.ip[0], ip_mask.ip[1], ip_mask.ip[2], ip_mask.ip[3], ip_mask.mask[0], ip_mask.mask[1], ip_mask.mask[2], ip_mask.mask[3], gateway))
f.write('ipconfig /flushdns\n')
f.write('pause\n')
#删除路由
def del_route(file, ip_tables):
f = open(file, 'w')
for ip_mask in ip_tables:
f.write('route delete %d.%d.%d.%d\n' % (ip_mask.ip[0], ip_mask.ip[1], ip_mask.ip[2], ip_mask.ip[3]))
f.write('ipconfig /flushdns\n')
f.write('pause\n')
def main():
if len(sys.argv) != 5:
usage()
return
#####
route_file = sys.argv[1]
add_file = sys.argv[2]
del_file = sys.argv[3]
gateway = sys.argv[4]
#####
route_content = open(route_file, 'r+').read()
ip_tables = parse_ip_table(route_content);
ip_tables.sort()
sub_net = len(ip_tables)
ip_tables = marge_subnet(ip_tables)
#####
add_route(add_file, ip_tables, gateway)
del_route(del_file, ip_tables)
#####
main()
(四)添加路由
- 右键以管理员身份运行
add_route.bat
由于需要修改系统路由表,必须以管理员权限运行生成的
add_route.bat
脚本。右键点击
add_route.bat
文件,选择“以管理员身份运行”。
2. 等待脚本执行
运行后,
add_route.bat
脚本将添加大约 5000 条路由。这个过程可能需要几分钟时间,等待脚本完成执行。当脚本执行完毕时,命令行会显示“请按任意键继续…”,此时按下回车键退出。
3. 检查路由是否添加成功
执行完脚本后,可以使用以下命令检查路由表是否已经成功添加:
route print
如果路由添加成功,能看到类似以下的输出:

六、OpenVPN 客户端测试
(一)启动 OpenVPN 客户端
启动OpenVPN客户端,并连接成功。
(二)测试百度的延迟
ping www.baidu.com

(三)测试 GitHub 的延迟
ping www.github.com

