ns2 AODV组件(函数)介绍

5 篇文章 2 订阅
在NS2.27中,AODV路由协议主要包括以下几个组件:
1、协议实体
2、路由表
3、定时器
(1)广播定时器
(2)周期Hello报文广播定时器
(3)用于邻居管理的定时器
(4)用于路由缓存的定时器
(5)用于本地修复的定时器
(6)缓存广播ID的定时器
4、日志记录器
5、路由缓存队列
首先来看协议从接收到一个分组开始的基本流程。
当协议接收到一个分组,即recv(Packet*, Handler*)函数被调用,函数根据分组类型调用不同的处理函数进行处理
1、如果是协议分组,则将分组的ttl值减1,并调用recvAODV(Packet*)函数进行处理。recvAODV函数再根据分组的不同类型来调用不同的函数进行处理。
(1)如果接收到的是路由请求分组,则调用recvRequest(Packet*)函数进行处理。
如果该分组由节点自身产生或已经接收过的,会被节点丢弃,并结束处理。
否则,节点将缓存该分组的序列号,并将该分组发送来的路径添加到反向路由中,转发相应分组。然后,节点根据该分组的目的地址进行判断并调用不同函数进行处理。
如 果节点自身即为目的节点,则调用sendReply(nsaddr_t, u_int32_t, nsaddr_t, u_int32_t, u_int32_t, double)函数进行响应。如果节点不是目的节点,但知道通往目的节点的路由,则调用sendReply函数进行响应,并在源和目的前驱列表中分别插入 到源和目的的下一跳节点。否则,不能直接响应该请求,将跳数加1,并调用forward(aodv_rt_entry*, Packet*, double)函数转发该分组。
在sendReply函数中,节点首先查找到达目的节点(即发送路由请求分组的节点)的路由,创建并填充分组,然后调用Scheduler::instance().schedule()函数来发送该分组。
(2)如果接收到的是路由响应分组,则调用recvReply(Packet*)函数进行处理。
节点首先查询前往分组目的节点的路由,如果不存在则新增一条路由项。然后,节点更新到该目的节点的路由项,并发送所有相关分组。
如果节点为目的节点则更新路由发现延迟并发送所有相关的分组。如果节点不是目的节点,但知道通往目的节点的路由,则将跳数加1,调用forward函数转发该分组,并修改响应的前驱列表。如果节点不是目的节点,也不知道通往目的节点的路由,则丢弃该分组。
(3)如果接收到的是路由错误分组,则调用recvError(Packet*)函数进行处理。
节点首先清除所有受到影响的路由项,丢弃所有受影响的分组。然后,如果前驱节点中存在会受该路由错误影响的分组,则调用sendError(Packet*, bool)函数转发该分组。
sendError函数创建并填充分组,然后调用Scheduler::instance().schedule()函数来发送该分组。
(4)如果接收到的是Hello消息分组,则调用recvHello(Packet*)函数进行处理。
节点会将该邻居的信息添加到邻居列表中(或更新该邻居的信息)。
2、如果是数据分组,则节点丢弃已经发送过或者ttl为0的分组,并结束处理。如果分组是由上层协议产生的,则节点添加IP报头。随后,节点根据目的路由进行不同处理。
(1)如果目的节点路由未知,则调用rt_resolve(Packet*)函数进行路由解析和转发
如 果目的节点路由在路由表中存在,则直接调用forward函数进行转发。如果分组是由节点自身产生的,则将分组保存到缓冲队列中,并调用 sendRequest(nsaddr_t)函数查询目的路由。如果目的路由已知,但正在进行本地修复,则将分组保存到缓冲队列中。否则,丢弃该分组,并 调用sendError函数报错。
(2)如果目的节点路由已知,则调用forward进行转发
节点丢弃ttl为0的分组,并根据分组类型决定下一步操作。
如 果接收到的是数据分组,且自身为目的节点,则通过调用PortClassifier对象的recv(Packet*, Handle*)函数将分组交递给高层协议,并结束处理。否则,节点设置分组属性,并调用 Scheduler::instance().schedule(Handler*, Event*, double)函数来发送分组。 其中,Handler为基类中的属性target_(会根据脚本中的设置指向相应的协议实体),Event为要发送的分组即可。
以上就是在节点收到分组后的一个处理过程。接下来看看各个定时器所做的工作。
1、 广播定时器BroadcastTimer在到时后调用id_purge()函数删除广播项中已超时的项目,并通过调用 Scheduler::instance().schedule()函数来设置下次被调用的时间(Handler为this指针,Event为类属性 intr)。
2、周期Hello报文广播定时器HelloTimer在到时后调用sendHello()函数向邻居创建并发送Hello消息,并调用schedule()函数来设置下次被调用的时间。
3、 邻居管理定时器NeighborTimer在到时后调用nb_purge()函数来清除邻居列表中已超时的邻居项,并调用schedule()来设置下次 被调用的时间。nb_purge会调用nt_delete(nsaddr_t)函数来清除超时的邻居项,其又会调用 handle_link_failure(nsaddr_t)函数来处理由于邻居节点被删除而引起的路由变化。
4、路由缓存定时器RouteCacheTimer在到时后调用rt_purge()函数来清除路由表中已超时的路由项,并丢弃相关的分组,再调用schedule()来设置下次被调用的时间。
5、本地修复定时器LocalRepairTimer在调用后根据传递的分组的目的地址关闭相应的路由项。
6、缓存广播ID定时器BroadcastID用来保存广播分组的ID。

