计算机网络
知 识 梳 理
(第一版)
建议先修课程:数据结构。
配套教材:
Computer Networking - A Top Down Approach, 7th edition James F. Kurose, Keith W. Ross
参考书目:
1、计算机网络(第8版) 谢希仁 编著 高等教育出版社
链接:https://pan.baidu.com/s/1GLDPR7p6UyDoWTY-e8s68w
提取码:0000
五 网络层——控制
5.1 简介
先简单说明路由器怎样转发分组。路由器通过路由算法构造(调整)路由表(上图便是一个示例)来决定路由,再根据路由表构造(调整)转发表,并转发。若路由表指出到每一台主机应怎样转发,表就会过于庞大;若指出到某个网络应如何转发,则路由表的条目数就能大大减少。路由表至少包含目标主机所在的网络号和下一跳地址。
虽然互联网的分组转发一般都基于目的主机所在的网络,但在大多数情况下都允许有特例,即对特定的目的主机指明一个路由。这叫做特定主机路由。特定主机路由可使网络管理人员更方便地控制、测试网络,同时也可在需要考虑某种安全问题时采用特定主机路由。在对网络连接或路由表排错时,指明到某一台主机的特殊路由就十分有用。
路由器还可采用默认路由(default route),以减小路由表的占用空间和搜索时间。这种转发方式在一个网络只有很少的对外连接时是很有用的。实际上,在主机发送IP数据报时,往往更能显示默认路由的好处:发送每一份IP数据报时,都要查找自己的路由表。如果一台主机连接在一个小网络上,而网络只用一台路由器和互联网连接,那么使用默认路由是非常合适的。只要目的网络是其它网络,就一律选择默认路由,提升了路由效率。
当路由器收到一份待转发的数据报,从路由表得出下一跳路由器的IP地址后,不是把它填入IP数据报,而是送交数据链路层的网络接口软件。网络接口软件使用地址解析协议(ARP,见第6章)把下一跳路由器的IP地址转换成硬件地址,并将此地址放在链路层帧的首部,然后根据该地址找到下一跳路由器。可见,发送一连串数据报时,这种查找路由表、用ARP得到硬件地址、把硬件地址写入链路层帧首部等过程不断重复,造成了一定的开销。
那么,能不能在路由表中不使用IP地址,而直接使用硬件地址呢?不行。我们一定要弄清楚,使用抽象的IP地址,本来就是为了隐蔽各种底层网络的复杂性,便于分析和研究问题,这样就不可避免地要付出些代价,例如:在选择路由时,多了一些开销。但反过来,如果在路由表中直接使用硬件地址,那就会带来多得多的麻烦。
在划分子网的条件下,分组转发的步骤是:
(1)【提地址】从数据报的首部提取目的主机的IP地址D。
(2)【考察直连网络】检查与路由器直接相连的网络:用各网络的子网掩码S和D进行AND运算,看结果是否和相应的网络地址匹配。若匹配,则把分组直接交付(当然,还需要把目标IP地址D转换成物理地址,把数据报封装成帧发送出去),转发任务结束。否则,就是间接交付,继续执行剩余步骤。
(3)【考察特定主机路由】若路由表含有目的地址为D的特定主机路由,则转发给指定的下一跳路由器。
(4)【考察一般的网络路由】对路由表的每一行(至少含有目的网络地址、子网掩码和下一跳地址),用子网掩码S和D进行AND运算,若结果与该行的目的网络地址匹配,则把数据报传送给指定的下一跳路由器。
(5)【考察默认路由】若路由表存在默认路由,则把数据报传送给路由表指明的默认路由器。
(6)【报错】报告转发分组出错。
再次强调,路由表并没有给指明到达某个网络的完整路径(先经过哪台路由器,再经过哪台路由器,等等)。路由表指出,到达某个网络,应当先到达某台路由器(下一跳路由器);到达下一跳路由器后,再继续查找其路由表,知道再下一步应跳至哪一台路由器。这样一步一步查找下去,直到到达目的网络。目前,路由表项目必须包括目的网络地址(及其子网掩码)和下一跳地址。
5.2 路由算法
理想条件下,路由算法选出将数据从发送方传输到接收方的成本最低的路径。当然,实际涉及的因素是很复杂的,比如:许多路由器会过滤数据包。路由算法给出的路由,只是特定要求下的较为合理的选择而已。
图论(graph theory)是路由问题的数学基础。一个图G由有序二元组⟨N,E⟩确定,记作G=⟨N,E⟩(有的资料记作G=(N,E))。N和E分别是结点集和边集。路由器等负责转发分组的网络设备用结点表示;连接结点与结点的边代表路由器之间的链路。每条边都是带权的,边权代表分组经过对应的链路需要花费的成本,可以是链路的物理长度、链路速率、经济成本,等等,也可以是这些因素的一个或几个的函数。此外,每条边通常都是双向(无向)的。
路径(path),是一个结点序列(x_1,x_2,…,x_p),p个结点的路径包含(p-1)条边(x_1,x_2 ),(x_2,x_3 ),…,(x_(p-1),x_p ),其中,x_1和x_p分别是起点和终点。
路由算法要做的就是:在网络中,指定发送方和接收方,求一条双方之间的路径,路径拥有某种尽量小的成本。
中心路由算法(centralized routing algorithm)在已知整个网络的必要信息的条件下,计算总开销最低的路径。这就要求计算之前获得足够多的已知条件,如:所有结点的标识符、所有的链路及其花费等。计算可以在每台路由器的路由部件上进行,也可以交由路由器连接的远程数据中心进行。具有全局状态信息的算法常称为链路状态算法(link-state algorithm),因为该算法必须考虑每条链路的成本(考虑链路状态)。
去中心路由算法(decentralized routing algorithm)则迭代地、分布式地计算所需路径。没有一个结点具有所有链路的开销的完整信息;相反,每个结点一开始只知道与自己直连的链路的开销。通过迭代的计算过程和信息交换,一个结点最后也能计算出到达若干个接收方的总成本最低的路径。后续将学习的一种去中心化路由算法叫做距离向量算法(distance-vector algorithm),因为每个结点维护一个记录了网络中的其它结点的花销的向量。去中心化路由算法与邻接的路由器交换需要的信息。
路由算法也可分为静态路由(static routing)算法和动态路由(dynamic routing)算法,分别也称非自适应路由算法和自适应路由算法。在静态路由算法中,路由改变得很慢,常常只有在人为干涉(如:编辑了一条链路的花费信息)时,才会导致路由变动。动态路由算法在网络拓扑或流量改变时,给出的路由一般也会变化。动态路由算法可以周期性地重新运行,也可在特定条件下(例如:链路的开销改变,或网络结构改变)自动重新运行。由于动态路由算法更容易受网络状况的影响,因此它们对路由环路(routing loop)、路由振荡(route oscillation)等问题更敏感。路由振荡就是路由的频繁变化。例如,某条链路频繁up / down(连接 / 断开),可能就会引起路由器不断重新计算路由。严重时,可以导致网络不稳定、不可用。
静态路由实现简单、开销较小;而动态路由较为复杂、开销较大。因此,动态路由选择适用于较复杂的大网络。
路由算法也可以按照负载敏感(load-sensitive)和负载不敏感(load-insensitive)来分类。在负载敏感型算法中,链路成本动态反映了拥塞程度。早期的ARPAnet路由算法是负载敏感的,算法的应用过程遇到诸多困难。今天的路由算法,如RIP、OSPF、BGP等,则对负载不敏感,链路成本不易受链路当前拥塞程度的影响。
在链路状态算法中,为了让所有结点都获得整个网络的必要信息,每个结点会广播链路状态分组给网络中的其它结点,每个分组包含它所连接的链路的标识符与开销。实际上,这经常通过链路状态广播(link-state broadcast)来完成。最终,所有结点都包含相同的、整个网络的总览。每个结点在这时都可以运行链路状态算法来完成计算。
Dijkstra算法属于链路状态算法,原是针对最短路求解设计,但可以直接套用于求得最小花费的路径。
Dijkstra算法(单源最短路)
用途:求连通图的定点到其余点的最短距离。
算法内容:
设边权为非负的带权连通图G(V,E),边权w(x,y)代表从x到y的边(x,y)的长(直接相连两点的通路长度),定点(源点)s,数组{d_|V| }代表定点s到各点的通路的估计最短距离。
记d_s=0,d的其余元素记为+∞。设集合U代表已经估计最短距离的顶点集合(先不要将s加入集合U)。
循环以下部分直到U=V(|U|=|V|):
对未加入集合U的其它顶点,选出d值最小的顶点x,更新其全部邻接点y(无论是否已访问):
d_y=min(d_y, d_x+w(x,y))=min(|sy|, |sx|+|xy|)
将x放入集合U。
算法结束。此时,{d_|V| }代表定点s到各点的通路的最短距离。
由于每次将新的顶点加入集合U之后,都要查找下一个未估计最短距离的顶点,因此时间复杂度按O(|V|^2 )估计。
Dijkstra算法是迭代的: k次迭代后,k个结点的最短路计算完毕。并且,在到所有结点的最短路中,这k条路径的长度最短。
若对上述算法稍加修改,额外进行一些必要的记录,则当链路状态算法结束后,对每个结点,我们都有沿源点到此结点的最短路的前驱;于是,可以构造出源点到每个结点的最短路径。在转发表中,可以为每个结点记录以最小开销到达每个目的地的下一跳需要到达的结点。
网络状态变化时,重新运行链路状态算法后,得到的路径通常也会变化。这种振荡不仅出现在链路状态算法中,还可以出现在基于拥塞或延时的链路测度算法中。若要避免这种振荡,可能第一时间想到的是:将算法设计为不依赖链路流量的。但这并不可行,因为路由算法的目标之一,就是避免经过高度拥塞的链路。另一个解决方案是:不让所有路由器在相同时间运行链路状态算法。这看起来是很容易实现的:当路由器运行同一算法的理论速率相同时,将开始运行的时间错开,就可以达到此目的。但是,研究表明,Internet上的路由器在此情况之下会发生自同步,最终使得路由器再次同步执行同一算法。避免自同步的一种方法是:让路由器随机决定发送链路通告的时间。
距离向量算法是迭代的、异步的、分布式的。“分布式的”是因为:每个结点从它的一些邻接结点中获取信息,进行计算,并将计算结果返回给邻接结点。“迭代的”是因为:算法将一直继续,直到邻接结点之间没有任何新信息可以交换为止。这种算法也是可以自行终止的,无需额外的停止信号。“异步的”是因为:算法不要求所有结点按照某种先后顺序(或者说,步调或节奏)配合。
设d_x (y)代表结点x到y的最短路长度,则最短路与著名的Bellman-Ford方程相关,即
█(d_x (y)=min┬v(w(x,v)+d_v (y))#(1) )
min┬v表示:在所有可取的结点v中取最小值。该方程的意义相当直观:找到从x到v的最短路后,如果继续取到了v到y的最短路,就得到了x到y的最短路程w(x,v)+d_v (y)。我们必须先从x的某个邻接点v开始,从x到y的最短路路程便为w(x,v)+d_v (y)可以取到的最小值。其中,必须取遍所有邻接点v。这之后,对d_v (y)继续按类似方法处理。
方程(1)的解可用于构造起始结点x的转发表项。设v^*为方程(1)中d_x (y)取得最小值时的邻接点。如果结点x打算向结点y通过最小开销路径发送数据包,则结点x应当先把数据包发送给结点v^*。也就是说,结点x的转发表中应当指出目的地为y时下一跳为v^*。(1)的另一个重要贡献是:指出了距离向量算法中邻接点之间通信的形式。
其基本思想如下:设网络G=<N,E>,每个结点x从D_x (y)开始,其中:D_x (y)是从结点x到y的最小开销路径的开销估计值,x,y都位于网络G中。DV算法令每个结点x维护如下的路由信息:
·对每个邻接结点v,从x到v的开销为c(x,v)。
·结点x的距离向量D_x=(D_x (y): y∈N),包含x到任意的目的地y的总开销的估计值。
·x的每个邻接结点v的距离向量D_v=(D_v (y): y∈N)。
在这个分布式、异步的算法中,每个结点时不时向它的每个邻接结点发送它自己的距离向量的副本。当结点x从一个邻接结点v收到一个新的距离向量时,它保存v的距离向量,并使用方程(1)更新其自己的距离向量:
D_x (y)=min┬v(C(x,v)+D_v (y)), y∈N
如果在此更新步骤中,结点x的距离向量改变了,则结点x将发送更新后的距离向量给它的每个邻接结点,邻接结点便可更新各自的距离向量。照此下去,结点x到任意的目的地y的开销估计值D_x (y)将收敛至实际的最低开销d_x (y)。
以下是一种距离向量算法:
算法中,与结点x相连的链路的开销有更新,或从邻接结点收到距离向量更新时,将自己的距离向量的估计值更新,并将更新后的距离向量发送给全部的邻接结点。但为了更新x的转发表中包含目的地y的项,结点x需要知道的并不是最短路的路程,而是下一跳需要跳至的结点。
链路状态算法是中心化的:所有结点在运行Dijkstra算法之前,都需要已知整个网络的必要信息。而距离向量算法是去中心化的,无需这些全局信息,每个结点仅需邻接结点的相关信息和从这些邻接结点发来的消息。距离向量算法应用于许多路由协议,比如Internet的RIP、BGP、ISO IDRP和Novell IPX。早期的ARPAnet也有使用。
上图给出一个环路,考虑两种情况(为了简便,仅考虑y和z的距离表中目的地为x的项):
a、边(x,y)的开销从4减少到1;b、边(x,y)的开销从4增加到60。
【情况a】
·在t_0时刻,y检测到了链路开销改变(从4到1),因此更新了自己的距离向量,并通知其邻接结点。
·在t_1时刻,z收到了y发来的距离向量,更新了自己的距离表。它重新计算了到x的最小开销(从5减低到2),并将它的新的距离向量发送给邻接结点。
·在t_2时刻,y收到了z发来的更新,更新了自己的距离表。y到其它结点的最小开销并未改变,所以y不向z发送任何消息。算法结束。
在本情况中,距离向量算法只进行了两次迭代就停止了。
【情况b】
需要用到的部分变量的初值是:
D_y (x)=4, D_y (z)=1, D_z (y)=1, D_z (x)=5
·在t_0时刻,y发现链路开销改变了(从4到60),因此重新计算了到x的最小开销路。注意:现在只有D_y (x)从4变成了60,其它值还没来得及更新,因此最短路为y→z→x,路程长度为6。
这就构成了一条路由环路(routing loop)。路由环路是一个黑洞——一个原计划转发到目的地的数据包,因故在某段路径之间循环。如果路由表不再改变,那么数据包就会永远在环路中循环。在本例中,如果数据包从y出发(无论是在传送中途继续出发还是开始传送),走最小开销路径就要经过z;如果数据包从z出发(无论是在传送中途继续出发还是开始传送),走最小开销路径就要经过y。
·后来,y更新了自己的距离向量,并通知给邻接结点。
·在t_1时刻,z收到了y的距离向量更新:y到x的最小开销路径长为6。于是z重新计算最小开销路径为z