VPN(Virtual Private Network)是通过一个公用网络建立一个临时的、安全的连接,是一条穿过混乱的公用网络的安全、稳定的隧道。使用这条隧道可以对数据进行几倍加密达到安全使用互联网的目的。

一、给VPN添加路由表

然而,在VPN流量的有限的情况下,一来我们希望部分特定的IP走VPN线路,访问其他IP时候依然走公网网关;二来,对于海外VPN,访问国内IP走公网线路也有利于访问速度的保证;再者,也可以消除一些中国内部的资源也限制海外的ip访问情况;要实现这一目的,我们需要给VPN添加路由表。以OpenVPN为例。

1.Windows

Windows用户最好使用最新OpenVPN 2.1以上的版本,因为openvpn v2.1比之前版本增加了一个名为max-routes的新参数, 通过设置该参数, 我们可以在配置文件里(服务端, 客户端)直接添加超过100条以上的路由信息.
chnroutes上给出的脚本

#!/usr/bin/env python

import re
import urllib

url=r'http://ftp.apnic.net/apnic/dbase/data/country-ipv4.lst'

handler=urllib.urlopen(url)

routes_file=open('routes.txt','w')

line_count=0

for line in handler.readlines():
    if line.find(': cn ') < 0: continue
    line_count+=1
    r=line.split(':')[1]
    r=r.strip()
    ip,mask=r.split('/')
    ip=ip.split('.')
    while len(ip) < 4:
        ip.append('0')

    mask=int(mask)

    bm='1'*mask+'0'*(32-mask)
    mask="%d.%d.%d.%d"%(int(bm[0:8],2),int(bm[8:16],2),int(bm[16:24],2),int(bm[24:32],2))

    routes_file.write('route %s %s %s 5\n'%('.'.join(ip),mask,'net_gateway'))

routes_file.close()

print "Usage: Append the content of the newly created routes.txt to your openvpn config file," \
      " and also add 'max-routes %d', which takes a line, to the head of the file." % (line_count+50)

教育网同学可以考虑使用这个添加教育网IP的路由表版本(修改自 @Felixonmars 的脚本)

import re
import urllib
a=urllib.urlopen('http://www.nic.edu.cn/RS/ipstat/internalip/real.html').read()
b=re.compile("([\d\.]+)\s+[\d\.]+\s+([\d\.]+)")
c=b.findall(a)
line_count=0
m=["#iptable for CERNET"]
for d in c:
    m.append("route "+d[0]+" "+d[1]+" net_gateway 5")
    line_count=line_count+1
e=open('routes_cernet.txt',"w")
e.write("\n".join(m))
e.close()

具体设置步骤如下:

  1. 在命令行里执行 python chnroutes_openvpn_v2.1/cernet_ovpn, 这将生成一个名为 routes.txt/routes_cernet.txt 的文本文件.
  2. 使用你喜欢的文本编辑器打开上述文件, 并把内容复制粘贴到openvpn配置文件的末尾
  3. 同时在openvpn配置文件的头部添加一句 max-routes num, 其中num是一个不小于文件routes.txt的行数的数字, 实际上因为还有一些服务器端push过来的路由信息, 所以保险起见可以用 routes.txt的行数加上50, 比如目前得到的routes.txt的行数是940, 你可以把数字设置为1000: max-routes 1000

2.Linux

Linux平台不建议使用opvn中添加路由表(本人Ubuntu 10.10系统采用此方法时在退出OpenVPN时删除路由表时出现权限问题),而是采用一个脚本文件。Windows平台也不建议使用脚本,因为Windows平台创建进程的开销非常大,具体表现出来为route程序执行过慢,导致整个脚本执行的时间太长;Linux平台基本不算问题,因为Linux创建进程是非常快的,所以脚本执行时间非常短。基本在5秒左右就完成,5秒后网络立马就稳定下来了。
chnroutes上给出的脚本

#!/usr/bin/env python

import re
import urllib

VPNUPBASE="""#!/bin/bash
export PATH="/bin:/sbin:/usr/sbin:/usr/bin"

OLDGW=`ip route show | grep '^default' | sed -e 's/default via \\([^ ]*\\).*/\\1/'`

if [ $OLDGW == '' ]; then
    exit 0
fi

if [ ! -e /tmp/openvpn_oldgw ]; then
    echo $OLDGW > /tmp/openvpn_oldgw
fi

"""

VPNDOWNBASE="""#!/bin/bash
export PATH="/bin:/sbin:/usr/sbin:/usr/bin"

OLDGW=`cat /tmp/openvpn_oldgw`

"""

url=r'http://ftp.apnic.net/apnic/dbase/data/country-ipv4.lst'

