生成树相关框架代码

生成树相关框架代码

stp.h

# ifndef _STP_H
# define _STP_H

# include"stp_proto.h"
# include"stp_timer.h"

# include"base.h"
# include"types.h"

# define STP_MAX_PORTS 32 
// 定义一共至多32个端口

extern const u8 eth_stp_addr[];
// 定义全局变量,eth_stp_addr数组

// 定义端口结构体
struct stp_port{
	stp_t *stp;   // 指向 stp的指针

	int port_id;  // 端口id
	char *port_name;   // 端口名称,类似b1-eth0
	iface_info_t *iface;   // 数据包发送和接收端口

	int path_cast;   // 端口路径开销,本实验中恒为 1

	u64 designated_root; // 端口认为的根结点
	u64 designated_switch; // 发送这条Config消息的结点
	int designated_port;	// 发送这条Config消息的端口
	int designated_cost;	// 端口到根结点的路径开销
};

typedef struct stp stp_t;
// stp类型的结构体为 stp_t
typedef struct stp_port stp_port_t;

// 定义结点结构体
struct stp{
	u64 switch_id;  // 结点id

	u64 designated_root; // 结点认为的根结点
	int root_path_cost;  // 到根结点的花销(结点自身)
	stp_port_t *root_port; // 到根结点的根端口

	long long int last_tick;	// 结点计时器

	stp_time_t hello_timer;   // 计时器时间

	// 端口
	int nports;
	stp_port_t ports[STP_MAX_PORTS]; // 定义端口列表

	pthread_mutex_t lock; // 定义锁
	pthread_t timer_thread; // 定义时间线程
};

void stp_init(struct list_head *iface_list);
void stp_destory();

void stp_port_handle_packer(stp_port_t *,char *packet,int pkt_len);

# endif

stp.c

# include "stp.h"

# include "base.h"
# include "ether.h"
# include "utils.h"
# include "types.h"
# include "log.h"

# include <stdlib.h>
# include <string.h>
# include <stdio.h>

# include <sys/types.h>
# include <unistd.h>

# include <pthread.h>
# include <signal.h>

stp_t *stp;

const u8 eth_stp_addr[] = {0x01,0x80,0xc2,0x00,0x00,0x01};

/* 判断结点为根结点,判断的依据是:只要结点认为的根结点就是自己的
 结点编号*/
static bool stp_is_root_switch(stp_t *sp)
{
	return stp->desinated_root == stp->switch_id;
}


/*判断端口为指定端口,对于端口结构体 *p
发送这条Config消息的结点 为 其所在的 stp 结构体的结点
表明是从本端口所在的结点发送出去的,否则不能判断
同时,发送这条Config消息的端口 就是本端口的端口id*/
static bool stp_port_is_designated(stp_port_t *p)
{
	return p->designated_switch == p->stp->switch_id &&
		p->designated_port == p->port_id;
}

/*判断端口的状态,是根端口或者指定端口或者非指定端口*/
static const char*stp_port_state(stp_port_t *p)
{
	if(p->stp->root_port && \
		p->port_id == p->stp->root_port->port_id)
	/*如果端口所在的stp指向的根端口存在,
	并且根端口的端口id就是p的端口id*/
		return "ROOT";
	else if(p->designated_switch == p->stp->switch_id && \
		p->designated_port == p->port_id)
	/*发送这条Config消息的结点就是p所在的stp的结点信息
	并且发送这条Config消息的端口ID就是p的端口ID
	则p为指定端口*/
		return "DESIGNATED";
	else
	/*否则为非指定端口*/
		return "ALTERNATE";
}

