AODV路由分析:
经过一段时间对NS2模拟器的研究,今天真正进入到了对路由算法源代码的分析过程,这里我来一步一步的来对AODV路由算法来进行分析,是自己的一段学习历程,也希望对其他正在受NS2折磨的小伙伴们带来一些帮助。下面进入正题,我们先来分析AODV的数据包的包头开始下手:
具体代码在ns-2.35/aodv/aodv_packet.h
#define AODVTYPE_HELLO 0x01
#define AODVTYPE_RREQ 0x02
#define AODVTYPE_RREP 0x04
#define AODVTYPE_RERR 0x08
#define AODVTYPE_RREP_ACK 0x10
以上为AODV中所用到的各种数据包的包头类型的宏定义;
#define HDR_AODV(p) ((struct hdr_aodv*)hdr_aodv::access(p))
#define HDR_AODV_REQUEST(p) ((struct hdr_aodv_request*)hdr_aodv::access(p))
#define HDR_AODV_REPLY(p) ((struct hdr_aodv_reply*)hdr_aodv::access(p))
#define HDR_AODV_ERROR(p) ((struct hdr_aodv_error*)hdr_aodv::access(p))
#define HDR_AODV_RREP_ACK(p) ((struct hdr_aodv_rrep_ack*)hdr_aodv::access(p))
获取包头对象的结构体指针,这个是基本框架;
request请求数据包格式:
struct hdr_aodv_request {
u_int8_t rq_type; // Packet Type
u_int8_t reserved[2];
u_int8_t rq_hop_count; // Hop Count
u_int32_t rq_bcast_id; // Broadcast ID
nsaddr_t rq_dst; // Destination IP Address
u_int32_t rq_dst_seqno; // Destination Sequence Number
nsaddr_t rq_src; // Source IP Address
u_int32_t rq_src_seqno; // Source Sequence Number
double rq_timestamp; // when REQUEST sent;
// used to compute route discovery latency
// This define turns on gratuitous replies- see aodv.cc for implementation contributed by
// Anant Utgikar, 09/16/02.
//#define RREQ_GRAT_RREP 0x80
inline int size() {
int sz = 0;
/*
sz = sizeof(u_int8_t) // rq_type
+ 2*sizeof(u_int8_t) // reserved
+ sizeof(u_int8_t) // rq_hop_count
+ sizeof(double) // rq_timestamp
+ sizeof(u_int32_t) // rq_bcast_id
+ sizeof(nsaddr_t) // rq_dst
+ sizeof(u_int32_t) // rq_dst_seqno
+ sizeof(nsaddr_t) // rq_src
+ sizeof(u_int32_t); // rq_src_seqno
*/
sz = 7*sizeof(u_int32_t);
assert (sz >= 0);
return sz;
}
};
路径的发现
无论何时,当源节点需要和其他节点通讯,但在其路由表内又没有相关节点的路由信息,源节点就会初始化一个路径发现进程。每个节点都会维护两个独立的计数器。一个是节点序号计数器,一个是广播ID计数器。源节点通过广播一个路由请求包RREQ到其所有邻近节点来初始化路径发现进程。RREQ包含如下域:
<source_addr; source-sequence_#; broadcast_id; dest_addr; dest_sequence_#; hop_cnt>
参数对<source_addr; broadcast_id>唯一确定了一个路由请求。当源节点发布一个新的RREQ时,broadcast_id就增值。收到RREQ的邻节点只有两种情况。情况一是该邻节点即是满足RREQ的节点,随后该节点将发送路由响应包RREP到源节点。情况二是该邻节点不是RREQ所要求的节点,则该节点将再次广播RREQ到其自己的邻节点,并对跳计数加1。需要注意的是,一个节点可能收到来自多个不同邻节点的多份相同的路由广播包的拷贝。当一个中间节点收到一个RREQ,若之前它已经收到具有相同source_addr和broadcast_id的RREQ,则抛弃多余收到的RREQ而不会重新广播多余的RREQ。若一个节点不是RREQ所要求的节点,它必须追踪部分信息以设置反向路径,这也是最终的RREP所使用的转发路径。追踪的信息包括:1.目标节点ip地址。2.源节点IP地址。3.广播ID。4.路由项目中反向路径的过期时间。5.源节点序号。
struct hdr_aodv_reply {
u_int8_t rp_type; // Packet Type
u_int8_t reserved[2];
u_int8_t rp_hop_count; // Hop Count
nsaddr_t rp_dst; // Destination IP Address
u_int32_t rp_dst_seqno; // Destination Sequence Number
nsaddr_t rp_src; // Source IP Address
double rp_lifetime; // Lifetime
double rp_timestamp; // when corresponding REQ sent;
// used to compute route discovery latency
inline int size() {
int sz = 0;
/*
sz = sizeof(u_int8_t) // rp_type
+ 2*sizeof(u_int8_t) // rp_flags + reserved
+ sizeof(u_int8_t) // rp_hop_count
+ sizeof(double) // rp_timestamp
+ sizeof(nsaddr_t) // rp_dst
+ sizeof(u_int32_t) // rp_dst_seqno
+ sizeof(nsaddr_t) // rp_src
+ sizeof(u_int32_t); // rp_lifetime
*/
sz = 6*sizeof(u_int32_t);
assert (sz >= 0);
return sz;
}
};
一个联合体,用来设置不同的数据包头。
union hdr_all_aodv {
hdr_aodv ah;
hdr_aodv_request rreq;
hdr_aodv_reply rrep;
hdr_aodv_error rerr;
hdr_aodv_rrep_ack rrep_ack;
};
路由表结构:
class aodv_rt_entry {
friend class aodv_rtable;
friend class AODV;
friend class LocalRepairTimer;
public:
aodv_rt_entry();
~aodv_rt_entry();
void nb_insert(nsaddr_t id);
AODV_Neighbor* nb_lookup(nsaddr_t id);
void pc_insert(nsaddr_t id);
AODV_Precursor* pc_lookup(nsaddr_t id);
void pc_delete(nsaddr_t id);
void pc_delete(void);
bool pc_empty(void);
double rt_req_timeout; // when I can send another req
u_int8_t rt_req_cnt; // number of route requests
protected:
LIST_ENTRY(aodv_rt_entry) rt_link;
nsaddr_t rt_dst; //destination address
u_int32_t rt_seqno; //to reflect the referene of this add
/* u_int8_t rt_interface; */
u_int16_t rt_hops; // hop count
int rt_last_hop_count; // last valid hop count
nsaddr_t rt_nexthop; // next hop IP address
/* list of precursors */
aodv_precursors rt_pclist;
double rt_expire; // when entry expires
u_int8_t rt_flags; //the node status(valied,invalide,reparing)
#define RTF_DOWN 0
#define RTF_UP 1
#define RTF_IN_REPAIR 2
/*
* Must receive 4 errors within 3 seconds in order to mark
* the route down.
u_int8_t rt_errors; // error count
double rt_error_time;
#define MAX_RT_ERROR 4 // errors
#define MAX_RT_ERROR_TIME 3 // seconds
*/
#define MAX_HISTORY 3
double rt_disc_latency[MAX_HISTORY];
char hist_indx;
int rt_req_last_ttl; // last ttl value used
// last few route discovery latencies
// double rt_length [MAX_HISTORY];
// last few route lengths
/*
* a list of neighbors that are using this route.
*/
aodv_ncache rt_nblist;
};
AODV路由协议涉及路由表管理的处理,通常在路由请求发送RREQ过程中建立反向路由,在转发RREP的时候建立正向路由,当然在数据通信的过程中也会涉及到路由表的操作,如出现了路由出错的时候以及路由生命期的更新等。
下面对AODV路由条目中的几个重要字段进行介绍:
(1)目的节点地址n_dst:用于标志使用此路由的最终目的节点,决定了数据分组转发方向。
(2)目的节点序列号rt_seqno:反映此路由的新鲜度,一般序列号越大路由越新鲜, 这是保证开环的重要措施,在路由发现和路由应答更新路由时需要进行序列号的比较。
(3)路由状态标志rt_flags(有效、无效、正在修复等):反映此路由目前的状态,主要用于告知数据分组经过此节点的时候处理方式。如果路 由处于无效状态,那么数据分组将丢失;如果处于修复状态,那么数据分组进入等待路由队列中;如果有效状态,那么直接转发。
(4)网络接口rt_interface:移动节点访问信道的接口。
(5)跳数rt_hops:到达目的节点所需要的跳数。在断链时决定是否发起本地修复时会用到,目的是控制断链修复范围;在路由更新时也会用到,目的是选择最短路径,从而降低断链几率。
(6)下一跳rt _nexthop :数据分组经过本节点之后,数据分组将被直接转发的中继节点,通常下一跳节点应该出现在当前节点的邻节点列表中。
(7)前驱节点列表rt_pclist:使用这条路由的所有直接前驱节点列表。在出现断链的时候可以通过前驱节点列表中是否存有节点而决定是否广播RERR消息。
(8)路由生命期rt_expire:路由有效的生命期,在数据分组转发使用当前路由时会更新路由的有效生命期,当较长时间不使用此路由时,此路由的有效期将会过期,在路由管理时将会使路由失效。