handler=urllib.urlopen(url)

upfile=open('vpnup','w')
downfile=open('vpndown','w')

upfile.write(VPNUPBASE)
upfile.write('\n')

downfile.write(VPNDOWNBASE)
downfile.write('\n')

for line in handler.readlines():
    if line.find(': cn ') < 0: continue
    r=line.split(':')[1]
    r=r.strip()
    ip,mask=r.split('/')
    ip=ip.split('.')
    while len(ip) < 4:
        ip.append('0')

    mask=int(mask)

    bm='1'*mask+'0'*(32-mask)
    mask="%d.%d.%d.%d"%(int(bm[0:8],2),int(bm[8:16],2),int(bm[16:24],2),int(bm[24:32],2))

    upfile.write('route add -net %s netmask %s gw $OLDGW\n'%('.'.join(ip),mask))
    downfile.write('route del -net %s netmask %s\n'%('.'.join(ip),mask))

downfile.write('rm /tmp/openvpn_oldgw\n')

upfile.close()
downfile.close()

教育网同学可以考虑使用这个添加教育网IP的路由表版本(修改自 @Felixonmars 的脚本)

#!/usr/bin/env python

import re
import urllib

VPNUPBASE="""#!/bin/bash
export PATH="/bin:/sbin:/usr/sbin:/usr/bin"

OLDGW=`ip route show | grep '^default' | sed -e 's/default via \\([^ ]*\\).*/\\1/'`

if [ $OLDGW == '' ]; then
    exit 0
fi

if [ ! -e /tmp/openvpn_oldgw ]; then
    echo $OLDGW > /tmp/openvpn_oldgw
fi

"""

VPNDOWNBASE="""#!/bin/bash
export PATH="/bin:/sbin:/usr/sbin:/usr/bin"

OLDGW=`cat /tmp/openvpn_oldgw`

"""

a=urllib.urlopen('http://www.nic.edu.cn/RS/ipstat/internalip/real.html').read()
b=re.compile("([\d\.]+)\s+[\d\.]+\s+([\d\.]+)")
c=b.findall(a)

upfile=open('vpnup_cernet','w')
downfile=open('vpndown_cernet','w')

upfile.write(VPNUPBASE)
upfile.write('\n')

downfile.write(VPNDOWNBASE)
downfile.write('\n')

for d in c:
    upfile.write("route add -net "+d[0]+" netmask "+d[1]+" gw $OLDGW\n")
    downfile.write("sudo route delete -net "+d[0]+" netmask "+d[1]+"\n")

downfile.write('rm /tmp/openvpn_oldgw\n')

upfile.close()
downfile.close()

具体设置步骤如下:

  1. 下载 chnroutes_ovpn_linux/chnroutes_ovpn_linux_cernet
  2. 从终端进入下载目录, 执行python chnroutes_ovpn_linux/chnroutes_ovpn_linux_cernet, 执行完毕之后同一目录下将生成两个新文件’vpnup/vpnup_cernet’和’vpndown/vpndown_cernet’
  3. 在终端里运行命令 chmod a+x vpnup vpndown 把这两个文件设置为可执行
  4. 把这两个文件copy到openvpn的配置目录并修过openvpn配置文件, 在末尾加上两句
<code>script-security 2
up vpnup
down vpndown</code>

二、自动替换DNS服务器

一般在OpenVPN的server端都会写上类似于

push &quot;dhcp-option DNS 10.8.0.1&quot;
push &quot;dhcp-option DNS 208.67.222.222&quot;
push &quot;dhcp-option DNS 208.67.220.220&quot;

这样的语句将DNS设置push给客户端,但实际上,这个只对Win32平台下的OpenVPN有效,对于Linux平台下,无路是server端的push或者是client端配置文件中手动加上–dhcp-option都没有用。DNS服务器依然是连接OpenVPN前的DNS服务器,遭到DNS污染的网站依旧会遭到DNS污染,只能考虑执行自定义脚本来完成:

RESOLVE=/etc/resolv.conf
FOREIGNDNS1='8.8.8.8'
FOREIGNDNS2='8.8.4.4'
DNSMARK='_MK'

sed "s/^nameserver/#$DNSMARK nameserver/" -i $RESOLVE
echo "nameserver $FOREIGNDNS1" >> $RESOLVE
echo "nameserver $FOREIGNDNS2" >> $RESOLVE
RESOLVE=/etc/resolv.conf
DNSMARK='_MK'
sed -e '/^nameserver/d' -e "s/^#$DNSMARK //" -i $RESOLVE

很简单,就是使用sed来完成/etc/resolv.conf文件的内容替换。

嗯,就这样。

参考文献: