文章作者:姜南(Slyar) 文章来源:Slyar Home (www.slyar.com) 转载请注明,谢谢合作。
最近几天AT&T Fiber有点抽风,晚上7-11点微信就没法用了,一直无法连接。发了个AT&T帖子然后找客服几天也无果,也不知道啥时候能修好,于是开始自己troubleshooting. 在电脑上装了个Mac版微信,也无法连接,这是个好事,证明Mac客户端跟手机客户端连接的是同一个服务端。又装了个Wireshark抓微信app的包,发现去101.32.104.41这个IP的包都不通, Google了一下这个IP发现属于腾讯新加坡数据中心,那基本没错了。新加坡和香港是微信海外的2个比较大的数据中心。
City: Singapore
ISP: Tencent cloud computing
Autonomous system number (ASN): 132203
Autonomous system organization: Tencent Building, Kejizhongyi Avenue
在正常和晚上受影响期间分别traceroute了一下这个IP,发现在晚上受影响的时间,路径上的192.205.37.70无法联通,又查了一下这个IP发现192.205.36.0/23属于AT&T Services, Inc. (AS7018),这就确认问题肯定是在AT&T了。
既然确定了问题那就开始搞tunnel了,本来一开始想的是搞一台有公网IP的VM然后在自己的边界路由器上搞一个GRE Tunnel就行了,但是UDM Pro居然不支持点到点的GRE Tunnel,真的是非常沙雕了。UDM Pro只支持site to site VPN但想要实现我们静态路由的目的我们还是需要一个GRE Tunnel在上面。考虑了一下之后决定用二级路由(旁路由)来实现。二级路由可以是任何支持VPN和GRE的路由器,比如OpenWRT,DD-WRT之类的,我家里有一台闲置的MikroTik路由器(RouterOS),正好可以拿来用。下面是方案:
家里的LAN网段是192.168.0.0/23。RouterOS的LAN口(ether5)插交换机,WAN口(ether1)插UDM Pro(Port#2),在UDM Pro上新建一个新网络10.0.20.1/24 VLAN 20,给Port#2加一个port profile VLAN 20,在RouterOS上给WAN口设静态IP 10.0.20.20/24,LAN口DHCP获取IP就可以了。(为了方便可以给RouterOS的Bridge管理口设一个静态IP比如192.168.1.88/23方便访问,不影响方案)。至此RouterOS应该可以访问Internet了,路径是0.0.0.0/0 -> ether1 -> 10.0.20.20 -> 10.0.20.1 (Gateway UDM Pro) -> AT&T Fiber modem -> Internet.
下一步就是建立IPSec连接,我们需要一个对端,这里可以用GCP, AWS, Azure或者是OCI(Oracle Cloud),你可以用这些服务商提供的IPSec服务(一般是免费的)也可以用Strongswan之类的软件自建IPSec服务器。这里假设我们已经得到了服务端的公网IP 152.67.254.129,内网网段10.0.0.0/16。得到了这些信息之后我们就可以在RouterOS上建立IPSec连接了。

有几个点需要注意。因为我们是用二级路由而不是边界路由做IPSec,所以这里就涉及到一个NAT内网穿透的过程,一般的公网IP IPSec用的是UDP 500端口,而这里我们需要启用NAT traversal来使用UDP 4500端口通信。我们这一端的源IP是二级路由WAN口的IP (10.0.20.20)而不是边界路由的WAN口IP
1 2 3 4 5 6 7 8 9 10 11 |
> ip ipsec profile print 1 name="myvpn3" hash-algorithm=sha1 enc-algorithm=aes-256,aes-128 dh-group=ecp384,modp2048,modp1024 lifetime=1d proposal-check=obey nat-traversal=yes dpd-interval=10s dpd-maximum-failures=5 > ip ipsec identity print 0 peer=myvpn3 auth-method=pre-shared-key secret="YOUR SHARED SECRET" generate-policy=no > ip ipsec proposal print 1 name="myvpn3" auth-algorithms=sha256,sha1 enc-algorithms=aes-256-cbc,aes-256-ctr,3des lifetime=30m pfs-group=modp1024 > ip ipsec policy print # PEE TUN SRC-ADDRESS DST-ADDRESS 1 A myv yes 192.168.0.0/23 10.0.0.0/16 > ip ipsec peer print 0 name="myvpn3" address=152.67.254.129/32 local-address=10.0.20.20 profile=myvpn3 exchange-mode=main send-initial-contact=yes |
完成设置之后就可以看到IPSec连接已经成功建立,此时我们应该可以ping通对端内网VM的内网IP (这里是10.0.0.83)

至此我们已经成功建立IPSec VPN连接完成了2个内网的互联,但因为IPSec没有interface的概念,想要完成静态路由转发流量,我们还需要分别在对端VM和RouterOS上建立GRE隧道。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# 启动GRE隧道模块 sudo modprobe ip_gre # 增加一个GRE隧道接口gre1,192.168.1.88是RouterOS的内网IP,这个IP基于IPSec连接是可达的 > ip tunnel add gre1 mode gre remote 192.168.1.88 local 10.0.0.83 ttl 255 # 给GRE隧道一个IP地址10.10.10.1 > ip addr add 10.10.10.1/30 dev gre1 > ip link set gre1 up # 在RouterOS上建立隧道 > interface gre print 0 R name="gre-tunnel1" mtu=auto actual-mtu=1398 local-address=192.168.1.88 remote-address=10.0.0.83 dscp=inherit clamp-tcp-mss=yes dont-fragment=no allow-fast-path=yes # 给GRE隧道一个IP地址10.10.10.2 > ip address print # ADDRESS NETWORK INTERFACE 0 192.168.1.88/23 192.168.0.0 bridge 1 10.0.20.20/24 10.0.20.0 ether1 2 10.10.10.2/32 10.10.10.1 gre-tunnel1 |
隧道建立完成后,下一步在RouterOS上建立静态路由,把去微信新加坡数据中心的流量转到GRE tunnel里
1 2 3 4 5 6 7 8 9 |
> ip route print Flags: X - disabled, A - active, D - dynamic, C - connect, S - static, r - rip, b - bgp, o - ospf, m - mme, B - blackhole, U - unreachable, P - prohibit # DST-ADDRESS PREF-SRC GATEWAY DISTANCE 0 A S 0.0.0.0/0 10.0.20.1 1 1 ADC 10.0.20.0/24 10.0.20.20 ether1 0 2 ADC 10.10.10.1/32 10.10.10.2 gre-tunnel1 0 3 A S 101.32.0.0/16 gre-tunnel1 1 4 ADC 192.168.0.0/23 192.168.1.88 bridge 0 |
此时如果你从RouterOS ping 101.32.104.41发现是不通的,因为我们还缺少一个重要步骤叫做masquerade即IP地址伪装,与SNAT类似,用路由器发出接口的IP地址替换源IP。这里需要做2步,一步在RouterOS给GRE接口做,另一步在远程VM上做使得我们的数据包可以通过远程网络的默认网关到达Internet
1 2 3 4 5 6 7 8 9 10 |
# RouterOS firewall rule > ip firewall nat print 0 chain=srcnat action=masquerade out-interface=gre-tunnel1 # 这个是在远程VM上 # ip route default via 10.0.0.1 dev ens3 proto dhcp src 10.0.0.83 metric 100 10.0.0.0/24 dev ens3 proto kernel scope link src 10.0.0.83 10.10.10.0/30 dev gre1 proto kernel scope link src 10.10.10.1 169.254.0.0/16 dev ens3 proto dhcp scope link src 10.0.0.83 metric 1000 # iptables -t nat -A POSTROUTING -o ens3 -j MASQUERADE |
最后一步就是在UDM Pro上再设置一个静态路由,把我们内网去往微信新加坡数据中心的流量转到我们的二级路由上。至此我们的整个方案就全部实施完毕了,内网设备使用微信的流量会自动通过GRE隧道,而其他网站的流量则依旧通过AT&T网络,最小程度减少对速度的影响,而且内网设备无需额外配置任何VPN。还有一些进阶设置比如在RouterOS上通过脚本监控通过AT&T网络到微信新加坡数据中心的可达性,启用GRE静态路由当AT&T网络不可达,移除静态路由当网络恢复,这样可以更进一步减少对网络速度的影响。完整的路径如下:
内网iPhone微信客户端(eg. 192.168.0.11) -> 内网Gateway (192.168.0.1) -> 边界路由静态路由match 101.32.0.0/16 目标下一跳 10.0.20.20 (RouterOS) -> RouterOS静态路由match 101.32.0.0/16 目标下一跳10.10.10.1 -> GRE Tunnel 10.10.10.2 -> IP地址伪装 -> 远程GRE Tunnel 10.10.10.1 -> IP地址伪装 -> 远程网络默认网关 10.0.0.1 -> Internet -> 微信新加坡数据中心
相关链接:
https://wiki.mikrotik.com/wiki/Manual:IP/Firewall/NAT
https://help.mikrotik.com/docs/display/ROS/IPsec
https://docs.sim-cloud.net/en/operations/vpn-configurations/ipsec-site-to-site/mikrotik-behind-nat.html
https://docs.oracle.com/en-us/iaas/Content/Network/Tasks/overviewIPsec.htm