实现功能:设计一个新的action,实现在冗余链路中的数据包去重
一:在内核级定义OVS action
(一)在datapath/linux/compat/include/linux/openvswitch.h中添加:
enum ovs_action_attr {
/* ... */
/*
* after #ifndef __KERNEL__ ... #endif.
* the equals is thus ABSOLUTELY NECESSARY
*/
OVS_ACTION_ATTR_RMDUPQUEUE = 23, /* struct ovs_action_rmdupqueue. */
__OVS_ACTION_ATTR_MAX, /* Nothing past this will be accepted
* from userspace. */
/* ... */
}
(二)注意:指定显示值
OVS_ACTION_ATTR_RMDUPQUEUE = 23 如果我们不为该枚举条目指定显式值,则内核和用户区部分 ovs-vswitchd将对新操作使用不同的代码(这里不加会出错)
(三)定义内核级别的OVS action的消息结构体
/*
* struct ovs_action_rmdupqueue - %OVS_ACTION_ATTR_RMDUPQUEUE action argument.
* @queue_id: Algorithm used to choose queue number.
* @max_len: basis used for setting queue[queue_id] size.
*/
struct ovs_action_rmdupqueue{
uint32_t queue_id;
uint32_t max_len;
};
二:在内核模块中实现自定义action的实现函数,用于调用执行
(一)队列业务实现,在datapath/flow_netlink.h中定义队列
int nsh_hdr_from_nlattr(const struct nlattr *attr, struct nshhdr *nh,
size_t size);
//-------------------queue start---------------------
#define MAX_QUEUE_SIZE 1000 //最多可以为1000个流提供服务
#define MAX_QUEUE_LEN 1000 //----改进:动态自适应算法,自动选择队列大小 或者 滑动窗口协议PRP
typedef struct
{
int *queue; //队列指针(动态分配队列空间)
int NUM; //队列大小
int TOP, REAR; //队首队尾标识
int EmpFlag; //队列判空标识
}Queue;
void InitQueue(Queue* q, int n); //初始化队列
int EmptyOrFullQueue(Queue q); //队列判空以及判断满
int QueueLength(Queue q); //获取队列大小
int PushQueue(Queue* q,int ele); //入队操作
int PopQueue(Queue* q); //出队操作
int RePushQueue(Queue* q, int ele); //当一个数据第二次到达时对数据进行匹配出队操作
int FindElePos(Queue q, int ele,int* n); //查找元素位置
void ShowData(Queue q); //显示队列数据
extern Queue Que[];
//-----------------queue end----------------------------
#endif /* flow_netlink.h */

(二)队列业务实现,在datapath/flow_netlink.c中实现队列

//-------------------queue start---------------------
void InitQueue(Queue* q,int n)
{
q->NUM = n;
//空间回收
if (q->queue != NULL)
{
kfree(q->queue); //改进:设置一个新的action(---del-flows指令,不是action) 实现队列的释放,清除上一个action
q->queue = NULL;
}
if (q->NUM != 0)
{
q->queue = (int *)kmalloc(sizeof(int)*n,GFP_KERNEL);
memset(q->queue, 0, sizeof(int)*n);
}
q->TOP = q->REAR = 0;
q->EmpFlag = 1; //空队列
}
int EmptyOrFullQueue(Queue q)
{
return q.EmpFlag;
}
int QueueLength(Queue q)
{
if (EmptyOrFullQueue(q) == 1)
return 0;
if (EmptyOrFullQueue(q) == 2)
return q.NUM;
if (q.TOP > q.REAR)
return q.REAR + q.NUM - q.TOP;
else
return q.REAR - q.TOP;
}
int PushQueue(Queue* q, int ele)
{
if (q->NUM <= 0)
return 0; //队列空间已经释放 设置常量
if (RePushQueue(q, ele) == 1) //重复插入,冗余数据---重点 改进:定义网络新协议,替换ip标识
return 1;
if (q->EmpFlag == 2) //队列满的情况入队
PopQueue(q); //先出队队首,再入队
q->queue[q->REAR] = ele;
q->REAR = (q->REAR + 1) % q->NUM;
if (q->TOP == q->REAR)
q->EmpFlag = 2; //为满队列
else
q->EmpFlag = 3;
return 0;
}
int PopQueue(Queue* q)
{
if (q->NUM <= 0)
return -2; //队列空间已经释放 改进:可以队列动态空间划分
int temp = q->queue[q->TOP];
if (q->EmpFlag == 1) //队列为空时,不允许出队
return -1;
q->TOP++;
if (q->TOP == q->NUM)
q->TOP = 0;
if (q->TOP == q->REAR)
q->EmpFlag = 1; //为空队列
else
q->EmpFlag = 3;
return temp;
}
int RePushQueue(Queue* q, int ele)
{
int n; //用于记录元素个数
int pos = FindElePos(*q, ele, &n);
if (pos == -1)
return 0; //可以直接插入
q->TOP = pos;
if (QueueLength(*q) == 0)
q->EmpFlag = 1;
else
q->EmpFlag = 3;
return 1; //队列有重复
}
int FindElePos(Queue q, int ele,int* n)
{
int i;
for (i = 0; i < QueueLength