通过软件实现RIP协议,详细分析距离矢量路由算法,掌握网络协议的构建过程
在软件层面构建网络拓扑,编写路由器等代码,然后基于网络拓扑实现RIP路由协议,以及相应分析实验结果,如何实现路由表的动态更新等
-
路由信息协议(Routing Information Protocol)
- 简介
是一种内部网关协议,为最早出现的距离向量路由协议。属于网络层,其主要应用于规模较小的,可靠性较低的网络,可以通过不断的交换信息让路由器动态的适应网络连接的变化,这些信息包括每个路由器可以到达哪些网络,这些网络有多远等 ——维基百科
- 运作原理
每隔30秒 会于相邻的路由器交换子信息,以动态的创建路由表
- RIP数据包格式
command(1) version(1) must be zero(2) address family identifier (2) must be zero (2) IP address (4) must be zero (4) must be zero (4) metric (4) 内部(1)表示的为字节计数,命令Command字段为1时表示RIP请求,为2时表示RIP应答。地址类型标志符在实际应用中总是为2,即地址类型为IP地址。“IP”地址字段表明目的网络地址,“Metric”字段表明到达目的网络所需要的跳数,距离度量值用跳数来衡量,取值范围为1-16,其中16表示无限远(不可达)。路由器每经过30秒发送一次Response报文,这种报文用广播形式传播。
- RIP路由表的更新
路由器最初启动时只包含其直连网络的路由信息,并且其直连网络的Metric值为1,然后它向周围的其他路由器发送完整路由表的RIP请求。路由器根据接收到的RIP应答来更新路由表。若接收到与已有表项的目的地址相同的路由信息,则分别对待。 1.已有表项的来源端口与新表项的来源端口相同,则无条件根据最新的路由信息更新路由表。 2.已有表项的来源与新表项来源自不同的端口,那么比较它们的Metric值,讲metirc值较小的一个作为自己的路由表项。 3.新旧metric值相同,普遍的处理方法为保存旧的表项。 路由器每30秒发送一次自己的路由表。针对某一条路由消息,如果180秒以后都没有接收到新的关于它的路由消息,那么将其标记为失效,即metric值为16。在另外的120秒以后,如果仍然没有更新信息,该条信息被删除。
此时采用延迟作为距离的度量(和一般的RIP协议中使用的跳数不同)
此时我们需要写入的文件为dv_router和拓扑结构,也就是实现能处理我们自己定义的RoutingUpdate
包(相关代码分析见code.md
)。
首先我们需要了解一下实体连接后的相关机制,当我们连接好拓扑结构时,每一个实体都会向自己所有连接有实体的端口发送DiscoveryPacket
包,此时这个包将记住相关端口连线的延迟并加入到这个包中,同时将det
设置为NullAddress
,将src
设置为发送这个包的实体本身,这一部分我们不需要实现这是core
核中定义的方法,我们要做的就是处理发往自己的包。
当有邻居发送DiscoveryPacket
包给自己时,此时提取包内部信息(可提取的信息为邻居是谁,到这个邻居的延迟是多少),此时同时可以记录该包从实体的哪个端口来(handle_rx
函数特性每次有端口来包时都会调用该函数自然知道端口是谁同时来的包是什么),那么我们就可以构建我们邻居表的相关信息。
其次我们拥有了邻居信息后就应该构建路由表,同时应该隔固定时间将我们构建的路由表转发到别的路由器上去,由于此时将延迟作为距离的度量,此时我们router内部需要两张表来缓存相关的信息,分别是邻居表和路由表,格式如下:
-
邻居表:
实体 出口端口 延迟 邻居A 端口号 到邻居延迟 邻居B 端口号 到邻居延迟 -
路由表:
实体 出口端口 延迟 实体A 到达实体A所要出的端口 从当前路由器到实体A的估计延迟
两者如何工作的简单示例如下:
此时以A路由器为例(请参照组网图),此时A路由器会收到来自邻居B,C,H1的DiscoveryPacket
包,此时构建的相关的邻居表如下(需要查看相关结果请调用package_analyse
函数来查看输出):
{<BasicHost h1>: [0, 1], <DVRouter_New c>: [2, 7], <DVRouter_New b>: [1, 2]}
实体 | 出口端口 | 延迟 |
---|---|---|
H1 | 0 | 1 |
C | 2 | 7 |
B | 1 | 2 |
此时根据邻居表我们可以先将邻居放入路由表中,因为只有一跳已经最短此时就不需要再修改,此时A的路由表如下:
{<DVRouter_New a>: [NullAddress, 0], <BasicHost h1>: [0, 1], <DVRouter_New c>: [2, 7], <DVRouter_New b>: [1, 2]}
实体 | 出口端口 | 延迟 |
---|---|---|
H1 | 0 | 1 |
C | 2 | 7 |
B | 1 | 2 |
A | NullAddress | 0 |
此时各个router已经完成邻居表的构建,此时应该每个路由器都应该向自己相邻的节点发送自己的路由信息,此时我们应构建RoutingUpdate
包,RoutingUpdate
包的格式如下,我们假设此时B在构建自己的RoutingUpdate
包并准备泛洪发往所有端口:
实体 | 延迟 |
---|---|
H2 | 1 |
D | 3 |
C | 1 |
A | 2 |
B | 0 |
B泛洪发送完这个包后邻居A会收到这个包,然后对包的信息做相关解析,首先如果发现包中存在自己未存在的节点,首先将该节点加入到自己的路由表中同时初始化(设置默认端口同时将到达延迟设置为最大)
self.router_table[new_point] = [0, MAX_LATENCY]
实体 | 出口端口 | 延迟 |
---|---|---|
H1 | 0 | 1 |
C | 2 | 7 |
B | 1 | 2 |
A | NullAddress | 0 |
D | 0 | MAX_LATENCY |
H2 | 0 | MAX_LATENCY |
然后对该表进行处理,此时A
通过解析包知道当前包的src
为B
,同时有B
到达各个节点的估计路由,此时将A
到B
的距离记录,加上B
到某一个节点的估计延迟,从而生成A
到这个节点的新的估计延迟,如果这个新延迟比A
的路由表中查表得到的延迟要小,则将A
到某一个节点的端口设置为到B
的端口,同时将延迟设置为新延迟;否则舍弃新的延迟,通过这种方法构建的新的A的路由表如下:
实体 | 出口端口 | 延迟 |
---|---|---|
H1 | 0 | 1 |
C | 1 | 3 |
B | 1 | 2 |
A | NullAddress | 0 |
D | 1 | 5 |
H2 | 1 | 3 |
至此,一次传播过程完成,通过多次传播,可以将消息传播到每一个路由器,也就是每一个路由器都将知道所有节点的位置还有相关延迟。
最后我们处理ping
/pong
包的转发,直接查找路由表,通过包的dst
跟路由表中的实体作对比,如果找到就转发到相应查表得到的端口,如果没有查找到就直接丢弃。
-
在写第一版的router代码时使用get_port内核函数
solve
:此时不应该使用get_port内核函数,同时泛洪发DiscoveryPacket
包会使网络拥挤不堪,同时对于逻辑上导致不太清晰 -
如何解决泛洪后对于环形router连接导致的路由爆炸?
solve
:此时每个包都会记录自己经过的路径,当经过16跳后路由器就自动丢弃它,事实上真正的路由器也确实是这么做的。
完成每个主机之间都能够ping通,同时每个router都建立了相关的路由表,ping包也会按照最短路径来分发,与结果预期一致。
Liunx
– deepin2.0
python2.7