专栏名称: CSDN
CSDN精彩内容每日推荐。我们关注IT产品研发背后的那些人、技术和故事。
目录
相关文章推荐
新浪科技  ·  【#黄仁勋个人身价超英特尔市值# ... ·  2 天前  
新浪科技  ·  【#12306车票候补攻略# ... ·  2 天前  
36氪  ·  女掌门上任后,海天涨了430亿 ·  5 天前  
新浪科技  ·  【#特斯拉市值蒸发2012亿#】10月3日, ... ·  6 天前  
51好读  ›  专栏  ›  CSDN

BadVPN详解之组网原理剖析

CSDN  · 公众号  · 科技媒体  · 2017-05-02 14:08

正文

这可能是第一篇关于BadVPN原理的中文介绍,所以我恪守职责,希望能将BadVPN的原理阐述清楚,而不仅仅是为了欺世盗名取天下之先。


前言


我关注VPN已经多年了,有三类VPN让我觉得惊叹帮我理解了网络协议的体系结构。首先IPSec,I2TP,PPTP这种传统的VPN技术让我知道原来协议栈还可以如此的相互Over,然后就接触到了OpenVPN,如此一个简单的玩意儿竟然拥有如此强大的组网能力,我喜欢这个OpenVPN,一去就是三五年,期间这个玩意儿也为我带来很多现实的东西,比如奖金,比如加薪升职什么的,打心眼里,我觉得利用OpenVPN的理念,可以创造出世界第一等的VPN产品...直到我遇到了BadVPN!


当然,最基本的VPN就是IPSec VPN,然而要想配置好一个多点的IPSec VPN网络及其复杂和费事,最终EzVPN弥补了这个不足,它通过一个所谓的中心节点简化了配置。然而这一切都是“官方”的方案。落实到普通程序员可以就地使用的方案,没几个。OenVPN是一个,BadVPN也是一个。
       

我承认,前几年我被OpenVPN冲昏了头脑,竟然忽略了世界上还有一个叫做BadVPN的东西。都到了2017年了,我才第一次注意到它,这多多少少有点让自己觉得悲哀。


BadVPN是另一个VPN


为了能快速理解BadVPN的原理,我准备从OpenVPN开始,当你知道了OpenVPN有什么问题的时候,就会更加容易理解BadVPN是如何解决这些问题的了,而这些正是理解BadVPN的关键。我先来展示一个OpenVPN的结构图,假设使用TAP Bridge模式:




可以看出,OpenVPN有一个所谓的“中心节点”,其它所有的子节点都连接到这个中心节点。OpenVPN的这个中心节点身兼两职:


1. 提供总体的控制功能,负责与子节点密钥协商,分配虚拟子网的IP地址给子节点,推送策略给子节点等;
2. 同时作为一个对等的VPN节点存在,和各个子节点所携带的子网互联互通。


上述两点可以看出,第一点是控制平面的功能,第二点是数据平面的功能,二者完全在一个链路上传输,这意味着什么?这意味着要精心设计OpenVPN的协议,以区分到来的数据包包含的是控制信息还是数据。
       

OpenVPN协议区分了数据是控制面的数据还是数据面的数据,然而这种区分是带内实现的,也就是说通过协议头的字段来实现的,姑且不说封装这个协议头会带来什么开销,仅仅在OpenVPN进程中去多路复用并行处理控制平面和数据平面就够复杂的了。
       

按照传统的网络设计观点,中心节点即服务端就是一个交换机,而各个VPN子节点客户端就像是“该交换机端口的另一端”连接的各个主机或者级联交换机,这是一个典型的星型拓扑。然而按照SDN控制平面的观点来看的话,中心节点就是一个控制器,它控制了整个的VPN网络转发逻辑,所有的子节点要发送数据必须无条件转发到中心节点,让中心节点决定数据转发到何方,这意味着哪怕是子节点客户端之间互相通信,也必须经由中心节点来转发。
       

不管站在什么立场上来看,OpenVPN的中心节点都是一个单点,不管是从效率还是从可用性上来权衡,这都不是一个优雅的方式。说点题外话,幸亏我个人精通Linux网络,可以通过iptables,iproute2,多线程改造OpenVPN,bonding TAP网卡,Keepalived等措施来破除这种限制,消除种种效率和可用性上的威胁,我才得以驾驭OpenVPN,不然的话,用OpenVPN来组网还真的限制多多。即便我有能力Hold住存在单点问题的OpenVPN,最终我还是失败了。我已经把OpenVPN堆积的不成样子了,迫切需要一个新的思路。

