路由是ip网络中极端重要的一块,它管理着整个网络的地图,而路由器则是基于这张地图指挥交通。路由器不仅仅指挥交通,由于ip网络本身没有前向/后向拥塞控制机制,路由器就负担起了这个工作,一般的流控都在路由器上进行,无论怎样,路由器的首要用途还是指路,手工配置路由当然是可行的,但是面对一个很大很复杂的网络,任何手工配置的指望都将枉然,于是自动配置就成了必然的选择,因此路由协议就出现了,路由协议就是在一个网络内动态计算路由的协议,它的参与者是整个网络的几乎所有路由器,计算平台就是整个网络,计算以及数据传输标准路由协议本身,因此运行着的路由协议将整个网络作为一个整体,整个网络成了各条链接作为总线的一台分布式计算机系统,因此注意,单独一台机器上运行路由协议是没有意义的。每个路由协议的背后都是一个算法,一个求最短路径的算法,因此很容易将路由协议和该协议所使用的算法分离开来分步骤研究,协议的标准规定了消息的生成,传输,保存格式,而算法则导致了消息的产生。
RIP和OSPF是常见的两种路由协议,它们都是IGP协议,另外还有BGP协议,这些协议在cisco的教程上讲述的太清楚了,特别是CCIE的路由交换的教程,这本书十分精彩。 因此本文就不再细说协议和算法本身了,而着重说一下一些背后的思想。OSPF没有环路的依据是最短路径树的计算过程,也就是Dijkstra算法,该算法生成的是一棵树,肯定是没有环路的,正是由于每台路由器都是在本地自行计算路由才避免了盲目的更新路由表,所有的路由器所共享的仅仅是所有链路的状态,也就是一张全局的图。基于全局的图计算出来的路由肯定是全局一致的。反之,RIP的路由信息来源完全不是自己计算的,而是别的路由器告知的,因为计算路由本身就要考虑自己所在地的因素,所以仅仅依靠“距离+1”而无条件接受其它路由器的路由信息未免有些盲目,这就是眼见为实,耳听为虚。不过RIP的噩梦在理想情况下是不存在的,只是效率由于广播完整路由信息而略显低下,可是任何网络都不是理想情况,宕机,更换ip地址等等事件很多的。
依靠原始元素重新计算的路由一定是最可靠的,而从别处得到的直接路由或者得到的间接路由加以加工而成的路由都是不可靠的,因为后者很难把握全局的场景信息,因此最终的路由就可能会略带偏激,狭隘。
看得出来,RIP协议使用的是Bellman-Ford算法,和Dijkstra算法一样,都是用来求最短路径树的,如果仅仅在算法层面比较的话,RIP和OSPF只是一棵最短路径树的不同实现,但是它们的区别不止这些,RIP中,路由通告的过程就是Bellman-Ford算法执行的过程,算法是分布式执行的,所有的路由器参与了算法的执行,最终的结果也是所有机器一起参与计算的结果,而OSPF中,通告的仅仅是链路状态,算法执行是在每一台机器上完成的,并不是分布式的。不论哪种算法,算法的执行过程都被认为是无干扰的,Bellman-Ford算法和Dijkstra算法在这一点上是一样的,最短路径树生成的过程中,节点不能增加,减少,权值不能变更...在路由计算的过程中虽然同样也要求这些但是却无法保证这些,因此OSPF的方式带来了很大的稳定性,因为它采用类似触发更新的机制第一时间报告任何变化,然后各个路由器在得到链路变更通知并且更新了自己的LSDB之后,用CPU的速度快速计算出一棵新的最短路径树,而RIP却完全不是这样,链路的变更平摊在周期性的路由信息通知之中,因此算法全部执行完的周期特别长,甚至根本就没有执行完的那一天,如此长的周期内网络任意一个位置链路变化的可能性都很大,因为算法的不稳定性增加,虽然单独为链路崩溃等严重问题设计了毒性逆转,触发更新以及水平分割等机制,但是对于一般的链路变更还是需要等到路由信息通告周期的到来,但是,如果网络理想化,RIP最终也会在每台路由器中生成一棵最短路径树。
Bellman-Ford算法:
Bellman-Ford算法是一种很笨但是却很简单的单源最短路径树算法,它的实质就是对所有的距离进行遍历,比较,循环树的高度次遍历每一条边,将所有的节点尽可能加入已经确定的最短路径树中,就这样一层一层地加入,直到所有边都被包含在树中,可见这也是一种贪心算法,和Dijkstra算法不同的是,Dijkstra算法是基于点的,而Bellman-Ford算法却是基于边的,这种区别和求最小生成树的Prim算法与Kruskal算法的区别是一样的。
Bellman-Ford算法(以后简称BF)求解最短路径树是分层进行的,这一点和同样基于边的最小生成树Kruskal算法是不同的,后者最终求得的是一个全局的结果,而BF是基于单源的。因此BF需要算法围绕这个单独的源来进行,设点数为V,在V-1次的遍历-比较(一次迭代)中,每次迭代都扩张一层,一些边会被加入到既有的树中,如果此时迭代结束,那么无疑既有的树就是部分点最短路径树,而其它的边均被抛弃了,如果不抛弃其它的边就需要继续迭代下去,因为可能经过其它的边会有很短的路径存在,无疑,整个迭代过程就是截肢的过程,一开始的时候,从源可能有N条路径到达每一个节点,Kruskal算法的目的就是对每组路径,截去N-1条路径,仅仅保留一条最短的路径,这就是最短路径树。
单源就好像一个爆炸点,和Dijkstra算法不同的是,这次它是将尽可能短的边一条条吸引过来,而不再是将点一个个吸引过来,第一次迭代的时候,无疑和源直接相连的点都被连进了既有的树,如果不考虑别的点,这些点到源的距离肯定是最短的,毕竟只有一条路,这可以作为归纳法的初始条件,那么归纳假设是当第N次迭代后,所有的连进既有树的节点到源的距离都是最短的,考虑第N+1次迭代后的情况,无疑N+1次迭代后,又将有很多边被连接进既有的树,这是因为第N次迭代为很多边铺了路子,本来游离的边通过第N次迭代加入的边可以不再游离了。用反证法,假设N+1次迭代后有一条边(Nv,v)的起点u联入了N次迭代的结果边(Nu,Nv)的终点Nv,并且它不是最短的路径,假设另一条边(Nr,v)连入N次迭代的结果边(Np,Nr)的终点Nr才是最短的边,我们试着推翻这个假设,考虑第N+1次迭代过程,由于遍历所有的边,Distance(v)1=Distance(Nv)+weight(Nv,v)和Distance(v)2=Distance(Nr)+weight(Nr,v)是需要进行比较的,如果Distance(v)2更短的话,(Nv,v)是不会被连入树的,连入树的应该是(Nr,v),这个矛盾推翻了假设,因此Bellman-Ford算法是正确的--数学归纳法:一个初始条件,一个关系。
那么还有一个问题,为何仅仅迭代V-1次呢?从Bellman-Ford算法可以看出,每次迭代都会加入一层的边,使得既有的最短路径树的高度增加1,于是这棵树最终的最大高度就是迭代最多次数,最大高度是多少呢?极端的情况一棵树退化成了一个链表,于是最大的高度就是V-1。可是这是可以优化的,虽然V-1是最大的树高度,可是极端情况毕竟少见,很多的树都是扁平状的,这样迭代很少的次数即可。怎么判断何时迭代可以退出呢?如果迭代一次后,没有任何的新边被加入既有树以及没有任何旧边调整在既有树中的位置,那就说明已经没有什么可以调整了,全部迭代即可完成。对于RIP协议来讲,这个优化等于无物,因此周期性的发送路由信息通告报文的目的就是发现边的调整和新边的加入,记住,RIP的运行过程就是Bellman-Ford算法的执行过程。