折腾 strongSwan 在 Mac OS X 上的配置。但是 strongSwan 在 Mac 上有个 DNS 问题,导致连接上
VPN 之后,DNS 服务器设置成功,但是 DNS 查询用的 interface 并没有更新(还是 en0,而不是新建的 tun0),导致
resolver 的“Reachable”标记消失,无法查询域名。研究了几天 System Configuration Framework
都没有结果,后来想到干脆捡起之前通过 Raspberry Pi 全家翻墙的烂摊子,继续研究这一种方法,比较一劳永逸。
这个方法的大概意思是,当网络中的一个电脑需要访问不在子网掩码所遮罩的子网下时,网络会把数据包发给网关。默认的网关一般都是路由器。但是通过这 个方法,我们让 Raspberry Pi(或者其它的任何一台 Linux 电脑)作网关,而不是让原来的路由器作网关,并把收到的数据发给位于境外的 VPN 网关,从而达到从境外访问网络的目的。
这个方法要求配置 strongSwan 做一个 site-to-site 网络,所以需要你也有 VPN 网关的控制权。我还没有研究出来如何在 Raspberry Pi 上做 NAT,因为 strongSwan 在 Linux 上不会新建一个虚拟的网络接口,而是依赖 ip xfrm 的策略来实现 IPsec。因此暂时需要在远端网关上配置一个“conn”指定家中网络的子网,然后通过 iptables 设置转发整个子网。IPsec 本身的配置我就不再说了,这类的东西网上比较多,只需要一个 site-to-site,然后远端子网设置成 0.0.0.0/0 即可。
在配置过程中,会遇到一个问题,就是让远端子网设置成了 0.0.0.0/0 之后,连接上 VPN 之后,其它电脑就再也无法和 Raspberry Pi 通信了,大概是因为 Raspberry Pi 发出的包都通过隧道发送到 VPN 网关了。这个问题可以通过设置一个“shunt policy”来解决。在
另外一个大概会遇到的问题问题就是,在 Raspberry Pi 上运行 strongSwan 并且连接上了 VPN 之后,家中局域网的所有设备都会提示 IP 地址冲突。这是因为,RFC 2131 和 5227 说,在获得 DHCP 提供的 IP 地址后,在使用这个地址前电脑必须马上使用 ARP 来探测局域网中的地址冲突。ARP 是广播来问局域网中某个 IP 地址应该解析成哪个 MAC 地址。然而,strongSwan 的“farp”插件在这个情形下会导致严重问题, 因为我们的 rightsubnet 说的是 0.0.0.0/0,所以 farp 插件会对针对任何 IPv4 地址的 ARP 探测使用自己的 MAC 地址做出回应,所以局域网中的电脑不管想用什么 IP 地址都会发现“这个地址已经被占用了”。解决的方法是,运行
另外可能遇到的问题是,类似 Instagram 和 Speedtest.net 的网站可能会打不开(stall)。 这个问题是 MTU 设置过大导致的。Raspberry Pi 上 Arch Linux eth0 接口默认的 MTU 是 1500,如果所访问网站的 MSS 设置得很大,电脑发出去的包还是很大,再被加上 ESP header,就超过了 1500 字节了,会导致包被丢弃。解决的方法可以是改变家里每台电脑的 MTU 值,但是很复杂。另外的办法就是修改网站那一方的 MSS 值,让电脑误认为网站无法处理很大的数据包,这样电脑发出来的包再加上各种 header 就不会超过 1500 了。改写网站发来的 MSS 值可以通过在 Raspberry Pi 上使用
另外我想说一下这一种通过 Raspberry Pi 来连接 VPN 的方法是比较靠谱的,因为如果远端 VPN 服务器出现问题,“死掉的小伙伴检测”(Dead Peer Detection)理论上是可以在较短时间内发现并且移除 Raspberry Pi 中的 ip xfrm 规则的,这样的话经过 Raspberry Pi 的网络流量不会断掉,而是通过家里原来的默认路由访问互联网。虽然不能再经过 VPN 了,但是至少不会断网。
这个方法的大概意思是,当网络中的一个电脑需要访问不在子网掩码所遮罩的子网下时,网络会把数据包发给网关。默认的网关一般都是路由器。但是通过这 个方法,我们让 Raspberry Pi(或者其它的任何一台 Linux 电脑)作网关,而不是让原来的路由器作网关,并把收到的数据发给位于境外的 VPN 网关,从而达到从境外访问网络的目的。
这个方法要求配置 strongSwan 做一个 site-to-site 网络,所以需要你也有 VPN 网关的控制权。我还没有研究出来如何在 Raspberry Pi 上做 NAT,因为 strongSwan 在 Linux 上不会新建一个虚拟的网络接口,而是依赖 ip xfrm 的策略来实现 IPsec。因此暂时需要在远端网关上配置一个“conn”指定家中网络的子网,然后通过 iptables 设置转发整个子网。IPsec 本身的配置我就不再说了,这类的东西网上比较多,只需要一个 site-to-site,然后远端子网设置成 0.0.0.0/0 即可。
在配置过程中,会遇到一个问题,就是让远端子网设置成了 0.0.0.0/0 之后,连接上 VPN 之后,其它电脑就再也无法和 Raspberry Pi 通信了,大概是因为 Raspberry Pi 发出的包都通过隧道发送到 VPN 网关了。这个问题可以通过设置一个“shunt policy”来解决。在
/etc/ipsec.conf
中可以指定一个新的“conn”:conn local-net leftsubnet=192.168.11.0/24 rightsubnet=192.168.11.0/24 authby=never type=pass auto=route这样的话,来往 192.168.11.0/24 子网的通信都会 bypass VPN 隧道。
另外一个大概会遇到的问题问题就是,在 Raspberry Pi 上运行 strongSwan 并且连接上了 VPN 之后,家中局域网的所有设备都会提示 IP 地址冲突。这是因为,RFC 2131 和 5227 说,在获得 DHCP 提供的 IP 地址后,在使用这个地址前电脑必须马上使用 ARP 来探测局域网中的地址冲突。ARP 是广播来问局域网中某个 IP 地址应该解析成哪个 MAC 地址。然而,strongSwan 的“farp”插件在这个情形下会导致严重问题, 因为我们的 rightsubnet 说的是 0.0.0.0/0,所以 farp 插件会对针对任何 IPv4 地址的 ARP 探测使用自己的 MAC 地址做出回应,所以局域网中的电脑不管想用什么 IP 地址都会发现“这个地址已经被占用了”。解决的方法是,运行
ipsec statusall
然后列出正在使用的插件列表,删除“farp”然后将这个列表手工指定在 /etc/strongswan.conf
。另外可能遇到的问题是,类似 Instagram 和 Speedtest.net 的网站可能会打不开(stall)。 这个问题是 MTU 设置过大导致的。Raspberry Pi 上 Arch Linux eth0 接口默认的 MTU 是 1500,如果所访问网站的 MSS 设置得很大,电脑发出去的包还是很大,再被加上 ESP header,就超过了 1500 字节了,会导致包被丢弃。解决的方法可以是改变家里每台电脑的 MTU 值,但是很复杂。另外的办法就是修改网站那一方的 MSS 值,让电脑误认为网站无法处理很大的数据包,这样电脑发出来的包再加上各种 header 就不会超过 1500 了。改写网站发来的 MSS 值可以通过在 Raspberry Pi 上使用
iptables -t mangle -A FORWARD -o eth0 -p tcp -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1361:1536 -j TCPMSS --set-mss 1360
来实现。另外我想说一下这一种通过 Raspberry Pi 来连接 VPN 的方法是比较靠谱的,因为如果远端 VPN 服务器出现问题,“死掉的小伙伴检测”(Dead Peer Detection)理论上是可以在较短时间内发现并且移除 Raspberry Pi 中的 ip xfrm 规则的,这样的话经过 Raspberry Pi 的网络流量不会断掉,而是通过家里原来的默认路由访问互联网。虽然不能再经过 VPN 了,但是至少不会断网。