OpenVPN的中心节点扮演了过于重量级的角色,这导致了任意建立VPN通道的节点之间的关系不可能是对等的,OpenVPN必须以主从方式建立VPN通道。这就主从结构的限制使得OpenVPN在组网方面很难构建多点对多点的网状拓扑。

那么BadVPN有何不同呢?为了与OpenVPN做对比,我还是先来展示一个BadVPN的总览图:




所有的节点综合起来构成了一个巨大的交换机,大家一起完成交换机的功能,各个VPN子节点(为了对比,我依然沿用OpenVPN中心节点,子节点的术语)就是交换机端口本身,而不再是“端口对端”连接的设备。其中,中心节点不再负责实际的数据转发,而仅仅作为一个“维护这个交换机组成部分”的控制器存在,中心节点负责以下的事宜:


1. 记录该VPN网络中拥有多少节点主机,即“该大交换机中有多少端口”以及“端口的添加,删除以及通告”;
2. 构建子节点与中心节点之间,子节点与子节点之间的TLS控制通道,协商子节点之间的通信密钥。


这意味着各个子节点之间的VPN互通不再需要经过中心节点,实际上也根本就没有什么可以进行数据通信的VPN中心节点。VPN通信节点之间变成了真正的点对点通信,除非你配置中继(BadVPN的一种特殊运行方式,在权限受制的场景下使用,本文不讨论,详情参见BadVPN的Document),任何VPN之间的通信都是直接通信,不需要经过第三方。


这意味着什么?这意味着:


1. BadVPN的协议会非常简单;
2. BadVPN的处理过程会非常简单。


BadVPN将控制平面和数据平面做了带外的分离,不再仅仅是带内分离而依靠协议来区分。关于这一点,后面会详述,下面的小节我们在感官上认识一下BadVPN的原理。


BadVPN的运行原理图解


长篇大论终究会因为文笔不好而适得其反,所以还是通过画图具体来点情景分析为好。我分两种场景来描述BadVPN的行为,即非打洞场景和打洞场景,先看非打洞场景。


1. 普通情形的情景原理分析


  • 控制面的生成


  • 第一个节点连接中心节点时



  • 第二个节点连接中心节点时




  • 第三个节点连接中心节点时





三个节点都连接上了中心节点,本文中描述的“BadVPN构成的巨大交换机”就已经生成了,我们可以看到,是中心节点维护了这个巨大的交换机。
       

这个时候,这个交换机是空的,就像刚上好架,插好网线,加电完毕后的状态一样。我们知道,以太网交换机是自学习的,本文中叙述的“BadVPN交换机”自然也不例外。那么下面我们就看一下在有实际数据传输的时候,这个交换机到底是怎么运作的吧。

  • 数据面的运作


  • 子节点A或其携带子网需要ping子节点B的TAP地址,发送ARP




  • 子节点A收到子节点B的TAP回复的ARP Reply




  • 子节点A封装ICMP Ping在加密通道上发给子节点B




附:我的改进


其实我不很赞同BadVPN里面的Peer-Macs表的设计,我觉得应该是三元组设计,即Peer-Vips-Macs表,详见我下一篇文章,讲述我自研的一个VPN,可能介于OpenVPN和BadVPN之间,也可能是BadVPN介于OpenVPN和我的VPN之间...谁知道呢?总之,皮鞋爆炸。


2. NAT后需要打洞的情景原理分析


我不会在这个小节里重复上面常规场景里的图解,我觉得我只描述UDP打洞场景就好了(我直到现在依然记得江琦博士TCP打洞全世界第一,希望长春Leadwell做SIP的同事见文与我联系,想念你们)。
       

打洞原理非常简单,我假设中心节点是X,而子节点A和子节点C均在NAT之后,那么打洞过程如下图所示,分两步:


  • 第一步:让中心节点X分别记录下A和C的外网地址和端口:



  • 第二步:彼此尝试并最终成功连接




这样,即便子节点处在NAT之后,依然可以建立正常的点对点互联,并且如果二者在同一个NAT后面,还可以建立直连互联,这真是强大!事实上,这种模式早就被我们使用了好多年,伟大的腾讯公司早就使用这种方式运作QQ了。难道QQ不是一种特殊的VPN吗?QQ是什么:


