ZeroTier 00: 白皮书解析

ZeroTier是一种二层组网方案,其核心采用SDN架构,具有部署方便、性能强劲的优点。通过搭建自己的Controller、Planet和Node服务器,即可开箱即用ZeroTier的所有功能,并进行一键组网。除此之外,ZeroTier还支持许多开箱即用的高级功能,比如可路由下发、路由策略、流量策略等。ZeroTier控制器还支持P2P发现,能自动寻找最短路由、UDP打洞,不可谓不强大。本系列将开启ZeroTier之旅,一步步搭建属于自己的网络。

作为系列的开篇,本章主要结合官方白皮书,介绍下ZeroTier架构。

0 ZeroTier白皮书解析

首先我们来简单解析下ZeroTier官方提供的技术白皮书,从中解析出ZT到底有那些特性。

白皮书地址:https://docs.zerotier.com/zerotier/manual

ZT白皮书阅读路径

这篇文章主要从两个维度来解析ZT,即从VirtualLayer1,对应网络物理层,也就是说“网线”的部分;以及VirtualLayer2,对应网络链路层,的部分。先说明了VL1支持哪些特性,然后说明了VL2支持哪些特性。

我们先看VL1。

0.1 VL1特性

0.1.1 虚拟物理层

要实现两台主机的互相通信,一般需要一根“网线”来进行互联,这样数据才有一条通路,两台主机才能说得上话。ZT的VL1层就是做“网线”的工作,仅仅负责将两台主机联系起来。

仅有两台主机时的互联

0.1.2 自动P2P

同样是上面的情况,如果有三台主机需要互相通信,我们结合现实,发现需要一台交换机或路由器来将这三台主机互联起来。实际上,有一台主机可以配置成交换机的形式,也就是说需要两根网线。

传统的网线连接方式

这种组网方式在直连网线的情况下是可以的,但如果这两条网线太长了,以至于传输都要耗时很久呢?这样的话,右边的主机要访问左边的主机,就得绕路中间的主机。为什么不能直接将下面的两台主机连接起来呢?

这就是VPN组网的优势,不受限于传统的网络架构,可以同时向多台主机建立“物理”连接。但不同于普通协议,基于SDN架构的ZT可以做得更多,ZT支持Peer自动发现。比如说中间的主机发现左右两边的主机具有直连的条件,就会告诉它们,让它们自己再建立一条连接。

值得一提的是,即使左右两台主机位于NAT防火墙后方,中间的主机也会尝试以NAT打洞的方式协助建立连接。

Peer自动发现及NAT打洞连接

0.1.3 World, Planet, Root, Moon和Node

当你研读官方白皮书,或者阅读代码,查阅其他博客时,可能经常见到以上5个术语。这5个术语也困扰了我一会儿,官方试图通过通俗的语言将这几个关系弄清楚,但感觉有点适得其反了,再加上一些博客也说得云里雾里的,更加混淆了。所以在这里,结合我的实践,我要以非常直观的方式解读一下。

先明确一个前提条件:在ZT网络中,各种节点均运行同一套程序,即ZeroTierOne。

先上一张图:

ZT各种术语和节点数据流向图

乍一看,是不是眼花缭乱?没关系,我们从客户端入网开始,一步一步来看。

第1步 客户端向RootServer发起连接

客户端刚刚配置好时,是不知道任何其他设备的,除了内部预置的几台RootServer。因此,客户端第一时间会向RootServer建立连接,RootServer会记录客户端ID,客户端ID是唯一的,此时客户端就相当于已经加入ZT大拓扑了。(就像客户端获取了一个公网IP,只要有这个IP,就能准确找到客户端)

客户端直接向RootServer发起连接,此时客户端之间已可相互通信

第2步 客户端加入某个网络

随后,用户在zerotier.com创建一个网络,假设ID为deadbeef,这个ID就存到RootServer中了。这个网络ID也是唯一的,只要有这个网络,就能精准定位。

当NodePC尝试加入此网络时,就直接询问RootServer:deadbeef在哪里?请求加入!RootServer一看,网络ID找到到,于是同意加入,或等待用户确认后加入。NodeAndroid进行了同样的流程后,NodePC和NodeAndroid就在同一个网络下了。

第3步 数据传输、路由收敛、建立P2P连接

加入网络后,相当于仅仅在整个ZT网络中注册了这个客户端的存在,此时并没有触发其他动作。其他动作是伴随数据的传输而发生的。

比如NodePC开始ping NodeAndroid了。当第一个ping包发送出去时,客户端同时还会将自己的一些信息逐级发送给上游(当然目前我们只有一个上游,只有RootServer),上游收到后,即开始查找是否有更快的路径,并同时协助两个客户端进行P2P连接(包括但不限于NAT打洞、uPnP配置、LAN广播发现等等),一旦发现两台设备之间有更快的路径,后续所有数据包均通过这个路径来传输了。

需要指出的是,一旦发现的这个路径失效(比如运营商的NAT映射计数器超时了),就会自动切换回RootServer中转模式;处于中转模式下时,同时也会周期性地探测其他更快路径,一旦探测成功,就又会切换回去。

加入Moon,继续

通过以上经典的3步,我们发现,这个架构其实就是每台设备接入个中央路由器来组网。但这台路由器相比传统的傻瓜路由有个最大的优点:它能自动收敛路由,能帮助两台设备之间建立直接连接。

利用ZT收敛网络的特性,我们加入Moon节点继续探究,在Moon节点的介入下,数据流是如何流动的。

在官方白皮书写道,Moon就是围绕Planet运行的一组节点。我觉得不如这样解释更好:Moon是RootServer的下游节点,是客户端与Root之间的节点,其意义就是实现就近中转。

