AODV协议详解1 AODV 报文格式AODV 有三种基本的协议报文类型:RREQ 报文、RREP 报文和 RRER 报文1.1 RREQ 报文a. 对 RREQ 的处理接收到 RREQ 的结点做如下处理:(1)创建一个表项,先不分配有效序列号,用于记录反向路径2)如果在“路由发现定时”内已收到一个具有相同标识的 RREQ 报文,则抛弃该报文,不做任何处理;否则,对该表项进行更新如下:I.下一跳结点=广播 RREQ 的邻居II.跳数=RREQ 报文的“跳计数”字段值III.设置表项的“过时计时器”3)如果满足以下条件,则结点产生“路由回答报文”RREP,并发送到信源;否则更新 RREQ 报文并广播更新后的 RREQ 报文I.该结点是信宿II.结点的路由表中有到信宿的活动表项,且表项的信宿序列号大于 RREQ中的信宿序列号4)更新 RREQ 报文并广播更新后的 RREQ 报文I.信宿序列号=本结点收到的信宿相关的最大序列号II.跳计数加 11.2 RREP 报文(1)信宿结点产生 RREP执行如下操作:I.如果收到相应的 RREQ 的信宿序列号与信宿维护的当前序列号相等,则信宿将自己维护的序列号加 1,否则不变。
II.跳计数=0III.定时器值2)中间结点产生的 RREP执行如下操作:I.本结点获取的该信宿的最大序列号II.跳计数=本结点到信宿的跳数(查相应表项即可得到)III.更新本结点维护的“前向路由表项”的下一跳和“反向路由表项”的前一跳b. 对 RREP 的处理结点对接收到的 RREP 作如下处理1)如果没有与 RREP 报文中的信宿相匹配的表项,则先创建一个“前向路表”空表项2)否则,满足如下条件对已有表项进行更新条件:I.现有表项的信宿序列号小于 RREP 报文中的序列号II.现有的表项没有激活III.信宿序列号相同,但 RREP 报文的“跳计数”值小于表项相对应的值;通过更新或创建,产生一个新的前向路由更新:IV.下一跳=广播 RREP 的邻居结点V.信宿序列号=RREP 中的信宿序列号VI.跳计数加 13)按照上述的过程,任何转发 RREP 的结点,都记录了到信宿的下一跳,当RREP到达信源时结点地址匹配,不再转发 RREP,信源到信宿的前向路由已经建立起来了信源可以沿这条前向路径进行数据传输1.3 RRER 报文邻居间周期性的互相广播“Hello”报文,用来保持联系,若在一段时间内没有收到“Hello”报文,则认定为链路断。
例如当结点 X、Y 之间链路产生断路使数据无法通过此条链路传至信宿,则结点 X 会产生 RRER 报文向信源报告此情况RRER 通过广播形式传送,维护路由表的结点收到此报文会更新路由表(将X、Y间的路由设成无效),并转发 RRER 报文2 协议从接收到一个分组开始的基本流程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。