基本原理:
网络中每个节点只选择自己邻居节点的一个子集,作为多点中继集MPR。根据MPR的节点产生链路状态信息。通过节点不断的选择自己的MPR或该节点作为其它节点MPR,对广播信息进行转发,最后根据新信息计算到达目的节点的最短路径。
比如:节点A向节点B发消息,节点A会选择自己的MPR节点,节点B也会选择自己的MPR节点,只有MPR节点会转发信息。
MPR节点具体的产生步骤:
比如节点A,在初始阶段,在他的一跳范围内,广播Hello消息,节点C收到这个消息,更新自己的本地链路信息表,即将节点A作为自己的邻居节点,并且标记C->A是非对称的。
然后,节点C在它的一跳范围内,广播Hello消息,这个Hello消息中包含着:节点A是他的邻居节点,并且C->A是非对称的,当节点A收到这个消息,更新自己的本地链路信息表,会在自己的邻居集中将节点C的状态更新为对称。
节点A再次广播Hello消息,此消息中包含,节点C是节点A的邻居节点,并且链路信息是对称的。
当节点C收到这个信息,在自己的邻居集中,将节点A的链路信息更新为对称。
总之,网络中的每个节点都要选择一部分自己的对称邻居节点作为通信的中继节点,即MPR节点。
数据结构:
每个分组封装了一一个或多个消息,这些消息共用一个消息头 OLSR使用同一的数据包格式,使用UDP进行通信。OLSR路由协议头部的基本格式:
- Packet length:包的字节长度
- Packet sequence number 包的序列号,每产生一个新的数据包,序列号+1
- Message type 消息类型:
-
1 -> Hello
-
2 -> topology control(TC)
-
3 -> multiple interface declaration(MID)
-
4 -> host and network association(HNA)
- Vtime 节点接收消息后消息的有效时间
- Message size 消息的字节长度,从消息类型开始到下一个消息类型结束。
- Originator address 发送该节点的源地址,在传输的过程中保持不变
- TTL 最大跳数,每转发一次,该值-1
- Hop count 当前跳数
- Message sequence number 消息的序列号
typedef struct OLSR_msg {
u_int8_t msg_type_; ///< Message type.
u_int8_t vtime_; ///< 有效时间.
u_int16_t msg_size_; ///< 消息大小
nsaddr_t orig_addr_; ///< 发送此消息的源节点
u_int8_t ttl_; ///生存时间,(最大跳数,每转发一次-1
u_int8_t hop_count_; ///消息跳数,(此消息在转发过程中经历的跳数)
u_int16_t msg_seq_num_;///消息序列号(唯一的)
//union将所有字段重叠在内存中的相同偏移处,各字段共享一块内存
//struct为每个字段在不同的偏移处分配内存空间
union {
OLSR_hello hello_;
OLSR_tc tc_;
OLSR_mid mid_;
} msg_body_; ///< Message body.
//在函数前加上inline关键字,成为内联函数,可以解决
//频繁调用的函数大量消耗内存的问题
//函数返回值为引用类型---》返回的是对象本身
inline u_int8_t& msg_type() {
return msg_type_; }
inline u_int8_t& vtime() {
return vtime_; }
inline u_int16_t& msg_size() {
return msg_size_; }
inline nsaddr_t& orig_addr() {
return orig_addr_; }
inline u_int8_t& ttl() {
return ttl_; }
inline u_int8_t& hop_count() {
return hop_count_; }
inline u_int16_t& msg_seq_num() {
return msg_seq_num_; }
inline OLSR_hello& hello() {
return msg_body_.hello_; }
inline OLSR_tc& tc() {
return msg_body_.tc_; }
inline OLSR_mid& mid() {
return msg_body_.mid_; }
//分别计算每种类型的数据包大小
inline u_int32_t size() {
u_int32 += hello().size();
else if (msg_type() == OLSR_TC_MSG)
sz += tc().size();
else if (msg_type() == OLSR_MID_MSG)
sz += mid().size();
return sz;
}
} OLSR_msg;
Hello消息基本格式:
HELLO消息用于建立一个节点的邻居表,其中包括邻居节点的地址以及本节点到邻居节点的延时和开销,OLSR采用周期性的广播HELLO分组来侦听邻居节点的状态和无线链路的对称性。节点之间无线链路的状态包括:非对称链路,对称链路,连接MPR的链路。但OLSR协议只关心对称链路,同时HELLO分组只在一跳的范围内广播,不能被转发。
- Reserved:保留字段,设置为0000000000000
- Htime: 发送hello分组的周期
- Willingness: 表示节点为其他节点转发分组的愿意程度,如果一个节点的Willingness为WILL_NEVER,任何节点都不会选择该节点作为MPR.
- Link Code: 标识了节点与其邻居节点之间的链路类型和邻居类型,链路的类型有:一般链路、非对称链路、对称链路和失效链路,邻居类型有:对称邻居、MPR邻居和非邻居;
- (1) ASYM _ LINK:发送HELLO分组的节点与列表中的邻节点间的链路是非对称的。表示可以收到邻节点的消息,但不确定邻节点是否能收到本节点的消息
- (2)SYM _LINK :发送HELLO分组的节点与列表中的邻节点间的链路是对称的。表示链路已经被验证为双向的。
- (3)MPR_ LINK:表示列表中的节点已被发送该HELLO分组的节点选择为MPR 。
- Link Message Size:链路信息的大小
- Neighbor interface address:邻居节点的接口地址
typedef struct OLSR_hello {
/// Reserved.设置为000000000
u_int16_t reserved_;
/// 该节点发送hello分组的周期
u_int8_t htime_;
/// 转发节点的意愿程度
u_int8_t willingness_;
/// List of OLSR_hello_msg.
OLSR_hello_msg hello_body_[OLSR_MAX_HELLOS];
//包含在hello_body中的OLSR_HELLO_msg消息的数量
int count;
inline u_int16_t& reserved() {
return reserved_; }
inline u_int8_t& htime() {
return htime_; }
inline u_int8_t& willingness() {
return willingness_; }
inline OLSR_hello_msg& hello_msg(int i) {
return hello_body_[i]; }
inline u_int32_t size() {
u_int32_t sz = OLSR_HELLO_HDR_SIZE;
for (int i = 0; i < count; i++)
sz