OSPF 是一种 TCP/IP 路由协议,在RIP协议中最大路径长度是15,在很多场景下不能满足大型网络的需求,这就有了开放最短路径优先(OSPF),设计用于在大型互联网网络中交换路由信息。据说最多可支持几百台路由器。
OSPF有三个版本,IPV6使用OSPFv3,IPv4使用的是OSPFv2,在OSPFv2经过多次修改,可以参考RFC2328。
OSPF路由器的类型
OSPF路由器可以分为以下4种类型:
- 区域内路由器(Internal Router)
该类路由器的所有接口都属于同一个OSPF区域。
- 区域边界路由器(Area Border Router,ABR)
该类路由器可以同时属于两个以上的区域,但其中一个必须是主干区域,ABR用来连接主干区域和分支区域,它与主干区域之间既可以是物理连接,也可以是逻辑上的连接。
- 主干路由器(Backbone Router)
主干路由器我们也可以称为骨干路由器,至少有一个接口属于主干区域,所有的ABR和位于主干区域的内部路由器都是主干路由器。
- 自治系统边界路由器(Autonomous System Border Router,ASBR)
与其他自治系统交换路由信息的路由器称为ASBR。
OSPF路由分级
OSPF将路由分为4种级别,按照优先级从高到底的顺序依次为:
区域内路由
区域间路由
第一类外部路由
第二类外部路由
区域内和区域间路由描述的是自治系统内部的网络结构,外部路由则用于选择自治系统以外目的地址的路由。
路由聚合
路由聚合是指边界路由器(ABR或ASBR)将具有相同前缀的路由信息聚合,只发布一条路由到其他区域。自治系统划分成不同的区域后,区域间可以通过路由聚合来减少路由信息,减少路由表规模,提高路由表的处理速度。
下图中,区域内有4个区域。
链路状态通告(LSA)
OSPF是一个链路状态协议。链路状态通告(Link State Advertisement,LSA)又叫链路状态数据包(Link State Advertisement Packet,LSP)是链接状态协议使用的一个数据包,它包含有关邻居和路径成本的信息。
OSPF网络类型
对于不同链路层的协议,OSPF网络采用不同的数据包发送方式。
- 广播
当链路层协议是以太网时,OSPF默认的网络类型是广播。
- 非广播多路访问
当链路层协议是帧中继或者ATM时,可以减少对其他设备的干扰。
- 点到点
当链路层协议是PPP时,OSPF默认的网络类型是P2P。
- 点到多点
由其他OSPF网络类型强制更改的一种类型。
OSPF数据包类型
将OSPF协议传输的协议数据单元称为OSPF数据包。
OSPF共有以下5种类型的协议数据包。主要包括:
- 问候(Hello)数据包
周期性发送 ,用来发现和维持OSPF邻居关系以及指定路由器或备份指定路由器的选举。这也是最常用的数据包。
- 数据库描述(Database Description,DD)数据包
描述了本地LSDB中每条LSA的摘要信息,用于两台路由器进行数据库同步。
- 链路状态请求(Link State Repuest,LSR)数据包
向对方请求所需的LSA,内容包括所需要的LSA的摘要。
链路状态更新(Link State Update,LSU)数据包
向对方发送其所需要的LSA。
- 链路状态确认(Link State Acknowledggment,LSAck)数据包
用来对收到的LSA进行确认。
OSPF数据包结构
OSPF数据包直接封装为IP数据报,其协议号为89。
OSPF数据包包括首部和数据两个部分。
OSPF首部数据包
在这5种数据包类型中,OSPF拥有相同的数据包首部
主要字段说明如下
版本(Version):OSPF的版本号,对于OSPFv2来说,其值为2。
类型( Message Type):OSPF数据包的类型。数值从1到5,分别对应Hello数据包、DD数据包、LSR数据包、LSU数据包和LSAck数据包。
数据包长度(Packet length):包括首部在内的OSPF数据包的总长度,以字节为单位。
源 OSPF 路由器 (Source OSPF Router):源OSPF的IP地址
区域ID(Area ID):发送该数据包的路由器所在的区域ID
检验和(Checksum):对整个OSPF数据包的校验和。
认证类型(Auth Type):可分为不认证、简单口令认证和MD5认证,其值为0、1、2。
认证数据(Auth Data):厂商认证数据
问候(Hello)数据包格式
主要的字段如下:
网络掩码(Network Mask):发送问候数据包的接口所在网络的掩码。
问候间隔(Hello Interval):发送问候数据包的时间间隔。
选项(Options):路由器所支持的能力。
路由器优先级(Router Priority):用来选择指定路由器。如果设置为0,则该路由器接口不能成为指定路由器或备份指定路由器。
指定路由器(Designated Router):设置指定路由器的接口的IP地址。
备份指定路由(Backup Designated Router):设置备份指定路由器的接口IP地址。
OSPF选项字段
OSPF选项字段出现在问候数据包、数据库描述包和所有的LSA中,用于使OSPF路由器支持可选择的功能,并向其他OSPF路由器通告其能力,不同能力的路由器可以混合在一个OSPF路由域中。
OSPF选项字段长8位,其中定义了以下标志位,格式如下
DN标志用来避免在MPLS 中出现环路。
O标志用来说明路由器是否有能力发送和接收opaque LSA即类型9,类型10和类型11。
DC标志表示处理按需链路。
MC标志表示转发IP多播包。
E标志表示洪泛AS外部LSA。
MT标志表示始发路由器支持多拓扑OSPF(MT-OSPF)。
OSPF代码实现
static void dissect_ospf(u_char *data_info, int offset,int ip_total_len)
{
uint8_t version;
uint8_t packet_type;
uint16_t ospflen;
//vec_t cksum_vec[4];
int cksum_vec_len;
uint32_t phdr[2];
uint16_t cksum, computed_cksum;
unsigned int length, reported_length;
uint16_t auth_type;
int crypto_len = 0;
unsigned int ospf_header_length;
uint8_t instance_id;
uint32_t areaid;
uint8_t address_family = OSPF_AF_6;
in_addr ip;
version = data_info[offset];
printf("version: %d\n",version);
switch (version) {
case OSPF_VERSION_2:
ospf_header_length = OSPF_VERSION_2_HEADER_LENGTH;
break;
case OSPF_VERSION_3:
ospf_header_length = OSPF_VERSION_3_HEADER_LENGTH;
break;
default:
ospf_header_length = 14;
break;
}
offset += 1;
packet_type = data_info[offset];
printf("packet_type: %d\n",packet_type);
offset += 1;
ospflen = ntohs(*(uint16_t*)(data_info + offset));
printf("ospflen: %d\n",ospflen);
offset += 2;
if (ospf_msg_type_to_filter(packet_type) != -1) {
}
memcpy(&ip.s_addr,data_info + offset,sizeof(ip.s_addr));
printf("source router: %s\n",inet_ntoa(ip));
offset += 4;
areaid = ntohl(*(uint32_t*)(data_info + offset));
if(areaid == 0){
printf("(Backbone)\n");
}
memcpy(&ip.s_addr,data_info + offset,sizeof(ip.s_addr));
printf("area id: %s\n",inet_ntoa(ip));
offset += 4;
cksum = ntohs(*(uint16_t*)(data_info + offset));
if(cksum == 0){
printf("(None)\n");
}
offset += 2;
/*如果是未知的 OSPF 版本,请在此时退出*/
if(version != OSPF_VERSION_2 && version != OSPF_VERSION_3) {
return ;
}
switch (version)
{
case OSPF_VERSION_2:
/* 认证仅对 OSPFv2 有效 */
auth_type = ntohs(*(uint16_t*)(data_info + offset));
switch (auth_type) {
case OSPF_AUTH_NONE:
printf("ospf header auth data none \n");
break;
case OSPF_AUTH_SIMPLE:
printf("ospf header auth data simple \n");
break;
case OSPF_AUTH_CRYPT:
printf("ospf auth crypt \n");
crypto_len = data_info[19];
printf("crypto_len: %d\n",crypto_len);
break;
default:
printf("ospf header auth data unknown \n");
break;
}
break;
case OSPF_VERSION_3:
/* 实例 ID 和“保留”仅适用于 OSPFv3 */
printf("ospf header instance id \n");
instance_id = data_info[14];
/* 默认设置 address_family 为 OSPF_AF_6 */
address_family = OSPF_AF_6;
if(instance_id > 65 && instance_id < 128) {
address_family = OSPF_AF_4;
}
break;
default:
break;
}
offset += 2;/*auth_type*/
offset += 8;/*auth_data*/
switch (packet_type){
case OSPF_HELLO:
printf("OSPF_HELLO\n");
dissect_ospf_hello(data_info, ospf_header_length,version,
(uint16_t)(ospflen - ospf_header_length));
break;
case OSPF_DB_DESC:
break;
case OSPF_LS_REQ:
break;
case OSPF_LS_UPD:
break;
case OSPF_LS_ACK:
break;
default:
break;
}
/* 处理 LLS 数据块 */
if (ospf_has_lls_block(data_info, ospf_header_length, packet_type, version)){
dissect_ospf_lls_data_block(data_info,ospflen + crypto_len, version);
}
/*处理 AT(Authentication Trailer)数据块 */
}
编译运行
总结
OSPF的最大优点是效率高,要求很小的开销,适应范围广,可以说是目前应用最广、性能最好的路由器协议。
欢迎关注微信公众号【程序猿编码】,需要OSPF源码和报文的添加本人微信号(17865354792)
参考:RFC2328