将本来由Root中转的流量收敛到了Moon上

根据官方白皮书,使用Moon有以下好处:

  • 在离结点更近的地方建立Moon节点,有利于提高网络性能
  • 能减轻Root节点的负担
  • 若在局域网内建立Moon,当互联网连接断开时也可继续维持小网的运行

但是,要使用Moon作为中转节点,需要主动配置客户端使用某些Moon,而且也要拿到它们的公钥才可连接。

通过以上描述,你应该明白这几个术语了:

  • World就是一个概念,指的是整个ZT拓扑
  • Planet和World可以看成一个概念
  • Root节点是一个Planet的重要基础设施,是公共接入点,位于网络拓扑树的根
  • Moon节点位于中间,其上游与Root连接,是网络拓扑树的枝,使用Moon构建网络有多种优点
  • Node泛指一个节点,可以是RootNode,MoonNode,也可以是客户端Node
  • 但白皮书中常常用Node指代客户端节点,是网络拓扑的叶。

0.1.4 聊些细节问题

本节是我在实践中总结出来的几条规律,有一些不是白皮书的内容,属于个人的推理。但我觉得可以拿出来和大家分享,有助于大家更好地理解ZT大拓扑。

第一,ZT根节点知道这个大网络下的所有节点和网络。

这一点是白皮书上的内容了,放在这里仅仅是做铺垫,引出下面的议题。

值得一提的是,如果你自己建立了一个Moon节点,且此Moon节点配置了官方的Planet(Root),同时你在这个Moon节点上建立了一个控制器,且仅在Moon上建立了一个网络,那么这个网络也会出现在Root中。(也就是说,只要自己建立了Moon和控制器,即使这个Moon和控制器没有暴露在公网,无需注册也可直接开始组网!因为Moon会主动向Planet建立连接!)

第二,所有的ZT节点都运行着同样的软件,即ZeroTierOne。

这一点也是白皮书上的内容,放这里也是铺垫一下。

第三,ZT网络拓扑树,是由客户端自己决定的。

这是一个推断。依据:所有ZT节点本质上一样(参考第二条),但客户端可以自由配置使用哪些节点作为Root,也可以自由配置使用哪些节点配置Moon,甚至可以自由配置Peers。

所以如果将原本的Moon服务器(moon1)配置成Root服务器,将原本的Root服务器(root1)配置成Moon,那么客户端就相信原来的Moon(moon1)为网络拓扑树的根节点。如果按照这种改动,那么客户端加入网络时,将首先向moon1服务器发送请求,moon1不知道有这个网络,就接着向root1发送请求,最终获取到网络信息(参考第一条)。

第四,ZT的网络拓扑的收敛是从下往上的,仅可使用沿途的节点进行收敛。

这是针对实践现象的一个推测。现象是,客户端加入网络后,peers列表中仅会展示Planet,Moon和Leaf节点,不会有陌生的节点。况且如果是从上往下收敛,也不可能实现,因为Root的下游肯定是有千千万万的Moon,客户端不可能把这些Moon全部便利一遍来查找最快路径。

这也意味着,我们搭建的Moon节点不会被别人拿来中转流量。事实上,我们无论配置Moon还是Root/Planet时,都需要提供对应的identity信息,只有identity匹配才可建立连接,其他设备当然无法获取identity,因此自然无法连接到我们的Moon节点。(其机制应该类似于WireGuard的非对称加密)

这也确保了网络的安全性。当然,我们最好不要去连别人的Moon,即使他们共享了identity出来。要是遇到不法分子利用中间人攻击,劫持流量,就不安全了。

0.1.4 从SDN的角度理解ZT

看完0.1.3节,你可能还是一头雾水,没关系,我们可以从另外一个角度来理解ZT的架构。

SDN为软件定义网络,这是一种新型的网络架构。传统的转发网络是各节点维护自己的路由表,一旦需要改变路由,可能就需要逐个修改沿途每个节点的路由表。SDN网络则设了中心的控制器,可以批量修改每台设备的路由表。

ZT的Root服务器其实就相当于总控制器,所以它要维护所有节点的数据。不同的Moon就是下游控制器,受控于上游Root控制器,且当下游连接了新节点时,也要告知上游。

所以,当客户端A与客户端B组网时,会由控制器统一调度,Moon控制器发现有设备想联系一台陌生的设备,就联系上游询问,如果上游也不知道,上游又询问上游,一直到所有信息都知道的Root。控制器们通过相互交流,确定一条路径后,客户端们就可以联系了。

0.1.5 Multipath

客户端往往需要同时与Root、Moon、Peer建立连接。这意味着往往会有冗余的连接。如果条件允许,能否将这些链路都用起来呢?

这就是SDN网络的另一个魅力了。通过配置,多条链路可以配置成备份关系、聚合关系,甚至可以以策略的方式合理利用每一条链路。比如VoIP数据走稳定但带宽小的路径,CDN数据走带宽大但延时不确定的路径。这些ZT都支持。我们将在后面的实践章节一一解析。

0.2 VL2特性

ZT的VL2提供了虚拟的普通二层网络,是以类似于VLAN的方式在VL1层传输的。其功能与普通二层网络类似,支持广播,多播,ARP,NDP等常见协议,拿来单纯组网是肯定没问题的。这里就不展开说了。只不过需要指出有意思的一点,即使可能你的网络出口MTU只有1500,但ZT的VL2的丝毫不受影响,其MTU支持9000+!也就是支持巨型帧!这对于提高带宽利用率,以及对部署软件隧道(如SRv6)的支持,无疑是件好事。

本节关于ZT白皮书就介绍到这里,后续我们将开始实践,开始搭建一个属于自己的ZT网络!

发表评论