/*stp端口发送packet消息,本实验应该不需要*/
static void stp_port_send_packet(stp_port_t *p,void *stp_msg,int msg_len)
{
	int pkt_len = ETHER_HDR_SIZE + LLC_HDR_SIZE + msg_len;
	char *pkt = malloc(pkt_len);
	/*定义消息长度并分配空间*/
	// 以太网表头
	struct ether_header *eth = (sturct ether_header *)pkt;
	memcpy(eth->ether_dhost,eth_stp_addr,6);
	/*目的地址已经默认写好*/
	memcpy(eth->ether_shost,p->iface->mac,6);
	/*调用了端口的MAC地址*/
	eth->ether_type = htons(pkt_len - ETHER_HDR_SIZE);
	/*类型:转换字节序和大小端*/

	// LLC 头部
	struct llc_header *llc = (struct llc_header *)(pkt + ETHER_HDR_SIZE);
	llc->llc_dsap = LLC_DSAP_SNAP;
	llc->llc_ssap = LLC_SSAP_SNAP;
	llc->llc_cntl = LLC_CNTL_SNAP;

	memcpy(pkt + ETHER_HDR_SIZE + LLC_HDR_SIZE,stp_msg,msg_len);
	/*定义发送的消息和消息长度,LLC头部封装*/
	iface_send_packet(p->iface,pkt,pkt_len);
}

/*这里需要的是:stp端口发送Config消息
传入参数是需要发送的端口*/
static void stp_port_send_config(stp_port_t *p)
{
	stp_t *stp = p->stp;
	// 端口p所在的结点
	bool is_root = stp_is_root_switch(stp);
	// p所在的结点是否为根结点
	if(!is_root && !stp->root_port){
		return;
	}
	/*如果p所在的结点的根端口为空 且 p所在的结点不是根结点,
	则不需要发送,返回 */

	struct stp_config config;
	//定义Config消息
	memset(&config,0,sizeof(config));
	//先清零,下面为定义
	config.header.proto_id = htons(STP_PROTOCOL_ID);
	config.header.version = STP_PROTOCOL_VERSION;
	config.header.msg_type = STP_TYPE_CONFIG;
	config.flags = 0;
	config.root_id = htonl(stp->desinated_root);
	config.root_path_cost = htonl(stp->root_path_cost);
	config.switch_id = htonl(stp->switch_id);
	/*感觉应该是
	config.switch_id = htonl(p->designated_switch)*/
	config.port_id = htons(p->port_id);
	/*感觉应该是
	config.port_id = htons(p->designated_port)*/
	config.msg_age = htons(0);
	config.max_age = htons(STP_MAX_AGE);
	config.hello_time = htons(STP_HELLO_TIME);
	config.fwd_delay = htons(STP_FWD_DELAY);

	// 打印输出信息
	stp_port_send_packet(p,&config,sizeof(config));
}

/*stp结点发送Config消息,需要指定端口*/
static void stp_send_config(stp_t *stp)
{
	for(int i = 0; i < stp->nports;i++){
		stp_port_t *p = &stp->ports[i];
		if(stp_port_is_designated(p)){
			stp_port_send_config(p);
		}
	}
}

/*设置发送消息时间*/
static void stp_handle_hello_timeout(void *arg)
{
	stp_t *stp = arg;
	stp_send_config(stp);
	stp_start_timer(&stp->hello_timer,time_tick_now());
	/*开始计时*/
}


/*初始化 port端口*/
static void stp_port_init(stp_port_t *p)
{
	stp_t *stp = p->stp;
/*认为发送Config消息的端口就是自己,switch就是stp所在的结点
id,花销就是到根结点的最小花销*/
	p->desinated_root = stp->desinated_root;
	p->designated_switch = stp->switch_id;
	p->designated_port = p->port_id;
	p->designated_cost = stp->root_path_cost;
}

/*设置计时器运行机制,加锁与解锁
记录当前时间,上锁,直到超时判断成功
释放锁,等待100s的时间,再次循环*/
void *stp_timer_routine(void *arg)
{
	while(true){
		long long int now = time_tick_now();

		pthread_mutex_lock(&stp->lock);
		stp_timer_run_once(now);

		pthread_mutex_unlock(&stp->lock);

		usleep(100);
	}
	return NULL;
}

/*stp处理Config包*/
static void stp_handle_config_packet(stp_t *stp, \
	stp_port_t *p,struct stp_config *config)
{
	//TODO:handle config packet here
	fprintf(stdout,"TODO: handle config packet here.\n");
}

