OSPF协议解析及代码实现
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
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