(1) QQ对等客户端双方加密通信;
(2) QQ自带打洞功能,处在不同NAT后面的客户端依然可以直接建立连接(若真不行,经由服务器中转是补偿方案);
(3) QQ自带打洞功能,处在同一NAT后面的客户端可以不经NAT而直连。


3. 对协议设计的影响


BadVPN无需在协议中区分控制信息和数据,BadVPN是完全的带外控制,而不像OpenVPN那般的带内控制。
       

我们看看BadVPN的数据通道协议,即子节点之间互发数据的协议,非常简单。它经由下图所述的封装即可传输:




因为数据发给谁是“交换机查表”的结果,不会错的,所以无需在接收端进一步判断。接收端收到之后,可以信任一定是发给自己的(当然为了安全起见,还是要判断一下的,但这不是根本)。
       

这有点类似于全双工交换式以太网的运作方式,一条网线连接且仅仅连着两台设备,如果这条网线上有流量,那一定是这两台设备之间的流量,如果其中一台设备发出数据,那么接收端一定是网线另一端的设备,这就是点对点全双工通信。
       

除了数据通道不再需要中心节点中转,对于控制通道协议而言,我们依然可以看到这里解除控制和数据的耦合后的好处。仅举一例,即子节点之间的密钥协商过程。假设子节点A和B同时连接于中心节点X,当B节点接入后,B要和A之间建立SSL通道,这个SSL通道是SSL中的SSL,分为A到X以及X到B两段,如下图所示:




然后A和B之间的对称密钥就是在这两段的SSL通道中协商出来的。协商方式非常多,可以让A或者B随意一方生成一个密钥,经由A-X-B或者B-X-A的SSL通道传输给B或者A,也可以使用简单的DH算法来直接交换生成密钥,总之,就跟IPSec的IKE两阶段协商一个道理,第一节点通道要为第二阶段提供保护。
      

这种事情在OpenVPN就会非常复杂,因为在数据通道层面上,必须将整个控制通道分为不同数据通道的不同控制通道,而不是一个统一的控制通道,因此,对于OpenVPN而言,只能将两种通道彻底分开,然后依靠“类似深度包解析”的方式来判断哪个数据属于哪个通道的控制面还是数据面,从而导致了大量的冗余。这有点类似于数据库表的设计,要通过键沟通的方式来建立关联,而不是把所有数据都塞入一张复杂的大表中:




附:命令行参数解析


我说过,BadVPN是靠点对点的方式完成VPN通信的,那么完成这种通信的基础就是底层的TCP/IP网络,显然需要对五元组。和OpenVPN的理由一样,BadVPN建议使用UDP来作为传输协议以防重传叠加。但是即便是UDP,也要确定哪个4元组不是吗?是的。
       