/*stp_port端口处理Config包*/
void stp_port_handle_packet(stp_port_t *p,char packet,int pkt_len)
{
	stp_t *stp = p->stp;

	pthread_mutex_lock(&stp->lock);

	// 协议检查
	struct stp_header *header = (struct stp_header *)(packet + ETHER_HDR_SIZE + LLC_HDR_SIZE);
	// 如果是Config消息,则调用先前的函数
	if(header->msg_type == STP_TYPE_CONFIG){
		stp_handle_config_packet(stp,p,(sturct stp_config*)header);
	}
	// 其它类型不做处理
	else if(header->msg_type == STP_TYPE_TCN){
		log(ERROR,"TCN packet is not supported in thie lab.");
	}
	else{
		log(ERROR,"received invalid STP packet.");
	}
	// 释放锁
	pthread_mutex_unlock(&stp->lock);
}

/*打印stp表的状态,获取stp表的结点类型
如果是根结点,则打印,否则输出指定的根结点和路径
并且循环遍历每一个端口,打印端口id,端口类型,
获取端口的id,发送此Config消息的结点和端口,到根结点花销*/
static void *stp_dump_state(void *arg)
{
# define get_switch_id(switch_id) (int)(switch_id & 0xFFFF)
# define get_port_id(port_id) (int)(port_id & 0xFF)
	pthread_mutex_lock(&stp->lock);

	bool is_root = stp_is_root_switch(stp);
	if(is_root){
		log(INFO,"this switch is root.");
	}
	else{
		log(INFO,"non-root switch,designated root : %04x,
			root path cost: %d.",\ get_switch_id(stp->designated_root),stp->root_path_cost);
	}

	for(int i = 0; i < stp->nports; i++){
		stp_port_t *p = &stp->ports[i];
		log(INFO,"port id: %02d,role: %s.",get_port_id(p->port_id), \
			stp_port_state(p));
		log(INFO,"\tdesignated -> root: %04x, -> switch: %04x," \
			"-> port: %02d, -> cost: %d.",\
			get_switch_id(p->designated_root), \
			get_switch_id(p->designated_switch),\
			get_port_id(p->designated_port),\
			p->designated_cost);
	}
	pthread_mutex_unlock(&stp->lock);

	exit(0);	
}


/*如果输入 SIGTERM,则终止进程,并打印stp表*/
static void stp_handle_signal(int signal)
{
	if(signal == SIGTERM){
		log(DEBUG,"received SIGTERM,terminate this program.");

		pthread_t pid;
		pthread_create(&pid,NULL,stp_dump_state,NULL);
	}
}


void stp_init(struct list_head *iface_list)
{
	stp = malloc(sizeof(*stp));

	// 设置 switch ID
	u64 mac_addr = 0;
	iface_info_t *iface = list_entry(iface_list->next,iface_info_t,list);
	for(int i = 0; i < sizeof(iface->mac);i++){
		mac_addr <<= 8;
		mac_addr += iface->mac[i];
	}
	stp->switch_id = mac_addr | ((u64) STP_BRIDGE_PRIORITY << 48);

/*初始时,自己为根结点,路径开销为0,根端口未知*/
	stp -> designated_root = stp->switch_id;
	stp -> root_path_cost = 0;
	stp -> root_port = NULL;

	stp_init_timer(&stp->hello_timer,STP_HELLO_TIME, \
		stp_handle_hello_timeout,(void *)stp);
	stp_start_timer(&stp->hello_timer,time_tick_now());

	stp->nports = 0;
	list_for_each_entry(iface,iface_list,list){
		stp_port_t *p = &stp->ports[stp->nports];

		p-> stp = stp;
		p-> port_id = (STP_PORT_PRIORITY << 8) | (stp->nports + 1);
		p -> port_name = strdup(iface->name);
		p -> iface = iface;
		p -> path_cost = 1;

		stp_port_init(p);

		iface ->port = p;
		stp ->nports += 1;
	}

	pthread_mutex_init(&stp->lock,NULL);
	pthread_create(&stp->time_thread,NULL,stp_timer_routine,NULL);

	signal(SIGTERM,stp_handle_signal);
}

void stp_destory()
{
	pthread_kill(stp -> timer_thread,SIGKILL);

	for(int i = 0; i < stp->nports; i++){
		stp_port_t *port = &stp->ports[i];
		port->iface->port = NULL;
		free(port->port_name);
	}
	free(stp);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值