对比距离矢量路由协议(RIP),OSPF协议交换的不是路由条目,而是链路信息,并通过SPF算法计算出最佳路由,链路状态信息内含有路由接口、IP地址、掩码、cost值等,进而形成了链路状态通告(LSA)。
OSPF虽然不受路由器数量限制,但是网络中路由器太多,相互交换的LSA也会增加,随之而来的网络负担也会变大,因此需要给OSPF路由器划分区域。
区域主要分为两种:
常规区域主要限制了这一个区域之间的通信,多个常规区域之间一般是相互隔绝的,如果它们之间进行通信需要经过骨干区域转发。因此常规区域一定与骨干区域相连。
OSPF寻找最佳路径采用Dijkstra算法,也叫**最短路径优先(SPF)**算法:
一段链路的成本或者说度量的计算如下(默认情况):
C
o
s
t
=
1
0
8
b
p
s
B
a
n
d
w
i
t
h
Cost = \frac{10^8 bps}{Bandwith}
Cost=Bandwith108bps?
到达目的的链路总成本为每段链路成本之和:
T
o
t
a
l
C
o
s
t
=
∑
i
=
1
n
C
o
s
t
i
TotalCost = \sum_{i=1}^{n} Cost_i
TotalCost=i=1∑n?Costi?
此处带宽的单位为bps(比特每秒),其中参考带宽在默认情况下为1E8 bps
这里需要注意,在使用默认值的情况下,我们无法比较百兆带宽和千兆带宽哪个好(本文对于带宽的单位都以比特每秒为基础,例如百兆为100Mbps)。
当带宽为100Mbps的时候,代入公式成本为1;当带宽为1000Mbps的时候成本为0.1,按1算。
可以通过修改参考带宽来区分百兆和前兆网络,在思科路由器上可以采用一下命令:
Router(config)#router ospf 100
Router(config-router)#auto-cost reference-bandwith ref-bw
其中ref-bw为你需要设置的参考带宽的值,范围在1-4294967M/s
还有一种方式是直接设置成本(假设入方向接口为g0/0)
Router(config)#interface g0/0
Router(config-if)#ip ospf cost int-cost
其中int-cost替换为你所需要设置的值,范围在1-65535之间。
关于该算法具体流程请参考数据结构与算法。
OSPF报文头如上图所示,包含如下字段:
我们来看下报文内容,以下图片要拼接上上图的报文头才是一段完整的OSPF报文。
Hello报文内容部分(不包括报文头)如上图所示:
通过LSA的头部信息确认收到该LSA
每个LSA在LSDB中都有一个序号。此序号的大小为4个字节,则编号有2的32次方个,数字范围从0x80000001到0x7FFFFFF。
此处我们先来看看0x80000000
的二进制位为1000 0000 0000 0000 0000 0000 0000 0000
,最高位为符号位,后面的31位为序号位,并不是值位,也就是说1后面的31位为000 0000 0000 0000 0000 0000 0000 0000
表示为1,其含义为负数中从小到大的第一个数,即int32类型的最小值-2147483648。
而对于0x7fffffff
来说,符号为为0,是一个正数,而后面的31个序号位都为1,表示2的31次方,也就是2147483647
。
换句话说,LSA编号的范围与int32
类型的范围一致。
我们回到LSA上来,对于LSA有如下情况:
0x7FFFFFFF
循环回0x80000001
,如果发生这种情况,这个LSA会被路由器将老化时间设置为60分钟,并从LSBD清除。当路由器接收到一条LSU报文的时候,判断LSA在本地是否存在,判断要素有三个:
上述三要素如果存在任何一个不同,那么这条LSA在本地不存在。
如果这条LSA是首次更新,之前不存在,就直接放到数据库表内,回应LSAck报文。如果这路由器外还连接着其他路由,则继续泛洪给其他路由,5秒后,运行SPF算法,更新本地路由表。
如果之前存在,判断是否是最新的,通过序号大小决定是否最新:
(Tips:判断序号是否相同,不光可以比较序号,还可以看生存时间和校验和)
通过发送Hello包,建立邻居关系形成邻居表。Hello发送时间间隔根据网络类型而定:
多路访问网络(MA网络),是指一条物理信息可以发送给同一个网络上的其他路由器,其类型有两种:
影响邻居建立要素如下:
两台互联的OSPF路由器不会上来直接交换整个LSA,而是先交换LSA目录,然后根据目录缺少的条目,再向邻居请求。
当路由收到具体的请求内容消息的时候,回应请求的更新,这里面有真正的LSA。当请求方接收到LSA后,要回应确认包,确认包只针对Update包确认,区别于EIGRP。
综上,大致分为5步:
每一个路由器都有一个唯一的Router-ID来区分不同路由器,形式类似于IP地址(点分四段十进制数,32位字符串,但不是IP地址)。
Router ID配置有如下优先情况:
我们使用Cisco路由器为例,来进行演示
6Router(config)#interface loopback 0
Router(config-if)#ip address 172.16.17.5 255.255.255.0
Router(config-if)#no shut
Router(config-if)#exit
Router(config)#do show ip ospf
注意如果OSPF进程如果已经运行,要想使配置生效,必须在特权模式下执行clear ip ospf process
重新启动OSPF进程才行。
通过show ip ospf
命令,我们可以看到当前Router ID与我们配置的loopback 0
接口的IP是一致的。
我们再来使用router-id
命令来手动设置ID试一试。
顺带一提,Cisco路由器OSPF默认进程号为100,也就是说要进入进程号为100的OSPF进程配置模式下修改!
进入OSPF进程配置命令如下,这个命令后如果跟别的数字则会激活别的OSPF进程。
Router(config)#router ospf 100
Router(config-router)#router-id 1.1.1.1
Router(config-router)#exit
Router(config)#do clear ip ospf process
Reset ALL OSPF processes? [no]: yes
Router(config)#do show ip ospf
我们可以看到Router-ID已经变成了刚才指定的1.1.1.1
我们先来看下数据链路层的一些东西,OSPF网络类型与数据链路层的这些对应。
前三种与数据链路层对应
后面两种要想用,只能修改接口连接类型
其实还有一种特殊的网络类型(五种网络类型有六个不是很正常吗,雾OWO)
我们考虑OSPF网络类型主要是因为以下几种问题:
DR/BDR选举可谓OSPF非常重要的一个环节。
多接入网络对OSPF的LSA泛洪提出了两项挑战:
解决上述提到的问题的方案就是选出DR(指定路由器),在存在DR的情况下,如上图右侧,ABCE路由器所有更新信息都会发送给DR,DR将拥有最完整的数据库信息,之后借由DR告知ABCE对应的邻居路由器更新条目。
DR的选举依靠Hello包的优先级和Router-ID:
总之,DR和BDR选举都是通过优先级——RouterID的顺序来进行的。
这里需要注意,在OSPF协议中数值大的就越高,在其他一些协议中有些是数值越小越高。
在Cisco路由器中可以这样来设置优先级:
Router(config-if)# ip ospf priority [number]
命令中的number范围为0-255(包括0和255)。默认情况下优先级为1,不同的接口可能有不同的值,优先级为0的将永远不参与竞选DR和BDR。
当BDR失效后,重新选举BDR。当DR失效,但BDR没有失效,则原来的BDR变成DR,再重新选举BDR。
选举期一般为40秒,在选举期外不存在抢占。
DR与BDR选举完成后,DRother只和DR/BDR存在邻接关系,所有路由器将组播Hello包到地址224.0.0.5
,以便它们能跟踪消息。
对于LSU,DRother只组播到224.0.0.6
,只有DR和BDR会监听这个地址。
我们来看上图,当A下面的网络变化时,A向224.0.0.6
发送LSU,告知DR,DR向224.0.0.5
发送LSU告知DRother。在这个过程中BDR也是在监听DR与DRother的消息的。
如果BDR不这样做,当DR失效,但是没有来得及通知BDR,导致原BDR称为DR时仍认为A下的网络是有效的。
OSPF接口可以处于以下8种状态:
OSPF状态过渡流程如下图所示
本地一旦发出Hello包就立即跳出Down状态,进入下一状态。因此Down状态即为没有启用OSPF状态,邻居失效后也是该状态。
当启用OSPFv2的时候,已经启用的Gigabit Ethernet 0/0
从Down状态变为Init状态。R1开始通过所有启用OSPF协议的接口发送Hello数据包,发现要建立临接关系的OSPF邻居(此时为自动发现邻居模式,此状态看不到;当手动指定邻居的时候可以看到邻居表里有Down状态)。
这里有一个问题,当R1发送Hello包的时候应当是处于哪种状态?其实很简单,R1的G0/0接口在宣告OSPF进程的时候,就会发送Hello包,但是对端没有邻居,所以看不到Down状态(Down是与邻居关系的状态,没邻居看不到此状态)。
第一个Hello包含内容大致如下:
注意对于此Hello包是进行组播到224.0.0.5
,但是对于回包是单播。
R2从R1接收Hello数据包,并将R1的Router-ID添加到R2的邻居列表。随后R2向R1发送Hello数据包(单播),数据包在同一接口的邻居列表中包含R2的Router-ID与R1的Router-ID。(在R1收到这个包之前,R1都是Init)
上图中Router-ID为3.3.3.3
的代表R3,4.4.4.4
的为R4,其余同理。
我们来看上方图中R3和R4各自的邻居关系。我们手动指定R4将R3点包过滤掉,但是R3可以接受R4的包。令R3发送Hello包给R4,R4将包丢弃,因此在上图的R4邻居表中是没有R3的信息的。然而R4发送给R3一个Hello包,但是R3能接收到,所以R3的表中有R4的信息,且R4的状态显示为Init状态。
简单来讲,收到一个不包含自己信息的Hello包进入Init状态。
因此对于最上方图中的R1和R2,当R2接受到来自R1的Hello包的时候,R1并不知道R2的表中是否已经包含了R1的Router-ID,所以R1此时状态为Init,直到R1收到我们上方所说的R2的包的时候。
可以看出Init状态是一个很短暂的状态,如果长期处于该状态一般是不正常的。
R1接收到了来自R2的Hello包后,把R2的Router-ID添加到本地的OSPF邻居表中。另外,R1还看到了R2的邻居表中包含R1的Router-ID,说明双方都知道了各自的Router-ID,此时R1就可以放心地进入Two-way状态了。
总结来讲就是,收到一个包含自己信息的Hello包即进入Two-way状态。
Two-way状态后接下来会怎么做取决于邻居之间的链路:
这里说明下,之所以进入选举,是因为接下来的状态DBD需要自己确认自己就必须选出Master来主持。
当Hello数据包用于建立邻接关系的时,在交换和同步LSDB的过程中,使用其它四种类型的OSPF数据包。
在ExStart状态下,这两个路由器确定是哪个路由器首先发送DBD数据包。当处于ExStart状态时,都认为自己是主的,告诉别人自己的Router-ID,并说明准备发送DBD了,在相互传递完信息后,了解到谁的Router-ID大,谁发送DBD。
使用LSAck数据包确认收到DBD。
R1收到来自主路由R2的DBD后,将收到的信息与其LSDB中的信息进行比较,如果DBD数据包有较新的链路状态条目,路由器将转换为Loading状态。然后,R1将DBD数据包发送到R2,R2确认R1。
在ExChange状态下,两个路由器交换一个或者多个DBD数据包。DBD数据包包括路由器LSDB中显示的LSA条目报文头的相关信息。条目可与链路有关,也可与网络有关。每个LSA条目报头包括关于链路状态类型、通告路由器的地址、链路开销以及序列号信息。
路由器使用序列号来确定接收到的链路状态信息的更新程度。
该状态下使用LSR和LSU用于获取更多的路由信息。路由使用SPF算法进行处理。
当如图所示,R1向R2发送与网络172.16.6.0有关的完整信息,同样,R1接收到LSU时,它会发送LSAck。R1岁后将新的链路状态添加到其LSDB。
当所有LSR满足特定路由要求时,邻接路由器被视为已同步并处于Full状态。OSPF路由器只会在以下情况发送LSU更新:
Full状态表示路由器已经收敛。
DBD实际上是自己确认自己。
type 1 LSA有如下特点:
type 2 LSA有如下特点
默认情况下,没有路由的自动汇总,type 3 LSA通告每个子网。由于扩散范围广,所以需要手动汇总。一个良好的网络规划,每个区域应该利于做OSPF手动汇总。
其实OSPF协议的用法远不止这些,还有很多复杂的问题需要具体分析。诸如6类、8类等LSA对于一般的网络并不常用。