在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
posted @ 2020-07-27 09:47  Sno_W_olF  阅读(518)  评论(0编辑  收藏  举报