生成树相关框架代码
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);
}