当一堆设备想要互相通信时,网络就出现了。网络可以小到两个设备,也可以大到整个互联网。当规模逐渐变大的时候,各种不同的技术就被发明出来,最终形成了今天谁也闹不清楚它是怎么连起来的但是就是连起来了的互联网。互联网是个奇迹,因为任意给你一个IP地址,不管这个IP地址地理上位于哪里,你和它之间有多远的距离,你都不需要关心,就可以和对方建立双向通讯。
从最小的情况说起,两个设备想要通信。显然,一条双向链路就足够了,而且你们俩互相都知道这条链路对面有且仅有一个设备。需要任何地址吗?不需要的,吼一声就行了:“喂,你。”对面传来:“哎,啥事儿,对面的?”这就是最简单的点对点通讯。
当三个设备想要通讯的时候,建立三条点对点链路就可以了。四个设备需要六条,五个设备需要十条,这就开始吃不消了。在此忽略掉早期的组网技术(令牌环!),现在的做法就是一股脑把所有的设备往一条总线上一接。总线是需要地址的,我得知道我说这句话是给谁听的,谁说了一句话是给我讲的。继续忽略早期的组网技术(IPX!),IP网络横空出世。在一个网络内部,每个设备需要一个唯一的IP地址,这个地址可能是32位也可能是128位的,这不重要,只要唯一就行了。在这个网络内部,任何人都可以嗷一嗓子:“哎所有人听着!”(组播/广播)也可以:“小李,这儿有你一封信。”(单播)
有时房间不够大,需要几个小房间打通来办公。每个小房间就是一个交换机,交换机与交换机之间互联的时候需要额外注意不要兜圈子(A房间穿到B房间然后穿到C房间然后不小心回到了A房间然后继续穿到B房间然后……),因此交换机使用生成树协议(STP)来规定递件的路线。总之,这就是一个大房间,里面熙熙攘攘挤着几十个人在办公,关系都挺紧密的。
上面这个小网络可能在信息处用得好好的,但是当网络规模开始变大,现在保卫处人事处各个都要来互联,不能吼一声小李然后全单位都停下来应一声。于是每个科室需要隔离起来变成子网,广播只能在子网内部进行了,跨子网需要穿过科室大办公室的门卫(gateway/网关)。现在需要寻址的时候,不能只叫“小李”了,要叫“保卫处的小李”,其中“保卫处的”是网络前缀,“小李”是主机ID,不过一般来说没人在意这个区别,只要记住“保卫处的小李”就可以吼到那个胖子就行了。网关除了帮科室看大门的,也有顺便帮顺路的科室递下手的,甚至有专门负责倒手的,这种力气够大可以帮人递件的路由器叫做transit router。
既然一个办公室是一个封闭的网络,在更大规模的单位网看来每个科室其实就是一个点,所以问题转化为N个点之间怎么建立正确和经济的互联网络。显然子网不多的时候还是几条点对点连接就可以了,但是规模大了,需要挂总线,或者请求transit。总线在这个时候并不是很好用,因为总线需要全通(任意两点间可以线速通讯)是很不经济的,而且任意两点可能线速还不同,因此这个规模上又回到了横七竖八的点对点时代,几个科室合计合计画了一张路由图出来(保卫处要经过信息科转手送人事处),这张图是静态的,画好之后每个网关看了看自己在图上的位置,记住自己要和哪些网关打交道,这个应酬列表就是静态路由表。
有一天,单位一把手发话了,整个单位大楼,全都连起来。这下麻烦了,整个单位几十个科室,除了经常打交道的,别的科室老死不相往来啊,还时不时有科室组建和裁撤,手工维护路由变得不太实际。动态路由协议这个时候被制定出来,路由图不再是手工绘制的,而是网关们互相商量(发现)出来的。用OSPF为例,发现过程就是一个网关站在门口,往左边吼了一嗓子:“我这儿保卫处,那边是谁?”对面吼回来:“我这儿档案室,我还可以帮你递给会计科和宣传部。”然后网关往右边又吼了一嗓子:“我这儿保卫处,我左边还有档案室、会计科和宣传部,那边是谁?”对面吼回来:“我这儿人事处。”“我这儿办公室。”于是保卫处网关写出了自己的路由表:我左边是一条走廊(点对点),走廊另一头是档案室,而且会计科和宣传部在档案室后面;我右边是一个门厅(总线),人事处和办公室的网关都在那儿蹲着。每隔一段时间,网关就得这么吼一顿,不断更新路由表。
然后邮差来了,他绕着办公楼走了一圈,说你们来个人收一下信。收发室老头嗷一嗓子出来了,给我给我。邮差问:“你这是江南皮革厂不,我这儿有寄给你们小王的信,给你了。”收发室老头说:“给我吧。另外我这儿有我们厂里黄鹤寄给他小姨子的信,你给带走。”邮差并不关心厂里的组织结构怎么样,路由怎么走,因为江南皮革厂是一个自治系统(Autonomous system),邮差只要记得这个自治系统的门牌号(ASN)是12345就可以了。收发室老头是边界路由器,坐在AS和AS之间。在这个例子中,江南皮革厂只有一个对等连接(peer),而这个peer是它通往整个外界的唯一渠道,因此邮差就是其默认路由,任何不在本系统内的东西都交给默认路由就可以了。
在现实世界中,只有一个peer的AS往往是没有独立门牌号码的,因为其上级AS是它的唯一通路,只要上级AS知道怎么到达这个AS就可以了,互联网上其他AS是不需要知道这个细节的。此时,上级AS是下级AS的唯一ISP,通常下级AS只需要设定一个默认路由就可以了,下级不需要关心全球的AS互联信息。上级AS会把下级AS的IP段直接合并到上级的IP段中,其他AS需要和下级AS通讯时只是扔给上级AS,其他就属于上级AS的事情了。最常见的家用网络结构就是这种简单的单ISP边界路由后面挂个交换机,接个几台设备。
在讨论AS互联之前,需要先讨论一下为什么会出现AS这种东西。从前面的讨论中我们可以发现,每当网络规模扩大一级的时候,只需要把原来更小的那个网络缩成一个点,然后再加一层汇聚就可以了。这个结构,是一棵树,树的最顶端是一个或者一组最高级的互联节点。然而这个结构有一个最显然的问题,就是树越靠近顶端,就需要transit更大规模的流量,而且一旦高级节点挂了,下面的所有子树就会裂解成独立的子网。在小规模的情况下,这是很自然的选择,但是规模大了就肯定撑不住了。
为了增加互联带宽,避免中心出现,各个节点之间需要增加更多的连接,形成一个大量互联链路的网格(mesh)。由于mesh不存在中心结构,所有的节点都可能出现互联(实际上也要考虑地理限制),因此每个节点都需要设定边界,边界内就形成了一个黑箱(AS),并且分配全球唯一的节点ID(ASN)。由于AS的边界是预先定义(也就是人手分配的),这样运行全局算法的开销才能控制到合理范围内。与小规模时尽量剪枝(STP和OSPF的目标都是建立最小生成树/最短路径树)相反,这个规模的网络是尽量尽量利用每一条可用链路,因此需要一个完全不同的算法。一个伟大的协议——BGP——诞生了。
BGP(Border Gateway Protocol)顾名思义就是只在边界路由器之间运行,其特性包括:尽量高的可靠性(使用TCP),算法开销尽量低(peer全部人工定义而不是自动发现,节约开销,反正跨AS链路也没几条),快速的算法收敛,当然还有去中心化。
每个单位需要到邮局去申请自己的门牌才能加入到国际邮政网络中,然后为了照顾各方面的感情,世界上一共有五个这样的顶级邮局(Regional Internet Registry),它们同时负责IP和ASN的分配。在RIR之下还有LIR(Local Internet Registry),一般来说每个ISP就是一个LIR。相对于IP地址,ASN其实是需求更加稀少的存在,因为AS才是真正的地理集中的一个组织,而IP则可以天马行空,所以正如一开始IP地址只有32位一样,ASN最早只有16位,后来才升位成32位。行文之时,全球一共也只有8万多个AS,但是有大约100万个IP段。
回到之前江南皮革厂的例子。邮差是默认路由,但是“默认路由”其实是不存在的概念,邮差们必须知道每一个IP段怎么走。两点之间一定是存在路径的(否则会网络不可达),但是因为是mesh,可能会不止唯一一条路径。此时BGP需要进行一系列的计算,来试图寻找一条最优的路径(不一定最快,不一定最大带宽),然后把包送给这个路径上的下一跳。不同于OSPF,最优路径并不全网共识,当包送给下一跳时,下一跳却可能算出不同的最优路径,因此包最终可能会走和出发点计算不一样的路径。并且因为大量的信息在路由生成的过程中无法传递过去(信息量太大了,只送出去最基本的信息),因此本地计算出来的最优路径也可能根本就不合理,导致包莫名其妙从地球另一边绕一圈回来的情况。全局路由优化是一个世界性难题,到今天还没有很有效的办法。
运行BGP需要首先定义本AS的边界及互联关系,这个过程叫做peering。与家用网络不同,mesh里面每个AS都可能和很多个AS进行peering,而且每个peer都是能够transit的(不能transit的AS比如你家,一般来说没有全局ASN,不参与BGP),也就是说理论上你发包给任何一个peer最终它都能到达目的地。BGP根据一些规则计算出某些目的IP应该走peer A,有些走peer B,效果会比较好。BGP机房其实就是一个机房同时连接几个ISP(电信、联通、教育网……),然后根据规则决定这个包走哪条。然而反过来,因为BGP不存在全局共识,你一个包发给了电信,回包可能从联通回来了,卡死在回家路上。因此BGP不但要接收全局的路由信息,更重要的是怎么把自己的路由信息尽量准确地发出去(announce)。
BGP广播的目的是让其他AS知道有这么一些IP段在我这里,如果有发给这些IP段的包,请送我这里来。然后每当这样的广播向外传播一跳,中继AS就会根据它的其他peer的状况决定一下反向路由,然后把这样的路径再次向外传播。可见BGP的内容并不只是“保卫处在我后面”这么简单,而是“从我到保卫处应该先过收发室,再过信息科。”下一跳会继续将这个路径延长,直到这条路径最终到达一个AS,这个AS的所有peer都表示已经有了关于这个源AS的路由信息为止。换句话说,全球每个IP都知道了到达这些IP的路径,这就是互联网可以两个IP之间都可以双向通讯的奥义。之所以要把整条路径都广播出去,也是因为不存在全局共识,能多送出去点信息就多送出去点,这样万一路径中间成环了可以尽快发现,或者断了,全网震荡的时间更短,因为全局“大体上”是有这个源AS的拓扑信息的。
然后就发现好像什么地方不太对。一个transit AS必然有两个或以上peer,如果这两个peer都连接在同一个路由器上,问题是很显然的,从peer A来的transit包,发给peer B就可以了。但是如果peer A和peer B不在同一个路由器上呢?假设peer A连接在router A上,peer B连接在router B上,那么这个包必须穿过AS内部的路由网络,以便从router A到达router B。router A是怎么知道如何到达router B的呢?
回到AS内部,OSPF这类IGP(Interior Gateway Protocol)相对于BGP这类EGP(Exterior Gateway Protocol)来说,是专门为AS内部建立路由表而设计的,因此存在一些并不适合外部使用的假设。虽然道理上讲,一个包从peer A到达了router A,它一定可以穿过IGP建立的内部路由到达router B,然后发给peer B,这个过程peer是不需要干预的。但是对于router A和router B来说,它们俩会很困惑。router A和peer A建立了peering,当它收到包的时候,它会想我代表的就是这个ASN啊,但是我明明没有通往peer B的链路(因为它并没有和peer B建立peering),也就是说本AS并不能到达peer B,为什么我可以发给peer B包?
这种IGP和EGP之间信息的不对称将会导致EGP无法正确完成AS内部transit的描绘,因此需要一个桥梁可以把两者整合起来。因为EGP的绝对主力是BGP,所以这个协议还需要尽量和BGP兼容。或者,干脆就是BGP本身呢?
于是BGP就分裂成了iBGP和eBGP两个子类。eBGP也就是传统的用于EGP的BGP,而iBGP则是使用BGP协议但是仅仅在AS内部和IGP交换数据用的特殊用法。说得玄乎,实现上其实特别直白:设定一个peer,其ASN和本路由器的ASN相同。当BGP发现这种同ASN间互联时,就会询问IGP:“我怎么到达router B呢?”然而又同时假装IGP不存在似的,和另一个路由器直接BGP交换EGP数据,仿佛两个路由器是点对点直连的一样,从而使得router A和router B即达成EGP的一致性(都知道本AS是同时peering peer A和peer B的了),又能利用IGP的路由信息实现互联。
到这里,江南皮革厂终于可以实现每个科员都可以和世界上任何一个IP通讯了。为了实现这个目的,厂里需要同时运行OSPF、iBGP和eBGP。
8万多个AS在互联网上互相通告来通告去,这个过程是没有任何权威机构进行管理的,也就是一个完全的去中心化系统。去中心化系统的优点是没有办法仅破坏系统的一部分,来瘫痪其余的正常部分,因此没有一个组织可以因为故意或者失误而破坏互联网。
是吗?
答案是否定的。互联网从未被设计为一个可信任的系统,互联网上的各个层级都存在严重的安全问题,这些问题来源于早期设计缺陷。为了解决去中心化系统的权威问题(即信息是可信任的),高级的协议已经设计了一系列的半中心化或者中心化修正方案。例如,HTTP通过给网站颁发证书的方式来证明这个网站的所有者信息,而DNS则通过DNSSEC来提供未被篡改证明。然而,更底层的BGP,尚未形成这样的广泛认可的安全机制。
破坏互联网的方法出人意料的简单。由于互联网是一个mesh,这也就意味着任何一个peer都存在可能连接到任何一个你想要到达的IP,但是当一个peer声明它可以很低成本到达目标IP的时候,它是可以说谎的。目前有两种说谎的方式,两种都实际发生过互联网崩溃级别的灾难。
第一种是声明本AS包含目标IP。有一天邮差来到江南皮革厂门口,说哎你这儿都有些什么科室啊?门卫老头说,我这儿有保卫科、人事处、宣传部、紫光阁、档案室。邮差一听很开心,哎呀我这儿正好有发给紫光阁的信,你拿去。江南皮革厂是不可能拥有一个北京的IP地址的,但是信已经送进来了,要么查无此人退回去,更多的情况是直接扔垃圾箱了。如果遇到敌意AS,直接伪造IP地址也是可以的,导致发给别人的包被敌意服务器截获。这类事故发生了好几次,基本上每次都发生在敌意AS上(也就是说是故意试图拦截、窃听等)。由于BGP是全网大体上知晓其拓扑的,所以当事故发生时(比如某某网站突然全球打不开啦),可以非常快定位到始作俑者,然而始作俑者往往一句“哦配错了不好意思啊”就搪塞过去了。
为了减少这类攻击/失误,IRR(Internet Routing Registry)被发明。IRR是一个可以去中心化但是目前是中心化的数据库,试图维护一些关于路由信息的附属资料,例如一个IP段是谁注册的,目前这个IP段应当是由哪个AS承载,等等。当BGP通告发出时,接收方可以查询IRR记录以确定AS和IP的对应关系(浙江的工厂咋能有个北京的IP段呢?)从而丢弃非法的广播。由于IRR信息是静态的(相对于路由表来说),因此其信息可以被离线证明,然后通过密码学进行在线保护。如果大部分的transit AS都实现了强制IRR检查,就不会再发生AS错误地广播不属于它的IP的情况。
那万一一个敌意AS说这个IP不是我的,但是我到目标AS非常近呢?这就是第二种说谎,伪造不存在的peering,使得别人相信去往这个IP的权威AS的最近的路,是通过这个敌意AS进行transit。虽然未必骗得到整个互联网,但是靠近敌意AS的AS很可能被劫持。
很遗憾,目前还没有办法防御这种攻击。毕竟ISP们在提高互联互通的道路上还是非常努力的,有时,两个地球背面的AS会决定拉一条几乎直线的海底光缆,使得本来两个遥远的AS突然变成peer。而且不光是凭空伪造peering这种事,两个真的peering的AS,敌意AS也可以在中途篡改流量,毕竟你通过我transit,信件就在我的掌控之下了。所以虽然说现代互联网想了很多办法试图阻止IP伪造,但是当你遇到国家力量的时候,你的数据可以完全被敌意窃听和篡改,哪怕你看着地球仪,完全想不明白你到目标的流量,怎么会在地球另一端的毫无联系的路由器上绕了一圈。
截止到这里,路由的核心一直都是路由表,不管是静态的,还是动态的,路由的最终目标都是维护一张合理的路由表,然后每当有一个包需要transit,就取出其目的地址,查一下路由表,找到下一跳,发出去。
“取出起目的地址,查一下路由表,找到下一跳。”这是一条规则!如果,路由的时候,允许不同的规则,世界会变成什么样?这个念头一产生,整个路由技术就出现了巨大的变化,因为从理论上,路由表本身就可以被逐条拆解为规则:如果目标地址是毛纺厂,走奋进路;如果目标地址是牛马市,走三环;其他的都交给建外大街上一个叫老王的收发室老头。然而,规则这个东西,可没有说必须得是 (目标地址->路径)这个写法。(源地址)可不可以?(目的端口)可不可以?(时间)可不可以?都可以!
这种基于规则的,可能仍然保留路由表但是也可能不保留的技术,叫做策略路由(PBR:Policy Based Routing)。这个东西解决了困扰大家多年的一个问题:负载均衡。和业务负载均衡不同,网络如果带宽耗尽了,你再开一条一模一样的路是没有用的,因为根据目标地址,你只能走一条路,新修的路放那儿就闲置了。虽然目的地址可能是一个大块,你设法把这个大块拆散成一些小块然后分别走两条路,是可以解决问题的,但是且不说维护成本,就是单个IP都能比其他另外100个IP的流量总和还大这种情况,真是没法弄。
想要负载均衡,本质上就是一个合适的分类,使得一些包走线路A,另一些走线路B,使得两条线路的流量正好和其最大带宽成正比。假设一个理想情况,所有的包一样大,那么我们只需要计数就可以了。如果线路A带宽300M,线路B带宽200M,我们给每个包都计数,AAABB AAABB,或者ABABA ABABA,打上标签。路由策略写成:任何带标签A的包走线路A,任何带标签B的包走线路B,没标签的查路由表。
然后发现好像包大小不一样啊……其实也不难,做一个带宽监控,统计统计流量上不同包出现的概率,也可以算出合适的分配比例来。你看,统计学、机器学习,都可以拿来为路由技术服务了。而策略路由是不依赖路由表的,这东西不但可以写成一些简单的条件,甚至它就可以写成一段代码,其可能性就完全不受限制了。
遗憾的是到今天为止,策略路由还不像传统的路由表信息那样有成熟的交换协议。传统的基于目的地址的路由交换,世界上任何一个AS吼一句“这个IP段在我这里”,所有的路由器都会很快学习到“凡是去那个IP段的包都想方设法送到那个AS去”。然而策略路由,就拿最简单的双线负载均衡,你就没办法让回包一定从去程线路上回来,因为对方不知道你用的什么策略分类的啊,这个策略可不能像目的地址那么简单地交换给对方。
然后就出现了一个骚操作:那要不我们把策略包装成一个可以交换的信息?
(待续)