对于在文章《BadVPN详解之编译与运行》(http://blog.csdn.net/dog250/article/details/70343441)中的“运行”一节,命令行中的ext-addr参数所指定的IP地址和端口就是VPN数据通信的IP地址和端口,也是上面图示中中心节点向新接入的子节点通告的既已经接入的子节点的IP地址和端口,这个元组是控制通道的元组。
       

与ext-addr参数不同的是,中心节点的listen-addr参数以及子节点中的server-addr参数均指代控制通道的数据通信元组。


4. 对组网拓扑的影响


BadVPN一开始就是点对点的对等拓扑,中心节点不负责任何数据传输和转发任务,正如其实例文档所述:


NOTE: badvpn-server does not actually participate in the virtual network. If you want the server machine to be part of the network, run a local badvpn-client, like on other peers.


再次看一眼“BadVPN是另一个VPN”小节中的BadVPN总览图,会发现,子节点之间谁也不比谁更重要,大家完全是对等的。所以对于BadVPN而言,拓展整个虚拟子网变得超级容易,就像在一台以太网交换机的一个端口上连接一台主机一样。
       

BadVPN在配置中去除了TAP和TUN之间的区分,也不再BadVPN内部配置虚拟网卡的IP地址等。BadVPN直接帮你构建了一个数据通道,该数据通道由一个既已存在的控制通道所控制。所有的子节点均在一个虚拟出来的以太网中,所有人都知道什么是以太网,那就是只要把网线插入交换机,配上IP地址就可以互联互通的网络,BadVPN正是帮你构建了这么一个网络,非常简单!


我的遗憾


我遗憾自己没有在2015年9月前看到这个BadVPN项目。如果在此之前得知这个项目的话,我可能就不会迷茫,我会继续钻研技术细节而不是什么产品的市场场景,我可能会重构BadVPN而不是不可救药的OpenVPN。
       

但是一切都太晚了。我于2015年9月份放弃了自己的OpenVPN项目,背井离乡来到了一个对自己而言非常陌生的地方深圳,这里拥有来自全国各地(两湖两广居多)怀着激情和梦想的年轻人(以在大城市买房置业为目标),却连一条像样的快速路都没有(不拿堵车说事,深圳的快速路高架桥完全不敌广州,上海,毫无规划,基本也就是见招拆招),产业更是出其不意的单调,但这里的人却无比喜欢自己生活的城市,也是是他们大多数从自己的家乡到武汉等地上大学然后就到了深圳没在别的地方生活过的缘故吧,说实话,像我这种以文化享受为己任的多元化人,并不喜欢深圳这座城市。但是就因为我没有在正确的时间接触到BadVPN,导致了我无力进一步深挖VPN的架构,具有讽刺意味的是,我现在也成了深圳人,也开始喜欢上了这个地方,和我的性格比较类似,如果我不喜欢深圳的道路,那么我来自己建设它,所以如果我不干IT了,我希望能成为深圳的城市规划者,当然,这是后话。


我曾经的c2c组网方案


OpenVPN的主从模式限制了OpenVPN的组网灵活性。有一个实际的需求是建立N个VPN节点间的对等通信,这在BadVPN看来就是一个原始的配置,N个子节点同时连接中心节点即可,这样它们就自然而然建立了对等的P2P拓扑,然而如果使用OpenVPN,便不然。OpenVPN的方案需要太多额外的支持,比如要用Bonding,Bridge,iproute2,Policy routing,iptables,ebtables等工具助力完成配置,过程及其复杂。
      

这里只设一个题目,如下A,B,C三个节点需要建立对等的点对点连接,该如何?由于OpenVPN必须采用主从模式,那么A,B,C三点必须采用咬尾巴蛇的策略来建立互通,如下图所示:




显然很合理。那么如果是四个节点,五个节点,甚至更多的节点呢?显然环形的咬尾巴蛇策略就不可行了,那么要实现两两互通那该怎么办?典型且直接的方案就是每一个VPN节点均运行一个中心节点模式VPN,再运行N-1个子节点模式VPN,这样便可以彼此互通:




拓扑是搞定了,还挺和谐对称(PS:这是一个真实的案例拓扑,目前依然在一个规模想当的城域网上运行着)。然而你考虑以下的问题了吗:


1. 一个VPN节点上要创建多少tap虚拟网卡?
2. 如何实现多个节点间流量的隔离?
3. 由于每个节点均运行一个中心节点模式的VPN,那么策略怎么推送?
4. 要配置多少Policy routing路由,用以支持不同的子网之间的互通和隔离?
5. 要想完成上述第4点,需要配置多少iptables规则?
……
我相信,上面的清单要是列出来,很少有人可以瞬间理清头绪的。我记得当时我在做这个产品的时候,曾经无数次的跟同事阐述以上这些原理,最终都是无果而终,最终的最终,我自己都已然忘却,在我从OpenVPN相关的项目组离开后直到现在,有很多人问起过我关于OpenVPN的问题,恕不能答,因为我自己也理不清了...我每次都要看我之前写的那些博客或者笔记才能明白到底是怎么一回事,唉。总之,罪魁祸首就是OpenVPN的主从模式导致组网太复杂了。
       

如果使用BadVPN,则基本没有这么一回事了。BadVPN在搭建好之后,就是点对点的,无须设置任何规则即可所有节点互联互通:




以上就是BadVPN的大部分原理知识了。如果你还是不理解,请继续听我下回分解,在下文中,我将讲述一个我自研的一个设计。该设计可能比较垃圾,也可能比较比较爆款,但不管怎样是我的真情实感,在我遇上BadVPN之前就思考了一段时间,最终看到BadVPN之后,发现确实如此的相类似。


不是英雄所见略同,而是正确的道路往往只有很少的几条。且听下回分解!


本文来自CSDN博客:http://blog.csdn.net/dog250/article/details/70343660