此外,路由表、日志记录和队列三个类就相对比较简单了,都只实现了一些非常基本的功能




NS里实现的aodv是单播的,多播的aodv叫MAODV,网上有源代码。我觉得看代码之前最好先对协议有个了解,这样看起来会更有效率;另外,实现与理论不一样,如果不对代码做改动的话,没必要对每个细节都了解很清楚。以下将简略介绍aodv中的重要函数。不足之处请谅解,我也是两年前看的源代码了,可能有些地方理解不对。
     AODV中aodv.cc是最重要的,了解aodv看这个文件也就差不多了,这个文件中,起点从AODV::recv(Packet *p, Handler*) 开始,文件开头那些都不用管。
1.Void AODV::recv(Packet *p, Handler*)
//判断是否是aodv包,是则调用函数recvAODV(p)
if(ch->ptype() == PT_AODV) {   recvAODV(p);
//本节点产生的数据包,添加IP头
if((ih->saddr() == index) && (ch->num_forwards() == 0))   Add the IP Header
//收到本节点发送的包,说明有路由环路,丢包
else if(ih->saddr() == index)   drop(p, DROP_RTR_ROUTE_LOOP);
//本节点是中间节点
else {//TTL是分组最多能转发的次数 if(--ih->ttl_ == 0)   drop(p, DROP_RTR_TTL);
//收到的不是广播分组,解析分组
if ( (u_int32_t)ih->daddr() != IP_BROADCAST) rt_resolve(p);
//转发 else forward((aodv_rt_entry*) 0, p, NO_DELAY);

2.void AODV::rt_resolve(Packet *p) {//解析收到的分组
//查找是否有到目的节点的路由 rt = rtable.rt_lookup(ih->daddr());
//没有,则添加到该目的节点的路由,此时添加的路由是无效的
if(rt == 0)    rt = rtable.rt_add(ih->daddr());
//有效路由,则根据路由表中信息转发分组
if(rt->rt_flags == RTF_UP) forward(rt, p, NO_DELAY);
//如果本节点是该分组的源节点,说明没有到目的节点的路,此时发送RREQ找路
else if(ih->saddr() == index)   rqueue.enque(p); sendRequest(rt->rt_dst);
//链路中断,在维护中 else if (rt->rt_flags == RTF_IN_REPAIR)   rqueue.enque(p);
// 本节点转发数据分组,但是不知道该到目的节点的路,发送RERR说明链路中断
else sendError(rerr, false);

3. AODV::sendRequest(nsaddr_t dst) { //发送RREQ
//添加到目的节点的路由,此时的路由不可用,是无效的
aodv_rt_entry *rt = rtable.rt_lookup(dst);
//不到发送RREQ的时间,注意文件开头的RREQ定时器,若没收到RREP,源节点需要定时发送RREQ
if (rt->rt_req_timeout > CURRENT_TIME) Packet::free((Packet *)p);
//AODV中多次发送RREQ,多次发送RREQ后仍找不到路,则丢包
if (rt->rt_req_cnt > RREQ_RETRIES) rt->rt_req_timeout = CURRENT_TIME + MAX_RREQ_TIMEOUT;     drop(buf_pkt, DROP_RTR_NO_ROUTE);
//余下部分是填充路由表以及RREQ分组的内容,不涉及实现的话不用了解,了解有一定难度,需要结合整个过程看
4.void AODV::sendError(Packet *p, bool jitter) {//链路中断,需要发送RERR,通知所有受影响的节点,函数体关于填充RERR内容,不用细看

5. void AODV::sendHello() {//周期发送Hello分组,以检测邻节点的连通性

6.void AODV::recvAODV(Packet *p) {//根据包类型调用不同函数
case AODVTYPE_RREQ recvRequest(p);
case AODVTYPE_RREP: recvReply(p);
case AODVTYPE_RERR: recvError(p);
case AODVTYPE_HELLO: recvHello(p);

7. void AODV::recvRequest(Packet *p) {//收到RREQ
// I'm the source - I recently heard this request.则丢弃该RREQ
if(rq->rq_src == index) Packet::free(p);
   if (id_lookup(rq->rq_src, rq->rq_bcast_id)) Packet::free(p);
//   * Cache the broadcast ID ,用于判断是否已收到过该RREQ
id_insert(rq->rq_src, rq->rq_bcast_id);
//查找是否有到源节点的路由,有则更新,无则添加a
//* Find out whether any buffered packet can benefit from the * reverse route.缓存中是否有到源节点的数据分组,有,则建立好路由后开始发送数据
//查找是否有到目的节点的有效路由,有则向源节点回复RREP,没有则继续转发该RREQ,注意转发前需要更新RREQ中部分内容

8.void AODV::sendReply(nsaddr_t ipdst, u_int32_t hop_count, nsaddr_t rpdst,
            u_int32_t rpseq, u_int32_t lifetime, double timestamp) {//填充RREP内容

9. void   AODV::recvReply(Packet *p) {
//查找是否有到目的节点的路由,没有则建立,否则更新
//如果是RREP的目的节点,即RREQ的源节点则建立到目的节点的路;否则根据路由表中到源节点的路由(此路由在发送RREQ过程中已建立)转发该RREP

10. void AODV::recvError(Packet *p) {//收到RERR,查看有哪些路径受到中断链路的影响,更新RERR内容,并向受影响的节点发送该RERR

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值