在NS2中移植实现MFlood协议
一、将MFlood路由协议添加到NS2中。
1. 在NS2中新建文件夹放置协议的代码文件。
首先需要下载mflood协议,其中包括两个文件夹:一个是mflood协议(还有文件把mflood.cc,mflood.h,mflood-seqtable.cc,mflood-seqtable.h,mflood-packet.h),一个是用于mflood测试的文件夹。
在ns-2.35目录下建立mflood文件夹,把上述的(mflood.cc,mflood.h,mflood-seqtable.cc,mflood-seqtable.h,mflood-packet.h)文件放在该mflood文件夹下面。
2. 修改NS2中的相关文件packet.h、ns_packet.tcl和ns_lib.tcl。
修改ns-2.35/common/packet.h文件
修改ns-2.35/tcl/lib/ns-packet.tcl文件
修改ns2.35/tcl/lib/ns-lib.tcl文件
3. 修改Makefile文件。
修改ns-2.35/Makefile,在OBJ_CC中增加一行:
mflood/mflood.o mflood/mflood-seqtable.o \
,特殊注意点:mflood/mflood.o mflood/mflood-seqtable.o
后面是空格,再加上\结尾。
4. 保存操作。
cd /home/ns-allinone-2.35/ns-2.35
/ns-allinone-2.35/ns-2.35# make
/ns-allinone-2.35/ns-2.35# make install
二、验证/测试移植结果
测试,进入mflood_test文件夹(存放有cbr-50n -30c-1p,scene-50n-0p-40s-400t- 1200-1200,getNodeRecv.awk,getRatio.awk,mflood-3nodes.tcl,mflood- scene.tcl)
1.测试mflood-3nodes.tcl
首先以root用户进入以下路径:
ns-allinone-2.35/ns-2.35/mflood_test
,然后输入ns mflood-3nodes.tcl
,如果协议移植成功则会出现以下结果
2.测试mflood-scene.tcl
终端中输入ns mflood-sceen.tcl,若正确,会出现如下仿真结果:
根据代码找到相对应生成的trace文件。
可以看到,当模拟场景中的节点在收到目的不是自己的包后就将其广播出去,但同时,它们不会重复广播同一个包。说明 MFlood 协议的移植成功。
三、代码
1. mflood.cc
#include <mflood/mflood.h>
#include <mflood/mflood-packet.h>
//#include <mflood.h>
#include <random.h>
#include <cmu-trace.h>
//#include <iostream>
// 新的包类型
int hdr_mflood::offset_;
// MFloodHeaderClass是PacketHeaderClass的派生类,功能是将mflood的信息添加到包头中并编辑偏移量
static class MFloodHeaderClass : public PacketHeaderClass {
public:
MFloodHeaderClass() : PacketHeaderClass("PacketHeader/MFlood",
sizeof(hdr_mflood)) {
bind_offset(&hdr_mflood::offset_);
}
} class_mfloodhdr;
// 类MFloodclass连接新的C++项目mflood和tcl。必须注意的是,TCLClass的每个派生类都应该是一个静态类,以便NS2在初始化时传递函数的构造函数
// TCL钩子
static class MFloodclass : public TclClass {
public:
MFloodclass() : TclClass("Agent/MFlood") {}
TclObject* create(int argc, const char*const* argv) {
assert(argc == 5);
return (new MFlood((nsaddr_t) atoi(argv[4]))); // PBO agrv[4] 是索引
}
} class_rtProtoMFlood;
// 函数命令用于TCL在C++中使用mflood的函数
int MFlood::command(int argc, const char*const* argv) {
Tcl& tcl = Tcl::instance();
if(argc == 2) {
if(strncasecmp(argv[1], "id", 2) == 0) {
tcl.resultf("%d", index_);
return TCL_OK;
} // 可以看做GetIndex
else if (strcmp(argv[1], "uptarget") == 0) {
if (uptarget_ != 0)
tcl.result(uptarget_->name());
return (TCL_OK);
} // 输出上目标的名称
} else if(argc == 3) {
if(strcmp(argv[1], "index_") == 0) {
index_ = atoi(argv[2]); // 可以看作SetIndex
return TCL_OK;
} else if(strcmp(argv[1], "log-target") == 0 || strcmp(argv[1], "tracetarget") == 0) {
logtarget = (Trace*) TclObject::lookup(argv[2]);
if(logtarget == 0) return TCL_ERROR; // 作为SetLogtarget
return TCL_OK;
}
else if (strcmp(argv[1], "uptarget") == 0) {
if (*argv[2] == '0') {
target_ = 0; // 设置target
return (TCL_OK);
}
uptarget_ = (NsObject*)TclObject::lookup(argv[2]);
if (uptarget_ == 0) {
tcl.resultf("no such object %s", argv[2]);
return (TCL_ERROR);
}
return (TCL_OK);
}
else if (strcasecmp (argv[1], "port-dmux") == 0) {
TclObject *obj;
port_dmux_ = (NsObject *) obj; // 设置port_dmux
return (TCL_OK);
}
}
return Agent::command(argc, argv);
}
// MFlood类的构造函数,它将设置节点的IP地址并初始化参数
MFlood::MFlood(nsaddr_t id) : Agent(PT_MFLOOD), port_dmux_(0) {
index_ = id;
logtarget = 0;
myseq_ = 0;
}
// 路由处理函数
void
MFlood::rt_resolve(Packet *p) {
struct hdr_cmn *ch = HDR_CMN(p);
struct hdr_ip *ih = HDR_IP(p);
struct hdr_mflood *fh = HDR_MFLOOD(p);
MFlood_RTEntry* rt;
rt = rtable_.rt_lookup(ih->saddr());
if(rt == NULL) {
rt = new MFlood_RTEntry(ih->saddr(), fh->seq_);
LIST_INSERT_HEAD(&rtable_.rthead,rt,rt_link);
//printf("%.8f %d,no uptarget,\n",NOW,index_);
forward(rt,p,FORWARD_DELAY);
//printf("%.8f %d,no rt,so forward.rt_seq:%d,pkt seq:%d\n",NOW,index_,rt->max_seqno,fh->seq_);
rtable_.rt_print();
}
// else if(rt->seq_ < fh->seq_ )
else if(rt->isNewSeq(fh->seq_) )
{
//printf("%.8f %d,no uptarget,\n",NOW,index_);
forward(rt, p, FORWARD_DELAY);
// rt->seq_ = fh->seq_;
rt->addSeq(fh->seq_);
//printf("%.8f %d,rt seq too small,so forward,rt_seq:%d,packet seq:%d.\n",NOW,index_,rt->max_seqno,fh->seq_);
rtable_.rt_print();
}
else
{
drop(p, "LOWSEQ");
}
}
// 数据包接收例行程序
void MFlood::recv(Packet *p, Handler*) {
struct hdr_cmn *ch = HDR_CMN(p);
struct hdr_ip *ih = HDR_IP(p);
struct hdr_mflood *fh = HDR_MFLOOD(p);
assert(initialized());
if((ih->saddr() == index_) && (ch->num_forwards() == 0)) { // 数据包由节点本身创建的其情况,recv函数将编辑数据包的头,然后发送它(通过转发)
ch->size() += IP_HDR_LEN; // 增加IP头
ih->ttl_ = NETWORK_DIAMETER;
fh->seq_ = myseq_++;
forward((MFlood_RTEntry*)1,p,0);
return;
} else if(ih->saddr() == index_) { // 节点接收到它发送的数据包的情况。它指的是可能发生了一个路由循环,所以函数recv将会丢弃无意义的数据包
drop(p, DROP_RTR_ROUTE_LOOP);
return;
} else { // 节点接收到的数据包正在转发的情况,函数recv只是让函数rt_resolve判断接下来应该做什么
if(--ih->ttl_ == 0) { // 检测TTL,如果为0,则丢弃
drop(p, DROP_RTR_TTL);
return;
}
}
rt_resolve(p);
}
// 数据包传输程序,它根据延迟情况决定是否进行转发
void
MFlood::forward(MFlood_RTEntry* rt, Packet *p, double delay) {
struct hdr_cmn *ch = HDR_CMN(p);
struct hdr_ip *ih = HDR_IP(p);
assert(ih->ttl_ > 0); // 使用函数断言以确保没有发生意外错误(因为在多次扫描之后,ttl<0的数据包发生了向前的功能)
assert(rt != 0);
// assert(rt->rt_flags == RTF_UP);
ch->next_hop_ = -1; // 广播地址,next_hop为-1表示数据包是广播数据包(无dst)
ch->addr_type() = NS_AF_INET; // 标记作为广播报文
ch->direction() = hdr_cmn::DOWN; //改变数据包的方向,让调度器发送数据包到LL层
if(delay > 0.0) {
Scheduler::instance().schedule(target_, p, Random::uniform(delay*2));
} else { // 不是广播包,没有延迟,立即发送
Scheduler::instance().schedule(target_, p, 0.);
}
}
2. mflood.h
#ifndef __mflood_h__
#define __mflood_h__
#include <sys/types.h>
#include <cmu-trace.h>
#include <priqueue.h>
#include <mflood/mflood-seqtable.h>
#include <list>
//#include <mflood_seqtable.h>,课本源码错误,注释掉
#define NOW (Scheduler::instance().clock())
// 应该由用户设置使用
#define NETWORK_DIAMETER 30 // 30跳
// 以下用于转发函数,控制函数
#define FORWARD_DELAY 0.01 // 随机延迟
#define NO_DELAY -1.0 // 无延迟
// 路由代理
class MFlood: public Agent {
friend class MFlood_RTEntry;
public:
MFlood(nsaddr_t id);
void recv(Packet *p, Handler *);
protected:
int command(int, const char *const *);
inline int initialized() { return 1 && target_; }
// 路由表管理
void rt_resolve(Packet *p);
// 报文发送
void forward(MFlood_RTEntry *rt, Packet *p, double delay);
nsaddr_t index_; // 节点的IP地址
// 路由表
MFlood_RTable rtable_;
// 记录路由内容的机制
Trace *logtarget;
NsObject *uptarget_;
NsObject *port_dmux_;
private:
u_int32_t myseq_;
};
#endif
3. mflood-packet.h
#ifndef __mflood_packet_h__
#define __mflood_packet_h__
/*
* JFlood 路由协议头宏
*/
#define HDR_MFLOOD(p) ((struct hdr_mflood*)hdr_mflood::access(p))
/*
* 通用JFlood头
*/
struct hdr_mflood {
u_int32_t seq_;
u_int32_t dst_group; // 目标组标识
// 头访问方法
static int offset_; // PacketHeaderManager要求
inline static int& offset() { return offset_; }
inline static hdr_mflood* access(const Packet* p) { // 带有偏移量,接受者可以知道数据信息在分组中的位置
return (hdr_mflood*) p->access(offset_);
}
};
#endif /* __mflood_packet_h__ */
4. mflood-seqtable.cc
#include <mflood/mflood-seqtable.h>
// 路由表
MFlood_RTEntry::MFlood_RTEntry() {
src_ = 0;
// seq_ = 0;
for(int i=0;i<REM_SEQ_COUNT;i++)
rt_seqnos[i] = 0xffffffff;
max_seqno = 0;
min_seqno = 0;
seq_it = 0;
};
// 路由表
MFlood_RTEntry::MFlood_RTEntry(nsaddr_t src,u_int32_t seq) {
src_ = src;
// seq_ = seq;
for(int i=0;i<REM_SEQ_COUNT;i++)
rt_seqnos[i] = 0xffffffff;
rt_seqnos[0] = seq;
max_seqno = seq;
min_seqno = 0;
seq_it = 1;
};
bool MFlood_RTEntry::isNewSeq(u_int32_t seq)
{
if(seq > max_seqno) // 如果序列号比它之前的每一个都更新
return true; // 则一个新的数据包不重复
//采用min_seqno来抛弃掉太久以前的包
if(seq < min_seqno) // min_seqno是数据包是否太久的标准
return false;
for(int i=0;i<REM_SEQ_COUNT;i++) // 检查数组以确定数据包是否重复
if(seq == rt_seqnos[i])
return false;
return true;
}
void MFlood_RTEntry::addSeq(u_int32_t seq)
{
u_int16_t min_it = 0;
if(seq < min_seqno)
return;
if(seq > max_seqno)
max_seqno = seq; // 更新max_seqno
/*
for(int i=0;i<REM_SEQ_COUNT;i++)
if(seq == rt_seqnos[i])
return;
*/
rt_seqnos[seq_it++] = seq;
seq_it %= REM_SEQ_COUNT; // 这个过程是重用数组,如果元素超过最大量
min_seqno = 0xffffffff; // 则更新min_seqno
//计算出min_seqno
for(int i=0;i<REM_SEQ_COUNT;i++)
if(min_seqno > rt_seqnos[i])
min_seqno = rt_seqnos[i];
}
// 路由表
MFlood_RTEntry*
MFlood_RTable::rt_lookup(nsaddr_t id) {
MFlood_RTEntry *rt = rthead.lh_first;
for(; rt; rt = rt->rt_link.le_next) {
if(rt->src_ == id)
break;
}
return rt;
}
void
MFlood_RTable::rt_delete(nsaddr_t id) {
MFlood_RTEntry *rt = rt_lookup(id);
if(rt) {
LIST_REMOVE(rt, rt_link);
delete rt;
}
}
void
MFlood_RTable::rt_print() { // 这个函数是打印每个RTEntry的RTable,打印功能只是为了调试
MFlood_RTEntry *rt = rthead.lh_first;
//printf("\n Seq table:\n");
for(; rt; rt = rt->rt_link.le_next) {
//printf("index: %d , seq: %d \n",rt->src_,rt->max_seqno);
}
return;
}
5. mflood-seqtable.h
#ifndef __mflood_seqtable_h__
#define __mflood_seqtable_h__
#include <assert.h>
#include <sys/types.h>
#include <config.h>
#include <lib/bsd-list.h>
#include <scheduler.h>
#define INFINITY 0xff
#define RTF_DOWN 0
#define RTF_UP 1
#define REM_SEQ_COUNT 5000
// 路由表条目
class MFlood_RTEntry {
friend class MFlood_RTable;
friend class MFlood;
public:
MFlood_RTEntry();
MFlood_RTEntry(nsaddr_t src,u_int32_t seq);
bool isNewSeq(u_int32_t seq); // 旧 -> false, 新->true
void addSeq(u_int32_t seq); // 添加一个seqno到seqno数组(rt_seqnos)
protected:
LIST_ENTRY(MFlood_RTEntry) rt_link;
nsaddr_t src_;
// u_int32_t seq_;
u_int32_t rt_seqnos[REM_SEQ_COUNT]; // seqno数组
u_int32_t max_seqno; // 最大序列号
u_int32_t min_seqno; // 最小序列号
u_int16_t seq_it; // seqno的迭代
};
// 路由表
class MFlood_RTable {
friend class MFlood;
public:
MFlood_RTable() { LIST_INIT(&rthead); }
void rt_delete(nsaddr_t id);
MFlood_RTEntry* rt_lookup(nsaddr_t id);
void rt_print();
private:
LIST_HEAD(, MFlood_RTEntry) rthead;
};
#endif