NS2中无线网络模拟之三(AODV路由分析2)

来源:互联网 发布:查工商银行余额软件 编辑:程序博客网 时间:2024/05/16 03:54

在分析AODV路由协议时,首先要明白,AODV只是一个路由协议,它的本质工作就是去管理路由表,所有的更新工作只是在实现对路由表的增、删、改、查工作。下面,我们继续之前的分析。

首先,我选择的切入点当然是从路由请求开始,分析源码,看它如何实现,关键代码贴出来分析:

我们从recv函数来分析

主函数

voidAODV::recv(Packet *p, Handler*) {struct hdr_cmn *ch = HDR_CMN(p);struct hdr_ip *ih = HDR_IP(p); assert(initialized()); //assert(p->incoming == 0); // XXXXX NOTE: use of incoming flag has been depracated; In order to track direction of pkt flow, direction_ in hdr_cmn is used instead. see packet.h for details. if(ch->ptype() == PT_AODV) {   ih->ttl_ -= 1;   recvAODV(p);   return; } /*  *  Must be a packet I'm originating...  */if((ih->saddr() == index) && (ch->num_forwards() == 0)) { /*  * Add the IP Header.    * TCP adds the IP header too, so to avoid setting it twice, we check if  * this packet is not a TCP or ACK segment.  */  if (ch->ptype() != PT_TCP && ch->ptype() != PT_ACK) {    ch->size() += IP_HDR_LEN;  }   // Added by Parag Dadhania && John Novatnack to handle broadcasting  if ( (u_int32_t)ih->daddr() != IP_BROADCAST) {    ih->ttl_ = NETWORK_DIAMETER;  }} /*  *  I received a packet that I sent.  Probably  *  a routing loop.  */else if(ih->saddr() == index) {   drop(p, DROP_RTR_ROUTE_LOOP);   return; } /*  *  Packet I'm forwarding...  */ else { /*  *  Check the TTL.  If it is zero, then discard.  */   if(--ih->ttl_ == 0) {     drop(p, DROP_RTR_TTL);     return;   } }// Added by Parag Dadhania && John Novatnack to handle broadcasting if ( (u_int32_t)ih->daddr() != IP_BROADCAST)   rt_resolve(p); else   forward((aodv_rt_entry*) 0, p, NO_DELAY);}<span style="color:#ff0000;"></span>


分析1、

 if(ch->ptype() == PT_AODV) {   ih->ttl_ -= 1;   recvAODV(p);   return; }
如果本节点收到一个数据包,这个数据包首先类型是PT_AODV,而且不是由我自己产生的,那么调用recvAodv(p)函数,并且给其TTL-1.

调用函数

voidAODV::recvAODV(Packet *p) { struct hdr_aodv *ah = HDR_AODV(p); assert(HDR_IP (p)->sport() == RT_PORT); assert(HDR_IP (p)->dport() == RT_PORT); /*  * Incoming Packets.  */ switch(ah->ah_type) { case AODVTYPE_RREQ:   recvRequest(p);   break; case AODVTYPE_RREP:   recvReply(p);   break; case AODVTYPE_RERR:   recvError(p);   break; case AODVTYPE_HELLO:   recvHello(p);   break;         default:   fprintf(stderr, "Invalid AODV type (%x)\n", ah->ah_type);   exit(1); }}
在recvAODV(Packet p)函数中,首先获取到hdr_aodv的包头指针,然后根据IP头判断源端口与目的端口是否相同。这个是因为我们的AODV继承自Agent,作为一个代理,所以,保证目的端口要一致。接下来,利用switch来判断接收到的数据包是什么类型,然后调用相应的函数。这几个函数一会儿再来分析,先回到主线程。因为现在我们知道了我们的AODV路由协议基本函数是在这里调用的。

分析2、

回到主函数接下来,我们再来分析,如果这个数据包是我自己生成的(根据节点原理图,就是这个数据包我的上层TCP传下来的,如蓝色代码,

if((ih->saddr() == index) && (ch->num_forwards() == 0)) { /*  * Add the IP Header.    * TCP adds the IP header too, so to avoid setting it twice, we check if  * this packet is not a TCP or ACK segment.  */  if (ch->ptype() != PT_TCP && ch->ptype() != PT_ACK) {    ch->size() += IP_HDR_LEN;  }   // Added by Parag Dadhania && John Novatnack to handle broadcasting  if ( (u_int32_t)ih->daddr() != IP_BROADCAST) {    ih->ttl_ = NETWORK_DIAMETER;  }}
通过查看TCP的源码我们会发现,TCP中有一个headersize()函数,这个函数返回tcpip_base_hdr_size_变量的值,即为tcp+ip的字节大小。所以,如果收到的不是TCP/ACK,则需要在包的大小上加上IP的大小。如果目地址不是广播地址的话,那么设置其生存周期为(NETWORK_DIAMETER=30)。
else if(ih->saddr() == index) {   drop(p, DROP_RTR_ROUTE_LOOP);   return; }
发生环路,丢弃数据包。

 else { /*  *  Check the TTL.  If it is zero, then discard.  */   if(--ih->ttl_ == 0) {     drop(p, DROP_RTR_TTL);     return;   } }
检测数据包的生命周期,到期就丢弃。

接下来的判断

 if ( (u_int32_t)ih->daddr() != IP_BROADCAST)   rt_resolve(p); else   forward((aodv_rt_entry*) 0, p, NO_DELAY);
如果收到的这个数据包的目的地址不是广播地址,那么去检索路由表,否则如果是广播地址,那么立即发送出去。这里有一点需要注意,这个数据包可能是接收的来自其他节点,也有可能是自己产生的数据包。

因为我们先来分析发送数据包的情况,所以当对接收到PT_AODV类型的数据包时的调用一会儿再看。我们先来分析这种情况,如果这个数据包是来自我自身节点的TCP/UDP产生的,经上层的协议传过来并且要发送出去,作为路由协议,该如何处理(单播)?很明显,如果这个数据包是第一次要发送的数据包,那么这段代码肯定是第一次执行。

函数rt_resolve(p)被调用。

voidAODV::rt_resolve(Packet *p) {struct hdr_cmn *ch = HDR_CMN(p);struct hdr_ip *ih = HDR_IP(p);aodv_rt_entry *rt; /*  *  Set the transmit failure callback.  That  *  won't change.  */ ch->xmit_failure_ = aodv_rt_failed_callback; ch->xmit_failure_data_ = (void*) this; rt = rtable.rt_lookup(ih->daddr()); if(rt == 0) {  rt = rtable.rt_add(ih->daddr()); } /*  * If the route is up, forward the packet   */ if(rt->rt_flags == RTF_UP) {   assert(rt->rt_hops != INFINITY2);   forward(rt, p, NO_DELAY); } /*  *  if I am the source of the packet, then do a Route Request.  */  else if(ih->saddr() == index) {   rqueue.enque(p);   sendRequest(rt->rt_dst); } /*  *A local repair is in progress. Buffer the packet.   */ else if (rt->rt_flags == RTF_IN_REPAIR) {   rqueue.enque(p); } /*  * I am trying to forward a packet for someone else to which  * I don't have a route.  */ else { Packet *rerr = Packet::alloc(); struct hdr_aodv_error *re = HDR_AODV_ERROR(rerr); /*   * For now, drop the packet and send error upstream.  * Now the route errors are broadcast to upstream  * neighbors - Mahesh 09/11/99  */    assert (rt->rt_flags == RTF_DOWN);   re->DestCount = 0;   re->unreachable_dst[re->DestCount] = rt->rt_dst;   re->unreachable_dst_seqno[re->DestCount] = rt->rt_seqno;   re->DestCount += 1;#ifdef DEBUG   fprintf(stderr, "%s: sending RERR...\n", __FUNCTION__);#endif   sendError(rerr, false);   drop(p, DROP_RTR_NO_ROUTE); }}
首先,获取common和ip数据包头指针。申明 aodv_rt_entry *rt; 路由表指针。然后,在接下来的两条语句:

ch->xmit_failure_ = aodv_rt_failed_callback;ch->xmit_failure_data_ = (void*) this;
在common包头中,给出了解释:

  // called if pkt can't obtain media or isn't ack'd. not called if        // droped by a queue        FailureCallback xmit_failure_;        void *xmit_failure_data_;
其中FailureCallback是一个函数指针。因此,被申明为一个回调函数。当链路层传输失败的时候去调用 aodv_rt_failed_callback函数(主要用在修复阶段)。具体查看参考博客路由层传输失败和回调函数2篇文章。

 rt = rtable.rt_lookup(ih->daddr()); if(rt == 0) {  rt = rtable.rt_add(ih->daddr()); }
先去查找本节点的路由表中有没有目的地址,如果没有,那么去增加。如果是第一次,那么肯定为0.
此时rt一定是为0的,所以调用rt_add函数。
aodv_rt_entry*aodv_rtable::rt_add(nsaddr_t id){aodv_rt_entry *rt; assert(rt_lookup(id) == 0); rt = new aodv_rt_entry; assert(rt); rt->rt_dst = id; LIST_INSERT_HEAD(&rthead, rt, rt_link); return rt;}
调用函数:(列表查找指定的ID)
aodv_rt_entry*aodv_rtable::rt_lookup(nsaddr_t id){aodv_rt_entry *rt = rthead.lh_first; for(; rt; rt = rt->rt_link.le_next) {   if(rt->rt_dst == id)     break; } return rt;}
此时如果没有对应的Id,即look_up==0,如果经过assert后断定为真,说明在路由表中没有这个目的地址。那么新建路由项,设定目的地址,并且插入表的头部。下面是在aodv_rt_entry的构造函数中,为一个路由项做的初始化工作;
odv_rt_entry::aodv_rt_entry(){int i; rt_req_timeout = 0.0; rt_req_cnt = 0; rt_dst = 0; rt_seqno = 0; rt_hops = rt_last_hop_count = INFINITY2; rt_nexthop = 0; LIST_INIT(&rt_pclist); rt_expire = 0.0; <span style="color:#ff0000;">rt_flags = RTF_DOWN;</span> /* rt_errors = 0; rt_error_time = 0.0; */ for (i=0; i < MAX_HISTORY; i++) {   rt_disc_latency[i] = 0.0; } hist_indx = 0; rt_req_last_ttl = 0; LIST_INIT(&rt_nblist);}
由于初始化 rt_flags = RTF_DOWN向下的,所以我们调用

 if(rt->rt_flags == RTF_UP) {   assert(rt->rt_hops != INFINITY2);   forward(rt, p, NO_DELAY); } /*  *  if I am the source of the packet, then do a Route Request.  */   else if(ih->saddr() == index) {   rqueue.enque(p);   sendRequest(rt->rt_dst); }
将数据包加入队列,并且开始路由请求过程。这也正如AODV中一样,如果要发送一个数据包的时候,如果此时在路由表中没有要到达的目的地址的路由项,那么将此数据包入队,并且开始路由请求阶段。下面我么将到NS2实现的aodv_rqueue.h/aodv_rqueue.cc这2个文件,是如何实现对数据包存储功能的。






0 0
原创粉丝点击