在分析AODV路由协议时,首先要明白,AODV只是一个路由协议,它的本质工作就是去管理路由表,所有的更新工作只是在实现对路由表的增、删、改、查工作。下面,我们继续之前的分析。
首先,我选择的切入点当然是从路由请求开始,分析源码,看它如何实现,关键代码贴出来分析:
我们从recv函数来分析
主函数
void
AODV::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.
调用函数
void
AODV::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)被调用。
void
AODV::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个文件,是如何实现对数据包存储功能的。