前言
上回说到了
通过 VPN 进行加速, 有不少朋友已经体会到了 VPN 的好处,许多网站从缓慢到无法访问,变得可以流畅的访问了。但是,我们经常面临一个问题,VPN 拨通后,所有流量都会流经 VPN,导致本地的网络访问可能会很不顺畅,有的速度非常缓慢,有的甚至不能访问。特别是对于那些在使用 VPN 同时还在进行本地下载的用户,这个问题更加明显。对于那些收费的 VPN 而言,因为它们速度较快,所以感觉问题不大。但是,对于那些免费的、速度比较缓慢、甚至限制流量的 VPN 来说,这就是一个比较严重的问题了。很多人不得不同时只干一件事情,要使用 VPN 就停止本地网络的访问,要访问本地网络就需要断开 VPN。那么,可不可以只有访问镇外的流量走 VPN,而本地网络依旧使用本地连接呢?答案自然是可以的。
解决方案
其实这个问题在使用 VPN 之前就存在过。使用过教育网的朋友肯定有经验,国内流量是免费的,而国外流量是收费的。一般来说大家都会访问国内网络,但是毕竟偶尔也需要访问国外网络。 有的学校做的比较一刀切,干脆禁止国外流量,而有的学校则收取国外流量高昂的费用。所以,在教育网生活的时间,大家都到处寻找国内代理服务器,一时间代理 服务器搜索软件、代理服务器列表比比皆是。当时我们学校面临这个问题的时候,我们采用了另外一种办法,除了教育网光纤接入外,我们还申请了电信的包月 ADSL 作为第二链路。当时对于非服务器的网段使用的是 NAT,分别在教育网和ADSL接入上做NAT。然后,修改路由设置,凡是国内流量,都路由到教育网光纤接口;凡是国外流量,都路由到电信的 ADSL 接口。虽然,国外流量的带宽不大,但是对于当时的学生对国外网站的需求不高的情况下,这个问题还是得到了很好的解决。
再回过来看我们 VPN 的问题,会发现其实是一个问题。我们可以添加路由,将所有本地的网络(如上例的所有国内网络范围),路由到我们使用 VPN 前的默认网关。而剩余的,也就是国外的网络范围,自动流经 VPN。这就很好的解决了 VPN 降低本地网络访问速度的问题。
然后,我们需要寻找到本地的 IP 地址范围,在本例中,我使用的是国内的地址范围。因为这个数据库属于公开数据,我们可以从 CNNIC 或者 APNIC 获得相关数据。剩下的就是对地址库进行分析,然后生成路由添加和删除的脚本就可以了,下列脚本只对 VPN 类的链路性的加速有意义,对于SOCKS类的代理是一点帮助也没有。
由于我的机器分别有 Ubuntu 和 Windows 7,因此,我将针对这两个系统写脚本。脚本语言不同,但是参数是一样的。
- on 表明开启国内路由走默认网关的配置。也就是说添加国内网络的路由指向原默认网关。
- once 表明只开启国内路由走默认网关的配置一次,重新启动系统后配置将丢失。
- off 表明关闭这个功能。也就是说删除之前操作所添加的路由。
- update 是指下载更新 IP 地址库。
代码编写以及使用
Linux
对于 Linux 最通用的莫过于 Shell 了,因此,我使用 Bash 完成了这个脚本。虽然我是在 Ubuntu 10.10 系统上测试的,但是它应该支持大部分 Debian 派生系统。我也写了 Redhat 及其派生系统的支持代码,不过没有进行测试罢了,有环境的朋友可以测试后告诉我在什么版本上可以执行,或者是有什么问题,我会修改的。
005 | IPV4_HTML=/tmp/cnnic_ipv4.html |
006 | IPV4_DATA=~/cnnic_ipv4.data |
007 | VPN_IF=`ip tuntap | cut -d ':' -f 1` |
009 | if [ -z "$VPN_IF" ] ; then |
013 | GATEWAY=`route -n | grep - v "$VPN_IF" | grep UG | tr -s ' ' | cut -d ' ' -f 2` |
015 | if [ "`echo " $GATEWAY " | wc -l`" - gt "1" ] ; then |
017 | GATEWAY=` echo "$GATEWAY" | head -n 1` |
020 | GATEWAY_IF=`route -n | grep $GATEWAY | head -n 1 | tr -s ' ' | cut -d ' ' -f8` |
024 | ROUTE_DIR_DEBIAN=/etc/network/ if -up.d |
025 | ROUTE_FILE_DEBIAN=$ROUTE_DIR_DEBIAN/vpnroute |
027 | ROUTE_DIR_REDHAT=/etc/sysconfig/network-scripts |
028 | ROUTE_FILE_REDHAT=$ROUTE_DIR_REDHAT/route-$GATEWAY_IF |
030 | function get_cn_data() { |
031 | echo "Downloading CN network data from CNNIC..." |
032 | RE_PATTERN= "s/^.*searchtext=\([./0-9]*\).*$/\1/" |
033 | wget -O $IPV4_HTML $URL_CNNIC |
034 | grep "whois.pl?" $IPV4_HTML | sed -e "$RE_PATTERN" > $IPV4_DATA |
040 | function check_data() { |
041 | if [ ! -f $IPV4_DATA ] |
043 | echo "CN network data file [$IPV4_DATA] is not exist." |
048 | function add_route_cn() { |
051 | # Check whether the routes are already added |
052 | if [ "$ROUTE_ADDED" == "true" ] ; then |
053 | echo "Routes already added, should be removed first." |
055 | GATEWAY=` echo "$GATEWAY" | head -n 1` |
057 | echo "Adding routes of CN ..." |
058 | echo "Default gateway is $GATEWAY on dev $GATEWAY_IF" |
059 | if [ "$PERMANENT" == "yes" ] ; then |
060 | echo "Generating route config file for next boot. [$ROUTE_FILE_DEBIAN]" |
062 | if [ -d $ROUTE_DIR_DEBIAN ] ; then |
063 | # Prepare ROUTE_FILE_DEBIAN header |
064 | rm -f $ROUTE_FILE_DEBIAN |
065 | echo "#!/bin/bash" >> $ROUTE_FILE_DEBIAN |
066 | chmod a+x $ROUTE_FILE_DEBIAN |
069 | if [ -d $ROUTE_DIR_REDHAT ] ; then |
070 | # Use backup overwrite current file, otherwise backup the file |
071 | if [ -x "$ROUTE_FILE_REDHAT.bak" ] ; then |
072 | cp -f "$ROUTE_FILE_REDHAT.bak" "$ROUTE_FILE_REDHAT" |
074 | cp -f "$ROUTE_FILE_REDHAT" "$ROUTE_FILE_REDHAT.bak" |
082 | net=` echo "$line" | cut -d '/' -f1` |
083 | cidr=` echo "$line" | cut -d '/' -f2` |
085 | # generate ip route cmd |
086 | ip_args= "$net/$cidr via $GATEWAY" |
087 | if [ "$PERMANENT" == "yes" ] |
090 | if [ -d $ROUTE_DIR_DEBIAN ] ; then |
091 | echo "$IP_PROG route add $ip_args" >> $ROUTE_FILE_DEBIAN |
094 | if [ -d $ROUTE_DIR_REDHAT ] ; then |
095 | echo "$ip_args" >> $ROUTE_FILE_REDHAT |
098 | $IP_PROG route add $ip_args |
099 | count=$(( $count + 1 )) |
101 | echo "Added $count networks." |
105 | function remove_route_cn() { |
107 | echo "Removing routes of CN ..." |
109 | if [ -x $ROUTE_FILE_DEBIAN ] ; then |
110 | rm -f $ROUTE_FILE_DEBIAN |
113 | if [ -x $ROUTE_FILE_REDHAT ] ; then |
114 | # If the backup exist, then use backup overwrite current file, otherwise filter the file |
115 | if [ -x "$ROUTE_FILE_REDHAT.bak" ] ; then |
116 | mv -f $ROUTE_FILE_REDHAT.bak $ROUTE_FILE_REDHAT |
118 | cp -f $ROUTE_FILE_REDHAT $ROUTE_FILE_REDHAT.old |
119 | sed "/ via $GATEWAY$/d" $ROUTE_FILE_REDHAT > $ROUTE_FILE_REDHAT.tmp |
120 | mv -f $ROUTE_FILE_REDHAT.tmp $ROUTE_FILE_REDHAT |
127 | net=` echo "$line" | cut -d '/' -f1` |
128 | cidr=` echo "$line" | cut -d '/' -f2` |
129 | $IP_PROG route del $net/$cidr via $GATEWAY 2> /dev/null |
130 | count=$(( $count + 1 )) |
132 | echo "Removed $count networks." |
137 | echo "Route networks of CN to the default gateway instead of VPN tunnel" |
139 | echo "usage: $PROG {on|off|update}" |
141 | echo " on Add routes of CN to default gateway." |
142 | echo " once Add routes of CN to default gateway. Only for this time, reboot the configure will be disappeared." |
143 | echo " off Remove routes of CN" |
144 | echo " update Force download/update CN network data" |
149 | "on" ) add_route_cn yes ;; |
150 | "once" ) add_route_cn no ;; |
151 | "off" ) remove_route_cn ;; |
152 | "update" ) get_cn_data ;; |
下载脚本到本地
3 | sudo bash vpnroute.sh on |
需要删除添加的路由,可以执行:
1 | sudo bash vpnroute.sh off |
需要更新网络路由数据,可以执行:
1 | bash vpnroute.sh update |
Windows
Windows 的批处理文件虽然也能写一些东西,但是总觉得它的功能太受限了。因此,这次我使用
PowerShell 来写这个脚本。
PowerShell 是微软 2006 年推出的抗衡 *nix 中的 shell 的产品,通过紧密结合 .Net Framework 大幅提高命令行及脚本的能力。熟悉 linux shell 脚本的朋友会在使用中会发现有很多语法似曾相识。虽然有各种不适应,但是不得不说 PowerShell 还是很强大的。
001 | $prog = $myinvocation .mycommand.name |
003 | $curdir = ( get-location ).path |
004 | $ipv4_html = join-path $curdir "cnnic_ipv4.html" |
005 | $ipv4_data = join-path $curdir "cnnic_ipv4.data" |
006 | $gateway = route print -4 | where { $_ -match "^\s+0.0.0.0\s+0.0.0.0" } | %{ $_ -replace '\s+' , ' ' } | %{ $_.Split( " " )[3] } |
008 | function cidr -to -mask ( $cidr ) { |
010 | $full_octets = $cidr /8 |
011 | $partial_octet = $cidr % 8 |
013 | for ( $i = 0; $i -lt 4; $i ++) { |
014 | $a = $full_octets - $i |
015 | if (( $a -gt 0) -and ( $a -ge 1)) { |
017 | } elseif (( $a -gt 0) -and ( $a -lt 1)) { |
018 | $mask += (256 - [math]::Pow(2,(8-$partial_octet))) |
022 | if ( $i -lt 3) { $mask += "." } |
027 | function get -cn -data { |
028 | Write-Host "Downloading CN network data from CNNIC..." |
029 | $webclient = new-object System.Net.WebClient |
030 | $webclient .DownloadFile($url_cnnic, $ipv4_html) |
032 | cat $ipv4_html | where { $_ -match "whois.pl?" } | % { $_ -replace "^.*searchtext=([.\d/]+).*$" , '$1' } > $ipv4_data |
036 | function check -data { |
037 | if (!( test-path $ipv4_data)) { |
038 | Write-Host "CN network data file [$ipv4_data] is not exist." |
043 | function add -route -cn ([bool] $permanent ) { |
046 | $route_count = route print -4 | where { $_ -match "$gateway" } | measure-object |
047 | if ($route_count > 1) { |
048 | Write-Host "Routes already added, should be removed first." |
052 | Write-Host "Adding routes of CN ..." |
053 | Write-Host "Default gateway is $gateway" |
055 | foreach ( $line in Get-Content $ipv4_data) |
057 | $item = $line .split( "/" ) |
060 | if (( $cidr -ge 0) -and ( $cidr -le 32)) |
062 | $mask = cidr -to -mask ( $cidr ) |
067 | route.exe add $opt $net mask $mask $gateway > null |
071 | Write-Host "Added $count networks." |
075 | function remove -route -cn { |
077 | Write-Host "Removing routes of CN ..." |
079 | foreach ( $line in Get-Content $ipv4_data) |
081 | $item = $line .split( "/" ) |
084 | if (( $cidr -ge 0) -and ( $cidr -le 32)) |
086 | $mask = cidr -to -mask ( $cidr ) |
088 | route delete $net mask $mask > null |
091 | Write-Host "Removed $count networks." |
096 | Write-Host "Route networks of CN to the default gateway instead of VPN tunnel" |
098 | Write-Host "usage: $prog {on|off|update}" |
100 | Write-Host " on Add routes of CN to default gateway" |
101 | Write-Host " once Add routes of CN to default gateway. Only for this time, reboot the configure will be disappeared." |
102 | Write-Host " off Remove routes of CN" |
103 | Write-Host " update Download/update CN network data" |
108 | "on" { add -route -cn ( $true ) } |
109 | "once" { add -route -cn ( $false ) } |
110 | "off" { remove -route -cn } |
111 | "update" { get -cn -data } |
下载脚本
http://files.cnblogs.com/dancefire/vpnroute.zip 到某个目录,比如 d:\tmp
修改 PowerShell 执行权限,默认只能执行签名脚本。:
开始->附件->Windows PowerShell->右键点击 Windows PowerShell->以管理员身份执行
在命令行里执行:
1 | Set-ExecutionPolicy -ExecutionPolicy Unrestricted |
可能会提示如下信息:
2 | 执行策略可以防止您执行不信任的脚本。更改执行策略可能会使您面临 |
3 | about_Execution_Policies |
4 | 帮助主题中所述的安全风险。是否要更改执行策略? |
5 | [Y] 是(Y) [N] 否(N) [S] 挂起(S) [?] 帮助 (默认值为"Y"): |
选择 Y,或者回车即可。
然后,进入下载文件所在目录,执行脚本
如果脚本可以执行,它会返回如下帮助信息:
1 | PS d:\tmp> .\vpnroute.ps1 |
2 | Route networks of CN to the default gateway instead of VPN tunnel |
4 | usage: vpnroute.ps1 {on|off|update} |
6 | on Add routes of CN to default gateway |
7 | once Add routes of CN to default gateway. Only for this time, reboot the configure will be disappeared. |
8 | off Remove routes of CN |
9 | update Force download/update CN network data |
如要添加路由,希望所有中国范围的IP使用默认的网关访问,就运行:
如果想要去掉添加的路由,就运行
想更新中国IP路由数据,就运行
我注意到 Windows 的 PowerShell 脚本执行速度不是很高,在 Linux 上使用 Bash 添加路由大约只需要10-20秒,在 Windows 上使用 PowerShell 需要 70-80 秒。大家如果看到有段时间没响应,不要着急,给他一些时间就会好的。
连接后,分别访问
http://www.ip.cn 和
http://whatismyipaddress.com 以测试路由设置是否正常。
如果路由添加正确,访问国内网站
http://www.ip.cn 应该看到的是你国内的 ip。而访问国外网站
http://whatismyipaddress.com ,应该看到的是你 vpn 的 ip。
原文:http://www.cnblogs.com/dancefire/archive/2011/03/28/how-to-setup-vpn-cn-route.html
没有评论:
发表评论