Mac 上的 L2TP 分流路由:为什么写主机地址不行
setadditionalroutes 要的是网络地址加掩码成对给出,还会整表替换,并且必须使用字面的服务名。先查表,再怪网关。
VPN 连上了,菜单栏也有图标,可内网主机依然超时。这种情况下有三种可能的成因,具体是哪一种取决于隧道是怎么配置的。要么路由命令在该写网络地址的地方写了主机地址,要么配置文件本身装了一条默认路由、把判断整个吞掉,要么客户端是需要 hook 而不是配置键的 PPP 隧道。按这个顺序排查,因为第一种最容易排除。
目标是网络地址,掩码描述前缀
networksetup -setadditionalroutes 接受「目标、子网掩码、网关」三元组。要把一个内网子网送进隧道的调用是这样的:
sudo networksetup -setadditionalroutes "VPN (L2TP)" 192.168.1.0 255.255.255.0 10.2.0.0
注意目标写的是什么:是网络地址,而不是你要访问的那台服务器的地址。在促成这篇文章的那个案例里,同样的命令把 192.168.1.20 放在那个位置毫无用处,换成 192.168.1.0 立刻就好了。同样的掩码、同样的网关,只差一个数字。整件事的毛病就在此处,只是缩小了看。
原因是算术,不是策略。主机地址配上 /24 掩码并不描述 192.168.1.0/24;它描述的是路由表无法有效保存的东西,于是条目被拒绝或被忽略,隧道最终承载的比你以为的要少。
确实只要一台主机时,用主机地址配 255.255.255.255 掩码。出错的是混搭的配对,而不是「用主机地址」这件事本身。由单个地址构成的 /32 是合法路由;由单个地址构成的 /24 才是错。
这次调用是整表替换,名字必须字面一致
有两个行为会让头一次尝试的人意外。命令设置的是完整的附加路由列表,而不是追加,所以加一个前缀会悄悄删掉之前存在的其他条目。请一次把整个集合建完。
服务名也不是你随手起的标签。从工具里读出来,连类型后缀一起原样复制:
networksetup -listallnetworkservices
差一个字符的名字不会给出值得一读的错误,也不会产生路由。这里正确的做法是复制粘贴,而不是凭记忆重敲。
当配置文件声称拥有默认路由时
对于任何没有被显式覆盖的目标,分流路由都会输给默认路由,而为全隧道配置的 L2TP 配置文件装的恰恰就是那条。如果配置说一切都走隧道,附加路由列表几乎不起作用。你的条目没有错,只是对那些它从未点名的目标毫无意义。这件事的控制项通常在配置文件的「发送全部流量」设置里,而不在任何路由命令里。
在继续编辑更多条目之前,先确定你真正想要哪种模型。带例外的全隧道与带特定前缀的分流隧道是两种不同的配置,把两者混在一起会得到一张谁也预测不了的表。
如果组织要求全隧道,就别再在本地试图分流,把要求交给拥有策略的人。与之矛盾的本地新增路由,反正也会在下次配置文件更新时被删掉。
PPP 隧道要的是 hook,不是配置键
对基于 PPP 的隧道,defaultroute 是创建默认路由的配置键;去掉它,客户端就不再声称拥有每个目标。之后的路由要在接口出现后添加,通常来自 /etc/ppp/ip-up:
#!/bin/sh
/sbin/route add -net 192.168.1.0 -interface ppp0
让脚本归 root 所有并可执行;当机器上存在多个隧道配置时,还要对接口名加一道校验。一个对每条 PPP 链路都运行、又假定只有一个设备名的 hook,是个小毛病,却会活很久。
用三个请求验证分流
连接时抓下路由表,记下是哪块接口在服务隧道。然后去查具体目标,而不是相信创建它的那条命令的退出状态。退出码报告的是工具接受了参数,不是流量走了你想要的路。
最后用三个请求证明分流:按地址访问一台内网主机、通过内网解析器解析一个内网名字,以及一个仍应走本地连接的公网站点。如果公网站点现在也走了隧道,那分流根本没发生,不管路由表看起来怎么说。
再连一次并重复内网请求,因为 手工添加的路由可能比让它生效的接口活得更久,隧道断开之后就会悄悄把流量打进黑洞。