一个路由器,两种网络,VPN和下载两不误

最近在用 Titanium 写一个 Twitter 客户端,因为要用到 OAuth 认证,所以就在手机连接 VPN,但是速度比较慢。刚好想在家里的路由器上加上 OpenVPN,昨天就折腾了一下。

在家里路由器上加上 OpenVPN 的一个主要问题是,家里的网络中还有下载机,而下载的流量是不想通过 VPN 去传输的。虽然 chnroutes 项目的路由表可以让国内的 IP 走直连,国外的 IP 走 VPN,但是 eMule 或者 BT 下载时,难免会连接到国外的用户或者服务器,这个时候也不想去浪费 VPN 的流量。

因为这些,我的想法是在路由器上做判断,如果是从下载机过来流量,就通过直接连接,如果其他机器,例如笔记本,就根据目标 IP 来判断是通过直接连接还是 VPN 来传输。

下载机是通过 LAN 连接到路由器的,本来想按进入流量的设备来判断是否为下载机,后来发现实现比较麻烦,就决定按流量来源 IP 来判断是否为下载机的数据。

准备工作

  • 运行 dd-wrt 的路由器一个,要带有 OpenVPN
  • OpenVPN 服务器一个,认证方式选择证书认证
  • Linux 知识若干

当然 dd-wrt 并不是必须的,也可以是 openwrt 或者 tomato 之类,只要带有 OpenVPN 就行,如果不带 OpenVPN,就需要在启动过程中去外部下载相关的软件,那就是另外的内容了,暂且不提。

网络分段

因为要按 IP 来区分流量是否要走 VPN,因此要先划分一下局域网里要用到的 IP 段。

路由器的 IP 是 192.168.2.1,DHCP 分配范围为 192.168.2.100~149,按照需求将一个 /24 的网段分为三个部分:

  • 192.168.2.16~31,此 IP 段的设备流量均走直连
  • 192.168.2.32~63,此 IP 段的设备流量根据目标 IP 判断走直连还是走 VPN
  • 192.168.2.100~149,此 IP 段为 DHCP 分配的 IP 段,流量也根据目标 IP 来判断是否走 VPN

因为 DHCP 分配的 IP 并不可控,所以将流量走直接的设备,例如下载机,通过静态 IP 的方式,直接分配一个在 192.168.2.16~32 中固定 IP,可以保证不会连接到有 VPN 的网络。

路由策略表

先创建一个用来直连的路由策略表,用来将所有指定 IP 段的流量走直连。

# 添加一个路由策略表,此表针对 192.168.2.16/28 IP 段有效
ip rule show | grep "lookup 10" || ip rule add from 192.168.2.16/28 ta 10

# 设置策略表的默认路由
WAN_IP=`ifconfig ppp0 | grep "inet addr" | cut -d ":" -f 2 | cut -d " " -f1`
ip route replace 192.168.2.0/24 dev br0 proto kernel scope link src 192.168.2.1 ta 10
ip route replace 127.0.0.0/8 dev lo  scope link
ip route replace 169.254.0.0/16 dev br0  proto kernel  scope link  src 169.254.255.1
ip route replace default via $WAN_IP dev ppp0 ta 10

将以上代码保存在 dd-wrt 的 Filewall Script 中,这样在每次 WAN IP 改变的时候,都可以更新这个路由策略表了。

OpenVPN 配置

OpenVPN 按默认配置即可,需要注意的是,路由器上的 tun mtu、tun mtu extra 以及 mssfix 需要与服务器一致,或者服务器与路由器上的配置一致。

因为要用到 chnroutes,但是 dd-wrt 中的 OpenVPN 配置并不支持自定义配置,没办法添加 route 选项,因此要把这些选项放到 OpenVPN 服务端的配置文件中,使用 push 指令在连接时推送到客户端来。

例如:

push "route 1.0.0.0 255.255.0.0 net_gateway 5"

另外一有点需要注意,如果在 dd-wrt 中同时启用了 OpenVPN Daemon,建议将 OpenVPN Daemon 的启动方式设置为“System Startup”而不是“WAN Up”,在我的路由器上,使用“WAN Up”时 OpenVPN Daemon 和 OpenVPN Client 会冲突,导致 OpenVPN Client 启动失败。

解决 max-routes

OpenVPN 客户端,默认最多只能添加 100 条路由记录,但是 chnroutes 正常生成的路由表,可能会在 1000 条以上,因此 100 条是远远不够的。

这个可以通过 max-routes 配置项来解决,本来打算这个配置同样从服务端推送过来,但是 OpenVPN 现在并不支持 push “max-routes 1500” 这样的指令。

在 dd-wrt 的 OpenVPN 配置中,也没有相应的选项,为了解决这个问题,只能采取一个比较取巧的办法来解决。dd-wrt 中的 OpenVPN 配置都是存在 nvram 中的,在 dd-wrt 启动后,会自动从 nvram 中取 OpenVPN 的相关配置,组合成一个 openvpn.conf,而这个配置除了可以在 dd-wrt 的 Web 界面中修改,还可以直接 SSH 到 dd-wrt 上,直接使用 nvram 命令修改。

在这里要 hack 的配置是 mssfix,当然其他的属性也可以,选择 mssfix 是因这个属性比较简单,改起来方便。

我这里设置了 mssfix 为 1400,另外服务器要推送的路由表为 1300 条左右,直接将 max-routes 设置为 1500,在路由器上运行下面这个指令:

nvram set openvpncl_mssfix="1400
max-routes"

需要注意的是,一定要分两行来输入,否则生成的 openvpn.conf 中,mssfix 1400 和 max-routes 1500 会在同一行而导致配置失效。

这样在生成的 OpenVPN 配置文件中,就有了 max-routes 选项,服务端也可以正常推送路由表了。

不过这样也有一点坏处,那就是如果再修改了 OpenVPN 配置并保存,会把 mssfix 中的那个回车给去掉,再次导致连接失败。不过 OpenVPN 一旦配置完成,也不会经常改动,倒也不是很大的问题。

配置 DNSMasq

一般手机上连接上 WiFi 的时候,设置 DNS 等内容会比较麻烦,而如果不设置 DNS,会导致在手机上解析域名时,使用了国内的 DNS 服务器,而这也会导致一些问题,可以按照 autoddvpn 中的说明,将 DNS 设置为 Google Public DNS 和 OpenDNS:

8.8.8.8
8.8.4.4
208.67.222.222

打完收工

配置完成之后,就可以方便的分配家庭局域网里设备的流量走向了,想要设备的流量走直连,只要分配到 192.168.2.16~31 这个 IP 段就可以了,至于其他的设备,可以使用静态 IP,也可以直接使用 DHCP 分配。

嗯,这样再在真机上调试 Twitter 客户端之类的程序就方便了。

PS. 非常感谢 @tjmao 在折腾过程中帮助。

参考资料

  1. autoddvpn: DNSMasq
  2. OpenVPN 2.1 Manual
  3. linux 高级路由及流量控制总结
  4. ip(8)
  5. route(8)
  6. ddwrt: Hardware

— EOF —

发表评论?

7 条评论。

  1. 恭喜折腾成功!

  2. 搜Joggler找过来,发现都是网友啊,:D

  3. 好棒,房子装好就实验下,折腾星人万岁

  4. :mrgreen: :mrgreen: :mrgreen:

    哈哈,原来是你啊!!我也准备折腾路由器呢,不懂了再来咨询你

发表评论


注意 - 你可以用以下 HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>